/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file adc_sample.c * @author Motor Control SDK Team, Yuwell Software XiangenWang * @brief Voice Recognition Module Initialization Section, including peripheral initialization and message node insertion, etc. * @version 1.0 * @changelog version 1.0 初始版本 2025.12.01 - 新增:新建第一个版本的软件,待完善解析命令后的程序执行部分 ****************************************************************************** * @attention * *

© Copyright (c) 2025 Yuwell Software Danyang.Jiangsu.China. * All rights reserved.

* * Redistribution and use in source and binary forms, with or without * modification, are permitted, provided that the following conditions are met: * * 1. Redistribution of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of Yuwell Software nor the names of other * contributors to this software may be used to endorse or promote products * derived from this software without specific written permission. * 4. This software, including modifications and/or derivative works of this * software, must execute solely and exclusively on microcontroller or * microprocessor devices manufactured by or for Yuwell Software. * 5. Redistribution and use of this software other than as permitted under * this license is void and will automatically terminate your rights under * this license. * * THIS SOFTWARE IS PROVIDED BY Yuwell Software AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT * SHALL Yuwell Software OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** */ /* USER CODE END Header */ #include "adc_sample.h" #include static uint16_t adc_result[ADC_SAMPLE_COUNT] = {0, 0, 0, 0, 0}; /** * @brief 温度-标称电阻值映射表(0~120℃) * @param NONE * @retval NONE * @note 关于配置说明: * 1.数组索引0对应0℃,索引n对应n℃,直至索引120对应120℃ * 2.电阻值放大10000倍存储(如100kΩ存储为1000000,单位:0.0001kΩ) * 3.数据来源为NTC热敏电阻官方校准表标称电阻值 * @author jarvis * @date 2025.11.25 */ static const uint32_t ntc_res_standard[] = { 3270195, //0℃: 327.0195kΩ → 3270195 (×10000) 3107640, //1℃: 310.7640kΩ 2954121, //2℃: 295.4121kΩ 2809084, //3℃: 280.9084kΩ 2672014, //4℃: 267.2014kΩ 2542428, //5℃: 254.2428kΩ 2419877, //6℃: 241.9877kΩ 2303940, //7℃: 230.3940kΩ 2194224, //8℃: 219.4224kΩ 2090361, //9℃: 209.0361kΩ 1992007, //10℃: 199.2007kΩ 1898841, //11℃: 189.8841kΩ 1810559, //12℃: 181.0559kΩ 1726881, //13℃: 172.6881kΩ 1647540, //14℃: 164.7540kΩ 1572290, //15℃: 157.2290kΩ 1500898, //16℃: 150.0898kΩ 1433144, //17℃: 143.3144kΩ 1368825, //18℃: 136.8825kΩ 1307749, //19℃: 130.7749kΩ 1249734, //20℃: 124.9734kΩ 1194612, //21℃: 119.4612kΩ 1142223, //22℃: 114.2223kΩ 1092417, //23℃: 109.2417kΩ 1045053, //24℃: 104.5053kΩ 1000000, //25℃: 100.0000kΩ 957132, //26℃: 95.7132kΩ 916333, //27℃: 91.6333kΩ 877492, //28℃: 87.7492kΩ 840505, //29℃: 84.0505kΩ 805274, //30℃: 80.5274kΩ 771707, //31℃: 77.1707kΩ 739717, //32℃: 73.9717kΩ 709222, //33℃: 70.9222kΩ 680144, //34℃: 68.0144kΩ 652411, //35℃: 65.2411kΩ 625954, //36℃: 62.5954kΩ 600707, //37℃: 60.0707kΩ 576610, //38℃: 57.6610kΩ 553604, //39℃: 55.3604kΩ 531635, //40℃: 53.1635kΩ 510651, //41℃: 51.0651kΩ 490602, //42℃: 49.0602kΩ 471443, //43℃: 47.1443kΩ 453130, //44℃: 45.3130kΩ 435621, //45℃: 43.5621kΩ 418878, //46℃: 41.8878kΩ 402862, //47℃: 40.2862kΩ 387539, //48℃: 38.7539kΩ 372876, //49℃: 37.2876kΩ 358842, //50℃: 35.8842kΩ 345405, //51℃: 34.5405kΩ 332538, //52℃: 33.2538kΩ 320214, //53℃: 32.0214kΩ 308408, //54℃: 30.8408kΩ 297096, //55℃: 29.7096kΩ 286253, //56℃: 28.6253kΩ 275860, //57℃: 27.5860kΩ 265895, //58℃: 26.5895kΩ 256338, //59℃: 25.6338kΩ 247171, //60℃: 24.7171kΩ 238376, //61℃: 23.8376kΩ 229937, //62℃: 22.9937kΩ 221836, //63℃: 22.1836kΩ 214061, //64℃: 21.4061kΩ 206594, //65℃: 20.6594kΩ 199424, //66℃: 19.9424kΩ 192537, //67℃: 19.2537kΩ 185920, //68℃: 18.5920kΩ 179562, //69℃: 17.9562kΩ 173452, //70℃: 17.3452kΩ 167578, //71℃: 16.7578kΩ 161930, //72℃: 16.1930kΩ 156499, //73℃: 15.6499kΩ 151276, //74℃: 15.1276kΩ 146251, //75℃: 14.6251kΩ 141417, //76℃: 14.1417kΩ 136764, //77℃: 13.6764kΩ 132286, //78℃: 13.2286kΩ 127976, //79℃: 12.7976kΩ 123825, //80℃: 12.3825kΩ 119828, //81℃: 11.9828kΩ 115978, //82℃: 11.5978kΩ 112270, //83℃: 11.2270kΩ 108697, //84℃: 10.8697kΩ 105254, //85℃: 10.5254kΩ 101935, //86℃: 10.1935kΩ 98736, //87℃: 9.8736kΩ 95652, //88℃: 9.5652kΩ 92678, //89℃: 9.2678kΩ 89809, //90℃: 8.9809kΩ 87042, //91℃: 8.7042kΩ 84373, //92℃: 8.4373kΩ 81797, //93℃: 8.1797kΩ 79312, //94℃: 7.9312kΩ 76912, //95℃: 7.6912kΩ 74596, //96℃: 7.4596kΩ 72360, //97℃: 7.2360kΩ 70201, //98℃: 7.0201kΩ 68115, //99℃: 6.8115kΩ 66101, //100℃: 6.6101kΩ 64155, //101℃: 6.4155kΩ 62274, //102℃: 6.2274kΩ 60457, //103℃: 6.0457kΩ 58701, //104℃: 5.8701kΩ 57003, //105℃: 5.7003kΩ 55362, //106℃: 5.5362kΩ 53775, //107℃: 5.3775kΩ 52240, //108℃: 5.2240kΩ 50755, //109℃: 5.0755kΩ 49319, //110℃: 4.9319kΩ 47930, //111℃: 4.7930kΩ 46586, //112℃: 4.6586kΩ 45285, //113℃: 4.5285kΩ 44026, //114℃: 4.4026kΩ 42807, //115℃: 4.2807kΩ 41627, //116℃: 4.1627kΩ 40484, //117℃: 4.0484kΩ 39378, //118℃: 3.9378kΩ 38306, //119℃: 3.8306kΩ 37268 //120℃: 3.7268kΩ }; /** * @brief 全局宏定义配置 * @param NONE * @retval NONE * @note 关于配置说明: * 1.TEMP_MIN/TEMP_MAX定义温度检测范围0~120℃ * 2.电阻值放大10000倍存储,提升整数插值精度 * @author jarvis * @date 2025.11.25 */ #define TEMP_MIN 0 #define TEMP_MAX 120 #define ARRAY_LEN (sizeof(ntc_res_standard)/sizeof(ntc_res_standard[0])) /** * @brief 从NTC电阻值插值计算温度 * @param ntc_res: NTC电阻值(放大1000倍,如100kΩ=100000) * @retval 计算得到的温度值(℃) * @note 关于配置说明: * 1.采用线性插值算法,全程无浮点运算 * 2.插值前放大1000倍计算,提升整数运算精度 * 3.超出温度范围时返回边界值(0℃或120℃) * 4.输入电阻值为×1000,内部转换为×10000匹配数组 * @author jarvis * @date 2025.11.25 */ static uint8_t calc_temp_by_res(uint32_t ntc_res) { // 1. 输入电阻值转换:×1000 → ×10000(匹配数组存储精度) uint32_t ntc_res_scaled = ntc_res * 10; // 2. 边界保护:电阻值越小温度越高 if (ntc_res_scaled > ntc_res_standard[0]) { // 电阻>0℃值 → 温度<0℃ return TEMP_MIN; } if (ntc_res_scaled < ntc_res_standard[ARRAY_LEN-1]) { // 电阻<120℃值 → 温度>120℃ return TEMP_MAX; } // 3. 查找电阻值所在的温度区间 uint8_t idx = 0; for (idx = 0; idx < ARRAY_LEN-1; idx++) { if ((ntc_res_scaled <= ntc_res_standard[idx]) && (ntc_res_scaled >= ntc_res_standard[idx+1])) { break; } } return (uint8_t)idx; } /** * @brief 从ADC采样电压计算温度 * @param adc_val: ADC采样值(3000对应3V,5000对应5V) * @retval 计算得到的温度值(℃) * @note 关于配置说明: * 1.电路为NTC与100kΩ并联后再与10kΩ串联分压 * 2.反向推导公式:R_NTC = (R并 × R13) / (R13 - R并) * 3.增加SCALE放大倍数,避免整数除法精度丢失 * @author jarvis * @date 2025.11.25 */ static uint8_t calc_temp_by_adc(uint16_t adc_val) { uint32_t r_parallel_ohm = (uint32_t)adc_val * 10000; r_parallel_ohm /= (5000 - adc_val); r_parallel_ohm /= 1000; uint32_t r_ntc_ohm = r_parallel_ohm * 100; if(r_parallel_ohm >= 100) // 防止电阻焊接错误 { r_parallel_ohm = 99; } r_ntc_ohm /= (100 - r_parallel_ohm); return calc_temp_by_res(r_ntc_ohm*1000); } // 校准数据表:采样值-实际电压(你的4组核心数据) typedef struct { uint16_t adc_val; // 采样值 uint16_t voltage; // 实际电压(V) } CalibData_t; static const CalibData_t calib_table[] = { {802, 160}, {864, 170}, {925, 180}, {985, 190}, {1045, 200}, {1105, 210}, {1164, 220}, {1225, 230}, {1284, 240}, {1345, 250}, {1405, 260}, {1465, 270}, {1524, 280}, }; #define CALIB_LEN (sizeof(calib_table)/sizeof(calib_table[0])) /** * @brief 采样值转实际电压(查表+线性插值,无浮点) * @param adc_val: 采样值(如1172) * @retval 实际电压(V) */ uint16_t calc_220v_voltage(uint16_t adc_val) { // 边界保护 if (adc_val <= calib_table[0].adc_val) { return calib_table[0].voltage; } if (adc_val >= calib_table[CALIB_LEN-1].adc_val) { return calib_table[CALIB_LEN-1].voltage; } // 查找采样值所在区间 uint16_t idx = 0; for (idx = 0; idx < CALIB_LEN-1; idx++) { if (adc_val >= calib_table[idx].adc_val && adc_val <= calib_table[idx+1].adc_val) { break; } } // 线性插值(纯整数运算,放大1000倍提升精度) int32_t x1 = calib_table[idx].adc_val; int32_t y1 = calib_table[idx].voltage; int32_t x2 = calib_table[idx+1].adc_val; int32_t y2 = calib_table[idx+1].voltage; uint16_t voltage = y1 + ((adc_val - x1) * (y2 - y1)) / (x2 - x1); return voltage; } /** * @brief ADC采样初始化 * @param NONE * @retval NONE * @note 关于配置说明: * 1.使用BSTIM16定时器每隔100ms进行一次ADC转换触发 * 2.配置DMA进行持续数据移动,将外设数据移动至RAM * 3.采用16次过采样硬件滤波策略,提升精度同时抑制噪声 * @author jarvis * @data 2025.11.25 */ void adc_sample_init(void) { // 初始化ADC采样的GPIO FL_GPIO_InitTypeDef GPIO_InitStruct; FL_BSTIM16_InitTypeDef InitStructer; FL_ADC_InitTypeDef ADC_InitStruct; FL_ADC_CommonInitTypeDef Common_InitStruct; FL_DMA_InitTypeDef DMA_InitStruct = {0}; FL_DMA_ConfigTypeDef DMA_ConfigStruct = {0}; /************************************************** 配置用于触发ADC的定时器频率 **********************************************************/ InitStructer.prescaler = 63; /* 配置BSTIM16预分频 */ InitStructer.autoReload = 9999; /* 配置BSTIM16目标寄存器 */ InitStructer.autoReloadState = FL_ENABLE; /* 配置BSTIM16预装载功能 */ InitStructer.clockSource = FL_CMU_BSTIM16_CLK_SOURCE_APBCLK; /* 配置BSTIM16工作时钟源 */ (void)FL_BSTIM16_Init(BSTIM16, &InitStructer); FL_BSTIM16_SetTriggerOutput(BSTIM16, FL_BSTIM16_TRGO_UPDATE); /* 配置BSTIM16 Trigger事件 */ FL_BSTIM16_ClearFlag_Update(BSTIM16); /* 清updata标志 */ /************************************************** 配置ADC通用配置 **********************************************************/ Common_InitStruct.clockSource = FL_ADC_CLK_SOURCE_APBCLK; /* 配置ADC工作时钟源 */ Common_InitStruct.clockPrescaler = FL_ADC_CLK_PSC_DIV2; /* 配置ADC工作时钟分配 */ Common_InitStruct.referenceSource = FL_ADC_REF_SOURCE_VDDA; /* 配置ADC参考源 */ Common_InitStruct.bitWidth = FL_ADC_BIT_WIDTH_12B; /* 配置ADC输出位宽 */ (void)FL_ADC_CommonInit(&Common_InitStruct); /************************************************** 配置GPIO引脚状态 **********************************************************/ GPIO_InitStruct.pin = ADC_PRESS_GPIO_PIN; /* 配置GPIO的引脚号 */ GPIO_InitStruct.mode = FL_GPIO_MODE_ANALOG; /* 配置GPIO的功能模式 */ GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL; /* 配置GPIO的输出模式 */ GPIO_InitStruct.pull = FL_DISABLE; /* 配置GPIO输上拉模式 */ GPIO_InitStruct.remapPin = FL_DISABLE; /* 配置GPIO数字重定向功能 */ GPIO_InitStruct.analogSwitch = FL_DISABLE; /* 配置GPIO模拟开关功能 */ (void)FL_GPIO_Init(ADC_PRESS_GPIO_PORT, &GPIO_InitStruct); /* GPIO初始化 */ GPIO_InitStruct.pin = ADC_NTC_GPIO_PIN; (void)FL_GPIO_Init(ADC_NTC_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.pin = ADC_220V_GPIO_PIN; (void)FL_GPIO_Init(ADC_220V_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.pin = ADC_ELEC_GPIO_PIN; (void)FL_GPIO_Init(ADC_ELEC_GPIO_PORT, &GPIO_InitStruct); /************************************************** 配置ADC工作通道 **********************************************************/ // 初始化ADC采样通道 ADC_InitStruct.conversionMode = FL_ADC_CONV_MODE_SINGLE; /* 配置ADC转换模式 */ ADC_InitStruct.autoMode = FL_ADC_SINGLE_CONV_MODE_AUTO; /* 配置ADC转换流程,仅对单次转换有效 */ ADC_InitStruct.waitMode = FL_DISABLE; /* 配置ADC等待模式 */ ADC_InitStruct.overrunMode = FL_ENABLE; /* 配置ADC_Overrun模式 */ ADC_InitStruct.scanDirection = FL_ADC_SEQ_SCAN_DIR_FORWARD; /* 配置ADC扫描顺序 */ ADC_InitStruct.externalTrigConv = FL_ADC_TRIGGER_EDGE_RISING; /* 配置非软件触发使能及极性 */ ADC_InitStruct.triggerSource = FL_ADC_TRGI_BSTIM16; /* 配置ADC非软件触发源 */ ADC_InitStruct.fastChannelTime = FL_ADC_FAST_CH_SAMPLING_TIME_16_ADCCLK; /* 配置ADC快速通道采样时间 */ ADC_InitStruct.lowChannelTime = FL_ADC_SLOW_CH_SAMPLING_TIME_512_ADCCLK; /* 配置ADC慢速通道采样时间 */ ADC_InitStruct.oversamplingMode = FL_ENABLE; /* 配置ADC过采样模式 */ ADC_InitStruct.overSampingMultiplier = FL_ADC_OVERSAMPLING_MUL_16X; /* 配置ADC过采样率 */ ADC_InitStruct.oversamplingShift = FL_ADC_OVERSAMPLING_SHIFT_4B; /* 配置ADC过采样结果移位 */ (void)FL_ADC_Init(ADC, &ADC_InitStruct); FL_ADC_EnableSequencerChannel(ADC, ADC_PRESS_CHANNEL); /*通道选择*/ FL_ADC_EnableSequencerChannel(ADC, ADC_NTC_CHANNEL); FL_ADC_EnableSequencerChannel(ADC, ADC_220V_CHANNEL); FL_ADC_EnableSequencerChannel(ADC, ADC_ELEC_CHANNEL); FL_ADC_EnableSequencerChannel(ADC, FL_ADC_INTERNAL_VREF1P2); FL_VREF_EnableVREFBuffer(VREF); FL_ADC_EnableDMAReq(ADC); /*配置ADC_DMA*/ /************************************************** 配置DMA转换 **********************************************************/ DMA_InitStruct.periphAddress = FL_DMA_PERIPHERAL_FUNCTION1; //配置DMA通道功能 DMA_InitStruct.direction = FL_DMA_DIR_PERIPHERAL_TO_RAM; //配置DMA通道方向 DMA_InitStruct.memoryAddressIncMode = FL_DMA_MEMORY_INC_MODE_INCREASE; //配置RAM的增减方向 DMA_InitStruct.dataSize = FL_DMA_BANDWIDTH_16B; //配置DMA传输位宽 DMA_InitStruct.priority = FL_DMA_PRIORITY_HIGH; //配置DMA通道优先级 DMA_InitStruct.circMode = FL_ENABLE; //配置DMA通道循环缓存 (void)FL_DMA_Init(DMA, &DMA_InitStruct, FL_DMA_CHANNEL_0); FL_DMA_Enable(DMA); //配置DMA全局开关 /************************************************** 配置DMA通道 **********************************************************/ DMA_ConfigStruct.memoryAddress = (uint32_t)adc_result; //配置DMA_RAM地址 DMA_ConfigStruct.transmissionCount = ADC_SAMPLE_COUNT - 1; //配置DMA传输长度 (void)FL_DMA_StartTransmission(DMA, &DMA_ConfigStruct, FL_DMA_CHANNEL_0); FL_ADC_ClearFlag_EndOfConversion(ADC); FL_ADC_Enable(ADC); FL_BSTIM16_Enable(BSTIM16); } void sample_adc_process_task(void) { // 对采样结果进行修改 MsgQueueItem queue_item; int32_t temp_press; queue_item.type = MSG_TYPE_AD_SAMPLING; queue_item.data.adc_data.adc_elec_value = adc_result[ADC_IDX_ELEC]; queue_item.data.adc_data.adc_ntc_value = adc_result[ADC_IDX_NTC]; queue_item.data.adc_data.adc_press_value = adc_result[ADC_IDX_PRESS]; queue_item.data.adc_data.adc_220v_value = adc_result[ADC_IDX_220V]; // 计算各个通道的真实电压 queue_item.data.adc_data.voltage_elec = (uint32_t)(((uint64_t)adc_result[ADC_IDX_ELEC] * ADC_VREF * 3000) / ((uint64_t)adc_result[ADC_IDX_INTERNAL_VREF1P2] * 4095)); queue_item.data.adc_data.voltage_ntc = (uint32_t)(((uint64_t)adc_result[ADC_IDX_NTC] * ADC_VREF * 3000) / ((uint64_t)adc_result[ADC_IDX_INTERNAL_VREF1P2] * 4095)); queue_item.data.adc_data.voltage_press = (uint32_t)(((uint64_t)adc_result[ADC_IDX_PRESS] * ADC_VREF * 3000) / ((uint64_t)adc_result[ADC_IDX_INTERNAL_VREF1P2] * 4095)); queue_item.data.adc_data.voltage_220v = (uint32_t)(((uint64_t)adc_result[ADC_IDX_220V] * ADC_VREF * 3000) / ((uint64_t)adc_result[ADC_IDX_INTERNAL_VREF1P2] * 4095)); // 计算真实压力值 temp_press = (int32_t)1908 * queue_item.data.adc_data.voltage_press - 852000; if(temp_press < 0) { temp_press = 0; } queue_item.data.adc_data.real_press = temp_press / 10000; // 计算真实温度值 queue_item.data.adc_data.real_ntc = calc_temp_by_adc(queue_item.data.adc_data.voltage_ntc); // 计算真实网电压值 queue_item.data.adc_data.real_220V = calc_220v_voltage(queue_item.data.adc_data.voltage_220v); // 计算真实压缩机电流值 queue_item.data.adc_data.real_elec = queue_item.data.adc_data.voltage_elec * 639/100; modify_or_add_queue_node_by_type(&global_queue, queue_item.type, queue_item); // 每隔100ms修改队列中的AD采样数据 } /************************ (C) COPYRIGHT YUWELL *****END OF FILE****/