基于HAL库串口DMA的RS485收发控制要点

         HAL库条件下,串口+DMA收发相对容易,串口收发有各自通道,收发互不影响。而RS485通信是半双工通信模式,同一时刻,RS485网络只能有一个网络设备处于发送状态,其他网络设备必须处于接收状态,所以,要保证RS485网络正常工作,必须协调好网络设备的收发时机。设备收发控制的本质就是对RS485设备接口芯片的收发控制引脚的电平进行控制,本文将重点介绍RS485接口芯片的收发控制引脚电平的控制时机。

1  RS485接口芯片电路

       如图1所示,U7是典型的RS485接口芯片,其接收输出引脚RO连接到MCU串口2的串口输入端RXD2;驱动输入DI引脚连接到MCU串口2的串口输出端TXD2;接收输出使能RE#引脚和驱动输出使能引脚DE短接作为DE2,连接到MCU的GPIO引脚,该GPIO引脚配置为推挽输出引脚。根据MAX3075的真值表,DE2高电平时,接口芯片处于发送状态;DE2低电平,接口芯片处于接收状态(A,B高阻态,不影响总线网络)。所以,DE2通常默认状态为低电平,使RS485接口芯片处于接收状态。该电路没有考虑远距离互联,仅适用于小型设备各部分互联使用,如果要实现较远距离(数十米甚至数百米以上)的设备之间互联,485A、485B网络标号部分要加上保护电路,请参见相关资料,这里不再赘述。

图1  RS485接口芯片电路图

    可将MCU的PA1作为DE2。

2 串口2扩展RS485+DMA的收发控制

     2.1 CUBEMAX 配置要点

下面以STM32F103RBT6为例介绍

      串口2 参数配置

串口DMA配置串口中断

串口的端口配置

为了提高串口输入抗干扰性能,通常应将RX引脚配置为输入上拉模式,参见

《STM32系列MCU串口RX引脚上拉的必要性》。RS485的收发控制端口配置

例如,将PA1作为DE2——RS485芯片的收发控制引脚,默认低电平。

工程管理按如下配置

时钟配置

CUBEMAX的其他配置从略。

然后生成MDK-ARM工程框架代码。

2.2 在工程中添加的代码

在GPIO.h中添加如下代码

#define RS485_2_TX  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); //RS485_串口2发
#define RS485_2_RX  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);//RS485_串口2收

在USART.c文件中添加如下代码:

    uint8_t  RX2_BUF[RX2_BUFFER_SIZE];          //串口2接收缓冲区
    uint8_t  TX2_BUF[TX2_BUFFER_SIZE];          //串口2发送缓冲区    
    volatile uint8_t  RX2_LEN;                   //串口2接收的数据长度实际大小    
    volatile uint8_t  RECV2_END_FLG;            //串口2接收完成标志    
    volatile uint8_t  TRANS2_END_FLG;            //串口2发送完成标志

/
//函数名称:void RS485_Usart3_DMA_Send(uint8_t *buf,uint8_t len)
//功能描述:串口2采用RS485总线实现DMA发送
//参数说明:*buf 数据帧的指针,len数据帧的长度
//返 回 值: 无
void RS485_Usart2_DMA_Send(uint8_t *buf,uint8_t len)
{
    uint8_t i;
    if(TRANS_END_FLG==0)                //如果串口发送处于完成状态
    {
        RS485_2_TX;                    //发送控制置位到发送状态——DE2高电平正在发送
        TRANS_END_FLG=1;               //发送完成标识置位———有发送
        for( i=0;i<10;i++);            //短暂延时,使DE2进入高电平稳定状态
        if(HAL_UART_Transmit_DMA(&huart2, buf,len)!= HAL_OK) //判断是否发送正常,如果出现异常则进入异常中断函数
        {
            Error_Handler();
        }
    }
}  

在USART.c的void MX_USART3_UART_Init(void)函数中添加:

/* USER CODE BEGIN USART3_Init 2 */
  __HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);				//使能串口2空闲中断
  /* USER CODE END USART3_Init 2 */

开启串口2的空闲中断接收。 

在USART.h文件中添加如下代码:

/* USER CODE BEGIN Private defines */
#define RX3_BUFFER_SIZE 100
#define TX3_BUFFER_SIZE 100	
/* USER CODE END Private defines */
/* USER CODE BEGIN Prototypes */
void RS485_Usart2_DMA_Send(uint8_t *buf,uint8_t len);
/* USER CODE END Prototypes */

在main.c中添加如下代码:

/* USER CODE BEGIN PV */

	extern uint8_t  RX2_BUF[RX3_BUFFER_SIZE];  		//串口2接收缓冲区
	extern uint8_t  TX2_BUF[TX3_BUFFER_SIZE];  		//串口2发送缓冲区	
	extern volatile uint8_t  RX2_LEN;				//串口2接收的数据长度实际大小	
	extern volatile uint8_t  RECV2_END_FLG;			//串口2接收完成标志	
	extern volatile uint8_t  TRANS2_END_FLG;		//串口2接收完成标志
/* USER CODE END PV */


/* USER CODE BEGIN PFP */
//
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	
    if(huart == &huart2)
    {		
    	RS485_2_RX; 				// RS485 接收模式
		HAL_UART_Receive_DMA(&huart2,RX2_BUF,RX2_BUFFER_SIZE);//重新打开DMA接收
        TRANS_END_FLG=1;    //发送完成标志置位		
    }
}
/* USER CODE END PFP */

