资讯详情

资讯详情

STM32 ADC采集NTC温度实战:从硬件设计到标定避坑指南

简介面向嵌入式开发者的STM32 NTC温度测试完整工程基于STM32F103实现负温度系数热敏电阻的实时温度采集与换算涵盖ADC采样、分压电路、B值公式计算及USMART调试等关键环节适合学习传感器采集与单片机外设驱动的开发者参考。资源为zip压缩包共90个文件以C源码.c/.h为主同时包含Keil工程配置.uvprojx/.uvoptx、编译生成的hex/axf和辅助调试的map/crf等整体仅1.3MB便于直接打开、编译和烧录。已有3762人学习使用除核心温度测量程序外还整理了LED、OLED、LCD、ADC、EXTI、DMA、定时器等外设驱动模块及readme说明可帮助读者从电压采集到温度显示、异常判断完成完整温控系统搭建。 做温控相关项目时很多人第一次接触stm32的ADC都会选NTC热敏电阻。我之前做一个小型的培养箱温度监测模块原理图非常简单——一个10k NTC加一个10k采样电阻分压后送给stm32的ADC引脚。结果板子贴好、固件烧进去室温25℃居然读出来17℃。当时第一反应是代码写错翻来覆去查了一整晚最后才发现问题根本不在算法而是从硬件分压、采样时间到标定方式整条链路都有坑。这篇就把NTC温度测试的完整链路过一遍从电路设计、ADC配置到温度算法选型和实测标定尽量把我在项目里踩过的坑都说清楚。这套内容适合刚接触stm32 ADC采集的人也适合那些用NTC做产品但又觉得“读数总飘、精度上不去”的人。文章偏实战原理部分我会用比较容易理解的方式讲透代码和参数都给出可直接参考的配置。1. 硬件方案先行NTC分压电路与STM32采样通道设计1.1 为什么是NTC而不是其他温度传感器NTC的全称是负温度系数热敏电阻温度升高时阻值下降。它的优点是便宜、响应快、电路结构简单常温精度做到±0.5℃左右完全可行测温范围在-40℃到150℃之间覆盖了绝大多数消费级和工业级场景。相比之下DS18B20虽然接口简单、不用算电阻但它的封装规格相对固定测量延迟也更大热电偶能测很高温度却需要冷端补偿和放大电路PT100精度高但成本高、调理电路复杂用在几十块钱成本的板子上并不划算。因此在大多数中低温、小体积、低成本场景下NTC是综合性价比最高的选择。STM32做NTC采集也不需要外部专用ADC芯片芯片自带的12位ADC配合好电路设计已经能满足0.1℃级别的分辨率需求。这个小项目里选用的参数是25℃标称阻值10kΩ、B值3950这是市面上最常见也是资料最全的型号。1.2 分压电路怎么接、采样电阻选多大NTC本身是电阻它不能直接进ADC必须通过分压电路把电阻变化转换成电压变化。工程上最常用的接法是让NTC放在下臂采样电阻R0接VCC中间的分压点接STM32的ADC引脚VCC —— R0 —— (ADC采样点) —— NTC —— GND此时ADC引脚电压V_adc VCC * R_ntc / (R0 R_ntc)R0的取值直接影响测量分辨率和量程分布选型原则是让R0接近测温范围中间点对应的NTC阻值。以10k NTC、测温范围-20℃到80℃为例25℃时NTC阻值是10k那么R0取10k25℃的分压点正好落在VCC的一半ADC读数在中间两边都有充足的动态范围。我按实测参数算一组数据。VCC3.3V12位ADC满量程4095R010k25℃时R_ntc10kV_adc1.65VADC读数≈2048。温度到30℃时按B3950计算R_ntc≈8.04kV_adc≈1.47VADC读数≈1823。也就是说5℃温差产生225个LSB的变化换算下来大概0.022℃/LSB这个分辨率对于大多数应用场景都够用了。如果只测低温区间R0可以适当取大一些让低温段的分压变化更“舒展”如果侧重高温段R0取小一些。但不要为了让某个区间的分辨率极端化而牺牲整体量程R0和NTC标称值同数量级是工程上最通用的做法。1.3 长引线和滤波电容带来的ADC读数误差NTC探头如果通过长线连接主板线缆电阻会叠加在NTC阻值上。比如普通的杜邦线内阻大约0.1Ω左右影响不大但如果用几十米的信号线线缆电阻可能达到几欧姆甚至十几欧姆这时候测温偏差就不可忽略了。这种情况我建议改用三线制接法或者干脆把信号调理电路移到探头端再传模拟电压。很多人在ADC引脚上加一个100nF的滤波电容这对于抑制高频干扰确实有帮助但有一个副作用必须清楚NTC分压点的输出阻抗比较高常温下大约是R0与R_ntc的并联值也就是5kΩ左右。100nF电容和5kΩ阻抗组成的时间常数是0.5ms而STM32的ADC默认采样时间只有1.5-2.4μs左右采样电容根本来不及充满ADC读到的电压会明显偏低。初次调试时如果发现ADC读数远低于理论值并且换采样时间后读数变化很大多半就是这个原因。基础项目里滤波电容建议控制在10nF以内同时配合下文说到的长时间采样配置。2. ADC转换不是想当然采样时间、参考电压与源阻抗的影响2.1 真正决定ADC读数稳定性的参数STM32的12位ADC在配置上有几个容易被忽略却又至关重要的参数参考电压、采样时间、ADC时钟分频和转换分辨率。参考电压VREF通常是直接接VDDA的如果VDDA受负载波动影响那么ADC的满量程也会跟着波动导致同一个温度读出来的ADC值不一致。对精度要求高的项目建议给VDDA单独加LDO或者用高精度基准源同时把模拟电源和数字电源做好磁珠隔离。ADC采样时间是另一个关键点。STM32内部的采样开关闭合后采样电容需要从外部信号源汲取电荷外部等效阻抗越高需要的充电时间就越长。这个参数可以在CubeMX里直接配置HAL库对应的代码片段是hadc1.Init.ClockPrescaler ADC_CLOCK_SYNC_PCLK_DIV4; hadc1.Init.Resolution ADC_RESOLUTION_12B; hadc1.Init.SamplingTime ADC_SAMPLETIME_480CYCLES;480个ADC时钟周期的采样时间在APB2时钟72MHz、分频4即18MHz ADC时钟下大约26.7μs。实测下来这个配置配合5kΩ级别的NTC分压源阻抗读数非常稳定。如果你用的是标准外设库对应修改ADC_SMPR2寄存器里的SMP位为0b111即可效果一样。2.2 源阻抗陷阱一个很容易忽略的坑很多人在做NTC采集时为了让信号“更干净”在ADC引脚前串联一个几kΩ的电阻再加一个大电容滤波。这个做法在低频慢变信号里听起来合理但对于ADC采样实际上是灾难。我调试时就遇到过ADC读数总是比万用表实测电压低几十个mV后来我用示波器量了采样瞬间的引脚波形发现每次采样时引脚电压都会被拉低一块采样结束后再慢慢回升典型的采样电容充电不足现象。判断源阻抗是否过高的办法很简单把采样时间从默认值逐步调大比如从14周期调到480周期如果ADC读数有明显上升且最终趋于稳定那就是源阻抗导致充电不充分。解决办法有三种第一降低串联电阻、减小滤波电容第二增加采样时间第三在ADC前端加一个低输出阻抗的运放缓冲器让运放去驱动ADC采样电容。前两种适合大多数简单项目第三种适合信号源阻抗实在降不下来的场景。2.3 多次采样取平均依然是小成本大收益NTC本身是慢变信号温度变化的时间常数通常以秒计所以ADC采回来的数据做软件滤波几乎不需要担心滞后问题。最基础的做法是连续采样多次取平均比如每次采集64次去掉最大值和最小值然后对剩余值平均。这样做的意义在于单次ADC采样存在量化噪声和系统噪声多次平均可以显著降低随机噪声的影响。我实际统计过单次采集的ADC读数波动幅度大概在±3个LSB64次平均后波动降到±1个LSB以内换算成温度大约是0.02℃完全够用。滤波代码也很简单用一个32位的累加器采样完除以次数即可。注意不要用uint16_t累加64次12位ADC值累加会超过65535直接溢出导致错误结果这个细节我见过不少人踩过。3. 从ADC原始值到温度查表、公式、拟合三种路线的选型与实现3.1 第一步把ADC原始值换算成NTC电阻值ADC原始值只是电压的数字量要换算成温度中间必须先得到NTC当前的阻值。用上文的接法NTC在下臂、采样电阻接VCC且VREF≈VCC时存在一个非常方便的换算关系R_ntc R0 * adc / (4095 - adc)注意这里VCC和VREF被约掉了这意味着只要分压电源和ADC参考电压用同一路供电参考电压的绝对精度几乎不影响最终电阻换算ADC天然具备比例测量的优势。这个公式推导起来很简单V_adc adc * VREF / 4095同时V_adc VCC * R_ntc / (R0 R_ntc)令VREF≈VCC两边约分就得到上面的式子。实际编码时有一个常见错误先用整数运算算adc / (4095 - adc)得到0就直接丢精度了。先转成float再计算或者直接用浮点表达式否则高温区数据会严重失真。3.2 查表法工程上最稳的土办法查表法是最直观也最容易控制的方案。先从NTC手册里拿到R-T对照表或者用仿真工具生成一张-20℃到100℃、每1℃一个阻值的表存成const数组。每次采样后先算R_ntc然后在表中二分查找相邻两个温度点用线性插值得到小数部分温度。查表法最大的优点是把NTC的非线性完全“吃”进了表格里只要表数据可靠精度就能保证。缺点是Flash占用会多一些比如-20℃到100℃每1℃一个点一共121个float值约500字节对STM32来说完全不值一提。如果你的NTC手册给的表精度不够还可以用自己的标定数据填充表格后面第4章会讲。实现上推荐用R值建表而不是ADC值建表。用R值建表的好处是电路参数变化时表不用改换R0或者换供电电压都不影响算法的正确性。const float temp_table[] { -20.0f, -19.0f, -18.0f, ... }; const float r_table[] { 97000.0f, 92000.0f, ... }; float R_to_temp(float R) { // 二分查找R_table中的区间 // 对temp_table做线性插值 }二分查找加插值在STM32上执行只需要几微秒性能完全不是问题。3.3 Steinhart-Hart公式精度上限更高但别硬套默认系数Steinhart-Hart方程是目前精度最高的NTC温度换算数学表达式1/T A B * ln(R) C * (ln(R))^3其中T是开尔文温度R是当前NTC阻值A、B、C是三个待定系数。三个系数最好用三个已知的温度-电阻标定点来求解。比如用0℃、25℃、70℃三组实测数据通过解三元一次方程组就能得到。用Python求解非常方便import numpy as np R1, T1 32650.0, 273.15 0.0 R2, T2 10000.0, 273.15 25.0 R3, T3 1751.0, 273.15 70.0 M np.array([ [1, np.log(R1), np.log(R1)**3], [1, np.log(R2), np.log(R2)**3], [1, np.log(R3), np.log(R3)**3] ]) b np.array([1/T1, 1/T2, 1/T3]) A, B, C np.linalg.solve(M, b) print(A, B, C)然后把求出来的A、B、C写进C代码float calc_temp_sh(float r_ntc) { float lnR logf(r_ntc); float t_k_inv A B * lnR C * lnR * lnR * lnR; return 1.0f / t_k_inv - 273.15f; }需要注意的是网上很多文章直接给一组所谓“通用”的A、B、C系数但NTC的阻值和B值离散度通常有±1%到±3%套用通用系数在不同批次的NTC上精度可能差得很远。最稳妥的做法是拿自己实际使用的NTC型号做标定求系数不要偷懒。如果只做粗略测量可以退一步用简化β公式T 1 / (1/T0 (1/B) * ln(R/R0)) - 273.15β公式只需要三个参数T0298.15KR025℃标称阻值B值。优点是计算量小但在远离25℃的温度区间误差会变大因为B值本身随温度是缓慢变化的。实际项目中如果测温范围窄比如20℃到40℃β公式够了如果范围宽S-H公式或查表更可靠。3.4 最小二乘拟合和查表到底怎么选搜相关资料时“NTC曲线拟合精度高”这个说法很常见。所谓拟合通常是用实测标定数据构造一条温度-电阻曲线比如四阶多项式或者分段插值然后用这条曲线去换算温度。最小二乘多项式拟合的思路是测量8到10个标定点的温度与阻值用最小二乘拟合出一个三阶或四阶多项式之后用多项式直接计算。优点是计算速度极快几乎不消耗Flash缺点是外推能力弱超出拟合范围后误差剧增。我自己的使用建议是测温范围窄且固定用三阶多项式拟合省Flash且精度高。测温范围宽或希望通用性更好用S-H公式加标定或者直接查表加线性插值。对精度要求不高、想最快跑通功能β公式就够。这三条路线我都在实际项目里用过。印象最深的是有个项目为了省Flash用四阶多项式拟合拟合范围内精度做到了±0.3℃但换了个探头批次后误差立刻扩大到±1.5℃说明拟合参数对器件个体很敏感最终我还是改回了标定查表方案。4. 实测标定与误差修正把采集精度从“能用”拉到“可信”4.1 标定的具体操作流程不管选哪种算法只要NTC来自常规生产批次不做标定就声称“精度±0.1℃”基本不现实。标定是提高精度的关键一环。我的做法是准备一个保温容器装水配合高精度的参考温度计和水银温度计或已校准的数字温度计将NTC探头和参考探头绑在一起放入水中。每取一个温度点等水温稳定后用万用表记录NTC阻值同时记录参考温度计读数。稳定判据是1分钟内NTC阻值变化不超过1Ω。标定点建议从低温到高温至少取5个点例如0℃、15℃、25℃、40℃、60℃。0℃可以用冰水混合物其它温度通过掺热水或冷水调节。每个点都等稳定后记录然后整理成一张表标定点参考温度℃NTC阻值kΩ10.232.6215.118.9325.010.0440.35.8560.12.9有了这组数据你可以做S-H三系数标定也可以直接填充查表法所需的R-T表或者用最小二乘拟合多项式。4.2 误差来源分析为什么算出来总差一点标定前先把可能导致误差的因素排查一遍。这张表是我反复整理过的误差源典型量级对策NTC个体离散25℃阻值和B值偏差±1%~±3%阻值约±0.5℃~±2℃实测标定自热效应0.1℃~1℃甚至更高降低工作电流、间歇采集ADC参考电压漂移按比例折算为温度偏差外部基准或使用比例测量采样时间不足读数偏低温度偏高增大采样时间到480周期长线引线电阻每欧姆约0.03℃~0.1℃短线、三线制滤波电容导致的建立时间同上缩小电容或加运放其中自热效应非常多见但经常被忽略。NTC在工作时自身有电流通过发热会导致阻值偏低测出的温度比实际高。以3.3V供电、R010k、NTC10k为例NTC上的功率约为0.27mW常见贴片NTC的耗散系数约2mW/℃自热温升约0.1℃基本可以忽略。但如果把R0换成了1k电流增大后NTC功耗约2.7mW温升可能到1℃以上这个误差在恒温箱验证时会非常刺眼。解决办法是尽量选阻值较大的NTC如100k或者采用间歇供电方式只在采集前的几十毫秒打开分压电源采集完立刻关闭把自热影响降到最低。4.3 标定后的静态误差修正即使做了标定计算出的温度与参考温度之间仍可能存在系统偏差。如果偏差呈近似线性趋势可以用一阶修正T_corrected k * T_measured b用两个标定点的实测温度和参考温度解出k和b即可。比如0℃时实测为-0.5℃60℃时实测为61.2℃那么k (60 - 0) / (61.2 - (-0.5)) ≈ 0.973b 0 - 0.973 * (-0.5) ≈ 0.486。实际使用效果是这条直线把整体偏移拉回来了。如果误差随温度呈弯曲趋势用一阶修正不够建议做分段线性修正把标定点之间的误差做成一个与温度相关的修正表按当前计算温度查表得到修正量。这个方法实现简单也是我在产品里用得最多的方法。修正完成后一定要做一次独立验证重新配置水浴温度在标定点之间选几个温度进行复测统计最大误差和平均误差。只有复测数据能稳定在目标精度范围内才能说这块板子达到了设计指标。4.4 一个快速排查问题的亲测小技巧最后分享一个我在调试多路NTC采集时常用的检验方法取两个同型号NTC插到同一个板子上的两个ADC通道让它们悬空处于同一环境温度看两个通道读数是否一致。如果两只探头读数相差超过0.3℃那问题多半不是NTC本身而是两个ADC通道之间或两路分压电路之间存在差异比如某一路的采样时间没配够、某一颗采样电阻精度不够。这个方法不需要恒温设备十几分钟就能把硬件问题基本定位清楚。NTC温度测试看起来是一件“搭个分压电路、读个ADC值”的小事实际上从电路设计到算法选型再到标定修正每个环节都会直接影响最终精度。很多时候精度上不去问题并不在算法而在更早的硬件和配置环节。先把硬件基础打好、把采样时间配好再谈软件算法你会发现自己项目的温控精度比预期高不少。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →