STM32F429第二十七篇之DMA实验详解

Posted 海洋想想

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32F429第二十七篇之DMA实验详解相关的知识,希望对你有一定的参考价值。

文章目录

前言

本篇博客主要介绍DMA的HAL库源码解析,会涉及以下两个部分:

  • 正点原子程序整体介绍
  • 重点的结构体介绍

需要注意的是:本文介绍的源代码由正点原子提供,可能会根据个人习惯进行适量调整,与增加注释。

HAL库版本

  • STM32Cube_FW_F4_V1.25.0

结构体

/** 
  * @brief  DMA handle Structure definition
  */
typedef struct __DMA_HandleTypeDef

  DMA_Stream_TypeDef         *Instance;                                                        /*!< Register base address                  */

  DMA_InitTypeDef            Init;                                                             /*!< DMA communication parameters           */ 

  HAL_LockTypeDef            Lock;                                                             /*!< DMA locking object                     */  

  __IO HAL_DMA_StateTypeDef  State;                                                            /*!< DMA transfer state                     */

  void                       *Parent;                                                          /*!< Parent object state                    */ 

  void                       (* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma);         /*!< DMA transfer complete callback         */

  void                       (* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);     /*!< DMA Half transfer complete callback    */

  void                       (* XferM1CpltCallback)( struct __DMA_HandleTypeDef * hdma);       /*!< DMA transfer complete Memory1 callback */
  
  void                       (* XferM1HalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);   /*!< DMA transfer Half complete Memory1 callback */
  
  void                       (* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma);        /*!< DMA transfer error callback            */
  
  void                       (* XferAbortCallback)( struct __DMA_HandleTypeDef * hdma);        /*!< DMA transfer Abort callback            */  

  __IO uint32_t              ErrorCode;                                                        /*!< DMA Error code                          */
  
  uint32_t                   StreamBaseAddress;                                                /*!< DMA Stream Base Address                */

  uint32_t                   StreamIndex;                                                      /*!< DMA Stream Index                       */
 
DMA_HandleTypeDef;

Instance(实例)

用于标明初始化的具体数据流,可以选择的参数如下:

#define DMA1_Stream0        ((DMA_Stream_TypeDef *) DMA1_Stream0_BASE)
#define DMA1_Stream1        ((DMA_Stream_TypeDef *) DMA1_Stream1_BASE)
#define DMA1_Stream2        ((DMA_Stream_TypeDef *) DMA1_Stream2_BASE)
#define DMA1_Stream3        ((DMA_Stream_TypeDef *) DMA1_Stream3_BASE)
#define DMA1_Stream4        ((DMA_Stream_TypeDef *) DMA1_Stream4_BASE)
#define DMA1_Stream5        ((DMA_Stream_TypeDef *) DMA1_Stream5_BASE)
#define DMA1_Stream6        ((DMA_Stream_TypeDef *) DMA1_Stream6_BASE)
#define DMA1_Stream7        ((DMA_Stream_TypeDef *) DMA1_Stream7_BASE)
#define DMA2_Stream0        ((DMA_Stream_TypeDef *) DMA2_Stream0_BASE)
#define DMA2_Stream1        ((DMA_Stream_TypeDef *) DMA2_Stream1_BASE)
#define DMA2_Stream2        ((DMA_Stream_TypeDef *) DMA2_Stream2_BASE)
#define DMA2_Stream3        ((DMA_Stream_TypeDef *) DMA2_Stream3_BASE)
#define DMA2_Stream4        ((DMA_Stream_TypeDef *) DMA2_Stream4_BASE)
#define DMA2_Stream5        ((DMA_Stream_TypeDef *) DMA2_Stream5_BASE)
#define DMA2_Stream6        ((DMA_Stream_TypeDef *) DMA2_Stream6_BASE)
#define DMA2_Stream7        ((DMA_Stream_TypeDef *) DMA2_Stream7_BASE)

Init(初始化结构体)

该成员变量用于配置初始化的参数。该结构体DMA_InitTypeDef下文将会详细介绍。

Lock(锁)

/** 
  * @brief  HAL Lock structures definition  
  */
