STM32通信接口RS485

您所在的位置:网站首页 嵌入式SPI0接口初始化函数代码 STM32通信接口RS485

STM32通信接口RS485

2024-05-28 14:37| 来源: 网络整理| 查看: 265

18.1关于 RS485

RS485是美国电子工业协会(Electronic Industries Association,EIA)于1983年发布的串行通信接口标准,经通讯工业协会(TIA)修订后命名为TIA/EIA-485-A

RS485具有支持多节点(32个节点)、传输距离远(最大1219m)、接收灵敏度高(200mV电压)、连接简单(在构成通信网络时,仅需要一对双绞线作传输线)、能抑制共模干扰(差分传输)、成本低廉等特点,在多站、远距离通信等多种工控环境中获得了广泛应用。

RS485比RS232晚出现20多年,很多RS232的缺点,在RS485上有了改进。

RS232的电平从-15V至+15V,较高的电平值易损坏接口电路的芯片,而RS485采用差分信号后,电平范围为-6V至+6V,相对不易损坏接口电路芯片,同时RS485接口信号电平与TTL信号电平兼容,便于连接TTL电路。

RS232传输速率比较低,传输速率为20Kbps,而RS485最高传输速率达10Mbps。过高的传输速率会降低传输距离,在实际应用中,RS485传输速率往往设置为9600bps或更低。

RS232采用逻辑电平,共地传输容易产生共模干扰,抗噪声干扰性弱,传输距离有限,常用传输距离就几十米左右。而RS485采用平衡发送和差分接收方式,具有抑制共模干扰的能力,加之总线收发器具有高灵敏度,能检测低至200mV的电压,因此RS485的传输距离达到千米以外。

RS232在总线上只允许连接1个收发器,即单站能力,而RS485在总线上允许连接多达128个收发器,即具有多站能力,可以利用单一的RS485方便地建立起设备网络,如图 18.1.1 所示,为RS485通信网络结构。

在RS485通信网络中,通常使用485收发器将TTL电平转换成RS485的差分信号。MCU的串口控制器TxD发送数据,经485收发器转换成差分信号,传输到总线上。接收数据时,485收发器将总线上的差分信号转化成TTL信号由RxD到串口控制器。整个通信网络中,通常只有一个主机,剩下的全部为从机。在RS485总线中,通常还需要在总线起止端分别加上约120Ω的终端匹配电阻,以保证RS485总线的稳定性。 在这里插入图片描述 RS485同样可以使用DB9接口将信号引脚引出,实际工程中通常使用接线端子引出,如图 18.1.2 所示。图中左边的为螺钉式接线端子,适合固定连接的场合,图中右边为插拔式接线端子,适合需要调整的场合。本开发板使用的插拔式接线端子,如上图 3.3.1 中编号10部分所示。

在这里插入图片描述

18.2 硬件设计

如图 18.2.1 为开发板RS485部分的原理图,U16为3.3V低功耗半双工收发器,满足RS-485和RS-422标准。 USART的RX和TX,经过U16转换,变为RS485的A、B。

U16的2脚RE����为接收使能,上划线表示低电平有效,即当U16的2脚为低电平时,U16接收数据。U16的3 脚DE为输出使能,高电平有效,即当U16的3脚为高电平是,U16发送数据。

因此,RS485除了USART,还多了一个收发控制引脚,该引脚使用的PC5。R64为终端匹配电阻,阻值为120Ω。 在这里插入图片描述 结合前面图 17.2.1 电路和表格可知,如果需要将USART2分配给RS485使用,还要将J11(蓝色拨码开关)的1号脚拨到ON位置。

18.3软件设计 18.3.1软件设计思路

实验目的:RS485是差分信号,收发数据时,A、B都在工作。开发板也只提供了一个RS485接口,因此不能自发自收实验,需要至少两个RS485设备进行实验。这里假设两个开发板进行RS485通信,一个做主机,一个做从机,主机发送数据给从机,从机收到数据再发给主机,实现两个设备的收发数据,供读者参考和方便移植。

