
一、项目概述本项目基于STM32F103C8T6 FreeRTOSCMSIS-RTOS v1实现NTC热敏电阻温度采集与OLED显示。系统采用软件定时器500ms周期触发采样通过任务通知唤醒温度任务完成ADC采样、NTC温度换算、5点滑动平均滤波后经长度为1的队列发送至OLED任务显示。核心设计要求软件定时器回调仅发送任务通知不执行任何采样或刷新操作温度任务完成ADC读取、换算、滤波并发队断线或ADC越界时OLED显示Sensor ERR系统不崩溃手摸NTC时Raw先变化AVG平滑跟随二、硬件接线STM32引脚连接对象说明PA0NTC分压中点NTC接VCC10k电阻接GNDPB6OLED SCL软件模拟I2CPB7OLED SDA软件模拟I2CPA9串口TX调试输出NTC参数10kΩ标称电阻B值3950R010kΩ25°C。三、系统架构软件定时器500ms自动重装 回调xTaskNotifyGive(tempTask) ← 仅通知 │ 任务通知 ▼ 温度任务栈512字优先级Normal ① ulTaskNotifyTake 等待通知 ② ADC采样PA0 ③ 越界检测 断线检测 ④ NTC Beta公式换算 ⑤ 5点滑动平均 ⑥ xQueueOverwrite 写入队列 │ 队列长度1 ▼ OLED任务栈256字优先级BelowNormal xQueueReceive 接收数据 显示ADC原始值 / Raw温度 / AVG温度关键配置configTOTAL_HEAP_SIZE8192温度任务栈512字OLED任务栈256字。四、关键问题与解决方案4.1 软件定时器静态分配配置开启configUSE_TIMERS后链接报错vApplicationGetTimerTaskMemory未定义。原因是工程用静态分配开定时器后需要提供定时器服务任务的TCB和栈空间。在FreeRTOSConfig.h添加#defineconfigUSE_TIMERS1#defineconfigTIMER_TASK_PRIORITY(tskIDLE_PRIORITY1)#defineconfigTIMER_TASK_STACK_DEPTH128#defineINCLUDE_xTaskNotifyGive1freertos.c实现回调staticStaticTask_t xTimerTaskTCBBuffer;staticStackType_t xTimerStack[configTIMER_TASK_STACK_DEPTH];voidvApplicationGetTimerTaskMemory(StaticTask_t**ppxTCB,StackType_t**ppxStack,uint32_t*ppxSize){*ppxTCBxTimerTaskTCBBuffer;*ppxStackxTimerStack[0];*ppxSizeconfigTIMER_TASK_STACK_DEPTH;}4.2 NTC分压方向与换算公式匹配室温正常但手捏NTC温度反而下降。代码公式对应NTC接GND实际接线是NTC接VCC。25°C时两边电阻都约10k看不出问题偏离后方向错误暴露。修正公式R_NTC10000.0f*(4095.0f-adc)/adc;T_kelvin1.0f/(1.0f/298.15flogf(R_NTC/10000.0f)/3950.0f);temp_cT_kelvin-273.15f;4.3 ADC断线检测设计拔掉PA0线后OLED不显示ERR。PA0是模拟输入高阻态悬空时电压随机飘ADC读到中间值不触发越界。直接改下拉输入能触发ERR但内部40k下拉和外部10k并联改变分压比温度不准。最终方案平时保持模拟输入采样后短暂切下拉读数字电平判断完立刻切回staticuint8_tSensorIsDisconnected(void){GPIO_InitTypeDef g{0};g.PinGPIO_PIN_0;g.ModeGPIO_MODE_INPUT;g.PullGPIO_PULLDOWN;HAL_GPIO_Init(GPIOA,g);for(volatileuint32_ti0;i2000;i)__NOP();uint8_tdisconnected(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)GPIO_PIN_RESET);g.ModeGPIO_MODE_ANALOG;g.PullGPIO_NOPULL;HAL_GPIO_Init(GPIOA,g);returndisconnected;}正常接线PA0约1.5V读到高悬空被下拉到0V读到低。检测仅数微秒不影响分压。五、核心代码软件定时器回调staticvoidvSamplingTimerCallback(TimerHandle_t xTimer){(void)xTimer;xTaskNotifyGive(temperatureTaskHandle);}温度任务staticvoidStartTemperatureTask(void*argument){SensorData_t data;(void)argument;for(;;){ulTaskNotifyTake(pdTRUE,portMAX_DELAY);data.adc_rawADC_Read_Value();data.sensor_err(data.adc_raw50||data.adc_raw4045)||SensorIsDisconnected();if(data.sensor_err){data.temp_rawdata.temp_avg0;ma_cnt0;ma_sum0;ma_idx0;}else{data.temp_rawNTC_Convert(data.adc_raw);ma_sum-ma_buf[ma_idx];ma_buf[ma_idx]data.temp_raw;ma_sumdata.temp_raw;ma_idx(ma_idx1)%5;if(ma_cnt5)ma_cnt;data.temp_avgma_sum/ma_cnt;}xQueueOverwrite(sensorQueueHandle,data);}}六、测试结果测试项预期结果实际结果正常接线显示ADC/Raw/AVG三行通过手捏NTCRaw先变AVG平滑跟随通过拔掉PA0显示Sensor ERR通过插回PA0自动恢复通过编译0 Error 0 Warning通过七、设计总结定时器回调只发通知耗时操作全在任务里做静态分配开定时器必须补vApplicationGetTimerTaskMemoryNTC分压方向要和公式对应标称点附近容易看不出问题断线检测用时间分片平时AIN检测瞬间切下拉环形缓冲滑动平均O(1)更新启动阶段按实际样本数平均