diff --git a/.gitignore b/.gitignore index 5e89576..903c8b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ **/build/ **/.cache/ **/.vscode/ +*.out compile_commands.json diff --git a/Chapter11/compile_time/.clang-tidy b/Chapter11/compile_time/.clang-tidy index 44ea8be..9ab07d4 100644 --- a/Chapter11/compile_time/.clang-tidy +++ b/Chapter11/compile_time/.clang-tidy @@ -9,6 +9,7 @@ Checks: > -performance-no-int-to-ptr, portability-*, readability-*, + -readability-identifier-length readability-identifier-naming CheckOptions: diff --git a/Chapter11/compile_time/CMakeLists.txt b/Chapter11/compile_time/CMakeLists.txt index d570840..a87e149 100644 --- a/Chapter11/compile_time/CMakeLists.txt +++ b/Chapter11/compile_time/CMakeLists.txt @@ -86,7 +86,9 @@ include_directories( ${CMAKE_SOURCE_DIR}/hal/uart/inc ${CMAKE_SOURCE_DIR}/hal/inc ${CMAKE_SOURCE_DIR}/hal/gpio/inc + ${CMAKE_SOURCE_DIR}/hal/adc/inc ${CMAKE_SOURCE_DIR}/cstdlib_support + ${CMAKE_SOURCE_DIR}/util/inc ) set(EXECUTABLE ${PROJECT_NAME}.elf) @@ -108,7 +110,9 @@ add_executable( hal/uart/src/uart_stm32.cpp hal/gpio/src/gpio.cpp hal/gpio/src/gpio_interrupt_manager.cpp + hal/adc/src/adc_stm32.cpp cstdlib_support/retarget.cpp + util/src/units.cpp ${MAIN_CPP_PATH}/${MAIN_CPP_FILE_NAME} ) diff --git a/Chapter11/compile_time/app/src/main_lookup_table.cpp b/Chapter11/compile_time/app/src/main_lookup_table.cpp index eb0fe5b..325e32f 100644 --- a/Chapter11/compile_time/app/src/main_lookup_table.cpp +++ b/Chapter11/compile_time/app/src/main_lookup_table.cpp @@ -4,69 +4,31 @@ #include #include +#include #include +#include #include +#include #include #include -#include - -ADC_HandleTypeDef hadc; - -static void MX_ADC_Init(void) -{ - - /* USER CODE BEGIN ADC_Init 0 */ - - /* USER CODE END ADC_Init 0 */ - - ADC_ChannelConfTypeDef sConfig = {0}; - - /* USER CODE BEGIN ADC_Init 1 */ - - /* USER CODE END ADC_Init 1 */ - - /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) - */ - hadc.Instance = ADC1; - hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1; - hadc.Init.Resolution = ADC_RESOLUTION_12B; - hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT; - hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD; - hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV; - hadc.Init.LowPowerAutoWait = DISABLE; - hadc.Init.LowPowerAutoPowerOff = DISABLE; - hadc.Init.ContinuousConvMode = DISABLE; - hadc.Init.DiscontinuousConvMode = DISABLE; - hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START; - hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; - hadc.Init.DMAContinuousRequests = DISABLE; - hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED; - if (HAL_ADC_Init(&hadc) != HAL_OK) - { - //Error_Handler(); - } - - /** Configure for the selected ADC regular channel to be converted. - */ - sConfig.Channel = ADC_CHANNEL_0; - sConfig.Rank = ADC_RANK_CHANNEL_NUMBER; - sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5; - if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) - { - //Error_Handler(); - } - /* USER CODE BEGIN ADC_Init 2 */ - - /* USER CODE END ADC_Init 2 */ - -} +namespace { + struct voltage_divider { + units::resistance r2; + units::voltage vcc; + units::resistance get_r1(units::voltage vadc) { + return r2 * (vcc/vadc - 1); + } + }; +}; int main() { + using namespace units; + hal::init(); hal::uart_stm32 uart(USART2); @@ -74,28 +36,58 @@ int main() retarget::set_stdio_uart(&uart); - MX_ADC_Init(); + hal::adc_stm32 adc(3.3_V); + adc.init(); + + constexpr float A = 1.18090254918130e-3; + constexpr float B = 2.16884014794388e-4; + constexpr float C = 1.90058756197216e-6; + constexpr float D = 1.83161892641824e-8; + + constexpr int c_lut_points = 50; + + constexpr signal resistance(1e3, 10e3); + + constexpr auto temperature_k = 1 / (A + + B * signal(resistance, [](float x) + { return std::log(x); }) + + C * signal(resistance, [](float x) + { return std::pow(std::log(x), 2); }) + + D * signal(resistance, [](float x) + { return std::pow(std::log(x), 3); })); + + constexpr auto temperature_celsius = temperature_k - 273.15; + + voltage_divider divider{10e3_Ohm, 3.3_V}; - - const hal::gpio_stm32 button1(hal::pin::p4, [](){ - printf("Button1 pressed!\r\n"); - }); - - const hal::gpio_stm32 button2(hal::pin::p5, [](){ - printf("Button2 pressed!\r\n"); - }); - - while(true) { - HAL_ADC_Start(&hadc); - HAL_ADC_PollForConversion(&hadc, 1000); + auto adc_val = adc.get_reading(); + if(adc_val) { + auto adc_val_voltage = *adc_val; + auto thermistor_r = divider.get_r1(adc_val_voltage); - auto adc_val = HAL_ADC_GetValue(&hadc); - float mv = 3.3f * static_cast(adc_val) / 4096.f; - printf("%d, %.2f\r\n", adc_val, mv); - hal::time::delay_ms(1000); + auto it = std::lower_bound(resistance.begin(), resistance.end(), thermistor_r.get()); + + if(it != resistance.end()) { + + std::size_t pos = std::distance(resistance.begin(), it); + float temperature = temperature_celsius.at(pos); + + printf("%d mV, %d Ohm, %d.%d C\r\n", static_cast(adc_val_voltage.get_mili()), + static_cast(thermistor_r.get()), + static_cast(temperature), + static_cast(10*(temperature-std::floor(temperature))) + ); + } + } + hal::time::delay_ms(200); } } -// adc FeedVoltageSampleToChannel 0 3100 10 \ No newline at end of file +// adc FeedVoltageSampleToChannel 0 3000 3 -> 1001 Ohm +// adc FeedVoltageSampleToChannel 0 2800 3 -> 1787 Ohm +// adc FeedVoltageSampleToChannel 0 2400 3 -> 3754 Ohm +// adc FeedVoltageSampleToChannel 0 2200 3 -> 5003 Ohm +// adc FeedVoltageSampleToChannel 0 2000 3 -> 6502 Ohm +// adc FeedVoltageSampleToChannel 0 1700 3 -> 9412 Ohm \ No newline at end of file diff --git a/Chapter11/compile_time/hal/adc/inc/adc.hpp b/Chapter11/compile_time/hal/adc/inc/adc.hpp new file mode 100644 index 0000000..cea69ec --- /dev/null +++ b/Chapter11/compile_time/hal/adc/inc/adc.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include + +namespace hal +{ + class adc { + public: + enum class error { + timeout + }; + + virtual void init() = 0; + virtual std::expected get_reading() = 0; + }; +}; \ No newline at end of file diff --git a/Chapter11/compile_time/hal/adc/inc/adc_stm32.hpp b/Chapter11/compile_time/hal/adc/inc/adc_stm32.hpp new file mode 100644 index 0000000..050ca43 --- /dev/null +++ b/Chapter11/compile_time/hal/adc/inc/adc_stm32.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +#include +#include + +namespace hal +{ + + class adc_stm32 : public adc{ + public: + adc_stm32(units::voltage ref_voltage) : ref_voltage_(ref_voltage) {} + + void init() override; + std::expected get_reading() override; + private: + ADC_HandleTypeDef adc_handle_; + units::voltage ref_voltage_; + }; +}; \ No newline at end of file diff --git a/Chapter11/compile_time/hal/adc/src/adc_stm32.cpp b/Chapter11/compile_time/hal/adc/src/adc_stm32.cpp new file mode 100644 index 0000000..ba15253 --- /dev/null +++ b/Chapter11/compile_time/hal/adc/src/adc_stm32.cpp @@ -0,0 +1,47 @@ +#include + +void hal::adc_stm32::init() { + ADC_ChannelConfTypeDef sConfig = {0}; + + /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) + */ + adc_handle_.Instance = ADC1; + adc_handle_.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1; + adc_handle_.Init.Resolution = ADC_RESOLUTION_12B; + adc_handle_.Init.DataAlign = ADC_DATAALIGN_RIGHT; + adc_handle_.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD; + adc_handle_.Init.EOCSelection = ADC_EOC_SINGLE_CONV; + adc_handle_.Init.LowPowerAutoWait = DISABLE; + adc_handle_.Init.LowPowerAutoPowerOff = DISABLE; + adc_handle_.Init.ContinuousConvMode = DISABLE; + adc_handle_.Init.DiscontinuousConvMode = DISABLE; + adc_handle_.Init.ExternalTrigConv = ADC_SOFTWARE_START; + adc_handle_.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; + adc_handle_.Init.DMAContinuousRequests = DISABLE; + adc_handle_.Init.Overrun = ADC_OVR_DATA_PRESERVED; + if (HAL_ADC_Init(&adc_handle_) != HAL_OK) + { + //Error_Handler(); + } + + /** Configure for the selected ADC regular channel to be converted. + */ + sConfig.Channel = ADC_CHANNEL_0; + sConfig.Rank = ADC_RANK_CHANNEL_NUMBER; + sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5; + if (HAL_ADC_ConfigChannel(&adc_handle_, &sConfig) != HAL_OK) + { + //Error_Handler(); + } +} + +std::expected hal::adc_stm32::get_reading() { + HAL_ADC_Start(&adc_handle_); + if(HAL_ADC_PollForConversion(&adc_handle_, 1000) != HAL_OK) { + return std::unexpected(hal::adc::error::timeout); + } + + auto adc_val = HAL_ADC_GetValue(&adc_handle_); + + return ref_voltage_ * adc_val / 4096.f; +} \ No newline at end of file diff --git a/Chapter11/compile_time/hal/gpio/inc/gpio.hpp b/Chapter11/compile_time/hal/gpio/inc/gpio.hpp index 9e9c45d..9141590 100644 --- a/Chapter11/compile_time/hal/gpio/inc/gpio.hpp +++ b/Chapter11/compile_time/hal/gpio/inc/gpio.hpp @@ -3,8 +3,6 @@ #include #include -//#include - namespace hal { class gpio diff --git a/Chapter11/compile_time/util/inc/signal.hpp b/Chapter11/compile_time/util/inc/signal.hpp new file mode 100644 index 0000000..45d6198 --- /dev/null +++ b/Chapter11/compile_time/util/inc/signal.hpp @@ -0,0 +1,102 @@ +#pragma once + +#include +#include + +template +struct signal : public std::array +{ + + constexpr signal() {} + + constexpr signal(T begin, T end) + { + static_assert(N > 1, "N must be bigger than 1"); + float step = (end - begin) / (N - 1); + + for (std::size_t i = 0; i < N; i++) + { + this->at(i) = begin + i * step; + } + } + + constexpr signal(const std::array &x, auto fun) + { + for (std::size_t i = 0; i < N; i++) + { + this->at(i) = fun(x.at(i)); + } + } + + constexpr signal(const signal &sig, auto fun) + { + for (std::size_t i = 0; i < N; i++) + { + this->at(i) = fun(sig.at(i)); + } + } + + constexpr signal operator+(const T &t) const + { + return signal(*this, [&](T elem) + { return elem + t; }); + }; + + constexpr signal operator-(const T &t) const + { + return signal(*this, [&](T elem) + { return elem - t; }); + }; + + constexpr signal operator*(const T &t) const + { + return signal(*this, [&](T elem) + { return elem * t; }); + }; + + constexpr signal operator/(const T &t) const + { + return signal(*this, [&](T elem) + { return elem / t; }); + }; + + constexpr signal operator+(const signal &sig) const + { + signal ret; + for (std::size_t i = 0; i < N; i++) + { + ret.at(i) = this->at(i) + sig.at(i); + } + return ret; + }; + + constexpr signal operator-(const signal &sig) const + { + signal ret; + for (std::size_t i = 0; i < N; i++) + { + ret.at(i) = this->at(i) - sig.at(i); + } + return ret; + }; + + friend constexpr signal operator+(const T &t, const signal &sig) + { + return sig + t; + } + + friend constexpr signal operator*(const T &t, const signal &sig) + { + return sig * t; + } + + friend constexpr signal operator/(const T &t, const signal &sig) + { + signal ret; + for (std::size_t i = 0; i < N; i++) + { + ret.at(i) = t / sig.at(i); + } + return ret; + } +}; diff --git a/Chapter11/compile_time/util/inc/units.hpp b/Chapter11/compile_time/util/inc/units.hpp new file mode 100644 index 0000000..dd8b55c --- /dev/null +++ b/Chapter11/compile_time/util/inc/units.hpp @@ -0,0 +1,33 @@ +#pragma once + +namespace units +{ + template + class unit { + private: + T val_; + public: + explicit unit(T val) : val_(val){} + [[nodiscard]] T get() const {return val_;} + [[nodiscard]] T get_mili() const {return 1e3 * val_;} + + constexpr T operator/(const unit& second) const{ + return val_ / second.get(); + } + + constexpr unit operator*(const T& second) const{ + return unit(val_ * second); + } + + constexpr unit operator/(const T& second) const{ + return unit(val_ / second); + } + }; + + using voltage = unit; + using resistance = unit; + + voltage operator""_V(long double volts); + + resistance operator""_Ohm(long double ohms); +}; \ No newline at end of file diff --git a/Chapter11/compile_time/util/src/units.cpp b/Chapter11/compile_time/util/src/units.cpp new file mode 100644 index 0000000..0db3cf6 --- /dev/null +++ b/Chapter11/compile_time/util/src/units.cpp @@ -0,0 +1,11 @@ +#include + +namespace units { +voltage operator""_V(long double volts) { + return voltage(static_cast(volts)); +} + +resistance operator""_Ohm(long double ohms) { + return resistance(static_cast(ohms)); +} +};