• 干货---stm32f103之DMA双缓冲__也算我为网络贡献的微薄之力


    思考再三:终究是要拿出一些干货--单片机基础核心代码,串口的高效率使用请这里开始。--举一反三,我只列出串口一的双dma缓冲应用范例,剩下的自己扩展。并给与了我迄今觉得最好的串口配置架构-感谢野火的高质量代码

    #include "sys.h"
    #include "usart.h"     
    #include "string.h"
    #include "stdio.h"
    #include "stdlib.h"
    
    //蓝牙
    //////////////////////////////////////////////////////////////////////////////////      
    uart_find uart1s;//定义串口一的接受处理结构体
    
    //局部定义以便实现模块化
    
    //USART
    #define USART                             USART1
    #define USART_CLK                         RCC_APB2Periph_USART1
    #define USART_CLK_SET                                          RCC_APB2PeriphClockCmd
    
    #define USART_GPIO_CLK_SET                              RCC_APB2PeriphClockCmd
    #define USART_RX_GPIO_PORT                GPIOA
    #define USART_RX_GPIO_CLK                 RCC_APB2Periph_GPIOA                                                                                 
    #define USART_RX_PIN                      GPIO_Pin_10
    #define USART_RX_IRQ                           USART1_IRQn
    #define USART_RX_IRQ_HANDLER              USART1_IRQHandler    //USART3_IRQHandler
    
    #define USART_TX_GPIO_PORT                GPIOA
    #define USART_TX_GPIO_CLK                 RCC_APB2Periph_GPIOA
    #define USART_TX_PIN                      GPIO_Pin_9
    
    
    #define USART_BAUDRATE                    115200
    
    //DMA tx
    #define USART_DR_BASE                    (USART1_BASE+0x04)        // 0x40013800 + 0x04 = 0x40013804,串口数据寄存器地址
    #define SEND_BUFF_SIZE                    128                                        //发送的数据量,SEND_BUFF_SIZE * DMA_MemoryDataSize
    #define USART_TX_DMA_CLK                  RCC_AHBPeriph_DMA1    
    #define USART_TX_DMA_CHANNEL              DMA1_Channel4 
    #define USART_TX_DMA_IRQ                       DMA1_Channel4_IRQn//中断接口
    #define USART_TX_DMA_IRQ_HANDLER               DMA1_Channel4_IRQHandler//中断接口
    #define USART_Tx_DMA_FLAG                                    DMA1_FLAG_GL4
    //错误标志
    #define USART_Tx_ERR_DMA_FLAG                            DMA1_FLAG_GL4
    //DMA rx
    #define USART_DR_BASE                     (USART1_BASE+0x04)        // 0x40013800 + 0x04 = 0x40013804,串口数据寄存器地址
    #define REC_BUFF_SIZE                     128                                    //发送的数据量,SEND_BUFF_SIZE * DMA_MemoryDataSize
    #define USART_RX_DMA_CLK                  RCC_AHBPeriph_DMA1    
    #define USART_RX_DMA_CHANNEL              DMA1_Channel5
    #define USART_RX_DMA_IRQ                           DMA1_Channel5_IRQn//中断接口
    #define USART_RX_DMA_IRQ_HANDLER                   DMA1_Channel5_IRQHandler//中断接口
    #define USART_Rx_DMA_FLAG                                    DMA1_FLAG_GL5 
    //错误标志
    #define USART_Rx_ERR_DMA_FLAG                            DMA1_FLAG_GL5 
    
    static uint8_t SendBuff[SEND_BUFF_SIZE];//发送测试缓冲
    
    static uint8_t rec_by=0;//缓冲位置
    static uint8_t recBuff[REC_BUFF_SIZE];    //接受测试缓冲 临时缓冲
    static uint8_t recBuff1[REC_BUFF_SIZE];    //接受测试缓冲
    static uint8_t recBuff2[REC_BUFF_SIZE];    //接受测试缓冲
    
    static u16 rec_counter=0;
    
    //static uint8_t *send_buff = SendBuff;    //锁定发送地址
    //static uint8_t *rec_buff1 = recBuff1;    //锁定接收地址1
    //static uint8_t *rec_buff2 = recBuff2;    //锁定接收地址2
    
        
    static void uart_dma_send(u8 *buff,u8 len);
    static void USART_DMA_Config(void);
    static void user_heander(void);
    static void USART_Config(void);
    /**********************************
    定义函数接口
    
    ************************************/
    void blue_uart_int(void)
    {
        uart1s.uart_send = uart_dma_send;
        
        uart1s.uart_rxbuf_len = &rec_counter;
        uart1s.uart_init = USART_Config;
        //初始化
        uart1s.uart_init();
        
        uart1s.uart_rxbuf1 = recBuff1;
        uart1s.uart_rxbuf2 = recBuff2;
        
    }
    
    /**
    * @brief  USART GPIO 配置,工作模式配置。115200 8-N-1
    * @param  无
    * @retval 无
    */
    static void USART_Config(void)
    {
        
        GPIO_InitTypeDef GPIO_InitStructure;
      USART_InitTypeDef USART_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
        
        /*配置使用DMA模式*/
        USART_DMA_Config();
      USART_GPIO_CLK_SET( USART_RX_GPIO_CLK|USART_TX_GPIO_CLK, ENABLE);
    
      /* Enable UART clock */
      USART_CLK_SET(USART_CLK, ENABLE);  //时钟将会出现大问题
        USART_DeInit(USART);  //复位串口
      /* Configure USART Tx as alternate function  */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    //复用推挽输出
      GPIO_InitStructure.GPIO_Pin = USART_TX_PIN  ;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(USART_TX_GPIO_PORT, &GPIO_InitStructure);
    
      /* Configure USART Rx as alternate function  */
        GPIO_InitStructure.GPIO_Pin = USART_RX_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
        GPIO_Init(USART_RX_GPIO_PORT, &GPIO_InitStructure);  //
            
        /* Enable the DMA Interrupt */
        NVIC_InitStructure.NVIC_IRQChannel = USART_RX_IRQ;   // 发送DMA通道的中断配置  满中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6;     // 优先级设置
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);            
                
      /* USART mode config */
      USART_InitStructure.USART_BaudRate = USART_BAUDRATE;
      USART_InitStructure.USART_WordLength = USART_WordLength_8b;
      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(USART, &USART_InitStructure); 
        
        USART_Cmd(USART, ENABLE);
    //    USART_ITConfig(USART, USART_IT_RXNE, ENABLE);//开启中断
    //    
        USART_ITConfig(USART, USART_IT_IDLE, ENABLE);  // 开启 串口空闲IDEL 中断    
        /* Enable USARTy DMA TX request */
        USART_ITConfig(USART,USART_IT_TC,DISABLE);        
    //    USART_DMACmd(USART, USART_DMAReq_Tx, ENABLE);  // 开启串口DMA发送  这两个不能随便开启 开启就是发送
        USART_DMACmd(USART, USART_DMAReq_Rx, ENABLE); // 开启串口DMA接收  接受可以提前开启
        USART_DMACmd(USART3, USART_DMAReq_Tx, ENABLE);  // 开启串口DMA发送
    
        printf("串口2初始化
    ");
        
    }
    
    /**
      * @brief  USART1 TX DMA 配置,内存到外设(USART1->DR)
      * @param  无
      * @retval 无
      */
    static DMA_InitTypeDef DMA_InitStructure1;
    static void USART_DMA_Config(void)
    {
            DMA_InitTypeDef DMA_InitStructure;
        
        /*usart1 tx对应dma2,通道4,数据流7*/    
      
            /*开启DMA时钟*/
            RCC_AHBPeriphClockCmd(USART_TX_DMA_CLK, ENABLE);
            DMA_Cmd(USART_TX_DMA_CHANNEL, DISABLE);
        // 关DMA通道
        DMA_DeInit(USART_TX_DMA_CHANNEL);                                 // 恢复缺省值
        DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART->DR);// 设置串口发送数据寄存器
        DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SendBuff;         // 设置发送缓冲区首地址
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                      // 设置外设位目标,内存缓冲区 -> 外设寄存器
        DMA_InitStructure.DMA_BufferSize = uart1s.uart_txbuf_len;                     // 需要发送的字节数,这里其实可以设置为0,因为在实际要发送的时候,会重新设置次值
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // 外设地址不做增加调整,调整不调整是DMA自动实现的
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 // 内存缓冲区地址增加调整
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据宽度8位,1个字节
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         // 内存数据宽度8位,1个字节
        DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           // 单次传输模式
        DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                 // 优先级设置
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                            // 关闭内存到内存的DMA模式
        DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);               // 写入配置
        DMA_ClearFlag(USART_Tx_DMA_FLAG);                                 // 清除DMA所有标志
            DMA_Cmd(USART_TX_DMA_CHANNEL, DISABLE); // 关闭DMA
        DMA_ITConfig(USART_TX_DMA_CHANNEL, DMA_IT_TC, ENABLE);
            //中断配置            
            DMA_ITConfig(USART_TX_DMA_CHANNEL, DMA_IT_TC, ENABLE); 
            
            NVIC_InitTypeDef NVIC_InitStructure;        
            /* Enable the DMA Interrupt */
        NVIC_InitStructure.NVIC_IRQChannel = USART_TX_DMA_IRQ;   // 发送DMA通道的中断配置  满中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6;     // 优先级设置
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);        
             
            /**************************************************************/
        //****************************配置接收  
            /*--- LUMMOD_UART_Rx_DMA_Channel DMA Config ---*/
            //启动DMA时钟  
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);   
        DMA_Cmd(USART_RX_DMA_CHANNEL, DISABLE);                           // 关DMA通道
        DMA_DeInit(USART_RX_DMA_CHANNEL);                                 // 恢复缺省值
        DMA_InitStructure1.DMA_PeripheralBaseAddr = (uint32_t)(&USART->DR);// 设置串口接收数据寄存器
        DMA_InitStructure1.DMA_MemoryBaseAddr = (uint32_t)recBuff1;         // 设置接收缓冲区首地址
        DMA_InitStructure1.DMA_DIR = DMA_DIR_PeripheralSRC;                      // 设置外设为数据源,外设寄存器 -> 内存缓冲区
        DMA_InitStructure1.DMA_BufferSize = *uart1s.uart_rxbuf_len;                     // 需要最大可能接收到的字节数  不能为零
        DMA_InitStructure1.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // 外设地址不做增加调整,调整不调整是DMA自动实现的
        DMA_InitStructure1.DMA_MemoryInc = DMA_MemoryInc_Enable;                 // 内存缓冲区地址增加调整
        DMA_InitStructure1.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据宽度8位,1个字节
        DMA_InitStructure1.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         // 内存数据宽度8位,1个字节
        DMA_InitStructure1.DMA_Mode = DMA_Mode_Normal;                           // 单次传输模式
        DMA_InitStructure1.DMA_Priority = DMA_Priority_VeryHigh;                 // 优先级设置
        DMA_InitStructure1.DMA_M2M = DMA_M2M_Disable;                            // 关闭内存到内存的DMA模式
        DMA_Init(USART_RX_DMA_CHANNEL, &DMA_InitStructure1);               // 写入配置
        DMA_ClearFlag(USART_Rx_DMA_FLAG);                                 // 清除DMA所有标志
        DMA_Cmd(USART_RX_DMA_CHANNEL, ENABLE);                            // 开启接收DMA通道,等待接收数据
            //DMA_ITConfig(LUMMOD_UART_Rx_DMA_Channel, DMA_IT_TC, ENABLE);            // 开启接收完成DMA通道中断
                
            /* Enable the DMA Interrupt */
        NVIC_InitStructure.NVIC_IRQChannel = USART_RX_DMA_IRQ;   // 发送DMA通道的中断配置  满中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6;     // 优先级设置
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);         
    }
    /*********************************
    dma 发送
    ***********************************/
    static void uart_dma_send(u8 *buff,u8 len)
    {
        memset(SendBuff,0,SEND_BUFF_SIZE);
        memcpy(SendBuff,buff,len);            
               
        DMA_SetCurrDataCounter(USART_TX_DMA_CHANNEL,len);          //数据传输量     
        DMA_Cmd(USART_TX_DMA_CHANNEL, ENABLE);                      //开启DMA传输  
        /* USART1 向 DMA发出TX请求 */
        USART_DMACmd(USART, USART_DMAReq_Tx, ENABLE);//开始发送
    }
    
    
    /*********************************************END OF FILE**********************/
    /*下面代码我们直接把中断控制逻辑写在中断服务函数内部。*/
    //DMA传输接收完成中断
    void USART_RX_DMA_IRQ_HANDLER(void)
    {
        
        if(DMA_GetITStatus(USART_Rx_DMA_FLAG) != RESET)
        {
            DMA_ClearITPendingBit(USART_Rx_DMA_FLAG);
            //        Dma_FreeBuf_Ok = 1;//有准备好的数据了
            //数据溢出会出现在这里
        }
        else
        {
            DMA_ClearFlag(USART_Rx_ERR_DMA_FLAG);
        }
    }
    /*********************
    参数说明:
    dma串口通道切换
    **********************/
    static void buf_chn2(void)
    {
        if(rec_by==0)
        {
            rec_by=1;
            DMA_InitStructure1.DMA_MemoryBaseAddr = (uint32_t)recBuff2;         // 设置接收缓冲区首地址
            DMA_Init(USART_RX_DMA_CHANNEL, &DMA_InitStructure1);               // 写入配置
        }
        else
        {
            rec_by=0;
            DMA_InitStructure1.DMA_MemoryBaseAddr = (uint32_t)recBuff1;         // 设置接收缓冲区首地址
            DMA_Init(USART_RX_DMA_CHANNEL, &DMA_InitStructure1);               // 写入配置    
        }
        DMA_Cmd(USART_RX_DMA_CHANNEL, ENABLE);  
    }
    //串口1中断服务程序
    void USART_RX_IRQ_HANDLER(void)                    
    { 
        u8 res;
        if(USART_GetITStatus(USART, USART_IT_RXNE) != RESET)//接收到数据
        {     
            res=USART->DR; //读取就是清空
    //        
    //        while((USART1->SR&0X40)==0);//等待发送结束
    //        USART1->DR=res;                                                          
        }     
        else if(USART_GetITStatus(USART, USART_IT_IDLE) != RESET)//空闲中断
        {
            u8 clear;
            clear=USART->SR;//读SR寄存器
            clear=USART->DR;//读DR寄存器  清空数据
            /*******************************/
            DMA_Cmd(USART_RX_DMA_CHANNEL, DISABLE); // 关闭DMA ,防止干扰 
            DMA_ClearFlag( USART_Rx_ERR_DMA_FLAG );           // 清DMA标志位
            rec_counter = REC_BUFF_SIZE - DMA_GetCurrDataCounter(USART_RX_DMA_CHANNEL); //获得接收到的字节数
        USART_RX_DMA_CHANNEL->CNDTR = REC_BUFF_SIZE;    //  重新赋值计数值,必须大于等于最大可能接收到的数据帧数目    
            //开始切换buff
            buf_chn2();
            //        printf("当前是通道二:%s
    ",recBuff1);
            DMA_Cmd(USART_RX_DMA_CHANNEL, ENABLE); 
            //数据转移
            memset(recBuff,0,REC_BUFF_SIZE);
            if(rec_by==1)
            {
                memcpy(recBuff,recBuff1,REC_BUFF_SIZE);    
                memset(recBuff1,0,REC_BUFF_SIZE);
            }
            else
            {
                memcpy(recBuff,recBuff2,REC_BUFF_SIZE);
                memset(recBuff2,0,REC_BUFF_SIZE);
            }
            user_heander();
            //cc3200_rec();        //some things doing
            /* DMA 开启,等待数据。注意,如果中断发送数据帧的速率很快,MCU来不及处理此次接收到的数据,中断又发来数据的话,这里不能开启,否则数据会被覆盖。有2种方式解决。    
            1. 在重新开启接收DMA通道之前,将LumMod_Rx_Buf缓冲区里面的数据复制到另外一个数组中,然后再开启DMA,然后马上处理复制出来的数据。
        2. 建立双缓冲,在LumMod_Uart_DMA_Rx_Data函数中,重新配置DMA_MemoryBaseAddr 的缓冲区地址,那么下次接收到的数据就会保存到新的缓冲区中,不至于被覆盖。*/
            //注意双缓冲:只需要重新配置,数据地址即可        
        }
    } 
    //dma2 7 tx  发送完成中断实验
    void USART_TX_DMA_IRQ_HANDLER(void)                    
    { 
        if(DMA_GetITStatus(USART_Tx_DMA_FLAG)!=RESET)
        {        
                DMA_ClearFlag(USART_Tx_DMA_FLAG);         // 清除标志
                DMA_Cmd(USART_TX_DMA_CHANNEL, DISABLE);   // 关闭DMA通道
        }        
            else
            {
                DMA_ClearFlag(USART_Tx_ERR_DMA_FLAG);         // 清除所有错误标志
            }
    } 
    /********************************
    用户处理程序
    **********************************/
    static void user_heander(void)
    {
        // your code 
        //rest 
        printf("your printf : %s",recBuff);
    }
    #ifndef __USART_H
    #define __USART_H
    #include "stdio.h"    
    #include "sys.h" 
    
    
    typedef struct
    {
        uint8_t *    uart_rxbuf1;//接收缓存1    
        uint8_t *    uart_rxbuf2;//接收缓存1    
        
        uint8_t *    uart_rxbuf;//接收缓存
    
        
        u16 *uart_rxbuf_len;
        //uint8_t rxbuf_counter;//接收位置
        
        uint8_t *uart_txbuf;//接收缓存    
        uint8_t txbuf_counter;//接收位置    
        u16 uart_txbuf_len;
    
        u8 buff_by;//双缓冲标志
        
        uint8_t recby;//接收标志      
        uint8_t clear;//清除标志
        
        void (* uart_init)(void);  //初始化函数
        void (* uart_send)(u8 *buff,u8 len);  //发送函数
    }uart_find;
    
    
    
    extern uart_find uart1s;//定义串口一的接受处理结构体
    ////////////////////uart2
    extern uart_find uart2s;//定义串口一的接受处理结构体
    ///////////////////uart3//////////////////////////////
    extern uart_find uart3s;//定义串口一的接受处理结构体
    
    
    void uart_int(void);
    
    void wifi_uart_int(void);
    void debug_uart_int(void);
    void blue_uart_int(void);
    
    //void USART_Config_test(void);
    
    u8 String_match(u8 *str_aim,u8 *str_buf,u8 len,u8 len1);//数据匹配函数
    u8 String_collect(u8 * aim_buf,u8 *buff,u8 start);
    #endif
  • 相关阅读:
    ok6410驱动usb摄像头
    自己动手写CPU之第五阶段(1)——流水线数据相关问题
    ListView嵌套ListView时发生:View too large to fit into drawing cache的问题
    算法导论 第8章 线性时间排序(计数排序、基数排序、桶排序)
    Android_通过ContentObserver监听短信数据变化
    【MyEcplise】导入项目报错:Errors running builder 'JavaScript Validator' on project '项目名'. java.lang.ClassCastException
    【js】js中const,var,let区别
    【Node.js】2.开发Node.js选择哪个IDE 开发工具呢
    【Node.js】1.安装步骤
    【POI】对于POI无法处理超大xls等文件,官方解决方法【已解决】【多线程提升速率待定】
  • 原文地址:https://www.cnblogs.com/polar-lights/p/9211560.html
Copyright © 2020-2023  润新知