5 DMA简介
DMA全称direct memory access,即直接存储器访问。
其主要作用是不占用CPU大量资源,在AMBA AHB总线上的设备之间以硬件方式高速有效地传输数据。
5.1 功能描述
5.1.2 搬运方式
单次数据块搬运:DMA使用单个通道,一次使能将数据从SRC到DST位置搬运一次
成串多数据块搬运:DMA使用单个通道,一次使能按照DMA链表信息依次将数据从SRC到DST位置搬运多次或循环搬运。
其根本区别是有无注册有效的DMA链表。
5.1.4 中断类型
IntErr:错误中断表示DMA传输发生了错误而触发中断,主要包括总线错误、地址没对齐和传输数据宽度没对齐等。
IntAbt:终止传输中断会在终止DMA通道传输时产生。
IntTC:TC中断会在没有产生IntErr和IntAbt的情况下完成一次传输时产生。
5.1.5 数据地址类型
Increment address
Decrement address
Fixed address
如果Increment则DMA从地址由小到大搬运数据,相反的Decrement则由大到小搬运。fixed地址适用于外设FIFO的寄存器搬运数据。
5.2 使用方法
5.3 编程指南
5.3.1 驱动接口
DMA_PrepareMem2Mem:memory到memory搬运标准DMA寄存器配置接口
DMA_PreparePeripheral2Mem:Peripheral到memory搬运标准DMA寄存器配置接口
DMA_PrepareMem2Peripheral:memory到Peripheral搬运标准DMA寄存器配置接口
DMA_PreparePeripheral2Peripheral:Peripheral到Peripheral搬运标准DMA寄存器配置接口
DMA_Reset:DMA复位接口
DMA_GetChannelIntState:DMA通道中断状态获取接口
DMA_ClearChannelIntState:DMA通道清中断接口
DMA_EnableChannel:DMA通道使能接口
DMA_AbortChannel:DMA通道终止接口
5.3.2 代码示例
5.3.2.1 单次搬运
下面以memory到memory单次搬运展示DMA的基本用法:
#define CHANNEL_ID 0
char src[] = "hello world!";
char dst[20];
((aligned (8)));
DMA_Descriptor test __attribute__static uint32_t DMA_cb_isr(void *user_data)
{
uint32_t state = DMA_GetChannelIntState(CHANNEL_ID);
(CHANNEL_ID, state);
DMA_ClearChannelIntState
("dst = %s\n", dst);
printfreturn 0;
}
void DMA_Test(void)
{
();
DMA_Reset(PLATFORM_CB_IRQ_DMA, DMA_cb_isr, 0);
platform_set_irq_callback(&test[0],
DMA_PrepareMem2Mem,
dst, strlen(src),
src, DMA_ADDRESS_INC, 0);
DMA_ADDRESS_INC(CHANNEL_ID, &test);
DMA_EnableChannel}
最终会在DMA中断程序里面将搬运到dst中的“hello world!”字符串打印出来。
5.3.2.2 成串搬运
下面以memory到memory两块数据搬运拼接字符串展示DMA成串搬运的基本用法:
#define CHANNEL_ID 0
char src[] = "hello world!";
char src1[] = "I am ING916.";
char dst[100];
[2] __attribute__((aligned (8)));
DMA_Descriptor teststatic uint32_t DMA_cb_isr(void *user_data)
{
uint32_t state = DMA_GetChannelIntState(CHANNEL_ID);
(CHANNEL_ID, state);
DMA_ClearChannelIntState
("dst = %s\n", dst);
printfreturn 0;
}
void DMA_Test(void)
{
();
DMA_Reset(PLATFORM_CB_IRQ_DMA, DMA_cb_isr, 0);
platform_set_irq_callback[0].Next = &test[1]; // make a DMA link chain
test[1].Next = NULL;
test(&test[0],
DMA_PrepareMem2Mem,
dst, strlen(src),
src, DMA_ADDRESS_INC, 0);
DMA_ADDRESS_INC(&test[1],
DMA_PrepareMem2Mem+ strlen(src),
dst , sizeof(src1),
src1, DMA_ADDRESS_INC, 0);
DMA_ADDRESS_INC(CHANNEL_ID, &test[0]);
DMA_EnableChannel}
最终将会打印出“hello world!I am ING916.”字符串。
5.3.2.3 DMA乒乓搬运
DMA乒乓搬运是一种DMA搬运的特殊用法,其主要应用场景是将外设FIFO中数据循环搬运到memory中并处理。
可实现“搬运”和“数据处理”分离,从而大大提高程序处理数据的效率。
对于大量且连续的数据搬运,如音频,我们推荐选用DMA乒乓搬运的方式。
5.3.2.3.1 DMA乒乓搬运接口
在最新SDK中我们已将DMA乒乓搬运封装成标准接口,方便开发者调用,提高开发效率。
使用时请添加 pingpong.c
文件,并包含 pingpong.h
文件。
DMA_PingPongSetup:DMA乒乓搬运建立接口
DMA_PingPongIntProc:DMA乒乓搬运标准中断处理接口
DMA_PingPongGetTransSize:获取DMA乒乓搬运数据量接口
DMA_PingPongEnable:DMA乒乓搬运使能接口
DMA_PingPongDisable:DMA乒乓搬运去使能接口
更多程序开发者可以参考voice_remote_ctrl例程。
5.3.2.3.2 DMA乒乓搬运示例
下面将以最常见的DMA乒乓搬运I2s数据为例展示DMA乒乓搬运的用法。
I2s的相关配置不在本文的介绍范围内,默认I2s已经配置好,DMA和I2s协商burstSize=8。
#include "pingpong.h"
#define CHANNEL_ID 0
;
DMA_PingPong_t PingPong
static uint32_t DMA_cb_isr(void *user_data)
{
uint32_t state = DMA_GetChannelIntState(CHANNEL_ID);
(CHANNEL_ID, state);
DMA_ClearChannelIntState
// call 'DMA_PingPongIntProc' to get the pointer of data-buff.
uint32_t *rr = DMA_PingPongIntProc(&PingPong, CHANNEL_ID);
uint32_t i = 0;
// call 'DMA_PingPongGetTransSize' to kwon how much data in data-buff.
uint32_t transSize = DMA_PingPongGetTransSize(&PingPong);
while (i < transSize) {
// do something with data 'rr[i]'
++;
i}
return 0;
}
void DMA_Test(void)
{
// call 'DMA_PingPongSetup' to setup ping-pong DMA.
(&PingPong, SYSCTRL_DMA_I2S_RX, 100, 8);
DMA_PingPongSetup(PLATFORM_CB_IRQ_DMA, DMA_cb_isr, 0);
platform_set_irq_callback
// call 'DMA_PingPongEnable' to start ping-pong DMA transmission.
(&PingPong, CHANNEL_ID);
DMA_PingPongEnable(APB_I2S);
I2S_ClearRxFIFO(APB_I2S, 1, 1);
I2S_DMAEnable(APB_I2S, 0, 1); // Enable I2s finally
I2S_Enable}
停止DMA乒乓搬运可以调用以下接口:
void Stop(void)
{
// call 'DMA_PingPongEnable' to disable ping-pong DMA transmission.
(&PingPong, CHANNEL_ID);
DMA_PingPongDisable(APB_I2S, 0, 0);
I2S_Enable(APB_I2S, 0, 0);
I2S_DMAEnable}