news 2026/9/1 8:58:32

STM32+LM2904+VDO NTC水温传感器采集电路设计详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32+LM2904+VDO NTC水温传感器采集电路设计详解

简介:这份资源面向嵌入式开发者与电子工程学习者,提供一套基于STM32F407的完整温度监测方案。方案结合LM2904运算放大器、分压电阻网络与VDO温度传感器,实现模拟信号采集、放大、ADC转换及串口输出,最终通过串口助手实时显示温度值。工程基于HAL库编写,代码结构清晰,适合需要掌握ADC单通道采集、信号调理电路设计及USART通信的初中级开发者参考。资源包共1135个文件,大小20.46MB,包含592个c源码文件、284个h头文件、78个s汇编文件、46个icf链接配置文件,以及uvprojx工程文件、ioc引脚配置、hex烧录文件、axf调试文件和map映射文件等,涵盖从源码编译到硬件调试的完整链路。已有255人学习使用,工程可直接导入STM32CubeMX生成的项目环境中查看引脚配置与初始化逻辑,便于二次开发和移植。 最近在调试一套车载水温采集板,朋友特意指定要用VDO温度传感器,这就把一套很经典的组合凑齐了:STM32F103 + HAL库 + LM2904双运放 + ADC采集 + VDO温度传感器。单独看每个器件都很常见,但真要把它们正确搭在一起,电路参数怎么算、ADC怎么配、温度怎么算出来,每一步都有不少坑。这篇就把整个电路设计和代码实现完整拆开讲一遍。

这套方案解决的是什么问题?VDO的NTC温度传感器输出的是电阻值变化,不能直接被单片机读取,需要先做分压转换和信号调理,再由STM32内置12位ADC采样,最后在软件里通过B值公式或者查表换算成真实温度。适合正在做车载仪表、水温/油温监控、工业设备温控的朋友参考,即使你不用VDO传感器,改用其他NTC一样能套用。

1. 方案选型与分析

1.1 VDO温度传感器输出的是什么信号

VDO是汽车电子领域非常常见的传感器品牌,尤其是发动机水温传感器、机油温度传感器、变速箱油温传感器,大量用的都是NTC热敏电阻方案。NTC的意思就是负温度系数,温度升高时电阻值下降,这和PT100正好相反。以常见的一款VDO水温传感器为例,25摄氏度时电阻可能是1kΩ,到了100摄氏度可能只剩180Ω左右,具体数值要看型号的数据手册,不同车型规格差异很大。

很多新手第一次接触这种传感器,第一反应就是“既然是个电阻,直接和固定电阻分压然后接ADC不就行了?”表面看确实是这样,但实际测温环境往往很恶劣。汽车发动机舱里线束很长,点火线圈、继电器、电机产生的电磁干扰非常强,直接分压出来的信号阻抗又高,ADC引脚本身的采样电容充放电行为会和源阻抗互相作用,导致采样结果飘忽不定。这时候就需要运放介入,做阻抗变换和信号幅度调理。

1.2 为什么用LM2904而不是LM358

LM2904和LM358在电路上几乎可以互换,很多初学者会觉得这两者没区别。其实LM2904的定位就是LM358的工业/汽车级版本,工作温度范围通常做到-40摄氏度到+125摄氏度,而普通商用级LM358一般是0到70摄氏度。上车的项目就要选LM2904,这也是标题里特别标注LM2904的原因。

但用LM2904有一个必须注意的点:它不是轨到轨运放。在3.3V单电源供电下,它的输出电压最高大约只能到电源电压减1V左右,也就是2.3V上下,负载电流越大这个掉得越多;输入共模电压范围也有限制,正相输入最高大约是VCC减1.5V,也就是1.8V左右。这就决定了后续电路设计时必须把信号工作区压在比较低的位置,不能在满量程3.3V附近做文章。

2. 硬件电路设计与计算

2.1 传感器分压网络设计

要让NTC的电阻变化变成电压变化,最基本的做法就是电阻分压。我采用的方式是上拉电阻接3.3V,NTC传感器一端接分压节点,另一端接地,分压节点作为信号输出,然后送入运放。这样温度升高时NTC电阻减小,分压节点电压降低,不过我这个方案里实际取的是反过来的思路。

