简介这份资源面向嵌入式初学者与温度采集开发者围绕STM32F103C8T6微控制器展开PT100热电阻测温的完整工程实践解决从传感器阻值变化到ADC采样、再到串口上传计算机的数据链路问题。压缩包共253个文件、约7.42MB以40个h头文件与38个c源文件构成核心代码配合37个d依赖文件、37个o目标文件及36个crf、36个__i等Keil编译中间文件另有uvprojx工程文件、hex烧录文件、sct分散加载与map映射文件完整保留了可编译可复现的工程目录。资源已有1214人学习下载说明其在同类测温项目中具备参考价值。读者可从中获取ADC通道配置与采样参数设置思路、连续转换配合中断或DMA读取的实现方式、USART波特率与帧格式配置方法以及计算机端解析温度帧的对接参考适合用来对照排查采样精度与串口通信问题。1. 从一颗 PT100 说起这个压缩包到底能帮你解决什么问题手上有个温度采集的小活传感器是 PT100主控是 STM32F103C8T6网上翻了一圈要么是只有原理图没有代码要么是代码里全是库函数调用、连个注释都没有。这个压缩包解决的正是这个场景它把 PT100 的微弱电阻变化经过恒流激励、差分放大、ADC 采样最后换算成摄氏度整条链路在 STM32F103C8T6 上跑通代码和工程文件一起给你。适合两类人一类是刚接触模拟前端、想照着跑一遍看波形的嵌入式新手另一类是手上有现成板子、想直接拿这套采集逻辑改吧改吧用到自己项目里的熟手。PT100 这东西不像 DS18B20 那样一根线读数字它的坑全在模拟侧这份工程的价值也正在这里。2. 工程结构与信号链路从 PT100 到 ADC 寄存器2.1 目录里都有什么解压之后是一个标准的 Keil MDK 工程目录结构大致如下不同版本可能略有出入但主干一致STM32f103PT100/ ├── CORE/ # 内核启动文件、core_cm3.h ├── HARDWARE/ │ ├── PT100/ # 本项目核心采集与换算 │ │ ├── pt100.c │ │ └── pt100.h │ ├── ADC/ # ADC1 初始化与 DMA 配置 │ ├── USART/ # 串口打印方便看实时温度 │ └── TIM/ # 定时采样节拍 ├── STM32F10x_FWLib/ # 标准外设库 ├── USER/ │ ├── main.c │ ├── stm32f10x_it.c │ └── system_stm32f10x.c └── OBJ/ # 编译输出看目录就能判断技术路线用的是 STM32 标准外设库不是 HALADC 走 DMA串口负责把温度打出来。这种组合在 F103 上非常典型资源占用小、时序可控适合做周期性采集。HARDWARE/PT100是唯一需要重点读的模块其余都是支撑。2.2 模拟前端是怎么接的PT100 在 0℃ 时是 100Ω温度每升 1℃电阻大约增加 0.385Ω。这个变化量很小直接分压进 ADC 分辨率根本不够看。工程里常见的做法是恒流源激励给 PT100 灌一个 1mA 左右的恒定电流那么它两端的电压就是V I × R0℃ 时约 100mV100℃ 时约 138.5mV。这个电压再送进差分放大器比如仪表放大器结构放大 1020 倍落到 03.3V 的 ADC 量程内。这里有个关键点单端接法会把引线电阻也算进去三线制 PT100 就是为了抵消引线电阻。工程里如果用的是三线制硬件上会有两根线分别接到放大器的两个输入端软件侧不需要额外处理但接线必须接对否则误差会稳定偏大几欧姆。我见过有人把三线制当两线制接读数一直偏高 23℃查了半天以为是代码问题其实是线接错了。2.3 ADC 采样与 DMA 搬运采集部分的核心是让 ADC 连续转换、DMA 自动搬数据CPU 不参与搬运。看HARDWARE/ADC里的初始化典型配置是这样的void ADC_DMA_Init(void) { ADC_InitTypeDef ADC_InitStructure; DMA_InitTypeDef DMA_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); /* PA0 作为模拟输入接放大后的 PT100 电压 */ GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_Init(GPIOA, GPIO_InitStructure); DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)ADC1-DR; DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)adc_buffer; DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize ADC_BUFFER_SIZE; /* 多次采样做平均 */ DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode DMA_Mode_Circular; DMA_Init(DMA1_Channel1, DMA_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE); ADC_InitStructure.ADC_Mode ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode DISABLE; ADC_InitStructure.ADC_ContinuousConvMode ENABLE; ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel 1; ADC_Init(ADC1, ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5); ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); /* ADC 校准这一步不能省 */ ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1)); ADC_SoftwareStartConvCmd(ADC1, ENABLE); }逐段说清楚DMA_Mode_Circular让 DMA 循环填充adc_buffer缓冲区满了自动从头覆盖主循环随时取最近一批数据做平均即可不用等中断。ADC_SampleTime_239Cycles5是最长采样时间PT100 前端输出阻抗通常不低采样时间短了会采不准这是很多人忽略的点。ADC_ResetCalibration和ADC_StartCalibration这两步是 F103 的固定流程跳过校准零点会漂读数整体偏几十个 LSB属于典型的“代码看着没错但就是不准”。adc_buffer的长度ADC_BUFFER_SIZE一般取 16 或 32主循环里对这批值求平均能压掉一部分工频噪声。如果现场干扰大还可以在软件里做中值滤波先排序取中间值再平均效果比单纯平均稳。3. 温度换算与主循环从 ADC 码值到摄氏度3.1 换算公式怎么来的拿到 ADC 平均值之后要反推温度。链路是ADC 码值 → 电压 → 放大前电压 → PT100 电阻 → 温度。工程里通常把前几步合并成一个线性系数最后用 Callendar-Van Dusen 公式或者分段线性表换算。0100℃ 范围内用线性近似R R0 × (1 αT)α 取 0.00385误差在 0.4℃ 以内做一般监测够用要更准就上二次项。/* 把 ADC 平均码值换算成摄氏度 */ float ADC_To_Temperature(uint32_t adc_avg) { float voltage, r_pt100, temperature; /* 1. 码值转电压12 位 ADC参考 3.3V */ voltage (float)adc_avg * 3.3f / 4095.0f; /* 2. 去掉放大倍数还原 PT100 两端电压GAIN 按实际电路填 */ voltage voltage / PT100_GAIN; /* 3. 恒流源 1mAU I*R反推电阻 */ r_pt100 voltage / PT100_EXCITE_CURRENT; /* 4. 线性换算温度R0 100Ωα 0.00385 */ temperature (r_pt100 - 100.0f) / (100.0f * 0.00385f); return temperature; }参数说明PT100_GAIN必须和硬件放大倍数一致填错了温度整体缩放错PT100_EXCITE_CURRENT是恒流源实际电流用万用表量一下再填别信标称值。第 4 步的线性公式在负温区误差会变大如果项目要测零下换成查表法更稳。这段代码看着简单但每一步的系数都来自硬件软件和电路必须对得上这也是为什么光有代码跑不起来——你得知道自己板子上的增益和激励电流是多少。3.2 主循环与串口输出main.c的结构很直白初始化时钟、串口、ADC-DMA然后进 while(1)每隔固定时间取一次缓冲区平均值、换算、打印。int main(void) { float temp; uint32_t avg; delay_init(); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); uart_init(115200); ADC_DMA_Init(); while (1) { avg Get_ADC_Average(adc_buffer, ADC_BUFFER_SIZE); temp ADC_To_Temperature(avg); printf(Temp: %.2f C\r\n, temp); delay_ms(500); /* 采样节拍500ms 一次足够 */ } }Get_ADC_Average就是遍历求和再除注意用 32 位累加避免溢出。delay_ms(500)决定了刷新率做温度监测没必要更快太快反而让串口刷屏看不清。串口波特率 115200接上 USB-TTL 就能在串口助手里看到温度曲线这是验证整条链路最快的方式手指捏住 PT100读数应该在几秒内往上走松手慢慢回落说明采集和换算都通了。3.3 采样节拍与滤波的取舍有人会问为什么不用定时器触发 ADC 而用软件延时。对这个项目来说温度是慢变量500ms 一次的节拍用延时完全够省一个定时器配置。但如果你的项目还要同时干别的活延时阻塞主循环就不合适了常见做法是改成 TIM 定时中断里置标志位主循环轮询标志或者干脆用定时器 TRGO 触发 ADC让采样节拍和 CPU 解耦。这是二次开发时第一个该动的地方。4. 避坑与排查读数不准、跳变、跑不起来的常见原因4.1 读数整体偏高或偏低几度现象串口打出来的温度稳定地比实际高 23℃重复性很好不是随机跳。原因多半在硬件侧三线制接成两线制引线电阻被算进 PT100或者恒流源实际电流和代码里的PT100_EXCITE_CURRENT对不上。解决先用精密电阻箱代替 PT100给 100Ω看读数是不是 0℃ 附近不是就调系数再检查接线方式三线制的补偿线必须接到放大器对应端。4.2 读数在小数点后乱跳现象温度值在 ±0.5℃ 内高频抖动平均值也不稳。原因通常是采样时间太短、前端输出阻抗高ADC 采样电容没充到位或者电源和地噪声大。解决把ADC_SampleTime调到 239.5 周期缓冲区加大到 32 做平均模拟地和数字地单点连接放大器的电源加 0.1uF 去耦。还压不住就在软件里加中值滤波。4.3 编译通过但串口没输出现象程序下载进去了串口助手一片空白。原因常见两个一是printf重定向没配工程里usart.c里要有fputc重定向到 USART1二是波特率或引脚接错F103C8T6 的 USART1 是 PA9/PA10接反了收不到。解决先确认fputc存在且USE_MICROLIB勾选再用示波器或逻辑分析仪看 PA9 有没有波形有波形就是接线或波特率问题。4.4 ADC 值一直是 0 或满量程现象adc_buffer里全是 0 或者全是 4095。原因是模拟输入引脚配置错了模式配成普通 GPIO 而不是GPIO_Mode_AIN或者 DMA 通道和 ADC 没对上F103 的 ADC1 固定用 DMA1_Channel1。解决对照参考手册核对引脚和 DMA 通道映射确认ADC_DMACmd在ADC_Cmd之前调用顺序错了 DMA 请求发不出来。4.5 换块板子就不准了现象同一份代码在原板子上正常换一块就不准。原因是代码里的增益、激励电流、参考电压都是按原板子写死的新板子的电阻精度、基准源不同。解决把硬件相关参数抽到头文件宏里换板子只改宏有条件的话在出厂前做一次两点校准0℃ 和 100℃把偏移和斜率存进 Flash运行时读出来修正这比改代码靠谱。5. 二次开发把这套采集逻辑搬进自己的项目拿到这份工程最值的不是原样跑而是把PT100模块抽出来复用。我的习惯是先把硬件相关参数全部宏化做成一个配置头/* pt100_config.h —— 换板子只改这里 */ #define PT100_GAIN 11.0f /* 实测放大倍数别用标称 */ #define PT100_EXCITE_CURRENT 0.001f /* 恒流源电流单位 A */ #define PT100_R0 100.0f #define PT100_ALPHA 0.00385f #define ADC_VREF 3.3f #define ADC_FULL_SCALE 4095.0f这样移植到新项目时只改这几个数换算函数一行不动。接着把ADC_To_Temperature的线性公式换成查表加分段线性插值覆盖 -50200℃精度能到 0.1℃ 级别代价是一张几十项的电阻-温度表Flash 占用可以忽略。验证方法很土但有效拿几个已知温度的環境冰水混合物 0℃、沸水 100℃ 视海拔修正各测一遍看偏差是否在允许范围内再决定要不要加校准。再往上一层如果项目要多路温度把adc_buffer和换算函数改成数组驱动DMA 开扫描模式一次搬多通道主循环按通道号取对应系数。这套结构在 F103 上跑 48 路毫无压力。最后提醒一句模拟前端的布局比代码更影响结果放大器尽量靠近 PT100 接线端子走线避开电机和开关电源这些是代码救不回来的。从那以后我每次做模拟采集都先把硬件参数用万用表量一遍再写代码不再对着标称值想当然。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?