初始化USART1、2:设置波特率,收发选择,有效数据位等;将所使用的串口引脚初始化:USART使能、GPIO端口时钟使能、GPIO引脚设置为USART复用;RS485采用中断方式发送,编写中断回调函数;主函数编写控制逻辑:按下按键KEY1(KEY_U),主机RS485发送一次数据,从机RS485接收到数据并打印,然后从机RS485发送数据,主机RS485接受到数据并打印;在软件方面,RS485的本质跟串口没有差别,不同的地方在于:RS485在发送、接收之前,需要设置收发控制引脚。

本实验配套代码位于“5_程序源码\10_通信—RS485\”。

18.3.2软件设计讲解 GPIO 引脚选择与串口选择 本实验会用到两个串口,USART1用于调试、USART2用于RS485,在代码框架上,将每个串口都单独放在“.c”文件里,方便修改裁剪。

代码段 18.3.1 调试串口 USART1 相关宏定义(driver_usart1.h)

/********************* * 引脚宏定义 **********************/ #define DEBUG_USART USART1 #define DEBUG_USART_RX_PIN GPIO_PIN_10 #define DEBUG_USART_TX_PIN GPIO_PIN_9 #define DEBUG_USART_PORT GPIOA #define DEBUG_USART_GPIO_CLK_EN() __HAL_RCC_GPIOA_CLK_ENABLE() #define DEBUG_USART_CLK_EN() __HAL_RCC_USART1_CLK_ENABLE() #define DEBUG_USART_CLK_DIS() __HAL_RCC_USART1_CLK_DISABLE() #define DEBUG_USART_IRQn USART1_IRQn

代码段 18.3.2 调试串口 USART2 相关宏定义(driver_usart2.h)

/********************* * 引脚宏定义 **********************/ #define RS485 USART2 #define RS485_RX_PIN GPIO_PIN_3 #define RS485_TX_PIN GPIO_PIN_2 #define RS485_PORT GPIOA #define RS485_GPIO_CLK_EN() __HAL_RCC_GPIOA_CLK_ENABLE() #define RE_DE_PIN GPIO_PIN_5 #define RE_DE_PORT GPIOC #define RE_DE_GPIO_CLK_EN() __HAL_RCC_GPIOC_CLK_ENABLE() /********************* * 函数宏定义 **********************/ // 此引脚高电平是发送有效接收无效;低电平时接收有效发送无效 #define RE_DE_TX() HAL_GPIO_WritePin(RE_DE_PORT, RE_DE_PIN, GPIO_PIN_SET) #define RE_DE_RX() HAL_GPIO_WritePin(RE_DE_PORT, RE_DE_PIN, GPIO_PIN_RESET) #define RS485_IRQn USART2_IRQn #define RS485_IRQHandler USART2_IRQHandler #define RS485_CLK_ENABLE() __HAL_RCC_USART2_CLK_ENABLE() #define RS485_CLK_DISABLE() __HAL_RCC_USART2_CLK_DISABLE()

分别定义了两个串口、对应GPIO、时钟使能,方便代码复用,同时定义了RS485的收发控制引脚。

初始化USART USART初始化包含两部分:协议部分和硬件部分。 协议部分放在各自“.c”文件里,硬件部分都是调用“HAL_UART_Init()”,单独创建一个“.c”文件处理。USART1作为调试串口,初始化和前面的实验一样,这里直接跳过。USART2作为RS485,初始化如代码段 18.3.3 所示。

代码段 18.3.3 USART2 初始化(driver_usart2.c)

