STM32实战:基于RS485与Modbus RTU的土壤检测仪制作
简介面向物联网与智慧农业开发者一套基于STM32F103C8T6的土壤监测完整工程方案通过RS485接口连接四合一综合土壤传感器可实时采集土壤pH、氮、磷、钾含量并在OLED屏显示适用于精准农业、环境监测及嵌入式教学实践。压缩包共232个文件大小约8.69MB包含Keil工程文件uvprojx、C源文件与H头文件、编译生成的hex/axf/map/lst等文件以及大量o、d、crf中间文件便于查看源码结构、重新编译或直接烧录调试。已有2109人学习/下载。资源覆盖从传感器问询帧发送、应答帧解析到数据换算与OLED显示的完整链路适合中高级嵌入式开发者参考通过研究代码可快速掌握STM32的USART中断收发、RS485通信时序及I2C/SPI驱动OLED的方法也可在此基础上扩展无线远程监控功能。 很多人以为做一个土壤检测仪核心难度在传感器选型其实真正坑人的地方在通信链路和协议解析。RS485总线、Modbus RTU协议、OLED显示这三块才是项目能不能跑通的关键。农业大棚、育苗基地、家庭菜园现在都有土壤检测需求。ST官方主推的F103C8T6这颗芯片因为价格便宜、外设够用、资料铺天盖地依然是很多人的首选。配上RS485接口的土壤传感器测PH、氮磷钾再用0.96寸OLED把数据直接显示出来整套方案下来硬件成本能控制在六七十块做原型验证或者小型农业项目完全够用。这个项目适合正在学STM32的嵌入式爱好者、做农业物联网方案的工程师以及想低成本搭建土壤检测系统的创客。我会把硬件接线、协议解析、CubeMX配置、代码逻辑全部讲清楚最后把调试时最容易踩的坑列出来照着做基本不会翻车。1. 项目整体思路与关键选型1.1 为什么是RS485而不是I2C或SPI土壤传感器在农业现场的使用距离动辄几十米甚至上百米传感器埋在土里或者插在田垄上主控板放在配电箱里中间隔着电缆。I2C和SPI这种短距离板级通信协议超过一两米就废了抗干扰能力也不行。RS485用差分信号传输A、B两根线的电压差表示逻辑0和1共模抑制能力强传输距离理论上一千二百米支持多点组网一条总线最多挂32个节点用485芯片扩展还能更多。土壤检测这种典型的远距离、多节点、强干扰场景本来就该用RS485。1.2 硬件清单与选型背后的小心思主控选了STM32F103C8T6最小系统板这个板子实在没什么好纠结的。Flash 64KB、RAM 20KB跑Modbus解析和OLED显示绰绰有余。GPIO、USART、I2C、定时器全都有国产替代型号也便宜十几块钱一片坏了不心疼。传感器这块我推荐买7合1的RS485土壤传感器自带探头插入土壤直接返回PH值、氮磷钾数值。市面上这类传感器内部基本是Modbus RTU协议买的时候问清楚寄存器地址表不同厂家寄存器分布不一样。有的传感器标称12V供电有的标称5V买回来一定要看说明书供电错了直接烧。OLED屏用0.96寸I2C接口的SSD1306四根线搞定VCC、GND、SCL、SDA省引脚显示两行数字绰绰有余。线少就能把更多精力放在通信和协议上没必要为了省几十字节Flash去搞SPI版本。2. RS485通信链路的核心底层逻辑2.1 物理层细节A/B线怎么接终端电阻什么情况才加RS485物理层就两根线A和B。连接的时候记住传感器A接单片机485芯片的AB接B很多新手一上来接反导致收发完全没反应。A和B之间是个差分电压逻辑1时A比B高逻辑0时B比A高电平判断靠这个差值所以这两根线不能反。终端电阻是另一个高频问题。RS485总线两端要求各并联一个120Ω电阻用来消除信号反射。距离短几米、节点少一两个的情况下不加也能跑但超过十米或者节点变多建议还是老老实实加上。注意是总线的物理两端加不是每个设备都加。我之前见过一个项目三个节点每个都焊了120Ω结果信号衰减严重数据偶发错误去掉多余的两个才恢复正常。2.2 收发器和方向控制SP3485/MAX485怎么接STM32的USART引脚输出的是TTL电平RS485总线上的差分信号需要专门的收发器转换。常用芯片是SP3485和MAX485两者Pin-to-Pin兼容。接法很简单芯片RO接STM32的RXDI接TXDE和RE接一个GPIO控制脚拉高时发送拉低时接收。因为RS485是半双工通信收发不能同时进行这个方向脚必须软件精确控制。用HAL库时发送前把方向脚拉高发送完最后一帧数据之后延时1ms再拉低确保数据完整发送出去再切换为接收状态。还有一个容易被忽略的点STM32F103C8T6的GPIO是3.3V电平SP3485完全兼容但MAX485有个别批次在3.3V下能跑只是不建议长期用。所以给3.3V系统配RS485芯片优先SP3485。另外户外使用可以考虑带隔离的RS485模块如ISO3082把地环路隔断防止雷击浪涌损坏主控。2.3 协议层基础Modbus RTU帧结构与CRC校验市面上大多数RS485土壤传感器默认采用Modbus RTU协议请求帧和响应帧都是固定的格式。请求帧格式为从站地址1字节、功能码1字节读取保持寄存器一般是0x03、起始寄存器地址2字节、寄存器数量2字节、CRC16校验2字节低字节在前。传感器返回的帧是从站地址、功能码、字节数、数据区、CRC16。CRC16是Modbus RTU的核心校验不对传感器直接不理你。计算时用查表法速度最快在STM32上跑几乎不占资源。后面代码部分我会给出完整的Modbus CRC实现。3. 核心代码实现从CubeMX配置到数据解析3.1 CubeMX配置要点用STM32CubeMX生成工程选STM32F103C8T6芯片。时钟树选择外部8MHz晶振PLL倍频到72MHz这是F103的标准跑法。USART2选择异步模式波特率96008数据位、无校验、1停止位。这是传感器最常用的默认参数有些传感器支持9600/4800切换买回来默认就是9600。使能USART2全局中断。接收数据用DMA空闲中断这是处理Modbus不定长帧最省CPU的方式。DMA负责把数据搬进缓冲区空闲中断负责触发解析逻辑。CubeMX里给USART2添加DMA接收请求模式为Normal数据宽度Byte。开启中断时在NVIC里全勾上。3.2 Modbus请求帧构造与CRC校验代码uint16_t modbus_crc16(uint8_t *buffer, uint16_t length) { uint16_t crc 0xFFFF; for (uint16_t i 0; i length; i) { crc ^ buffer[i]; for (uint8_t j 0; j 8; j) { if (crc 0x0001) { crc (crc 1) ^ 0xA001; } else { crc 1; } } } return crc; } void send_modbus_request(uint8_t slave_addr, uint16_t reg_addr, uint16_t reg_count) { uint8_t req[8]; req[0] slave_addr; req[1] 0x03; req[2] (reg_addr 8) 0xFF; req[3] reg_addr 0xFF; req[4] (reg_count 8) 0xFF; req[5] reg_count 0xFF; uint16_t crc modbus_crc16(req, 6); req[6] crc 0xFF; req[7] (crc 8) 0xFF; RS485_DIR_GPIO_Port-BSRR RS485_DIR_Pin; // 方向脚拉高进入发送模式 HAL_UART_Transmit(huart2, req, 8, 100); HAL_Delay(1); RS485_DIR_GPIO_Port-BRR RS485_DIR_Pin; // 拉低切回接收模式 HAL_Delay(1); }寄存器地址表就看你的传感器手册比如有的传感器PH寄存器地址是0x0000氮是0x0001磷是0x0002钾是0x0003。手册一般还说明数据是整数还是小数有的PH值以10倍整数形式存储比如读回来是65实际PH6.5氮磷钾以mg/kg为单位有的还要再乘0.1。这块不做转换的话屏幕上显示的数字完全是错的。3.3 接收与解析DMA空闲中断判帧结束Modbus RTU帧没有固定长度怎么判断一帧传完了协议规定两帧之间至少要有3.5个字符时间的静默间隔。最简单的实现方式是串口空闲中断接收线上超过一个字符时间没数据就认为当前帧结束了触发一次空闲中断读取DMA当前计数得到帧长度然后丢给解析函数。uint8_t rx_buffer[256]; volatile uint8_t rx_len 0; volatile uint8_t rx_complete 0; void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { if (huart huart2) { rx_len Size; rx_complete 1; } }主循环里当rx_complete为1时拿到rx_len先校验CRCCRC通过再看从站地址是否正确。地址校验通过后把数据区按寄存器顺序解析填入全局结构体变量。注意解析完之后一定要重新调用HAL_UARTEx_ReceiveToIdle_DMA(huart2, rx_buffer, sizeof(rx_buffer))否则空闲中断只触发一次后续数据进不来。3.4 OLED显示SSD1306驱动与界面刷新策略0.96寸OLED是SSD1306控制芯片I2C地址一般是0x3C也有0x3D的代码里改一下宏定义就行。HAL库驱动SSD1306可以直接用现成的开源驱动移植注意三点I2C句柄要改成你自己的huart或者hi2c实例SCL和SDA引脚在CubeMX里配置为开漏上拉速度模式选Fast Mode。显示这块有个坑要提醒千万别在【主循环里】频繁清屏再重绘OLED会闪烁。我一般把屏幕分成几个静态区域只刷新数值变化的区域。比如第一行固定显示“PH: 6.5”第二行显示“N: 52 P: 33 K: 120”每次只把对应区域的数值擦除重画其余部分不动。还有一个更省事的方法只在数值变化超过阈值时刷新比如PH变化超过0.1才刷新一次氮磷钾变化超过1才刷新。这样既省CPU又延时屏幕老化。void display_soil_data(float ph, uint16_t n, uint16_t p, uint16_t k) { char buf[16]; OLED_ShowString(0, 0, PH:); sprintf(buf, %.1f, ph); OLED_ShowString(24, 0, buf); OLED_ShowString(0, 2, N:); sprintf(buf, %d, n); OLED_ShowString(16, 2, buf); OLED_ShowString(0, 4, P:); sprintf(buf, %d, p); OLED_ShowString(16, 4, buf); OLED_ShowString(0, 6, K:); sprintf(buf, %d, k); OLED_ShowString(16, 6, buf); }4. 调试实录RS485和OLED最常见的坑4.1 传感器完全无应答最典型的场景代码写好了发送请求帧传感器就是不回数据。按照我排查的顺序走大概率能定位问题。先看485芯片方向脚有没有正确切换。用示波器或者逻辑分析仪抓USART2的TX引脚看有没有数据发出。有数据但传感器无应答用万用表量一下A和B之间的电压空闲态约2V左右发送时应该有2V左右的电压差跳变。如果A、B电压量出来是0V检查是不是SP3485的供电没接对或者芯片焊反了。共地问题也经常遇到。RS485只是抗干扰不是隔离A/B线必须在所有节点共地才能正常工作。很多新手传感器电源用独立12V适配器主控用USB供电两边不共地最终数据就是收不到。解决方法是把两边的GND连在一起或者直接用同一个电源模块给主控和传感器供电。4.2 数据乱码和CRC校验失败CRC一直失败一般先从波特率下手。确认传感器是9600不是19200。接着看数据位、校验位Modbus RTU大部分是8N1少数厂家用8E1。这玩意配置错了收到的字节就是乱码CRC肯定不对。接线接触不良也会导致偶发乱码。接线端子压线没压紧稍微一碰就松数据帧丢字节或错位。拖到现场排查还以为是干扰问题其实就是在板子上脱焊了。所以调试阶段尽量把线焊死或者用螺丝端子压牢固。4.3 OLED只亮蓝屏但无内容SSD1306初始化失败最常见两个原因。I2C地址不对驱动代码写的0x3C实际模块是0x3D改一下就行。还有一个是复位引脚没接对有些模块RES引脚悬空也能跑但有的必须由MCU拉高。在初始化函数里先把RES拉高再执行SSD1306_Init基本能解决。4.4 氮磷钾数值明显不合理传感器返回数值看起来正常但PH250那我跟你保证数值位序搞反了。Modbus返回的数据是高字节在前比如寄存器值0x0065十进制101传感器返回的字节流是0x00、0x65不是0x65、0x00。用memcpy或者手动组装的时候注意按大端序或者直接用((uint16_t)data[0] 8) | data[1]这样的方式换算。还有传感器校准的问题。土壤传感器出厂标定后埋在含水量极低的干土里和干净水里读数可能差异很大。测试时可以先用纯净水校准PH基准氮磷钾在标准营养液里对比。我建议买到传感器后先拿到已知环境测一遍心里有数再上项目。5. 进阶玩法从单点检测到RS485组网轮询单个节点测一块地没意思农业项目往往是多块地、多个点位这个时候RS485的好处才真正发挥出来。很多土壤传感器支持设置Modbus从站地址默认一般是0x01或0x02可以通过串口命令修改。把每根探头设置成不同的地址比如1号探头地址0x012号探头地址0x02全部并联到RS485总线上主控定期轮询各个地址就能用一根双绞线测多个点位的土壤数据。轮询间隔一般1~2秒每个从站响应时间几十毫秒10个节点轮询一遍也就一两秒实时性完全够用。组网布线有几个纪律要遵守。手拉手菊花链拓扑从主控到一个探针再到下一个探针别搞星形结构。屏蔽双绞线的屏蔽层只在主控端单点接地双端接地会产生地环路电流反而增加干扰。总线末端加120Ω终端电阻别忘了。还有一种玩法是再接一个RS485转TTL模块把数据转发到ESP8266或者ESP32通过MQTT上报到云端手机端远程看土壤数据。OLED本地显示只是第一步数据上云之后才能做自动灌溉、施肥决策这才是农业物联网的完整闭环。ST官方现在推荐用G431等新品做新设计但F103C8T6胜在生态成熟、参考资料多做原型验证和中小型项目还是够用的。最后分享一个我调试过程中的小技巧RS485方向切换后面那1ms延时不要省。HAL_UART_Transmit是阻塞式发送但函数返回不代表串口移位寄存器已经把最后一位发出去了。立马切回接收模式会把最后一个字节的尾巴吃掉导致传感器收到残缺帧直接忽略。延时1ms之后再切方向实测下来稳定可靠。同理解析完一帧数据主循环里稍微延时100ms再发下一次请求给传感器足够的响应时间避免频繁请求导致传感器忙不过来。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →