update lookup table example
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
**/build/
|
**/build/
|
||||||
**/.cache/
|
**/.cache/
|
||||||
**/.vscode/
|
**/.vscode/
|
||||||
|
*.out
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ Checks: >
|
|||||||
-performance-no-int-to-ptr,
|
-performance-no-int-to-ptr,
|
||||||
portability-*,
|
portability-*,
|
||||||
readability-*,
|
readability-*,
|
||||||
|
-readability-identifier-length
|
||||||
readability-identifier-naming
|
readability-identifier-naming
|
||||||
|
|
||||||
CheckOptions:
|
CheckOptions:
|
||||||
|
|||||||
@@ -86,7 +86,9 @@ include_directories(
|
|||||||
${CMAKE_SOURCE_DIR}/hal/uart/inc
|
${CMAKE_SOURCE_DIR}/hal/uart/inc
|
||||||
${CMAKE_SOURCE_DIR}/hal/inc
|
${CMAKE_SOURCE_DIR}/hal/inc
|
||||||
${CMAKE_SOURCE_DIR}/hal/gpio/inc
|
${CMAKE_SOURCE_DIR}/hal/gpio/inc
|
||||||
|
${CMAKE_SOURCE_DIR}/hal/adc/inc
|
||||||
${CMAKE_SOURCE_DIR}/cstdlib_support
|
${CMAKE_SOURCE_DIR}/cstdlib_support
|
||||||
|
${CMAKE_SOURCE_DIR}/util/inc
|
||||||
)
|
)
|
||||||
|
|
||||||
set(EXECUTABLE ${PROJECT_NAME}.elf)
|
set(EXECUTABLE ${PROJECT_NAME}.elf)
|
||||||
@@ -108,7 +110,9 @@ add_executable(
|
|||||||
hal/uart/src/uart_stm32.cpp
|
hal/uart/src/uart_stm32.cpp
|
||||||
hal/gpio/src/gpio.cpp
|
hal/gpio/src/gpio.cpp
|
||||||
hal/gpio/src/gpio_interrupt_manager.cpp
|
hal/gpio/src/gpio_interrupt_manager.cpp
|
||||||
|
hal/adc/src/adc_stm32.cpp
|
||||||
cstdlib_support/retarget.cpp
|
cstdlib_support/retarget.cpp
|
||||||
|
util/src/units.cpp
|
||||||
${MAIN_CPP_PATH}/${MAIN_CPP_FILE_NAME}
|
${MAIN_CPP_PATH}/${MAIN_CPP_FILE_NAME}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -4,69 +4,31 @@
|
|||||||
#include <stm32f072xb.h>
|
#include <stm32f072xb.h>
|
||||||
|
|
||||||
#include <hal.hpp>
|
#include <hal.hpp>
|
||||||
|
#include <units.hpp>
|
||||||
#include <uart_stm32.hpp>
|
#include <uart_stm32.hpp>
|
||||||
|
#include <adc_stm32.hpp>
|
||||||
|
|
||||||
#include <retarget.hpp>
|
#include <retarget.hpp>
|
||||||
|
#include <signal.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include <gpio_stm32.hpp>
|
namespace {
|
||||||
|
struct voltage_divider {
|
||||||
ADC_HandleTypeDef hadc;
|
units::resistance r2;
|
||||||
|
units::voltage vcc;
|
||||||
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 */
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
units::resistance get_r1(units::voltage vadc) {
|
||||||
|
return r2 * (vcc/vadc - 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
using namespace units;
|
||||||
|
|
||||||
hal::init();
|
hal::init();
|
||||||
|
|
||||||
hal::uart_stm32 uart(USART2);
|
hal::uart_stm32 uart(USART2);
|
||||||
@@ -74,28 +36,58 @@ int main()
|
|||||||
|
|
||||||
retarget::set_stdio_uart(&uart);
|
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<float, c_lut_points> 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<hal::port_a> button1(hal::pin::p4, [](){
|
|
||||||
printf("Button1 pressed!\r\n");
|
|
||||||
});
|
|
||||||
|
|
||||||
const hal::gpio_stm32<hal::port_a> button2(hal::pin::p5, [](){
|
|
||||||
printf("Button2 pressed!\r\n");
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
HAL_ADC_Start(&hadc);
|
auto adc_val = adc.get_reading();
|
||||||
HAL_ADC_PollForConversion(&hadc, 1000);
|
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);
|
auto it = std::lower_bound(resistance.begin(), resistance.end(), thermistor_r.get());
|
||||||
float mv = 3.3f * static_cast<float>(adc_val) / 4096.f;
|
|
||||||
printf("%d, %.2f\r\n", adc_val, mv);
|
if(it != resistance.end()) {
|
||||||
hal::time::delay_ms(1000);
|
|
||||||
|
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<int>(adc_val_voltage.get_mili()),
|
||||||
|
static_cast<int>(thermistor_r.get()),
|
||||||
|
static_cast<int>(temperature),
|
||||||
|
static_cast<int>(10*(temperature-std::floor(temperature)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hal::time::delay_ms(200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// adc FeedVoltageSampleToChannel 0 3100 10
|
// 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
|
||||||
18
Chapter11/compile_time/hal/adc/inc/adc.hpp
Normal file
18
Chapter11/compile_time/hal/adc/inc/adc.hpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <expected>
|
||||||
|
|
||||||
|
#include <units.hpp>
|
||||||
|
|
||||||
|
namespace hal
|
||||||
|
{
|
||||||
|
class adc {
|
||||||
|
public:
|
||||||
|
enum class error {
|
||||||
|
timeout
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual void init() = 0;
|
||||||
|
virtual std::expected<units::voltage, adc::error> get_reading() = 0;
|
||||||
|
};
|
||||||
|
};
|
||||||
22
Chapter11/compile_time/hal/adc/inc/adc_stm32.hpp
Normal file
22
Chapter11/compile_time/hal/adc/inc/adc_stm32.hpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <adc.hpp>
|
||||||
|
#include <units.hpp>
|
||||||
|
|
||||||
|
#include <stm32f0xx_hal.h>
|
||||||
|
#include <stm32f072xb.h>
|
||||||
|
|
||||||
|
namespace hal
|
||||||
|
{
|
||||||
|
|
||||||
|
class adc_stm32 : public adc{
|
||||||
|
public:
|
||||||
|
adc_stm32(units::voltage ref_voltage) : ref_voltage_(ref_voltage) {}
|
||||||
|
|
||||||
|
void init() override;
|
||||||
|
std::expected<units::voltage, adc::error> get_reading() override;
|
||||||
|
private:
|
||||||
|
ADC_HandleTypeDef adc_handle_;
|
||||||
|
units::voltage ref_voltage_;
|
||||||
|
};
|
||||||
|
};
|
||||||
47
Chapter11/compile_time/hal/adc/src/adc_stm32.cpp
Normal file
47
Chapter11/compile_time/hal/adc/src/adc_stm32.cpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#include <adc_stm32.hpp>
|
||||||
|
|
||||||
|
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<units::voltage, hal::adc::error> 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;
|
||||||
|
}
|
||||||
@@ -3,8 +3,6 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
//#include <gpio_interrupt_handler.hpp>
|
|
||||||
|
|
||||||
namespace hal
|
namespace hal
|
||||||
{
|
{
|
||||||
class gpio
|
class gpio
|
||||||
|
|||||||
102
Chapter11/compile_time/util/inc/signal.hpp
Normal file
102
Chapter11/compile_time/util/inc/signal.hpp
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
struct signal : public std::array<T, N>
|
||||||
|
{
|
||||||
|
|
||||||
|
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<T, N> &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;
|
||||||
|
}
|
||||||
|
};
|
||||||
33
Chapter11/compile_time/util/inc/units.hpp
Normal file
33
Chapter11/compile_time/util/inc/units.hpp
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace units
|
||||||
|
{
|
||||||
|
template<typename T, typename ST>
|
||||||
|
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<float, struct the_voltage>;
|
||||||
|
using resistance = unit<float, struct the_resistance>;
|
||||||
|
|
||||||
|
voltage operator""_V(long double volts);
|
||||||
|
|
||||||
|
resistance operator""_Ohm(long double ohms);
|
||||||
|
};
|
||||||
11
Chapter11/compile_time/util/src/units.cpp
Normal file
11
Chapter11/compile_time/util/src/units.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#include <units.hpp>
|
||||||
|
|
||||||
|
namespace units {
|
||||||
|
voltage operator""_V(long double volts) {
|
||||||
|
return voltage(static_cast<float>(volts));
|
||||||
|
}
|
||||||
|
|
||||||
|
resistance operator""_Ohm(long double ohms) {
|
||||||
|
return resistance(static_cast<float>(ohms));
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user