☰
STM32无线温度计实战:DS18B20与NRF24L01双板方案
2026/11/3 1:05:18 网站建设 项目流程

1. 为什么我要用两块板子做温度计

温度计这个东西,单片的方案满地都是,DS18B20接STM32,OLED一挂,十分钟就能跑起来。但真正放到实际场景里,比如冷库、温室大棚、鱼缸、机房,你会发现一个问题:传感器和显示端往往不在一起。你不可能为了看一眼温度,专门跑到冷库最里面去。这时候无线传输的价值就出来了。

这个项目的核心思路很直接:一块STM32做发送端,负责采集DS18B20的温度数据,通过NRF24L01无线模块发出去;另一块STM32做接收端,收到数据后在OLED上显示出来。两块板子之间不需要任何物理连线,发送端可以塞在角落里,接收端放在你随手能拿到的地方。

关键词里提到的STM32、DS18B20、OLED、NRF24L01,正好构成了一个完整的无线温度监测链路。这套方案适合谁?如果你正在做课程设计、毕业设计,或者想入门STM32的外设驱动开发,这个项目是一个非常好的练手载体。它涉及单总线协议、SPI/I2C通信、无线收发、OLED显示驱动,覆盖面够广,但每个模块的难度又不会让你卡死。

我前后做过三版,第一版用标准库,第二版换HAL库,第三版加了低功耗处理。踩过的坑不少,下面把整个链路拆开讲。

2. 发送端硬件选型与DS18B20的时序陷阱

2.1 为什么选DS18B20而不是DHT11

热词里同时出现了DS18B20和DHT11,这两个都是常见的温度传感器,但适用场景完全不同。DHT11便宜,能同时测温湿度,但精度只有±2℃,而且响应速度慢,一秒最多读一次。DS18B20贵一点,但精度能到±0.5℃,支持多点组网,一根总线上可以挂多个传感器。

做无线温度计,我选DS18B20的理由很明确:精度够用、单总线省引脚、支持多点扩展。你想想,如果以后要在冷库里布三个测温点,DS18B20只需要一根数据线就能串起来,DHT11就得占三个IO口。而且DS18B20的防水探头版本很成熟,直接扔水里测鱼缸温度都没问题。

STM32F103C8T6是这个项目最常用的主控,72MHz主频,资源足够,价格便宜,最小系统板十几块钱。发送端只需要一个GPIO接DS18B20的数据脚,另外SPI接口接NRF24L01。

2.2 DS18B20的复位时序为什么容易翻车

DS18B20用的是单总线协议,所有通信都靠一根线完成,靠的是精确的时序控制。这也是新手最容易翻车的地方。

复位时序是这样的:主机拉低总线至少480微秒,然后释放,等待15到60微秒,DS18B20会拉低总线60到240微秒作为应答。如果你用HAL库的HAL_Delay()来做微秒级延时,基本必挂,因为HAL_Delay()的最小分辨率是毫秒。

我的做法是用DWT计数器或者定时器来做微秒延时。DWT(Data Watchpoint and Trace)是Cortex-M3内核自带的一个调试单元,可以当高精度计时器用。初始化代码大概是这样:

void DWT_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 * (SystemCoreClock / 1000000); while ((DWT->CYCCNT - start) < ticks); }

有了微秒延时,复位、写位、读位的时序才能精确控制。写时序里,写0是拉低总线60微秒后释放,写1是拉低1到15微秒后释放。读时序更微妙,主机拉低1微秒后释放,然后在15微秒内采样总线电平。

注意:DS18B20对时序非常敏感,如果你用中断或者RTOS,读写过程中一定要关中断,否则一个中断打进来,时序就乱了,读出来的温度会是85℃这个经典错误值。

2.3 温度数据的解析与校验

DS18B20读出来的是16位数据,低4位是小数部分,高12位是整数部分。温度值 = 原始数据 × 0.0625。比如读出来0x0191,十进制是401,401 × 0.0625 = 25.0625℃。

CRC校验不能省。DS18B20的64位ROM里有一个CRC字节,每次读暂存器也有CRC。我见过太多人跳过CRC校验,结果偶尔读出一个离谱的值。加上CRC校验,代码多不了几行,但稳定性提升明显。

uint8_t DS18B20_CRC8(uint8_t *data, uint8_t len) { uint8_t crc = 0; for (uint8_t i = 0; i < len; i++) { uint8_t byte = data[i]; for (uint8_t j = 0; j < 8; j++) { if ((crc ^ byte) & 0x01) { crc = (crc >> 1) ^ 0x8C; } else { crc >>= 1; } byte >>= 1; } } return crc; }