更常见、更直观的接法是:NTC接在低侧,上拉电阻从3.3V接到分压节点P,分压点P再接到NTC到地。这样温度升高,NTC电阻下降,分压点电压也会降低。如果你想温度升高时ADC数值变大,就把NTC放在低侧,让分压节点电压从NTC两端取,V_in = 3.3V * R_NTC / (R_pullup + R_NTC)。在这个接法里,NTC两端电压就是分压点对地电压,温度升高时R_NTC减小,这个电压也减小,所以ADC读数会随温度升高而下降。想让读数随温度升高而上升,就把上拉电阻和NTC位置互换,NTC接3.3V,固定电阻在下拉到地,再从NTC和固定电阻之间取信号。我这次用的是固定电阻下拉、NTC接电源侧的方案,电压随温度升高而上升,逻辑上更直观。

举例:假设25摄氏度时NTC为1kΩ,100摄氏度时NTC为180Ω,下拉电阻R_pullup取2.2kΩ,VCC=3.3V。25摄氏度时V_in=3.31000/(2200+1000)=1.03V,100摄氏度时V_in=3.32200/(2200+180)?不对,这里要仔细算。如果用NTC在上、固定电阻在下,节点电压是固定电阻两端电压,那么V_in=VCCR_pullup/(R_NTC+R_pullup)。因此25摄氏度时V_in=3.32200/(1000+2200)=2.27V,100摄氏度时V_in=3.32200/(180+2200)=3.06V。这个范围对LM2904来说就太高了,输入共模范围超了,输出也不可行。所以这里其实应该改成NTC在下、固定电阻在上,确保信号电压在低区间。即:VCC->R_pullup->节点P->NTC->GND,节点P作为输出。25摄氏度时V_in=3.31000/(1000+2200)=1.03V,100摄氏度时V_in=3.3*180/(180+2200)=0.25V。这个0.25到1.03V的范围既在LM2904共模范围内,又比较低,方便后面放大。

所以实际方案要按这个顺序来:VCC接固定上拉电阻,节点P接NTC,再接GND,信号从P点取。上拉电阻选2.2kΩ是比较折中的,既保证分压曲线在测温区间内有足够斜率,又不至于让功耗过大。如果你手里的VDO传感器规格不同,比如25摄氏度下是500Ω,那上拉电阻就要相应调整,原则是让目标测温区间的电压落在0.2V到1.2V之间。

分压节点后面我加了一级RC滤波:一个1kΩ电阻串联再加100nF电容到地,截止频率算下来大约是1.6kHz。温度信号本身就是缓慢变化的,这个滤波既能滤掉高频干扰,又不会影响响应速度。RC时间常数约100µs,对于温度传感器来说完全够用。

2.2 LM2904放大电路设计

因为直接分压出来的信号只有0.25V到1.03V,12位ADC在3.3V参考下的分辨率是0.000805V/LSB,这段电压只对应大约969个码值,直接把测温分辨率压到0.1摄氏度以下,其实也够,但还是浪费了量程。所以用LM2904做个同相放大器,把信号拉伸到更合适的位置。

同相放大器的增益公式是G=1+Rf/Rg。我选用Rg=10kΩ,Rf=8.2kΩ,算下来G=1.82倍,输出范围就变成0.45V到1.87V左右。这个值是有讲究的,LM2904在3.3V供电下输出最多大约2.3V,我留了0.4V左右的余量,避免放大器先饱和了,后面ADC读满量程还在涨,那就完全失真了。

运放供电方面,LM2904的VCC直接接3.3V,和STM32同一路电源。芯片电源脚旁要放一个100nF陶瓷电容,建议再加一个10µF电解电容,尤其当板子上还有继电器或者电机驱动时,电源纹波会直接影响运放输出精度。运放输出到STM32的ADC引脚之间串一个100Ω电阻,这个电阻不参与分压,纯粹是保护作用,万一ADC引脚配置错或者带电插拔,能起到限流作用。

LM2904里面有两个运放,第二个运放如果暂时不用,一定不要让它悬空。我通常把同相输入端接地,反相输入端接输出,做成一个电压跟随器,这样既不会产生不必要的振荡,又能在将来需要扩展第二路信号时直接用。

2.3 与STM32 ADC的接口细节

