项目场景:
串口用作RS485使用的场景
市面上有两种RS485芯片,一种是需要手动控制收/发的,便宜且用得普遍,另一种是自动控制收发的,贵且用得少。 对于普通的RS485芯片,要求传输速率不快的,比如就9600bps, 很多硬件工程师直接将收/发使能脚和串口TX引之间加反向电路,并在AB线上加上下拉来实现自动控制收发,而对于需要快一点通信的情形下,需要软件精准控制收发,比如发送时使能发送,发送完成时立即使能接收,但是使能接收的时机很重要。首先转换到接收要尽可能快,对方可能马上就回复了;其次要确保数据完整的发送到从总线。
问题描述
使用GD32F4XX(或其他Arm芯片)串口用作RS485时,使用常见问题:
1、总线瘫痪,任何设备发送数据,总线监控不到
2、其中某个设备发送数据,总线监控总是少最后一个数据或者最后的数据不对
原因分析:
- 对于第一个问题:RS485是总线芯片,总线同一时刻只能一个设备发送数据,
因此它只适合用在半双工通信情景。需要发送数据时,RS485的发送使能一打开,总线便被该485的设备占用。有些人的项目在使用串口DMA发送的时候,异常情况下(比如发送的数据长度为0时)没有处理异常,开启DMA发送前,设置485为发送占用了总线,之后又没有进入发送完成的调用从而没有将RS485设置为接收,导致整个总线瘫痪。 - 第二个问题原因(GD32F4XX为例):单个字节发送完成状态有两个,一个是 USART_FLAG_TBE,即“transmit data buffer empty”(数据缓存为空,数据已经从芯片内部寄存器发送出去了),注意,这仅仅代表MCU寄存器数据是空的了,并不代表数据移位的完成,而串口数据是以bit为单位移位出去的; 另一个是 USART_FLAG_TC 即“transmission complete”(发送完成),这个才是移位完成的标记,因此我们要使用这个移位完成的标记,来判断一个字节的完成。
有些朋友使用DMA发送, DMA发送完成的标记 DMA_FLAG_FTF “full transfer finish flag”(传输完成),代表的仅仅是RAM -> 外设的传输完成,我们可以理解是最后一个字节刚好通过DMA通道传输到了串口发送寄存器,但是可能还没有移位完成。如果在串口总中断处查询到这个标记,并不能代表数据移位到RS485总线的完成。因此在串口总中断里面,(如果是使用STM的hal库,是在相关回调函数里面),你还需要开启USART_INT_TC 即“transmission complete interrupt and flag”中断,当此中断完成后,才是真正的一帧数据由串口完全发送了出去。
解决方案:
1、添加异常调用,异常后使能RS485接收
2、USART_FLAG_TC 即“transmission complete”(发送完成),这个才是移位完成的标记,因此我们要使用这个移位完成的标记,来判断一个字节的完成; DMA发送完成中断后,继续等待USART_INT_TC 中断。
GD32F4XX的串口收发处理描述为例:
/***接收完成处理,一般是读取DMA通道的数据后再开启DMA接收且通知上层应用处理***/
__weak void usartCom1_rx_cplt(uint16_t u16transLen)
{
;
}
/***发送完成处理,一般是打开RS485接收,通知更上层应用发送完成***/
__weak void usartCom1_tx_cplt(void)
{
;
}
/***普通轮询发送***/
void usartCom1Send(uint8_t *ucBuf, uint8_t ucLen)
{
int i;
for (i = 0; i < ucLen; i++)
{
while(