3. NRF24L01无线链路的配置与调试

3.1 为什么NRF24L01是入门无线的首选

NRF24L01工作在2.4GHz频段,SPI接口,理论速率最高2Mbps,空旷环境下传输距离能到几十米,加PA功放能到上百米。模块便宜,一对也就十几块钱。对于温度数据这种小数据包,完全够用。

它的配置不算复杂,但有几个关键点:地址宽度、频道、速率、CRC使能、自动应答。发送端和接收端的这些参数必须完全一致,否则收不到数据。

我用的是STM32的SPI1,配置如下:

参数发送端接收端
SPI速率9MHz9MHz
地址宽度5字节5字节
频道4040
速率1Mbps1Mbps
CRC2字节使能2字节使能
自动应答使能使能

3.2 地址和频道的坑

NRF24L01的地址是5个字节,发送端和接收端的地址必须一致。但这里有个容易搞混的地方:接收端的管道0地址是5字节,管道1到5只有低4字节可配,高字节共用管道0的。如果你只用管道0,那就没这个问题。

频道选择也有讲究。2.4GHz频段很拥挤,WiFi、蓝牙都在这个频段。NRF24L01有125个频道,间隔1MHz。我一般选40、76、100这几个,避开常见的WiFi信道1、6、11。如果你发现数据丢包严重,先换个频道试试。

3.3 发送失败重试机制

NRF24L01有自动重传功能,配置SETUP_RETR寄存器可以设置重传延迟和重传次数。我一般设成重传延迟250微秒,重传15次。这样即使偶尔有干扰,数据也能送达。

但要注意,自动重传只在自动应答使能时才有效。如果你关了自动应答,发送端不知道数据有没有送到,重传也就无从谈起。

发送端的核心逻辑:

void NRF24L01_TxPacket(uint8_t *txbuf) { CE_LOW(); NRF24L01_Write_Buf(WR_TX_PLOAD, txbuf, TX_PLOAD_WIDTH); CE_HIGH(); delay_us(130); }

发送完成后,检查STATUS寄存器的TX_DS位,如果是1,说明发送成功;如果是MAX_RT,说明重传次数用尽还没送到,需要处理。

3.4 接收端的中断处理

接收端我建议用中断方式,NRF24L01的IRQ引脚接到STM32的外部中断。收到数据后,IRQ拉低,触发中断,在中断里读数据、清标志。

void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) != RESET) { uint8_t status = NRF24L01_Read_Reg(STATUS); NRF24L01_Write_Reg(WRITE_REG + STATUS, status); if (status & RX_DR) { NRF24L01_Read_Buf(RD_RX_PLOAD, rxbuf, TX_PLOAD_WIDTH); rx_flag = 1; } EXTI_ClearITPendingBit(EXTI_Line0); } }

注意:中断里不要做太多事情,读数据、置标志位就够了,OLED刷新放到主循环里做。OLED刷新一次要几十毫秒,放在中断里会阻塞其他中断。

4. OLED显示驱动的选型与汉字显示

4.1 0.96寸OLED的I2C兼容性问题

热词里有个"0.9寸oled对i2c兼容问题",这个我深有体会。0.96寸OLED一般是SSD1306驱动,I2C地址通常是0x78或0x7A。但有些模块的地址是0x3C或0x3D,这是因为地址字节的格式不同。

7位地址0x3C,左移一位变成0x78,这就是写地址。如果你用HAL库的I2C函数,它用的是7位地址,所以填0x3C。如果你用软件I2C,自己拼地址字节,那就是0x78。

我建议用软件I2C,因为STM32的硬件I2C在某些型号上有已知的bug,容易卡死。软件I2C虽然速度慢一点,但稳定可控,引脚随便选。

void I2C_WriteByte(uint8_t addr, uint8_t data) { I2C_Start(); I2C_SendByte(addr); I2C_WaitAck(); I2C_SendByte(data); I2C_WaitAck(); I2C_Stop(); }

4.2 显示汉字需要取模

OLED显示英文和数字很简单,用现成的字库就行。但显示汉字需要取模。常用的工具是PCtoLCD2002,设置成阴码、逐列式、顺向,生成的字模数据直接放到数组里。

比如"温度"两个字的16×16字模:

