DMA: Direct Memorty Access, 直接存储器访问(控制器);
STM32F407 has 2 DMA;
DMA传输3要素:
1> 传输源:
2> 传输目标:
3> 触发信号
#传输模式:
1> 存储器 -> 存储器;
2> 存储器 -> 外设;
3> 外设 -> 存储器;
DMA
1> DMA框图解析
- DMA控制器2,连接到AHB上,因此可以实现,存储器 to 存储器的连接;
1.1>DMA请求通道选择
外设DMA请求数量多,因此分2个DMA控制器来处理,每个控制器有8X8=64个通道选择;
- DMA2中可以把空白没指定的数据流和通道设置为内存to内存模式;
1.2> 仲裁器(Arbiter)
管理8个输入流,设置优先级;
1.3> FIFO(First In First Out)
1.3.1> FIFO框图
1.3.2> FIFO特性
1> 是存储器;
2> 存储的数据先进先出,类似水管;
3> 无需读写地址,操作简单;
1.3.3> FIFO大小
STM32F407中FIFO大写为:4-word FIFO
处理输入输出宽度不同数据:
字节进,字出(4个字节);
字节进,半字出(2个字节);
半字进,字出;
半字进,字节出;
1.4> 突发增量(burst Size):
一次写进FIFO的字节数:
Single:1Byte;
4 Increment:4Byte;
8 Increment:8Byte;
16 Increme:16Byte;
!注意:选择FIFO的 Threshold;
1.5> 存储器接口:
连接到:Flash,SRAM;
1.6> 存储器接口:
连接到:外设,UART,定时器,SPI控制器等;
1.7> 编程接口:
DMA控制器也是一个器件,Cotrex-M4通过编程接口,配置他;
2> DMA结构体
2.1> 句柄结构体
typedef struct __DMA_HandleTypeDef
{
DMA_Stream_TypeDef *Instance; // DMA外设基地址;
DMA_InitTypeDef Init; // 初始化结构体;
HAL_LockTypeDef Lock; // 资源锁
__IO HAL_DMA_StateTypeDef State; // 传输状态
void *Parent; // 指向外设句柄,这样存储器与外设就链一块了;
/* DMA 传输完成回调函数 */
void (* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma);
/* DMA 传输半完成回调函数 */
void (* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);
/* 存储器1 DMA传输完成回调函数 */
void (* XferM1CpltCallback)( struct __DMA_HandleTypeDef * hdma);
/* 存储器1 DMA传输半完成回调函数 */
void (* XferM1HalfCpltCallback)( struct __DMA_HandleTypeDef * h
/* DMA传输错误回调函数 */
void (* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma);
/* DMA传输取消回调函数 */
void (* XferAbortCallback)( struct __DMA_HandleTypeDef * hdma);
__IO uint32_t ErrorCode; // 错误代码
uint32_t StreamBaseAddress; // DMA数据流基地址
uint32_t StreamIndex; // DMA 数据流编号
}DMA_HandleTypeDef;
2.2> 初始化结构体
typedef struct
{
uint32_t Channel; // 通道选择 通道0~7;
uint32_t Direction; // 传输方向
uint32_t PeriphInc; // 外设地址自增选择
uint32_t MemInc; // 存储器地址自增选择
uint32_t PeriphDataAlignment; // 外设数据宽度
uint32_t MemDataAlignment; // 存储器数据宽度
uint32_t Mode; // 模式选择:单循环,死循环, 外设数据流控制
uint32_t Priority; // 优先级
uint32_t FIFOMode; // FIFO 功能选择
uint32_t FIFOThreshold; // FIFO 阈值
uint32_t MemBurst; // 内存突发
uint32_t PeriphBurst; // 外设突发
}DMA_InitTypeDef;
Direction: 3种传输方向
#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 */
突发是怎么回事?
2.3> DMA结构体
typedef struct
{
__IO uint32_t CR; /*!< DMA stream x configuration register */
__IO uint32_t NDTR; /*!< DMA stream x number of data register */
__IO uint32_t PAR; /*!< DMA stream x peripheral address register */
__IO uint32_t M0AR; /*!< DMA stream x memory 0 address register */
__IO uint32_t M1AR; /*!< DMA stream x memory 1 address register */
__IO uint32_t FCR; /*!< DMA stream x FIFO control register */
} DMA_Stream_TypeDef;
3> 实验_存储器to存储器
实现2个数组拷贝,类似memcpy(void *str1, const void *str2, size_t n)
Step 1> 初始化配置:
/* DMA_Exported_Functions_Group1 Initialization */
HAL_StatusTypeDef HAL_DMA_Init(DMA_HandleTypeDef *hdma);
Step 2> 启动DMA:
#define DMA_SxCR_DBM_Pos (18U)
#define DMA_SxCR_DBM_Msk (0x1UL << DMA_SxCR_DBM_Pos) /*!< 0x00040000 */
#define DMA_SxCR_DBM DMA_SxCR_DBM_Msk
/* 启动DMA */
HAL_DMA_Start(); // 程序员用
|
调用
|
V
DMA_SetConfig(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
{
/* SteP1 : Clear DBM bit */
hdma->Instance->CR &= (uint32_t)(~DMA_SxCR_DBM);
/*Step 2: Configure DMA Stream data length */
hdma->Instance->NDTR = DataLength; // Number of Data Register
/*Step 3: Configure DMA Stream source address */
hdma->Instance->PAR = SrcAddress;
/*Step 4: Configure DMA Stream destination address */
hdma->Instance->M0AR = DstAddress;
}
Step 3> 等待传输完成;为什么需要这个?延时函数替代行不?
#define DMA_FLAG_TCIF0_4 0x00000020U
HAL_DMA_PollForTransfer()
{
mask_cpltlevel = DMA_FLAG_TCIF0_4 << hdma->StreamIndex;
regs = (DMA_Base_Registers *)hdma->StreamBaseAddress;
tmpisr = regs->ISR;
while(((tmpisr & mask_cpltlevel) == RESET) && ((hdma->ErrorCode & HAL_DMA_ERROR_TE) == RESET))
}