摘要:在实际项目中经常用到串口接收一些不定长的数据,怎么判断这一帧数据接收完成了呢?通常使用UART非空中断配合简单的数据协议,在数据中加入帧头、帧尾,在程序中判断是否接收到帧尾来确定数据接收完毕,对每个字节数据都要进行判断,比较消耗系统资源,尤其是在一些实时性要求较高的场合。而串口空闲中断可以大大简化数据接收过程的判断,在这一块起到非常重要的作用。

关于这种方法,可以看以前STM32第五章-串口通讯详解。下面说一下空闲中断+DMA。

一、什么是空闲中断?

空闲中断(IDLE),俗称帧中断,即第一帧数据接收完毕到第二帧数据开始接收期间存在一个空闲状态(每接收一帧数据后空闲标志位置1),检测到此空闲状态后即执行中断程序。空闲中断的优点在于省去了帧头帧尾的检测,进入中断程序即意味着已经接收到一组完整数据,仅需即时对数据处理或将数据转移出缓冲区即可。

串口空闲中断在串口无数据接收的情况下,是不会产生的,产生的条件是当清除空闲标志位后,必须有接收到第一个数据后,才开始触发,一旦接收的数据断流,没有接收到数据,即产生空闲中断

MM32F3277系列的UART支持空闲中断功能。相关的寄存器包括中断状态寄存器(UART_ISR)、 中断使能寄存器(UART_IER)、 中断清除寄存器(UART_ICR),对应的位如下:

中断状态寄存器(UART_ISR)

中断使能寄存器(UART_IER)

中断清除寄存器(UART_ICR)

二、串口初始化

这里以串口1为例,串口1一般就这打印调试使用。

static void RS485_Init(uint32_t baudrate)
{   GPIO_InitTypeDef GPIO_InitStruct;UART_InitTypeDef UART_InitStruct;NVIC_InitTypeDef NVIC_InitStructure;//打开串口和GPIO的时钟RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART3, ENABLE);//打开GPIO的复用功能,具体复用到哪一个线,要看数据手册GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_7);GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_7);//UART3_TX   GPIOB.10GPIO_StructInit(&GPIO_InitStruct);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(GPIOB, &GPIO_InitStruct);//UART3_RX    GPIOB.11GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(GPIOB, &GPIO_InitStruct);//串口参数初始化UART_StructInit(&UART_InitStruct);UART_InitStruct.BaudRate = baudrate;UART_InitStruct.WordLength = UART_WordLength_8b;UART_InitStruct.StopBits = UART_StopBits_1;UART_InitStruct.Parity = UART_Parity_No;UART_InitStruct.HWFlowControl = UART_HWFlowControl_None;UART_InitStruct.Mode = UART_Mode_Rx | UART_Mode_Tx;   UART_Init(UART3, &UART_InitStruct);//使能串口空闲中断UART_ITConfig(UART3,  UART_IER_RXIDLE, ENABLE);//使能串口UART_Cmd(UART3, ENABLE);    //因为要使用中断,所以必须配置中断优先级NVIC_InitStructure.NVIC_IRQChannel= UART3_IRQn; //指定中断源NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE;NVIC_Init(&NVIC_InitStructure);
}

中断服务函数

void UART3_IRQHandler(void)
{if(UART_GetITStatus(UART3, UART_ISR_RXIDLE) != RESET){/*清除串口空闲中断标志位*/UART_ClearITPendingBit(UART3, UART_ICR_RXIDLE);/*数据处理*/}
}

这样就可以了。但是串口空闲中断一般是配合DMA的方式。

三、串口DMA

一般串口空闲中断配合MM32F0270 UART使用DMA方式接收数据可以减小CPU的开销。

对于接收定长数据,可以将DMA接收缓冲区的长度设定为待接收数据的长度,这样利用DMA的传输完成中断就可以知道已经接收了一帧数据。

对于接收不定长数据,由于内核在串口接收数据到空闲这段时间,是不受理串口数据的,所以可以使用DMA来协助我们把数据传送到指定的地方,当数据传输完成后,通知内核去处理。