/* * 函数名:void RS485_Init(uint32_t baudrate) * 输入参数:baudrate-串口波特率 * * 输出参数:无 * 返回值:无 * 函数作用:初始化 USART 的波特率,收发选择,有效数据位等 */ void RS485_Init(uint32_t baudrate) { husart2.Instance = RS485; // 选择 USART2 husart2.Init.BaudRate = baudrate; // 配置波特率 husart2.Init.WordLength = USART_WORDLENGTH_8B; // 配置数据有效位为 8bit husart2.Init.StopBits = USART_STOPBITS_1; // 配置一位停止位 husart2.Init.Parity = USART_PARITY_NONE; // 不设校验位 husart2.Init.Mode = USART_MODE_TX_RX; // 可收可发 husart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 使用库函数初始化 USART2 的参数 if (HAL_UART_Init(&husart2) != HAL_OK) { Error_Handler(); } }

RS485的本质还是串口,串口的初始化和之前的基本一样。RS485通常也遵循“96-N-8-1”格式,96指波特率9600,N指无校验,8指8bits数据位,1指1bit停止位。

串口协议初始化完后,都调用“HAL_UART_Init()”进行设置,在“HAL_UART_Init()”调用 “HAL_UART_MspInit()”初始化串口硬件部分。

代码段 18.3.4 USART MSP 初始化(driver_msp_usart.c)

/* * 函数名:void AL_USART_MspInit(USART_HandleTypeDef* husart) * 输入参数:husart-USART 句柄 * 输出参数:无 * 返回值:无 * 函数作用:使能 USART1、2 的时钟,使能引脚时钟,并配置引脚的复用功能 */ void HAL_UART_MspInit(UART_HandleTypeDef* husart) { // 定义 GPIO 结构体对象 GPIO_InitTypeDef GPIO_InitStruct = {0}; if(husart->Instance==DEBUG_USART) { // 使能 USART1 的时钟 DEBUG_USART_CLK_EN(); // 使能 USART1 的输入输出引脚的时钟 DEBUG_USART_GPIO_CLK_EN(); /**USART1 GPIO Configuration PA9 ------> USART1_TX PA10 ------> USART1_RX */ GPIO_InitStruct.Pin = DEBUG_USART_TX_PIN; // 选择 USART1 的 TX 引脚 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 配置为复用推挽功能 GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 引脚翻转速率快 HAL_GPIO_Init(DEBUG_USART_PORT, &GPIO_InitStruct); // 初始化 TX 引脚 GPIO_InitStruct.Pin = DEBUG_USART_RX_PIN; // 选择 RX 引脚 GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT; // 配置为输入 HAL_GPIO_Init(DEBUG_USART_PORT, &GPIO_InitStruct); // 初始化 RX 引脚 } else if(husart->Instance==RS485) { // 使能 USART2 的时钟 RS485_CLK_ENABLE(); // 使能 USART2 的输入输出和方向引脚的时钟 RS485_GPIO_CLK_EN(); RE_DE_GPIO_CLK_EN(); /**USART2 GPIO Configuration PA2 ------> USART2_TX PA3 ------> USART2_RX */ GPIO_InitStruct.Pin = RS485_TX_PIN; // 选择 USART2 的 TX 引脚 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 配置为复用推挽功能 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 引脚翻转速率快 HAL_GPIO_Init(RS485_PORT, &GPIO_InitStruct); // 初始化 TX 引脚 GPIO_InitStruct.Pin = RS485_RX_PIN; // 选择 RX 引脚 GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 配置为输入 GPIO_InitStruct.Pull = GPIO_NOPULL; // 不上拉 HAL_GPIO_Init(RS485_PORT, &GPIO_InitStruct); // 初始化 RX 引脚 GPIO_InitStruct.Pin = RE_DE_PIN; // 选择方向引脚 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 配置为输出 GPIO_InitStruct.Pull = GPIO_NOPULL; // 不上拉 HAL_GPIO_Init(RE_DE_PORT, &GPIO_InitStruct); // 初始化方向引脚 RE_DE_RX(); // 初始化后默认处于接收状态 HAL_NVIC_SetPriority(RS485_IRQn, 1, 1); // 设置 USART2 的中断等级(0-15)(0-15) // 规则:(0,0)最高,(0,1)次之依次由高到低排序到(15,15) HAL_NVIC_EnableIRQ(RS485_IRQn); // 使能 USART2 的中断 } }

先后初始化了USART1和USART2的硬件部分,其中USART2设置了中断优先级和使能了中断,便可以使用“HAL_UART_Receive_IT()”和“HAL_UART_Transmit_IT()”收发数据。接着将RS485的收发函数进行封装,如代码段 18.3.5 所示。

代码段 18.3.5 RS485 收发函数(driver_usart2.c)

/* * 函数名:void RS485_Tx(uint8_t *pdata, uint16_t sz) * 输入参数:pdata->指向发送数据所存储的首地址 sz->发送数据个数 * 输出参数:无 * 返回值:无 * 函数作用:USART2 的发送函数 */ void RS485_Tx(uint8_t *pdata, uint16_t sz) { usart2_tx_finish = 0; RE_DE_TX(); HAL_UART_Transmit_IT(&husart2, pdata, sz); } /* * 函数名:void RS485_Rx(uint8_t *pdata, uint16_t sz) * 输入参数:pdata->指向接收数据所存储的首地址 sz->接收数据个数 * 输出参数:无 * 返回值:无 * 函数作用:USART2 的接收函数 */ void RS485_Rx(uint8_t *pdata, uint16_t sz) { usart2_rx_finish = 0; HAL_UART_Receive_IT(&husart2, pdata, sz); } 11行:usart2_tx_finish为一个全局变量,用来标记USART2是否发送完成。这里将其设置为0,USART2发送完成后,在中断函数将其置为1,通过该标记便可得知USART2是否发送完成;13行:RS485设备通常默认为接收状态,以方便接收数据。这里发送数据,需要手动临时改为发送状态;14行:调用串口中断函数发送数据;27行:usart2_rx_finish为一个全局变量,用来标记USART2是否接收完成。这里将其设置为0,USART2接收数据完成后,在中断函数将其置为1,通过该标记便可得知USART2是否接收到数据;29行:RS485设备通常默认为接收状态,这里无需其它设置,直接调用串口中断函数接收数据; 中断回调函数 当USART2发生中断时,将自动调用“USART2_IRQHandler()”,“USART2_IRQHandler()”又调用“HAL_UART_IRQHandler()”,最后调用“HAL_UART_TxCpltCallback()”或“HAL_UART_RxCpltCallback()”, 在这两个回调函数里修改USART2接收/发送完成标志,以便后面查询是否收发成功。

代码段 18.3.6 USART2 中断回调函数(driver_msp_usart.c)

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == RS485) { usart2_tx_finish = 1; RE_DE_RX(); } } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == RS485) { usart2_rx_finish = 1; } }

