在使用单片机的串口通信功能时,常用的接收数据方法是通过固定的字节数来判断一帧数是否发送完成,或者是通过固定的结束标志位来表示一帧数据发送完成。但是有时候会遇到发送的数据长度不固定,也没有固定的结束标志,对于这样的数据通常的做法是每隔一段时间查看一下接收数据的长度是否发生了变化,如果指定的一段时间内接收数据长度没有发生变化,就认为是一帧数据发送完成。在STM32单片机中串口提供了一个更好用的功能,就是空闲中断功能。也就是说当一帧数据发送结束后,就会产生一个空闲中断。这样就可以利用这个空闲中断来判断一帧数据接收是否完成。

关于串口空闲检测可以在STM32参考手册上找到相关介绍

通过这个图可以看出来,当第一组数据Data1、Data2、Data3、Data4发送结束后,总线就会处于空闲状态,这时就会产生一个空闲中断。

当Data1、Data2、Data3、Data4每一个数据到来时串口产生的中断为 RXNE:读数据寄存器非空中断。具体的相关介绍可以在状态寄存器(USART_SR)中查看。

也就是每接收一个字节,串口会产生一个RXNE中断,当一帧数据发送完成就产生一个IDLE中断。

这样就可以在串口每个RXNE中断来临后将数据先存储起来,然后在IDLE中断到来后说明数据接收结束。这时候就可以去处理接收到的数据了。

下面就通过代码来说明一下如何使用串口空闲中断来接收不定长数据

首先初始化串口

void  uart2_init( u16 baud )
{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef  NVIC_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;           //推挽复用模式GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init( GPIOA, &GPIO_InitStructure );GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //浮空输入模式GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init( GPIOA, &GPIO_InitStructure );NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init( &NVIC_InitStructure );USART_InitStructure.USART_BaudRate = baud;USART_InitStructure.USART_WordLength = USART_WordLength_8b;USART_InitStructure.USART_StopBits = USART_StopBits_1;USART_InitStructure.USART_Parity = USART_Parity_No;USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_Init( USART2, &USART_InitStructure );USART_ITConfig( USART2, USART_IT_IDLE, ENABLE );    //使能串口空闲中断USART_ITConfig( USART2, USART_IT_RXNE, ENABLE );  //使能串口RXNE接收中断USART_Cmd( USART2, ENABLE );                      //使能串口2
}

与常规的串口初始化唯一不同的就是多了一行空闲中断初始化代码

USART_ITConfig( USART2, USART_IT_IDLE, ENABLE );                                             //使能串口空闲中断

下来在中断中根据中断类型来处理数据

void USART2_IRQHandler( void )
{u8 tem = 0;if( USART_GetITStatus( USART2, USART_IT_RXNE ) != RESET )   //接收中断  接收到一个字节产生一次中断{tem = USART_ReceiveData( USART2 );  //读取数据,可以自动将中断标志位RXNE清零rec_buff[uart2_rec_cnt++] = tem;   //存储接收到的数据}if( USART_GetITStatus( USART2, USART_IT_IDLE ) != RESET )//空闲中断 接收到一帧数据 产生一次中断{tem = USART2->SR;    //读取SR寄存器tem = USART2->DR;    //读取DR寄存器 (先读USART_SR,然后读USART_DR可以清除空闲中断标志位IDLE)copy_data( rec_buff, uart2_rec_cnt );    //备份数据receiveOK_flag = 1;                      //接收完成标志位置位uart2_rec_cnt = 0;                      //接收数据长度清零}
}

当串口是RXNE中断时,就直接将数据存储到数组中,当IDLE中断到来后就将接收到的数据存储起来,然后置位接收一帧数据成功标志。这样主程序在检测到接收数据成功标志后,就可以去处理数据了。

这样通过IDLE中断,就可以轻松接收到不定长的数据了,不论数据长度是多少,只要是一帧数据结束,IDLE中断就可以检测到。

完整代码如下