注意每一款单片机的串口DMA通道不一样,一定要看数据手册。MM32F3277的DMA各通道请求映像表如下,其中UART3_RX对应通道3。
再来看以下MM32F0270的DMA各通道请求映像,两款芯片串口对应的通道是不一样的。
通过DMA方式把数据传送到指定的缓存区。当UART接收完一帧数据后,会产生一个空闲中断。这个中断在UART其他任何状态都不产生,只会在接收完一帧数据后才会产生,一帧数据可以是1个字节或者多个字节。

四、DMA初始化

void UART_Receive_DMA(UART_TypeDef* uart, uint32_t cmar, uint16_t cndtr)
{DMA_InitTypeDef  DMA_StructInit;//UART1_RX DMA1_Channel5//UART2_RX DMA1_Channel6//UART3_RX DMA1_Channel3RCC_AHBPeriphClockCmd(RCC_AHBENR_DMA1, ENABLE);/* DMA1 Channel3 (triggered by USART3 Rx event) Config */DMA_DeInit(DMA1_Channel3);DMA_StructInit.DMA_PeripheralBaseAddr = (uint32_t)&UART3->RDR;DMA_StructInit.DMA_MemoryBaseAddr     = cmar;DMA_StructInit.DMA_DIR                = DMA_DIR_PeripheralSRC;      //外设到内存DMA_StructInit.DMA_BufferSize         = cndtr;                      //DMA 通道的 DMA 缓存的大小DMA_StructInit.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;  //外设地址不变DMA_StructInit.DMA_MemoryInc          = DMA_MemoryInc_Enable;      //内存地址递增DMA_StructInit.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //8 位DMA_StructInit.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;       //8 位DMA_StructInit.DMA_Mode               = DMA_Mode_Normal;              //工作在正常缓存模式DMA_StructInit.DMA_Priority           = DMA_Priority_Medium;            //DMA 通道 x 拥有中优先级DMA_StructInit.DMA_M2M                   = DMA_M2M_Disable;DMA_StructInit.DMA_Auto_reload           = DMA_Auto_Reload_Disable;DMA_Init(DMA1_Channel3, &DMA_StructInit);/* Enable USART3 DMA Rx request */UART_DMACmd(UART3, UART_DMAReq_EN, ENABLE);DMA_Cmd(DMA1_Channel3, ENABLE);
}

关于DMA的就不用多说了,数据从内存到外设,或者外设到内存,或者内存到内存都不需要CPU的参与,直接存储器访问即可,减轻了CPU的压力。

既然有了增加了DMA,所以我们的串口终端服务函数也要修改一下了。