typedef enum 

  HAL_UNLOCKED = 0x00U,
  HAL_LOCKED   = 0x01U  
 HAL_LockTypeDef;

State(状态)

/** 
  * @brief  HAL DMA State structures definition
  */
typedef enum

  HAL_DMA_STATE_RESET             = 0x00U,  /*!< DMA not yet initialized or disabled */
  HAL_DMA_STATE_READY             = 0x01U,  /*!< DMA initialized and ready for use   */
  HAL_DMA_STATE_BUSY              = 0x02U,  /*!< DMA process is ongoing              */
  HAL_DMA_STATE_TIMEOUT           = 0x03U,  /*!< DMA timeout state                   */
  HAL_DMA_STATE_ERROR             = 0x04U,  /*!< DMA error state                     */
  HAL_DMA_STATE_ABORT             = 0x05U,  /*!< DMA Abort state                     */
HAL_DMA_StateTypeDef;

XferCpltCallback(传输完成回调函数)

该参数为函数指针,当传输完成时,回调该函数。

XferHalfCpltCallback(传输完成一半回调函数)

该参数为函数指针,当数据传输到一半时,回调该函数。

XferM1CpltCallback(内存1传输完成回调函数)

该参数为函数指针,当内存1 的数据传输完成时,回到该函数

XferM1HalfCpltCallback(内存1传输一半回调函数)

该参数为函数指针,当内存1的数据传输完成一半时,回调该函数

XferErrorCallback(错误回调函数)

该参数为函数指针,当出现错误时,回调该函数

XferAbortCallback(中止回调函数)

该参数为函数指针,当中止传输时,回调该函数

ErrorCode(错误代码)

错误代码

StreamBaseAddress(流基地址)

DMA数据流的基地址

StreamIndex(流索引值)

DMA数据流的索引值

DMA_InitTypeDef

/** 
  * @brief  DMA Configuration Structure definition
  */
typedef struct

  uint32_t Channel;              /*!< Specifies the channel used for the specified stream. 
                                      This parameter can be a value of @ref DMA_Channel_selection                    */

  uint32_t Direction;            /*!< Specifies if the data will be transferred from memory to peripheral, 
                                      from memory to memory or from peripheral to memory.
                                      This parameter can be a value of @ref DMA_Data_transfer_direction              */

  uint32_t PeriphInc;            /*!< Specifies whether the Peripheral address register should be incremented or not.
                                      This parameter can be a value of @ref DMA_Peripheral_incremented_mode          */

  uint32_t MemInc;               /*!< Specifies whether the memory address register should be incremented or not.
                                      This parameter can be a value of @ref DMA_Memory_incremented_mode              */

  uint32_t PeriphDataAlignment;  /*!< Specifies the Peripheral data width.
                                      This parameter can be a value of @ref DMA_Peripheral_data_size                 */

  uint32_t MemDataAlignment;     /*!< Specifies the Memory data width.
                                      This parameter can be a value of @ref DMA_Memory_data_size                     */

  uint32_t Mode;                 /*!< Specifies the operation mode of the DMAy Streamx.
                                      This parameter can be a value of @ref DMA_mode
                                      @note The circular buffer mode cannot be used if the memory-to-memory
                                            data transfer is configured on the selected Stream                        */

  uint32_t Priority;             /*!< Specifies the software priority for the DMAy Streamx.
                                      This parameter can be a value of @ref DMA_Priority_level                       */

  uint32_t FIFOMode;             /*!< Specifies if the FIFO mode or Direct mode will be used for the specified stream.
                                      This parameter can be a value of @ref DMA_FIFO_direct_mode
                                      @note The Direct mode (FIFO mode disabled) cannot be used if the 
                                            memory-to-memory data transfer is configured on the selected stream       */

  uint32_t FIFOThreshold;        /*!< Specifies the FIFO threshold level.
                                      This parameter can be a value of @ref DMA_FIFO_threshold_level                  */

  uint32_t MemBurst;             /*!< Specifies the Burst transfer configuration for the memory transfers. 
                                      It specifies the amount of data to be transferred in a single non interruptible
                                      transaction.
                                      This parameter can be a value of @ref DMA_Memory_burst 
                                      @note The burst mode is possible only if the address Increment mode is enabled. */

  uint32_t PeriphBurst;          /*!< Specifies the Burst transfer configuration for the peripheral transfers. 
                                      It specifies the amount of data to be transferred in a single non interruptible 
                                      transaction. 
                                      This parameter can be a value of @ref DMA_Peripheral_burst
                                      @note The burst mode is possible only if the address Increment mode is enabled. */
