/* * ADC采集模块 - v2.3 (10次平均版本) * * 功能:使用HAL库直接采集ADC通道数据,连续10次读取求平均值 * - NTC温度(PA5-ADC1_IN5)-> 查表转换为温度存入 dev_info.ambient_temp * - 光控(PA4-ADC1_IN4) * - 电池电压(PA7-ADC1_IN7)-> 存入 sys_info.battery_voltage * - 太阳能充电电压(PA6-ADC1_IN6)-> 存入 sys_info.charge_voltage * - 灯管电流(PB0-ADC1_IN8) * - 高压输出电流(PB1-ADC1_IN9) * * Version: 2.3.0 */ /*********************↓ include ↓************************/ #define DBG_TAG "ADC" #define DBG_LVL DBG_INFO #include #include #include #include "stm32f4xx_hal.h" #include "ym310_protecl.h" /*********************↑ include ↑************************/ /*********************↓ define ↓************************/ /* ADC通道配置 */ #define ADC_CH_NTC 5 /* PA5 - ADC1_IN5 */ #define ADC_CH_LIGHT 4 /* PA4 - ADC1_IN4 */ #define ADC_CH_BAT 7 /* PA7 - ADC1_IN7 */ #define ADC_CH_PV 6 /* PA6 - ADC1_IN6 */ #define ADC_CH_LED 8 /* PB0 - ADC1_IN8 */ #define ADC_CH_ELE 9 /* PB1 - ADC1_IN9 */ /* 采集周期(毫秒) */ #define ADC_CYCLE_NTC 1000 #define ADC_CYCLE_LIGHT 100 #define ADC_CYCLE_BAT 500 #define ADC_CYCLE_PV 500 #define ADC_CYCLE_LED 500 #define ADC_CYCLE_ELE 5 #define ADC_BASE_CYCLE_MS 5 /* 采样次数(求平均值) */ #define ADC_SAMPLE_COUNT 5 /* 电压转换系数:ADC值 * 3.3 / 4095 * 2 * 1000 = ADC值 * 1.611 */ #define ADC_TO_MV(x) ((x) * 1611 * 11 / 2 / 1000) //#define ADC_TO_MV(x) ((x) * 1611 / 1000) #define ADC_TO_MV_BAT(x) ((x) * 1611 * 11 / 2 / 1000) /* NTC温度查表:-30℃到100℃,共131个点 */ /* 索引0对应-30℃,索引130对应100℃ */ static const uint16_t tempRes_buf[131] = { 3889, 3876, 3862, 3848, 3833, 3817, 3801, 3784, 3766, 3747, /* -30 ~ -21 */ 3727, 3707, 3686, 3663, 3640, 3616, 3592, 3566, 3539, 3511, /* -20 ~ -11 */ 3483, 3453, 3423, 3392, 3359, 3326, 3292, 3257, 3221, 3185, /* -10 ~ -1 */ 3147, /* 0 */ 3109, 3070, 3030, 2990, 2948, 2906, 2864, 2821, 2777, 2733, /* 1 ~ 10 */ 2689, 2644, 2599, 2553, 2508, 2462, 2416, 2369, 2324, 2277, /* 11 ~ 20 */ 2231, 2185, 2139, 2093, 2048, 2003, 1958, 1914, 1870, 1825, /* 21 ~ 30 */ 1783, 1739, 1698, 1656, 1615, 1575, 1534, 1495, 1457, 1419, /* 31 ~ 40 */ 1382, 1345, 1310, 1275, 1242, 1207, 1174, 1143, 1111, 1080, /* 41 ~ 50 */ 1051, 1021, 993, 965, 938, 911, 886, 861, 835, 811, /* 51 ~ 60 */ 790, 766, 744, 725, 702, 683, 663, 645, 625, 607, /* 61 ~ 70 */ 589, 574, 556, 540, 525, 512, 497, 481, 468, 455, /* 71 ~ 80 */ 442, 429, 419, 406, 396, 382, 372, 362, 352, 342, /* 81 ~ 90 */ 335, 324, 314, 307, 300, 289, 282, 275, 268, 261 /* 91 ~ 100 */ }; /*********************↑ define ↑************************/ extern SYS_STATUS sys_sta; /*********************↓ 静态变量 ↓************************/ static ADC_HandleTypeDef s_adc_handle; static rt_thread_t s_adc_thread = RT_NULL; static rt_bool_t s_adc_running = RT_FALSE; /* 各通道上次采集时间 */ static rt_tick_t s_last_ntc = 0; static rt_tick_t s_last_light = 0; static rt_tick_t s_last_bat = 0; static rt_tick_t s_last_pv = 0; static rt_tick_t s_last_led = 0; static rt_tick_t s_last_ele = 0; /*********************↑ 静态变量 ↑************************/ /*********************↓ 函数实现 ↓************************/ /** * @brief 判断是否到达采样时间 */ static rt_bool_t is_time_to_sample(rt_tick_t *last_tick, rt_uint32_t cycle_ms) { rt_tick_t now = rt_tick_get(); rt_tick_t cycle_tick = rt_tick_from_millisecond(cycle_ms); if ((now - *last_tick) >= cycle_tick) { *last_tick = now; return RT_TRUE; } return RT_FALSE; } /** * @brief 配置ADC引脚为模拟输入模式 */ static void adc_pin_init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* 使能GPIOA和GPIOB时钟 */ __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /* 配置PA4, PA5, PA6, PA7为模拟输入 */ GPIO_InitStruct.Pin = GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* 配置PB0, PB1为模拟输入 */ GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); //LOG_I("ADC pins configured as analog input"); } /** * @brief 初始化ADC */ static void adc_hw_init(void) { /* 使能ADC1时钟 */ __HAL_RCC_ADC1_CLK_ENABLE(); /* 配置ADC */ s_adc_handle.Instance = ADC1; s_adc_handle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; s_adc_handle.Init.Resolution = ADC_RESOLUTION_12B; s_adc_handle.Init.ScanConvMode = DISABLE; s_adc_handle.Init.ContinuousConvMode = DISABLE; s_adc_handle.Init.DiscontinuousConvMode = DISABLE; s_adc_handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; s_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; s_adc_handle.Init.NbrOfConversion = 1; s_adc_handle.Init.DMAContinuousRequests = DISABLE; s_adc_handle.Init.EOCSelection = ADC_EOC_SEQ_CONV; if (HAL_ADC_Init(&s_adc_handle) != HAL_OK) { LOG_E("ADC HAL init failed"); return; } //LOG_I("ADC HAL initialized"); } /** * @brief 读取单个ADC通道(单次读取) * @param channel ADC通道号 (ADC_CHANNEL_x) * @return ADC值(0-4095) */ static uint32_t adc_read_single(uint32_t channel) { uint32_t value = 0; ADC_ChannelConfTypeDef sConfig = {0}; /* 配置通道 */ sConfig.Channel = channel; sConfig.Rank = 1; sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES; sConfig.Offset = 0; if (HAL_ADC_ConfigChannel(&s_adc_handle, &sConfig) != HAL_OK) { LOG_E("ADC config channel %d failed", channel); return 0; } /* 启动ADC转换 */ HAL_ADC_Start(&s_adc_handle); /* 等待转换完成(超时100ms) */ if (HAL_ADC_PollForConversion(&s_adc_handle, 100) == HAL_OK) { value = HAL_ADC_GetValue(&s_adc_handle); } else { LOG_E("ADC conversion timeout, channel=%d", channel); } /* 停止ADC */ HAL_ADC_Stop(&s_adc_handle); return value; } /** * @brief 读取ADC通道(连续读取多次求平均值) * @param channel ADC通道号 (ADC_CHANNEL_x) * @return 平均ADC值(0-4095) */ static uint32_t adc_read_average(uint32_t channel) { uint32_t sum = 0; uint32_t value; uint8_t i; for (i = 0; i < ADC_SAMPLE_COUNT; i++) { value = adc_read_single(channel); sum += value; /* 采样间隔5ms,让ADC稳定 */ if (i < ADC_SAMPLE_COUNT - 1) { rt_thread_mdelay(5); } } return sum / ADC_SAMPLE_COUNT; } /** * @brief ADC值查表转换为温度(整数温度) * @param adc_value ADC采样值(0-4095) * @return 温度值(整数℃),超出范围返回边界值 */ static int8_t adc_to_temperature(uint32_t adc_value) { uint8_t i; /* 超出量程上限(低于-30℃) */ if (adc_value >= tempRes_buf[0]) { return -30; } /* 超出量程下限(高于100℃) */ if (adc_value <= tempRes_buf[130]) { return 100; } /* 线性查表 */ for (i = 0; i < 130; i++) { if (adc_value >= tempRes_buf[i + 1] && adc_value <= tempRes_buf[i]) { /* 线性插值,返回整数温度 */ int32_t temp = -30 + i; uint32_t diff = tempRes_buf[i] - tempRes_buf[i + 1]; if (diff > 0) { temp += (tempRes_buf[i] - adc_value) * 1 / diff; } return (int8_t)temp; } } return 100; } /** * @brief 执行一次ADC采集 */ static void adc_collect(void) { uint32_t value; int8_t temp; /* 1. NTC温度(PA5)- 查表转换为温度存入 dev_info.ambient_temp */ if (is_time_to_sample(&s_last_ntc, ADC_CYCLE_NTC)) { value = adc_read_average(ADC_CH_NTC); temp = adc_to_temperature(value); sensor.temp_ntc = temp * 10; /* 转换为0.1℃精度存储 */ sensor.valid_ntc = VALID; //sys_sta.board_temp = temp * 10; LOG_D("NTC: avg=%d, temp=%d°C", value, temp); } /* 2. 光控(PA4)- 转换为百分比存入 sys_sta.light_percent */ if (is_time_to_sample(&s_last_light, ADC_CYCLE_LIGHT)) { value = adc_read_average(ADC_CH_LIGHT); /* ADC取值范围0-4095,计算百分比(保留一位小数,乘以10存储)*/ /* 百分比 = (value / 4095) * 100 = value * 100 / 4095 */ /* 保留一位小数:乘以10存储,即 (value * 1000 / 4095) */ uint16_t percent_x10 = (uint16_t)(((uint32_t)value * 1000) / 4095); /* 限制范围0-1000(0.0%-100.0%)*/ if (percent_x10 > 1000) percent_x10 = 1000; sys_sta.light_percent = percent_x10; LOG_D("LIGHT: avg=%d, percent=%d.%d%%", value, percent_x10 / 10, percent_x10 % 10); } /* 3. 电池电压(PA7)- 存入 sys_info.battery_voltage */ if (is_time_to_sample(&s_last_bat, ADC_CYCLE_BAT)) { value = adc_read_average(ADC_CH_BAT); sys_sta.battery_voltage = ADC_TO_MV_BAT(value); LOG_D("BAT: avg=%d, %dmV", value, sys_info.battery_voltage); } /* 4. 太阳能充电电压(PA6)- 存入 sys_info.charge_voltage */ if (is_time_to_sample(&s_last_pv, ADC_CYCLE_PV)) { value = adc_read_average(ADC_CH_PV); sys_sta.charge_voltage = ADC_TO_MV(value); LOG_D("PV: avg=%d, %dmV", value, sys_info.charge_voltage); } /* 5. 灯管电流(PB0)*/ if (is_time_to_sample(&s_last_led, ADC_CYCLE_LED)) { value = adc_read_average(ADC_CH_LED); //LOG_I("LED: avg=%d", value); sys_sta.lamp_current = value; } // static char times = 0; // times ++; /* 6. 高压输出电流(PB1)*/ if (1) { value = adc_read_average(ADC_CH_ELE); // if(times >= 10) // { // LOG_I("ELE: avg=%d", value); // times = 0; // } sys_sta.hv_current = value; } } /** * @brief ADC采集线程入口 */ static void adc_thread_entry(void *parameter) { //LOG_I("ADC thread started (10 samples average)"); // LOG_I(" NTC:%dms LIGHT:%dms BAT:%dms PV:%dms LED:%dms ELE:%dms", // ADC_CYCLE_NTC, ADC_CYCLE_LIGHT, ADC_CYCLE_BAT, // ADC_CYCLE_PV, ADC_CYCLE_LED, ADC_CYCLE_ELE); /* 初始化各通道上次采样时间 */ rt_tick_t start_tick = rt_tick_get(); s_last_ntc = start_tick; s_last_light = start_tick; s_last_bat = start_tick; s_last_pv = start_tick; s_last_led = start_tick; s_last_ele = start_tick; while (1) { adc_collect(); rt_thread_mdelay(ADC_BASE_CYCLE_MS); } LOG_I("ADC thread stopped"); } /** * @brief 启动ADC采集线程 */ static void adc_thread_start(void) { if (s_adc_thread != RT_NULL) { LOG_D("ADC thread already running"); return; } /* 配置引脚 */ adc_pin_init(); /* 初始化ADC硬件 */ adc_hw_init(); /* 创建采集线程 */ s_adc_running = RT_TRUE; s_adc_thread = rt_thread_create("adc_mon", adc_thread_entry, NULL, 1024, 20, 10); if (s_adc_thread == RT_NULL) { LOG_E("Failed to create ADC thread"); s_adc_running = RT_FALSE; return; } rt_thread_startup(s_adc_thread); //LOG_I("ADC thread started"); } /*********************↓ 自动初始化 ↓************************/ static int adc_auto_init(void) { adc_thread_start(); return 0; } INIT_APP_EXPORT(adc_auto_init); /*********************↑ 自动初始化 ↑************************/ /*********************↓ MSH命令 ↓************************/ static void cmd_adc_read(int argc, char **argv) { uint32_t val; int8_t temp; rt_kprintf("\n========== ADC VALUES (10 samples avg) ==========\n"); val = adc_read_average(ADC_CH_NTC); temp = adc_to_temperature(val); rt_kprintf("NTC (PA5-ADC5): %d -> %d°C\n", val, temp); val = adc_read_average(ADC_CH_LIGHT); rt_kprintf("LIGHT (PA4-ADC4): %d %d.%d\n", val , sys_sta.light_percent /10 ,sys_sta.light_percent %10 ); val = adc_read_average(ADC_CH_BAT); rt_kprintf("BAT (PA7-ADC7): %d (%d mV)\n", val, ADC_TO_MV_BAT(val)); val = adc_read_average(ADC_CH_PV); rt_kprintf("PV (PA6-ADC6): %d (%d mV)\n", val, ADC_TO_MV(val)); val = adc_read_average(ADC_CH_LED); rt_kprintf("LED (PB0-ADC8): %d\n", val); val = adc_read_average(ADC_CH_ELE); rt_kprintf("ELE (PB1-ADC9): %d\n", val); rt_kprintf("==================================================\n"); rt_kprintf("sys_sta.battery_voltage = %d mV\n", sys_sta.battery_voltage); rt_kprintf("sys_sta.charge_voltage = %d mV\n", sys_sta.charge_voltage); rt_kprintf("sys_sta.ambient_temp = %d.%d°C\n", sensor.temp_ntc / 10, sensor.temp_ntc % 10); } MSH_CMD_EXPORT(cmd_adc_read, Read all ADC channels with 10 samples average); /*********************↑ MSH命令 ↑************************/