第21章 RS485介绍及应用

第21章 RS485介绍及应用

第二十一章 RS485介绍及应用

1. RS485简介

RS485(也被称为TIA-485或EIA-485)是一种工业串行通信标准,它定义了通信系统中驱动器和接收器的电气特性。 它不是一种通信协议,而是一种物理层标准,通常被更高级别的协议(如Modbus RTU、ProfiBus、DMX512等)所引用。

RS485的主要特点:

差分信号传输: RS485采用差分信号传输方式,通过两根信号线(A线和B线)传输一对大小相等、极性相反的对称信号。 这种方式能有效抵御共模噪声和电磁干扰,从而大大增强了抗干扰能力,使其在工业噪声环境中仍能保持较高的可靠性。

长距离传输: 得益于差分信号的高抗噪性和较低的信号衰减率,RS485可以在相对较低的数据速率下实现远距离通信,通常可达1200米(4000英尺),在某些条件下甚至能达到更远。

多点通信能力: RS485支持在同一条总线上连接多个设备,实现多节点通信。 理论上可以连接多达32个标准单元负载设备,而现在许多收发器提供更低的单位负载(如1/8 UL),允许连接多达256个收发器到总线上。

半双工通信: RS485通常采用两线半双工工作模式,即数据可以在两个方向上传输,但不能同时进行收发。 这种模式简化了硬件设计,降低了成本,对于大多数控制和监控应用来说已经足够。 也有四线全双工的RS485实现,允许同时收发数据,但需要两对信号线。

拓扑结构: RS485网络通常采用线性、总线型(菊花链)或多点配置。 为了减少信号反射和数据错误,通常需要在总线两端使用终端电阻(通常为120Ω),特别是在长距离或高速通信时。

应用广泛: RS485因其可靠性、远距离传输和多设备连接能力,在工业自动化、楼宇自动化、过程控制、电机控制、智能仪表和安防系统等领域得到了广泛应用。 例如,在工业自动化中,它常用于连接传感器、PLC(可编程逻辑控制器)和HMI(人机界面)等设备。

2. RS485应用示例

2.1 RS485宏定义

#ifndef __RS485_H__

#define __RS485_H__

#include "sys.h"

#define RS485_EN_RX 1 // 使能接收使能

#define RS485_REC_LEN 64 // 定义最大接收字节数 64

/* 控制RS485_RE脚, 控制RS485发送/接收状态

* RS485_RE = 0, 进入接收模式

* RS485_RE = 1, 进入发送模式

*/

#define RS485_RE(x) do{ x ? \

HAL_GPIO_WritePin(GPIOG, GPIO_PIN_9, GPIO_PIN_SET) : \

HAL_GPIO_WritePin(GPIOG, GPIO_PIN_9, GPIO_PIN_RESET); \

}while(0)

void rs485_init(uint32_t baudrate);

void rs485_send_data(uint8_t *buf, uint8_t len);

void rs485_receive_data(uint8_t *buf, uint8_t *len);

#endif /* __RS485_H__ */

2.2 RS485初始化

// 初始化RS485

void rs485_init(uint32_t baudrate)

{

// RE-PG8 TX-PA2 RX-PA3 UX-USART2

__HAL_RCC_GPIOG_CLK_ENABLE();

__HAL_RCC_GPIOA_CLK_ENABLE();

__HAL_RCC_USART2_CLK_ENABLE();

GPIO_InitTypeDef GPIO_InitStructure;

/* GPIO 初始化 */

GPIO_InitStructure.Pin = GPIO_PIN_2 | GPIO_PIN_3;

GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;

GPIO_InitStructure.Pull = GPIO_PULLUP;

GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;

GPIO_InitStructure.Alternate = GPIO_AF7_USART2;

HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_InitStructure.Pin = GPIO_PIN_8;

GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_InitStructure.Pull = GPIO_PULLUP;

GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;

HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);

/* USART2 初始化 */

rs485_handle.Instance = USART2;

rs485_handle.Init.BaudRate = baudrate;

rs485_handle.Init.WordLength = UART_WORDLENGTH_8B;

rs485_handle.Init.StopBits = UART_STOPBITS_1;

rs485_handle.Init.Parity = UART_PARITY_NONE;

rs485_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;

rs485_handle.Init.Mode = UART_MODE_TX_RX;

HAL_UART_Init(&rs485_handle);

__HAL_UART_DISABLE_IT(&rs485_handle, UART_IT_TC);

#ifdef RS485_EN_RX