DMA_InitTypeDef;

Channel(通道)

选择DMA的通道选项

#define DMA_CHANNEL_0                 0x00000000U    /*!< DMA Channel 0 */
#define DMA_CHANNEL_1                 0x02000000U    /*!< DMA Channel 1 */
#define DMA_CHANNEL_2                 0x04000000U    /*!< DMA Channel 2 */
#define DMA_CHANNEL_3                 0x06000000U    /*!< DMA Channel 3 */
#define DMA_CHANNEL_4                 0x08000000U    /*!< DMA Channel 4 */
#define DMA_CHANNEL_5                 0x0A000000U    /*!< DMA Channel 5 */
#define DMA_CHANNEL_6                 0x0C000000U    /*!< DMA Channel 6 */
#define DMA_CHANNEL_7                 0x0E000000U    /*!< DMA Channel 7 */

Direction(方向)

选择DMA的数据传输方向

/** @defgroup DMA_Data_transfer_direction DMA Data transfer direction
  * @brief    DMA data transfer direction 
  * @
  */ 
#define DMA_PERIPH_TO_MEMORY          0x00000000U                 /*!< Peripheral to memory direction */
#define DMA_MEMORY_TO_PERIPH          ((uint32_t)DMA_SxCR_DIR_0)  /*!< Memory to peripheral direction */
#define DMA_MEMORY_TO_MEMORY          ((uint32_t)DMA_SxCR_DIR_1)  /*!< Memory to memory direction     */
/**
  * @
  */

PeriphInc(外设地址自增)

/** @defgroup DMA_Peripheral_incremented_mode DMA Peripheral incremented mode
  * @brief    DMA peripheral incremented mode 
  * @
  */ 
#define DMA_PINC_ENABLE               ((uint32_t)DMA_SxCR_PINC)   /*!< Peripheral increment mode enable  */
#define DMA_PINC_DISABLE              0x00000000U                 /*!< Peripheral increment mode disable */
/**
  * @
  */ 

MemInc(内存地址自增)

/** @defgroup DMA_Memory_incremented_mode DMA Memory incremented mode
  * @brief    DMA memory incremented mode 
  * @
  */ 
#define DMA_MINC_ENABLE               ((uint32_t)DMA_SxCR_MINC)   /*!< Memory increment mode enable  */
#define DMA_MINC_DISABLE              0x00000000U                 /*!< Memory increment mode disable */
/**
  * @
  */

PeriphDataAlignment(外设数据对齐)

用于设定DMA外设数据的宽度

/** @defgroup DMA_Peripheral_data_size DMA Peripheral data size
  * @brief    DMA peripheral data size 
  * @
  */ 
#define DMA_PDATAALIGN_BYTE           0x00000000U                  /*!< Peripheral data alignment: Byte     */
#define DMA_PDATAALIGN_HALFWORD       ((uint32_t)DMA_SxCR_PSIZE_0) /*!< Peripheral data alignment: HalfWord */
#define DMA_PDATAALIGN_WORD           ((uint32_t)DMA_SxCR_PSIZE_1) /*!< Peripheral data alignment: Word     */
/**
  * @
  */ 

MemDataAlignment(内存数据对齐)

/** @defgroup DMA_Memory_data_size DMA Memory data size
  * @brief    DMA memory data size 
  * @ 
  */
#define DMA_MDATAALIGN_BYTE           0x00000000U                  /*!< Memory data alignment: Byte     */
#define DMA_MDATAALIGN_HALFWORD       ((uint32_t)DMA_SxCR_MSIZE_0) /*!< Memory data alignment: HalfWord */
#define DMA_MDATAALIGN_WORD           ((uint32_t)DMA_SxCR_MSIZE_1) /*!< Memory data alignment: Word     */
/**
  * @
  */

