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

文章目录

  • 前言
  • 结构体
    • Instance(实例)
    • Init(初始化结构体)
    • Lock(锁)
    • State(状态)
    • XferCpltCallback(传输完成回调函数)
    • XferHalfCpltCallback(传输完成一半回调函数)
    • XferM1CpltCallback(内存1传输完成回调函数)
    • XferM1HalfCpltCallback(内存1传输一半回调函数)
    • XferErrorCallback(错误回调函数)
    • XferAbortCallback(中止回调函数)
    • ErrorCode(错误代码)
    • StreamBaseAddress(流基地址)
    • StreamIndex(流索引值)
  • DMA_InitTypeDef
    • Channel(通道)
    • Direction(方向)
    • PeriphInc(外设地址自增)
    • MemInc(内存地址自增)
    • PeriphDataAlignment(外设数据对齐)
    • MemDataAlignment(内存数据对齐)
    • Mode(模式)
    • Priority(优先级)
    • FIFOMode(FIFO模式)
    • FIFOThreshold(FIFO阈值)
    • MemBurst(内存突发)
    • PeriphBurst(外设突发)
  • 源代码
    • 主函数
  • 配置函数
    • MYDMA_Config
    • HAL_DMA_Init
    • MYDMA_USART_Transmit
    • HAL_DMA_Start
    • DMA_SetConfig

前言

本篇博客主要介绍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 */

STM32F429第二十七篇之DMA实验详解_第1张图片

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     */
/**
  * @}
  */

STM32F429第二十七篇之DMA实验详解_第2张图片

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 */
/**
  * @}
  */ 

STM32F429第二十七篇之DMA实验详解_第3张图片

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 */
/**
  * @}
  */

STM32F429第二十七篇之DMA实验详解_第4张图片

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     */
/**
  * @}
  */ 

STM32F429第二十七篇之DMA实验详解_第5张图片

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     */
/**
  * @}
  */

STM32F429第二十七篇之DMA实验详解_第6张图片

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 */
/**
  * @}
  */

STM32F429第二十七篇之DMA实验详解_第7张图片
STM32F429第二十七篇之DMA实验详解_第8张图片

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 */
/**
  * @}
  */ 

STM32F429第二十七篇之DMA实验详解_第9张图片

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  */
/**
  * @}
  */ 

STM32F429第二十七篇之DMA实验详解_第10张图片

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          */
/**
  * @}
  */ 

STM32F429第二十七篇之DMA实验详解_第11张图片

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)  
/**
  * @}
  */ 

STM32F429第二十七篇之DMA实验详解_第12张图片

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)
/**
  * @}
  */

STM32F429第二十七篇之DMA实验详解_第13张图片

