最近经常看见坛友在论坛上问串口接收的问题,我之前刚好由于项目需要用到PLC的PPI协议,需要不停地利用串口接收数据,一开始的时候采用单字节中断的方式接收判断。但是用来做通信的时候需要不停的产生串口接收中断,会严重影响主程序的运行。后来采用DMA接收的方式,但是一般情况下配置的DMA都是接定长的串口数据,对于未知长度的串口数据接收并不适用。后来在网上发现了一种方法可以利用串口的 空闲中断+DMA接收 的方法完美解决此类问题,特别适用于不需要每个就收字节都判断的串口数据接收,下面说一下事实现思路和程序。

实现思路: 采用STM32F103的串口1,并配置成空闲中断模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这帧数据长度是100个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用 DMA_GetCurrDataCounter();函数计算出本次的数据接受长度,从而进行数据处理。

应用对象:适用于各种串口相关的通信协议,如:MODBUS,PPI ;还有类似于GPS数据接收解析,串口WIFI的数据接收等,都是很好的应用对象。

关键代码分析:
usart.H
#ifndef __USART_H
#define __USART_H
#include "stdio.h"
#include "sys.h"

#define DMA_Rec_Len 200      //定义一个长度为200个字节的数据缓冲区。(建议定义的长度比你可能接收到的最长单帧数据长度长!)

void uart_init(u32 bound);
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx);

#endif

usart.C
//初始化IO 串口1 
//bound:波特率
void uart_init(u32 bound)
{
    //GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能USART2时钟

USART_DeInit(USART1);  //复位串口1
   //USART1_TX   PA.9
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9
   
    //USART1_RX  A.10
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);  //初始化PA10

//Usart1 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
  
   //USART 初始化设置
  USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
  USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
  USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式

USART_Init(USART1, &USART_InitStructure); //初始化串口
    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启空闲中断
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);   //使能串口1 DMA接收
    USART_Cmd(USART1, ENABLE);                    //使能串口 
 
    //相应的DMA配置
  DMA_DeInit(DMA1_Channel5);   //将DMA的通道5寄存器重设为缺省值  串口1对应的是DMA通道5
  DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;  //DMA外设ADC基地址
  DMA_InitStructure.DMA_MemoryBaseAddr = (u32)DMA_Rece_Buf;  //DMA内存基地址
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //数据传输方向,从外设读取发送到内存
  DMA_InitStructure.DMA_BufferSize = DMA_Rec_Len;  //DMA通道的DMA缓存的大小
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常缓存模式
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
  DMA_Init(DMA1_Channel5, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道

DMA_Cmd(DMA1_Channel5, ENABLE);  //正式驱动DMA传输
}

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

if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
      {
          USART_ReceiveData(USART1);//读取数据 注意:这句必须要,否则不能够清除中断标志位。
          Usart1_Rec_Cnt = DMA_Rec_Len-DMA_GetCurrDataCounter(DMA1_Channel5); //算出接本帧数据长度
   
         //***********帧数据处理函数************//
          printf ("The lenght:%d\r\n",Usart1_Rec_Cnt);
          printf ("The data:\r\n");
          Usart1_Send(DMA_Rece_Buf,Usart1_Rec_Cnt);
         printf ("\r\nOver! \r\n");
        //*************************************//
         USART_ClearITPendingBit(USART1, USART_IT_IDLE);         //清除中断标志
         MYDMA_Enable(DMA1_Channel5);                   //恢复DMA指针,等待下一次的接收
     }

}

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

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

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

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

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

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

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

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

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

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

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

  6. MM32F3277空闲中断+DMA接收不定长数据

    摘要:在实际项目中经常用到串口接收一些不定长的数据,怎么判断这一帧数据接收完成了呢?通常使用UART非空中断配合简单的数据协议,在数据中加入帧头.帧尾,在程序中判断是否接收到帧尾来确定数据接收完毕,对 ...

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

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

  8. STM32使用串口IDLE中断的两种接收不定长数据的方式

    现在有很多数据处理都要用到不定长数据,而单片机串口的RXNE中断一次只能接收一个字节的数据,没有缓冲区,无法接收一帧多个数据,现提供两种利用串口IDLE空闲中断的方式接收一帧数据,方法如下: 方法1: ...

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

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

最新文章

  1. 第三章 三层交换配置DHCP服务器
  2. tkinter笔记:通过点击button 控制标签的显示 (莫烦python笔记)
  3. 一个古帝国做产品的故事
  4. 练习5 键盘输入一个年份 判断年份 是否为闰年
  5. iOS_CNBlog项目开发 (基于博客园api开发) 上篇
  6. Spring Boot的启动器Starter详解
  7. 后台权限html,cms后台权限架构.html
  8. php与plc,plc编程语言有哪几种
  9. 嵌入式Linux小项目之图片编解码播放器(5)
  10. 学而优则仕:中国古代政治原生态(转自 百度 读书吧)
  11. 4000倍的资本效率提升,Uniswap V3 将如何实现?
  12. 命令行工具 DOT和DOH测试
  13. 为什么要处理子线程中的异常?不处理可以吗?那该如何处理?
  14. 了解常见的模拟器及交换机的基本配置
  15. python 散点图点击链接图片_Python散点图。 标记的大小和样式
  16. 2021年低压电工考试报名及低压电工试题及解析
  17. 用 Java 实现贪吃蛇小游戏
  18. 10qbt超导量子计算机,南京大学于扬、朱诗亮团队在超导量子比特中实现参数空间的新型磁单极...
  19. JAD反编译tricks
  20. Android应用发送短信的实现

热门文章

  1. python 高阶函数作业(3.16)
  2. 2021年跨境品牌集体出海,2020年跨境电商数据报告分享
  3. SAT数学考试需要准备的物品
  4. 移动端点击出现蓝色框的解决方案
  5. “利空”砸盘?中本聪:这个锅我不背
  6. LT9211产品概述
  7. 题目描述:设有一头小母牛,从出生第四年起每年生一头小母牛,按此规律,第N年时有几头母牛?
  8. Docker报错Error spawning command line “dbus-launch --autolaunch=xxx --binary-syntax --close-stderr”
  9. 【leetcode】966. Vowel Spellchecker
  10. GEA 1.6 运行时引擎架构