Mode(模式)

/** @defgroup DMA_mode DMA mode
  * @brief    DMA mode 
  * @
  */ 
#define DMA_NORMAL                    0x00000000U                  /*!< Normal mode                  */
#define DMA_CIRCULAR                  ((uint32_t)DMA_SxCR_CIRC)    /*!< Circular mode                */
#define DMA_PFCTRL                    ((uint32_t)DMA_SxCR_PFCTRL)  /*!< Peripheral flow control mode */
/**
  * @
  */


Priority(优先级)

/** @defgroup DMA_Priority_level DMA Priority level
  * @brief    DMA priority levels 
  * @
  */
#define DMA_PRIORITY_LOW              0x00000000U                 /*!< Priority level: Low       */
#define DMA_PRIORITY_MEDIUM           ((uint32_t)DMA_SxCR_PL_0)   /*!< Priority level: Medium    */
#define DMA_PRIORITY_HIGH             ((uint32_t)DMA_SxCR_PL_1)   /*!< Priority level: High      */
#define DMA_PRIORITY_VERY_HIGH        ((uint32_t)DMA_SxCR_PL)     /*!< Priority level: Very High */
/**
  * @
  */ 

FIFOMode(FIFO模式)

/** @defgroup DMA_FIFO_direct_mode DMA FIFO direct mode
  * @brief    DMA FIFO direct mode
  * @
  */
#define DMA_FIFOMODE_DISABLE          0x00000000U                 /*!< FIFO mode disable */
#define DMA_FIFOMODE_ENABLE           ((uint32_t)DMA_SxFCR_DMDIS) /*!< FIFO mode enable  */
/**
  * @
  */ 

FIFOThreshold(FIFO阈值)

/** @defgroup DMA_FIFO_threshold_level DMA FIFO threshold level
  * @brief    DMA FIFO level 
  * @
  */
#define DMA_FIFO_THRESHOLD_1QUARTERFULL       0x00000000U                  /*!< FIFO threshold 1 quart full configuration  */
#define DMA_FIFO_THRESHOLD_HALFFULL           ((uint32_t)DMA_SxFCR_FTH_0)  /*!< FIFO threshold half full configuration     */
#define DMA_FIFO_THRESHOLD_3QUARTERSFULL      ((uint32_t)DMA_SxFCR_FTH_1)  /*!< FIFO threshold 3 quarts full configuration */
#define DMA_FIFO_THRESHOLD_FULL               ((uint32_t)DMA_SxFCR_FTH)    /*!< FIFO threshold full configuration          */
/**
  * @
  */ 

MemBurst(内存突发)

/** @defgroup DMA_Memory_burst DMA Memory burst
  * @brief    DMA memory burst 
  * @
  */ 
#define DMA_MBURST_SINGLE             0x00000000U
#define DMA_MBURST_INC4               ((uint32_t)DMA_SxCR_MBURST_0)  
#define DMA_MBURST_INC8               ((uint32_t)DMA_SxCR_MBURST_1)  
#define DMA_MBURST_INC16              ((uint32_t)DMA_SxCR_MBURST)  
/**
  * @
  */ 

PeriphBurst(外设突发)

/** @defgroup DMA_Peripheral_burst DMA Peripheral burst
  * @brief    DMA peripheral burst 
  * @
  */ 
#define DMA_PBURST_SINGLE             0x00000000U
#define DMA_PBURST_INC4               ((uint32_t)DMA_SxCR_PBURST_0)
#define DMA_PBURST_INC8               ((uint32_t)DMA_SxCR_PBURST_1)
#define DMA_PBURST_INC16              ((uint32_t)DMA_SxCR_PBURST)
/**
  * @
  */

源代码

  • main
    • MYDMA_Confi
      • HAL_DMA_Init
    • MYDMA_USART_Transmit
      • HAL_DMA_Start
        • DMA_SetConfig

主函数