源代码

  • 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 == NULL)
    {
     
        return HAL_ERROR;
    }

    /* Check the parameters */
    assert_param(IS_DMA_STREAM_ALL_INSTANCE(hdma->Instance));
    assert_param(IS_DMA_CHANNEL(hdma->Init.Channel));
    assert_param(IS_DMA_DIRECTION(hdma->Init.Direction));
    assert_param(IS_DMA_PERIPHERAL_INC_STATE(hdma->Init.PeriphInc));
    assert_param(IS_DMA_MEMORY_INC_STATE(hdma->Init.MemInc));
    assert_param(IS_DMA_PERIPHERAL_DATA_SIZE(hdma->Init.PeriphDataAlignment));
    assert_param(IS_DMA_MEMORY_DATA_SIZE(hdma->Init.MemDataAlignment));
    assert_param(IS_DMA_MODE(hdma->Init.Mode));
    assert_param(IS_DMA_PRIORITY(hdma->Init.Priority));
    assert_param(IS_DMA_FIFO_MODE_STATE(hdma->Init.FIFOMode));
    /* Check the memory burst, peripheral burst and FIFO threshold parameters only
     when FIFO mode is enabled */
    if (hdma->Init.FIFOMode != DMA_FIFOMODE_DISABLE)
    {
     
        assert_param(IS_DMA_FIFO_THRESHOLD(hdma->Init.FIFOThreshold));
        assert_param(IS_DMA_MEMORY_BURST(hdma->Init.MemBurst));
        assert_param(IS_DMA_PERIPHERAL_BURST(hdma->Init.PeriphBurst));
    }

    /* Allocate lock resource */
    __HAL_UNLOCK(hdma);

    /* Change DMA peripheral state */
    hdma->State = HAL_DMA_STATE_BUSY;

    /* 3.禁用DMA */
    /* Disable the peripheral */
    __HAL_DMA_DISABLE(hdma);

    /* Check if the DMA Stream is effectively disabled */
    while ((hdma->Instance->CR & DMA_SxCR_EN) != RESET)
    {
     
        /* Check for the Timeout */
        if ((HAL_GetTick() - tickstart) > HAL_TIMEOUT_DMA_ABORT)
        {
     
            /* Update error code */
            hdma->ErrorCode = HAL_DMA_ERROR_TIMEOUT;

            /* Change the DMA state */
            hdma->State = HAL_DMA_STATE_TIMEOUT;

            return HAL_TIMEOUT;
        }
    }

    /* 4.修改CR寄存器 */
    /* Get the CR register value */
    tmp = hdma->Instance->CR;

    /* Clear CHSEL, MBURST, PBURST, PL, MSIZE, PSIZE, MINC, PINC, CIRC, DIR, CT and DBM bits */
    tmp &= ((uint32_t) ~(DMA_SxCR_CHSEL | DMA_SxCR_MBURST | DMA_SxCR_PBURST |
                         DMA_SxCR_PL | DMA_SxCR_MSIZE | DMA_SxCR_PSIZE |
                         DMA_SxCR_MINC | DMA_SxCR_PINC | DMA_SxCR_CIRC |
                         DMA_SxCR_DIR | DMA_SxCR_CT | DMA_SxCR_DBM));

    /* Prepare the DMA Stream configuration */
    tmp |= hdma->Init.Channel | hdma->Init.Direction |
           hdma->Init.PeriphInc | hdma->Init.MemInc |
           hdma->Init.PeriphDataAlignment | hdma->Init.MemDataAlignment |
           hdma->Init.Mode | hdma->Init.Priority;

    /* the Memory burst and peripheral burst are not used when the FIFO is disabled */
    if (hdma->Init.FIFOMode == DMA_FIFOMODE_ENABLE)
    {
     
        /* Get memory burst and peripheral burst */
        tmp |= hdma->Init.MemBurst | hdma->Init.PeriphBurst;
    }

    /* Write to DMA Stream CR register */
    hdma->Instance->CR = tmp;

    /* 5.修改FCR寄存器 */
    /* Get the FCR register value */
    tmp = hdma->Instance->FCR;

    /* Clear Direct mode and FIFO threshold bits */
    tmp &= (uint32_t) ~(DMA_SxFCR_DMDIS | DMA_SxFCR_FTH);

    /* Prepare the DMA Stream FIFO configuration */
    tmp |= hdma->Init.FIFOMode;

    /* The FIFO threshold is not used when the FIFO mode is disabled */
    if (hdma->Init.FIFOMode == DMA_FIFOMODE_ENABLE)
    {
     
        /* Get the FIFO threshold */
        tmp |= hdma->Init.FIFOThreshold;

        /* Check compatibility between FIFO threshold level and size of the memory burst */
        /* for INCR4, INCR8, INCR16 bursts */
        if (hdma->Init.MemBurst != DMA_MBURST_SINGLE)
        {
     
            if (DMA_CheckFifoParam(hdma) != HAL_OK)
            {
     
                /* Update error code */
                hdma->ErrorCode = HAL_DMA_ERROR_PARAM;

                /* Change the DMA state */
                hdma->State = HAL_DMA_STATE_READY;

                return HAL_ERROR;
            }
        }
    }

    /* Write to DMA Stream FCR */
    hdma->Instance->FCR = tmp;

    /* 6.修改IFCR的值 */
    /* Initialize StreamBaseAddress and StreamIndex parameters to be used to calculate
     DMA steam Base Address needed by HAL_DMA_IRQHandler() and HAL_DMA_PollForTransfer() */
    regs = (DMA_Base_Registers *)DMA_CalcBaseAndBitshift(hdma);

    /* Clear all interrupt flags */
    regs->IFCR = 0x3FU << hdma->StreamIndex;

    /* Initialize the error code */
    hdma->ErrorCode = HAL_DMA_ERROR_NONE;

    /* Initialize the DMA state */
    hdma->State = HAL_DMA_STATE_READY;

    return HAL_OK;
}

该函数句柄的状态为:

  1. 将状态变为BUSY,然后再初始化寄存器
  2. 初始化寄存器之后,句柄的状态变为READY。

