资讯动态

STM32F103多串口数据透传:WiFi+LoRa+RS485组网实战

发布时间:2026/9/15 20:35:25 来源:尧图企业网站定制
简介一套基于STM32F103C8微控制器、LoRa无线模组与RS485总线实现数据透传的嵌入式工程资料适合物联网、工业远程监控等领域开发者参考学习。资源包共234个文件大小5.46MB主要包含C语言源码32个.c、33个.h、STM32标准外设库、Keil工程配置文件.uvprojx、.uvopt以及编译生成的hex、map、axf等文件目录结构清晰便于检索。已有344人学习浏览。资源实现了RS485接口与LoRa无线链路的双向透传现场设备数据经STM32F103C8采集后由LoRa发送至远程端远程指令亦可回传整体展示了一种低功耗、远距离、强抗干扰的工业数据通信方案。完整源码与工程配置可直接用于二次开发尤其适合远程传感器数据采集、分布式设备控制等项目的快速落地。1. 一块 STM32F103 同时挂 WiFi、LoRa、RS485到底在解决什么问题把 lander_wifi、LoRa 模组、STM32F103、RS485 放在同一个标题里第一反应是“又要做网关”。但做过现场项目的人会立刻意识到另一层问题RS485 是半双工总线LoRa 是半双工无线WiFi 是网络侧接口三者串在一起真正难的其实是数据透传的方向判定和时序控制。STM32F103 的串口资源刚好够用但每一路串口的工作模式、中断优先级、缓冲区策略稍有差错就会出现“单测都通、联调就丢帧”的经典症状。这种组合常见于农业大棚、光伏电站、楼宇机电的采集终端现场老设备只出 RS485 串口后台或手机需要远程读取数据中间就得有一块板子把 Modbus/自定义协议“透明地”装进 LoRa 无线帧再交给 lander_wifi 接入网络。所谓透明就是 MCU 不做业务解析只当搬运工。这篇按“硬件接入 → 软件框架 → 组网调参 → 空中配置与验证”的顺序讲。新手可以先按第三节的最小代码跑通再回来看第二节的电路边界有经验的可以重点看第四、五节的半双工时序和无线参数下发协议。2. STM32F103 串口资源分配与 RS485 自动收发电路2.1 串口 1、串口 3 的使用差异先解决时钟和引脚STM32F103 的 USART1 挂接在 APB2 总线上USART2、USART3 挂在 APB1 上。APB2 的最高时钟是 72 MHzAPB1 是 36 MHz这意味着在相同波特率寄存器配置下两个总线的串口分频系数不同实际波特率误差也不同。常用 9600、115200 波特率时差异不明显一旦用到 460800 以上USART1 的误差更小。项目里最常见的分工是USART1 接 WiFilander_wifi 一般为 3.3V TTL 电平USART3 接 RS485外部加收发器芯片USART2 接 LoRa 模组。另一个原因是引脚冲突USART1 的 PA9/PA10 和 USART3 的 PB10/PB11 比较容易布线而 USART2 的 PA2/PA3 在很多最小系统板上被引到了调试串口容易和下载器打架。注意 PA11/PA12 的坑PA11 和 PA12 在 F103 上同时连接 USB 外设和 CAN 外设如果板子上的 USB 座子占用了 PA11USART1 的 CTS/RTS 硬件流控就不能用了。很多“串口 1 只能发不能收”的故障其实是 PA11 被 USB 的 D- 信号拉死。2.1.1 RS485 自动收发电路省掉一个 GPIORS485 是半双工所以必须控制收发器的 DE/RE 引脚。最省 IO 的接法是用三极管反相电路把 MCU 的 TXD 同时接到收发器 DI 和 NPN 三极管基极三极管集电极接 DE/RE。发送数据时TXD 为低三极管截止DE/RE 被上拉到高进入发送模式TXD 为高时三极管导通DE/RE 被拉低进入接收模式。这个电路的代价是发送时 TXD 空闲电平必须是高串口空闲就是高天然满足且发送完最后一个字节后要等一帧时间再切回接收。很多工程师在这里栽过跟头——如果发送完立刻把 USART 的发送中断关掉最后一个字节可能还没完全挤出移位寄存器总线上就切到接收了对端会收到 CRC 错误。RS485_Circuit MCU_TX —— 10K —— MAX485 DI MCU_TX —— 10K —— NPN(B) NPN(E) —— GND NPN(C) —— DE/RE(并联) DE/RE —— 10K —— 3V3提示如果使用 5V 供电的 MAX485STM32F103 的 TXD 高电平是 3.3V直接驱动三极管和 DI 通常没问题但长线时建议在 DE/RE 上再接一个 1K 上拉到 5V保证收发器进入发送态更果断。2.2 TTL 转 RS485 的电平匹配与终端电阻STM32F103 的串口是 3.3V TTLRS485 是差分信号中间必须加收发器比如 MAX485、SP3485 或 ISL3170。选型时注意 SPI3485 是 3.3V 供电的可以直接用 MCU 的 VCC而 MAX485 是 5V 供电需要额外 5V 电源。总线侧要接 120Ω 终端电阻但这个电阻不是随便焊上去的。规则是总线两端的设备各接一个 120Ω中间设备不接。如果整条总线上接了 10 个节点只有最远端的两个节点接电阻。现场常见问题是所有节点都接导致 RS485 信号幅度被拉低通讯距离缩短一个都不接则会出现总线反射波特率越高越明显。对于 lander_wifi 一侧它通常是一个带网口的WiFi 网关内部可能已经内置了 RS485 接口。如果 lander_wifi 是终端节点只用两根线 A/B 挂到总线上即可如果它是主站那它的 RS485 接口上一般已经有终端电阻不要再额外并联。2.3 双电源隔离与防雷接口的取舍热词里出现“控制器配备双电源”“标配网络防雷接口”这在 RS485 组网上是实打实的注意事项。STM32F103 和 RS485 收发器共地时如果现场还有变频器、电机地线上会有很大的共模干扰会导致 CRC 错误频发。常见做法是使用 DC-DC 隔离电源模块为 RS485 侧单独供电再用高速光耦如 6N137或者数字隔离器如 ISO3082隔离 MCU 和收发器的信号线。但注意隔离后MCU 侧和 RS485 侧的地是分开的A/B 线上的终端电阻、偏置电阻要放在 RS485 电源侧。很多同学把偏置电阻放在 MCU 侧等于没接。防雷接口不是 PCB 上画一个放电管就行而是要在 A/B 线对地之间并联 TVS 管再串一个自恢复保险丝。最简单可靠的是用带隔离的成品模块比如 B0505S 加 ISO3082成本比自搭电路高一点但能少跑一次现场。3. STM32F103 数据透传软件框架双缓冲、空闲中断与路由判定3.1 数据透传的本质不解析业务只搬运帧“透传”听起来简单实际上有两个关键技术点一是从哪里判断一帧数据结束二是多路串口数据同时到达时怎么办。RS485 总线上最常见的报文是 Modbus RTU它没有固定的帧头帧尾而是靠“静默时间”来分帧3.5 个字符时间内没有新的字节就认为一帧结束。LoRa 模组也有类似机制但 LoRa 的空中速率一般在 0.3~50 kbps比串口慢得多所以 MCU 发往 LoRa 模组的数据要拼成一整帧不能让 LoRa 一个字节一个字节地往外发否则空中占用时间会成倍增加。我的做法是所有串口都开 DMA 空闲中断。接收时串口把字节直接搬进 DMA 缓冲区空闲中断触发表示一帧接收完毕。然后判断这一帧来自哪路串口再决定往哪里转发。三路串口镜像转发的问题在于MCU 的内存很小。STM32F103 的 RAM 只有 20KB 到 64KB如果给每个串口都开 1KB 接收缓冲再开 1KB 发送缓冲6 个缓冲区就占了 6KB在 C8T6 上已经很紧张。建议发送方向不要拷贝数据而是用指针传递接收缓冲区才需要独立开辟。3.2 串口空闲中断 DMA 的初始化代码框架以 HAL 库为例下面给出使用 USART1接 WiFi、USART2接 LoRa、USART3接 RS485的初始化框架。名字按工程习惯定义实际项目中请按自己的外设替换。// 每个串口的接收缓冲区以及 DMA 接收句柄 #define RX_BUF_SIZE 256 uint8_t usart1_rx_buf[RX_BUF_SIZE]; uint8_t usart2_rx_buf[RX_BUF_SIZE]; uint8_t usart3_rx_buf[RX_BUF_SIZE]; volatile uint8_t usart1_frame_flag 0; volatile uint16_t usart1_frame_len 0; // usart2、usart3 同理 void MX_USART1_UART_Init(void) { huart1.Instance USART1; huart1.Init.BaudRate 115200; huart1.Init.WordLength UART_WORDLENGTH_8B; huart1.Init.StopBits UART_STOPBITS_1; huart1.Init.Parity UART_PARITY_NONE; huart1.Init.Mode UART_MODE_TX_RX; huart1.Init.HwFlowCtl UART_HWCONTROL_NONE; HAL_UART_Init(huart1); // 开启空闲中断需要注意必须在 DMA 接收之前使能 IDLE 中断 __HAL_UART_ENABLE_IT(huart1, UART_IT_IDLE); HAL_UART_Receive_DMA(huart1, usart1_rx_buf, RX_BUF_SIZE); } // 中断入口中判断 IDLE 标志保存长度后重新调用 DMA 接收 void USART1_IRQHandler(void) { uint32_t isr READ_REG(huart1.Instance-SR); if (isr UART_FLAG_IDLE) { __HAL_UART_CLEAR_IDLEFLAG(huart1); HAL_UART_DMAStop(huart1); // 停止 DMA防止覆盖 usart1_frame_len RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(huart1.hdmatx); // 注意是 hdmarx usart1_frame_flag 1; HAL_UART_Receive_DMA(huart1, usart1_rx_buf, RX_BUF_SIZE); } HAL_UART_IRQHandler(huart1); }上面的代码里有一个容易搞错的点__HAL_DMA_GET_COUNTER传入的是huart-hdmarx而不是hdmatx因为我们要拿的是接收 DMA 剩余的字节数剩余量等于“缓冲区大小减实际接收长度”。很多新手在这里传入hdmatx导致帧长度永远算错看起来像丢包。HAL_UART_DMAStop和HAL_UART_Receive_DMA的组合作用是先停掉 DMA再从缓冲区头开始接收。这里有个误区如果一帧数据正好填满 256 字节缓冲区且后续还有数据空闲中断不会触发DMA 会自动停止循环模式除外。因此缓冲区大小要大于业务最长大包工程上给最大协议帧留 20% 余量。3.3 多路数据同时到达时的路由判定有了三路独立接收标志后主循环里做路由。最简单的策略是优先级RS485 总线上收到的一帧优先转发到 LoRaLoRa 收到的一帧往 RS485 发同时往 WiFi 串口发一份用于监控。这样避免回环RS485 收到的数据不转发给 WiFi 串口除非你确认 lander_wifi 不会把同样的数据再发回来。while (1) { // 从 RS485 收上来发到 LoRa 无线 if (usart3_frame_flag) { HAL_UART_Transmit(huart2, usart3_rx_buf, usart3_frame_len, 100); usart3_frame_flag 0; } // 从 LoRa 收到无线数据发往 RS485 总线 if (usart2_frame_flag) { HAL_UART_Transmit(huart3, usart2_rx_buf, usart2_frame_len, 100); // 同时把数据分析发到 WiFi 串口用于上位机监控 HAL_UART_Transmit(huart1, usart2_rx_buf, usart2_frame_len, 100); usart2_frame_flag 0; } // 从网络侧下发指令直接走 LoRa if (usart1_frame_flag) { HAL_UART_Transmit(huart2, usart1_rx_buf, usart1_frame_len, 100); usart1_frame_flag 0; } }这里有一个必须处理的时序问题RS485 是半双工主机发完一帧后从机需要处理时间才能应答。如果主循环在发送完给 RS485 的数据后立刻切换到接收模式从机还没来得及拉高总线就会丢应答。所以发送函数HAL_UART_Transmit返回后不要立即打开接收建议根据波特率延时至少 1ms9600 波特率下一字节约 1.04ms需要 1.5 字节时间以上。这就是很多人做一主多从轮询时最后一个从机总是无响应的原因——不是从机坏了是主机切得太快。4. 组网参数设置与 RS485 一主多从的时序控制4.1 LoRa 参数与串口波特率的匹配LoRa 模组的串口波特率和空中速率是两个概念。串口波特率决定了 MCU 与模组之间的本地传输速度空中速率决定了无线传输的时间。大多数 LoRa 模组支持 1200~115200 的串口波特率但空中速率一般只有 0.3~50 kbps。这里的匹配法则是串口波特率可以高于空中速率但不能相差 10 倍以上。原因是模组内部有 FIFO当空中速率低于串口速率时模组会把待发送数据存在 FIFO 里慢慢发完一旦 FIFO 满了串口再发进来的数据就会丢弃。很多教程建议下面这样设LoRa 串口波特率 9600空中速率 2400这样一包 64 字节的数据串口 70ms 传完空中要 210ms 左右。实际项目中如果网络有多台节点要避免 LoRa 模组使用默认的“透明传输”模式一拥而上。透明传输模式下节点收到 RS485 数据立刻往空中发多个节点同时抢信道就互相干扰。建议把 LoRa 模组设为“定点传输”或“分包传输”模式在模组配置里加上目标地址让 MCU 在数据头加上目标节点地址这样主站发下来的 Modbus 轮询命令只会被目标节点接收到。4.2 RS485 一主多从轮询的时序表用 STM32F103 做主站轮询 RS485 总线推荐一个常规的总线时序表。假设波特率 9600Modbus RTU 协议环节时间要求说明帧间间隔不小于 3.5 字符时间9600 下约 4ms从机用它判断帧结束主机发送到从机应答的等待时间50ms~200ms 可配从机可能正在处理其他任务从机无应答的超时重发3 次每次间隔 500ms超过 3 次则认为该从机离线轮询下一台的间隔至少 10ms给总线释放时间这组参数怎么调实践中如果现场连了变频器建议把帧间间隔从 4ms 加大到 10ms。因为变频器会产生谐波干扰RS485 总线上的数据经过长线后边沿变缓从机的采样点不确定静默时间太短容易把上一帧的尾巴和新一帧的头误拼在一起。另外主站发广播命令地址 0时所有从机都会执行但不回复因此广播后不需要等待应答直接发下一帧。4.3 通讯干扰的排查与 CRC 错误处理RS485 通讯干扰的典型表现是350 米以内正常超过 400 米偶尔乱码或者马达一启动数据就错。排查步骤是先用示波器测 A 对 B 之间的差分波形。正常时空闲总线 A-B 电压差约 200mV~500mV由偏置电阻决定数据跳变时波形边沿应干净无振铃。如果波形边沿有毛刺先确认终端电阻是否完整数量是否超过 2 个。如果波形幅度低于 200mV检查总线上的设备是否太多部分旧设备 A/B 端有下拉电阻会拉低差分电平。用笔记本串口配合抓包工具看错误帧的 CRC。如果 CRC 错误的帧总是出现在同一从机地址附近怀疑该从机的收发器芯片老化或者它的地线接触不良。还有一个现场玄学RS485 的 A/B 线接反了通常一点数据都收不到但如果从机端有 A/B 自动识别电路可能只有部分帧出错。这种时候别怀疑协议先去检查每个端子的 A/B 标号——施工人员经常把 A 接到 B。5. 无线参数下发与远地验证的实用技巧5.1 用串口指令空中配置 LoRa 参数很多 LoRa 模组出厂时支持 AT 指令配置但配置完成后进入透传模式AT 指令被当作普通数据发到空中。要远程改参数常见做法是保留一个特俗帧头把配置指令包装成业务数据。我常用的协议格式是字节偏移内容说明00xAA帧头10x55帧头2帧类型0x01 表示查询参数0x02 表示设置参数3数据长度 N后面有效数据的字节数4~4N-1参数内容例如 “SF7;BW125;FREQ470.5”4NCRC8对第 0 到 4N-1 字节做校验STM32F103 收到这样的帧后不把它当业务数据转发而是解析格式把字符串提交给 LoRa 模组的配置接口。这个技巧的好处是不需要维护人跑到塔顶或田间就可以为所有节点切换频点、调整扩频因子。在频段跳变时先改主站再逐台改从站避免整个网络失联。5.2 用 lander_wifi 做远程监控的透传链验证lander_wifi 通常提供一个 TCP 服务端或串口透传模式。工程上验证整条链路最简单的办法是用电脑连上 lander_wifi 的 TCP 端口发送 Modbus 03 功能码读寄存器然后观察 RS485 总线上是否出现对应的请求帧。一个容易忽略的细节是WiFi 串口透传默认会加上 TCP 的粘包拆包处理如果 lander_wifi 的缓冲区设定很小一个大包会被拆成多个 TCP segment 发给 MCUMCU 的空闲中断会在每个 segment 结束后触发一次导致一帧数据被拆成两帧转发出去。这种情况下需要把 lander_wifi 的串口波特率设成和 MCU 一致并启用流控或者把包间隔调到 50ms 以上。如果没有现成的 TCP 调试工具可以用 MATLAB 的串口或 tcpclient 接口做快速验证。用 MATLAB 发送一段十六进制数据并读取回来的内容可以快速判断数据是否被拆包或合并。注意 MATLAB 串口默认以字节为单位读取时要设置ReadAsyncMode为continuous否则回调数据会不完整。5.3 丢帧追踪在透传路径上打时间戳最后给一个非常有用的调试技巧在 STM32F103 收到每一帧时记录一个 32 位计数器值DWT-CYCCNT 或 SysTick然后通过一个调试串口把“接收时间戳 帧长 路由方向”输出。// 在帧路由前的入口处记录 uint32_t t DWT-CYCCNT; // 需要先使能 DWT printf([%lu] from%d len%d\r\n, t, src_port, frame_len);DWT-CYCCNT 是 Cortex-M3 内核的周期计数器不需要改主频就能精确到 72MHz 周期。用它记录每一帧进入 MCU 的时间可以清楚看到是哪一段路径上产生了延迟或丢帧。如果显示 RS485 收到的帧时间间隔正常但 LoRa 发出时间隔却忽大忽小说明 LoRa 模组的 FIFO 出现拥塞此时应该降低串口发送波特率或把发送间隔加大。这是我调这类透传项目时最后会做的一步比盲调重发机制有效得多。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价