#ifndef __UART2_H
#define __UART2_H
#include "sys.h"#define   UART2_REC_LEN   20                              //串口缓存区长度void  uart2_init( u16 baud );
void uartDMA_Init( void );
void myDMA_Enable( DMA_Channel_TypeDef*DMA_CHx );
void uart2_Send( u8 *buf, u16 len );
void copy_data( u8 *buf, u16 len );#endif
//串口2空闲中断,接收不定长数据
#include "uart2.h"
u8 rec_buff[UART2_REC_LEN] = {0};
u16 uart2_rec_cnt = 0;                             //串口接收数据长度
u8 data_backup[UART2_REC_LEN] = {0};                                       //数据备份
u16 dataLen_backup = 0;                                                //长度备份
_Bool receiveOK_flag = 0;                                      //接收完成标志位
/*
空闲中断是什么意思呢?
指的是当总线接收数据时,一旦数据流断了,此时总线没有接收传输,处于空闲状态,IDLE就会置1,产生空闲中断;又有数据发送时,IDLE位就会置0;
注意:置1之后它不会自动清0,也不会因为状态位是1而一直产生中断,它只有0跳变到1时才会产生,也可以理解为上升沿触发。
所以,为确保下次空闲中断正常进行,需要在中断服务函数发送任意数据来清除标志位。
*/void  uart2_init( u16 baud )
{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef  NVIC_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                   //推挽复用模式GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init( GPIOA, &GPIO_InitStructure );GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;      //浮空输入模式GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init( GPIOA, &GPIO_InitStructure );NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init( &NVIC_InitStructure );USART_InitStructure.USART_BaudRate = baud;USART_InitStructure.USART_WordLength = USART_WordLength_8b;USART_InitStructure.USART_StopBits = USART_StopBits_1;USART_InitStructure.USART_Parity = USART_Parity_No;USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_Init( USART2, &USART_InitStructure );USART_ITConfig( USART2, USART_IT_IDLE, ENABLE );                                            //使能串口空闲中断USART_ITConfig( USART2, USART_IT_RXNE, ENABLE );                                      //使能串口RXNE接收中断USART_Cmd( USART2, ENABLE );                                                                                  //使能串口2
}//发送len个字节
//buf:发送区首地址
//len:发送的字节数
void uart2_Send( u8 *buf, u16 len )
{u16 t;for( t = 0; t < len; t++ )                                                                                      //循环发送数据{while( USART_GetFlagStatus( USART2, USART_FLAG_TC ) == RESET );USART_SendData( USART2, buf[t] );}while( USART_GetFlagStatus( USART2, USART_FLAG_TC ) == RESET );
}//备份接收到的数据
void copy_data( u8 *buf, u16 len )
{u16 t;dataLen_backup = len;                                                                                                        //保存数据长度for( t = 0; t < len; t++ ){data_backup[t] = buf[t];                                                                                          //备份接收到的数据,防止在处理数据过程中接收到新数据,将旧数据覆盖掉。}
}//利用空闲中断接收串口不定长数据
//RXNE中断和IDLE中断的区别?
//当接收到1个字节,就会产生RXNE中断,当接收到一帧数据,就会产生IDLE中断。比如给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。void USART2_IRQHandler( void )
{u8 tem = 0;if( USART_GetITStatus( USART2, USART_IT_RXNE ) != RESET )                        //接收中断  接收到一个字节产生一次中断{tem = USART_ReceiveData( USART2 );                                                                 //读取数据,可以自动将中断标志位RXNE清零rec_buff[uart2_rec_cnt++] = tem;                                                               //存储接收到的数据}if( USART_GetITStatus( USART2, USART_IT_IDLE ) != RESET )                           //空闲中断 接收到一帧数据 产生一次中断{tem = USART2->SR;                                                                                                     //读取SR寄存器tem = USART2->DR;                                                                                                  //读取DR寄存器 (先读USART_SR,然后读USART_DR可以清除空闲中断标志位IDLE)//uart2_Send( rec_buff, uart2_rec_cnt );                                                    //一帧数据接收完毕,将接收到的数据发送出去copy_data( rec_buff, uart2_rec_cnt );                                                        //备份数据receiveOK_flag = 1;                                                                                                //接收完成标志位置位uart2_rec_cnt = 0;                                                                                              //接收数据长度清零}
}
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "uart2.h"
#include "string.h"extern u8 data_backup[UART2_REC_LEN];                                                  //数据备份
extern u16 dataLen_backup;                                                                          //长度备份
extern _Bool receiveOK_flag;                                                                        //接收完成标志位int main( void )
{u8  j = 0;NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );delay_init();                                                                                      //延时函数初始化LED_Init();                                                                                        //初始化与LED连接的硬件接口uart2_init( 9600 );while( 1 ){if( receiveOK_flag )                                                                      //一帧数据接收完成后开始处理数据{receiveOK_flag = 0;uart2_Send( data_backup, dataLen_backup );                    //发送数据memset( data_backup, 0, sizeof( data_backup ) );      //清空备份数组}j++;if( j > 50 ){j = 0;LED = !LED;}delay_ms( 10 );}
}

测试效果如下

只要发送的数据长度不超过接收缓存区的长度,发送的数据都可以准确的接收到。

完整工程下载地址 https://download.csdn.net/download/qq_20222919/12926272

