正点原⼦STM32串⼝通讯实验详解
这⼏天看完了正点原⼦STM32的串⼝通讯部分的内容,总感觉很多东西似是⽽⾮,前后花了好⼏天研究了下,这篇博客很多内容是从其他博客上整理来的,并⾮完全原创,由于前后查了⼏天好多篇博客,摘抄的谁的也不好了,看到的可以提醒⼀下,只希望⾃⼰整理的内容能帮到其他的初学者。
经提醒第⼆部分来源于 ,感兴趣的可以去原⽂看看。
1、实验内容梳理
⾸先结合串⼝调试助⼿对实验进⾏说明,以便后续结合代码熟悉整个流程。整个实验其实就是通过串⼝调试助⼿向单⽚机发送数据,然后单⽚机将接收到的数据返回给上位机并加以显⽰。
简单来串⼝调试助⼿说其实就是⽤于上位机和下位机通信⽤的⼀个桥梁软件,功能主要有两个这也是本实验的两个步骤:
1. ⼈⼯发送数据给单⽚机处理,即通过串⼝调试助⼿的下⽅窗⼝编辑数据,然后点击发送按钮,就能发送数据给单⽚机;
2. 接受单⽚机发送的数据显⽰给你看,即通过串⼝调试助⼿上⽅窗⼝,将单⽚机发回给上位机的数据进⾏显⽰;
关于串⼝调试助⼿,还应知道:
发送英⽂字符需要⽤⼀个字符即8位,发送汉字需要两个字符即16位,如上图,发送汉字“宋”实际是发送“CB(1100
1011)CE(1100 1110)”⽽发送英⽂字符S实际是发送“53(0101 0011)”,本质上没有太⼤区别;
勾选了下⽅“发送新⾏”后,XCOM就会再你输⼊的需要发送的数据后⾃动加上⼀个回车(0X0D+0X0A),如果不勾选则我们在⼿动输⼊完“宋S”后还需敲⼀个回车键只有这样点击发送后,调试助⼿上⽅窗体才能将其显⽰,这是因为我们在程序的串⼝中断中⾃定义了⼀个数据接收协议,即只有当接受的数据以回车结尾(0X0D+0X0A),串⼝才认可数据接受完毕。
2、对于串⼝中断函数(⾃定义数据接收协议)的理解
正点原⼦的例程中通过语句USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)开启相关中断,当读数据寄存器⾮空,即单⽚机⼀接收到数据时,便会触发串⼝1的中断函数。
改协议的核⼼是定义了⼀个16位的变量USART_RX_STA,该变量的0-13位⽤于存储接收到的数据,最后的14、15两位作⽤在于,当14、15位依次接收到da0x0d和0x0a时,依次将这两位置1,作为判断数据是否接收完的标志位。
以下是我看到的⼀个注释⽐较详细的代码,和⼀个实例,结合两者就可以很好的理解这个过程和代码,
void USART1_IRQHandler(void)                //串⼝1中断服务程序
{
u8 Res;    //定义unsigned char型字符Res
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
//接收中断(接收到的数据必须是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;
//接收数据错误,超出数组⼤⼩,⼜开始接收向数组重新写
}
}
}
}
}
假设我们发送的数据是“abcd”经过串⼝调试助⼿加上0X0D+0X0A后发送给单⽚机,即单⽚机要接受的数据
是“abcd”+“0X0D+0X0A”
(1)当接收到“a”时读寄存器⾮空,RXNE为1,第⼀次进⼊串⼝中断处理函数,我们先判断是否接是因为USART1接受到了数据产⽣的中断,如果是,则将USART1接受到的⼀位数据“a”存⼊变量Res⾥(Res = “a”)
if(USART_GetFlagStatus(USART1, USART_IT_RXNE) != RESET)
(2)然后我们来判断接收的⼀系列数据是否没接收完(即)。(当然没有啦,我们还有b、c、d三个数据没有接收)。这个时候
呢,USART_RX_STA——这个在全部函数之间实现消息传递的变量的值仍然为0,和0x8000相与以后为0,那么执⾏该if语句的内层函数。
if((USART_RX_STA&0x8000)==0) //接收未完成
(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语句。
if(USART_RX_STA&0x4000)//接收到了0x0d
(4)该else语句的内层语句是⼀个if-else语句
if(Res==0x0d)USART_RX_STA|=0x4000;
这⾥我就不再赘述,我们接收的是数据a,还没有接收到0x0d,执⾏else语句。
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ; // 0x3ff = 0011 1111 1111 1111
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
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)。
我们可以从上⾯程序⾥到如下代码:
else //还没收到0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000; //再次判断这次接收到的是不是0x0d
当接收到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:         
if(USART_RX_STA&0x4000)//接收到了0x0d    0100 0000 0000 0000
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始--接收到了0X0d但是没有接受到0x0a
else USART_RX_STA|=0x8000; //接收完成了--0x0d后⾯是0x0a
}
这⾥第⼀个判断语句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函数如下,⼤致的执⾏流程为:
int main(void)
{
u8 t;
u8 len;
u16 times=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168);  //延时初始化
uart_init(115200); //串⼝初始化波特率为115200
LED_Init();      //初始化与LED连接的硬件接⼝
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
printf("\r\n您发送的消息为:\r\n");
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
for(t=0;t<len;t++)
{
USART_SendData(USART1, USART_RX_BUF[t]);        //向串⼝1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
printf("\r\n\r\n");//插⼊换⾏
USART_RX_STA=0;
}else
{
times++;
if(times%300==0)
{
printf("\r\nALIENTEK 探索者STM32F407开发板串⼝实验\r\n");重定向过多是什么意思
printf("正点原⼦@ALIENTEK\r\n\r\n");
}
if(times%200==0)printf("请输⼊数据,以回车键结束\r\n");
if(times%30==0)LED0=!LED0;//闪烁LED,提⽰系统正在运⾏.
delay_ms(10);
}
}
}
1. 完成串⼝的相关设置,使能相应中断,以及初始化过程,然后进⼊while等待;
2. 当在串⼝调试助⼿的想窗体输⼊想要发送的数据,并点击发送后,当单⽚机开始接受数据时便会触发中断函数USART1 IRQHandler(
)对数据进⾏接收并最终存储进缓存RX_BUF_USART中,其在usart.h中的⽣命为extern u8
USART_RX_BUF[USART_REC_LEN]。其中USART_REC_LEN=200
3. 通过语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区域所⽰。
4. 当数据没有接受完,或者没有向单⽚机发送数据时,单⽚机便会通过printf函数向上位机按照⼀定时间间隔发送“\r\nALIENTEK 探
索者STM32F407开发板 串⼝实验\r\n”、“正点原⼦@ALIENTEK\r\n\r\n”和“请输⼊数据,以回车键结束\r\n”语句。
注意:重定向的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);//等待发送结束
for(t=0;t<len;t++)
{
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
USART_SendData(USART1, USART_RX_BUF[t]);        //向串⼝1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
修改后实验现象恢复正常如下:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。