zoukankan      html  css  js  c++  java
  • STM32使用串口1配合DMA接收不定长数据,减轻CPU载荷

    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 ;//抢占优先级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
    ",Usart1_Rec_Cnt);
              printf ("The data:
    ");
              Usart1_Send(DMA_Rece_Buf,Usart1_Rec_Cnt);
             printf ("
    Over! 
    ");
            //*************************************//
             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
    }

  • 相关阅读:
    思考未来:你的目标是什么
    从非同凡响开始:绝不要做他人也在做的事
    Elasticsearch的内置分词器
    Elasticsearch倒排索引的核心组成
    Session 与 JWT
    vue全屏组件
    css弹性盒骰子
    es6模块化
    移动端适配
    echarts-3D地图
  • 原文地址:https://www.cnblogs.com/mylinux/p/5374134.html
Copyright © 2011-2022 走看看