串口通信是经常使用到的功能,在STM32中UART具有DMA功能,并且收发都可以使用DMA,使用DMA发送基本上大家不会遇到什么问题,因为发送的时候会告知DMA发送的数据长度,DMA按照发送的长度直接发送就OK了,但是使用DMA接收时候就不同了,因为有时候数据接收并不是每一次都是定长的,但是DMA只在接收数据长度和设定数据长度相同的时候才可以触发中断,告诉MCU数据接收完毕,针对这个问题,解决方法如下,有一点复杂,但是很管用。
1、        首先了解串口通信的协议

(原文件名:1.jpg)

从上图可知,UART在传输一个字节的时候,首先拉低,传输起始位,然后在是LSB –MSB,最后是停止位,停止位是高电平
2、        超时时间
搞过串口通信的都知道,如果串口有协议,一般都是有个超时时间的,超时时间是定义两个帧之间的间隔的,如果串口接收到一个字节后,在规定的超时时间内没有接收到其他数据,我们则认为前面接收的数据位一帧。

(原文件名:2.jpg)  
3、        定时器复位复位模式
STM32定时器功能比较强大,其中有一种模式为复位模式,

(原文件名:3.jpg)  
上图STM32 用户手册中的举例,注意红色箭头指向的位置,TI1的输入上升沿会复位定时器的计数器,具体请查阅STM32用户手册关于这部分的描述。
整体的思路是这样的,一开始设置好DMA接收,可以把缓冲区长度设置为帧最大长度,我们可以把RX连接到定时器的管脚输入端,并且一开始设置输入并且使能引脚下降沿中断,当帧的第一个字节发送时,因为起始位为低电平,空闲时UART为高电平,满足条件,进入中断,禁止中断,并且在中断中开启定时器,该定时器工作在复位模式,上升沿复位,并且设置好定时器输出比较值为超时时间,比如20ms,这样,在传输后面字节时,肯定会有高低电平出现,即便是传输的是0x00,0xFF,虽然UART数据区不变,但是都为1,或都为0,但是因为起始位为低电平,停止位是高电平,所以肯定会有上升沿,定时器会一直复位,输出定时器的计数器一直到达不了输出比较值,当一帧传输结束后,定时在最后一个字节复位后,由于没有数据继续到达,无法复位,则计数器就能计到输出比较值,这时发出中断,在定时器中断中可以计算出接收数据的长度,并且通知外部数据已经接收完毕。
4、        功能实现
实现的步骤:
1、硬件连接:UART的RX线在连接外部的同时,还需要连接到一个定时器的输入端TIMx_CHx,定时器可以为任意定时器,但是CHx,只能为CH1或CH2,具体的需要看STM32的定时器逻辑图,以STM32F101CB为例,我们暂定把UART1的RX在连接RS232的同时,还连接到TIM4_CH2。
2、软件设置
a) IO、中断设置:在把UART功能口设置好后,还需要设置TIM4_CH2为输入上拉,并且使能该引脚外部中断

/**
  * @brief  Configures the different GPIO ports.
  * @param  None
  * @retval : None
  */
void GPIO_Configuration(void)
{
/* Configure USART1_Rx as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
    
/* Configure USART1_Tx as alternate push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* GPIOB.7 Configuration: TIM4 Channel2 as input floatinng */
GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING ;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/**
  * @brief  Configures the system EXIT.
  * @param  None
  * @retval None
  */
void EXTI_Configuration(void)
{
    EXTI_InitTypeDef   EXTI_InitStructure;
    /* Connect EXTI8 Line to PB.07 pin */
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource7);
    
    /* Configure EXTI8 line */
    EXTI_InitStructure.EXTI_Line = EXTI_Line7;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
    
    /* Clears  EXTI line pending bits. */
    EXTI_ClearITPendingBit(EXTI_Line7);
}

/**
  * @brief  Configures NVIC and Vector Table base location.
  * @param  None
  * @retval : None
  */
