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
- HAL_DMA_Start
- MYDMA_Confi
主函数
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;
}
}
}
该主函数大体可以分成以下几个步骤:
- 变量初始化
- 外设初始化
- 显示提示信息
- 填充DMA需要发送的数据
- while循环
下面重点分析以下while循环内部的内容:
- 首先判断按键0是否按下。若按键0按下,则表示开启串口的DMA传输,继续执行以下步骤。
- 在oled屏幕上显示提示信息
- 启动DMA发送数据
- 等待数据传输,并且显示传输进度
- 若传输结束,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);
}
配置函数共分成三个部分:
- RCC时钟使能
- 连接DMA与外设
- 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;
STM32F429第二十七篇之DMA