STM32串口通信详解以及通信异常或者卡死常见问题分析

发布时间:
来源: 电子工程世界

异常一:数据传输中会出现乱码


数据传输中会出现乱码,很有可能是数组溢出,或者定义的数组长度不够。或者中断被打断。


异常二:程序卡在中断函数里面无法跳出执行主函数的逻辑


中断标志位没有被清除,在这里要注意一点,串口中断标志位自动清空的前提是软件需要先读USART_SR寄存器,然后读USART_DR寄存器来自动清除。即串口中断事件发生后,如果使能的接收中断,而中断函数里面什么都不执行的话,接收中断标志位是无法自动清空的,故而,函数会一直卡在中断函数里面。


比如一下这个函数,该函数没有逻辑问题,但会引发以上问题,代码如下


extern unsigned char star_time_led ;  //计时开始变量

unsigned char recv_flag = 0;//定义接受标志位

unsigned long recv_cnt = 0;//串口1接收数据缓存

unsigned char recv_buf[MAX_REV_NUM];//串口1接收数据缓存

extern unsigned char star_time;

extern unsigned char recv_time_cnt;

/*

以下写法有严重问题


如果没有这句函数→USART_ClearFlag(USART1,USART_FLAG_RXNE); //清空中断标志位


串口接收中断标志位将文法被清空,会导致函数卡在中断函数里面一直循环,无法正常运行主函数


原因分析:


中断条件成立后,中断标志位将会标记,程序将会进入中断函数运行,软件自动轻触中断标志位的条件是


先读USART_SR寄存器,再读USART_DR寄存器。


void USART1_IRQHandler(void)                //串口1中断服务程序

{

if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //是否发送中断事件

{

star_time = 1;         //接受到一帧数据的时候,打开软件定时器,去计数

if(recv_cnt < MAX_REV_NUM)//数组长度是否超过缓存区

{

recv_buf[recv_cnt] =USART_ReceiveData(USART1);//将接收到的数据存在数组Usart1RecBuf[RxCounter]里

recv_cnt++;

}

else

{

recv_cnt = MAX_REV_NUM

;//限制数组长度,超过缓存区则不再接收

}

recv_time_cnt = 0; //每接收到一帧数据,把定时计数器清零,相当于喂狗

                   //但是在定时器中断里面会不断的累加

USART_ClearFlag(USART1,USART_FLAG_RXNE); //清空中断标志位

}

*/

上述代码优化后如下

void USART1_IRQHandler(void)                //串口1中断服务程序

{

static char ch;

if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //是否发送中断事件

{

ch = USART_ReceiveData(USART1);//将接收到的数据存在数组Usart1RecBuf[RxCounter]里

star_time = 1;         //接受到一帧数据的时候,打开软件定时器,去计数

if(recv_cnt < MAX_REV_NUM)//数组长度是否超过缓存区

{

recv_buf[recv_cnt] =ch;//将接收到的数据存在数组Usart1RecBuf[RxCounter]里

recv_cnt++;

}

else

{

recv_cnt = MAX_REV_NUM

;//限制数组长度,超过缓存区则不再接收

}

recv_time_cnt = 0; //每接收到一帧数据,把定时计数器清零,相当于喂狗

                   //但是在定时器中断里面会不断的累加

USART_ClearFlag(USART1,USART_FLAG_RXNE); //清空中断标志位

}

异常三:数据发送中间歇性数据异常漏发乱发等

对于这些奇奇怪怪的问题,首先要了解一下发送函数是怎么发送的


USART_DR 包含了已发送的数据或者接收到的数据。USART_DR 实际是包含了两个寄存器,一个专门用于发送的可写 TDR,一个专门用于接收的可读 RDR。当进行发送操作时,往 USART_DR 写入数据会自动存储在 TDR 内;当进行读取操作时,向 USART_DR读取数据会自动提取 RDR 数据。


TDR 和 RDR 都是介于系统总线和移位寄存器之间。串行通信是一个位一个位传输的,发送时把 TDR 内容转移到发送移位寄存器,然后把移位寄存器数据每一位发送出去,接收时把接收到的每一位顺序保存在接收移位寄存器内然后才转移到 RDR。


当 TDR 内容转移到发送移位寄存器,还没有发送出去的,就再次把TDR 内容转移到发送移位寄存器里,就会出现少发的现象。


什么时候会有这种情况呢?错误操作代码如下:


void USART2_IRQHandler(void)                //串口2中断服务程序

{

if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //是否发送中断事件

{

Usart1RecBuf[RxCounter] =USART_ReceiveData(USART2);//将接收到的数据存在数组Usart1RecBuf[RxCounter]里

RxCounter++;//指向数组地址自加

if(RxCounter==2) 

USART_SendData(USART1, Usart1RecBuf[0]);//发送Usart1RecBuf[0]

                USART_SendData(USART1, Usart1RecBuf[1]);//发送Usart1RecBuf[1]

USART_SendData(USART1, Usart1RecBuf[2]);//发送Usart1RecBuf[2]

}

}

}


上述代码连续运行了3次USART_SendData(USART1, Usart1RecBuf);这个函数,这种情况一般都会出现只有最后一个数据发送成功出去。原因可能就是数据还没有发送出去,发送移位寄存器就更新了。


文章来源于: 电子工程世界 原文链接

本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。