From a9506c81fcf7b177fa5ecc641b2f0aa2ca57496c Mon Sep 17 00:00:00 2001 From: velichko Date: Sun, 24 Aug 2025 02:01:31 +0300 Subject: [PATCH 01/12] Issue #24: add files adc_driver.c and adc_driver.h --- KPI_Rover/ADC/adc_driver.c | 0 KPI_Rover/ADC/adc_driver.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 KPI_Rover/ADC/adc_driver.c create mode 100644 KPI_Rover/ADC/adc_driver.h diff --git a/KPI_Rover/ADC/adc_driver.c b/KPI_Rover/ADC/adc_driver.c new file mode 100644 index 0000000..e69de29 diff --git a/KPI_Rover/ADC/adc_driver.h b/KPI_Rover/ADC/adc_driver.h new file mode 100644 index 0000000..e69de29 From 8c64e6da67e152ca16102cdb424841e9193ff844 Mon Sep 17 00:00:00 2001 From: velichko Date: Sun, 24 Aug 2025 02:04:05 +0300 Subject: [PATCH 02/12] Issue #24: add ADC driver: DMA, EMA, calibration, FreeRTOS notify, callback --- KPI_Rover/ADC/adc_driver.c | 124 +++++++++++++++++++++++++++++++++++++ KPI_Rover/ADC/adc_driver.h | 27 ++++++++ 2 files changed, 151 insertions(+) diff --git a/KPI_Rover/ADC/adc_driver.c b/KPI_Rover/ADC/adc_driver.c index e69de29..4d69b47 100644 --- a/KPI_Rover/ADC/adc_driver.c +++ b/KPI_Rover/ADC/adc_driver.c @@ -0,0 +1,124 @@ +#include "adc_driver.h" +#include "stm32f4xx_hal.h" +#include "ulog.h" + +#define MAX_ADC_CHANNELS 16 +#define EMA_ALPHA 0.2f + +static uint8_t adc_channels[MAX_ADC_CHANNELS]; +static uint16_t adc_raw_values[MAX_ADC_CHANNELS]; +static uint16_t adc_filtered_values[MAX_ADC_CHANNELS]; +static size_t channel_count = 0; + +static TaskHandle_t notify_task = NULL; +static adc_callback_t registered_callback = NULL; + +static adc_calibration_t adc_calibration_data[MAX_ADC_CHANNELS]; + +static float adc_filtered_ema[MAX_ADC_CHANNELS] = {0.0f}; + +extern ADC_HandleTypeDef hadc1; + +void ADC_Driver_Init(const uint8_t* channels, size_t count) { + channel_count = count > MAX_ADC_CHANNELS ? MAX_ADC_CHANNELS : count; + for (size_t i = 0; i < channel_count; i++) { + adc_channels[i] = channels[i]; + ULOG_INFO("ADC channel %d initialized", channels[i]); + } + ULOG_INFO("ADC driver initialized with %d channels", channel_count); +} + +void ADC_Driver_Start(void) { + if (channel_count == 0) { + ULOG_ERROR("ADC start failed: no channels configured"); + return; + } + if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_raw_values, channel_count) != HAL_OK){ + ULOG_ERROR("Failed to start ADC DMA"); + }else { + ULOG_INFO("ADC DMA started with %d channels", channel_count); + } +} + +void ADC_Driver_Calibrate(void) { + for (size_t i = 0; i < channel_count; i++) { + adc_filtered_ema[i] = 0.0f; + adc_filtered_values[i] = 0; + adc_raw_values[i] = 0; + } + ULOG_INFO("ADC calibration: filters and raw values reset"); +} + +void ADC_Driver_SetCalibration(uint8_t channel, adc_calibration_t calib) { + for (size_t i = 0; i < channel_count; ++i) { + if (adc_channels[i] == channel) { + adc_calibration_data[i] = calib; + ULOG_INFO("Calibration set for channel %u", channel); + return; + } + } + ULOG_WARNING("Attempted to calibrate unknown channel %u", channel); +} + +float ADC_Driver_GetCalibratedValue(uint8_t channel) { + for (size_t i = 0; i < channel_count; ++i) { + if (adc_channels[i] == channel) { + adc_calibration_t calib = adc_calibration_data[i]; + uint16_t raw = adc_filtered_values[i]; + + if (calib.raw_high == calib.raw_low) return 0.0f; + + float phys = calib.phys_low + ((float)(raw - calib.raw_low)) * + (calib.phys_high - calib.phys_low) / + (float)(calib.raw_high - calib.raw_low); + return phys; + } + } + + ULOG_WARNING("Calibrated value requested for unknown channel %u", channel); + return 0.0f; +} + +void ADC_Driver_NotifyTaskOnConversion(TaskHandle_t taskHandle) { + notify_task = taskHandle; +} + +void ADC_Driver_RegisterCallback(adc_callback_t cb) { + registered_callback = cb; + ULOG_DEBUG("ADC callback registered"); +} + +uint16_t ADC_Driver_GetLastValue(uint8_t channel) { + for (size_t i = 0; i < channel_count; ++i) { + if (adc_channels[i] == channel) + return adc_filtered_values[i]; + } + ULOG_WARNING("ADC: Requested value for unregistered channel %u", channel); + return 0; +} + +void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { + if (hadc->Instance == hadc1.Instance) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + for (size_t i = 0; i < channel_count; i++) { + float raw = (float)adc_raw_values[i]; + adc_filtered_ema[i] = EMA_ALPHA * raw + (1.0f - EMA_ALPHA) * adc_filtered_ema[i]; + adc_filtered_values[i] = (uint16_t)adc_filtered_ema[i]; + } + + + if (notify_task) { + vTaskNotifyGiveFromISR(notify_task, &xHigherPriorityTaskWoken); + } + + if (registered_callback) { + for (size_t i = 0; i < channel_count; i++) { + registered_callback(adc_channels[i], adc_filtered_values[i]); + } + ULOG_DEBUG("ADC callback invoked"); + } + + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } +} diff --git a/KPI_Rover/ADC/adc_driver.h b/KPI_Rover/ADC/adc_driver.h index e69de29..588bce2 100644 --- a/KPI_Rover/ADC/adc_driver.h +++ b/KPI_Rover/ADC/adc_driver.h @@ -0,0 +1,27 @@ +#ifndef ADC_DRIVER_H +#define ADC_DRIVER_H + +#include +#include +#include "cmsis_os.h" + +typedef struct { + uint16_t raw_low; + uint16_t raw_high; + float phys_low; + float phys_high; +} adc_calibration_t; + + +typedef void (*adc_callback_t)(uint8_t channel, uint16_t value); + +void ADC_Driver_Init(const uint8_t* channels, size_t count); +void ADC_Driver_Start(void); +void ADC_Driver_Calibrate(void); +void ADC_Driver_SetCalibration(uint8_t channel, adc_calibration_t calib); +float ADC_Driver_GetCalibratedValue(uint8_t channel); +void ADC_Driver_NotifyTaskOnConversion(TaskHandle_t taskHandle); +uint16_t ADC_Driver_GetLastValue(uint8_t channel); +void ADC_Driver_RegisterCallback(adc_callback_t cb); + +#endif From 6643aabdca2690cfaff7464e50c9348606e5fd72 Mon Sep 17 00:00:00 2001 From: velichko Date: Sun, 24 Aug 2025 02:09:02 +0300 Subject: [PATCH 03/12] Issue #24: add manager and two-point calibration --- KPI_Rover/ADC/adc_driver.c | 64 ++++++++++++++++++++++++++++++------- KPI_Rover/ADC/adc_driver.h | 3 +- KPI_Rover/ADC/adc_manager.c | 55 +++++++++++++++++++++++++++++++ KPI_Rover/ADC/adc_manager.h | 17 ++++++++++ 4 files changed, 125 insertions(+), 14 deletions(-) create mode 100644 KPI_Rover/ADC/adc_manager.c create mode 100644 KPI_Rover/ADC/adc_manager.h diff --git a/KPI_Rover/ADC/adc_driver.c b/KPI_Rover/ADC/adc_driver.c index 4d69b47..8fb95ac 100644 --- a/KPI_Rover/ADC/adc_driver.c +++ b/KPI_Rover/ADC/adc_driver.c @@ -3,7 +3,7 @@ #include "ulog.h" #define MAX_ADC_CHANNELS 16 -#define EMA_ALPHA 0.2f +#define EMA_ALPHA 0.05f static uint8_t adc_channels[MAX_ADC_CHANNELS]; static uint16_t adc_raw_values[MAX_ADC_CHANNELS]; @@ -40,13 +40,45 @@ void ADC_Driver_Start(void) { } } -void ADC_Driver_Calibrate(void) { - for (size_t i = 0; i < channel_count; i++) { - adc_filtered_ema[i] = 0.0f; - adc_filtered_values[i] = 0; - adc_raw_values[i] = 0; +void ADC_PerformTwoPointCalibration(uint8_t channel) { + adc_calibration_t calib = {0}; + + osDelay(500); + + ULOG_INFO("Calibration: 0 В on channel %u", channel); + uint32_t sum = 0; + uint32_t count = 0; + uint32_t start = HAL_GetTick(); + while (HAL_GetTick() - start < 5000) { + sum += ADC_Driver_GetLastValue(channel); + count++; + osDelay(100); } - ULOG_INFO("ADC calibration: filters and raw values reset"); + calib.raw_low = (count > 0) ? (uint16_t)(sum / count) : 0; + calib.phys_low = 0.0f; + ULOG_INFO("raw_low = %u", calib.raw_low); + + osDelay(100); + + ULOG_INFO("Wait 10s, 3.3 В on channel %u", channel); + osDelay(10000); + + sum = 0; + count = 0; + start = HAL_GetTick(); + while (HAL_GetTick() - start < 5000) { + sum += ADC_Driver_GetLastValue(channel); + count++; + osDelay(100); + } + calib.raw_high = (count > 0) ? (uint16_t)(sum / count) : 0; + calib.phys_high = 3.3f; + ULOG_INFO("raw_high = %u", calib.raw_high); + + ADC_Driver_SetCalibration(channel, calib); + + ULOG_INFO("Calibration complete: raw_low=%u, raw_high=%u", + calib.raw_low, calib.raw_high); } void ADC_Driver_SetCalibration(uint8_t channel, adc_calibration_t calib) { @@ -102,12 +134,18 @@ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; for (size_t i = 0; i < channel_count; i++) { - float raw = (float)adc_raw_values[i]; - adc_filtered_ema[i] = EMA_ALPHA * raw + (1.0f - EMA_ALPHA) * adc_filtered_ema[i]; - adc_filtered_values[i] = (uint16_t)adc_filtered_ema[i]; + uint16_t raw = adc_raw_values[i]; + + if (raw == 4095) { + adc_filtered_values[i] = raw; + adc_filtered_ema[i] = (float)raw; + } else { + adc_filtered_ema[i] = EMA_ALPHA * (float)raw + + (1.0f - EMA_ALPHA) * adc_filtered_ema[i]; + adc_filtered_values[i] = (uint16_t)adc_filtered_ema[i]; + } } - if (notify_task) { vTaskNotifyGiveFromISR(notify_task, &xHigherPriorityTaskWoken); } @@ -116,9 +154,11 @@ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { for (size_t i = 0; i < channel_count; i++) { registered_callback(adc_channels[i], adc_filtered_values[i]); } - ULOG_DEBUG("ADC callback invoked"); } + HAL_ADC_Stop_DMA(&hadc1); + HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_raw_values, channel_count); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } diff --git a/KPI_Rover/ADC/adc_driver.h b/KPI_Rover/ADC/adc_driver.h index 588bce2..17633ea 100644 --- a/KPI_Rover/ADC/adc_driver.h +++ b/KPI_Rover/ADC/adc_driver.h @@ -12,12 +12,11 @@ typedef struct { float phys_high; } adc_calibration_t; - typedef void (*adc_callback_t)(uint8_t channel, uint16_t value); void ADC_Driver_Init(const uint8_t* channels, size_t count); void ADC_Driver_Start(void); -void ADC_Driver_Calibrate(void); +void ADC_PerformTwoPointCalibration(uint8_t channel); void ADC_Driver_SetCalibration(uint8_t channel, adc_calibration_t calib); float ADC_Driver_GetCalibratedValue(uint8_t channel); void ADC_Driver_NotifyTaskOnConversion(TaskHandle_t taskHandle); diff --git a/KPI_Rover/ADC/adc_manager.c b/KPI_Rover/ADC/adc_manager.c new file mode 100644 index 0000000..592dfc6 --- /dev/null +++ b/KPI_Rover/ADC/adc_manager.c @@ -0,0 +1,55 @@ +#include "adc_manager.h" +#include "ulog.h" +#include "cmsis_os.h" + +static const adc_channel_config_t adc_config[] = { + { .channel = 2, .phys_high = 3.3f, .calibration_required = 1 } +}; + +static uint32_t last_log_tick = 0; + +#define ADC_CONFIG_COUNT (sizeof(adc_config) / sizeof(adc_config[0])) + +static void adc_manager_callback(uint8_t channel, uint16_t value) { + uint32_t now = HAL_GetTick(); + + if (now - last_log_tick >= 500) { + int filtered = ADC_Driver_GetLastValue(channel); + float calibrated = ADC_Driver_GetCalibratedValue(channel); + int calibrated_mv = (int)(calibrated * 1000); + ULOG_INFO("CH%u: RAW=%u, CALIB=%d mV", channel, filtered, calibrated_mv); + last_log_tick = now; + } +} + +void ADC_Manager_Init(void) { + uint8_t channels[ADC_CONFIG_COUNT]; + + for (size_t i = 0; i < ADC_CONFIG_COUNT; i++) { + channels[i] = adc_config[i].channel; + } + + ADC_Driver_Init(channels, ADC_CONFIG_COUNT); + + ADC_Driver_Start(); + + osDelay(500); + + for (size_t i = 0; i < ADC_CONFIG_COUNT; i++) { + if (adc_config[i].calibration_required) { + ADC_PerformTwoPointCalibration(adc_config[i].channel); + } + } + ADC_Driver_RegisterCallback(adc_manager_callback); + + +} + +void ADC_Manager_Task(void *argument) { + ADC_Manager_Init(); + + for (;;) { + // DB_SaveADCValue(ch, calibrated); + + } +} diff --git a/KPI_Rover/ADC/adc_manager.h b/KPI_Rover/ADC/adc_manager.h new file mode 100644 index 0000000..da301ec --- /dev/null +++ b/KPI_Rover/ADC/adc_manager.h @@ -0,0 +1,17 @@ +#ifndef ADC_MANAGER_H +#define ADC_MANAGER_H + +#include +#include +#include "adc_driver.h" + +typedef struct { + uint8_t channel; + float phys_high; + uint8_t calibration_required; +} adc_channel_config_t; + +void ADC_Manager_Init(void); +void ADC_Manager_Task(void *argument); + +#endif // ADC_MANAGER_H From ada0c4abbcf815ffd1898fca09d468d3161c77c0 Mon Sep 17 00:00:00 2001 From: velichko Date: Sun, 24 Aug 2025 02:18:28 +0300 Subject: [PATCH 04/12] Issue #24: add thread --- Core/Src/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/Src/main.c b/Core/Src/main.c index 824509b..633c192 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -85,6 +85,7 @@ static void MX_TIM3_Init(void); static void MX_TIM4_Init(void); static void MX_USART3_UART_Init(void); void StartDefaultTask(void *argument); +void ADC_Manager_Task(void *argument); /* USER CODE BEGIN PFP */ From d80a5bac2741166cda04cd2a7fe7e45a72e59891 Mon Sep 17 00:00:00 2001 From: velichko Date: Sun, 24 Aug 2025 02:45:35 +0300 Subject: [PATCH 05/12] Issue #24: ioc file settings --- Core/Src/main.c | 19 +++++++++++++++++++ ecu_sw.ioc | 1 + 2 files changed, 20 insertions(+) diff --git a/Core/Src/main.c b/Core/Src/main.c index 633c192..d8ccd51 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -74,6 +74,7 @@ const osThreadAttr_t defaultTask_attributes = { /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); +static void MX_DMA_Init(void); static void MX_I2C1_Init(void); static void MX_I2S3_Init(void); static void MX_SPI1_Init(void); @@ -87,6 +88,7 @@ static void MX_USART3_UART_Init(void); void StartDefaultTask(void *argument); void ADC_Manager_Task(void *argument); + /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ @@ -125,6 +127,7 @@ int main(void) /* Initialize all configured peripherals */ MX_GPIO_Init(); + MX_DMA_Init(); MX_I2C1_Init(); MX_I2S3_Init(); MX_SPI1_Init(); @@ -719,6 +722,22 @@ static void MX_USART3_UART_Init(void) } +/** + * Enable DMA controller clock + */ +static void MX_DMA_Init(void) +{ + + /* DMA controller clock enable */ + __HAL_RCC_DMA2_CLK_ENABLE(); + + /* DMA interrupt init */ + /* DMA2_Stream0_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn); + +} + /** * @brief GPIO Initialization Function * @param None diff --git a/ecu_sw.ioc b/ecu_sw.ioc index 99771d1..c3a47b8 100644 --- a/ecu_sw.ioc +++ b/ecu_sw.ioc @@ -120,6 +120,7 @@ Mcu.UserName=STM32F407VGTx MxCube.Version=6.16.0 MxDb.Version=DB.6.0.160 NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:false\:false +NVIC.DMA2_Stream0_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:false\:false NVIC.ForceEnableDMAVector=true NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:false\:false From d28213889f0aa32b5ca6a0c48a877fcdd3e95be0 Mon Sep 17 00:00:00 2001 From: velichko Date: Mon, 25 Aug 2025 13:06:15 +0300 Subject: [PATCH 06/12] Issue #24: add calibration changes --- KPI_Rover/ADC/adc_driver.c | 88 +++++++++++++++++++++++-------------- KPI_Rover/ADC/adc_driver.h | 4 +- KPI_Rover/ADC/adc_manager.c | 27 ++++++++---- 3 files changed, 76 insertions(+), 43 deletions(-) diff --git a/KPI_Rover/ADC/adc_driver.c b/KPI_Rover/ADC/adc_driver.c index 8fb95ac..867336b 100644 --- a/KPI_Rover/ADC/adc_driver.c +++ b/KPI_Rover/ADC/adc_driver.c @@ -40,47 +40,69 @@ void ADC_Driver_Start(void) { } } -void ADC_PerformTwoPointCalibration(uint8_t channel) { - adc_calibration_t calib = {0}; - - osDelay(500); - - ULOG_INFO("Calibration: 0 В on channel %u", channel); - uint32_t sum = 0; - uint32_t count = 0; - uint32_t start = HAL_GetTick(); - while (HAL_GetTick() - start < 5000) { - sum += ADC_Driver_GetLastValue(channel); - count++; - osDelay(100); - } - calib.raw_low = (count > 0) ? (uint16_t)(sum / count) : 0; - calib.phys_low = 0.0f; - ULOG_INFO("raw_low = %u", calib.raw_low); +uint16_t ADC_PerformTwoPointCalibration(uint8_t channel) { + uint16_t samples[36]; + uint32_t start = HAL_GetTick(); + size_t count = 0; + + while ((HAL_GetTick() - start < 100) && (count < 36)) { + samples[count++] = ADC_Driver_GetLastValue(channel); + osDelay(3); + } - osDelay(100); + uint16_t min1=0xFFFF, min2=0xFFFF, max1=0, max2=0; + for (size_t i=0; i max1) { + max2=max1; + max1=v; + } + else if (v > max2) { + max2=v; + } + } - ULOG_INFO("Wait 10s, 3.3 В on channel %u", channel); - osDelay(10000); + uint16_t avg_min = (min1+min2)/2; + uint16_t avg_max = (max1+max2)/2; - sum = 0; - count = 0; - start = HAL_GetTick(); - while (HAL_GetTick() - start < 5000) { - sum += ADC_Driver_GetLastValue(channel); - count++; - osDelay(100); - } - calib.raw_high = (count > 0) ? (uint16_t)(sum / count) : 0; - calib.phys_high = 3.3f; - ULOG_INFO("raw_high = %u", calib.raw_high); + uint32_t sum = 0; + for (size_t i=0; i Date: Mon, 25 Aug 2025 14:09:40 +0300 Subject: [PATCH 07/12] Issue #24: add files KPIRover.c and KPIRover.h --- Core/Src/main.c | 1 - KPI_Rover/ADC/adc_manager.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Core/Src/main.c b/Core/Src/main.c index d8ccd51..549b3bd 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -86,7 +86,6 @@ static void MX_TIM3_Init(void); static void MX_TIM4_Init(void); static void MX_USART3_UART_Init(void); void StartDefaultTask(void *argument); -void ADC_Manager_Task(void *argument); /* USER CODE BEGIN PFP */ diff --git a/KPI_Rover/ADC/adc_manager.c b/KPI_Rover/ADC/adc_manager.c index 41274de..7d212ba 100644 --- a/KPI_Rover/ADC/adc_manager.c +++ b/KPI_Rover/ADC/adc_manager.c @@ -1,6 +1,7 @@ #include "adc_manager.h" #include "ulog.h" #include "cmsis_os.h" +#include "stm32f4xx_hal.h" static const adc_channel_config_t adc_config[] = { { .channel = 2, .phys_high = 3.3f, .calibration_required = 1 } @@ -58,7 +59,5 @@ void ADC_Manager_Task(void *argument) { default: break; } - - osDelay(200); } } From d6c2332454bab0cadf6b4612bb6a2214f6cc9187 Mon Sep 17 00:00:00 2001 From: velichko Date: Tue, 16 Sep 2025 22:49:33 +0300 Subject: [PATCH 08/12] Issue #24: add periodic TimerTask with non-blocking state machines --- KPI_Rover/ADC/adc_driver.c | 306 ++++++++++++++++++++++++++---------- KPI_Rover/ADC/adc_driver.h | 10 +- KPI_Rover/ADC/adc_manager.c | 59 +++++-- 3 files changed, 278 insertions(+), 97 deletions(-) diff --git a/KPI_Rover/ADC/adc_driver.c b/KPI_Rover/ADC/adc_driver.c index 867336b..95d2428 100644 --- a/KPI_Rover/ADC/adc_driver.c +++ b/KPI_Rover/ADC/adc_driver.c @@ -4,6 +4,17 @@ #define MAX_ADC_CHANNELS 16 #define EMA_ALPHA 0.05f +#define CALIB_SAMPLES_TARGET 36 + +typedef enum { + ADC_STATE_IDLE = 0, + ADC_STATE_REQUEST_CALIB_LOW, + ADC_STATE_CALIB_LOW_PENDING, + ADC_STATE_REQUEST_CALIB_HIGH, + ADC_STATE_CALIB_HIGH_PENDING, + ADC_STATE_MEASURE_VOLTAGE, + ADC_STATE_ERROR +} adc_driver_state_t; static uint8_t adc_channels[MAX_ADC_CHANNELS]; static uint16_t adc_raw_values[MAX_ADC_CHANNELS]; @@ -19,6 +30,21 @@ static float adc_filtered_ema[MAX_ADC_CHANNELS] = {0.0f}; extern ADC_HandleTypeDef hadc1; +static volatile adc_driver_state_t adc_driver_state = ADC_STATE_IDLE; +static volatile uint32_t adc_state_change_tick = 0; +static volatile uint8_t adc_request_calib_low_channel = 0xFF; +static volatile uint8_t adc_request_calib_high_channel = 0xFF; + +static uint16_t calib_samples[MAX_ADC_CHANNELS][CALIB_SAMPLES_TARGET]; +static uint8_t calib_sample_counts[MAX_ADC_CHANNELS] = {0}; + +static int find_channel_index(uint8_t channel) { + for (size_t i = 0; i < channel_count; ++i) { + if (adc_channels[i] == channel) return (int)i; + } + return -1; +} + void ADC_Driver_Init(const uint8_t* channels, size_t count) { channel_count = count > MAX_ADC_CHANNELS ? MAX_ADC_CHANNELS : count; for (size_t i = 0; i < channel_count; i++) { @@ -40,99 +66,55 @@ void ADC_Driver_Start(void) { } } -uint16_t ADC_PerformTwoPointCalibration(uint8_t channel) { - uint16_t samples[36]; - uint32_t start = HAL_GetTick(); - size_t count = 0; - - while ((HAL_GetTick() - start < 100) && (count < 36)) { - samples[count++] = ADC_Driver_GetLastValue(channel); - osDelay(3); - } - - uint16_t min1=0xFFFF, min2=0xFFFF, max1=0, max2=0; - for (size_t i=0; i max1) { - max2=max1; - max1=v; - } - else if (v > max2) { - max2=v; - } - } - - uint16_t avg_min = (min1+min2)/2; - uint16_t avg_max = (max1+max2)/2; - - uint32_t sum = 0; - for (size_t i=0; i= 0) calib_sample_counts[idx] = 0; + adc_driver_state = ADC_STATE_REQUEST_CALIB_LOW; + adc_state_change_tick = HAL_GetTick(); + ULOG_INFO("ADC: queued START CALIB LOW for channel %u", channel); } void ADC_Driver_StartCalibrationHigh(uint8_t channel) { - uint16_t raw = ADC_PerformTwoPointCalibration(channel); - adc_calibration_t calib = adc_calibration_data[channel]; - calib.raw_high = raw; - calib.phys_high = 3.3f; - - ADC_Driver_SetCalibration(channel, calib); - ULOG_INFO("Calibration HIGH complete: raw=%u", raw); + adc_request_calib_high_channel = channel; + int idx = find_channel_index(channel); + if (idx >= 0) calib_sample_counts[idx] = 0; + adc_driver_state = ADC_STATE_REQUEST_CALIB_HIGH; + adc_state_change_tick = HAL_GetTick(); + ULOG_INFO("ADC: queued START CALIB HIGH for channel %u", channel); } void ADC_Driver_SetCalibration(uint8_t channel, adc_calibration_t calib) { - for (size_t i = 0; i < channel_count; ++i) { - if (adc_channels[i] == channel) { - adc_calibration_data[i] = calib; - ULOG_INFO("Calibration set for channel %u", channel); - return; - } - } - ULOG_WARNING("Attempted to calibrate unknown channel %u", channel); + int idx = find_channel_index(channel); + if (idx >= 0) { + adc_calibration_data[idx] = calib; + ULOG_INFO("Calibration set for channel %u", channel); + } else { + ULOG_WARNING("Attempted to calibrate unknown channel %u", channel); + } } float ADC_Driver_GetCalibratedValue(uint8_t channel) { - for (size_t i = 0; i < channel_count; ++i) { - if (adc_channels[i] == channel) { - adc_calibration_t calib = adc_calibration_data[i]; - uint16_t raw = adc_filtered_values[i]; + int idx = find_channel_index(channel); + if (idx < 0) { + ULOG_WARNING("Calibrated value requested for unknown channel %u", channel); + return 0.0f; + } - if (calib.raw_high == calib.raw_low) return 0.0f; + adc_calibration_t calib = adc_calibration_data[idx]; + uint16_t raw = adc_filtered_values[idx]; - float phys = calib.phys_low + ((float)(raw - calib.raw_low)) * - (calib.phys_high - calib.phys_low) / - (float)(calib.raw_high - calib.raw_low); - return phys; - } - } + if (calib.raw_high == calib.raw_low) return 0.0f; - ULOG_WARNING("Calibrated value requested for unknown channel %u", channel); - return 0.0f; + float phys = calib.phys_low + ((float)(raw - calib.raw_low)) * + (calib.phys_high - calib.phys_low) / + (float)(calib.raw_high - calib.raw_low); + return phys; } + void ADC_Driver_NotifyTaskOnConversion(TaskHandle_t taskHandle) { notify_task = taskHandle; } @@ -143,14 +125,15 @@ void ADC_Driver_RegisterCallback(adc_callback_t cb) { } uint16_t ADC_Driver_GetLastValue(uint8_t channel) { - for (size_t i = 0; i < channel_count; ++i) { - if (adc_channels[i] == channel) - return adc_filtered_values[i]; + int idx = find_channel_index(channel); + if (idx >= 0) { + return adc_filtered_values[idx]; } ULOG_WARNING("ADC: Requested value for unregistered channel %u", channel); return 0; } + void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { if (hadc->Instance == hadc1.Instance) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; @@ -173,7 +156,7 @@ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { } if (registered_callback) { - for (size_t i = 0; i < channel_count; i++) { + for (size_t i = 0; i < channel_count; ++i) { registered_callback(adc_channels[i], adc_filtered_values[i]); } } @@ -184,3 +167,166 @@ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } + + +void ADC_Driver_TimerTask(void) { + uint32_t now = HAL_GetTick(); + + switch (adc_driver_state) { + case ADC_STATE_IDLE:{ + if (now >= adc_meas.next_voltage_tick) { + adc_driver_state = ADC_STATE_MEASURE_VOLTAGE; + adc_meas.next_voltage_tick = now + 100; + } + + }break; + + case ADC_STATE_MEASURE_VOLTAGE: { + int idx = find_channel_index(2); + if (idx >= 0) { + adc_meas.voltage_raw = ADC_Driver_GetLastValue(adc_channels[idx]); + //ULOG_INFO("Voltage raw=%u", adc_meas.voltage_raw); + } + adc_driver_state = ADC_STATE_IDLE; + } break; + + case ADC_STATE_REQUEST_CALIB_LOW: { + uint8_t ch = adc_request_calib_low_channel; + int idx = find_channel_index(ch); + if (idx >= 0) { + calib_sample_counts[idx] = 0; + adc_driver_state = ADC_STATE_CALIB_LOW_PENDING; + adc_state_change_tick = now; + ULOG_INFO("ADC: CALIB LOW pending (channel %u)", ch); + } else { + adc_driver_state = ADC_STATE_IDLE; + } + } break; + + case ADC_STATE_CALIB_LOW_PENDING: { + uint8_t ch = adc_request_calib_low_channel; + int idx = find_channel_index(ch); + if (idx >= 0) { + if ((now - adc_state_change_tick) > 5000) { + ULOG_ERROR("ADC: CALIB LOW timeout (channel %u)", ch); + adc_driver_state = ADC_STATE_IDLE; + adc_request_calib_low_channel = 0xFF; + break; + } + + if (calib_sample_counts[idx] < CALIB_SAMPLES_TARGET) { + calib_samples[idx][calib_sample_counts[idx]++] = ADC_Driver_GetLastValue(ch); + } + + if (calib_sample_counts[idx] >= CALIB_SAMPLES_TARGET) { + uint16_t min1=0xFFFF, min2=0xFFFF, max1=0, max2=0; + uint32_t sum=0; + for (size_t i=0; i max1) { max2=max1; max1=v; } + else if (v > max2) max2=v; + } + uint16_t avg_min = (min1+min2)/2; + uint16_t avg_max = (max1+max2)/2; + uint16_t avg_all = sum / CALIB_SAMPLES_TARGET; + uint16_t final = (avg_min+avg_max+avg_all)/3; + + adc_calibration_t calib = adc_calibration_data[idx]; + calib.raw_low = final; + calib.phys_low = 0.0f; + ADC_Driver_SetCalibration(ch, calib); + + ULOG_INFO("ADC: CALIB LOW done ch=%u raw=%u", ch, final); + + adc_driver_state = ADC_STATE_IDLE; + adc_request_calib_low_channel = 0xFF; + } + } else { + adc_driver_state = ADC_STATE_IDLE; + } + } break; + + case ADC_STATE_REQUEST_CALIB_HIGH: { + uint8_t ch = adc_request_calib_high_channel; + int idx = find_channel_index(ch); + if (idx >= 0) { + calib_sample_counts[idx] = 0; + adc_driver_state = ADC_STATE_CALIB_HIGH_PENDING; + adc_state_change_tick = now; + ULOG_INFO("ADC: CALIB HIGH pending (channel %u)", ch); + } else { + adc_driver_state = ADC_STATE_IDLE; + } + } break; + + case ADC_STATE_CALIB_HIGH_PENDING: { + uint8_t ch = adc_request_calib_high_channel; + int idx = find_channel_index(ch); + if (idx >= 0) { + if ((now - adc_state_change_tick) > 5000) { + ULOG_ERROR("ADC: CALIB HIGH timeout (channel %u)", ch); + adc_driver_state = ADC_STATE_IDLE; + adc_request_calib_high_channel = 0xFF; + break; + } + + if (calib_sample_counts[idx] < CALIB_SAMPLES_TARGET) { + calib_samples[idx][calib_sample_counts[idx]++] = ADC_Driver_GetLastValue(ch); + } + + if (calib_sample_counts[idx] >= CALIB_SAMPLES_TARGET) { + uint16_t min1=0xFFFF, min2=0xFFFF, max1=0, max2=0; + uint32_t sum=0; + for (size_t i=0; i max1) { max2=max1; max1=v; } + else if (v > max2) max2=v; + } + uint16_t avg_min = (min1+min2)/2; + uint16_t avg_max = (max1+max2)/2; + uint16_t avg_all = sum / CALIB_SAMPLES_TARGET; + uint16_t final = (avg_min+avg_max+avg_all)/3; + + adc_calibration_t calib = adc_calibration_data[idx]; + calib.raw_high = final; + calib.phys_high = 3.3f; + ADC_Driver_SetCalibration(ch, calib); + + ULOG_INFO("ADC: CALIB HIGH done ch=%u raw=%u", ch, final); + + adc_driver_state = ADC_STATE_IDLE; + adc_request_calib_high_channel = 0xFF; + } + } else { + adc_driver_state = ADC_STATE_IDLE; + } + } break; + + case ADC_STATE_ERROR: + ULOG_ERROR("ADC driver error state"); + break; + + default: + adc_driver_state = ADC_STATE_IDLE; + break; + } + + static uint32_t last_log = 0; + if ((now - last_log) >= 500) { + for (size_t i = 0; i < channel_count; ++i) { + uint8_t ch = adc_channels[i]; + int filtered = adc_filtered_values[i]; + float calibrated = ADC_Driver_GetCalibratedValue(ch); + int calibrated_mv = (int)(calibrated * 1000.0f); + ULOG_INFO("ADC TimerTask: CH%u filtered=%u cal=%d mV", ch, filtered, calibrated_mv); + } + last_log = now; + } +} + diff --git a/KPI_Rover/ADC/adc_driver.h b/KPI_Rover/ADC/adc_driver.h index d5a9547..9e95267 100644 --- a/KPI_Rover/ADC/adc_driver.h +++ b/KPI_Rover/ADC/adc_driver.h @@ -14,9 +14,16 @@ typedef struct { typedef void (*adc_callback_t)(uint8_t channel, uint16_t value); +typedef struct { + uint32_t next_voltage_tick; + uint16_t voltage_raw; +} adc_measurements_t; + +static adc_measurements_t adc_meas = {0}; + + void ADC_Driver_Init(const uint8_t* channels, size_t count); void ADC_Driver_Start(void); -uint16_t ADC_PerformTwoPointCalibration(uint8_t channel); void ADC_Driver_StartCalibrationLow(uint8_t channel); void ADC_Driver_StartCalibrationHigh(uint8_t channel); void ADC_Driver_SetCalibration(uint8_t channel, adc_calibration_t calib); @@ -24,5 +31,6 @@ float ADC_Driver_GetCalibratedValue(uint8_t channel); void ADC_Driver_NotifyTaskOnConversion(TaskHandle_t taskHandle); uint16_t ADC_Driver_GetLastValue(uint8_t channel); void ADC_Driver_RegisterCallback(adc_callback_t cb); +void ADC_Driver_TimerTask(void); #endif diff --git a/KPI_Rover/ADC/adc_manager.c b/KPI_Rover/ADC/adc_manager.c index 7d212ba..a1737cf 100644 --- a/KPI_Rover/ADC/adc_manager.c +++ b/KPI_Rover/ADC/adc_manager.c @@ -10,15 +10,26 @@ static const adc_channel_config_t adc_config[] = { static uint32_t last_log_tick = 0; #define ADC_CONFIG_COUNT (sizeof(adc_config) / sizeof(adc_config[0])) +#define ADC_TASK_FLAG_TIMER (1U << 0) + +#define ADC_TIMER_PERIOD_MS 10 + +static osTimerId_t adc_periodic_timer = NULL; +static osThreadId_t adcTaskHandle = NULL; + +static void adc_periodic_timer_cb(void *arg) { + (void)arg; + osThreadFlagsSet(adcTaskHandle, ADC_TASK_FLAG_TIMER); +} static void adc_manager_callback(uint8_t channel, uint16_t value) { uint32_t now = HAL_GetTick(); if (now - last_log_tick >= 500) { - int filtered = ADC_Driver_GetLastValue(channel); + int filtered = ADC_Driver_GetLastValue(channel); float calibrated = ADC_Driver_GetCalibratedValue(channel); int calibrated_mv = (int)(calibrated * 1000); - ULOG_INFO("CH%u: RAW=%u, CALIB=%d mV", channel, filtered, calibrated_mv); + //ULOG_INFO("CH%u: RAW=%u, CALIB=%d mV", channel, filtered, calibrated_mv); last_log_tick = now; } } @@ -36,6 +47,18 @@ void ADC_Manager_Init(void) { ADC_Driver_RegisterCallback(adc_manager_callback); + adcTaskHandle = osThreadGetId(); + + const osTimerAttr_t timer_attr = { + .name = "ADC_Periodic" + }; + adc_periodic_timer = osTimerNew(adc_periodic_timer_cb, osTimerPeriodic, NULL, &timer_attr); + if (osTimerStart(adc_periodic_timer, ADC_TIMER_PERIOD_MS) != osOK) { + ULOG_ERROR("Failed to start ADC periodic timer"); + } else { + ULOG_INFO("ADC periodic timer started %d ms", ADC_TIMER_PERIOD_MS); + } + } @@ -43,21 +66,25 @@ void ADC_Manager_Task(void *argument) { ADC_Manager_Init(); for (;;) { -// int state = DB_ReadCalibState(); - switch (0) { // (state) -// case 1: // start 0В -// ADC_Driver_StartCalibrationLow(adc_config[0].channel); -// DB_WriteCalibState(2); -// break; + //osThreadFlagsWait(ADC_TASK_FLAG_TIMER, osFlagsWaitAny, 100); + ADC_Driver_TimerTask(); + +// int state = DB_ReadCalibState(); + + switch (0) { // (state) +// case 1: // start 0В +// ADC_Driver_StartCalibrationLow(adc_config[0].channel); +// DB_WriteCalibState(2); +// break; // -// case 3: // start 3.3В -// ADC_Driver_StartCalibrationHigh(adc_config[0].channel); -// DB_WriteCalibState(4); -// break; - - default: - break; - } +// case 3: // start 3.3В +// ADC_Driver_StartCalibrationHigh(adc_config[0].channel); +// DB_WriteCalibState(4); +// break; + + default: + break; + } } } From 95660fe85efbf5f3f0289d14e6293575d4288072 Mon Sep 17 00:00:00 2001 From: velichko Date: Tue, 23 Sep 2025 05:32:51 +0300 Subject: [PATCH 09/12] Issue #24: removed dependency on FreeRTOS in the driver and other improvements --- KPI_Rover/ADC/adc_driver.c | 29 ++------------------- KPI_Rover/ADC/adc_driver.h | 4 +-- KPI_Rover/ADC/adc_manager.c | 52 +++++++++++++++++++------------------ KPI_Rover/ADC/adc_manager.h | 3 +++ 4 files changed, 33 insertions(+), 55 deletions(-) diff --git a/KPI_Rover/ADC/adc_driver.c b/KPI_Rover/ADC/adc_driver.c index 95d2428..741eaaa 100644 --- a/KPI_Rover/ADC/adc_driver.c +++ b/KPI_Rover/ADC/adc_driver.c @@ -3,7 +3,7 @@ #include "ulog.h" #define MAX_ADC_CHANNELS 16 -#define EMA_ALPHA 0.05f +#define EMA_ALPHA 0.1f #define CALIB_SAMPLES_TARGET 36 typedef enum { @@ -21,7 +21,6 @@ static uint16_t adc_raw_values[MAX_ADC_CHANNELS]; static uint16_t adc_filtered_values[MAX_ADC_CHANNELS]; static size_t channel_count = 0; -static TaskHandle_t notify_task = NULL; static adc_callback_t registered_callback = NULL; static adc_calibration_t adc_calibration_data[MAX_ADC_CHANNELS]; @@ -114,11 +113,6 @@ float ADC_Driver_GetCalibratedValue(uint8_t channel) { return phys; } - -void ADC_Driver_NotifyTaskOnConversion(TaskHandle_t taskHandle) { - notify_task = taskHandle; -} - void ADC_Driver_RegisterCallback(adc_callback_t cb) { registered_callback = cb; ULOG_DEBUG("ADC callback registered"); @@ -136,7 +130,6 @@ uint16_t ADC_Driver_GetLastValue(uint8_t channel) { void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { if (hadc->Instance == hadc1.Instance) { - BaseType_t xHigherPriorityTaskWoken = pdFALSE; for (size_t i = 0; i < channel_count; i++) { uint16_t raw = adc_raw_values[i]; @@ -151,10 +144,6 @@ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { } } - if (notify_task) { - vTaskNotifyGiveFromISR(notify_task, &xHigherPriorityTaskWoken); - } - if (registered_callback) { for (size_t i = 0; i < channel_count; ++i) { registered_callback(adc_channels[i], adc_filtered_values[i]); @@ -163,15 +152,13 @@ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { HAL_ADC_Stop_DMA(&hadc1); HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_raw_values, channel_count); - - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } void ADC_Driver_TimerTask(void) { uint32_t now = HAL_GetTick(); - + adc_measurements_t adc_meas = {0}; switch (adc_driver_state) { case ADC_STATE_IDLE:{ if (now >= adc_meas.next_voltage_tick) { @@ -316,17 +303,5 @@ void ADC_Driver_TimerTask(void) { adc_driver_state = ADC_STATE_IDLE; break; } - - static uint32_t last_log = 0; - if ((now - last_log) >= 500) { - for (size_t i = 0; i < channel_count; ++i) { - uint8_t ch = adc_channels[i]; - int filtered = adc_filtered_values[i]; - float calibrated = ADC_Driver_GetCalibratedValue(ch); - int calibrated_mv = (int)(calibrated * 1000.0f); - ULOG_INFO("ADC TimerTask: CH%u filtered=%u cal=%d mV", ch, filtered, calibrated_mv); - } - last_log = now; - } } diff --git a/KPI_Rover/ADC/adc_driver.h b/KPI_Rover/ADC/adc_driver.h index 9e95267..4100059 100644 --- a/KPI_Rover/ADC/adc_driver.h +++ b/KPI_Rover/ADC/adc_driver.h @@ -3,7 +3,6 @@ #include #include -#include "cmsis_os.h" typedef struct { uint16_t raw_low; @@ -19,7 +18,7 @@ typedef struct { uint16_t voltage_raw; } adc_measurements_t; -static adc_measurements_t adc_meas = {0}; +extern adc_measurements_t adc_meas; void ADC_Driver_Init(const uint8_t* channels, size_t count); @@ -28,7 +27,6 @@ void ADC_Driver_StartCalibrationLow(uint8_t channel); void ADC_Driver_StartCalibrationHigh(uint8_t channel); void ADC_Driver_SetCalibration(uint8_t channel, adc_calibration_t calib); float ADC_Driver_GetCalibratedValue(uint8_t channel); -void ADC_Driver_NotifyTaskOnConversion(TaskHandle_t taskHandle); uint16_t ADC_Driver_GetLastValue(uint8_t channel); void ADC_Driver_RegisterCallback(adc_callback_t cb); void ADC_Driver_TimerTask(void); diff --git a/KPI_Rover/ADC/adc_manager.c b/KPI_Rover/ADC/adc_manager.c index a1737cf..d998c9f 100644 --- a/KPI_Rover/ADC/adc_manager.c +++ b/KPI_Rover/ADC/adc_manager.c @@ -12,28 +12,17 @@ static uint32_t last_log_tick = 0; #define ADC_CONFIG_COUNT (sizeof(adc_config) / sizeof(adc_config[0])) #define ADC_TASK_FLAG_TIMER (1U << 0) -#define ADC_TIMER_PERIOD_MS 10 +#define ADC_TIMER_PERIOD_MS 100 static osTimerId_t adc_periodic_timer = NULL; static osThreadId_t adcTaskHandle = NULL; static void adc_periodic_timer_cb(void *arg) { + ULOG_INFO("adc_periodic_timer_cb"); (void)arg; osThreadFlagsSet(adcTaskHandle, ADC_TASK_FLAG_TIMER); } -static void adc_manager_callback(uint8_t channel, uint16_t value) { - uint32_t now = HAL_GetTick(); - - if (now - last_log_tick >= 500) { - int filtered = ADC_Driver_GetLastValue(channel); - float calibrated = ADC_Driver_GetCalibratedValue(channel); - int calibrated_mv = (int)(calibrated * 1000); - //ULOG_INFO("CH%u: RAW=%u, CALIB=%d mV", channel, filtered, calibrated_mv); - last_log_tick = now; - } -} - void ADC_Manager_Init(void) { uint8_t channels[ADC_CONFIG_COUNT]; @@ -45,30 +34,42 @@ void ADC_Manager_Init(void) { ADC_Driver_Start(); - ADC_Driver_RegisterCallback(adc_manager_callback); + ADC_Driver_RegisterCallback(NULL); adcTaskHandle = osThreadGetId(); const osTimerAttr_t timer_attr = { - .name = "ADC_Periodic" + .name = "ADC_Periodic", }; adc_periodic_timer = osTimerNew(adc_periodic_timer_cb, osTimerPeriodic, NULL, &timer_attr); - if (osTimerStart(adc_periodic_timer, ADC_TIMER_PERIOD_MS) != osOK) { - ULOG_ERROR("Failed to start ADC periodic timer"); - } else { - ULOG_INFO("ADC periodic timer started %d ms", ADC_TIMER_PERIOD_MS); - } - - } + void ADC_Manager_Task(void *argument) { - ADC_Manager_Init(); + + osTimerStart(adc_periodic_timer, ADC_TIMER_PERIOD_MS); + + ADC_Manager_Init(); for (;;) { - //osThreadFlagsWait(ADC_TASK_FLAG_TIMER, osFlagsWaitAny, 100); - ADC_Driver_TimerTask(); + uint32_t flags = osThreadFlagsWait(ADC_TASK_FLAG_TIMER, osFlagsWaitAny, 10); + + if (flags & ADC_TASK_FLAG_TIMER) { + ADC_Driver_TimerTask(); + } + + uint32_t now = HAL_GetTick(); + if (now - last_log_tick >= 500) { + last_log_tick = now; + for (size_t i = 0; i < ADC_CONFIG_COUNT; ++i) { + uint8_t ch = adc_config[i].channel; + int filtered = ADC_Driver_GetLastValue(ch); + float cal = ADC_Driver_GetCalibratedValue(ch); + int cal_mv = (int)(cal * 1000.0f); + ULOG_INFO("ADC CH%u: filtered=%u cal=%d mV", ch, filtered, cal_mv); + } + } // int state = DB_ReadCalibState(); @@ -86,5 +87,6 @@ void ADC_Manager_Task(void *argument) { default: break; } + osDelay(100); } } diff --git a/KPI_Rover/ADC/adc_manager.h b/KPI_Rover/ADC/adc_manager.h index da301ec..ddda3f9 100644 --- a/KPI_Rover/ADC/adc_manager.h +++ b/KPI_Rover/ADC/adc_manager.h @@ -4,6 +4,9 @@ #include #include #include "adc_driver.h" +#include "FreeRTOS.h" +#include "queue.h" + typedef struct { uint8_t channel; From 64dc20fb710b32780cb502d3a45ee7452beb2da2 Mon Sep 17 00:00:00 2001 From: velichko Date: Tue, 23 Sep 2025 18:30:24 +0300 Subject: [PATCH 10/12] Issue #24: ADC callback to pass data instead of polling --- KPI_Rover/ADC/adc_driver.c | 2 +- KPI_Rover/ADC/adc_manager.c | 23 +++++++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/KPI_Rover/ADC/adc_driver.c b/KPI_Rover/ADC/adc_driver.c index 741eaaa..2e7046c 100644 --- a/KPI_Rover/ADC/adc_driver.c +++ b/KPI_Rover/ADC/adc_driver.c @@ -3,7 +3,7 @@ #include "ulog.h" #define MAX_ADC_CHANNELS 16 -#define EMA_ALPHA 0.1f +#define EMA_ALPHA 0.05f #define CALIB_SAMPLES_TARGET 36 typedef enum { diff --git a/KPI_Rover/ADC/adc_manager.c b/KPI_Rover/ADC/adc_manager.c index d998c9f..0bdba8a 100644 --- a/KPI_Rover/ADC/adc_manager.c +++ b/KPI_Rover/ADC/adc_manager.c @@ -17,6 +17,19 @@ static uint32_t last_log_tick = 0; static osTimerId_t adc_periodic_timer = NULL; static osThreadId_t adcTaskHandle = NULL; +static uint16_t adc_last_raw[ADC_CONFIG_COUNT]; +static float adc_last_cal[ADC_CONFIG_COUNT]; + +static void adc_data_cb(uint8_t channel, uint16_t value) { + for (size_t i = 0; i < ADC_CONFIG_COUNT; i++) { + if (adc_config[i].channel == channel) { + adc_last_raw[i] = value; + adc_last_cal[i] = ADC_Driver_GetCalibratedValue(channel); + break; + } + } +} + static void adc_periodic_timer_cb(void *arg) { ULOG_INFO("adc_periodic_timer_cb"); (void)arg; @@ -34,7 +47,7 @@ void ADC_Manager_Init(void) { ADC_Driver_Start(); - ADC_Driver_RegisterCallback(NULL); + ADC_Driver_RegisterCallback(adc_data_cb); adcTaskHandle = osThreadGetId(); @@ -63,11 +76,9 @@ void ADC_Manager_Task(void *argument) { if (now - last_log_tick >= 500) { last_log_tick = now; for (size_t i = 0; i < ADC_CONFIG_COUNT; ++i) { - uint8_t ch = adc_config[i].channel; - int filtered = ADC_Driver_GetLastValue(ch); - float cal = ADC_Driver_GetCalibratedValue(ch); - int cal_mv = (int)(cal * 1000.0f); - ULOG_INFO("ADC CH%u: filtered=%u cal=%d mV", ch, filtered, cal_mv); + int cal_mv = (int)(adc_last_cal[i] * 1000.0f); + ULOG_INFO("ADC CH%u: filtered=%u cal=%d mV", + adc_config[i].channel, adc_last_raw[i], cal_mv); } } From 0400116189b6844bc0cb0edf6b749b69b200a09e Mon Sep 17 00:00:00 2001 From: velichko Date: Tue, 7 Oct 2025 14:09:26 +0300 Subject: [PATCH 11/12] Issue #24: added sub states for calibration, changed DMA to polling --- KPI_Rover/ADC/adc_driver.c | 266 +++++++++++++++++++++--------------- KPI_Rover/ADC/adc_driver.h | 20 +++ KPI_Rover/ADC/adc_manager.c | 94 ++++++------- 3 files changed, 219 insertions(+), 161 deletions(-) diff --git a/KPI_Rover/ADC/adc_driver.c b/KPI_Rover/ADC/adc_driver.c index 2e7046c..f26f992 100644 --- a/KPI_Rover/ADC/adc_driver.c +++ b/KPI_Rover/ADC/adc_driver.c @@ -6,16 +6,6 @@ #define EMA_ALPHA 0.05f #define CALIB_SAMPLES_TARGET 36 -typedef enum { - ADC_STATE_IDLE = 0, - ADC_STATE_REQUEST_CALIB_LOW, - ADC_STATE_CALIB_LOW_PENDING, - ADC_STATE_REQUEST_CALIB_HIGH, - ADC_STATE_CALIB_HIGH_PENDING, - ADC_STATE_MEASURE_VOLTAGE, - ADC_STATE_ERROR -} adc_driver_state_t; - static uint8_t adc_channels[MAX_ADC_CHANNELS]; static uint16_t adc_raw_values[MAX_ADC_CHANNELS]; static uint16_t adc_filtered_values[MAX_ADC_CHANNELS]; @@ -34,6 +24,11 @@ static volatile uint32_t adc_state_change_tick = 0; static volatile uint8_t adc_request_calib_low_channel = 0xFF; static volatile uint8_t adc_request_calib_high_channel = 0xFF; +static adc_calib_substate_t calib_substate_low = CAL_SUB_IDLE; +static adc_calib_substate_t calib_substate_high = CAL_SUB_IDLE; + +#define CALIB_STABLE_MS 10 + static uint16_t calib_samples[MAX_ADC_CHANNELS][CALIB_SAMPLES_TARGET]; static uint8_t calib_sample_counts[MAX_ADC_CHANNELS] = {0}; @@ -48,28 +43,37 @@ void ADC_Driver_Init(const uint8_t* channels, size_t count) { channel_count = count > MAX_ADC_CHANNELS ? MAX_ADC_CHANNELS : count; for (size_t i = 0; i < channel_count; i++) { adc_channels[i] = channels[i]; - ULOG_INFO("ADC channel %d initialized", channels[i]); + ADC_ChannelConfTypeDef sConfig = {0}; + sConfig.Channel = adc_channels[i]; + sConfig.Rank = 1; + sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES; + if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { + ULOG_ERROR("ADC: channel %u config failed", adc_channels[i]); + } else { + ULOG_INFO("ADC channel %u initialized", adc_channels[i]); + } } - ULOG_INFO("ADC driver initialized with %d channels", channel_count); } void ADC_Driver_Start(void) { - if (channel_count == 0) { - ULOG_ERROR("ADC start failed: no channels configured"); - return; - } - if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_raw_values, channel_count) != HAL_OK){ - ULOG_ERROR("Failed to start ADC DMA"); - }else { - ULOG_INFO("ADC DMA started with %d channels", channel_count); - } + if (channel_count == 0) { + ULOG_ERROR("ADC start failed: no channels configured"); + return; + } + if (HAL_ADC_Start(&hadc1) != HAL_OK) { + ULOG_ERROR("Failed to start ADC (polling mode)"); + } else { + ULOG_INFO("ADC started in polling mode with %d channels", channel_count); + } } void ADC_Driver_StartCalibrationLow(uint8_t channel) { adc_request_calib_low_channel = channel; int idx = find_channel_index(channel); if (idx >= 0) calib_sample_counts[idx] = 0; - adc_driver_state = ADC_STATE_REQUEST_CALIB_LOW; + + adc_driver_state = ADC_STATE_CALIB_LOW_PENDING; + calib_substate_low = CAL_SUB_WAIT_STABLE; adc_state_change_tick = HAL_GetTick(); ULOG_INFO("ADC: queued START CALIB LOW for channel %u", channel); } @@ -78,7 +82,9 @@ void ADC_Driver_StartCalibrationHigh(uint8_t channel) { adc_request_calib_high_channel = channel; int idx = find_channel_index(channel); if (idx >= 0) calib_sample_counts[idx] = 0; - adc_driver_state = ADC_STATE_REQUEST_CALIB_HIGH; + + adc_driver_state = ADC_STATE_CALIB_HIGH_PENDING; + calib_substate_high = CAL_SUB_WAIT_STABLE; adc_state_change_tick = HAL_GetTick(); ULOG_INFO("ADC: queued START CALIB HIGH for channel %u", channel); } @@ -128,52 +134,68 @@ uint16_t ADC_Driver_GetLastValue(uint8_t channel) { } -void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { - if (hadc->Instance == hadc1.Instance) { - - for (size_t i = 0; i < channel_count; i++) { - uint16_t raw = adc_raw_values[i]; - - if (raw == 4095) { - adc_filtered_values[i] = raw; - adc_filtered_ema[i] = (float)raw; - } else { - adc_filtered_ema[i] = EMA_ALPHA * (float)raw + - (1.0f - EMA_ALPHA) * adc_filtered_ema[i]; - adc_filtered_values[i] = (uint16_t)adc_filtered_ema[i]; - } - } - - if (registered_callback) { - for (size_t i = 0; i < channel_count; ++i) { +void ADC_Driver_ReadChannels(void) { + for (size_t i = 0; i < channel_count; i++) { + HAL_ADC_Start(&hadc1); + if (HAL_ADC_PollForConversion(&hadc1, 5) == HAL_OK) { + uint16_t raw = HAL_ADC_GetValue(&hadc1); + + adc_filtered_ema[i] = EMA_ALPHA * (float)raw + + (1.0f - EMA_ALPHA) * adc_filtered_ema[i]; + adc_filtered_values[i] = (uint16_t)adc_filtered_ema[i]; + + if (adc_driver_state == ADC_STATE_CALIB_LOW_PENDING && + calib_substate_low == CAL_SUB_COLLECT && + adc_channels[i] == adc_request_calib_low_channel) + { + int idx = find_channel_index(adc_channels[i]); + if (idx >= 0 && calib_sample_counts[idx] < CALIB_SAMPLES_TARGET) { + calib_samples[idx][calib_sample_counts[idx]++] = adc_filtered_values[idx]; + } + } + + if (adc_driver_state == ADC_STATE_CALIB_HIGH_PENDING && + calib_substate_high == CAL_SUB_COLLECT && + adc_channels[i] == adc_request_calib_high_channel) + { + int idx = find_channel_index(adc_channels[i]); + if (idx >= 0 && calib_sample_counts[idx] < CALIB_SAMPLES_TARGET) { + calib_samples[idx][calib_sample_counts[idx]++] = adc_filtered_values[idx]; + } + } + + if (registered_callback) { registered_callback(adc_channels[i], adc_filtered_values[i]); } } - - HAL_ADC_Stop_DMA(&hadc1); - HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_raw_values, channel_count); + HAL_ADC_Stop(&hadc1); } } + void ADC_Driver_TimerTask(void) { uint32_t now = HAL_GetTick(); - adc_measurements_t adc_meas = {0}; + static adc_measurements_t adc_meas = {0}; + switch (adc_driver_state) { - case ADC_STATE_IDLE:{ - if (now >= adc_meas.next_voltage_tick) { - adc_driver_state = ADC_STATE_MEASURE_VOLTAGE; - adc_meas.next_voltage_tick = now + 100; - } - }break; + case ADC_STATE_IDLE: { + if (now >= adc_meas.next_voltage_tick) { + adc_driver_state = ADC_STATE_MEASURE_VOLTAGE; + } + } break; case ADC_STATE_MEASURE_VOLTAGE: { int idx = find_channel_index(2); if (idx >= 0) { - adc_meas.voltage_raw = ADC_Driver_GetLastValue(adc_channels[idx]); - //ULOG_INFO("Voltage raw=%u", adc_meas.voltage_raw); - } + adc_meas.voltage_raw = ADC_Driver_GetLastValue(adc_channels[idx]); + adc_meas.voltage_cal = ADC_Driver_GetCalibratedValue(adc_channels[idx]); + ULOG_INFO("Voltage: raw=%u, filtered=%.3f V", + adc_meas.voltage_raw, + adc_meas.voltage_cal); + } + adc_meas.next_voltage_tick = now + 10; adc_driver_state = ADC_STATE_IDLE; } break; @@ -193,33 +215,41 @@ void ADC_Driver_TimerTask(void) { case ADC_STATE_CALIB_LOW_PENDING: { uint8_t ch = adc_request_calib_low_channel; int idx = find_channel_index(ch); - if (idx >= 0) { - if ((now - adc_state_change_tick) > 5000) { - ULOG_ERROR("ADC: CALIB LOW timeout (channel %u)", ch); - adc_driver_state = ADC_STATE_IDLE; - adc_request_calib_low_channel = 0xFF; - break; - } - - if (calib_sample_counts[idx] < CALIB_SAMPLES_TARGET) { - calib_samples[idx][calib_sample_counts[idx]++] = ADC_Driver_GetLastValue(ch); - } - - if (calib_sample_counts[idx] >= CALIB_SAMPLES_TARGET) { - uint16_t min1=0xFFFF, min2=0xFFFF, max1=0, max2=0; - uint32_t sum=0; - for (size_t i=0; i CALIB_STABLE_MS) { + calib_sample_counts[idx] = 0; + calib_substate_low = CAL_SUB_COLLECT; + ULOG_INFO("ADC: CALIB LOW COLLECT start (channel %u)", ch); + } + break; + + case CAL_SUB_COLLECT: + if (calib_sample_counts[idx] >= CALIB_SAMPLES_TARGET) { + calib_substate_low = CAL_SUB_COMPUTE; + } + break; + + case CAL_SUB_COMPUTE: { + uint16_t min1 = 0xFFFF, min2 = 0xFFFF, max1 = 0, max2 = 0; + uint32_t sum = 0; + for (size_t i = 0; i < CALIB_SAMPLES_TARGET; i++) { uint16_t v = calib_samples[idx][i]; sum += v; - if (v < min1) { min2=min1; min1=v; } - else if (v < min2) min2=v; - if (v > max1) { max2=max1; max1=v; } - else if (v > max2) max2=v; + if (v < min1) { min2 = min1; min1 = v; } + else if (v < min2) min2 = v; + if (v > max1) { max2 = max1; max1 = v; } + else if (v > max2) max2 = v; } - uint16_t avg_min = (min1+min2)/2; - uint16_t avg_max = (max1+max2)/2; + uint16_t avg_min = (min1 + min2) / 2; + uint16_t avg_max = (max1 + max2) / 2; uint16_t avg_all = sum / CALIB_SAMPLES_TARGET; - uint16_t final = (avg_min+avg_max+avg_all)/3; + uint16_t final = (avg_min + avg_max + avg_all) / 3; adc_calibration_t calib = adc_calibration_data[idx]; calib.raw_low = final; @@ -227,12 +257,15 @@ void ADC_Driver_TimerTask(void) { ADC_Driver_SetCalibration(ch, calib); ULOG_INFO("ADC: CALIB LOW done ch=%u raw=%u", ch, final); - adc_driver_state = ADC_STATE_IDLE; adc_request_calib_low_channel = 0xFF; - } - } else { - adc_driver_state = ADC_STATE_IDLE; + calib_substate_low = CAL_SUB_IDLE; + } break; + + default: + calib_substate_low = CAL_SUB_IDLE; + adc_driver_state = ADC_STATE_IDLE; + break; } } break; @@ -252,33 +285,41 @@ void ADC_Driver_TimerTask(void) { case ADC_STATE_CALIB_HIGH_PENDING: { uint8_t ch = adc_request_calib_high_channel; int idx = find_channel_index(ch); - if (idx >= 0) { - if ((now - adc_state_change_tick) > 5000) { - ULOG_ERROR("ADC: CALIB HIGH timeout (channel %u)", ch); - adc_driver_state = ADC_STATE_IDLE; - adc_request_calib_high_channel = 0xFF; - break; - } - - if (calib_sample_counts[idx] < CALIB_SAMPLES_TARGET) { - calib_samples[idx][calib_sample_counts[idx]++] = ADC_Driver_GetLastValue(ch); - } - - if (calib_sample_counts[idx] >= CALIB_SAMPLES_TARGET) { - uint16_t min1=0xFFFF, min2=0xFFFF, max1=0, max2=0; - uint32_t sum=0; - for (size_t i=0; i CALIB_STABLE_MS) { + calib_sample_counts[idx] = 0; + calib_substate_high = CAL_SUB_COLLECT; + ULOG_INFO("ADC: CALIB HIGH COLLECT start (channel %u)", ch); + } + break; + + case CAL_SUB_COLLECT: + if (calib_sample_counts[idx] >= CALIB_SAMPLES_TARGET) { + calib_substate_high = CAL_SUB_COMPUTE; + } + break; + + case CAL_SUB_COMPUTE: { + uint16_t min1 = 0xFFFF, min2 = 0xFFFF, max1 = 0, max2 = 0; + uint32_t sum = 0; + for (size_t i = 0; i < CALIB_SAMPLES_TARGET; i++) { uint16_t v = calib_samples[idx][i]; sum += v; - if (v < min1) { min2=min1; min1=v; } - else if (v < min2) min2=v; - if (v > max1) { max2=max1; max1=v; } - else if (v > max2) max2=v; + if (v < min1) { min2 = min1; min1 = v; } + else if (v < min2) min2 = v; + if (v > max1) { max2 = max1; max1 = v; } + else if (v > max2) max2 = v; } - uint16_t avg_min = (min1+min2)/2; - uint16_t avg_max = (max1+max2)/2; + uint16_t avg_min = (min1 + min2) / 2; + uint16_t avg_max = (max1 + max2) / 2; uint16_t avg_all = sum / CALIB_SAMPLES_TARGET; - uint16_t final = (avg_min+avg_max+avg_all)/3; + uint16_t final = (avg_min + avg_max + avg_all) / 3; adc_calibration_t calib = adc_calibration_data[idx]; calib.raw_high = final; @@ -286,22 +327,25 @@ void ADC_Driver_TimerTask(void) { ADC_Driver_SetCalibration(ch, calib); ULOG_INFO("ADC: CALIB HIGH done ch=%u raw=%u", ch, final); - adc_driver_state = ADC_STATE_IDLE; adc_request_calib_high_channel = 0xFF; - } - } else { - adc_driver_state = ADC_STATE_IDLE; + calib_substate_high = CAL_SUB_IDLE; + } break; + + default: + calib_substate_high = CAL_SUB_IDLE; + adc_driver_state = ADC_STATE_IDLE; + break; } } break; case ADC_STATE_ERROR: - ULOG_ERROR("ADC driver error state"); + ULOG_ERROR("ADC driver error state"); break; default: - adc_driver_state = ADC_STATE_IDLE; + ULOG_INFO("ADC: unknown state=%u", adc_driver_state); + adc_driver_state = ADC_STATE_IDLE; break; } } - diff --git a/KPI_Rover/ADC/adc_driver.h b/KPI_Rover/ADC/adc_driver.h index 4100059..e7c7f13 100644 --- a/KPI_Rover/ADC/adc_driver.h +++ b/KPI_Rover/ADC/adc_driver.h @@ -4,6 +4,23 @@ #include #include +typedef enum { + ADC_STATE_IDLE = 0, + ADC_STATE_REQUEST_CALIB_LOW, + ADC_STATE_CALIB_LOW_PENDING, + ADC_STATE_REQUEST_CALIB_HIGH, + ADC_STATE_CALIB_HIGH_PENDING, + ADC_STATE_MEASURE_VOLTAGE, + ADC_STATE_ERROR +} adc_driver_state_t; + +typedef enum { + CAL_SUB_IDLE = 0, + CAL_SUB_WAIT_STABLE, + CAL_SUB_COLLECT, + CAL_SUB_COMPUTE +} adc_calib_substate_t; + typedef struct { uint16_t raw_low; uint16_t raw_high; @@ -16,10 +33,12 @@ typedef void (*adc_callback_t)(uint8_t channel, uint16_t value); typedef struct { uint32_t next_voltage_tick; uint16_t voltage_raw; + float voltage_cal; } adc_measurements_t; extern adc_measurements_t adc_meas; +extern uint32_t counter; void ADC_Driver_Init(const uint8_t* channels, size_t count); void ADC_Driver_Start(void); @@ -30,5 +49,6 @@ float ADC_Driver_GetCalibratedValue(uint8_t channel); uint16_t ADC_Driver_GetLastValue(uint8_t channel); void ADC_Driver_RegisterCallback(adc_callback_t cb); void ADC_Driver_TimerTask(void); +void ADC_Driver_ReadChannels(void); #endif diff --git a/KPI_Rover/ADC/adc_manager.c b/KPI_Rover/ADC/adc_manager.c index 0bdba8a..3a472d2 100644 --- a/KPI_Rover/ADC/adc_manager.c +++ b/KPI_Rover/ADC/adc_manager.c @@ -2,20 +2,17 @@ #include "ulog.h" #include "cmsis_os.h" #include "stm32f4xx_hal.h" +#include "stdbool.h" static const adc_channel_config_t adc_config[] = { { .channel = 2, .phys_high = 3.3f, .calibration_required = 1 } }; -static uint32_t last_log_tick = 0; - #define ADC_CONFIG_COUNT (sizeof(adc_config) / sizeof(adc_config[0])) -#define ADC_TASK_FLAG_TIMER (1U << 0) #define ADC_TIMER_PERIOD_MS 100 -static osTimerId_t adc_periodic_timer = NULL; -static osThreadId_t adcTaskHandle = NULL; +static osTimerId_t adc_timer_handle; static uint16_t adc_last_raw[ADC_CONFIG_COUNT]; static float adc_last_cal[ADC_CONFIG_COUNT]; @@ -30,12 +27,13 @@ static void adc_data_cb(uint8_t channel, uint16_t value) { } } -static void adc_periodic_timer_cb(void *arg) { - ULOG_INFO("adc_periodic_timer_cb"); - (void)arg; - osThreadFlagsSet(adcTaskHandle, ADC_TASK_FLAG_TIMER); +static volatile bool adc_timer_flag = false; + +static void adc_timer_callback(void *arg) { + adc_timer_flag = true; } + void ADC_Manager_Init(void) { uint8_t channels[ADC_CONFIG_COUNT]; @@ -45,59 +43,55 @@ void ADC_Manager_Init(void) { ADC_Driver_Init(channels, ADC_CONFIG_COUNT); + ADC_Driver_RegisterCallback(adc_data_cb); + ADC_Driver_Start(); - ADC_Driver_RegisterCallback(adc_data_cb); + const osTimerAttr_t timer_attrs = { + .name = "ADC_Timer" + }; - adcTaskHandle = osThreadGetId(); + adc_timer_handle = osTimerNew(adc_timer_callback, osTimerPeriodic, NULL, &timer_attrs); + if (!adc_timer_handle) { + ULOG_ERROR("Failed to create ADC timer"); + osThreadExit(); + } - const osTimerAttr_t timer_attr = { - .name = "ADC_Periodic", - }; - adc_periodic_timer = osTimerNew(adc_periodic_timer_cb, osTimerPeriodic, NULL, &timer_attr); + osDelay(10); + + if (osTimerStart(adc_timer_handle, ADC_TIMER_PERIOD_MS) != osOK) { + ULOG_ERROR("Failed to start ADC timer"); + osThreadExit(); + } } void ADC_Manager_Task(void *argument) { - - osTimerStart(adc_periodic_timer, ADC_TIMER_PERIOD_MS); - - ADC_Manager_Init(); + ADC_Manager_Init(); for (;;) { - - uint32_t flags = osThreadFlagsWait(ADC_TASK_FLAG_TIMER, osFlagsWaitAny, 10); - - if (flags & ADC_TASK_FLAG_TIMER) { - ADC_Driver_TimerTask(); - } - - uint32_t now = HAL_GetTick(); - if (now - last_log_tick >= 500) { - last_log_tick = now; - for (size_t i = 0; i < ADC_CONFIG_COUNT; ++i) { - int cal_mv = (int)(adc_last_cal[i] * 1000.0f); - ULOG_INFO("ADC CH%u: filtered=%u cal=%d mV", - adc_config[i].channel, adc_last_raw[i], cal_mv); - } + ADC_Driver_ReadChannels(); + if (adc_timer_flag) { + adc_timer_flag = false; + ADC_Driver_TimerTask(); } + osDelay(10); -// int state = DB_ReadCalibState(); - - switch (0) { // (state) -// case 1: // start 0В -// ADC_Driver_StartCalibrationLow(adc_config[0].channel); -// DB_WriteCalibState(2); -// break; +// int state = DB_ReadCalibState(); +// +// switch (0) { // (state) +// case 1: // start 0В +// ADC_Driver_StartCalibrationLow(adc_config[0].channel); +// DB_WriteCalibState(2); +// break; +// +// case 3: // start 3.3В +// ADC_Driver_StartCalibrationHigh(adc_config[0].channel); +// DB_WriteCalibState(4); +// break; // -// case 3: // start 3.3В -// ADC_Driver_StartCalibrationHigh(adc_config[0].channel); -// DB_WriteCalibState(4); -// break; - - default: - break; - } - osDelay(100); +// default: +// break; +// } } } From a0f74d0fbf12189d2bed7bb47f8f00003da4fb9d Mon Sep 17 00:00:00 2001 From: velichko Date: Sun, 25 Jan 2026 02:12:00 +0200 Subject: [PATCH 12/12] Issue #24: updated code accordingly Software Architecture Design document --- KPI_Rover/ADC/adc_driver.c | 351 -------------------------------- KPI_Rover/ADC/adc_driver.h | 54 ----- KPI_Rover/ADC/adc_manager.c | 97 --------- KPI_Rover/ADC/adc_manager.h | 20 -- KPI_Rover/ADC/drvAdc.c | 184 +++++++++++++++++ KPI_Rover/ADC/drvAdc.h | 16 ++ KPI_Rover/ADC/ulAdc.c | 259 +++++++++++++++++++++++ KPI_Rover/ADC/ulAdc.h | 11 + KPI_Rover/Database/ulDatabase.h | 12 +- KPI_Rover/KPIRover.c | 21 +- 10 files changed, 501 insertions(+), 524 deletions(-) delete mode 100644 KPI_Rover/ADC/adc_driver.c delete mode 100644 KPI_Rover/ADC/adc_driver.h delete mode 100644 KPI_Rover/ADC/adc_manager.c delete mode 100644 KPI_Rover/ADC/adc_manager.h create mode 100644 KPI_Rover/ADC/drvAdc.c create mode 100644 KPI_Rover/ADC/drvAdc.h create mode 100644 KPI_Rover/ADC/ulAdc.c create mode 100644 KPI_Rover/ADC/ulAdc.h diff --git a/KPI_Rover/ADC/adc_driver.c b/KPI_Rover/ADC/adc_driver.c deleted file mode 100644 index f26f992..0000000 --- a/KPI_Rover/ADC/adc_driver.c +++ /dev/null @@ -1,351 +0,0 @@ -#include "adc_driver.h" -#include "stm32f4xx_hal.h" -#include "ulog.h" - -#define MAX_ADC_CHANNELS 16 -#define EMA_ALPHA 0.05f -#define CALIB_SAMPLES_TARGET 36 - -static uint8_t adc_channels[MAX_ADC_CHANNELS]; -static uint16_t adc_raw_values[MAX_ADC_CHANNELS]; -static uint16_t adc_filtered_values[MAX_ADC_CHANNELS]; -static size_t channel_count = 0; - -static adc_callback_t registered_callback = NULL; - -static adc_calibration_t adc_calibration_data[MAX_ADC_CHANNELS]; - -static float adc_filtered_ema[MAX_ADC_CHANNELS] = {0.0f}; - -extern ADC_HandleTypeDef hadc1; - -static volatile adc_driver_state_t adc_driver_state = ADC_STATE_IDLE; -static volatile uint32_t adc_state_change_tick = 0; -static volatile uint8_t adc_request_calib_low_channel = 0xFF; -static volatile uint8_t adc_request_calib_high_channel = 0xFF; - -static adc_calib_substate_t calib_substate_low = CAL_SUB_IDLE; -static adc_calib_substate_t calib_substate_high = CAL_SUB_IDLE; - -#define CALIB_STABLE_MS 10 - -static uint16_t calib_samples[MAX_ADC_CHANNELS][CALIB_SAMPLES_TARGET]; -static uint8_t calib_sample_counts[MAX_ADC_CHANNELS] = {0}; - -static int find_channel_index(uint8_t channel) { - for (size_t i = 0; i < channel_count; ++i) { - if (adc_channels[i] == channel) return (int)i; - } - return -1; -} - -void ADC_Driver_Init(const uint8_t* channels, size_t count) { - channel_count = count > MAX_ADC_CHANNELS ? MAX_ADC_CHANNELS : count; - for (size_t i = 0; i < channel_count; i++) { - adc_channels[i] = channels[i]; - ADC_ChannelConfTypeDef sConfig = {0}; - sConfig.Channel = adc_channels[i]; - sConfig.Rank = 1; - sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES; - if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { - ULOG_ERROR("ADC: channel %u config failed", adc_channels[i]); - } else { - ULOG_INFO("ADC channel %u initialized", adc_channels[i]); - } - } -} - -void ADC_Driver_Start(void) { - if (channel_count == 0) { - ULOG_ERROR("ADC start failed: no channels configured"); - return; - } - if (HAL_ADC_Start(&hadc1) != HAL_OK) { - ULOG_ERROR("Failed to start ADC (polling mode)"); - } else { - ULOG_INFO("ADC started in polling mode with %d channels", channel_count); - } -} - -void ADC_Driver_StartCalibrationLow(uint8_t channel) { - adc_request_calib_low_channel = channel; - int idx = find_channel_index(channel); - if (idx >= 0) calib_sample_counts[idx] = 0; - - adc_driver_state = ADC_STATE_CALIB_LOW_PENDING; - calib_substate_low = CAL_SUB_WAIT_STABLE; - adc_state_change_tick = HAL_GetTick(); - ULOG_INFO("ADC: queued START CALIB LOW for channel %u", channel); -} - -void ADC_Driver_StartCalibrationHigh(uint8_t channel) { - adc_request_calib_high_channel = channel; - int idx = find_channel_index(channel); - if (idx >= 0) calib_sample_counts[idx] = 0; - - adc_driver_state = ADC_STATE_CALIB_HIGH_PENDING; - calib_substate_high = CAL_SUB_WAIT_STABLE; - adc_state_change_tick = HAL_GetTick(); - ULOG_INFO("ADC: queued START CALIB HIGH for channel %u", channel); -} - - - -void ADC_Driver_SetCalibration(uint8_t channel, adc_calibration_t calib) { - int idx = find_channel_index(channel); - if (idx >= 0) { - adc_calibration_data[idx] = calib; - ULOG_INFO("Calibration set for channel %u", channel); - } else { - ULOG_WARNING("Attempted to calibrate unknown channel %u", channel); - } -} - -float ADC_Driver_GetCalibratedValue(uint8_t channel) { - int idx = find_channel_index(channel); - if (idx < 0) { - ULOG_WARNING("Calibrated value requested for unknown channel %u", channel); - return 0.0f; - } - - adc_calibration_t calib = adc_calibration_data[idx]; - uint16_t raw = adc_filtered_values[idx]; - - if (calib.raw_high == calib.raw_low) return 0.0f; - - float phys = calib.phys_low + ((float)(raw - calib.raw_low)) * - (calib.phys_high - calib.phys_low) / - (float)(calib.raw_high - calib.raw_low); - return phys; -} - -void ADC_Driver_RegisterCallback(adc_callback_t cb) { - registered_callback = cb; - ULOG_DEBUG("ADC callback registered"); -} - -uint16_t ADC_Driver_GetLastValue(uint8_t channel) { - int idx = find_channel_index(channel); - if (idx >= 0) { - return adc_filtered_values[idx]; - } - ULOG_WARNING("ADC: Requested value for unregistered channel %u", channel); - return 0; -} - - -void ADC_Driver_ReadChannels(void) { - for (size_t i = 0; i < channel_count; i++) { - HAL_ADC_Start(&hadc1); - if (HAL_ADC_PollForConversion(&hadc1, 5) == HAL_OK) { - uint16_t raw = HAL_ADC_GetValue(&hadc1); - - adc_filtered_ema[i] = EMA_ALPHA * (float)raw + - (1.0f - EMA_ALPHA) * adc_filtered_ema[i]; - adc_filtered_values[i] = (uint16_t)adc_filtered_ema[i]; - - if (adc_driver_state == ADC_STATE_CALIB_LOW_PENDING && - calib_substate_low == CAL_SUB_COLLECT && - adc_channels[i] == adc_request_calib_low_channel) - { - int idx = find_channel_index(adc_channels[i]); - if (idx >= 0 && calib_sample_counts[idx] < CALIB_SAMPLES_TARGET) { - calib_samples[idx][calib_sample_counts[idx]++] = adc_filtered_values[idx]; - } - } - - if (adc_driver_state == ADC_STATE_CALIB_HIGH_PENDING && - calib_substate_high == CAL_SUB_COLLECT && - adc_channels[i] == adc_request_calib_high_channel) - { - int idx = find_channel_index(adc_channels[i]); - if (idx >= 0 && calib_sample_counts[idx] < CALIB_SAMPLES_TARGET) { - calib_samples[idx][calib_sample_counts[idx]++] = adc_filtered_values[idx]; - } - } - - if (registered_callback) { - registered_callback(adc_channels[i], adc_filtered_values[i]); - } - } - HAL_ADC_Stop(&hadc1); - } -} - - - -void ADC_Driver_TimerTask(void) { - uint32_t now = HAL_GetTick(); - static adc_measurements_t adc_meas = {0}; - - switch (adc_driver_state) { - - case ADC_STATE_IDLE: { - if (now >= adc_meas.next_voltage_tick) { - adc_driver_state = ADC_STATE_MEASURE_VOLTAGE; - } - } break; - - case ADC_STATE_MEASURE_VOLTAGE: { - int idx = find_channel_index(2); - if (idx >= 0) { - adc_meas.voltage_raw = ADC_Driver_GetLastValue(adc_channels[idx]); - adc_meas.voltage_cal = ADC_Driver_GetCalibratedValue(adc_channels[idx]); - ULOG_INFO("Voltage: raw=%u, filtered=%.3f V", - adc_meas.voltage_raw, - adc_meas.voltage_cal); - } - adc_meas.next_voltage_tick = now + 10; - adc_driver_state = ADC_STATE_IDLE; - } break; - - case ADC_STATE_REQUEST_CALIB_LOW: { - uint8_t ch = adc_request_calib_low_channel; - int idx = find_channel_index(ch); - if (idx >= 0) { - calib_sample_counts[idx] = 0; - adc_driver_state = ADC_STATE_CALIB_LOW_PENDING; - adc_state_change_tick = now; - ULOG_INFO("ADC: CALIB LOW pending (channel %u)", ch); - } else { - adc_driver_state = ADC_STATE_IDLE; - } - } break; - - case ADC_STATE_CALIB_LOW_PENDING: { - uint8_t ch = adc_request_calib_low_channel; - int idx = find_channel_index(ch); - if (idx < 0) { - adc_driver_state = ADC_STATE_IDLE; - break; - } - - switch (calib_substate_low) { - case CAL_SUB_WAIT_STABLE: - if ((now - adc_state_change_tick) > CALIB_STABLE_MS) { - calib_sample_counts[idx] = 0; - calib_substate_low = CAL_SUB_COLLECT; - ULOG_INFO("ADC: CALIB LOW COLLECT start (channel %u)", ch); - } - break; - - case CAL_SUB_COLLECT: - if (calib_sample_counts[idx] >= CALIB_SAMPLES_TARGET) { - calib_substate_low = CAL_SUB_COMPUTE; - } - break; - - case CAL_SUB_COMPUTE: { - uint16_t min1 = 0xFFFF, min2 = 0xFFFF, max1 = 0, max2 = 0; - uint32_t sum = 0; - for (size_t i = 0; i < CALIB_SAMPLES_TARGET; i++) { - uint16_t v = calib_samples[idx][i]; - sum += v; - if (v < min1) { min2 = min1; min1 = v; } - else if (v < min2) min2 = v; - if (v > max1) { max2 = max1; max1 = v; } - else if (v > max2) max2 = v; - } - uint16_t avg_min = (min1 + min2) / 2; - uint16_t avg_max = (max1 + max2) / 2; - uint16_t avg_all = sum / CALIB_SAMPLES_TARGET; - uint16_t final = (avg_min + avg_max + avg_all) / 3; - - adc_calibration_t calib = adc_calibration_data[idx]; - calib.raw_low = final; - calib.phys_low = 0.0f; - ADC_Driver_SetCalibration(ch, calib); - - ULOG_INFO("ADC: CALIB LOW done ch=%u raw=%u", ch, final); - adc_driver_state = ADC_STATE_IDLE; - adc_request_calib_low_channel = 0xFF; - calib_substate_low = CAL_SUB_IDLE; - } break; - - default: - calib_substate_low = CAL_SUB_IDLE; - adc_driver_state = ADC_STATE_IDLE; - break; - } - } break; - - case ADC_STATE_REQUEST_CALIB_HIGH: { - uint8_t ch = adc_request_calib_high_channel; - int idx = find_channel_index(ch); - if (idx >= 0) { - calib_sample_counts[idx] = 0; - adc_driver_state = ADC_STATE_CALIB_HIGH_PENDING; - adc_state_change_tick = now; - ULOG_INFO("ADC: CALIB HIGH pending (channel %u)", ch); - } else { - adc_driver_state = ADC_STATE_IDLE; - } - } break; - - case ADC_STATE_CALIB_HIGH_PENDING: { - uint8_t ch = adc_request_calib_high_channel; - int idx = find_channel_index(ch); - if (idx < 0) { - adc_driver_state = ADC_STATE_IDLE; - break; - } - - switch (calib_substate_high) { - case CAL_SUB_WAIT_STABLE: - if ((now - adc_state_change_tick) > CALIB_STABLE_MS) { - calib_sample_counts[idx] = 0; - calib_substate_high = CAL_SUB_COLLECT; - ULOG_INFO("ADC: CALIB HIGH COLLECT start (channel %u)", ch); - } - break; - - case CAL_SUB_COLLECT: - if (calib_sample_counts[idx] >= CALIB_SAMPLES_TARGET) { - calib_substate_high = CAL_SUB_COMPUTE; - } - break; - - case CAL_SUB_COMPUTE: { - uint16_t min1 = 0xFFFF, min2 = 0xFFFF, max1 = 0, max2 = 0; - uint32_t sum = 0; - for (size_t i = 0; i < CALIB_SAMPLES_TARGET; i++) { - uint16_t v = calib_samples[idx][i]; - sum += v; - if (v < min1) { min2 = min1; min1 = v; } - else if (v < min2) min2 = v; - if (v > max1) { max2 = max1; max1 = v; } - else if (v > max2) max2 = v; - } - uint16_t avg_min = (min1 + min2) / 2; - uint16_t avg_max = (max1 + max2) / 2; - uint16_t avg_all = sum / CALIB_SAMPLES_TARGET; - uint16_t final = (avg_min + avg_max + avg_all) / 3; - - adc_calibration_t calib = adc_calibration_data[idx]; - calib.raw_high = final; - calib.phys_high = 3.3f; - ADC_Driver_SetCalibration(ch, calib); - - ULOG_INFO("ADC: CALIB HIGH done ch=%u raw=%u", ch, final); - adc_driver_state = ADC_STATE_IDLE; - adc_request_calib_high_channel = 0xFF; - calib_substate_high = CAL_SUB_IDLE; - } break; - - default: - calib_substate_high = CAL_SUB_IDLE; - adc_driver_state = ADC_STATE_IDLE; - break; - } - } break; - - case ADC_STATE_ERROR: - ULOG_ERROR("ADC driver error state"); - break; - - default: - ULOG_INFO("ADC: unknown state=%u", adc_driver_state); - adc_driver_state = ADC_STATE_IDLE; - break; - } -} diff --git a/KPI_Rover/ADC/adc_driver.h b/KPI_Rover/ADC/adc_driver.h deleted file mode 100644 index e7c7f13..0000000 --- a/KPI_Rover/ADC/adc_driver.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef ADC_DRIVER_H -#define ADC_DRIVER_H - -#include -#include - -typedef enum { - ADC_STATE_IDLE = 0, - ADC_STATE_REQUEST_CALIB_LOW, - ADC_STATE_CALIB_LOW_PENDING, - ADC_STATE_REQUEST_CALIB_HIGH, - ADC_STATE_CALIB_HIGH_PENDING, - ADC_STATE_MEASURE_VOLTAGE, - ADC_STATE_ERROR -} adc_driver_state_t; - -typedef enum { - CAL_SUB_IDLE = 0, - CAL_SUB_WAIT_STABLE, - CAL_SUB_COLLECT, - CAL_SUB_COMPUTE -} adc_calib_substate_t; - -typedef struct { - uint16_t raw_low; - uint16_t raw_high; - float phys_low; - float phys_high; -} adc_calibration_t; - -typedef void (*adc_callback_t)(uint8_t channel, uint16_t value); - -typedef struct { - uint32_t next_voltage_tick; - uint16_t voltage_raw; - float voltage_cal; -} adc_measurements_t; - -extern adc_measurements_t adc_meas; - -extern uint32_t counter; - -void ADC_Driver_Init(const uint8_t* channels, size_t count); -void ADC_Driver_Start(void); -void ADC_Driver_StartCalibrationLow(uint8_t channel); -void ADC_Driver_StartCalibrationHigh(uint8_t channel); -void ADC_Driver_SetCalibration(uint8_t channel, adc_calibration_t calib); -float ADC_Driver_GetCalibratedValue(uint8_t channel); -uint16_t ADC_Driver_GetLastValue(uint8_t channel); -void ADC_Driver_RegisterCallback(adc_callback_t cb); -void ADC_Driver_TimerTask(void); -void ADC_Driver_ReadChannels(void); - -#endif diff --git a/KPI_Rover/ADC/adc_manager.c b/KPI_Rover/ADC/adc_manager.c deleted file mode 100644 index 3a472d2..0000000 --- a/KPI_Rover/ADC/adc_manager.c +++ /dev/null @@ -1,97 +0,0 @@ -#include "adc_manager.h" -#include "ulog.h" -#include "cmsis_os.h" -#include "stm32f4xx_hal.h" -#include "stdbool.h" - -static const adc_channel_config_t adc_config[] = { - { .channel = 2, .phys_high = 3.3f, .calibration_required = 1 } -}; - -#define ADC_CONFIG_COUNT (sizeof(adc_config) / sizeof(adc_config[0])) - -#define ADC_TIMER_PERIOD_MS 100 - -static osTimerId_t adc_timer_handle; - -static uint16_t adc_last_raw[ADC_CONFIG_COUNT]; -static float adc_last_cal[ADC_CONFIG_COUNT]; - -static void adc_data_cb(uint8_t channel, uint16_t value) { - for (size_t i = 0; i < ADC_CONFIG_COUNT; i++) { - if (adc_config[i].channel == channel) { - adc_last_raw[i] = value; - adc_last_cal[i] = ADC_Driver_GetCalibratedValue(channel); - break; - } - } -} - -static volatile bool adc_timer_flag = false; - -static void adc_timer_callback(void *arg) { - adc_timer_flag = true; -} - - -void ADC_Manager_Init(void) { - uint8_t channels[ADC_CONFIG_COUNT]; - - for (size_t i = 0; i < ADC_CONFIG_COUNT; i++) { - channels[i] = adc_config[i].channel; - } - - ADC_Driver_Init(channels, ADC_CONFIG_COUNT); - - ADC_Driver_RegisterCallback(adc_data_cb); - - ADC_Driver_Start(); - - const osTimerAttr_t timer_attrs = { - .name = "ADC_Timer" - }; - - adc_timer_handle = osTimerNew(adc_timer_callback, osTimerPeriodic, NULL, &timer_attrs); - if (!adc_timer_handle) { - ULOG_ERROR("Failed to create ADC timer"); - osThreadExit(); - } - - osDelay(10); - - if (osTimerStart(adc_timer_handle, ADC_TIMER_PERIOD_MS) != osOK) { - ULOG_ERROR("Failed to start ADC timer"); - osThreadExit(); - } -} - - -void ADC_Manager_Task(void *argument) { - ADC_Manager_Init(); - - for (;;) { - ADC_Driver_ReadChannels(); - if (adc_timer_flag) { - adc_timer_flag = false; - ADC_Driver_TimerTask(); - } - osDelay(10); - -// int state = DB_ReadCalibState(); -// -// switch (0) { // (state) -// case 1: // start 0В -// ADC_Driver_StartCalibrationLow(adc_config[0].channel); -// DB_WriteCalibState(2); -// break; -// -// case 3: // start 3.3В -// ADC_Driver_StartCalibrationHigh(adc_config[0].channel); -// DB_WriteCalibState(4); -// break; -// -// default: -// break; -// } - } -} diff --git a/KPI_Rover/ADC/adc_manager.h b/KPI_Rover/ADC/adc_manager.h deleted file mode 100644 index ddda3f9..0000000 --- a/KPI_Rover/ADC/adc_manager.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef ADC_MANAGER_H -#define ADC_MANAGER_H - -#include -#include -#include "adc_driver.h" -#include "FreeRTOS.h" -#include "queue.h" - - -typedef struct { - uint8_t channel; - float phys_high; - uint8_t calibration_required; -} adc_channel_config_t; - -void ADC_Manager_Init(void); -void ADC_Manager_Task(void *argument); - -#endif // ADC_MANAGER_H diff --git a/KPI_Rover/ADC/drvAdc.c b/KPI_Rover/ADC/drvAdc.c new file mode 100644 index 0000000..0613212 --- /dev/null +++ b/KPI_Rover/ADC/drvAdc.c @@ -0,0 +1,184 @@ +#include "drvAdc.h" +#include "stm32f4xx_hal.h" +#include "cmsis_os.h" +#include + +extern ADC_HandleTypeDef hadc1; + +#define DRV_ADC_FLAG_DMA_COMPLETE 0x01 +#define DRV_ADC_FLAG_TIMER_TRIG 0x02 +#define MEASUREMENT_PERIOD_MS 100 + +typedef enum { ADC_STATE_IDLE, ADC_STATE_BUSY } drv_adc_state_t; + +static drv_adc_state_t current_state = ADC_STATE_IDLE; +static uint8_t active_hw_channels[MAX_HW_ADC_ID]; +static size_t active_channel_count = 0; + +static uint16_t dma_buffer[MAX_HW_ADC_ID * SAMPLES_PER_CYCLE]; + +static osThreadId_t drv_adc_thread_id = NULL; +static osTimerId_t drv_adc_timer_id = NULL; + +typedef struct { + drvAdc_Callback cb; + void* ctx; +} adc_callback_entry_t; +static adc_callback_entry_t callback_table[MAX_HW_ADC_ID]; + + +static uint16_t Filter_Algorithm_36Samples(uint16_t* data, size_t step) { + uint32_t sum = 0; + uint16_t min1 = 0xFFFF, min2 = 0xFFFF; + uint16_t max1 = 0, max2 = 0; + + for (int i = 0; i < SAMPLES_PER_CYCLE; i++) { + uint16_t val = data[i * step]; + sum += val; + + if (val < min1) { + min2 = min1; min1 = val; + } else if (val < min2) { + min2 = val; + } + + if (val > max1) { + max2 = max1; max1 = val; + } else if (val > max2) { + max2 = val; + } + } + + sum -= (min1 + min2 + max1 + max2); + return (uint16_t)(sum >> 5); // / 32 +} + +static void drvAdc_WorkerTask(void *argument) { + for (;;) { + uint32_t flags = osThreadFlagsWait(DRV_ADC_FLAG_TIMER_TRIG | DRV_ADC_FLAG_DMA_COMPLETE, osFlagsWaitAny, osWaitForever); + + switch (current_state) { + + case ADC_STATE_IDLE: + if (flags & DRV_ADC_FLAG_TIMER_TRIG) { + HAL_StatusTypeDef status = HAL_ADC_Start_DMA(&hadc1, + (uint32_t*)dma_buffer, active_channel_count * SAMPLES_PER_CYCLE); + + if (status == HAL_OK) { + current_state = ADC_STATE_BUSY; + } + else { + } + } + break; + + case ADC_STATE_BUSY: + if (flags & DRV_ADC_FLAG_DMA_COMPLETE) { + + HAL_ADC_Stop_DMA(&hadc1); + HAL_ADC_Stop(&hadc1); + + for (size_t i = 0; i < active_channel_count; i++) { + uint8_t hw_ch = active_hw_channels[i]; + + uint16_t filtered = Filter_Algorithm_36Samples(&dma_buffer[i], active_channel_count); + + if (hw_ch < MAX_HW_ADC_ID && callback_table[hw_ch].cb != NULL) { + callback_table[hw_ch].cb(hw_ch, (uint32_t)filtered, callback_table[hw_ch].ctx); + } + } + + current_state = ADC_STATE_IDLE; + } + break; + + default: + HAL_ADC_Stop_DMA(&hadc1); + current_state = ADC_STATE_IDLE; + break; + } + } +} + +static void drvAdc_TimerCallback(void *arg) { + if (drv_adc_thread_id) { + osThreadFlagsSet(drv_adc_thread_id, DRV_ADC_FLAG_TIMER_TRIG); + } +} + +void drvAdc_init(uint32_t enabledChannels_mask) { + active_channel_count = 0; + memset(callback_table, 0, sizeof(callback_table)); + memset(dma_buffer, 0, sizeof(dma_buffer)); + + hadc1.Instance = ADC1; + for (int i = 0; i < MAX_HW_ADC_ID; i++) { + if (enabledChannels_mask & (1U << i)) { + active_channel_count++; + } + } + + hadc1.Init.NbrOfConversion = active_channel_count; + hadc1.Init.ScanConvMode = ENABLE; + hadc1.Init.ContinuousConvMode = ENABLE; + hadc1.Init.DiscontinuousConvMode = DISABLE; + hadc1.Init.DMAContinuousRequests = DISABLE; + HAL_ADC_Init(&hadc1); + + ADC_ChannelConfTypeDef sConfig = {0}; + uint8_t rank_counter = 1; + active_channel_count = 0; + + for (int i = 0; i < MAX_HW_ADC_ID; i++) { + if (enabledChannels_mask & (1U << i)) { + active_hw_channels[active_channel_count++] = i; + sConfig.Channel = i; + sConfig.Rank = rank_counter++; + sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES; + HAL_ADC_ConfigChannel(&hadc1, &sConfig); + + } + } + + const osThreadAttr_t task_attrs = { + .name = "DrvAdcTask", + .stack_size = 512, + .priority = osPriorityAboveNormal + }; + + drv_adc_thread_id = osThreadNew(drvAdc_WorkerTask, NULL, &task_attrs); + + const osTimerAttr_t timer_attrs = { + .name = "DrvAdcTimer" + }; + + drv_adc_timer_id = osTimerNew(drvAdc_TimerCallback, osTimerPeriodic, NULL, &timer_attrs); +} + +void drvAdc_startMeasurement(void) { + if (drv_adc_timer_id) { + osTimerStart(drv_adc_timer_id, MEASUREMENT_PERIOD_MS); + } +} + +void drvAdc_stopMeasurement(void) { + if (drv_adc_timer_id) { + osTimerStop(drv_adc_timer_id); + } + HAL_ADC_Stop_DMA(&hadc1); +} + +void drvAdc_registerCallback(uint8_t channel, drvAdc_Callback cb, void* ctx) { + if (channel < MAX_HW_ADC_ID) { + callback_table[channel].cb = cb; + callback_table[channel].ctx = ctx; + } +} + +void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { + if (hadc->Instance == ADC1) { + if (drv_adc_thread_id) { + osThreadFlagsSet(drv_adc_thread_id, DRV_ADC_FLAG_DMA_COMPLETE); + } + } +} diff --git a/KPI_Rover/ADC/drvAdc.h b/KPI_Rover/ADC/drvAdc.h new file mode 100644 index 0000000..ad6e0da --- /dev/null +++ b/KPI_Rover/ADC/drvAdc.h @@ -0,0 +1,16 @@ +#ifndef DRV_ADC_H +#define DRV_ADC_H + +#include + +#define SAMPLES_PER_CYCLE 36 +#define MAX_HW_ADC_ID 19 + +typedef void (*drvAdc_Callback)(uint8_t channel, uint32_t filteredRaw, void* ctx); + +void drvAdc_init(uint32_t enabledChannels_mask); +void drvAdc_startMeasurement(void); +void drvAdc_stopMeasurement(void); +void drvAdc_registerCallback(uint8_t channel, drvAdc_Callback cb, void* ctx); + +#endif // DRV_ADC_H diff --git a/KPI_Rover/ADC/ulAdc.c b/KPI_Rover/ADC/ulAdc.c new file mode 100644 index 0000000..f70bb25 --- /dev/null +++ b/KPI_Rover/ADC/ulAdc.c @@ -0,0 +1,259 @@ +#include "ulAdc.h" +#include "drvAdc.h" +#include +#include +#include +#include +#include "ulog.h" +#include "Database/ulDatabase.h" + +#define HW_CH_BATTERY 11 +#define HW_CH_TEMP 16 + +#define ADC_MAX_VAL 4095.0f +#define ADC_VREF 3.3f + +// 1. Battery: Voltage Divider /2.V = Raw * (3.3/4095) * 2 +#define BAT_DEFAULT_SCALE ((ADC_VREF / ADC_MAX_VAL) * 2.0f) +#define BAT_DEFAULT_OFFSET 0 + +// 2. Temp: T = (V - V25)/Slope + 25 +// V = Raw * (3.3/4095). +// T = [Raw * (3.3/4095) - V25] / Slope + 25 +// T = Raw * [3.3 / (4095*Slope)] - [V25/Slope - 25] +// T = Scale * (Raw - Offset_Raw) +// if Scale = 3.3 / (4095*Slope) and Offset_Raw represents the zero-crossing. +#define TEMP_V25 0.76f +#define TEMP_SLOPE 0.0025f +#define TEMP_DEFAULT_SCALE (ADC_VREF / (ADC_MAX_VAL * TEMP_SLOPE)) + +#define TEMP_DEFAULT_OFFSET (int32_t)(((TEMP_V25 - (25.0f * TEMP_SLOPE)) * ADC_MAX_VAL) / ADC_VREF) + +#define ADC_CAL_STATUS_OK 0x00 +#define ADC_CAL_STATUS_IN_PROGRESS 0x01 +#define ADC_CAL_STATUS_ERROR 0xFF + +#define LOG_INTERVAL_MS 1000 + +typedef enum { + CAL_STATE_IDLE = 0, + CAL_STATE_WAIT_FOR_SAMPLE +} adc_cal_state_t; + +typedef struct { + uint8_t hw_channel; + + uint16_t db_id_val; + uint16_t db_id_cal_offset; + uint16_t db_id_cal_scale; + + volatile int32_t active_offset; + volatile float active_scale; + + adc_cal_state_t cal_state; + uint8_t pending_point; + float pending_target; + + volatile uint32_t last_raw; + volatile float last_phys; +} adc_channel_ctx_t; + +adc_channel_ctx_t app_adc_ctx[] = { + { + .hw_channel = HW_CH_BATTERY, + .db_id_val = PARAM_BATTERY_VOLTAGE, + .db_id_cal_offset = ADC_CAL_CH_11_OFFSET, + .db_id_cal_scale = ADC_CAL_CH_11_SCALE, + .active_offset = BAT_DEFAULT_OFFSET, + .active_scale = BAT_DEFAULT_SCALE, + .cal_state = CAL_STATE_IDLE + }, + { + .hw_channel = HW_CH_TEMP, + .db_id_val = PARAM_MCU_TEMPERATURE, + .db_id_cal_offset = ADC_CAL_CH_TEMP_OFFSET, + .db_id_cal_scale = ADC_CAL_CH_TEMP_SCALE, + .active_offset = TEMP_DEFAULT_OFFSET, + .active_scale = TEMP_DEFAULT_SCALE, + .cal_state = CAL_STATE_IDLE + } +}; + +#define CTX_COUNT (sizeof(app_adc_ctx)/sizeof(app_adc_ctx[0])) + +static void ulAdc_GetHardcodedDefaults_Bat(float* defScale, int32_t* defOffset) { + const float Vref = 3.3f; + const float MaxAdc = 4095.0f; + const float DivFactor = 2.0f; // (R1+R2)/R2 + + // Val = Scale * (Raw - Offset) + // Offset = 0 + *defOffset = 0; + // Scale = (Vref / MaxAdc) * DivFactor + *defScale = (Vref / MaxAdc) * DivFactor; +} + +static void ulAdc_GetHardcodedDefaults_Temp(float* defScale, int32_t* defOffset) { + const float Vref = 3.3f; + const float MaxAdc = 4095.0f; + const float V25 = 0.76f; + const float Slope = 0.0025f; + + // T = (Raw * (Vref/Max) - V25) / Slope + 25 + // T = (Raw * Vref/Max)/Slope - V25/Slope + 25 + // T = Raw * (Vref/(Max*Slope)) - (V25/Slope - 25) + // T = Scale * (Raw - Offset_Raw) + + float scale_val = Vref / (MaxAdc * Slope); + float offset_val_phys = (V25 / Slope) - 25.0f; + + // Offset_Raw = offset_val_phys / scale_val + + *defScale = scale_val; + *defOffset = (int32_t)(offset_val_phys / scale_val); +} + +float ulAdc_applyCalibration(uint8_t channel, uint32_t rawValue) { + adc_channel_ctx_t* ctx = NULL; + for(size_t i=0; iactive_offset; + return (float)raw_shifted * ctx->active_scale; +} + +void ulAdc_CallbackHandler(uint8_t channel, uint32_t filteredRaw, void* ctx_void) { + adc_channel_ctx_t* ctx = (adc_channel_ctx_t*)ctx_void; + if (!ctx) { + return; + } + ctx->last_raw = filteredRaw; + + switch (ctx->cal_state) { + + case CAL_STATE_IDLE: + { + uint8_t start_cmd = 0; + + if (ulDatabase_getUint8(ADC_CALIBRATION_START, &start_cmd) == 0 && start_cmd == 0xF1) { + uint8_t target_ch = 0; + ulDatabase_getUint8(ADC_CALIBRATION_CHANNEL_ID, &target_ch); + if (target_ch == ctx->hw_channel) { + ulDatabase_getUint8(ADC_CALIBRATION_POINT, &ctx->pending_point); + ulDatabase_getFloat(ADC_CALIBRATION_POINT_VALUE, &ctx->pending_target); + + ctx->cal_state = CAL_STATE_WAIT_FOR_SAMPLE; + } + } + } + break; + + case CAL_STATE_WAIT_FOR_SAMPLE: + { + bool cal_ok = true; + + if (ctx->pending_point == 0) { + //OFFSET CALIBRATION --- + ctx->active_offset = (int32_t)filteredRaw; + ulDatabase_setInt32(ctx->db_id_cal_offset, ctx->active_offset); + } + else if (ctx->pending_point == 1) { + //SCALE CALIBRATION --- + int32_t raw_shifted = (int32_t)filteredRaw - ctx->active_offset; + + if (abs(raw_shifted) > 10) { + ctx->active_scale = ctx->pending_target / (float)raw_shifted; + ulDatabase_setFloat(ctx->db_id_cal_scale, ctx->active_scale); + } else { + cal_ok = false; + } + } + else { + cal_ok = false; + } + + if (cal_ok) { + ulDatabase_setUint8(ADC_CALIBRATION_START, ADC_CAL_STATUS_OK); + } else { + ulDatabase_setUint8(ADC_CALIBRATION_START, ADC_CAL_STATUS_ERROR); + } + ctx->cal_state = CAL_STATE_IDLE; + } + break; + + default: + ctx->cal_state = CAL_STATE_IDLE; + break; + } + + float phys_val = ulAdc_applyCalibration(channel, filteredRaw); + + ctx->last_phys = phys_val; + ulDatabase_setFloat(ctx->db_id_val, phys_val); +} + +void ulAdc_init(void) { + uint32_t adc_mask = 0; + + for (size_t i = 0; i < CTX_COUNT; i++) { + float def_scale; + int32_t def_offset; + + if (app_adc_ctx[i].hw_channel == HW_CH_BATTERY) { + ulAdc_GetHardcodedDefaults_Bat(&def_scale, &def_offset); + } else { + ulAdc_GetHardcodedDefaults_Temp(&def_scale, &def_offset); + } + + adc_channel_ctx_t* ctx = &app_adc_ctx[i]; + adc_mask |= (1U << ctx->hw_channel); + + int32_t stored_offset; + float stored_scale; + + if (ulDatabase_getInt32(ctx->db_id_cal_offset, &stored_offset) == 0) { + ctx->active_offset = stored_offset; + } else { + ctx->active_offset = def_offset; + } + + if (ulDatabase_getFloat(ctx->db_id_cal_scale, &stored_scale) == 0 && + isfinite(stored_scale) && fabsf(stored_scale) > 1e-9f) { + ctx->active_scale = stored_scale; + } else { + ctx->active_scale = def_scale; + } + } + + drvAdc_init(adc_mask); + + for (size_t i = 0; i < CTX_COUNT; i++) { + drvAdc_registerCallback(app_adc_ctx[i].hw_channel, ulAdc_CallbackHandler, (void*)&app_adc_ctx[i]); + } + + drvAdc_startMeasurement(); +} + +void ulAdc_task(void *argument) { + ulAdc_init(); + + for (;;) { + osDelay(LOG_INTERVAL_MS); + + for (int i = 0; i < CTX_COUNT; i++) { + adc_channel_ctx_t* ctx = &app_adc_ctx[i]; + + ULOG_INFO("[ADC CH%d] Raw: %lu | Phys: %.3f", ctx->hw_channel, ctx->last_raw, ctx->last_phys); + osDelay(20); + } + } +} diff --git a/KPI_Rover/ADC/ulAdc.h b/KPI_Rover/ADC/ulAdc.h new file mode 100644 index 0000000..0a4cba6 --- /dev/null +++ b/KPI_Rover/ADC/ulAdc.h @@ -0,0 +1,11 @@ +#ifndef ADC_MANAGER_H +#define ADC_MANAGER_H + +#include + +float ulAdc_applyCalibration(uint8_t channel, uint32_t rawValue); + +void ulAdc_init(void); +void ulAdc_task(void *argument); + +#endif // ADC_MANAGER_H diff --git a/KPI_Rover/Database/ulDatabase.h b/KPI_Rover/Database/ulDatabase.h index 724e9ec..d1f8971 100644 --- a/KPI_Rover/Database/ulDatabase.h +++ b/KPI_Rover/Database/ulDatabase.h @@ -23,7 +23,17 @@ enum ulDatabase_ParamId { MOTOR_RR_RPM, ENCODER_CONTROL_PERIOD_MS, ENCODER_TICKS_PER_REVOLUTION, - PARAM_COUNT + PARAM_COUNT, + ADC_CALIBRATION_START, + ADC_CALIBRATION_POINT, + ADC_CALIBRATION_POINT_VALUE, + ADC_CAL_CH_11_OFFSET, + ADC_CAL_CH_TEMP_OFFSET, + ADC_CAL_CH_11_SCALE, + ADC_CAL_CH_TEMP_SCALE, + ADC_CALIBRATION_CHANNEL_ID, + PARAM_BATTERY_VOLTAGE, + PARAM_MCU_TEMPERATURE }; struct ulDatabase_ParamMetadata { diff --git a/KPI_Rover/KPIRover.c b/KPI_Rover/KPIRover.c index d04d367..9c951d4 100644 --- a/KPI_Rover/KPIRover.c +++ b/KPI_Rover/KPIRover.c @@ -2,6 +2,7 @@ #include "cmsis_os.h" #include "Database/ulDatabase.h" +#include "ADC/ulAdc.h" static struct ulDatabase_ParamMetadata ulDatabase_params[] = { @@ -10,10 +11,28 @@ static struct ulDatabase_ParamMetadata ulDatabase_params[] = { {0, INT32, false, 0}, // MOTOR_RL_RPM, {0, INT32, false, 0}, // MOTOR_RR_RPM, {0, UINT16, true, 5}, // ENCODER_CONTROL_PERIOD_MS, - {0, FLOAT, true, 820.0f} // ENCODER_TICKS_PER_REVOLUTION, + {0, FLOAT, true, 820.0f}, // ENCODER_TICKS_PER_REVOLUTION, + {0, UINT8, false, 0}, // ADC_CALIBRATION_START, + {0, UINT8, false, 0}, // ADC_CALIBRATION_POINT, + {0, FLOAT, false, 0}, // ADC_CALIBRATION_POINT_VALUE, + {0, INT32, true, 0}, // ADC_CAL_CH_11_OFFSET, + {0, INT32, true, 0}, // ADC_CAL_CH_TEMP_OFFSET, + {0, FLOAT, true, 0.0f}, // ADC_CAL_CH_11_SCALE, + {0, FLOAT, true, 0.0f}, // ADC_CAL_CH_TEMP_SCALE, + {0, UINT8, false, 0}, // ADC_CALIBRATION_CHANNEL_ID + {0, FLOAT, false, 0}, // PARAM_BATTERY_VOLTAGE, + {0, FLOAT, false, 0}, // PARAM_MCU_TEMPERATURE }; void KPIRover_Init(void) { ulDatabase_init(ulDatabase_params, sizeof(ulDatabase_params) / sizeof(struct ulDatabase_ParamMetadata)); ulEncoder_Init(); + + // ADC + const osThreadAttr_t adcTask_attributes = { + .name = "adcTask", + .priority = (osPriority_t) osPriorityNormal, + .stack_size = 256 * 4 + }; + (void) osThreadNew(ulAdc_task, NULL, &adcTask_attributes); }