MCU利用RS485与外部通信,由于RS485是半双工通信协议,所以,要进行收发控制,要将485接口芯片的RE#和DE引脚短接,作为一个控制端,当控制端接高电平,为发送状态,否则,为接收状态。通常情况下,每个连接到RS485网络的设备,只有在本设备发送时才能将控制端置为高电平,其余时间要置于低电平(处于接收或监听状态)。
假设我们将MCU的GPIOC口的PC7作为控制端口,并定义如下宏,便于使用:
#define RS485_T HAL_GPIO_WritePin(GPIOC,GPIO_PIN_7,GPIO_PIN_SET);
#define RS485_R HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7,GPIO_PIN_RESET);
如果使用串口1,发送函数如下:
///
//函数名称:DMA_Usart_Send(uint8_t *buf, uint16_t len)
//功能描述:串口DMA方式发送数据帧
//参数说明:*buf 数据帧的指针,len数据帧的长度
//返 回 值: 无
//
void DMA_Usart_Send(uint8_t *buf, uint16_t len)
{
if(HAL_UART_Transmit_DMA(&huart1,buf,len)!=HAL_OK )
{
Error_Handler();
}
}
从逻辑上讲,RS485发送,在调用DMA_Usart_Send前,调用RS485_T,拉高发送控制端,在HAL_UART_TxCpltCallback函数中调用 RS485_R即可完成一帧数据的发送,见下面的代码。
发送一帧数据代码:
RS485_T; //485控制端置高电平
DMA_Usart_Send(buf,len);//发送数据帧,数据帧发送完成会触发发送完成中断
// 中断回调函数HAL_UART_TxCpltCallback 中将485
//控制端置为低电平(执行RS485_R;)
中断回调函数代码如下。
///
//函数名称:HAL_UART_TxCpltCallback (UART_HandleTypeDef *huart)
//功能描述:串口发送完成回调函数,使RS485芯片RE#/DE连在一起的引脚变低
//
void HAL_UART_TxCpltCallback (UART_HandleTypeDef *huart)
{
RS485_R; //串口发送完成将RS485芯片的发送控制端置为低电平——接收
}
但是,上述过程在时序上就会出现不可靠的情况,使得系统不能正常工作。产生不可靠的原因是RS485_T执行并不是理想的阶跃上升,通常是有一个上升时间的,如果这个时间并未达到485控制端最低高电平要求,接下来的DMA_Usart_Send发送已经执行,就可能造成发送的数据帧不完整,甚至不能触发发送完成中断,则 HAL_UART_TxCpltCallback回调函数不能执行,485控制端总是处于高电平,若设备接入RS485网络,将使整个瘫痪。
为了解决上述问题,应在执行宏RS485_T后,插入一个小的延时,约10微秒左右,用一个变量和while循环实现即可,DMA_Usart_Send后面是通过发送完成中断回调函数中,执行RS485_R;拉低485控制端。实测波形,已经有10us以上的时延,所以不需要再延时。改进后的RS485发送代码如下:
void RS485_Send_data(uint8_t *pdata,uint16_t data_size)
{
uint8_t i=100;
RS485_T;
while(i--);
DMA_Usart_Send(pdata,data_size); //发送完成产生发送完成中断,在中断回调函数中
//执行RS485_R
}
以上观点基于实验,有认识不妥之处请批评指正。