const uint8_t HZ_Temp[] = { 0x10,0x60,0x02,0x8C,0x00,0x00,0xFE,0x92,0x92,0x92,0xFE,0x00,0x00,0x00,0x00,0x00, /* ... 省略后续字节 ... */ };

显示的时候,先设置页地址和列地址,然后依次写入数据。

4.3 显示布局的设计

接收端的OLED我分成三行显示:第一行显示"温度:",第二行显示温度值,第三行显示信号强度或者更新时间。

温度值我保留一位小数,比如"25.1 C"。如果DS18B20读取出错,显示"--.- C",这样一眼就能看出问题。

刷新频率不用太高,一秒一次就够了。DS18B20的转换时间最长750毫秒,你刷太快也没用,数据还没更新。

5. 从发送到接收的完整数据流

5.1 数据包格式的设计

发送端和接收端之间传什么?我定义了一个简单的结构体:

typedef struct { uint8_t header; // 帧头,固定0xAA int16_t temperature; // 温度值,放大10倍,比如251表示25.1℃ uint8_t sensor_id; // 传感器编号,支持多点 uint8_t crc; // 校验和 } TempPacket_t;

温度值放大10倍存成整数,避免浮点数传输。接收端收到后除以10还原。帧头用来做同步,如果收到的第一个字节不是0xAA,直接丢弃。

5.2 发送端的低功耗处理

如果发送端用电池供电,低功耗就很重要。我的做法是:DS18B20转换期间让STM32进入睡眠模式,NRF24L01发送完成后进入掉电模式。

DS18B20启动转换后需要750毫秒,这段时间STM32什么都不用做,直接__WFI()等待。NRF24L01的掉电模式电流只有900纳安,发送间隙把它关掉能省不少电。

void Enter_LowPower(void) { NRF24L01_Write_Reg(WRITE_REG + CONFIG, 0x00); // 掉电模式 __WFI(); // 等待中断唤醒 }

5.3 接收端的数据校验与容错

接收端收到数据后,先检查帧头,再算校验和。如果校验不通过,丢弃这一包,继续等下一包。如果连续多次收不到数据,OLED上显示"信号丢失"。

我还加了一个简单的滑动滤波:连续读5次温度,去掉最高和最低,取中间3次的平均值。这样显示出来的温度不会跳来跳去。

6. 调试过程中遇到的典型问题

6.1 DS18B20读出来一直是85℃

这是最经典的问题。85℃是DS18B20上电后的默认值,说明转换根本没启动,或者时序不对。排查顺序:先确认延时函数准不准,用示波器看复位脉冲是不是480微秒;再确认写时序的拉低时间对不对;最后检查是不是中断打断了时序。

6.2 NRF24L01发送成功但接收端收不到

先检查两边的地址、频道、速率是不是完全一致。然后看接收端的STATUS寄存器,如果RX_DR一直是0,说明根本没收到。这时候用示波器看IRQ引脚有没有拉低。如果IRQ没动静,大概率是SPI通信有问题,检查MOSI、MISO、SCK三根线有没有接反。

6.3 OLED不亮

先量VCC和GND,确认供电正常。然后检查I2C地址对不对,用逻辑分析仪抓一下I2C波形,看有没有应答。如果地址对了但屏幕还是不亮,可能是初始化序列不对,SSD1306的初始化命令比较多,漏一条都可能不显示。

6.4 无线距离短、丢包严重

NRF24L01对电源很敏感,3.3V供电一定要干净。如果和STM32共用电源,加一个10微法和一个0.1微法的电容滤波。另外,模块的天线周围不要铺地,会影响辐射效率。

7. 这套方案还能怎么扩展

温度只是开始。这套无线链路的框架搭好之后,发送端换传感器就能测别的。换成DHT11测湿度,换成BH1750测光照,换成MQ-2测烟雾,接收端只需要改一下显示的单位和标签。

如果想做多点监测,发送端加一个拨码开关设置sensor_id,接收端根据ID显示不同的传感器数据。NRF24L01支持6个管道同时接收,理论上可以接6个发送端。

再进一步,接收端加一个ESP8266或者ESP32,把数据传到手机或者云端,就变成了一个简易的物联网监测系统。不过那是另一个话题了,先把眼前这条无线链路跑通再说。

我个人在实际操作中的体会是,DS18B20的时序和NRF24L01的配置是这套方案的两个关键点,把这两个地方吃透,剩下的OLED显示和数据处理都是体力活。调试的时候不要急着一把梭,先让发送端把温度读对,再让无线通起来,最后调显示,一步一步来,每一步都验证通过再往下走。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询