void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    /* Configure the NVIC Preemption Priority Bits*/  
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
    /* Set the Vector Table base location at 0x08000000 */ 
    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
  
    
    /* Enable the DMA1_Channel_Rx Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    /* Configure DMA1_Channel_Tx interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    /* Configure TIM1 update interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    /* Enable and set EXTI9_5 Interrupt to the lowest priority */
    NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 6;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
把DMA接收的数据缓冲区设置为你认为最大的帧长度,(如果最长不能确定,也可以随便指定一个长度,后面再讲怎么实现)。
b) 定时器设置
因为使用的是TIM4_CH2,所以需要配置TIM4,并且配置为复位模式,把超时时间定为20ms,为了方便TIM4时钟定输入为1KHZ
/*
* This function is called by timer_init() to perform the non-generic portion
* of the initialization of the timer module.
*/
void timer_init_non_generic(void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;
    TIM_ICInitTypeDef  TIM_ICInitStructure;
    
    /* TIM4 configuration ----------------------------------------------------*/ 
    TIM_TimeBaseStructure.TIM_Period = 65535;
    TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock/1000000 * 1000 - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
    
    /* Output Compare  Mode configuration: Channel1 */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
    TIM_OCInitStructure.TIM_Pulse = 20;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
    TIM_OC1Init(TIM4, &TIM_OCInitStructure);
    
    /* TIM4 Channel 2 Input Capture Configuration */
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICFilter = 0;
    TIM_ICInit(TIM4, &TIM_ICInitStructure);
    
    /* TIM4 Input trigger configuration: External Trigger connected to TI2 */
    TIM_SelectInputTrigger(TIM4, TIM_TS_TI2FP2);
    
    /* TIM4 configuration in slave reset mode  where the timer counter is 
    re-initialied in response to rising edges on an input capture (TI2) */
    TIM_SelectSlaveMode(TIM4,  TIM_SlaveMode_Reset);
    
    /* TIM4 IT CC1 enable */
    TIM_ITConfig(TIM4, TIM_IT_CC1, ENABLE);
    
}
c)工作过程如下
在串口传输起始位的时候,首先产生外部中断,在外部中断中开启定时器,禁止外部中断,只要串口上一直有数据,定时器肯定会不停的复位,到达不了定时时间,当串口上没有数据的时候,到超时时间后,定时器产生中断,此时可以读出接收的数据长度,然后开启外部中断,进入下一个周期。

(原文件名:4.jpg)

总结:本方法的缺点是程序开始的初始化麻烦些,但是优点是非常明显的,彻底解放了CPU,这样在计算串口超时的时候,就不需要定时器不停的中断,并且串口接收数据使用DMA方式,也不需要CPU参与,只是在接收结束的时候通知CPU取数据,CPU的利用率会更高。