STM32F103的ADC输入可以理解为一个采样电容加一个模拟开关,采样瞬间它需要从外部源抽取电荷。如果外部源阻抗太高,采样电容充不满,测出来的电压就会偏低。运放输出阻抗非常低,只有几十毫欧量级,所以加了跟随/放大之后相当于把传感器的“高阻”问题彻底解决,ADC采样精度由这个缓冲结构兜住了。

另一个细节是ADC参考电压。STM32F103C8T6的VREF+内部接到VDDA,也就是3.3V。如果3.3V电源精度不高,ADC的绝对精度也会受影响。实际项目中如果要求测温误差小于1摄氏度,最好给模拟部分单独用高精度LDO供电。

3. STM32 HAL库工程与ADC代码实现

3.1 CubeMX配置要点

打开STM32CubeMX,芯片选STM32F103C8T6,时钟配置为外部8MHz晶振,倍频到72MHz。在Analog菜单里开启ADC1,把通道0也就是PA0配置为模拟输入。

ADC参数建议这样设置:Scan Conversion Mode关闭,Continuous Conversion Mode开启,数据对齐方式选右对齐,采样时间选最大的239.5周期。采样时间这个很关键,F103的ADC工作在12MHz下,239.5周期算下来大约20µs一次采样,对付这种慢变温度信号绰绰有余,而且能最大限度降低源阻抗带来的误差。DMA设置里选Circular模式,数据宽度Half Word,这样DMA会自动把ADC结果刷新到内存变量里,不需要CPU介入。

USART也要开一个,用来打印温度数据方便调试。我习惯用USART1,115200/8N1。

生成工程之后,main函数里第一件事不是启动ADC,而是做ADC校准。STM32F1系列的ADC有一个内置校准功能,HAL库里对应的是HAL_ADCEx_Calibration_Start。不校准的话,ADC的零点误差可能有好几个LSB,温度上会误差好几度。

3.2 ADC采样与软件滤波

用DMA方式采样有一个很舒服的点:启动一次就可以一直跑,数据自动更新。代码如下:

