有块板子使用APM32F103RB的USART作为UART全双工串口和PC进行通信,PC上循环发送字符数据到单片机,单片机使用中断模式(只使能RXBNE事件)接收数据:
if (USART_ReadIntFlag(ptUart, USART_INT_RXBNE))
{
/* 检测接收是否有错误发生, 错误事件会在读取数据后自动清掉 */
dwSTS = ptUart->STS;
if (dwSTS & USART_FLAG_PE)
ptStat->dwRxPECnt++;
if (dwSTS & USART_FLAG_FE)
ptStat->dwRxFECnt++;
if (dwSTS & USART_FLAG_NE)
ptStat->dwRxNECnt++;
if (dwSTS & USART_FLAG_OVRE)
ptStat->dwRxOECnt++;
/* 读取数据 */
wData = USART_RxData(ptUart);
......
}
在main主循环里面将收到的字符数据再发送出去,即环回给PC,测试发现PC侧发了n个字符数据,但是收到的字符数据少于n个,中间数据丢了,在中断里面增加统计信息,发现中断计数和PC发送的字符个数是一致的,但是RXBNE事件的计数少于中断计数,而且RXBNE事件个数等于PC端收到的字符个数,说明是串口控制器丢了RXBNE事件。
芯片手册上有关RXBNE事件的清除动作是先读取STS寄存器,然后读取DATA寄存器:
但是DATA寄存器是收发共享的,怀疑是发送数据时写DATA寄存器时把RXBNE事件给清掉了。为了验证我的想法,对中断处理代码进行简化:
串口中断处理里面设置断点,即PC发了一个字符数据给单片机后会停住,然后读取寄存器的值分析
dwSTS = ptUart->STS; /* 这里读到的值是0xE0,Bit5=RXBNE=1是有事件的 */
USART_TxData(ptUart, 0x55); /* 然后故意发送一个字符,即随便写个值到DATA寄存器 */
dwSTS = ptUart->STS; /* 再次读取一次STS寄存器,发现值变成0xD0,RXBNE变0了 */
wData = USART_RxData(ptUart); /* 此时强行读取一次DATA寄存器值,这个值是PC发的那个字符;
说明控制器内部数据还是收发分开的,并不会串 */
由此可见,写DATA寄存器确实把接收的RXBNE事件给清掉了,这明显是芯片的一个BUG。
看了STM32F103的寄存器也是这样的,估计也会有同样的问题。
而STM32F0系列的芯片手册上UASRT里面已经把DATA寄存器分成RxDATA和TxDATA两个寄存器了,这个系列的芯片不存在该问题。