干貨---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初始化\r\n");
    
}

/**
  * @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\r\n",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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM