【STM32-HAL库】RS485中断接收实战:基于STM32F103VET6的稳定通信方案

张开发
2026/4/16 2:29:18 15 分钟阅读

分享文章

【STM32-HAL库】RS485中断接收实战:基于STM32F103VET6的稳定通信方案
1. RS485通信与STM32开发基础RS485是一种常见的工业级串行通信协议相比RS232具有传输距离远最远1200米、抗干扰能力强、支持多点通信等优势。在智能电表、工业传感器、PLC控制等场景中广泛应用。STM32F103VET6作为经典的Cortex-M3内核MCU其内置的USART外设完美支持RS485通信需求。实际项目中我遇到过这样的场景需要在30米外的车间部署多个温湿度传感器通过RS485总线将数据传回控制室。传统轮询方式会导致CPU占用率高而中断接收方案能显著提升系统效率。这里要特别注意RS485的半双工特性——同一时刻只能有一个设备发送数据因此收发模式切换的时机至关重要。2. 硬件连接与电路设计2.1 正点原子精英板接口定义使用正点原子精英板开发时RS485芯片SP3485通过USART2与MCU连接。关键引脚包括PA2/USART2_TX → 485芯片DI端PA3/USART2_RX → 485芯片RO端PD7 → 收发控制引脚高电平发送/低电平接收硬件连线时容易踩的坑是忘记接终端电阻。当通信距离超过50米或速率高于19200bps时建议在总线两端各接一个120Ω终端电阻。我曾遇到通信不稳定的问题后来发现是因为末端节点未接匹配电阻导致信号反射。2.2 电磁兼容设计要点工业现场电磁环境复杂建议采取以下措施使用双绞屏蔽线如CAT5e网线在AB线间并联TVS二极管如SMBJ6.5CA防护浪涌布线时远离电机、变频器等干扰源电源端加装磁珠和去耦电容3. CubeMX工程配置3.1 USART2参数设置打开CubeMX后按以下步骤配置在Connectivity选项卡启用USART2模式选择Asynchronous波特率设为9600工业常用值数据位8bit无校验停止位1开启NVIC中断并设置合适优先级特别注意要开启USART global interrupt这是实现中断接收的关键。有次调试时发现数据接收不全最后发现是NVIC优先级配置冲突导致中断被阻塞。3.2 GPIO输出配置PD7引脚需要配置为GPIO输出在GPIO设置中找到PD7模式选择Output Push Pull初始电平设为Low默认接收模式输出速度选择Medium即可4. 中断接收代码实现4.1 空闲中断初始化在main.c中添加全局变量和初始化代码/* 用户接收缓冲区 */ uint8_t rs485_rx_buf[64]; /* 空闲中断回调函数 */ void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { if(huart-Instance USART2) { /* 收到数据后立即切回接收模式 */ HAL_GPIO_WritePin(GPIOD, GPIO_PIN_7, GPIO_PIN_RESET); /* 数据处理代码放在这里 */ process_received_data(rs485_rx_buf, Size); /* 重新开启接收 */ HAL_UARTEx_ReceiveToIdle_IT(huart2, rs485_rx_buf, sizeof(rs485_rx_buf)); } } /* 在main()初始化部分添加 */ HAL_UARTEx_ReceiveToIdle_IT(huart2, rs485_rx_buf, sizeof(rs485_rx_buf));4.2 数据发送流程发送数据时需要先切换模式void rs485_send(uint8_t *data, uint16_t len) { /* 切换为发送模式 */ HAL_GPIO_WritePin(GPIOD, GPIO_PIN_7, GPIO_PIN_SET); HAL_Delay(1); // 等待稳定 /* 发送数据 */ HAL_UART_Transmit(huart2, data, len, 100); /* 立即切回接收模式 */ HAL_GPIO_WritePin(GPIOD, GPIO_PIN_7, GPIO_PIN_RESET); }这里有个细节要注意切换收发模式后建议加1ms延时确保SP3485芯片完全进入稳定状态。实测发现不延时可能导致前几个字节丢失。5. 通信稳定性优化5.1 超时与重传机制工业现场建议实现以下保护机制接收超时如果10ms内未收到完整帧清空缓冲区发送重试发送失败后自动重试2-3次CRC校验每帧数据添加CRC16校验码/* 带CRC校验的发送函数 */ void safe_send_with_retry(uint8_t *data, uint16_t len) { uint8_t retry 3; uint16_t crc calculate_crc16(data, len); while(retry--) { if(rs485_send(data, len) HAL_OK rs485_send((uint8_t*)crc, 2) HAL_OK) { break; } HAL_Delay(5); } }5.2 总线冲突处理多主机系统中可能出现总线冲突解决方法包括实现CSMA/CD机制发送前检测总线状态采用主从架构由主机轮询各从机添加随机退避时间冲突后随机延时重发我在一个光伏监控项目中采用令牌环方式通过软件实现了各节点有序发送完全避免了冲突问题。

更多文章