stm32标准库串口发送第一个字符覆盖

stm32标准库串口发送第一个字符覆盖


说明:这是在使用标准库下的一个坑,在使用hal库的时候没有这个问题

虽然现在几乎hal库一统天下,但是由于hal的冗余效率低下,对于资源受限的单片机来讲,标准库还是有非凡意义的

1. 系统环境

  • 系统:win10
  • ide:keil5
  • 芯片:stm32f103c8
  • 芯片驱动库:标准库

2. 当数据连续发送前一个被后一个覆盖解决办法

当使用下面语句发送大量数据的时候,如下:

for(TxCounter= 0;TxCounter < RxCounter; TxCounter++){
	USART_SendData(USART1, RxBuffer[TxCounter]);
}

使用USART_SendData()函数非连续发送单个字符是没有问题的;当连续发送字符时(两个字符间没有延时),就会发现发送缓冲区有溢出现象。若发送的数据量很小时,此时串口发送的只是最后一个字符,当发送数据量大时,就会导致发送的数据莫名其妙的丢失。

原因:函数体内部没有一个判断一个字符是否发送完毕的语句,而是把数据直接放入发送缓冲区,当连续发送数据时,由于发送移位寄存器的速度限制(与通信波特率有关),导致发送缓冲区的数据溢出,老的数据还未及时发送出去,新的数据又把发送缓冲区的老数据覆盖了。

这种现象的解决方法是在发送USART_SendData()函数后面添加一个while,来保证每次字符发送完,在发送下一个字符。如下:

for(TxCounter= 0;TxCounter < RxCounter; TxCounter++){
	USART_SendData(USART1, RxBuffer[TxCounter]);
    while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE)==RESET);//等待发送完成
}

这种问题比较简单,不多说了。

3. 硬件复位之后第一个字符丢失

stm32用printf函数重定向到串口USART1,当stm32复位后,发送的字符串,发现第一个字符没打印出来具体如下:

int fputc(int ch,FILE *f)
{
    USART_SendData(USART1,(uint8_t)ch);
    while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);
    return(ch);
}

然后其他外设配置好之后直接使用printf(“abc");发现a打印不出来。

具体原因为stm32的usart的sr寄存器的TC为初始为1,导致第一次while时的第一个数据被覆盖,所以打印不出

这种问题一般有2种解决方法:

  1. 既然TC位初始为1导致的问题,那么我们在串口初始化前把TC置0也可以解决问题,写多一句USART_ClearFlag(UART1, USART_FLAG_TC);
  2. 根据数据手册(后面讲解)将上面的USART_ClearFlag(UART1, USART_FLAG_TC);换成USART_GetFlagStatus(USART2,USART_FLAG_TC);也可以解决问题。

3. 关于硬件复位之后第一个字符丢失数据手册的解释

查阅stm32f10x参考手册,找到这样一句话:TC:发送完成当包含有数据的一帧发送完成后,由硬件将该位置位。如果USART_CR1中的TCIE为1,则产生中断。由软件序列清除该位(先读USART_SR,然后写入USART_DR)。TC位也可以通过写入0来清除,只有在多缓存通讯中才推荐这种清除程序。0:发送还未完成;1:发送完成。

注意到这一句:由软件序列清除该位(先读USART_SR,然后写入USART_DR)。也就是说,要先readUSART_SR,然后writeUSART_DR,才能完成TC状态位的清除。而硬件复位后,串口发送的首个数据之前没有readSR的操作,是直接writeDR,也就是说,TC没有被清除掉。这也就是为什么硬件复位之后第一个字符丢失的原因。

那么在初始化串口的时候,添加的USART_ClearFlag(USART2,USART_FLAG_TC);改为USART_GetFlagStatus(USART2,USART_FLAG_TC);,应该也能消除错误。测试后证实,确实如此,在发送首个数据之前,先读取一下USART_SR,那么就不会出现首个数据丢失的情况了。

硬件复位后,串口发送首个数据之前,先读取一下USART_SR,则能够保证首个数据发送时,不出现覆盖的情况。当然,也有别的方法,比如先清除TC状态位,或是,在writeUSART_DR之后,加入一个小延时,让数据发送完毕,应该也能间接排除这个错误。

4. 附加:TXE和TC标志位详细说明

在while中,有人想用USART_GetFlagStatus(USART1, USART_FLAG_TC),有人想用USART_GetFlagStatus(USARTx, USART_FLAG_TXE)来判断串口当前是否发送完成,其是在我认为,这两个在这种情景下作用是一致的,下面具体说明:
在USART的发送端有2个寄存器,一个是程序可以看到的USART_DR寄存器,另一个是程序看不到的移位寄存器。
对应USART数据发送有两个标志,一个是TXE=发送数据寄存器空,另一个是TC=发送结束;
当TDR中的数据传送到移位寄存器后,TXE被设置,此时移位寄存器开始向TX信号线按位传输数据,但因为TDR已经变空,程序可以把下一个要发送的字节(操作USART_DR)写入TDR中,而不必等到移位寄存器中所有位发送结束,所有位发送结束时(送出停止位后)硬件会设置TC标志。
另一方面,在刚刚初始化好USART还没有发送任何数据时,也会有TXE标志,因为这时发送数据寄存器是空的。
TXEIE和TCIE的意义很简单,TXEIE允许在TXE标志为’1’时产生中断,而TCIE允许在TC标志为’1’时产生中断。
至于什么时候使用哪个标志,需要根据你的需要自己决定。但我认为TXE允许程序有更充裕的时间填写TDR寄存器,保证发送的数据流不间断。TC可以让程序知道发送结束的确切时间,有利于程序控制外部数据流的时序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈搭石

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值