__HAL_UART_ENABLE_IT(&rs485_handle, UART_IT_RXNE); // 使能接收中断

HAL_NVIC_EnableIRQ(USART2_IRQn);

HAL_NVIC_SetPriority(USART2_IRQn, 3, 3);

#endif // RS485_EN_RX

RS485_RE(0); // 默认接收模式

}

2.3 RS485发送和接受数据

// 发送数据

void rs485_send_data(uint8_t *buf, uint8_t len)

{

RS485_RE(1); // 打开发送模式

HAL_UART_Transmit(&rs485_handle, buf, len, 1000);

rs485_rx_cnt = 0;

RS485_RE(0); // 回到接收模式

}

/**

* @brief RS485查询接收到的数据

* @param buf : 接收缓冲区首地址

* @param len : 接收到的数据长度

* @arg 0 , 表示没有接收到任何数据

* @arg 其他, 表示接收到的数据长度

* @retval 无

*/

void rs485_receive_data(uint8_t *buf, uint8_t *len)

{

uint8_t rxlen = rs485_rx_cnt;

uint8_t i = 0;

*len = 0; // 默认为0

delay_ms(10); // 等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束

if (rxlen == rs485_rx_cnt && rxlen) // 接收到了数据,且接收完成了

{

for (i = 0; i < rxlen; i++)

{

buf[i] = rs485_rx_buf[i];

}

*len = rs485_rx_cnt; // 记录本次数据长度

rs485_rx_cnt = 0;

}

}

2.4 主函数测试

#include "bsp_init.h"

#include "stdio.h"

#include "rs485.h"

int main(void)

{

uint8_t key_value = 0;

uint8_t i, t, cnt = 0;

uint8_t rs485buf[5];

bsp_init();

rs485_init(115200);

LCD_ShowString(30,50,200,16,16,"STM32 RS485 Test");

LCD_ShowString(30,110,200,16,16,"KEY0:Send");

LCD_ShowString(30,130,200,16,16,"Count:");

LCD_ShowString(30,150,200,16,16,"Sned Data:");

LCD_ShowString(30,190,200,16,16,"Receive Data:");

while(1)

{

key_value = key_scan(0);

if(key_value == KEY0_Press) // Send data

{

for(i=0;i<5;i++)

{

rs485buf[i] = cnt+i; // 填充发送缓冲区

LCD_ShowxNum(30+i*32,170,rs485buf[i],3,16,0x80); // 显示发送数据

}

rs485_send_data(rs485buf,5); // 发送数据

}

rs485_receive_data(rs485buf,&key_value); // 接收数据

if(key_value) // 接收到数据

{

if(key_value > 5)

{

key_value = 5;

}

for(i=0;i

{

LCD_ShowxNum(30+i*32,210,rs485buf[i],3,16,0x80); // 显示接收数据

}

}

t++;

delay_ms(10);

if(t == 20)

{

LED_TOGGLE(LED0_GPIO_Pin);

t = 0;

cnt++;

LCD_ShowxNum(78,130,cnt,3,16,0x80); // 显示计数

}

}

}

3. RS485相关函数(HAL库)

3.1硬件配置

3.1.1 GPIO 配置(方向控制引脚)

// RS485 方向控制引脚初始化

void RS485_DIR_Init(void) {

GPIO_InitTypeDef GPIO_InitStruct = {0};

__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIO时钟

GPIO_InitStruct.Pin = GPIO_PIN_8; // 方向控制引脚

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_InitStruct.Pull = GPIO_NOPULL;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET); // 默认接收模式

}

3.1.2 UART 配置(RS485 基础)

UART_HandleTypeDef huart2;

void MX_USART2_UART_Init(void) {

huart2.Instance = USART2;

huart2.Init.BaudRate = 115200;

huart2.Init.WordLength = UART_WORDLENGTH_8B;

huart2.Init.StopBits = UART_STOPBITS_1;

huart2.Init.Parity = UART_PARITY_NONE;

huart2.Init.Mode = UART_MODE_TX_RX; // 收发模式

huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;

huart2.Init.OverSampling = UART_OVERSAMPLING_16;

if (HAL_UART_Init(&huart2) != HAL_OK) {

Error_Handler();

}

}

3.2 方向控制函数

// 设置为发送模式(使能驱动器)

void RS485_EnableTx(void) {

HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); // DE/RE = HIGH

HAL_Delay(1); // 等待硬件稳定(根据收发器规格调整)

}