STM32单片机串口空闲中断接收不定长数据相关推荐

  1. STM32使用串口空闲中断接收不定长数据帧-USART_IT_IDLE使用(不使用DMA方式)

    STM32使用串口空闲中断接收不定长数据帧-USART_IT_IDLE使用(不使用DMA方式) 前言 串口空闲中断介绍 清中断方法 串口中断处理函数 串口中断用到的全局变量定义 串口初始化(使能接收中 ...

  2. stm32串口空闲中断接收不定长数据

    串口空闲中断接收不定长数据 空闲中断是接受数据后出现一个byte的高电平(空闲)状态,就会触发空闲中断.并不是空闲就会一直中断,准确的说应该是上升沿(停止位)后一个byte,如果一直是低电平是不会触发 ...

  3. STM32H7 DMA USART空闲中断接收不定长数据

    1.关键设置 2.代码 2.1 bsp_usart.h /*********************************************************************** ...

  4. STM32单片机串口空闲中断+DMA接收不定长数据

    在上一篇文章STM32单片机串口空闲中断接收不定长数据中介绍了利用串口空闲中断接收不定长数据,这种方式有一个问题就是串口每接收到一个字节就会进入一次中断,如果发送的数据比较频繁,那么串口中断就会不停打 ...

  5. stm32的串口DMA空闲中断接收不等长数据,stm32F4的usart2-DMA-IDLE收发

    1. 串口为什么要使用DMA?好处? 提高系统实时性:stm32单片机的串口没有FIFO,使用字节中断的方式去接收,会频繁进入中断,影响系统实时性.好在stm32的串口可以级联DMA使用,在大数据量连 ...

  6. stm32的串口DMA空闲中断接收不等长数据,stm32F1的usart1-DMA-IDLE收发

    stm32的DMA收发原理,和stm32F4 + USART2 +DMA +IDLE使用,见另一篇:https://blog.csdn.net/Mark_md/article/details/1072 ...

  7. STM32使用串口1配合DMA接收不定长数据,大大减轻CPU载荷

    摘自:http://www.openedv.com/thread-63849-1-1.html 参考:https://blog.csdn.net/heda3/article/details/80602 ...

  8. HAL库的串口基础学习(包含串口接收不定长数据的实现)

    HAL库的串口基础学习(1) HAL库有一个特点就是对于许多外设的初始化以及功能操作,都提供有一个weak版本的函数,这是充分的展现出库名字的含义(Hardware Abstraction Layer ...

  9. openmv串口数据 串口助手_STM32 串口接收不定长数据 STM32 USART空闲检测中断

    编者注: 单片机串口接收不定长数据时,必须面对的一个问题为:怎么判断这一包数据接收完成了呢?常见的方法主要有以下两种: 1.在接收数据时启动一个定时器,在指定时间间隔内没有接收到新数据,认为数据接收完 ...

最新文章

  1. vmware响应时间过长_性能调优高并发下如何缩短响应时间
  2. 基于ESP32的竞赛裁判系统功能调试-硬件修改建议
  3. oracle学习-存储过程返回一个值,和返回一个结果集
  4. 7-2 字符串逆序 (15 分)
  5. IT运维管理是企业信息化的大管家
  6. debian 安装 php,Ubuntu/Debian上安装Nginx+php环境详细教程
  7. 家庭路由器哪家强:固件漏洞多年不修复,更新无济于事
  8. WMV格式如何转为高清无损MP4视频格式
  9. PlutoSDR软件无线电平台带宽破解
  10. 神州数码交换机enable密码清除
  11. npm init vite@latest 报错
  12. 微博如何做到1小时增加一千台服务器应对鹿晗恋情带来的流量暴增
  13. 小白学习MySQL - 一次慢SQL的定位
  14. ant弹窗_基于AntDesign改造优化的Modal弹窗
  15. 百度网盘PC端扫描二维码登录时无法加载二维码问题解决方法
  16. 最新精仿小刀娱乐资源网模板源码,带前台会员投稿审核功能
  17. NOR 与 NAND的区别对比分析
  18. 机器学习:《统计学习方法》笔记(一)—— 隐马尔可夫模型
  19. 宝尚在线炒股 12.22 午评
  20. 重庆科创学院03级计算机文秘,重庆科创职业学院——国际商务文秘实务课程标准.doc...

热门文章

  1. 【js】js资料(2)
  2. 利用Tor(The Onion Router,洋葱路由)访问Sourceforge
  3. 建筑CAD制图教程:什么是双跑楼梯?
  4. 信用评分之四--What Is a Hard Inquiry?(Fico信用查询之“硬查询”)
  5. ssh登录の周辺 Mosh
  6. TouchGFX升级至V4.12,每秒帧数从9帧升级至60帧,同时更新TouchGFX Suite
  7. Unity(转载) 图形渲染与优化
  8. 【排序】【牛客模拟赛】牛半仙的妹子串
  9. 2022-2028年中国亲子装行业市场调查研究及投资策略研究报告
  10. 中国广电确定与一家运营商共建共享,电信?移动?联通?