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

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

关键代码分析:
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
NVIC_InitStructure.NVIC_IRQChannelSubPriority = ; //子优先级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指针,等待下一次的接收
} }

这种方式和传统的uart接收中断里面处理数据(解析协议的比较):

//普通方式
uart_rcv_irq()
{
    DISABLE_UARTX
    
    if G_Counter >= MAXLEN
        clear buffer and counter;
    else
        G_Buffer[G_Counter]= GetData(UARTX);
        G_Counter++;

if OK==unpack(G_Buffer,G_Counter)
            clear buffer and counter;
            set flag;
            
    ENABLE_UARTX
}

//idle中断 + dma方式
G_Buffer
G_Counter
G_DMARcvBuffer
uart_idle_irq()
{
    G_Counter += MAXLEN - DMAGetCurDataCounter(DMAx);
    copy G_DMARcvBuffer to G_Buffer;
    if OK==unpack(G_Buffer,G_Counter)
        clear buffers and counter;
        set flag;
            
    USART_ClearITPendingBit(USARTx, USART_IT_IDLE);
}

DMAx_OVERFLOW_IRQHandler()
{
    G_Counter += MAXLEN - DMAGetCurDataCounter(DMAx);
    copy G_DMARcvBuffer to G_Buffer;
    if OK==unpack(G_Buffer,G_Counter)
        clear buffers and counter;
        set flag;
    
    RESET DMA
}

STM32使用串口1配合DMA接收不定长数据,减轻CPU载荷的更多相关文章

  1. 串口1配合DMA接收不定长数据(空闲中断+DMA接收)

    1.空闲中断和别的接收完成(一个字节)中断,发送完成(发送寄存器控)中断的一样是串口中断: 2.空闲中断是接收到一个数据以后,接收停顿超过一字节时间  认为桢收完,总线空闲中断是在检测到在接收数据后, ...

  2. 串口配合DMA接收不定长数据(空闲中断+DMA接收)-(转载)

    1.空闲中断和别的接收完成(一个字节)中断,发送完成(发送寄存器控)中断的一样是串口中断: 2.空闲中断是接收到一个数据以后,接收停顿超过一字节时间  认为桢收完,总线空闲中断是在检测到在接收数据后, ...

  3. STM32之串口DMA接收不定长数据

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

  4. STM32 HAL库使用中断实现串口接收不定长数据

    以前用DMA实现接收不定长数据,DMA的方法接收串口助手的数据,全部没问题,不过如果接收模块返回的数据,而这些数据如果包含回车换行的话就会停止接收,例如接收:AT\r\nOK\r\n,就只能接收到AT ...

  5. 关于socket客户端接收不定长数据的解决方案

    #!/usr/bin/env python3.5 # -*-coding:utf8-*- """ 本实例客户端用于不断接收不定长数据,存储到变量res "&qu ...

  6. STM32串口接收不定长数据原理与源程序(转)

    今天说一下STM32单片机的接收不定长度字节数据的方法.由于STM32单片机带IDLE中断,所以利用这个中断,可以接收不定长字节的数据,由于STM32属于ARM单片机,所以这篇文章的方法也适合其他的A ...

  7. Python3的tcp socket接收不定长数据包接收到的数据不全。

    Python Socket API参考出处:http://blog.csdn.net/xiangpingli/article/details/47706707 使用socket.recv(pack_l ...

  8. STM32 ~ USART接收不定长数据

    IDLE中断什么时候发生? IDLE就是串口收到一帧数据后,发生的中断.什么是一帧数据呢?比如说给单片机一次发来1个字节,或者一次发来8个字节,这些一次发来的数据,就称为一帧数据,也可以叫做一包数据. ...

  9. Stm32使用串口空闲中断,基于队列来接收不定长、不定时数据

    串口持续地接收不定长.不定时的数据,把每一帧数据缓存下来且灵活地利用内存空间,下面提供一种方式供参考.原理是利用串口空闲中断和DMA,每当对方发来一帧完整的数据后,串口接收开始空闲,触发中断,在中断处 ...

随机推荐

  1. 如何隐藏DLL中,导出函数的名称?(转)

    如何隐藏DLL中,导出函数的名称?(转)   一.引言 很多时候,我们写了一个Dll,不希望别人通过DLL查看工具,看到我们的导出函数名称.可以通过以下步骤实现: 1. 在def函数中做如下定义: L ...

  2. html5 geolocation API

    清单 1. 检查浏览器支持性if (navigator.geolocation) 清单 2. 单次定位请求 API void getCurrentPosition(updateLocation, op ...

  3. pprint模块解析

    pprint模块提供了打印任意python数据结构的方法,如果这个数据结构不是python的基本类型,则可能无法打印结果.         pprint模块定义了一个类:         pprint ...

  4. js表格排序 & 去除字符串空格

    // a:列数 bool:排序升序判断参数 true false Str:支持多列 function newUnitSort(a, bool, str) { var oTable = document ...

  5. libcprops

    Install Howto Download the latest epel-release rpm from http://dl.fedoraproject.org/pub/epel/6/x86_6 ...

  6. 国内BI工具/报表工具厂商简介

    v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VM ...

  7. 用JS画斐波那契螺旋线(黄金螺旋线)

    偶然看到斐波那契螺旋线(黄金螺旋线)的定义及画图方法,试着用JS画了一下,很漂亮,很好玩 具体定义及画法大家查一下就有了,很简单. 以下是代码: <!DOCTYPE html> <h ...

  8. oracle命令大全

    内容包括三大项:    1.oracle基本操作语句 2.SQLServer基本操作语句 3.各种数据库连接方法 ******************************************* ...

  9. 《TCP/IP详细说明》读书笔记(17章)-TCP传输控制协定

    1.TCP的服务 在一个TCP连接中.仅有双方进行彼此通信. TCP通过下列方式来提供可靠性: 1)应用数据被切割成TCP觉得最适合发送的数据块. 这和UDP全然不同,应用程序产生的数据报长度保持不变 ...

  10. sql 和 nosql 说明

    在传统的数据库中, 数据库的格式是由表(table).行(row).字段(field)组成的.表有固定的结构,规定了每行有哪些字段,在创建时被定义,之后修改很困难.行的格式是相同的,由若干个固定的字段 ...