该函数的锁 在 寄存器参数配置之前进行解锁操作。

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

  1. 变量声明
  2. 参数检测
  3. 禁用DMA
  4. 初始化寄存器的值
  5. 初始化句柄的成员变量

重点关注该程序改变的寄存器,共有以下几个:

  • CR
  • FCR
  • IFCR

在禁用DMA处,HAL采用的方式为先写入值,然后判断自己写入的值是否有效,并且等待一段时间。若在规定的时间内,写入值生效,则继续配置其他选项。若在规定的时间内没有生效,则会返回错误。

在配置CR寄存器中,可以发现:
STM32F429第二十七篇之DMA实验详解_第14张图片
该位在HAL库中没有可以配置的定义,只能通过LL库函数或者直接配置寄存器来改变。

MYDMA_USART_Transmit

/** 
 * @brief 开启一次DMA传输
 * @note 无
 * @param {UART_HandleTypeDef} *huart 串口句柄
 * @param {uint8_t} *pData 传输的数据指针
 * @param {uint16_t} Size 传输的数据量
 * @retval 无
 */
void MYDMA_USART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
     
    huart->hdmatx->State = HAL_DMA_STATE_READY;
    __HAL_UNLOCK(huart->hdmatx);

    HAL_DMA_Start(huart->hdmatx, (u32)pData, (uint32_t)&huart->Instance->DR, Size); //开启DMA传输
    huart->Instance->CR3 |= USART_CR3_DMAT;                                         //使能串口DMA发送
}

该函数由正点原子提供。

前面两句因为版本升级,必须加上,是本人所加。

该函数两个作用:

  1. 启动DMA
  2. 开启串口的DMA功能。

HAL_DMA_Start

/**
  * @brief  Starts the DMA Transfer.
  * @param  hdma       pointer to a DMA_HandleTypeDef structure that contains
  *                     the configuration information for the specified DMA Stream.
  * @param  SrcAddress The source memory Buffer address
  * @param  DstAddress The destination memory Buffer address
  * @param  DataLength The length of data to be transferred from source to destination
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
{
     
    HAL_StatusTypeDef status = HAL_OK;

    /* Check the parameters */
    assert_param(IS_DMA_BUFFER_SIZE(DataLength));

    /* Process locked */
    __HAL_LOCK(hdma);

    if (HAL_DMA_STATE_READY == hdma->State)
    {
     
        /* Change DMA peripheral state */
        hdma->State = HAL_DMA_STATE_BUSY;

        /* Initialize the error code */
        hdma->ErrorCode = HAL_DMA_ERROR_NONE;

        /* Configure the source, destination address and the data length */
        DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength);

        /* Enable the Peripheral */
        __HAL_DMA_ENABLE(hdma);
    }
    else
    {
     
        /* Process unlocked */
        __HAL_UNLOCK(hdma);

        /* Return error status */
        status = HAL_BUSY;
    }
    return status;
}

该函数只有两个语句是我们需要的,为:

        /* Configure the source, destination address and the data length */
        DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength);

        /* Enable the Peripheral */
        __HAL_DMA_ENABLE(hdma);

DMA_SetConfig

/**
  * @brief  Sets the DMA Transfer parameter.
  * @param  hdma       pointer to a DMA_HandleTypeDef structure that contains
  *                     the configuration information for the specified DMA Stream.
  * @param  SrcAddress The source memory Buffer address
  * @param  DstAddress The destination memory Buffer address
  * @param  DataLength The length of data to be transferred from source to destination
  * @retval HAL status
  */
static void DMA_SetConfig(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
{
     
    /* Clear DBM bit */
    hdma->Instance->CR &= (uint32_t)(~DMA_SxCR_DBM);

    /* Configure DMA Stream data length */
    hdma->Instance->NDTR = DataLength;

    /* Memory to Peripheral */
    if ((hdma->Init.Direction) == DMA_MEMORY_TO_PERIPH)
    {
     
        /* Configure DMA Stream destination address */
        hdma->Instance->PAR = DstAddress;

        /* Configure DMA Stream source address */
        hdma->Instance->M0AR = SrcAddress;
    }
    /* Peripheral to Memory */
    else
    {
     
        /* Configure DMA Stream source address */
        hdma->Instance->PAR = SrcAddress;

        /* Configure DMA Stream destination address */
        hdma->Instance->M0AR = DstAddress;
    }
}

该函数用于配置DMA的源地址,目的地址,以及传输数据的长度。

你可能感兴趣的:(ARM,DMA,STM32,F429,正点原子,HAL库)