STM32F0与PJ85718DM单总线实现本地远程温度采集 干嵌入式开发这么多年温度测量这种活计看着简单做起来全是细节。尤其是暖通空调这种场景控制器往往装在电气盒里传感器却可能在风道、盘管或者室外机组上距离从几十厘米到几十米不等旁边还蹲着变频器、压缩机和风机这类干扰源。这段时间我搭了一套温控监测电路主控用了一颗 STM32F042C6温度传感器用的 PJ85718DM一根单总线同时接本地和远程两个传感器实现了本地温度和远程温度的同时采集。项目做下来踩了不少坑今天把这些经验整理出来给打算做类似方案的伙计们一个参考。1. 项目拆解嵌入式与 HVAC 场景下的温度监测需求1.1 本地温度和远程温度到底指什么先说清楚一个容易含糊的点标题里的“本地温度”和“远程温度”在暖通空调里可不是随便叫叫的。很多温控器出厂时只有一个传感器装在机器内部测的是机箱内空气温度这就是最常见的“本地温度”。但真正的现场需求往往不止一个点回风口温度、出风口温度、盘管表面温度、室外环境温度这些测量点离控制器有远有近传感器放在设备深处或者风道里就是“远程温度”。我这套方案里本地传感器直接焊接在主控板上紧挨着 MCU用于监测控制盒内温度和作为系统自身状态参考远程传感器通过接线端子引出放到实际需要控温的位置比如空调风道或者换热器附近。两个传感器型号一致都是 PJ85718DM走的同一条单总线但用不同的 ROM 地址区分。这里的“本地”和“远程”是物理位置上的区分不是电气定义上的区分固件层面必须靠传感器唯一编码来识别这一点后面会详细说。为什么本地温度也值得单独测很多人觉得机箱内温度不重要其实不然。控制器电路板本身会发热电源模块、驱动管、MCU 都是热源。如果本地温度过高说明散热风道堵了或者环境温度超标这本身就是一条重要告警。我在样机上把本地传感器放在 MCU 旁边实测板温比环境温度高 3 到 5℃这个数据后来被用来做温度补偿对整机寿命判断很有用。1.2 为什么放弃模拟传感器而选数字单总线早期很多暖通控制器用 NTC 热敏电阻因为便宜。但模拟方案在“本地 远程”这种场景下非常难受每路传感器都要占用一个 ADC 引脚还要配分压电阻、滤波电容生产时还得逐路做校准。最重要的是远程传感器一旦拉出两三米线线阻、接插件接触电阻、现场干扰都会直接影响采样电压温度值飘来飘去最后做出来的产品良品率很难看。PJ85718DM 这类数字单总线传感器就完全不同。它内部自带 ADC 和校准输出直接就是数字温度主控不用关心模拟电压换算线路上传的是数字脉冲抗干扰能力比模拟信号强很多。它的协议是单总线一根数据线就能挂多个传感器每个传感器有 64 位唯一 ROM 编码天然支持多点采集。对于本地一个、远程一个甚至未来要扩展成八个、十个测温点硬件上都不需要再增加引脚。I2C 传感器我也考虑过。I2C 在板内通信很好用但两条线加上电源和地远程线缆至少要四根而且 I2C 对总线电容和线长敏感超过一米就容易出现时序问题。单总线只有一根数据线加上电源和地总共三根线线长长一点影响也小非常适合从控制器引出到风道、盘管这种物理距离较远的场景。虽然单总线速度很慢但温度本身是缓变信号一秒更新一次绰绰有余慢速换抗干扰能力完全值当。2. 器件选型STM32F042C6 与 PJ85718DM 的组合逻辑2.1 STM32F042C6 有哪些点正好踩中需求STM32F042C6 是 ST 的 Cortex-M0 内核 MCU主频最高 48MHzFlash 32KBSRAM 6KB封装是 LQFP48工作温度范围 -40℃ 到 85℃。这颗芯片在暖通控制场景里非常合适主频不高但足够跑温度采集、通信和控制算法Flash 不大但放一套单总线驱动、温度处理、Modbus/CAN 协议栈绰绰有余M0 内核功耗低整机温升小这对摸机箱温度监控反而有利。外设方面这颗 MCU 不但有 USART、SPI、I2C 和 12 位 ADC还有 CAN 控制器和 USB 设备控制器。暖通空调里很多风机盘管、冷热源机组之间的通信用的就是 CAN 或者 Modbus。我最后选了 CAN 作为对外上报温度数据的通道因为 CAN 总线在工业现场的抗干扰能力和多节点组网能力都更好STM32F042C6 直接带 CAN 控制器只需要外接一个 CAN 收发器就行。这颗 MCU 还有一个容易被忽略的优势它的 GPIO 大多可以容忍任意配置成开漏模式这对单总线操作非常关键。单总线要求主机引脚既能拉低总线又能释放总线让外部上拉电阻把电平拉高开漏输出正好就是干这个的。很多老款 8 位机虽然也能做但引脚模式切换麻烦或者内部上拉电阻太小影响时序STM32F0 系列用起来顺手得多。2.2 PJ85718DM 的角色与实测表现PJ85718DM 是一颗单总线数字温度传感器指令集和时序与常见的 DS18B20 兼容这也是我敢直接拿过来用的原因。它有三个引脚VCC、GND 和 DQ供电范围比较宽标称工作范围和精度覆盖了暖通空调常用区间在风道、盘管这种温度场景里完全是杀鸡用牛刀。需要说明的是这颗传感器出厂时内部已经有校准数据接上就能用不需要每个传感器单独做温度校准这是数字传感器最大的省心之处。你只需要按单总线协议发起温度转换等一段时间再把暂存器里的原始值读出来乘以分辨率系数就能得到温度值。我在样机上实测本地传感器和远程传感器在恒温环境下读数差距在 0.2℃ 以内对于 HVAC 控制来说已经完全够用。这颗传感器的功耗不高正常温度转换时电流在毫安级待机时几乎不耗电。我的系统里用了低功耗设计多数时间让 MCU 进入 STOP 模式只有定时唤醒做温度采集和上报整体平均功耗控制得很好。如果你对功耗更敏感还可以在转换完成前让 MCU 睡一觉等时间到再醒。2.3 选型时容易忽略的几个坑第一个坑是上拉电平。PJ85718DM 虽然可以用 3V 到 5.5V 供电但 STM32F042C6 的 GPIO 不等于 5V 容忍你在 5V 系统下用上拉到 5V 的单总线MCU 引脚很可能会被反向电流搞坏。我们的板子统一用 3.3V 供电单总线上拉电阻也接到 3.3V这样 MCU 和传感器都在同一电压域最稳妥。如果你想用 5V 传感器中间必须加电平转换不要把 5V 直接怼到 MCU 引脚上。第二个坑是 PA11 和 PA12 的复用冲突。STM32F042C6 的 CAN 控制器引脚是 PA11 和 PA12但 USB 控制器也用这两个引脚两者不能同时使用。我在规划引脚时一开始想“反正 CAN 和 USB 都有外设全都配上”后来仔细看数据手册才发现冲突只能二选一。最后选了 CAN 作为上行通信口USB 就没法用了。设计定点前一定要把外设引脚冲突排查一遍别等画完板再改。第三个坑是 Flash 和 RAM 浪费。我曾经在一个类似项目里用了带完整实时操作系统的方案结果为了跑系统把 RAM 吃掉了大半温度采集这种任务用裸机轮询或者简单调度就足够了STM32F042C6 的 6KB RAM 根本不用省着用。过于复杂的软件架构在这个项目里只会增加调试成本。3. 硬件设计把本地和远程传感器接在同一根总线上3.1 引脚规划与最小系统我用的引脚分配是这样安排的单总线数据引脚用 PB5配置为开漏输出外部接上拉电阻到 3.3V调试串口用 USART2 的 PA2 和 PA3对外通信选择 CAN 用 PA11 和 PA12外接一个 CAN 收发器如果需要 Modbus还可以用 USART1 的 PA9 和 PA10。SWD 调试口在 PA13 和 PA14这两个引脚不要挪作他用除非你想把调试工具点废。功能引脚配置说明单总线 DQPB5开漏输出外部 4.7kΩ 上拉到 3.3V调试串口PA2(TX) / PA3(RX)USART29600 或 115200CAN 通信PA11(RX) / PA12(TX)外接收发器和终端电阻Modbus 可选PA9(TX) / PA10(RX)USART1可复用SWD 调试PA13 / PA14不用于其他功能最小系统其实很简单STM32F042C6 需要一组 3.3V 电源和若干个 100nF 去耦电容电源入口放一个 10μF 电解电容或者钽电容。虽然这颗 MCU 内部有 RC 振荡器但暖通现场有温度漂移和干扰我建议外接一个 8MHz 晶振让系统时钟更稳。复位引脚接一个 10kΩ 上拉电阻和 100nF 电容这是 ST 官方推荐的典型复位电路。上电时 MCU 默认从 Flash 启动不需要额外配置启动引脚。LQFP48 封装焊盘间距不算密手工贴片难度不大样品阶段用热风枪也能焊上但如果量产建议直接交给贴片厂。板上预留一个 3 引脚插针给本地传感器再预留一个端子给远程传感器两个传感器并联到同一条数据线上。3.2 上拉电阻与远程电缆的关键设计单总线最基本的要求是数据线上必须有一个上拉电阻。我本地传感器在板内走线很短远程传感器线缆最长到三米整体总线电容并不夸张所以首选阻值是 4.7kΩ。如果现场线长超过五米建议把上拉电阻降到 2.2kΩ让上升沿更陡否则总线电容一大信号半天抬不到高电平时序就容易出问题。远程传感器的线缆建议用三芯屏蔽线一芯给 VCC一芯给 GND一芯走 DQ 信号屏蔽层在控制器侧单端接地。千万不要两端都接地否则地电位差会形成地环路电流反而把干扰引进来。我踩过一次跟变频器共用电源地线的情况远程读数从 25℃ 跳到了 40℃后来把传感器供电单独用一路低噪声电源读数才稳定下来。连接器位置要加 ESD 保护。远程线缆是从设备外面引进来的很容易感应静电或者被雷击浪涌打到我习惯在连接器旁边放一只双向 TVS 二极管钳位到 3.3V 电源轨或者用低电容的 TVS 并联在 DQ 线对地上。这样做的好处是总线被瞬态高压打的时候MCU 引脚不会先烧掉损失最多是一颗几毛钱的二极管。3.3 本地和远程传感器的安装工艺本地传感器虽然叫“本地”但也不是随便找个地方焊上就行。我把本地传感器放在 MCU 对角的位置远离功率 MOSFET 和电感这些器件工作时会产生高频开关噪声离太近会让传感器供电质量变差影响转换稳定性。传感器引脚旁边放一个 100nF 去耦电容效果立竿见影。远程传感器如果是放到风道里最好买带金属护套或者环氧封装的探头原厂裸传感器直接接线在现场是不可靠的冷凝水、灰尘、腐蚀性气体会让传感器在几个月内失效。探头安装时尽量让感温头对着气流方向不要贴死金属风道壁否则测的是管壁温度而不是气体温度。还有一点传感器接头不要放在风道积水区现场难配平又容易结露短路。两个传感器连接到同一条总线上之后软件上要不要区分谁是谁这是我一开始没想清楚的地方。你可以用硬件的办法本地传感器固定焊在板内远程传感器从端子接入上电时总线连接关系固定有序也可以让软件在第一次启动时扫描所有 ROM 地址然后根据预设规则绑定。我最后采用了“先扫描、后绑定”的方式把本地传感器的 ROM 地址和远程传感器的 ROM 地址分别存在配置区里更换传感器时重新执行一次绑定流程这样灵活性最高。4. 固件实现从底层时序到应用层温度值4.1 初始化单总线引脚固件工程我用的是 STM32CubeMX 生成的基础框架外设初始化代码自动生成但在单总线引脚这里我做了一点自定义把 PB5 配置成开漏输出并且开启内部上拉。外部已经有了 4.7kΩ 上拉电阻的话内部上拉可以不开我开主要是为了在极端情况防总线悬浮实际使用没什么坏处。#define OW_PORT GPIOB #define OW_PIN GPIO_PIN_5 #define OW_SET() (OW_PORT-BSRR OW_PIN) #define OW_CLR() (OW_PORT-BRR OW_PIN) #define OW_READ() (HAL_GPIO_ReadPin(OW_PORT, OW_PIN) GPIO_PIN_SET) void bsp_onewire_init(void) { __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitTypeDef gpio {0}; gpio.Pin OW_PIN; gpio.Mode GPIO_MODE_OUTPUT_OD; // 开漏输出释放时为高阻 gpio.Pull GPIO_PULLUP; // 内部上拉可开可关 gpio.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(OW_PORT, gpio); }注意这里的开漏模式是关键。如果配置成推挽输出拉低之后想释放总线就必须切换成输入模式频繁切换容易引入毛刺。开漏模式拉低就是输出低置高就是释放总线由外部上拉把电平拉高这正是单总线主机最理想的工作方式。微秒级延时我用的是 DWT。STM32F042C6 内置数据观察点单元可以读取 CPU 周期计数器精度比 SysTick 中断高得多而且不占用中断资源。初始化时打开 DWT 计数延时函数直接数周期void bsp_timing_init(void) { CoreDebug-DEMCR | CoreDebug_DEMCR_TRCENA_Msk; DWT-CYCCNT 0; DWT-CTRL | DWT_CTRL_CYCCNTENA_Msk; } void delay_us(uint32_t us) { uint32_t start DWT-CYCCNT; uint32_t ticks us * 48; // 48MHz while ((DWT-CYCCNT - start) ticks); }用 DWT 的好处是延时不会被中断打断单总线这类对微秒级时序敏感的操作用其他延时方式经常越调越乱。如果有人在 RTOS 环境里用记得先把 DWT 初始化放在启动早期否则周期计数可能没起来。4.2 单总线复位、读位、写位单总线底层时序就三个动作复位、写位、读位。先看复位也就是主机把总线拉低至少 480μs然后释放传感器检测到这个下降沿后会回一个存在脉冲主机在释放后 60μs 到 240μs 之间读取到低电平说明总线上有传感器。uint8_t ow_reset(void) { uint8_t presence; OW_CLR(); delay_us(500); OW_SET(); // 释放总线 delay_us(40); // 等传感器存在脉冲 presence (OW_READ() 0) ? 1 : 0; delay_us(400); return presence; }如果 presence 是 1说明传感器在线如果是 0总线上没有传感器或者传感器供电掉了。这个返回值很重要应用层要用它来判断是否上报故障。写位和读位的时序都在微秒级别。写“1”时主机拉低总线 6μs然后释放让上拉电阻把总线拉高写“0”时主机拉低总线 60μs再释放。读位时主机先拉低总线 2μs释放后在 8μs 左右采样总线电平传感器会在这个窗口内把数据放到总线上。我整理出下面的代码void ow_write_bit(uint8_t bit) { delay_us(2); if (bit) { OW_CLR(); delay_us(6); OW_SET(); delay_us(58); } else { OW_CLR(); delay_us(60); OW_SET(); delay_us(2); } } uint8_t ow_read_bit(void) { uint8_t bit; OW_CLR(); delay_us(2); OW_SET(); delay_us(8); bit OW_READ() ? 1 : 0; delay_us(50); return bit; }写入一个字节就是把上面函数按 LSB First 的顺序循环八次。读取一个字节同理。写整个 ROM 地址的时候要记得低位在前地址字节的顺序和传感器内部存储顺序一致否则 Match ROM 永远对不上。4.3 枚举设备并绑定本地/远程地址单总线的独门技能就是多条总线只需要一根数据线每个传感器出厂烧录一个 64 位 ROM 编码。上电后总线上的传感器数量可能是一个或者多个甚至可能是零。这时固件要先做一次枚举把所有在线传感器的 ROM 地址扫描出来。枚举算法的标准做法是逐位搜索先发送复位脉冲再发送 0x33 搜索 ROM 命令然后按位判断总线上的分叉情况。这个算法网上有现成实现这里不展开全部代码我只说思路先读一位再读一位的补码如果两位都是 1说明总线上没有设备如果一位是 0 一位是 1说明这一位的值是确定的如果两位都是 0说明存在分叉需要自己决定走 0 分支还是 1 分支并记录路径。扫描完成之后我把 ROM 地址列表打印到调试串口。实际项目里本地传感器固定在板上远程传感器接在端子上上电扫描后总会有两个地址我先把这两个地址保存成默认配置然后在应用层定义两个常量#define LOCAL_TEMP_ADDR_0 0x28 #define LOCAL_TEMP_ADDR_1 0xA4 ... #define REMOTE_TEMP_ADDR_0 0x28 #define REMOTE_TEMP_ADDR_1 ...你不一定非要在代码里写死地址也可以放在 EEPROM 或者 Flash 配置区。我的习惯是固件里保留一个“自动绑定”模式首次上电且配置区为空时自动把扫描到的两个传感器按顺序绑定为本地和远程并在日志里打印出哪个是本地、哪个是远程。开发阶段这么干很省事等现场传感器更换时也不需要改程序重新执行一次绑定就行。4.4 启动温度转换并读取结果单总线读温度分两步先发转换命令再读暂存器。转换命令 0x44 会让传感器内部开始一次 ADC 采样12 位分辨率下最长要 750ms。我通常先用 Skip ROM 命令0xCC把所有传感器同时触发转换这样多个传感器同时采样时间上并行等转换完成后再逐个读取。uint8_t sensor_start_all(void) { if (!ow_reset()) return 0; ow_write_byte(0xCC); // Skip ROM ow_write_byte(0x44); // Start temperature conversion return 1; }读取某个指定传感器的温度时先用 Match ROM 命令0x55指定 ROM 地址再发送 Read Scratchpad 命令0xBE然后连续读 9 个字节。暂存器前两个字节就是 16 位温度原始值低字节在前。后面的字节包括上限、下限、配置和 CRC 校验值CRC 用来检查这一帧数据是否被干扰损坏。int16_t sensor_get_temp_raw(uint64_t rom) { uint8_t buf[9]; uint8_t i; if (!ow_reset()) return TEMP_ERROR; ow_write_byte(0x55); // Match ROM for (i 0; i 8; i) { ow_write_byte((uint8_t)(rom (8 * i))); } ow_write_byte(0xBE); // Read Scratchpad for (i 0; i 9; i) { buf[i] ow_read_byte(); } if (crc8(buf, 8) ! buf[8]) return TEMP_CRC_ERROR; return (int16_t)((buf[1] 8) | buf[0]); }CRC 校验不能省。现场传感器电缆长总线容易受到干扰读回来的暂存器数据可能中间有一位翻转了。如果不校验温度可能从 25℃ 变成 60℃这在暖通控制里是事故级别的误差。CRC8 的多项式是 0x8C实现方式可以查表也可以按位算我直接把按位版本贴出来uint8_t crc8(const uint8_t *data, uint8_t len) { uint8_t crc 0; while (len--) { uint8_t inbyte *data; for (uint8_t i 0; i 8; i) { uint8_t mix (crc ^ inbyte) 0x01; crc 1; if (mix) crc ^ 0x8C; inbyte 1; } } return crc; }温度原始值换算很简单12 位分辨率下每个 LSB 代表 0.0625℃。比如原始值 0x0191 换算下来是 25.00℃0xFF6E 是 -9.125℃。公式就是temp raw * 0.0625f。我建议应用层保留 float 或者用一个 int16_t 表示 0.01℃ 精度避免浮点开销太大的话就用定点temp_centi raw * 6 / 100? 不对raw * 100 / 16就能得到摄氏度百分度。实际上raw * 6.25也就是raw * 25 / 4用整数就能精确到 0.01℃。4.5 把温度值送到通信接口裸机采集到温度只是第一步暖通系统里温度要参与控制或者上报给上位机。我这边用 CAN 把两个温度值周期发出去每 500ms 发一帧。具体帧格式可以自己定义也可以走行业协议。我为了调试方便另外用调试串口打印一行日志[LOCAL] 25.4C [REMOTE] 22.8C如果希望温度直接参与 PID就在主循环里读一次local_temp和remote_temp然后根据温差计算风阀或者水阀开度。这里有一个经验不要在每次读取温度后都立刻执行控制输出否则 0.5℃ 的量化抖动会让阀门来回抖最好加上滞回或者滤波。我简单地把两次读数做了均值滤波控制效果立刻稳定很多。5. 现场问题与排查实录5.1 从 85℃ 说起做单总线温度采集的人多多少少都见过那个诡异的 85℃。我第一次上电时代码先读了一遍温度打印出来全是 85.0℃当时还以为是传感器坏了。后来查资料才明白很多这类传感器上电后内部暂存器默认值就是 85℃如果在温度转换还没完成时就去读暂存器读到的就是这个默认值。解决办法很简单确保发了 0x44 转换命令后等待时间超过转换时间再读取结果。12 位分辨率下等 750ms 是安全的如果你用 9 位分辨率只需要 94ms 左右。如果不想傻等可以周期查询发转换命令后每 50ms 读一次直到暂存器第 7 字节的忙标志位清零。这样既能省时间又能避开默认值陷阱。还有一种情况会让读数一直是 85℃传感器处于错误状态没有响应复位脉冲。此时ow_reset()返回 0整个温度采集链路直接判错误值不应该去读暂存器。如果固件忽略了复位结果直接发命令总线静默时读到的往往是 0xFF对应温度换算出来也是异常值。所以我建议在应用层加判断ow_reset()失败直接走传感器掉线告警不要进入后续读取流程。5.2 远程读数跳变的那点事远程传感器读数跳变是最让现场工程师头疼的。我有一次把远程传感器线缆沿着电源线布线结果温度从 25℃ 到 45℃ 之间来回跳。后来检查发现线缆虽然是屏蔽线但屏蔽层在现场设备侧也接了地两端接地形成了地环路变频器工作时地环路电流直接在屏蔽层上产生干扰电压传感器供电都受影响。处理方法是把传感器一侧的屏蔽层悬空只在控制器侧接地地环路立刻被切断。另外如果现场设备本身的接地阻抗高控制器和传感器之间的地电位差会直接加到信号地上我不建议传感器远程端和控制器共用地线太长的话尽量用差分或者隔离方案。距离三五米以内三线制加单端屏蔽地通常够用。总线上的干扰还可以通过并一个小电容解决。我在远程连接器的 DQ 线对地之间并了一个 1nF 的陶瓷电容对高频噪声有抑制作用。但这颗电容不能太大否则单总线读写时序的上升沿会被拉软导致通信失败。如果你发现并了 10nF 后温度反而读不出来那就是电容太大了换回 1nF 或去掉。5.3 长总线丢设备总线长度超过十米时传感器时有时无是常见现象。单总线协议本身支持几十米传输但实际受上拉电阻、线缆电容、接线方式影响很大。我遇到的情况是上拉电阻用 4.7kΩ总线长度四五米没问题延长到八九米时远程传感器经常枚举不到本地传感器倒是正常。排查过程很简单用示波器看 DQ 线上的上升沿发现上升沿明显变缓波形拉出一个长尾巴到了读时序采样点电平还没到高电平阈值。解决方式是降低上拉电阻到 2.2kΩ让上拉能力更强上升沿变陡问题解决。如果你用的是 5V 供电的单总线系统而 MCU 是 3.3V还可以考虑用有两路电源的有源上拉方案但对大多数项目2.2kΩ 已经够用。长线还会带来一个问题传感器供电线上的压降。如果传感器远程端 VCC 线太细十几米后电压可能掉到 3V 以下传感器工作不稳定。现场布线建议 VCC 和 GND 用 0.5 平方毫米以上的线DQ 信号线稍细一点没关系。如果可能尽量用星型接线不要手拉手串成一条长链否则末端设备的信号质量会很差。5.4 更换传感器后无法匹配本地传感器和远程传感器都用自己的 ROM 地址绑定如果现场坏了换一颗新的固件里保存的旧地址就匹配不上了。我遇到过现场工程师把远程传感器换掉之后设备一直报远程温度故障查了半天才发现是地址没重新绑定。解决方法是不要把 ROM 地址当作永久不变的参数而是提供一个“重新识别”流程长按按键或者通过通信接口发送一条命令让设备重新扫一遍总线上的 ROM 列表然后更新配置。在固件里我把这种逻辑设计成掉线自动重扫如果远程传感器连续 5 次读取失败自动进入枚举模式如果总线上恰好只有一个地址发生变化就用新地址替代旧地址。这样做虽然不能完全替代手动确认但能省掉好多上门维护的功夫。顺带说一句传感器更换后也要检查安装位置和线缆很多情况下不是传感器本身坏了而是接线端子松动或者现场潮湿导致接触不良。检修时先量 DQ 信号线和地之间的电阻正常应该能看到几兆欧以上的静态电阻接上传感器后复位脉冲会在总线上有活动信号用万用表也能粗略判断线路通断。5.5 常见故障速查表现象可能原因处理办法读回 85℃转换未完成读到默认值等待超过转换时间或查询忙标志读回异常大/负值CRC 校验失败加强屏蔽接地过滤错误帧找不到远程传感器上拉电阻偏大换 2.2kΩ减小总线电容远程温度跳变屏蔽层两端接地/靠近干扰源屏蔽层单端接地TVS 保护总线完全无应答传感器没供电或接线断检查 VCC/地线/端子氧化更换传感器后找不到ROM 地址变化重新枚举绑定地址这个表是我们项目内部排查手册的浓缩版现场出了问题先对着表查大概率能找到方向。6. 还可以继续扩展的方向6.1 一根总线上挂更多传感器单总线协议一个很自然的好处就是可以扩展多个传感器。现在我只接了两个本地和远程各一个但如果要测回风温度、送风温度、盘管温度、室外温度只需要再往总线上并几颗传感器靠 ROM 地址区分就行。实际的限制主要是传感器数量和转换时间总线上所有传感器可以并行转换然后再逐个读取所以传感器数量增加基本不影响更新周期。6.2 接入 Modbus 或 CAN 网关现在的方案里 CAN 直发简单帧但如果是楼宇自控系统绝大多数上位机还是走 Modbus 协议。STM32F042C6 的 USART 资源够用我其实在这个项目里预留了 Modbus 从站功能把所有温度值映射成 Modbus 保持寄存器通过 RS485 收发器接到上位机。Modbus 协议简单但建议把超时和重试机制做好否则现场一个从站异常会把整个总线拖住。6.3 低功耗采集与轮询策略前面提到过单总线传感器转换时电流较大转换完成后进入待机状态此时几乎不耗电。如果做电池供电的设备可以拉长采集周期比如 10 秒唤醒一次发转换命令后就进入 STOP 模式等 100ms 左右再从 STOP 唤醒读结果。这样平均功耗可以压到很低。唯一要注意的是单总线时序期间不能进 STOP否则 GPIO 释放和采样会乱套时序必须放在唤醒之后。6.4 温度校准的小经验数字传感器出厂校准过了但现场安装位置仍然会带来系统误差。比如本地传感器紧挨着电源模块温度比真实环境高几度远程传感器靠着金属风道壁温度又偏低。我在应用层给两个传感器各加了一个可配置偏移量存储在 Flash 配置区现场用标准温度计校准后写入。这个偏移量不用太复杂0.1℃ 步进就够关键是每台设备在校准时能独立修改而不是在固件里写死。最后再分享一个小技巧批量生产的设备要想避免每台都校准的麻烦最好在硬件设计时把本地传感器的位置固定下来同时把远程传感器的探头形式标准化。这样就算传感器之间有几度差异也能通过统一偏移量修正不用一台台逐个调。这套单总线方案我后来在其他项目里又复用了几次每次只要改一下引脚和配置逻辑都不用动省下的调试时间非常可观。