STM32 UART DMA实现未知数据长度接收(转自amoBBs)相关推荐

  1. STM32使用DMA发送串口数据

    1.概述 上一篇文章<STM32使用DMA接收串口数据>讲解了如何使用DMA接收数据,使用DMA外设和串口外设,使用的中断是串口空闲中断.本篇文章主要讲解使用DMA发送数据,不会讲解基础的 ...

  2. STM32 串口DMA接收 Openmv / K210 整数、小数字符串数据 (基于HAL库)

    目录 前言 一.工程配置 二.串口DMA部分代码 1.源文件UART_DMA.c 2.头文件UART_DMA.h 3.stm32f1xx_it.c的修改 4.串口收发DMA测试 三.字符串数字提取代码 ...

  3. STM32 Uart中断接收

    小明正在玩游戏,小明的妈妈喊小明吃饭,这时,小明放下游戏,先去吃饭,吃完饭后,继续玩游戏. 这就是中断!"正在玩游戏"是执行程序,"小明的妈妈"是中断源,&qu ...

  4. STM32使用DMA接收串口数据

    目录 01.概述 02.DMA接收 03.中断 04.代码 01.概述 在之前的文章里<STM32串口详解>和<STM32 DMA详解>文章中,详细讲解了STM32的串口和DM ...

  5. STM32串口DMA方式接收数据。类似环形FIFO。超省CPU资源!

    本文转自:http://www.amobbs.com/thread-5530588-2-1.html 一直以来都为串口接收数据所困扰: 1:如果用接收中断的话,每接收1byte就得中断一次.这样太消耗 ...

  6. STM32:DMA方式接收SPI总线数据,并按照协议进行处理

    一.前言 为满足高速数据传输的要求,采用SPI总线.MCU端(STM32F072  Cortex-M0)接收CPU发送的SPI数据(数据18个字节为一包,起始包为0xAA,最后一包为CheckSum校 ...

  7. FPGA 串口中断_一个严谨的STM32串口DMA发送amp;接收(1.5Mbps波特率)机制

    昨天分享的<嵌入式大杂烩读者福利:第一期>大家有去抽奖吗,没抽的可参与抽奖,碰碰运气.我最喜欢抽奖了,还记得前几个月疫情严重时期连抽中了3包口罩,真刺激,哈哈.之后多多安排抽奖,敬请期待. ...

  8. 一个严谨的STM32串口DMA发送接收(1.5Mbps波特率)机制

    文章目录 1 前言 2 串口有必要使用DMA吗 3 实现方式 4 STM32串口使用DMA 5 串口DMA接收 5.1 基本流程 5.2 相关配置 5.3 接收处理 5.3 .1 接收数据大小 5.3 ...

  9. 基于HAL库STM32串口驱动不定长数据接收

    STM32串口驱动不定长数据接收带环形缓冲区 最新框架代码 使用方法 源码 串口接口文件 环形缓冲区接口文件 移植图示 使用涉及4个文件, UART_Port.c UART_Port.h Circul ...

最新文章

  1. python 调用linux命令-四种执行python系统命令的方法
  2. MATLAB信号处理工具箱函数列表分类
  3. java通过jxl处理execl空行_jxl操作Excel导入数据库之空行的处理
  4. split函数python 未定义_python split函数基本用法
  5. python为什么import不了_解决python有时候import不了当前的包问题
  6. Linux命令之diff
  7. 【实习之T100开发】T100 Q查询开发流程
  8. 阿里动物园再添新丁,小蛮驴搞定物流最后三公里!
  9. 5.13 综合案例2.0-火焰检测系统(2.2版本接口有更新)
  10. stm32通过 一线串口通信控制JQ8900-16P语音模块
  11. Weblogic缓存清理
  12. android8.1刷机工具,DX200固件AndroidV8.1 Beta-V2刷机工具包
  13. java实现can通信_[MicroPython]TPYBoard v102 CAN总线通信
  14. 安装显卡驱动显示NVIDIA图形驱动程序安装失败
  15. 在2022年需要使用的25个最佳GOOGLE CHROME扩展
  16. 计算机睡眠和休眠哪个更好,windows7睡眠与休眠的区别_win7电脑休眠和睡眠哪个好...
  17. 网络工程师成长日记424-造假的人
  18. 世界数学难题——哥尼斯堡七桥问题 哥尼斯堡七桥问题
  19. Spring Boot的简单实现
  20. 济宁网络诈骗立案_在节日期间避免网络犯罪和诈骗

热门文章

  1. VUE引入Echarts 打包文件过大——解决方法2:CDN引入
  2. 健身运动燃烧脂肪的三个必要条件
  3. android11.0 Launcher3 高端定制之抽屉列表固定APP显示位置
  4. Dell服务器组建阵列-Raid(无阵列卡)
  5. 2022高处安装、维护、拆除考试模拟100题及模拟考试
  6. 消防应急疏散指示系统在居民住宅区的应用
  7. 还不了解什么是商业智能(BI)?看完这篇文章就懂了
  8. Armijo-Goldstein准则及Wolfe-Powell准则
  9. 面向对象的3个基本对象
  10. 最新WIN10 64位21H2正式版19044.2006