int main(void)

    /* 1.变量初始化 */
    u16 i;
    u8 t = 0;
    u8 j, mask = 0;
    float pro = 0;                   //进度

    /* 2.外设初始化 */
    HAL_Init();                      //初始化HAL库
    Stm32_Clock_Init(360, 25, 2, 8); //设置时钟,180Mhz
    delay_init(180);                 //初始化延时函数
    uart_init(115200);               //初始化USART
    LED_Init();                      //初始化LED
    KEY_Init();                      //初始化按键
    SDRAM_Init();                    //初始化SDRAM
    LCD_Init();                      //初始化LCD
    MYDMA_Config(DMA2_Stream7, DMA_CHANNEL_4); //初始化DMA

    /* 3.1 显示提示信息 */
    POINT_COLOR = RED;
    LCD_ShowString(30, 50, 200, 16, 16, "Apollo STM32F4/F7");
    LCD_ShowString(30, 70, 200, 16, 16, "DMA TEST");
    LCD_ShowString(30, 90, 200, 16, 16, "ATOM@ALIENTEK");
    LCD_ShowString(30, 110, 200, 16, 16, "2016/1/24");
    LCD_ShowString(30, 130, 200, 16, 16, "KEY0:Start");

    /* 3.2 填充DMA发送信息  */
    j = sizeof(TEXT_TO_SEND);
    for (i = 0; i < SEND_BUF_SIZE; i++) //填充待发送的数据
    
        if (t >= j) //加入换行符
        
            if (mask)
            
                SendBuff[i] = 0x0a; //换行符
                t = 0;
            
            else
            
                SendBuff[i] = 0x0d; //回车符
                mask++;
            
        
        else //复制TEXT_TO_SEND语句
        
            mask = 0;
            SendBuff[i] = TEXT_TO_SEND[t];
            t++;
        
    

    /* 4.while循环 */
    POINT_COLOR = BLUE; //设置字体为蓝色
    i = 0;
    while (1)
    

        t = KEY_Scan(0);
        if (t == KEY0_PRES) //KEY0按下
        
            /* 将数据发送出去 */
            printf("\\r\\nDMA DATA:\\r\\n");
            LCD_ShowString(30, 150, 200, 16, 16, "Start Transimit....");
            LCD_ShowString(30, 170, 200, 16, 16, "   %"); //显示百分号
            MYDMA_USART_Transmit(&UART1_Handler, (u8 *)SendBuff, SEND_BUF_SIZE); //启动传输

            /* 等待数据发送完成 */
            while (1)
            
                if (__HAL_DMA_GET_FLAG(&UART1TxDMA_Handler, DMA_FLAG_TCIF3_7)) //等待DMA2_Steam7传输完成
                
                    __HAL_DMA_CLEAR_FLAG(&UART1TxDMA_Handler, DMA_FLAG_TCIF3_7); //清除DMA2_Steam7传输完成标志
                    HAL_UART_DMAStop(&UART1_Handler);                            //传输完成以后关闭串口DMA
                    break;
                
                pro = __HAL_DMA_GET_COUNTER(&UART1TxDMA_Handler); //得到当前还剩余多少个数据
                pro = 1 - pro / SEND_BUF_SIZE;                    //得到百分比
                pro *= 100;                                       //扩大100倍
                LCD_ShowNum(30, 170, pro, 3, 16);
            
            LCD_ShowNum(30, 170, 100, 3, 16);                            //显示100%
            LCD_ShowString(30, 150, 200, 16, 16, "Transimit Finished!"); //提示传送完成
        

        /* LED灯闪烁 */
        i++;
        delay_ms(10);
        if (i == 20)
        
            LED0 = !LED0; //提示系统正在运行
            i = 0;
        
    


该主函数大体可以分成以下几个步骤:

  1. 变量初始化
  2. 外设初始化
  3. 显示提示信息
  4. 填充DMA需要发送的数据
  5. while循环

下面重点分析以下while循环内部的内容:

  1. 首先判断按键0是否按下。若按键0按下,则表示开启串口的DMA传输,继续执行以下步骤。
  2. 在oled屏幕上显示提示信息
  3. 启动DMA发送数据
  4. 等待数据传输,并且显示传输进度
  5. 若传输结束,led灯继续闪烁

