利用PIC单片机看门狗与二极管实现超低成本温度测量方案

利用PIC单片机看门狗与二极管实现超低成本温度测量方案 1. 项目概述与核心价值最近在做一个对成本极其敏感的小型温控设备核心需求是监测环境温度但留给硬件的预算非常有限。传统的方案要么需要独立的温度传感器芯片要么得用上单片机的高精度ADC模块前者增加物料成本后者则对MCU的选型提出了更高要求功耗也可能上去。在反复权衡后我决定尝试一个非常规的思路利用PIC单片机自带的看门狗定时器配合一个极其简单的二极管测温电路来实现超低成本的温度测量。这个方案的核心价值就两个字便宜。它几乎不增加任何额外的核心硬件成本主要利用MCU已有的资源看门狗定时器、一个GPIO和一个ADC通道甚至ADC都可以用更简单的方法替代特别适合那些对精度要求不是极端苛刻例如±2°C以内可接受但需要大规模部署或对单件成本锱铢必较的应用场景比如低成本的环境温度记录仪、恒温箱的辅助监测、或是消费电子产品的内部温度保护等。2. 方案核心原理与可行性分析2.1 看门狗定时器的“第二春”在大多数工程师眼里看门狗定时器就是个“保险丝”它的唯一职责就是在程序跑飞后复位系统。但如果我们深入看看PIC单片机尤其是中低档系列如PIC16F系列的看门狗定时器模块手册会发现它其实是一个独立的、由内部RC振荡器驱动的定时器。这个RC振荡器的频率虽然不精准典型值31kHz但受电压、温度影响变化范围可能从20kHz到50kHz但它有一个有趣的特性其振荡频率会随温度变化而改变。这就是我们方案的物理基础。我们不再将WDT视为一个简单的复位发生器而是把它当作一个对温度敏感的振荡信号源。通过测量WDT超时的时间间隔我们就能间接感知芯片结温的变化。当然直接用MCU自身的温度来反映环境温度是不准的因为MCU工作时自身会发热。这就需要我们引入第二个温度传感元件——一个普通的硅二极管。2.2 二极管测温与“时间-温度”转换硅二极管的正向压降Vf具有大约-2mV/°C的温度系数。这是一个非常稳定且可重复的物理特性。我们可以搭建一个简单的电路让二极管的Vf去影响一个RC电路的充电时间或者更直接地利用它来调制一个振荡器的频率。本方案采用一种更直观的方法将二极管作为温度传感器利用其Vf变化通过一个简单的比较器电路甚至可以用MCU内置的比较器模块产生一个脉宽随温度变化的信号PWM-like。然后我们利用看门狗定时器的周期性溢出作为“时间标尺”去测量这个脉宽。具体来说信号生成设计一个由二极管、电阻、电容构成的弛张振荡电路其输出方波的频率或占空比与二极管Vf即温度相关。或者用MCU的一个GPIO配合内部比较器模拟一个单稳态电路输出脉冲宽度与Vf成正比。时间测量启用看门狗定时器并将其超时期限设置为一个已知的固定值例如18ms。在待测脉冲的高电平期间我们不断“喂狗”清除WDT计数器防止系统复位。一旦脉冲结束我们停止喂狗。温度计算系统会在WDT下一次超时时复位。通过监测系统是否发生复位以及结合代码中记录“喂狗”的次数我们可以计算出待测脉冲的精确宽度。这个脉冲宽度与二极管的Vf有确定的函数关系而Vf与温度成近似线性关系。因此最终我们得到了一个与温度相关的“计数值”。注意这里存在一个关键技巧。我们不能让系统真的无限复位。实际操作中我们会在主循环里快速检测脉冲状态并用一个软件计数器记录在脉冲高电平期间成功“喂狗”的次数。脉冲结束后我们立即保存这个计数值然后主动进行一次正常的“喂狗”防止意外复位。这样系统并不会真正复位我们却借助WDT的精准相对定时能力完成了一次高分辨率的时间测量。2.3 方案优势与挑战优势成本极低核心传感器仅需一个几分钱的通用二极管如1N4148几个电阻电容。无需专用温度传感器IC如DS18B20, LM75。资源节省可以不占用宝贵的高精度ADC模块如果MCU有的话或者用于其他更关键的信号采集。对于没有ADC的极低成本MCU此方案是实现温度感知的唯一可行路径。功耗潜力整个测量电路可以在大部分时间断电仅在测量时由MCU引脚上电结合WDT唤醒功能可实现极低平均功耗。挑战与应对精度与校准二极管Vf有离散性且WDT的RC振荡器本身也受温度和电压影响。这决定了它无法实现高精度测量。必须通过两点校准来补偿在生产测试环节记录下在已知两个温度点如0°C和50°C的测量计数值然后在软件中建立线性插值公式。对于更高要求可以在软件中存储一个简单的分段线性补偿表。非线性二极管Vf与温度并非完全线性尤其是在极端温度下。对于0-70°C的常规范围线性近似带来的误差通常在可接受范围内。如果范围更宽则需要更复杂的拟合或查表。MCU自热测量时MCU运行会产生热量影响本地温度。对策是快速测量将整个采样、计算过程压缩在极短时间内完成例如几十毫秒然后MCU进入休眠让芯片温度与环境温度重新平衡。3. 硬件电路设计与核心器件选型3.1 二极管测温电路设计这里提供两种经过验证的实用电路。方案A基于恒流源与积分电路的脉宽调制这个方案利用MCU内置的比较器和定时器实现高分辨率的脉宽输出。Vdd (3.3V/5V) | R1 (10k) | ----- 到MCU比较器同相输入端 (CxIN) | C1 (100nF) - 积分电容 | ----- 到MCU GPIO (控制充电) | Q1 (小信号NPN如2N3904) | \ | \ | \ R2 (1k) D1 (1N4148传感器) | | GND GND工作原理MCU先将控制GPIO置低使Q1截止电容C1通过R1缓慢充电比较器输出为低。启动测量时MCU将控制GPIO置高Q1导通为二极管D1提供恒定电流由Vdd、R2和Q1的Vce(sat)决定约几mA。同时电容C1通过D1、Q1快速放电。放电至比较器反相端电压可设为固定阈值如0.6V时比较器输出翻转。关键点电容C1的放电时间常数由C1 * 二极管动态电阻决定而二极管动态电阻与其Vf相关Vf又随温度变化。因此从放电开始到比较器翻转的时间即脉冲宽度是温度的函数。MCU利用这个脉冲去控制“喂狗”逻辑。方案B简易弛张振荡电路更简单的方案直接产生频率受温度调制的方波。Vdd | R3 (100k - 1M) | ----- 到MCU GPIO (输入捕获) | C2 (10nF - 100nF) | ----- D2 (1N4148) | GND这个电路本质上是一个非稳态多谐振荡器的简化版其振荡周期T ≈ k * R3 * C2 * Vf / (Vdd - Vf)其中k是常数。温度变化引起Vf变化从而改变周期T。MCU可以用输入捕获功能测量周期但本方案中我们依然用WDT来测量高电平或低电平的持续时间。3.2 核心器件选型考量PIC单片机首选PIC16F183xx系列或PIC16F152xx系列。它们价格低廉且具备丰富的外设带窗口功能的增强型WDT可产生中断而非直接复位、模拟比较器、多个定时器。特别是增强型WDT允许我们在不引起系统复位的情况下获取超时事件极大地简化了软件设计。如果成本要压到极限PIC12F1501这类8引脚单片机也完全可行它具备比较器和定时器。二极管通用开关二极管1N4148是最佳选择。它便宜、易得、性能一致性好。避免使用发光二极管或肖特基二极管它们的温度特性不同。电阻电容积分电容C1或定时电容C2建议选用COG/NP0材质的陶瓷电容这类电容的容值随温度变化极小能保证测量基准的稳定。电阻选用普通的5%精度碳膜电阻即可因为电路依赖校准来消除绝对误差。电源电源稳定性至关重要。二极管Vf和WDT振荡频率都受电源电压影响。如果系统电源是电池电压会缓慢下降必须进行电源电压补偿。一个简单的办法是测量Vdd通过ADC或利用带隙基准电压在温度计算公式中引入电压修正项。4. 软件实现与关键代码解析软件部分是整个方案的灵魂需要精细地协调WDT、比较器/定时器中断以及主循环的状态。4.1 系统初始化与配置// 以PIC16F18323为例使用XC8编译器 #include xc.h // 配置位设置启用WDT设置较长的超时周期如1秒便于调试 #pragma config WDTE ON // 看门狗开启 #pragma config WDPSCLK LPRC // WDT时钟源为低功耗RC (31kHz) #pragma config WDTPS 1:32768 // 分频比决定超时时间 void System_Init(void) { // 1. 配置振荡器 OSCCON 0xFC; // 使用内部32MHz HF并分频到所需频率 // 2. 配置比较器如果使用方案A CM1CON0 0x00; CM1CON0bits.C1ON 1; // 使能比较器1 CM1CON0bits.C1POL 0; // 输出不反相 CM1CON0bits.C1SP 1; // 高速模式 CM1CON1 0x00; CM1CON1bits.C1HYS 0; // 无迟滞或根据需要加一点 CM1CON1bits.C1SYNC 0; // 输出异步响应快 // 设置比较器输入C1IN接电容电压C1IN-接内部固定参考电压(如0.6V) CM1CON1bits.C1NCH 0b010; // C1IN-接内部FVR缓冲器输出 CM1CON1bits.C1PCH 0b001; // C1IN接外部引脚 // 3. 配置FVR固定参考电压为比较器提供基准 FVRCON 0x00; FVRCONbits.ADFVR 0b01; // FVR给ADC提供2.048V参考如果用到ADC测电压 FVRCONbits.CDAFVR 0b01; // FVR给比较器提供1.024V参考 FVRCONbits.FVREN 1; // 使能FVR while(!FVRCONbits.FVRRDY); // 等待FVR稳定 // 4. 配置Timer1用于高精度时间戳辅助校准或备用方案 T1CON 0x00; T1CONbits.TMR1CS 0b00; // 时钟源为Fosc/4 T1CONbits.T1CKPS 0b00; // 1:1预分频 T1CONbits.TMR1ON 0; // 先关闭 // 5. 配置I/O引脚 TRISAbits.TRISA2 0; // RA2作为控制GPIO输出方案A放电控制 TRISAbits.TRISA3 1; // RA3作为比较器输出输入 ANSELAbits.ANSA3 1; // 使能RA3的模拟功能 // 6. 初始化变量 wdt_pulse_count 0; temperature_raw 0; measurement_done_flag 0; }4.2 核心测量流程与“喂狗”策略这是最关键的逻辑。我们利用WDT的周期性溢出作为“时钟节拍”。volatile unsigned int wdt_pulse_count 0; volatile bit measurement_active 0; volatile bit measurement_done_flag 0; unsigned int temperature_raw 0; void Start_Temperature_Measurement(void) { // 1. 准备阶段 CLRWDT(); // 清空WDT计数器防止立即复位 measurement_active 1; measurement_done_flag 0; wdt_pulse_count 0; // 2. 启动温度相关脉冲以方案A为例 LATAbits.LATA2 1; // 控制管脚拉高开始对电容放电/启动振荡 CM1CON0bits.C1ON 1; // 确保比较器开启 // 3. 核心测量循环在脉冲高电平期间持续“喂狗”并计数 while(PORTAbits.RA3 1) { // 假设比较器输出高电平为待测脉冲 CLRWDT(); // 每次执行CLRWDTWDT计数器清零重新开始计时 wdt_pulse_count; // 记录一次“喂狗”即一个WDT周期 // 此处可以加入短暂延时或执行其他低优先级任务但必须保证循环速度远快于WDT超时时间。 // 例如如果WDT超时设为18ms这个循环必须在18ms内至少执行一次CLRWDT。 } // 4. 脉冲结束保存结果 temperature_raw wdt_pulse_count; // 脉冲宽度正比于wdt_pulse_count measurement_active 0; measurement_done_flag 1; // 5. 安全退出确保不会因为退出测量循环而意外复位 LATAbits.LATA2 0; // 停止放电 CLRWDT(); // 最后再喂一次狗确保安全 // 可以在这里让MCU进入休眠等待下一次测量 }关键点剖析CLRWDT()指令是核心。它重置WDT计数器。只要在WDT超时前执行该指令系统就不会复位。wdt_pulse_count变量记录的是在待测脉冲有效期内WDT被成功清零的次数。这个次数乘以WDT的溢出周期例如18ms就是脉冲的宽度。循环速度必须确保while循环的执行时间远小于WDT超时时间。如果WDT是18ms那么循环体CLRWDT(); wdt_pulse_count;必须在十几毫秒内完成一次。这通常不是问题因为单片机指令速度很快。中断处理如果系统有其他中断必须确保中断服务程序执行时间也很短或者在中断里也执行CLRWDT()防止在测量期间因处理中断导致WDT超时复位。4.3 温度计算与校准算法得到temperature_raw后需要将其转换为实际温度值。// 两点校准参数需要在生产测试环节写入可存储于EEPROM typedef struct { int16_t cal_temp_low; // 低温校准点温度如 0°C int16_t cal_temp_high; // 高温校准点温度如 50°C uint16_t cal_raw_low; // 在cal_temp_low时测得的raw值 uint16_t cal_raw_high; // 在cal_temp_high时测得的raw值 } CALIBRATION_DATA; CALIBRATION_DATA calib; int16_t Calculate_Temperature(uint16_t raw_value) { int32_t temp; // 使用32位防止计算溢出 // 线性插值公式: T T_low ( (T_high - T_low) * (Raw - Raw_low) ) / (Raw_high - Raw_low) // 为了减少浮点运算先做乘法再做除法。 temp (int32_t)calib.cal_temp_low * 100; // 转换为0.01°C分辨率进行计算 temp ((int32_t)(calib.cal_temp_high - calib.cal_temp_low) * 100 * (int32_t)(raw_value - calib.cal_raw_low)) / (calib.cal_raw_high - calib.cal_raw_low); return (int16_t)(temp / 100); // 返回整数温度值 } // 更高级的查表法用于非线性补偿 int16_t Calculate_Temperature_LUT(uint16_t raw_value) { static const int16_t temp_lut[] { /* ... 预先计算好的温度值数组 ... */ }; static const uint16_t raw_lut[] { /* ... 对应的raw值数组按升序排列 ... */ }; uint8_t i; // 边界检查 if (raw_value raw_lut[0]) return temp_lut[0]; if (raw_value raw_lut[LUT_SIZE-1]) return temp_lut[LUT_SIZE-1]; // 线性插值查表 for (i 0; i LUT_SIZE - 1; i) { if (raw_value raw_lut[i] raw_value raw_lut[i1]) { return temp_lut[i] ((temp_lut[i1] - temp_lut[i]) * (raw_value - raw_lut[i])) / (raw_lut[i1] - raw_lut[i]); } } return 0; // 理论上不会执行到这里 }5. 精度优化、误差分析与实测调校5.1 主要误差来源及抑制措施WDT时钟源误差内部LPRC的初始精度和温漂是最大误差源。对策依赖系统级的两点校准。校准过程必须在稳定的、已知的环境温度下进行且最好覆盖产品预期的工作温度范围的两端。电源电压波动Vdd变化会影响二极管的工作点、比较器阈值以及LPRC频率。对策使用低压差线性稳压器LDO为MCU和传感器电路供电。实时电压补偿定期或在每次温度测量前测量Vdd。可以用ADC测量内部带隙参考电压相对于Vdd的比例反推出Vdd值。然后在温度计算公式中加入一个与Vdd相关的修正因子。// 简化的电压补偿示例 #define VDD_NOMINAL 3300 // 3.3V in mV uint16_t Measure_VDD(void) { // 配置ADC测量内部固定参考电压(如FVR2.048V)在Vdd下的ADC值 // VDD_mV (2048 * ADC_FULL_SCALE) / ADC_Reading // 返回VDD的毫伏值 } int16_t Calculate_Temperature_Compensated(uint16_t raw_value, uint16_t vdd_mv) { int32_t temp Calculate_Temperature(raw_value); // 简单的线性补偿假设Vdd变化1%温度读数漂移0.5% int32_t compensation (temp * (vdd_mv - VDD_NOMINAL)) / (VDD_NOMINAL * 200); return (int16_t)(temp compensation); }二极管自热测量电流会导致二极管发热。对策使用脉冲式测量。仅在极短的时间内如1ms施加测量电流然后立即关闭让二极管冷却。这要求MCU能快速完成一次采样循环。噪声干扰传感器电路可能引入噪声。对策在二极管两端并联一个小电容如100pF滤除高频噪声。软件上采用多次采样取中值或平均值。可以在一次测量中快速循环采样多次剔除明显异常值后取平均。5.2 实测调校步骤搭建环境准备一个高精度的恒温箱或冰水混合物与热水作为两个校准点一个经过计量的高精度温度计作为参考。写入初始代码代码中先不包含校准参数而是将测量到的raw_value通过串口或其他方式实时输出。两点校准将整个设备放入低温环境如0°C等待温度充分稳定至少30分钟。记录此时输出的raw_value作为cal_raw_low。将设备放入高温环境如50°C同样等待稳定后记录cal_raw_high。将这两个值以及对应的已知温度cal_temp_low和cal_temp_high写入到MCU的EEPROM或Flash中。验证与多点测试在多个其他温度点如10°C, 25°C, 40°C测试观察计算出的温度与参考温度计的差值。如果误差曲线呈现规律性如S型可以考虑使用三点校准或查表法进行非线性补偿。长期稳定性测试让设备在常温下连续运行数天观察温度读数的漂移情况。漂移主要来自元件老化对于低成本应用通常可以接受。6. 常见问题排查与实战心得6.1 问题速查表现象可能原因排查步骤与解决方案测量值完全不变1. 传感器电路未工作。2. 测量循环未正确进入或退出。3. WDT配置错误超时时间极短。1. 用万用表检查二极管两端电压在测量时应有变化。2. 调试检查measurement_active标志和while循环条件。3. 检查配置位WDTPS分频比设置一个较长的超时时间如1秒便于调试。测量值跳动剧烈噪声大1. 电源噪声大。2. 传感器电路阻抗过高易受干扰。3. 软件未做滤波。1. 检查电源纹波在Vdd和GND间加退耦电容10uF电解100nF陶瓷。2. 检查方案A中的R1是否过大适当减小以降低输出阻抗。3. 在软件中实现滑动平均滤波或中值滤波。温度读数整体偏高或偏低1. 校准参数错误。2. 电源电压与校准时的电压不同。3. 二极管型号或批次不一致。1. 重新进行两点校准。2. 启用并验证电源电压补偿功能。3. 确保生产中使用同一品牌、型号的二极管。系统偶尔无故复位1. 测量循环或中断服务程序执行时间过长导致错过CLRWDT。2. 其他任务阻塞了主循环。1. 优化代码确保最坏情况下CLRWDT的执行间隔小于WDT超时时间的70%。2. 如果使用中断在中断服务程序开头也加入CLRWDT()。低温下测量不准确1. 二极管在低温下特性非线性加剧。2. 比较器在低输入电压下响应变慢。1. 采用查表法进行非线性补偿。2. 检查比较器配置确保在低温下仍有足够的响应速度可以适当增加一点迟滞C1HYS。6.2 实战心得与技巧“喂狗”时机的黄金法则不要把CLRWDT()放在复杂的、执行时间不确定的函数里。最好放在主循环或一个高频定时器中断的最顶端确保它像心跳一样规律。利用增强型WDT中断如果使用的PIC单片机支持窗口看门狗或WDT中断如PIC16F18323的WDTCON寄存器一定要用起来这允许你在WDT即将溢出时进入中断保存关键数据然后进行安全复位或处理比直接硬件复位友好得多。动态调整WDT超时在正常运行时可以设置较长的WDT超时如1秒。在进入关键的测量循环时可以通过软件临时修改WDTCON寄存器将超时时间缩短如18ms这样可以提高时间测量的分辨率。测量结束后再恢复。休眠模式下的测量为了极致省电可以配置WDT在休眠时继续工作。MCU大部分时间休眠由WDT周期性唤醒如每2秒。唤醒后MCU快速上电传感器电路执行一次测量计算温度存储或发送数据然后再次进入休眠。这样平均电流可以降到10微安以下。二极管选材的一致性比绝对性能更重要批量生产时不要混用不同批次的1N4148。同一批次的二极管其Vf-温度曲线的一致性通常很好这能极大降低逐个校准的成本只需做一次系统性的两点校准然后将参数应用于整批产品即可。这个方案的精髓在于“变废为宝”将看似无关的看门狗定时器转化为一个有用的测量工具。它要求开发者对MCU外设有深入的理解并且愿意在软件上多下功夫来弥补硬件的不足。经过精心设计和校准这个低成本方案完全可以在许多对成本敏感的应用中替代那些更昂贵的传统温度传感器方案。