第一次遇到这个问题的时候,甚至有点怀疑自己,为什么直到现在才发现有这个问题
直接说解决方法吧,那就是在串口发送字符函数加上下面这句话:
USARTx->SR;
接下来说原因:哈哈哈其实是因为自己比较懒,来了新公司以后直接拿别人的串口相关函数来用了,他的串口发送字符的函数是这样的:
void UART_PutChar(USART_TypeDef* USARTx, uint8_t Data) //发送单字节
{
USART_SendData(USARTx,(uint8_t) Data);
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET){}
}
乍一看好像没什么问题,但是可以注意到这里while循环是等待USART_FLAG_TC被清0, 但TC 和 TXE 标志位在复位的时候被置1,因此第一个字符发送时while循环就是没有用的,直接跳过去了。这样导致了首次第一个字符还没有被输出,就被后面的字符覆盖掉了。
最终没有问题的串口发送字符函数应该是这样的:
void UART_PutChar(USART_TypeDef* USARTx, uint8_t Data) //发送单字节
{
USARTx->SR;
USART_SendData(USARTx,(uint8_t) Data);
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET){}
}
这是因为TC标志位的清0过程是:先读取SR寄存器的值,再向DR寄存器写入数据;
而TXE标志位只需要向DR寄存器写入数据就可以被清0。
保险起见我现在还在串口初始化完成之后加上了这行代码:
USART_ClearFlag(USART1,USART_IT_TC);
也就是在初始化完成后就先将TC标志位清0。当然也有的人会在发送字符串函数里面加上这句话,以防再出现TC不能及时被清零的问题。但我的习惯是字符串发送函数一般都是调用发送字符函数来实现的,所以就没有加。
两种字符串发送函数:
void UART_PutStr (USART_TypeDef* USARTx, uint8_t *str) //发送字符串
{
int i=0;
while ( i<14)
{
UART_PutChar(USARTx, *str);
str++;
i++;
}
}
void USART_SendString(USART_TypeDef* USARTx, unsigned char *DataString,uint16_t len)
{
int i = 0;
USART_ClearFlag(USARTx,USART_FLAG_TC); //发送字符前清空标志位(否则缺失字符串的第一个字符)
while(i<len) //字符串结束符
{
USART_SendData(USARTx,DataString[i]); //每次发送字符串的一个字符
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC) == 0); //等待数据发送成功
USART_ClearFlag(USARTx,USART_FLAG_TC); //发送字符后清空标志位
i++;
}
}
当然这种基础函数各有各的习惯,并不局限于一种实现方法,就像我以前也从来没发现还会有这样的差别。看别的博主说还可以将上面所有判断USART_FLAG_TC标志位的函数改成判断USART_FLAG_TXE标志位,不过我没有尝试。
看来今后还是要多翻翻参考手册了。