这几天一直再做gprs的调试工作,开始从网上得到的例程,在单片机上给GPRS发指令,单片机可以收到模块返回的指令。讲这次经历之前,先简单介绍下单片机如何给GPRS发AT指令的,AT指令有很多,在每发一个AT指令后都需要单片机给模块再发送一个回车“\r\n"字符才表示发送完成。下面是参考的发送指令部分:
for (; *b!='\0';b++)//*为发送的AT指令
{
while(USART_GetFlagStatus(USART2, USART_FLAG_TC)==RESET);
USART_SendData(USART2,*b);//UART2_SendData(*b);
}
USART2_Puts("\r\n");//发送回车
然后我参考了例程的发送指令函数移植到自己的工程里,结果一个百思不得其解的现象出现了:自己的程序下到单片机,然后给GPRS发指令,却无法得到模块的反应。因为这个问题调试了整整一天,同样的发送指令函数,到了自己的程序里怎么就无法实现了?当时的内心几乎是崩溃的!!
其实,还是自己不够细心(之前一直以为发送指令函数没问题而一直忽略)。后面的不断调试和检查中,在程序中发现了一个非常重要的细节,自己写的发送回车字符的函UART2_SendLR();
例程的该函数内容是:
void USART2_Puts(char * str)
{
while(*str)
{
while(USART_GetFlagStatus(USART2, USART_FLAG_TC)==RESET);
USART_SendData(USART2 ,*str++);//发送当前字符
}
}
而自己的函数是:
void USART2_Puts(char * str)
{
while(*str)
{
USART_SendData(USART2 ,*str++);//发送当前字符
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE)==RESET);
}
}
两条语句的顺序和标志位不一样,之前一直用这函数都正常,所以没去在意,但和先前的发AT指令部分结合,问题非常明显了,用我的函数话,发送完AT指令,那么未进行标志位判断就立马发送回车字符,此情况是不允许的,这样后面的字符会把前面的一个覆盖了,也就是AT指令的最后一个字符会消失,导致了GPRS无法识别而无返回数据。
因此将自己的函数进行了修改,问题解决了。
下面就是该问题牵引出的一个发现。由于两函数判断标志位不一样,判断与发送的次序也不一样,因此出于好奇,想通过此函数发送字符串给上位机,看显示效果是否一样,
结果发现了一个奇怪现象:例程的函数发送的字符串,第一个字符总会在程序复位开始时会丢失。而当使用USART_FLAG_TXE来判断时,可以正常。
通过网上查找了这两标志位的区别,查到了单片机的串口发送接收机制:
发送:软件将数据写到USARTx->DR里面,硬件自动把USARTx->DR里面的数据并行转移到“发送一位寄存器”,然后硬件自动将发送一位寄存器中的数据通过TX引脚串行发送出去。
接收:RX上有数据过来,则先将数据一位一位的放到“接收移位寄存器”里面,收满一个字节后,硬件自动将“接收移位寄存器”里面的数据并行转移到USARTx->DR里面。
while (!(USART1->SR & USART_FLAG_TC));这句是等待数据完完全全从“发送移位寄存器”中发送出去,while (!(USART1->SR & USART_FLAG_TXE));这句是等待数据从USARTx->DR转移到“发送移位寄存器”中。在后一句的写法中,并不需等待数据完全发送,只需要USARTx->DR里面的数据已经被转移走,就可以马上进行再次写入USARTx->DR操作,这样是不是就可以节省很多时间了?
至于出现乱码,你需要综合其他的代码来进行分析。(注:while (!(USART1->SR & USART_FLAG_TC));相于 while(USART_GetFlagStatus(USART2, USART_FLAG_TC)==RESET); while (!(USART1->SR & USART_FLAG_TXE));相当于while(USART_GetFlagStatus(USART2, USART_FLAG_TXE)==RESET); )
因此,USART_FLAG_TXE会先于USART_FLAG_TC判断,只要数据发送,USART_FLAG_TXE就会置0;而USART_FLAG_TC的判断会有所延迟,需要数据到了移位寄存器才能为0。这也就解释了为什么用USART_FLAG_TC会使得第一个数据无法判断,而丢失。