在stm32f1xx_it.c文件中添加如下代码:

/* USER CODE BEGIN Includes */
#include "usart.h"
#include "GPIO.h"
/* USER CODE END Includes */ 

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
	uint8_t  RX2_BUF[RX2_BUFFER_SIZE];          //串口2接收缓冲区
    uint8_t  TX2_BUF[TX2_BUFFER_SIZE];          //串口2发送缓冲区    
    volatile uint8_t  RX2_LEN;                   //串口2接收的数据长度实际大小    
    volatile uint8_t  RECV2_END_FLG;            //串口2接收完成标志    
    volatile uint8_t  TRANS2_END_FLG;            //串口2接收完成标志

/* USER CODE END PV */

/**
  * @brief This function handles USART2 global interrupt.
  */
void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */
	uint32_t tmp_flag = 0;
	uint32_t temp;
	tmp_flag =__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE); 	//获取IDLE标志位
	if((tmp_flag != RESET))									//idle标志被置位
	{ 
		__HAL_UART_CLEAR_IDLEFLAG(&huart2);					//清除标志位		
		HAL_UART_DMAStop(&huart2); 							//停止DMA传输,防止串行总线其他帧的干扰
		temp  =  __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);	//获取DMA中未传输的数据个数 
		RX2_LEN =  RX2_BUFFER_SIZE - temp; 						//总计数减去未传输的数据个数,得到实际接收的数据个数
		RECV2_END_FLG=1;       // 接收完成标志位置1

        //如果是从设备,此处可以用条件语句判断是否应该应答主机的呼叫(从设备地址与呼叫地址一致),如果是就发送(应答)。下面是直接发送收到的信息
        RS485_Usart3_DMA_Send(RX3_BUF,RX3_LEN);		//RS485发送数据

		TRANS_END_FLG=1;					//串口3发送完成标志置1,表示正要发送
        //下面的语句通常在发送完成回调函数内调用,在此处调用也可以
		//HAL_UART_Receive_DMA(&huart3,RX3_BUF,RX3_BUFFER_SIZE);//重新打开DMA接收	
		
        								
	 }

  /* USER CODE END USART3_IRQn 0 */
  HAL_UART_IRQHandler(&huart3);
  /* USER CODE BEGIN USART3_IRQn 1 */

  /* USER CODE END USART3_IRQn 1 */
}

      在有多个RS485设备的网络中,通常有一个主设备发起通信,网络中每个设备通常有一个唯一标识,也可称为地址,主设备发起通信,有点像点名,被点到的设备响应,向网络发送数据。不论是主设备还是从设备,仅在发数据时才拉高RS485接口芯片的发送数据控制引脚DE,其他时间DE一律为低电平。如图2所示,主设备呼叫帧通常包括帧头、设备地址、命令、数据、校验和帧尾等字节。通常从设备应答帧的长度也会根据主设备的命令不同而有变化,例如图中主设备两次呼叫从设备1,从设备1给出的应答长度是不一样的。帧长度不一样,DE的脉冲宽度也应随之变化。

      在RS485网络中,只要本地设备不发送,均可接收网络中任何设备的发送的信息。从上面的代码可见,接收是通过串口空闲中断实现的,它可接收不定长的数据帧。RS485通信的关键是发送,即发送的时机控制,控制发送时机是为了防止通信中的冲突,主要是发送控制DE电平拉高和拉低的时机掌握,也就是何时拉高,何时拉低。

        作为主设备,具有发起通信的主导权,网络中,主设备不发送,任何设备不能发送。主设备下一次发送是在确认不会有从设备发送时,才能进行再次发送。

        作为从设备,通常是不被呼叫不应答,即不发送。

       下面介绍DE的电平控制,对主设备和从设备是一样的,见代码:

void RS485_Usart2_DMA_Send(uint8_t *buf,uint8_t len)
{
    uint8_t i;
    if(TRANS_END_FLG==0)                //如果串口有发送且已完成,或发送处于完成状态
    {
        RS485_2_TX;                    //发送控制置位到发送状态——DE2高电平
        for( i=0;i<10;i++);            //短暂延时,使DE2进入高电平稳定状态
        if(HAL_UART_Transmit_DMA(&huart2, buf,len)!= HAL_OK) //判断是否发送正常,如果出现异常则进入异常中断函数
        {
            Error_Handler();
        }
    }
}  

在发送数据前执行RS485_2_TX; 拉高DE引脚,其后有一个小的延时,保证DE进入高电平的稳定状态,再执行DMA发送。当数据发送完成要及时将DE拉低,让出总线使用权,DE拉低在发送完成中断回调函数中实现:

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    
    if(huart == &huart2)
    {        
        RS485_2_RX;                 // RS485 接收模式
        HAL_UART_Receive_DMA(&huart2,RX2_BUF,RX2_BUFFER_SIZE);//重新打开DMA接收 
    }

      该回调函数一方面执行RS485_2_RX; 将DE拉低,另一方面,发送完成重新开启DMA接收。

       如图3是PC串口调试器(+USB转485)和STM32F103扩展的RS485芯片,实现通信的波形图,即PC定时发送数据,MCU收到数据直接再发给PC的波形图。       

        图4为串口调试器收发实况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值