2026/11/2 19:35:34

STM32+RS485土壤温湿度传感器通信入门

STM32+RS485土壤温湿度传感器通信入门 简介这份面向嵌入式初学者与STM32开发入门者的RS485通信文档围绕STM32F103微控制器如何借助UART接口实现半双工RS485通信展开重点解决DE方向控制、数据收发切换及传感器数据读取等常见问题。压缩包内共1个docx文件约1.22MB以图文说明配合C语言代码片段的形式组织便于对照硬件连接与程序流程阅读。文档结合土壤温湿度传感器实例给出RS485初始化、波特率设置、发送读取指令、接收响应数据并在LCD上显示温湿度的完整流程同时涉及GPIO模式配置、中断优先级、接收缓冲区与发送/接收模式宏定义等关键知识点。已有2629人学习适合需要快速理解RS485原理、掌握STM32串口编程并完成传感器采集实验的读者参考。1. 从一根线开始零基础也能看懂的 RS485 到底在干什么很多人第一次看到「STM32 RS485 传感器」这一串词脑子里会立刻冒出一堆问号RS485 是个芯片吗是协议吗STM32 又是什么是不是得先学完单片机原理、电路、C 语言才能碰这个东西我先给一个让你不再发怵的答案这套东西的本质是让一块单片机能跟几十米外的一颗土壤探头「说话」而且只需要两根线。生活里最贴近的类比是课堂点名。老师喊「一号同学报一下你的体温」一号回答其他同学安静听着谁被叫到谁说话这就是 RS485 常说的半双工——同一时刻只有一个人在讲话。而 STM32 就是那个老师传感器是被点名的同学两根线就是传递声音的空气。整份文档做的事情就是教你怎么当这个老师先把嗓子调好初始化串口再决定什么时候闭嘴听切换收发方向最后把同学报上来的数字翻译成人能看懂的温度和湿度显示在屏上。适合谁完全没碰过单片机、只学过一点点 C 语言、想做个能跑起来的小项目的新手最合适。2. 为什么值得先学它从一个跑不通的串口说起2.1 RS485 与普通串口的差别多了一根「说话开关」新手最容易混淆的一点是RS485 不是全新的通信方式它的数据格式和普通串口几乎一模一样一样是波特率、8 位数据、1 位停止位、无校验。真正的差别只在硬件层面——普通串口是两根线各管一个方向而 RS485 用一对差分线发送和接收共用这条总线所以必须有个开关来告诉你此刻是「说」还是「听」。这份文档里这个开关就是代码里的RS485_TX_EN它接到了单片机的一个引脚上。你把引脚拉高芯片就切到发送模式把数据推到总线上拉低就切回接收安静地等别人说话。一开始我没意识到这个开关的重要性只把数据往外发屏幕上一个数都没有后来才明白发完不改回接收模式传感器就算回答了你也听不见。为什么非得这么设计因为 RS485 的强项是省线和远距离。一根总线可以挂几十个设备布线成本低抗干扰能力强工业现场几十米上百米都在用。代价就是所有人共用一条「话筒线」必须轮流说话靠地址区分谁被点名。文档里那句0x01就是地址0x03是「读寄存器」的功能码后面跟着要读的寄存器位置和数量——这串指令不是随便编的是这颗土壤温湿度传感器手册里规定的格式。2.2 为什么新手适合拿它练手看得见摸得着学嵌入式最怕的是什么是代码跑完了什么也没发生你不知道是对是错。RS485 读传感器这个项目刚好反过来数字变了就是通了数字不变就是没通屏幕就是你的裁判。这种即时反馈对新手极其重要。而且它把嵌入式的几个核心动作全都串了一遍但每一个都不深时钟使能、GPIO 配置、串口初始化、中断、延时、数据解析、屏幕显示。你不需要先把每个模块啃透先跟着跑通一遍回头再看每个函数在干什么理解速度会快很多。这也是我建议新手不要一上来就啃寄存器手册的原因——先有体感再补原理顺序反了容易劝退。再补一句现实价值土壤温湿度、光照、气体浓度这类工业传感器大量走的就是 RS485 接口。学会读一个换一个型号无非是改改指令和解析公式套路是通用的。所以这个入门项目不是玩具它是你后面接各种传感器的第一块垫脚石。2.3 第一步该准备什么硬件清单与接线思路动手前先把东西备齐别到写代码时发现少一根线。你需要一块 STM32F103 的开发板文档里用的是 ZET6 型号引脚多接屏幕方便、一个 RS485 转接模块或者板载的 485 收发芯片、一个土壤温湿度传感器、一块 TFT 液晶屏、下载器和若干杜邦线。接线记住三件事就够了。第一传感器的 A 接模块的 AB 接 B接反了收不到数据这是最常见的翻车点别问我是怎么知道的。第二模块和单片机之间是串口连接单片机的 PA2 接模块的发送脚PA3 接接收脚这是文档里RS485_Init配置的那两个引脚。第三收发方向控制脚要接到单片机的某个 IO 上文档里用的是 PG3实际接线时你要和自己代码里的定义对上对不上就是发得出去收不回来。提示接线前先断电接好再上电。RS485 的 A/B 线在带电插拔时容易打火养成习惯比事后排查省事得多。3. 拆开第一个例子从初始化到屏幕出数一步步跟着跑3.1 先看懂那条指令八个字节在说什么在写任何代码之前先搞明白你要发出去的东西是什么。文档里这一行是最关键的// 发给土壤温湿度传感器的读取指令共 8 个字节 u8 TR_Read[8] {0x01, 0x03, 0x00, 0x06, 0x00, 0x02, 0xC4, 0x38};逐字节拆开看0x01是传感器地址总线上如果挂了好几个设备就靠它点名0x03是功能码意思是「我要读你的保持寄存器」0x00 0x06是起始寄存器地址告诉它从第 6 号位置开始给数据0x00 0x02表示要读两个寄存器一个存湿度一个存温度最后0xC4 0x38是校验码用来让传感器确认这条指令在传输中没有被干扰改坏。这最后两个字节新手最容易忽略觉得是凑数的。其实不是——它们是根据前面六个字节算出来的算错了传感器会直接不理你一个字节都不回。所以换传感器型号时指令不能照抄必须按新手册重新算一遍校验。这也是我建议第一次先用现成指令跑通、确认链路没问题再去动它的原因。3.2 初始化把串口 2 调成能说话的状态初始化函数看着长其实就干了四件事。第一件是开时钟不给外设供电它不会工作// 使能 GPIOA、GPIOG 和 USART2 的时钟不开时钟后面全部无效 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG | RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);注意 USART2 挂在 APB1 上GPIO 挂在 APB2 上这两个函数调错了不会报错但外设就是不动属于典型的「玄学」现象——代码没错、编译通过、毫无反应。第二件是配引脚。PA2 是发送要配成复用推挽输出因为它要由串口外设来驱动不是普通 IOPA3 是接收配成浮空输入PG3 是那个方向开关配成普通推挽输出。第三件是配串口参数波特率 9600、8 位数据、1 位停止、无校验、无硬件流控同时打开接收中断。第四件是配中断优先级文档里抢占优先级设 3、子优先级设 2只要不和别的中断冲突就行。USART_InitStructure.USART_BaudRate bound; // 波特率两边必须一致 USART_InitStructure.USART_WordLength USART_WordLength_8b; // 8 位数据 USART_InitStructure.USART_StopBits USART_StopBits_1; // 1 位停止位 USART_InitStructure.USART_Parity USART_Parity_No; // 无校验 USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx; // 收发都开最后一句RS485_TX_EN 0别漏它把方向默认设成接收。初始化完就应该是「听」的状态等你要发的时候再切。3.3 发送函数先开开关发完立刻关回去发送逻辑本身不复杂难的是时序。文档里的写法是拉高方向脚进入发送模式然后一个字节一个字节往外送每送一个都等发送完成标志位置位全部送完后把方向脚拉低回到接收模式。void RS485_Send_Data(u8 *buf, u8 len) { u8 t; RS485_TX_EN 1; // 切到发送模式 for (t 0; t len; t) { while (USART_GetFlagStatus(USART2, USART_FLAG_TC) RESET); // 等上一个字节发完 USART_SendData(USART2, buf[t]); // 送入一个字节 } while (USART_GetFlagStatus(USART2, USART_FLAG_TC) RESET); // 等最后一个字节彻底发完 RS485_RX_CNT 0; // 清空接收计数准备接新数据 RS485_TX_EN 0; // 切回接收模式 }那个while等待是关键。如果你不等发送完成就切回接收最后一个字节可能只发出去一半传感器收到的就是残包校验自然对不上表现就是「有时候能读到有时候读不到」非常折磨人。我第一次调的时候就是这里少等了一步折腾了大半天。3.4 接收函数靠「安静了」判断一帧结束RS485 没有明确的帧结束标志你只能靠时间来判断。文档里的思路是先记下当前收到的字节数等 10 毫秒再看一眼如果数量没变而且不为零就认为这一帧收完了把缓冲区里的数据搬出来。void RS485_Receive_Data(u8 *buf, u8 *len) { u8 rxlen RS485_RX_CNT; // 当前已收到的字节数 u8 i 0; *len 0; delay_ms(10); // 等待 10ms若期间没有新数据则认为帧结束 if (rxlen RS485_RX_CNT rxlen) // 数量没变且不为 0 { for (i 0; i rxlen; i) buf[i] RS485_RX_BUF[i]; *len RS485_RX_CNT; RS485_RX_CNT 0; // 清零为下一帧做准备 } }这里 10 毫秒不是随便定的。波特率 9600 下一个字节大约 1 毫秒正常一帧数据之间的间隔远小于 10 毫秒而两帧之间的空闲会超过它所以用它当分隔线。如果传感器回得慢或者你把波特率调得很低这个值可能要加大否则会把一帧拆成两半。3.5 主函数发、等、收、显示的循环主函数的节奏很清楚初始化一堆东西然后进死循环每轮发一次指令等 300 毫秒让传感器准备答案收一次数据把字节拼成温湿度刷到屏幕上再等 3 秒开始下一轮。while (1) { RS485_Send_Data(TR_Read, 8); // 发读取指令 delay_ms(300); // 给传感器时间处理 RS485_Receive_Data(rs485buf, len); // 收返回数据 if (len) { if (len 9) len 9; // 防止越界只取前 9 个字节 WD (rs485buf[3] * 256 rs485buf[4]) / 100; // 拼出湿度 SD (rs485buf[5] * 256 rs485buf[6]) / 10; // 拼出温度 LCD_ShowxNum(30, 190, WD, 4, 24, 0); LCD_ShowxNum(30, 230, SD, 4, 24, 0); } delay_ms(3000); // 3 秒采一次没必要太频繁 }重点看那两行* 256 。因为一个寄存器只有 8 位装不下温湿度的实际数值所以传感器把它拆成高字节和低字节两个寄存器存。高字节 * 256 低字节就是把两个 8 位拼回一个 16 位整数这是所有这类传感器的通用套路。后面的/100和/10是小数点处理因为传感器返回的是放大后的整数比如返回 2530 实际是 25.30。这两个除数每个型号都不一样一定要查手册抄错了数字就是错的。提示if (len 9) len 9;这行别删。中断接收是异步的缓冲区有可能被写进多余的字节不限制长度去取值轻则显示乱码重则读越界。4. 新手最容易踩的坑五个真实会卡住你的地方第一个坑程序烧进去屏幕一直是 0。现象是显示区域有数字但永远不变或者干脆没有。原因多半是方向控制脚接错了或者代码里的引脚定义和实际接线不一致单片机以为自己在收其实一直没切回来。解决办法很土但有效用万用表量一下那个引脚在发送前后的电平有没有变化没变化就是引脚定义错了去改RS485_TX_EN对应的那行宏定义。第二个坑明明接了线一个字节都收不到。现象是len始终为 0。原因最常见的是 A、B 两根线接反了RS485 是差分信号反了就是收不到其次是波特率不匹配单片机设 9600 但传感器出厂是 4800。解决顺序是先对调 A/B 试一次再逐个波特率试这两步能解决八成问题。第三个坑读到的数忽大忽小或者偶尔跳成几万。现象是数据不稳定。原因是没等发送完成就切换方向最后字节发送不完整或者接收端没做帧长度限制。解决办法是把发送函数末尾那个等待标志位的while补上同时加上长度上限判断别让脏数据进解析。第四个坑传感器地址不是 1 却照抄指令。现象是发出去毫无回应。原因是总线上多个设备时地址必须唯一你照抄的0x01可能和别的设备撞了或者这颗传感器出厂就是别的地址。解决办法是先只接一个传感器确保能通再考虑多设备多设备时逐个改地址别一次全挂上去。第五个坑校验码照抄换型号直接崩。现象是换了同类型传感器后完全读不到。原因是不同型号的寄存器地址和校验算法可能不同最后两个字节不是通用的。解决办法是拿到新手册用在线校验计算工具按新参数重算或者写个小函数自己算别靠猜。5. 跑通之后学什么三条不绕路的进阶方向第一个例子能在屏幕上看到数字说明你已经跨过了最难的那道坎。接下来别急着换项目先把这一个吃透。第一件事是改参数做实验把波特率从 9600 改成 4800看会出什么现象把采样间隔从 3 秒改成 500 毫秒观察屏幕刷新和数据稳定性把接收那 10 毫秒的等待改成 2 毫秒。每次只改一个改完记录现象这种「改动—观察—解释」的循环比看十篇教程都管用。我第一次这么做的时候把等待时间改小立刻复现了数据被截断的问题那一刻才真正理解帧间隔是什么意思。第二件事是把发送和接收拆开来看。现在的接收是靠中断往缓冲区里塞主循环去搬。你可以试着把中断里的逻辑加上一个帧头判断或者干脆改成用空闲中断来判断一帧结束这是实际项目里更常见的做法。再进一步把温湿度解析那段从主函数里抽成一个独立函数参数传缓冲区返回结构体。别小看这种重构它是从「能跑」走向「能维护」的分水岭。第三件事才是扩展。同一个 RS485 总线上再挂一个光照传感器学会用不同地址区分设备或者把读到的数据通过另一个串口发给电脑在电脑上画个曲线。这两步做完你对「总线」和「多设备」的理解就落地了。阶段练什么判断是否过关跑通原样烧录屏幕出数数字随环境变化而变调参改波特率、改间隔、改等待能解释每次改动后的现象重构抽出解析函数、改帧判断方式代码改动后功能仍正常扩展挂第二个传感器、上传数据能靠地址区分设备学习路径上我建议按这个顺序补基础先补 C 语言的指针和数组因为所有缓冲区操作都靠它再补串口通信的时序概念理解波特率和帧结构最后才去碰中断和优先级。顺序别反一上来啃中断优先级分组很容易卡在「为什么是 2 位抢占 2 位响应」这种细节上对入门毫无帮助。最后说一个我自己的习惯。从那以后我每次接一个新的 485 传感器都强制走一遍固定流程先只接一个设备、先把波特率对上、先发一条手册里的示例指令、先用串口助手看原始字节、确认无误再写解析代码。这套流程看着笨但帮我省掉了无数次「到底是硬件问题还是代码问题」的自我怀疑。入门阶段最贵的不是知识是信心别让一次莫名其妙的失败把你劝退。希望帮到你。本文还有配套的精品资源点击获取