在发送完成回调函数里,需要将RS485设置为默认的接收模式,以方便随时接收数据。

按键中断函数 因为是通过按键来控制RS485主机发送数据,这里还需要编写按键中断函数。参考前面的按键中断实验,首先初始化按键引脚、设置中断优先级、使能中断,便可在发生按键事件时,自动调用中断回调函数“HAL_GPIO_EXTI_Callback()”,在中断回调函数里,修改按键标志,以便随时查询是否该按键按下,按键中断回调函数如代码段 18.3.7 所示。

代码段 18.3.7 按键中断回调函数(driver_key.c)

/* * 函数名:void HAL_GPIO_EXTI_Callback(void) * 输入参数:无 * 输出参数:无 * 返回值:无 * 函数作用:外部中断处理函数的回调函数,用以处理不同引脚触发的中断服务最终函数 */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == KEY_UP_GPIO_PIN) { step = 0; } 主函数控制逻辑 RS485主机和RS485从机,通常是两套独立代码工程。但本实验中,除了主函数,其它的驱动代码都一样,因此将两个套代码公共部分共用,读者通过如图 18.3.1 所示下拉选择对应工程,即可切换RS485主机工程和RS485从机工程。 在这里插入图片描述 在RS485主机的主函数如代码段 18.3.8 所示。 代码段 18.3.8 RS485 主机主函数(master_main.c) // 初始化 USART1,设置波特率为 115200 bps DEBUG_USART_Init(115200); // 初始化 USART2,设置波特率为 9600 bps RS485_Init(9600); // 初始化按键 KeyInit(); // 在 windows 下字符串\n\r 表示回车 // 如果工程在编译下面这句中文的时候报错,请在“Option for target”->"C/C++"->"Misc Controls"添加“ --locale=english” printf("百问科技 www.100ask.net\n\r"); printf("RS485 收发实验\n\r"); printf("当前设备:主机\n\r"); printf("\n\r"); // 初始化 RS485 CAN 的发送信息 RS485_Msg.ID = 0x305; RS485_Msg.length = 8; for(i=0; i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3