赞
踩
1、串口是一个泛称,常用的串口设备:UART、TTL、RS232、RS485和RS422:
综上所述,UART是生成串行通信时序的硬件模块,而TTL、RS232、RS422和RS485则分别代表了不同的电气接口标准和逻辑电平标准。在实际应用中,UART产生的TTL电平信号通常需要通过电平转换器转化为RS232、RS422或RS485标准的信号,以便在不同的物理环境中进行可靠的串行通信。
2、USB转RS485串口线定义:
(1)USB口引脚定义
(2)RS422/RS485的DB9口引脚定义
(3)RS422/RS485转接板引脚定义
3、USB转RS422/485通讯接线示意图
(1)RS422到RS422通讯
(2)RS422点对多点/四线全双工通讯
(3)RS485到RS485通讯
(4)RS485点对多点半双工通讯
1、串口基础协议(Serial Port Basic Protocol)通常指的是用于串行通信的基本规则,它定义了数据在串行链路上如何进行传输,包括但不限于以下几个关键要素:
2、Modbus协议则建立在串口基础协议之上,是一种应用层协议,用于在不同设备间进行数据交换,特别是工业控制系统中的现场设备如PLC、智能仪表、传感器和执行器等。Modbus协议的特点包括:
3、Modbus协议的数据帧结构:
在实际应用中,串口基础协议提供的是物理层和链路层的通信基础,而Modbus协议则是更高层次的应用层协议,它规定了如何在串口通信的基础上构造有意义的消息结构,从而实现设备间复杂的控制和数据交换。Modbus协议在实际应用中,主设备可以向从设备发出读写请求,从设备在接收到请求后根据功能码执行相应操作,并将结果返回给主设备。由于其简洁、通用和开放的特性,MODBUS在工业自动化、楼宇自动化、能源管理系统等多个领域得到广泛应用。
特点:
常用RS485通信电路
如果 A - B 的电压差大于等于 +0.2V,表明总线上接收到了逻辑“1”,因此RO输出高电平(逻辑“1”)。
如果 A - B 的电压差小于等于 - 0.2V,表明总线上接收到了逻辑“0”,因此RO输出低电平(逻辑“0”)。
当DI为低电平时,驱动器会让A线变低,B线变高,从而在总线上形成逻辑“0”的差分信号
当DI为高电平时,驱动器会让A线变高,B线变低,从而在总线上形成逻辑“1”的差分信号
RS485通信波形图
1、先对串口进行初始化配置,需要注意的是:接收数据采用中断的等方式进行接收,也可以采用DMA的方式进行接收数据,并且在配置的时候需要对控制应交进行配置,默认为接收模式。使用其他串口的时候需要对引脚进行指定,使用gpio_initure.Alternate = GPIO_AF7_USART3; 这种来进行指定。
/** * @brief RS485初始化函数 * @note 函数主要是初始化串口 * @param baudrate: 波特率, 根据自己需要设置波特率值 */ void rs485_init(uint32_t baudrate) { /* IO 及 时钟配置 */ RS485_RE_GPIO_CLK_ENABLE(); /* 使能 RS485_RE DE 脚时钟 */ RS485_TX_GPIO_CLK_ENABLE(); /* 使能 串口TX脚 时钟 */ RS485_RX_GPIO_CLK_ENABLE(); /* 使能 串口RX脚 时钟 */ RS485_UX_CLK_ENABLE(); /* 使能 串口 时钟 */ GPIO_InitTypeDef gpio_initure; gpio_initure.Pin = RS485_TX_GPIO_PIN; gpio_initure.Mode = GPIO_MODE_AF_PP; gpio_initure.Pull = GPIO_PULLUP; gpio_initure.Speed = GPIO_SPEED_FREQ_HIGH; gpio_initure.Alternate = GPIO_AF7_USART3; HAL_GPIO_Init(RS485_TX_GPIO_PORT, &gpio_initure); /* 串口TX 脚 模式设置 */ gpio_initure.Pin = RS485_RX_GPIO_PIN; gpio_initure.Mode = GPIO_MODE_AF_PP; HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_initure); /* 串口RX 脚 必须设置成输入模式 */ gpio_initure.Pin = RS485_RE_GPIO_PIN | RS485_DE_GPIO_PIN; gpio_initure.Mode = GPIO_MODE_OUTPUT_PP; gpio_initure.Pull = GPIO_PULLUP; gpio_initure.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &gpio_initure); /* RS485_RE 脚 模式设置 */ /* USART 初始化设置 */ g_rs458_handler.Instance = RS485_UX; /* 选择485对应的串口 */ g_rs458_handler.Init.BaudRate = baudrate; /* 波特率 */ g_rs458_handler.Init.WordLength = UART_WORDLENGTH_8B; /* 字长为8位数据格式 */ g_rs458_handler.Init.StopBits = UART_STOPBITS_1; /* 一个停止位 */ g_rs458_handler.Init.Parity = UART_PARITY_NONE; /* 无奇偶校验位 */ g_rs458_handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 无硬件流控 */ g_rs458_handler.Init.Mode = UART_MODE_TX_RX; /* 收发模式 */ HAL_UART_Init(&g_rs458_handler); /* HAL_UART_Init()会使能UART3 */ #if RS485_EN_RX /* 如果使能了接收 */ /* 使能接收中断 */ __HAL_UART_ENABLE_IT(&g_rs458_handler, UART_IT_RXNE); /* 开启接收中断 */ HAL_NVIC_EnableIRQ(RS485_UX_IRQn); /* 使能USART3中断 */ HAL_NVIC_SetPriority(RS485_UX_IRQn, 0, 0); /* 抢占优先级3,子优先级3 */ #endif RS485_RE(0); /* 默认为接收模式 */ }
2、编写RS485的发送函数
/**
* @brief RS485发送len个字节
* @param buf : 发送区首地址
* @param len : 发送的字节数(为了和本代码的接收匹配,这里建议不要超过 RS485_REC_LEN 个字节)
*/
void rs485_send_data(uint8_t *buf, uint8_t len)
{
RS485_RE(1); /* 进入发送模式 */
HAL_UART_Transmit(&g_rs458_handler, buf, len, 1000); /* 串口3发送数据 */
g_RS485_rx_cnt = 0;
RS485_RE(0); /* 进入接收模式 */
}
3、编写接收中断函数
/** * @brief RS485查询接收到的数据 * @param buf : 接收缓冲区首地址 * @param len : 接收到的数据长度 0 - 表示没有接收到任何数据,其他 - 表示接收到的数据长度 */ void rs485_receive_data(uint8_t *buf, uint8_t *len) { uint8_t rxlen = g_RS485_rx_cnt; uint8_t i = 0; *len = 0; /* 默认为0 */ HAL_Delay(10); /* 等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束 */ if (rxlen == g_RS485_rx_cnt && rxlen) /* 接收到了数据,且接收完成了 */ { for (i = 0; i < rxlen; i++) { buf[i] = g_RS485_rx_buf[i]; } *len = g_RS485_rx_cnt; /* 记录本次数据长度 */ memset(g_RS485_rx_buf, 0, RS485_REC_LEN); g_RS485_rx_cnt = 0; /* 清零 */ } } /** * @brief RS485接收中断函数 * @param huart 串口句柄 */ void RS485_UX_IRQHandler(UART_HandleTypeDef *huart) { uint8_t res; if ((__HAL_UART_GET_FLAG(&g_rs458_handler, UART_FLAG_RXNE) != RESET)) /* 接收到数据 */ { HAL_UART_Receive(&g_rs458_handler, &res, 1, 1000); if (g_RS485_rx_cnt < RS485_REC_LEN) /* 缓冲区未满 */ { g_RS485_rx_buf[g_RS485_rx_cnt] = res; /* 记录接收到的值 */ g_RS485_rx_cnt++; /* 接收数据增加1 */ } } }
4、总体代码
#include "rs485.h" UART_HandleTypeDef g_rs458_handler; /* RS485控制句柄(串口) */ uint8_t g_RS485_rx_buf[RS485_REC_LEN]; /* 接收缓冲, 最大 RS485_REC_LEN 个字节. */ uint8_t g_RS485_rx_cnt = 0; /* 接收到的数据长度 */ /** * @brief RS485初始化函数 * @note 函数主要是初始化串口 * @param baudrate: 波特率, 根据自己需要设置波特率值 */ void rs485_init(uint32_t baudrate) { /* IO 及 时钟配置 */ RS485_RE_GPIO_CLK_ENABLE(); /* 使能 RS485_RE DE 脚时钟 */ RS485_TX_GPIO_CLK_ENABLE(); /* 使能 串口TX脚 时钟 */ RS485_RX_GPIO_CLK_ENABLE(); /* 使能 串口RX脚 时钟 */ RS485_UX_CLK_ENABLE(); /* 使能 串口 时钟 */ GPIO_InitTypeDef gpio_initure; gpio_initure.Pin = RS485_TX_GPIO_PIN; gpio_initure.Mode = GPIO_MODE_AF_PP; gpio_initure.Pull = GPIO_PULLUP; gpio_initure.Speed = GPIO_SPEED_FREQ_HIGH; gpio_initure.Alternate = GPIO_AF7_USART3; HAL_GPIO_Init(RS485_TX_GPIO_PORT, &gpio_initure); /* 串口TX 脚 模式设置 */ gpio_initure.Pin = RS485_RX_GPIO_PIN; gpio_initure.Mode = GPIO_MODE_AF_PP; HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_initure); /* 串口RX 脚 必须设置成输入模式 */ gpio_initure.Pin = RS485_RE_GPIO_PIN | RS485_DE_GPIO_PIN; gpio_initure.Mode = GPIO_MODE_OUTPUT_PP; gpio_initure.Pull = GPIO_PULLUP; gpio_initure.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &gpio_initure); /* RS485_RE 脚 模式设置 */ /* USART 初始化设置 */ g_rs458_handler.Instance = RS485_UX; /* 选择485对应的串口 */ g_rs458_handler.Init.BaudRate = baudrate; /* 波特率 */ g_rs458_handler.Init.WordLength = UART_WORDLENGTH_8B; /* 字长为8位数据格式 */ g_rs458_handler.Init.StopBits = UART_STOPBITS_1; /* 一个停止位 */ g_rs458_handler.Init.Parity = UART_PARITY_NONE; /* 无奇偶校验位 */ g_rs458_handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 无硬件流控 */ g_rs458_handler.Init.Mode = UART_MODE_TX_RX; /* 收发模式 */ HAL_UART_Init(&g_rs458_handler); /* HAL_UART_Init()会使能UART3 */ #if RS485_EN_RX /* 如果使能了接收 */ /* 使能接收中断 */ __HAL_UART_ENABLE_IT(&g_rs458_handler, UART_IT_RXNE); /* 开启接收中断 */ HAL_NVIC_EnableIRQ(RS485_UX_IRQn); /* 使能USART3中断 */ HAL_NVIC_SetPriority(RS485_UX_IRQn, 0, 0); /* 抢占优先级3,子优先级3 */ #endif RS485_RE(0); /* 默认为接收模式 */ } /** * @brief RS485发送len个字节 * @param buf : 发送区首地址 * @param len : 发送的字节数(为了和本代码的接收匹配,这里建议不要超过 RS485_REC_LEN 个字节) */ void rs485_send_data(uint8_t *buf, uint8_t len) { RS485_RE(1); /* 进入发送模式 */ HAL_UART_Transmit(&g_rs458_handler, buf, len, 1000); /* 串口3发送数据 */ g_RS485_rx_cnt = 0; RS485_RE(0); /* 进入接收模式 */ } /** * @brief RS485查询接收到的数据 * @param buf : 接收缓冲区首地址 * @param len : 接收到的数据长度 0 - 表示没有接收到任何数据,其他 - 表示接收到的数据长度 */ void rs485_receive_data(uint8_t *buf, uint8_t *len) { uint8_t rxlen = g_RS485_rx_cnt; uint8_t i = 0; *len = 0; /* 默认为0 */ HAL_Delay(10); /* 等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束 */ if (rxlen == g_RS485_rx_cnt && rxlen) /* 接收到了数据,且接收完成了 */ { for (i = 0; i < rxlen; i++) { buf[i] = g_RS485_rx_buf[i]; } *len = g_RS485_rx_cnt; /* 记录本次数据长度 */ memset(g_RS485_rx_buf, 0, RS485_REC_LEN); g_RS485_rx_cnt = 0; /* 清零 */ } } /** * @brief RS485接收中断函数 * @param huart 串口句柄 */ void RS485_UX_IRQHandler(UART_HandleTypeDef *huart) { uint8_t res; if ((__HAL_UART_GET_FLAG(&g_rs458_handler, UART_FLAG_RXNE) != RESET)) /* 接收到数据 */ { HAL_UART_Receive(&g_rs458_handler, &res, 1, 1000); if (g_RS485_rx_cnt < RS485_REC_LEN) /* 缓冲区未满 */ { g_RS485_rx_buf[g_RS485_rx_cnt] = res; /* 记录接收到的值 */ g_RS485_rx_cnt++; /* 接收数据增加1 */ } } }
#ifndef APPLICATIONS_RS485_H_ #define APPLICATIONS_RS485_H_ #include <drv_common.h> #include <string.h> /******************************************************************************************/ /* RS485 引脚 和 串口 定义 */ #define RS485_RE_GPIO_PORT GPIOB #define RS485_RE_GPIO_PIN GPIO_PIN_15 #define RS485_DE_GPIO_PORT GPIOB #define RS485_DE_GPIO_PIN GPIO_PIN_14 #define RS485_RE_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 */ #define RS485_TX_GPIO_PORT GPIOD #define RS485_TX_GPIO_PIN GPIO_PIN_8 #define RS485_TX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0) /* PD口时钟使能 */ #define RS485_RX_GPIO_PORT GPIOD #define RS485_RX_GPIO_PIN GPIO_PIN_9 #define RS485_RX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0) /* PD口时钟使能 */ #define RS485_UX USART3 #define RS485_UX_IRQn USART3_IRQn #define RS485_UX_IRQHandler USART3_IRQHandler #define RS485_UX_CLK_ENABLE() do{ __HAL_RCC_USART3_CLK_ENABLE(); }while(0) /* USART3 时钟使能 */ /******************************************************************************************/ /* 控制RS485_RE脚, 控制RS485发送/接收状态 * RS485_RE = 0, 进入接收模式 * RS485_RE = 1, 进入发送模式 */ #define RS485_RE(x) do{ x ? \ HAL_GPIO_WritePin(GPIOB, RS485_RE_GPIO_PIN | RS485_DE_GPIO_PIN, GPIO_PIN_SET) : \ HAL_GPIO_WritePin(GPIOB, RS485_RE_GPIO_PIN | RS485_DE_GPIO_PIN, GPIO_PIN_RESET); \ }while(0) #define RS485_REC_LEN 64 /* 定义最大接收字节数 64 */ #define RS485_EN_RX 1 /* 使能(1)/禁止(0)RS485接收 */ extern uint8_t g_RS485_rx_buf[RS485_REC_LEN]; /* 接收缓冲,最大RS485_REC_LEN个字节 */ extern uint8_t g_RS485_rx_cnt; /* 接收数据长度 */ void rs485_init( uint32_t baudrate); /* RS485初始化 */ void rs485_send_data(uint8_t *buf, uint8_t len); /* RS485发送数据 */ void rs485_receive_data(uint8_t *buf, uint8_t *len);/* RS485接收数据 */ #endif /* APPLICATIONS_RS485_H_ */
#include <rtthread.h> #include <drv_common.h> #define DBG_TAG "main" #define DBG_LVL DBG_LOG #include <rtdbg.h> #include <string.h> #include "rs485.h" int main(void) { uint8_t rs485buf[64]; uint8_t key; rs485_init(9600); /* 初始化RS485 */ for (int i = 0; i < 5; i++) { rs485buf[i] = i+5; /* 填充发送缓冲区 */ } rs485_send_data(rs485buf, 5); /* 发送5个字节 */ while (1) { rs485_receive_data(rs485buf, &key); if (key) { rs485_send_data(rs485buf, key); /* 发送5个字节 */ } rt_thread_mdelay(50); } return RT_EOK; }
1、通过串口进行测试,选择通信的串口、波特率为9600,选择CRC校验数据。
2、通过发送读取电源状态、输出电压、输出电流、温度的Modbus数据进行通信测试,可以看到发送的数据会被返回,当进行Modbus数据通信时,发送通信指令,会返回响应的数据,也是包含设备地址、功能码、数据、和CRC校验。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。