文章目录
- STM32-20-485
- 1. 485总线
- 2. 485相关HAL库驱动
- 3. 485配置步骤
- 4. 代码实现
STM32-20-485
1. 485总线
-
串口、UART、TTL、RS232、RS422、RS485关系:
关系与区别:
特性 串口 UART TTL RS232 RS422 RS485 定义 数据传输接口 硬件设备 电平标准 通信标准 通信标准 通信标准 电平 - - 0V/5V ±3V到±15V -6V到+6V -6V到+6V 通信方式 串行 串行 串行 串行 差分串行 差分串行 最大传输距离 - - 短距离 100米 1200米 1200米 支持设备数 - - - 点对点 多点 多点 典型应用 数据传输 微控制器 数字电路 计算机外设 工业自动化 工业自动化 串口通信是一种通用的通信接口,可以通过不同的硬件和电平标准(如UART、TTL、RS232、RS422、RS485)来实现各种不同的应用需求。这些标准各有优缺点,适用于不同的应用场景,如短距离高速通信或长距离多设备通信。
-
485总线介绍:
RS485是串行通信标准,使用差分信号传输,抗干扰能力强,常用于工控领域。RS485具有强大的组网功能,在串口基础协议之上还制定MODBUS协议。
-
串口基础协议:仅指封装了基本数据包格式的协议(基于数据位)
-
MODBUS协议:使用基本数据包组合成通讯帧格式的高层应用协议(基于数据包或字节)
-
关系:
特性 串口基础协议 MODBUS协议 通信模式 点对点 主从模式 数据传输方式 异步或同步 异步 数据格式 起始位、数据位、校验位、停止位 设备地址、功能码、数据域、校验码 波特率 可变 固定在特定应用中 应用场景 广泛应用于各种串行通信场景 工业自动化系统 校验方式 奇偶校验或无校验 CRC校验(RTU模式)或LRC校验(ASCII模式) 数据传输效率 较低 较高(特别是RTU模式) 串口基础协议是一种底层通信协议,用于实现基本的串行数据传输,而MODBUS协议是一种应用层协议,基于串行通信实现设备间的数据交换,特别适用于工业控制系统。MODBUS协议利用串口基础协议传输数据,但增加了设备地址、功能码和校验等机制,以实现更复杂和可靠的通信。
-
-
连接示意图:
-
485通信电路分析:
TP8485
是一种用于实现RS-485通信的收发器芯片。它的主要作用是将微控制器或其他设备的UART信号转换为RS-485标准的差分信号,从而实现可靠的远距离通信。 -
单片机A发送数据到单片机B示意图:
单片机A代码:
void RS485_Send_Init(void) {//配置UART发送UART_Init(baud_rate);//配置DE和RE控制的GPIO引脚GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.Pin = DE_PIN | RE_PIN;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;HAL_GPIO_Init(GPIO_PORT, &GPIO_InitStruct);//初始化时将DE和RE设置为低电平HAL_GPIO_WritePin(GPIO_PORT, DE_PIN, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIO_PORT, RE_PIN, GPIO_PIN_RESET); }void RS485_Send(uint8_t *data, uint16_t length) {//设置DE和RE为高电平,使能发送模式HAL_GPIO_WritePin(GPIO_PORT, DE_PIN, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIO_PORT, RE_PIN, GPIO_PIN_SET);//发送数据HAL_UART_Transmit(&huart, data, length, HAL_MAX_DELAY);//发送完成后,将DE和RE设置为低电平,禁用发送器HAL_GPIO_WritePin(GPIO_PORT, DE_PIN, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIO_PORT, RE_PIN, GPIO_PIN_RESET); }
单片机B代码:
void RS485_Receive_Init(void) {//配置UART接收UART_Init(baud_rate);//配置DE和RE控制的GPIO引脚GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.Pin = DE_PIN | RE_PIN;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;HAL_GPIO_Init(GPIO_PORT, &GPIO_InitStruct);//初始化时将DE设置为低电平,RE设置为低电平,使能接收器HAL_GPIO_WritePin(GPIO_PORT, DE_PIN, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIO_PORT, RE_PIN, GPIO_PIN_RESET); }uint8_t RS485_Receive(void) {uint8_t rec_data;HAL_UART_Receive(&huart, &rec_data, 1, HAL_MAX_DELAY);return rec_data; }
作为驱动器和接收器时,电平状态:
波形示意图:
接线示意图:
2. 485相关HAL库驱动
驱动函数 | 关联寄存器 | 功能描述 |
---|---|---|
__HAL_RCC_USARTx_CLK_ENABLE() | 使能串口时钟 | |
HAL_UART_Init() | USART_CR1/CR2 | 初始化串口 |
__HAL_UART_ENABLE_IT() | USART_CR1 | 使能串口相关中断 |
HAL_UART_Receive() | USART_DR | 串口接收数据 |
HAL_UART_Transmit() | USART_DR | 串口发送数据 |
__HAL_UART_GET_FLAG() | USART_SR | 查询当前串口的状态 |
3. 485配置步骤
-
配置串口工作参数
HAL_UART_Init()
-
串口底层初始化
HAL_UART_MspInit()
-
开启串口异步接收中断
__HAL_UART_ENABLE_IT()
-
设置优先级,使能中断
HAL_NVIC_SetPriority() HAL_NVIC_EnableIRQ()
-
编写中断服务函数
USARTx_IRQHandler() HAL_UART_Receive()
-
串口数据发送
HAL_UART_Transmit()
4. 代码实现
-
功能: 通过连接两个战舰STM32F103的RS485接口,然后由KEY0控制发送,当按下一个开发板的KEY0的时候,就发送5个数据给另外一个开发板,并在两个开发板上分别显示发送的值和接收到的值。
-
硬件原理图:
-
RS485初始化函数:
void rs485_init(uint32_t baudrate) {// IO 及 时钟配置 RS485_RE_GPIO_CLK_ENABLE(); // 使能 RS485_RE 脚时钟 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;HAL_GPIO_Init(RS485_TX_GPIO_PORT, &gpio_initure); // 串口TX 脚 模式设置 gpio_initure.Pin = RS485_RX_GPIO_PIN;gpio_initure.Mode = GPIO_MODE_AF_INPUT;HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_initure); // 串口RX 脚 必须设置成输入模式 gpio_initure.Pin = RS485_RE_GPIO_PIN;gpio_initure.Mode = GPIO_MODE_OUTPUT_PP;gpio_initure.Pull = GPIO_PULLUP;gpio_initure.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(RS485_RE_GPIO_PORT, &gpio_initure); // RS485_RE 脚 模式设置 // USART 初始化设置 g_rs485_handler.Instance = RS485_UX; // 选择485对应的串口 g_rs485_handler.Init.BaudRate = baudrate; // 波特率 g_rs485_handler.Init.WordLength = UART_WORDLENGTH_8B; // 字长为8位数据格式 g_rs485_handler.Init.StopBits = UART_STOPBITS_1; // 一个停止位 g_rs485_handler.Init.Parity = UART_PARITY_NONE; // 无奇偶校验位 g_rs485_handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 无硬件流控 g_rs485_handler.Init.Mode = UART_MODE_TX_RX; // 收发模式 HAL_UART_Init(&g_rs485_handler); // HAL_UART_Init()会使能UART2 #if RS485_EN_RX // 如果使能了接收 // 使能接收中断 __HAL_UART_ENABLE_IT(&g_rs485_handler, UART_IT_RXNE); // 开启接收中断 HAL_NVIC_EnableIRQ(RS485_UX_IRQn); // 使能USART2中断 HAL_NVIC_SetPriority(RS485_UX_IRQn, 3, 3); // 抢占优先级3,子优先级3 #endifRS485_RE(0); // 默认为接收模式 }
-
RS485发送len个字节函数:
void rs485_send_data(uint8_t *buf, uint8_t len) {RS485_RE(1); // 进入发送模式 HAL_UART_Transmit(&g_rs485_handler, buf, len, 1000); // 串口2发送数据 g_RS485_rx_cnt = 0;RS485_RE(0); // 进入接收模式 }
-
RS485查询接收到的数据函数:
void rs485_receive_data(uint8_t *buf, uint8_t *len) {uint8_t rxlen = g_RS485_rx_cnt;uint8_t i = 0;*len = 0; // 默认为0 delay_ms(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; // 记录本次数据长度 g_RS485_rx_cnt = 0; // 清零} }
-
接收中断服务函数:
uint8_t g_RS485_rx_buf[RS485_REC_LEN]; // 接收缓冲, 最大 RS485_REC_LEN 个字节. uint8_t g_RS485_rx_cnt = 0; // 接收到的数据长度 void RS485_UX_IRQHandler(void) {uint8_t res;if ((__HAL_UART_GET_FLAG(&g_rs485_handler, UART_FLAG_RXNE) != RESET)) // 接收到数据 {HAL_UART_Receive(&g_rs485_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 }} }
-
主函数:
int main(void) {uint8_t key;uint8_t i = 0, t = 0;uint8_t cnt = 0;uint8_t rs485buf[5];HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72); /* 延时初始化 */usart_init(115200); /* 串口初始化为115200 */led_init(); /* 初始化LED */lcd_init(); /* 初始化LCD */key_init(); /* 初始化按键 */rs485_init(9600); /* 初始化RS485 */lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);lcd_show_string(30, 70, 200, 16, 16, "RS485 TEST", RED);lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);lcd_show_string(30, 110, 200, 16, 16, "KEY0:Send", RED); /* 显示提示信息 */lcd_show_string(30, 130, 200, 16, 16, "Count:", RED); /* 显示当前计数值 */lcd_show_string(30, 150, 200, 16, 16, "Send Data:", RED); /* 提示发送的数据 */lcd_show_string(30, 190, 200, 16, 16, "Receive Data:", RED);/* 提示接收到的数据 */while (1){key = key_scan(0);if (key == 2) /* KEY0按下,发送一次数据 */{for (i = 0; i < 5; i++){rs485buf[i] = cnt + i; /* 填充发送缓冲区 */lcd_show_xnum(30 + i * 32, 170, rs485buf[i], 3, 16, 0X80, BLUE); /* 显示数据 */}rs485_send_data(rs485buf, 5); /* 发送5个字节 */}rs485_receive_data(rs485buf, &key);if (key) /* 接收到有数据 */{if (key > 5) key = 5; /* 最大是5个数据. */for (i = 0; i < key; i++){lcd_show_xnum(30 + i * 32, 210, rs485buf[i], 3, 16, 0X80, BLUE); /* 显示数据 */}}t++;delay_ms(10);if (t == 20){LED0_TOGGLE(); /* LED0闪烁, 提示系统正在运行 */t = 0;cnt++;lcd_show_xnum(30 + 48, 130, cnt, 3, 16, 0X80, BLUE); /* 显示数据 */}} }
声明:资料来源(战舰STM32F103ZET6开发板资源包)
- Cortex-M3权威指南(中文).pdf
- STM32F10xxx参考手册_V10(中文版).pdf
- STM32F103 战舰开发指南V1.3.pdf
- STM32F103ZET6(中文版).pdf
- 战舰V4 硬件参考手册_V1.0.pdf