学习32,串口的使用很重要,特别是现在学的越多就觉得在很多方面通信才是主角,原子历程写的挺好,但是我看论坛里好像没怎么有这个的解析那就按自己的理解写写吧,希望能帮助到正在学习的同学。然后写在前面,初学者或者想深入学习一定要多看看参考手册。
在主函中使用以下语句实现将接收到的数据发送出去:
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;
printf("\r\n您发送的消息为:\r\n\r\n");
for(t=0;t<len;t++)
{
USART_SendData(USART1, USART_RX_BUF[t]);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
}
首先USART_RX_STA是什么呢?在"usart.c"里面有说明这是个接收状态标记,它既指接收状态又表明接收到数据的位数,具体使用在下文的串口中断部分中进行说明解释。
u16 USART_RX_STA=0; //接收状态标记
USART_RX_STA的值在USART1_IRQHandler串口接收中断函数中被改变,这里USART_RX_STA&0x8000就是判断是否已经接收到了0x0a,0x0a是LF(line feed)换行的意思,光标到达下一行,也是判断数据接收结束的标志。
提一下,中断中是以是否接收到0x0d 0x0a这两个数据,判断数据是否发送结束的。假如你发的数据没有回车(0x0d )、换行(0x0a)那么单片机是不会停止接收的(在PC中回车、换行使用回车按键就都包含了,感兴趣可以具体查查)。
程序使用数组进行数据的存储,于是这段代码也就引出一个BUG,具体描述不会描述就简单说下,嘴笨是硬伤。
比如我给单片机发送 0x01 0x02 0x03 0x04 0x05 0x06 0x0d,这时候程序还在运行继续等待接收,
再发送 0x07 0x08 0x0d 0x0a这时候接收结束,看看最后输出什么?
导致这个BUG的原因就是数组存储了信息,知道出错但没有清除,只是提一下,注意一点就行,一般使用还是可以的。
if(Res==0x0d)USART_RX_STA|=0x4000;
这句话呢就是判断是否接收到回车(0x0d ),接收到了回车,那么让USART_RX_STA标志位第二位为1。
if(USART_RX_STA&0x4000)
{
if(Res!=0x0a)USART_RX_STA=0;
else USART_RX_STA|=0x8000;
}
这句话呢就是判断是否接收到换行(0x0a),如果收到换行并进行判断确认收到换行符那么让USART_RX_STA标志位最高位为1。不是很懂的话,那就看看后面的那个图。至此中断就差不多说明白了,要是还有其他问题,看看下面的注释,这里我是想说怎么进行的数据处理,毕竟我觉得这一块是个难点。
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
//中间变量,读取的数据进行保存
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
//查看RXNE寄存器是否为空,以此判断是否有数据发送过来
//接收中断(接收到的数据必须是0x0d 0x0a结尾)
//这里判断发送接收完成的依据就是串口数据0x0d 0x0a,
//0x0d是CR(carriage return)回车的意思,光标回到最左边,
//0x0a是LF(line feed)换行的意思,光标到达下一行,
//但是在PC上回车和换行是在一起的就是按下回车按键
//当然可以更改程序使用其他进行判断例如使用0x2a也就是*进行结束判断
{
Res =USART_ReceiveData(USART1);//(USART1->DR);
//读取接收到的数据,存放到变量Res中
if((USART_RX_STA&0x8000)==0)
//判断接收是否未完成
//接收完成未清除标志位,还是会不断进入到接收中断,所以使用标志进行判断,
//当接收完成便不会跳入到判断,从而不执行任何指令,空等待
//使用条件判断是否已经接收完数据,这里判断接收完的依据就是收到了0x0a;
//具体判断在后面
{
if(USART_RX_STA&0x4000)
//如果接收到了0x0d,那么再进一步执行是否接收到0x0a的判断
{
if(Res!=0x0a)USART_RX_STA=0;
//没有接收到0x0a那么说明,数据未正确传输或者接收错误,重新开始判断,
//但是这里没有将接收到的数据进行清空,也没有退出接收中断,此程序只是从头开始执行接收判断
else USART_RX_STA|=0x8000;
//接收完成了,收到了0x0a那么标志位USART_RX_STA将会变成0x8000(使用或指令,只改变最高位),将不再进行数据检测与存储
}
else
//还没收到0X0D,说明数据还未发送结束继续进行数据的检测与存储
{
if(Res==0x0d)USART_RX_STA|=0x4000;
//收到了数据0x0d,标志位USART_RX_STA变成0x4000(使用或指令,只改变次高位)
else
{
//如果没有接收到数据0x0d,执行判断是否存储数组已满,已满则重新开始接收
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
//将接收到的数据写进数组,标志位USART_RX_STA与上0X3FFF清除前两位以防止标志位与8000和4000冲突
USART_RX_STA++;
//数组地址加一,向后排
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;
//接收数据错误,超出数组大小,又开始接收向数组重新写
}
}
}
}
}
判断接收结束再判断此次接收数据的长度,USART_RX_STA也表明接收数据的位数,要想将接收到的数据全部发送出去就需要知道,一共接收到了几位然后一位一位的传出去,所以len=USART_RX_STA&0x3fff;就实现了将数据长度赋值给变量len,然后使用for循环依次将接收到的数据发送出去。这里也许会糊涂为什么是0x3fff呢?看下图能理解么?
接收到0x0d ,USART_RX_STA的值变成0x4000,又接收到0x0a,USART_RX_STA的值变成0xC000那么很容易发现数据的的前两位是作为标志位了,所以用来记录其他数据位数就只有0x3fff。
if(USART_RX_STA&0x8000)
//如果在串口接收中断中接收到0x0a,数据已接收完毕
{
len=USART_RX_STA&0x3fff;
//得到此次接收到的数据长度,传给for循环,循环依次发出接收到的数据
printf("\r\n您发送的消息为:\r\n\r\n");
for(t=0;t<len;t++)
//通过for循环依次将接收到的数据发送出去
{
USART_SendData(USART1, USART_RX_BUF[t]);
//向串口1发送数据,USART_SendData是库函数控制DR寄存器
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
//等待发送结束,看TC寄存器的状态 TC:发送完成 (Transmission complete)
}
差不多就是这样,不懂的可以评论,有错误也请指正。一般使用串口不是这么判断使用的,具体看有没有时间,有时间整理一份。过些时候将注释的文件上传以便下载阅读。