下面重点介绍几个函数:

MYDMA_Config()
MYDMA_USART_Transmit()
HAL_UART_DMAStop()
  • main
    • MYDMA_Config

    • MYDMA_USART_Transmit

    • HAL_UART_DMAStop

配置函数

MYDMA_Config

/** 
 * @brief DMA通道配置
 * @note 无
 * @param DMA_Stream_TypeDef *DMA_Streamx 数据流
 * @param u32 chx 通道
 * @retval 无
 */
void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx, u32 chx)

    /* 1.RCC使能 */
    if ((u32)DMA_Streamx > (u32)DMA2) //得到当前stream是属于DMA2还是DMA1
    
        __HAL_RCC_DMA2_CLK_ENABLE(); //DMA2时钟使能
    
    else
    
        __HAL_RCC_DMA1_CLK_ENABLE(); //DMA1时钟使能
    

    /* 2.连接DMA与外设 */
    __HAL_LINKDMA(&UART1_Handler, hdmatx, UART1TxDMA_Handler); //将DMA与USART1联系起来(发送DMA)

    /* 3.DMA初始化 */
    UART1TxDMA_Handler.Instance = DMA_Streamx;                         //数据流选择
    UART1TxDMA_Handler.Init.Channel = chx;                             //通道选择
    UART1TxDMA_Handler.Init.Direction = DMA_MEMORY_TO_PERIPH;          //方向:内存到外设
    UART1TxDMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE;              //外设地址固定
    UART1TxDMA_Handler.Init.MemInc = DMA_MINC_ENABLE;                  //内存地址自增
    UART1TxDMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; //外设数据宽度:8位
    UART1TxDMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;    //内存数据宽度:8位
    UART1TxDMA_Handler.Init.Mode = DMA_NORMAL;                         //外设普通模式
    UART1TxDMA_Handler.Init.Priority = DMA_PRIORITY_MEDIUM;            //中等优先级
    UART1TxDMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE;           //FIFO禁用
    UART1TxDMA_Handler.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;   //FIFO阈值满
    UART1TxDMA_Handler.Init.MemBurst = DMA_MBURST_SINGLE;              //存储器突发单次传输
    UART1TxDMA_Handler.Init.PeriphBurst = DMA_PBURST_SINGLE;           //外设突发单次传输
    HAL_DMA_Init(&UART1TxDMA_Handler);

配置函数共分成三个部分:

  1. RCC时钟使能
  2. 连接DMA与外设
  3. DMA初始化

其中,DMA与外设的连接宏定义如下所示:

#define __HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD__, __DMA_HANDLE__)               \\
                        do                                                      \\
                              (__HANDLE__)->__PPP_DMA_FIELD__ = &(__DMA_HANDLE__); \\
                              (__DMA_HANDLE__).Parent = (__HANDLE__);             \\
                           while(0U)

通过定义非常明白,将外设(例如串口)的句柄中的对应成员函数设定为DMA的句柄结构体变量的指针,然后将DMA结构体的Parent成员函数设置为外设的句柄结构体变量。

DMA结构体变量的定义为:

  void                       *Parent;  

是一个空指。

HAL_DMA_Init

/**
  * @brief  Initialize the DMA according to the specified
  *         parameters in the DMA_InitTypeDef and create the associated handle.
  * @param  hdma Pointer to a DMA_HandleTypeDef structure that contains
  *               the configuration information for the specified DMA Stream.  
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_DMA_Init(DMA_HandleTypeDef *hdma)

    /* 1.变量声明 */
    uint32_t tmp = 0U;
    uint32_t tickstart = HAL_GetTick();
    DMA_Base_Registers *regs;

    /* 2.参数检测 */
    /* Check the DMA peripheral state */
    if (hdma STM32F429第二十七篇之DMA

STM32F429第二十七篇之DMA

STM32F429第二十五篇之MCU屏实验详解

STM32F429第二十五篇之MCU屏实验详解

STM32F429第二十八篇之ADC

STM32F429第二十八篇之ADC