// 设置为接收模式(禁用驱动器)

void RS485_EnableRx(void) {

HAL_Delay(1); // 确保最后一个字节发送完成

HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET); // DE/RE = LOW

}

3.3 数据收发函数

3.3.1 阻塞模式发送

void RS485_SendData(uint8_t *pData, uint16_t Size) {

RS485_EnableTx(); // 切换为发送模式

HAL_UART_Transmit(&huart2, pData, Size, 100); // UART发送

RS485_EnableRx(); // 切换回接收模式

}

3.3.2 中断模式接收

// 启动接收

void RS485_StartReceive(void) {

HAL_UART_Receive_IT(&huart2, rxBuffer, RX_BUFFER_SIZE);

}

// 接收完成回调

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {

if(huart->Instance == USART2) {

// 处理接收到的数据

Process_RS485_Data(rxBuffer, RX_BUFFER_SIZE);

// 重新启动接收

HAL_UART_Receive_IT(&huart2, rxBuffer, RX_BUFFER_SIZE);

}

}

3.3.3 DMA 模式收发(高效方案)

uint8_t txBuffer[TX_BUFFER_SIZE];

uint8_t rxBuffer[RX_BUFFER_SIZE];

// 初始化DMA

void RS485_DMA_Init(void) {

__HAL_RCC_DMA1_CLK_ENABLE();

// 配置TX DMA

hdma_tx.Instance = DMA1_Stream6;

hdma_tx.Init.Channel = DMA_CHANNEL_4;

hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;

hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE;

hdma_tx.Init.MemInc = DMA_MINC_ENABLE;

hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;

hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;

hdma_tx.Init.Mode = DMA_NORMAL;

hdma_tx.Init.Priority = DMA_PRIORITY_HIGH;

HAL_DMA_Init(&hdma_tx);

__HAL_LINKDMA(&huart2, hdmatx, hdma_tx);

// 配置RX DMA

hdma_rx.Instance = DMA1_Stream5;

hdma_rx.Init.Channel = DMA_CHANNEL_4;

hdma_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;

// ... 类似TX配置

HAL_DMA_Init(&hdma_rx);

__HAL_LINKDMA(&huart2, hdmarx, hdma_rx);

// 启动接收

HAL_UART_Receive_DMA(&huart2, rxBuffer, RX_BUFFER_SIZE);

}

// DMA发送函数

void RS485_SendData_DMA(uint8_t *data, uint16_t size) {

RS485_EnableTx(); // 切换发送模式

HAL_UART_Transmit_DMA(&huart2, data, size);

// 注意:需要在发送完成回调中切换回接收模式

}

// 发送完成回调

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {

if(huart->Instance == USART2) {

RS485_EnableRx(); // 切换回接收模式

}

}

3.4 高级控制函数

3.4.1 总线冲突检测

// 检查总线状态(可选)

bool RS485_CheckBusConflict(void) {

if(HAL_GPIO_ReadPin(RS485_RX_PIN_PORT, RS485_RX_PIN) == GPIO_PIN_SET) {

return true; // 检测到总线冲突

}

return false;

}

3.4.2 超时处理

// 带超时的发送

HAL_StatusTypeDef RS485_SendData_Timeout(uint8_t *pData, uint16_t Size, uint32_t timeout) {

RS485_EnableTx();

HAL_StatusTypeDef status = HAL_UART_Transmit(&huart2, pData, Size, timeout);

RS485_EnableRx();

return status;

}

3.4.3 自动方向控制(使用硬件流控制)

// 使用RTS引脚自动控制方向(需硬件支持)

void MX_USART2_UART_Init(void) {

// ... 其他配置

huart2.Init.HwFlowCtl = UART_HWCONTROL_RTS; // 启用RTS流控

// ...

}

// 初始化RTS引脚

GPIO_InitStruct.Pin = GPIO_PIN_1; // RTS引脚

GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

GPIO_InitStruct.Pull = GPIO_NOPULL;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

GPIO_InitStruct.Alternate = GPIO_AF7_USART2;

HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

相关推荐

全运会|特写:徐根宝的告别战?不谈告别
365bet大陆网站

全运会|特写:徐根宝的告别战?不谈告别

📅 11-15 👁️ 4808
正在阅读:每日英语听力如何下载音频 下载MP3听力文件的方法每日英语听力如何下载音频 下载MP3听力文件的方法
人民币跨境收付金额明显上升,钱怎么走、多久到账?汇款路径规划,就听导航的