void UART3_IRQHandler(void)
{if(UART_GetITStatus(UART3, UART_ISR_RXIDLE) != RESET){/*清除串口空闲中断标志位*/UART_ClearITPendingBit(UART3, UART_ICR_RXIDLE);DMA_Cmd(DMA1_Channel3, DISABLE);//停止DMA传输        /*数据处理*/DMA_Cmd(DMA1_Channel3, ENABLE);  //使能DMA传输}
}

在中断服务函数中,当发生了空闲中断时要关闭DMA,停止DMA数据传输,然后进行处理数据,在数据处理完成之后在打开DMA,继续接收数据。

调试

打开串口调试助手发送一帧数据,如:666,程序运行至断点1
UART中断状态寄存器UART_ISRRXIDLE_INTF位置1,表示进入UART空闲中断。

程序运行至断点2,UART中断状态寄存器UART_ISRRXIDLE_INTF位清0。
这时串口接收到的数据就自动保存到了DMA初始化中你填写的那个内存基地址所在的内存区域了,想什么时候用就什么时候用。

你学会了吗?

MM32F3277空闲中断+DMA接收不定长数据相关推荐

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

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

  2. android 串口一直打开_STM32之串口DMA接收不定长数据

    STM32之串口DMA接收不定长数据 引言 在使用stm32或者其他单片机的时候,会经常使用到串口通讯,那么如何有效地接收数据呢?假如这段数据是不定长的有如何高效接收呢? 同学A:数据来了就会进入串口 ...

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

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

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

    STM32使用串口1配合DMA接收不定长数据,减轻CPU载荷 http://www.openedv.com/thread-63849-1-1.html 实现思路:采 用STM32F103的串口1,并配 ...

  5. STM32 HAL库 串口DMA接收不定长数据

    STM32 HAL库 串口DMA接收不定长数据 整体思路:我是用的CUBEMX软件生成的工程,使能了两个串口,串口2用来接收不定长的数据,串口1用来发送串口2接收到的数据:串口2我找了一个UBLOX卫 ...

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

    最近经常看见坛友在论坛上问串口接收的问题,我之前刚好由于项目需要用到PLC的PPI协议,需要不停地利用串口接收数据,一开始的时候采用单字节中断的方式接收判断.但是用来做通信的时候需要不停的产生串口接收 ...

  7. 第九章 AT32F403A基于V2库串口 dma接收不定长数据

    目录 概述 硬件 DMA 软件 流程 初始化 初始化代码: 中断服务函数: DMA1通道5设置函数:(重新使能通道) DMA1通道4发送函数:(设置dma长度和内存地址) 测试 最后 概述 本文主要是 ...

  8. 串口IDLE空闲中断+DMA实现接收不定长数据基于stm32cubemx

    引言:对于串口接收一些不定长的数据,必须面对一个问题:怎么判断一帧数据接收是否完成?通常使用RXNE非空中断配合简单的数据协议,在数据中加入帧头.帧尾,在程序中判断是否接收到帧尾来确定数据接收完毕,因 ...

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

    在使用单片机的串口通信功能时,常用的接收数据方法是通过固定的字节数来判断一帧数是否发送完成,或者是通过固定的结束标志位来表示一帧数据发送完成.但是有时候会遇到发送的数据长度不固定,也没有固定的结束标志 ...

最新文章

  1. 编译可在Android上运行的qemu user mode
  2. react ui框架_顶级React组件库推荐
  3. java request 原理_JavaWeb-seession原理
  4. Selenium + WebDriver 各浏览器驱动下载地址
  5. 用shell获取mysql主从状态_shell监控MySQL主从状态脚本两则
  6. 无废话WPF系列5:控件派生图
  7. 钱学森做的试卷你见过吗?100年前学霸的笔记,工整得像打印的
  8. [深度学习-原理]浅谈Attention Model
  9. L2-007. 家庭房产
  10. python开发总结
  11. Vue将echarts数据导出成excel文件
  12. Django之中间件,csrf跨站伪造请求,auth认证模块
  13. 查看exe代码_【安全风险通告】Windows Type 1字体解析远程代码执行漏洞安全风险通告...
  14. 字符串转换的UnicodeDecodeError—— ‘\xa0’问题
  15. 毕业设计html5作品,基于HTML5的年货购物网站的设计与实现毕业论文+任务书+开题报告+设计源码...
  16. Oracle 字段 中文英文拆分
  17. 三菱PLC通信(MC协议A-1E和Qna-3E模式)
  18. 在c语言中的变量分为三种类型,在C语言中的实型变量分为两种类型,它们是_______和__________ 答案:float double...
  19. 分布式任务调度平台如何选型?
  20. pythonstdin_关于python:从sys.stdin接收输入,非阻塞

热门文章

  1. Mikrotik路由器(routerOS)调试环境搭建
  2. java解析shp文件以及坐标转换(工具类)
  3. [编程题]漂流船问题
  4. android守护进程详解
  5. 因为文件目录存在空格导致kafka运行错误:提示找不到或者无法加载主类错误
  6. 10道字节跳动C++/Java笔试真题你能做对几道?3道就赢了80%的竞争者(含答案)
  7. Unity 给物体加贴图
  8. 什么是个人热点?安卓手机怎么开启个人热点?
  9. 手撕七大排序 (二)
  10. 快来体验一下,让你的浏览器焕然一新