近期白嫖君在使用GD32这款芯片时候,发现了一个大概率是芯片设计上的BUG,贴出来和大家分享一下。
我们在使用串口发送数据时,无非是使用两种方法,一种是逐字节发送,另一种是DMA发送。
一般串口发送数据前,我们需要先查看TBE标志位,判断缓存区内是不是已经空了,如果空了我们才会往里填数据。
但是当发送缓存区为空时,并不代表我们的数据已经实际发完了,只是代表缓存区内的数据空了,这时物理意义上的发送可能还在进行中,如果你使用RS485器件,这时候把发送使能关断的话,就会丢失一个字节的数据。
因此,一般发送完成后,需要检查TC标志是不是已经被拉高了,以此来判断数据线上的数据是不是确实已经发结束了。
void usart_sendbuf(uint32_t usart_periph, uint8_t *Buffer , uint16_t ucSend_num)
{
unsigned int i;
usart_flag_clear(usart_periph, USART_FLAG_TC); //在发送前要先清除TC
for(i = 0; i < ucSend_num; i++)
{
while(usart_flag_get(usart_periph, USART_FLAG_TBE) == RESET);
usart_data_transmit(usart_periph, Buffer[i]);
}
while(usart_flag_get(usart_periph, USART_FLAG_TC) == RESET);
}
这次白嫖君的程序就是还按照这个套路来写的,串口发送数据量比较大,在运行一段时间后,程序突然就死机了,查看一下,是死在了最后一行等待TC标志位这里。查看寄存器列表,TC始终为0。
下面是官方库函数手册上给出的说明:
TC标识是受单片机硬件控制的,并不受程序影响,这里无法拉高九成九就是芯片BUG了,于是白嫖君谷歌了一下,发现也有人遇到类似问题:
针对这种情况,硬件上无法做出改善,只能从软件上规避了:
void usart_sendbuf(uint32_t usart_periph, uint8_t *Buffer , uint16_t ucSend_num)
{
uint32_t i;
uint32_t j = 0;
usart_flag_clear(usart_periph, USART_FLAG_TC); //为了使用TC,在发送前要先清除TC
for(i = 0; i< ucSend_num; i++)
{
while(usart_flag_get(usart_periph, USART_FLAG_TBE) == RESET);
usart_data_transmit(usart_periph, Buffer[i]);
}
while(usart_flag_get(usart_periph, USART_FLAG_TC) == RESET)
{
if(j++ >= 0xFFFF) //在这里加超时机制
{
break;
}
}
}
引入超时机制,当等待时间超过设定阈值,则不再等待TC置位,以此来避免程序阻塞假死。
特别注意:出现这种情况目前来说可能GD全系都有可能存在这个问题,且不区分是USART0还是USARTx,同时似乎只在数据量较大时会出现此种情况。我以前用GD的片子也不少,从没遇见过这种情况。特此说明,避免抬杠嘛~
审核编辑 :李倩
全部0条评论
快来发表一下你的评论吧 !