本篇博客主要介绍DMA的HAL库源码解析,会涉及以下两个部分:
需要注意的是:本文介绍的源代码由正点原子提供,可能会根据个人习惯进行适量调整,与增加注释。
HAL库版本
/**
* @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;
用于标明初始化的具体数据流,可以选择的参数如下:
#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)
该成员变量用于配置初始化的参数。该结构体DMA_InitTypeDef
下文将会详细介绍。
/**
* @brief HAL Lock structures definition
*/
typedef enum
{
HAL_UNLOCKED = 0x00U,
HAL_LOCKED = 0x01U
} HAL_LockTypeDef;
/**
* @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;
该参数为函数指针,当传输完成时,回调该函数。
该参数为函数指针,当数据传输到一半时,回调该函数。
该参数为函数指针,当内存1 的数据传输完成时,回到该函数
该参数为函数指针,当内存1的数据传输完成一半时,回调该函数
该参数为函数指针,当出现错误时,回调该函数
该参数为函数指针,当中止传输时,回调该函数
错误代码
DMA数据流的基地址
DMA数据流的索引值
/**
* @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;
选择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 */
选择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 */
/**
* @}
*/
/** @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 */
/**
* @}
*/
/** @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 */
/**
* @}
*/
用于设定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 */
/**
* @}
*/
/** @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 */
/**
* @}
*/
/** @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 */
/**
* @}
*/
/** @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 */
/**
* @}
*/
/** @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 */
/**
* @}
*/
/** @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 */
/**
* @}
*/
/** @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)
/**
* @}
*/
/** @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)
/**
* @}
*/
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;
}
}
}
该主函数大体可以分成以下几个步骤:
下面重点分析以下while循环内部的内容:
下面重点介绍几个函数:
MYDMA_Config()
MYDMA_USART_Transmit()
HAL_UART_DMAStop()
MYDMA_Config
MYDMA_USART_Transmit
HAL_UART_DMAStop
/**
* @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);
}
配置函数共分成三个部分:
其中,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;
是一个空指。
/**
* @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;
}
该函数句柄的状态为:
该函数的锁 在 寄存器参数配置之前进行解锁操作。
该函数可以大致分成以下几个步骤:
重点关注该程序改变的寄存器,共有以下几个:
在禁用DMA处,HAL采用的方式为先写入值,然后判断自己写入的值是否有效,并且等待一段时间。若在规定的时间内,写入值生效,则继续配置其他选项。若在规定的时间内没有生效,则会返回错误。
在配置CR寄存器中,可以发现:
该位在HAL库中没有可以配置的定义,只能通过LL库函数或者直接配置寄存器来改变。
/**
* @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发送
}
该函数由正点原子提供。
前面两句因为版本升级,必须加上,是本人所加。
该函数两个作用:
/**
* @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);
/**
* @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的源地址,目的地址,以及传输数据的长度。