uint16_t adc_raw = 0; uint32_t adc_sum = 0; uint16_t adc_buffer[16]; uint8_t adc_index = 0; int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_DMA_Init(); MX_ADC1_Init(); MX_USART1_UART_Init(); HAL_ADCEx_Calibration_Start(&hadc1); HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&adc_raw, 1); while (1) { // 用最新ADC值做滑动平均 adc_sum -= adc_buffer[adc_index]; adc_buffer[adc_index] = adc_raw; adc_sum += adc_buffer[adc_index]; adc_index = (adc_index + 1) % 16; uint16_t adc_filtered = adc_sum / 16; float voltage = (float)adc_filtered * 3.3f / 4095.0f; float temp_c = convert_voltage_to_temperature(voltage); char msg[64]; sprintf(msg, "ADC:%4d V:%.3f T:%.1f\r\n", adc_filtered, voltage, temp_c); HAL_UART_Transmit(&huart1, (uint8_t *)msg, strlen(msg), 100); HAL_Delay(100); } }

滑动平均是16点平均,每100ms读一次,等效时间常数1.6秒,温度读数会非常平滑。如果觉得响应太慢,可以改成8点或5点平均,看具体需求。

有一个坑必须提醒:在使用DMA连续模式时,不要在每次循环里重复调用HAL_ADC_Start_DMA。很多人第一次接触HAL库会犯这个错,在while里反复启动ADC,结果DMA传输错乱,读出来的数据一直是同一个值。配置成Continuous模式后,启动一次就行,DMA会自动持续搬运。

3.3 电压到温度的换算

现在ADC采集到的是电压值,要回到温度值还得再走两步:电压反推NTC电阻,电阻再查温度。电压反推电阻很简单,NTC在低侧的情况下:

float convert_voltage_to_resistance(float voltage) { if (voltage >= 3.29f) return 999999.0f; // 防止除零 float rt = 2200.0f * voltage / (3.3f - voltage); return rt; }

这里2200是上拉电阻2.2kΩ,如果换成其他阻值要同步修改。拿到电阻之后用B值公式算温度:

float convert_resistance_to_temperature(float resistance) { float T0 = 298.15f; // 25摄氏度对应的开尔文温度 float RT25 = 1000.0f; // 25摄氏度下的NTC电阻,以手中传感器实测为准 float B = 3900.0f; // 传感器的B值,查数据手册 float t = (float)(B * T0) / (B + T0 * logf(resistance / RT25)) - 273.15f; return t; }

B值公式在窄温区范围内精度足够,比如0到120摄氏度之间,误差一般可以控制在1摄氏度以内。如果你的传感器没有给B值,或者要求全温区精度更高,那就用查表法。先把温度-电阻表做成结构体数组,然后线性插值。查表法代码稍长,但逻辑直白,不容易出错。

我这里B值设的3900是常见NTC参数,VDO传感器的具体B值需要看手册,或者用恒温箱实测标定。批量做项目时,建议每批抽样校准一次,不然不同的传感器批次B值差异会直接体现成温度偏差。

4. 调试排查与实战经验

4.1 ADC读数恒定满量程或者恒为零

这是我最常被问到的问题。如果ADC读到4095附近,先别急着怀疑代码,用万用表测运放输出引脚电压。如果运放输出已经顶到2.3V以上,问题大概率在模拟前端:上拉电阻有没有虚焊?分压节点是否被拉高?如果是NTC开路,比如传感器线束断了,那么固定上拉电阻会把节点电压拉到接近3.3V,运放输出自然顶到轨。我遇到过一次是因为传感器插头氧化,接触电阻变大,读出来直接满量程,当时排查了很久才想到是机械连接问题。

ADC恒为零的情况也类似,先量分压节点电压。如果节点有电压但运放输出为零,检查运放电源有没有接反,或者第二个运放有没有干扰到第一个。再往后就是STM32的ADC引脚是不是真的配置成了模拟输入,被复用成了GPIO输出低电平也会把电压拉死。

4.2 温度读数跳动剧烈

跳变的根因通常是高频干扰或者采样时间不够。车载环境里点火线圈和雨刮电机工作时,会产生很强的宽带噪声,虽然加了RC滤波,但如果布线时信号线走得太靠近电源线,还是会有串扰。这种情况下硬件上要把信号线换成双绞线,或者加屏蔽层;软件上增大采样时间到最大值,同时把滑动平均窗口加长。

还有一种情况是ADC的采样周期确实不够长。F103的ADC采样时间选项有1.5、7.5、13.5、28.5、41.5、71.5、239.5周期,如果你之前为了追求采样速率用7.5周期,源阻抗稍微大一点,读数就是会周期性偏小,看起来像跳动。直接选239.5周期,我实测同样硬件情况下码值波动从三四十个LSB降到五六个LSB。

4.3 DMA回调不触发或者程序卡死

HAL库的DMA模式依赖中断完成标志位的翻转。如果回调函数不触发,先检查两处:NVIC里DMA中断是否被勾选了,DMA中断优先级是否太低被其他中断饿死。另一个容易被忽略的问题是你的DMA缓冲区地址。STM32 DMA要求地址按半字对齐,如果定义的是uint8_t数组或者结构体里偏移了奇数地址,DMA传输时会进入HardFault。

如果你用回调函数HAL_ADC_ConvCpltCallback做数据搬运,不要在回调里做浮点运算或者打印,回调环境适合做轻量级工作,比如置标志位,真正的计算放到主循环。我当时调试时在回调里直接调printf,结果一圈下来时间全耗在串口上,数据直接卡死,后面改成主循环读标志位才算干净。

4.4 LM2904输出异常的非线性表现

LM2904最典型的症状是:输入电压明明在按曲线变化,但输出电压到了某个点之后不再跟着变,就像被人按住了。两种常见情况,一是输入共模电压超过了VCC-1.5V,内部输入级工作点不对,输出就失效了;二是输出电压逼近轨到轨上限,输出级进入饱和区域。解决办法就一句话,把信号工作区往下压。比如把上拉电阻从2.2kΩ加大到4.7kΩ,让25摄氏度时节点电压从1.03V降到0.6V左右,输出范围变成0.2V到1.2V,LM2904就很舒服了。如果这样动态范围又被压缩,就调整运放增益,把增益从1.82加大到2.5倍,仍然能保证不饱和。

5. 方案扩展与适用范围

5.1 扩展成多路温度采集

LM2904是双运放,也就是说天然支持两路信号的调理。第二个运放可以接成相同的同相放大器,接入另一只VDO温度传感器,然后STM32的ADC1开启扫描模式,通道0和通道1轮流转换,用DMA存储两路结果。这样一套硬件就能同时测水温、油温,在需要多点取样的设备上非常实用。要注意双通道扫描模式下,DMA的缓冲区长度要设为2,并且每次读取数据时按缓冲区下标区分通道。

5.2 把温度数据接入总线

温度采集的最终目的大多不是自己在板子上看,而是上报给仪表盘或者上位机。最省事的方式是串口发送,就像示例代码那样。如果要上车载总线,把UART那部分逻辑替换成CAN发送即可,STM32F103的bxCAN外设性能应对这种低频温度数据绰绰有余。温度值可以放大10倍后作为整数发送,比如25.3摄氏度发253,避免浮点协议解析的麻烦。对于工业场景,也可以换成RS485加Modbus协议,改动量都不大。

5.3 这套方案到底适合用在哪

从实际应用角度讲,这类“NTC+运放+MCU ADC”的组合是成本最低、可靠度又够高的测温通道标准做法。它不仅能测水温和机油温度,农机设备的液压油温、发电机组冷却水温、工业减速机轴承温度,凡是温度变化慢、环境干扰大的场景都能用。LM2904和STM32F103这两颗料的供货量非常大,成本控制很理想,后期的维护替换也容易。如果你在车内空间有12V供电,只要把测量系统的3.3V电源做好隔离和DC-DC降压,这套方案基本就能直接上车。

最后分享一个我自己的小习惯。整套系统搭好后,先用一个精密电阻箱模拟传感器,在几个标定温度点分别给到电阻值,然后看软件输出的温度误差,而不是直接拿着烙铁去试传感器。这样可以用很低的成本完成全温区标定,也方便复现问题。等电阻箱测试全部通过后,再换上真传感器做整机测试,这样排错效率高很多。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/1 8:57:41

三江源国家公园界线矢量数据ZIP包处理全攻略:从解压到QGIS上图

简介:三江源国家公园界线矢量数据集是一套面向地理信息系统从业者、生态科研人员及自然资源管理部门的矢量格式地理数据包,内容涵盖长江源、黄河源、澜沧江源园区及国家公园边界,可支撑保护区与周边用地关系分析、生态保护红线划定、环境变化…

作者头像 李华
网站建设 2026/9/1 8:57:34

qBittorrent增强版v4.3.4.11:集成搜索引擎与便携部署实战

简介:qBittorrent Enhanced Edition v4.3.4.11 是集成搜索引擎的免费开源 BitTorrent 客户端,适合经常使用磁力链接与 BT 种子下载、希望借助搜索插件快速发现资源的用户。它在原生版本基础上增强了搜索体验,支持订阅 Tracker 服务器列表、BT…

作者头像 李华
网站建设 2026/9/1 8:57:17

从在线近红外到AI选型:结果导向的付费模式与工程实践

如果你最近在帮团队选 AI 工具或者接大模型 API,应该会明显感觉到一个变化:选型讨论的重心,已经从“模型有多少亿参数”“用了什么架构”变成了“同样的预算,产出的结果能不能直接用”。这个变化,和工业领域里已经运行…

作者头像 李华
网站建设 2026/9/1 8:54:16

压缩感知算法实现:从源码解压到OMP重建实战

简介:本资源是一套面向信号处理、机器学习及电子信息类课程学习者与科研初学者的压缩感知(CS)核心算法实践包,聚焦于稀疏信号重建这一关键问题,助力理解奈奎斯特采样之外的高效采集范式。压缩包共7个MATLAB源文件&…

作者头像 李华
网站建设 2026/9/1 8:54:13

Cesium动态路径导航线实战:从Polyline到自定义材质

简介:面向Cesium开发者的路径导航线源码包,解决在三维地球场景中快速绘制移动轨迹与导航路线的需求。核心代码围绕PathLinePrimitive类实现,通过初始化一组经纬度坐标点并传入构造方法,即可生成可视化路径,同时支持调整…

作者头像 李华
网站建设 2026/9/1 8:53:13

无人机航拍目标检测数据集drone-data-1发布:含YOLO标注与小目标优化

简介:本资源是面向计算机视觉算法工程师与无人机应用开发者的目标检测专用数据集,聚焦小目标识别这一核心难点,适用于搜索救援、低空监管、集群避障等真实UAV场景的模型训练与验证。压缩包共15501个文件,含5167张JPG图像及严格对齐…

作者头像 李华