这几天看完了正点原子STM32的并口通信部份的内容,总觉得好多东西似是而非,前后花了好几天研究了下,这篇博客好多内容是从其他博客上整理来的,并非完全原创,由于前后查了几天好多篇博客,摘抄的谁的也不好找了,看到的可以提醒一下,只希望自己整理的内容能帮到其他的初学者。
1、实验内容梳理
首先结合串口调试助手对实验进行说明,以便后续结合代码熟悉整个流程。整个实验似乎就是通过串口调试助手向单片机发送数据,然后单片机将接收到的数据返回给上位机并加以显示。
简单来串口调试助手说虽然就是用于上位机和下位机通信用的一个桥梁软件,功能主要有两个这也是本实验的两个步骤:
人工发送数据给单片机处理,即通过串口调试助手的下方窗口编辑数据,然后点击发送按键,就能发送数据给单片机;接受单片机发送的数据显示给你看,即通过串口调试助手上方窗口,将单片机发回给上位机的数据进行显示;
#b:9:9:5:d:b:2:d:d:6:c:8:0:f:1:0:1:b:5:f:d:2:f:a:3:7:5:4:5:e:8:7#
#b:c:2:3:7:a:1:f:b:5:3:4:2:2:3:0:5:9:2:a:0:d:5:9:a:1:5:f:4:b:0:2#
关于串口调试助手,还应晓得:
2、对于并口中断函数(自定义数据接收合同)的理解
正点原子的类库中通过句子USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)开启相关中断,当读数据寄存器非空,即单片机一接收到数据时,便会触发并口1的中断函数。
改合同的核心是定义了一个16位的变量USART_RX_STA,该变量的0-13位用于储存接收到的数据,最后的14、15两位作用在于,当14、15位依次接收到da0x0d和0x0a时,依次将这两位置1,作为判别数据是否接收完的标志位。
以下是我听到的一个注释比较详尽的代码,和一个实例,结合二者就可以挺好的理解这个过程和代码,
#e:2:f:4:6:d:9:a:9:7:b:f:5:c:5:f:4:e:1:d:2:7:9:4:1:b:e:0:5:d:e:f#
假设我们发送的数据是“abcd”经过串口调试助手加上0X0D+0X0A后发送给单片机,即单片机要接受的数据是“abcd”+“0X0D+0X0A”
(1)当接收到“a”时读寄存器非空,RXNE为1,第一次步入并口中断处理函数,我们先判定是否接是因为USART1接受到了数据形成的中断,如果是,则将USART1接受到的一位数据“a”存入变量Res里(Res = “a”)
#4:a:f:6:a:6:8:b:e:f:4:b:e:c:c:f:6:f:7:2:4:a:6:f:5:b:7:6:6:0:8:1#
(2)然后我们来判定接收的一系列数据是否没接收完(即)。(当然没有啦,我们还有b、c、d三个数据没有接收)。这个时侯呢,USART_RX_STA——这个在全部函数之间实现消息传递的变量的值始终为0,和0x8000相与之后为0,那么执行该if句子的外层函数。
#e:6:9:5:5:e:a:4:f:a:6:1:f:5:f:5:7:d:8:1:6:a:5:3:b:d:5:4:8:1:2:3#
(3)进入该if句子的外层句子后判定句子如下,这里判定USART_RX_STA的第14位是否为1,如果我们接收到了回车,即0x0d这么USART_RX_STA的第14位会置1。在我们接收第一个数据a时USART_RX_STA其实还为0,(我们前面的的b、c、d、3个数据还都没接收了,当然不会收到0x0d)USART_RX_STA和0x400相与为0,该判定句子就为假,执行下边的else句子。
#3:5:e:f:f:4:f:3:0:5:b:9:1:0:3:1:c:c:6:8:5:1:5:1:f:b:6:3:d:7:d:3#
(4)该else句子的外层句子是一个if-else句子
#c:4:8:1:6:d:1:d:1:4:0:b:e:c:4:d:8:c:8:6:7:0:1:b:2:c:e:2:2:3:3:2#
这里我就不再赘言,我们接收的是数据a,还没有接收到0x0d,执行else句子。
#f:9:8:8:2:3:7:d:9:8:0:5:b:a:6:5:1:e:b:2:4:f:3:a:2:1:4:7:7:e:7:f#
USART_RX_STA的bit0~bit13代表的是接收到的有效数据个数,这里USART_RX_STA值仍为0,USART_RX_STA & 0X3FFF = 0 ,然后USART_RX_BUF[USART_RX_STA&0X3FFF]=Res,意思就是将Res里的数据储存到USART_RX_BUF[0]里了,并且USART_RX_STA自增1。
此时USART_RX_STA = 1这样在接收到下一个数据b后USART_RX_STA&0X3FFF = 1,将b存入到了USART_RX_BUF[1]里,一直循环下去,直到我们接收到了0x0d(Res = 0x0d)。
我们可以从前面程序里找到如下代码:
#5:5:3:3:4:f:5:c:e:4:c:a:1:d:7:2:0:3:a:6:5:0:f:f:7:0:4:3:3:5:5:b#
当接收到0x0d而且程序执行到这一步一时USART_RX_STA = 4,此时该if句子创立,执行USART_RX_STA|=0x4000,即0000 0000 0000 0100 | 0100 0000 0000 0000 = 0100 0000 0000 0100 ,我们可以清晰的看见bit13~0位是4,代表接收到了4个数据(a,b,c,d),第14位为1,是因为接收到了数据0x0d,也和最前面给的表对上了,然后程序向上执行,接收到了0x0d(回车),那下一个就是接收0x0a(换行)了,Res = 0x0a:
#f:4:f:9:e:c:a:8:1:f:9:b:9:e:5:3:f:7:f:6:c:5:0:d:4:5:0:1:e:a:8:3#
这里第一个判定句子if(USART_RX_STA&0x4000),当然为真,因为Res = 0x0a,那么执行else句子USART_RX_STA|=0x8000,即0100 0000 0000 0100 | 1000 0000 0000 0000 = 1100 0000 0000 0100。到了这一步,就说明这一串数据早已完完全全的接收完了USART_RX_STA = 1100 0000 0000 0100,最低位为1:代表接收到了0x0a,第十四位为1:代表接收到了0x0d,第0位到第13位为4,代表接收到了4位有效数据(a、b、c、d)
3、对main函数的理解
main函数如下,大致的执行流程为:
#2:3:3:a:e:0:7:4:b:2:c:b:2:1:f:6:e:7:7:9:2:c:7:f:8:e:0:e:7:1:e:f#
完成并口的相关设置,使能相应中断,以及初始化过程,然后步入while等待;当在串口调试助手的想窗体输入想要发送的数据,并点击发送后,当单片机开始接受数据时便会触发中断函数USART1 IRQHandler( )对数据进行接收并最终储存进缓存RX_BUF_USART中,其在usart.h中的生命为extern u8 USART_RX_BUF[USART_REC_LEN]。其中USART_REC_LEN=200通过语if(USART RX STA&0x8000)判断数据是否完成接受存入缓存字段RX_BUF_USART中,如果完成了,则借助重定向的printf函数向上位机发送句子“\r\n您发送的消息为:\r\n” , 如下图标号1区域(此处实验现象异常,后面会有解释)然后通过for循环将缓存RX_BUF_USART中的数据依次通过USART_SendData()函数发给上位机,发给上位机的数据将在串口调试助手上显示。所有数据发送完毕后,再次通过printf函数向上位机发送"\r\n\r\n"即在串口调试助手上窗体光标在原先位置的基础上下移两行。如下图标号2区域所示。当数据没有接受完,或者没有向单片机发送数据时,单片机便会通过printf函数向上位机依照一定时间间隔发送“\r\nALIENTEK 探索者STM32F407开发板 串口实验\r\n”、“正点原子@ALIENTEK\r\n\r\n”和“请输入数据,以回车键结束\r\n”语句。
#1:1:8:f:9:c:e:4:8:6:b:0:f:5:a:3:e:8:3:e:7:4:3:1:5:f:4:d:2:7:4:e#
注意:重定向的printf()函数本质上还是通过USART_SendData()向上位机发送数据,且此处发送的“\r\n”与中断函数里的须要作为接受完成标志位的“\r\n”(0X0D+0X0A),只是单纯的表示换行的通配符字符,电脑上位机接收到后会将光标下移两行,视觉上就是空一行显示在串口调试助手上。
4、实验异常现象及解决办法
上面有提及过图片标号1的区域出现了异常实验现象,即单片机借助printf("\r\n您发送的消息为:\r\n")语句显示的结果中句子末尾的“\r\n”并没有起到作用,即让光标移至下一行后再显示“逆光”。
这种现象的诱因是因为printf句子中的“\r\n”发送未完成,寄存器又被前面要发送的数据覆盖,解决方式就是在USART_SendData(USART1, USART_RX_BUF[t]);前面在多加一句 while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
#b:c:b:7:7:c:6:c:0:f:d:2:6:9:9:1:b:2:9:3:f:c:4:8:9:2:5:c:6:a:6:0#
修改后实验现象恢复正常如下:
#5:e:b:b:3:5:3:4:b:2:1:e:a:0:1:e:3:6:1:a:e:1:a:7:8:e:f:3:3:e:6:0#
我尝试过在printf句子的前面借助延时的方法解决问题,但是只有第一次才能正常显示,后面再度发送,现实的数据都会存在乱码的问题。
5、USART-FLAG-TXE与USART-FLAG-TC标志位
我感觉这两个标志位的使用应该严格分辨开,当个附加知识记录一下:
USART-FLAG-TXE发送缓冲区空标志:说明可以往数据寄存器写入数据了,但并不代码数据发送完成了。
USART-FLAG-TC发送完成标志:这个才是代表USART在缓冲区的数据发送完成了,即从机接收到了数据。
这两个标志的区别在于:它们分别表示数据在发送过程中,在两个不同的阶段中的完成情况.TXE表示数据被从发送缓冲区中拿走,转移到的移位寄存器中,此时发送缓冲是空的,可以向其中补充新的数据了。而 TC则表示最后装入发送缓冲区的数据已经完成了从移位寄存器向发送讯号线Tx上的转移。所以,判定数据最终发送完成的标志是TC,而不是IXE.