基础实验例程-3.9 直接内存存取控制器DMAC
一、实验目的
本节课主要学习K210的直接内存存取控制器DMAC的功能。
二、实验准备
1.实验元件
K210芯片的直接内存存取控制器DMAC功能
2.元件特性
直接存储访问 (Direct Memory Access, DMA) 用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何 CPU 操作的情况下通过 DMA 快速移动数据,从而提高了CPU 的效率。
DMA模块具有以下功能:
自动选择一路空闲的DMA通道用于传输,
根据源地址和目标地址自动选择软件或硬件握手协议,
支持 1、2、4、8 字节的元素大小,源和目标大小不必一致,
异步或同步传输功能,
循环传输功能,常用于刷新屏幕或音频录放等场景。
3.SDK中对应API功能
对应的头文件 dmac.h
• dmac_init:初始化DMA
• dmac_set_single_mode:设置单路DMA参数
• dmac_is_done:用于DMAC 启动后判断是否完成传输。用于DMAC 启动传输后,如果在启动前判断会不准确。
• dmac_wait_done:等待DMA 完成工作
• dmac_set_irq:设置DMAC 中断的回调函数
• dmac_set_src_dest_length:设置DMAC 的源地址、目的地址和长度,然后启动DMAC 传输。如果src 为NULL 则不设置源地址,dest 为NULL 则不设置目的地址,len<=0 则不设置长度。
该函数一般用于DMAC 中断中,使DMA 继续传输数据,而不必再次设置DMAC 的所有参数以节省时间。
• dmac_is_idle:判断DMAC 当前通道是否空闲,该函数在传输前和传输后都可以用来判断DMAC 状态。
• dmac_wait_idle:等待DMAC 进入空闲状态。
三、实验原理
DMA 传输将数据从一个地址空间复制到另外一个地址空间。当CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。
在实现DMA传输时,是由DMA控制器直接掌管总线,因此,存在着一个总线控制权转移问题。即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。
请求:CPU对DMA控制器初始化,并向I/O接口发出操作命令,I/O接口提出DMA请求。
响应:DMA控制器对DMA请求判别优先级及屏蔽,向总线裁决逻辑提出总线请求。当CPU执行完当前总线周期即可释放总线控制权。此时,总线裁决逻辑输出总线应答,表示DMA已经响应,通过DMA控制器通知I/O接口开始DMA传输。
传输:DMA控制器获得总线控制权后,CPU即刻挂起或只执行内部操作,由DMA控制器输出读写命令,直接控制RAM与I/O接口进行DMA传输。
在DMA控制器的控制下,在存储器和外部设备之间直接进行数据传送,在传送过程中不需要中央处理器的参与。开始时需提供要传送的数据的起始位置和数据长度。
结束:当完成规定的成批数据传送后,DMA控制器即释放总线控制权,并向I/O接口发出结束信号。当I/O接口收到结束信号后,一方面停止I/O设备的工作,另一方面向CPU提出中断请求,使CPU从不介入的状态解脱,并执行一段检查本次DMA传输操作正确性的代码。最后,带着本次操作结果及状态继续执行原来的程序。
由此可见,DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM与I/O设备开辟一条直接传送数据的通路,使CPU的效率大为提高。
四、实验过程
1.首先初始化K210的硬件引脚和软件功能使用的是FPIOA映射关系。
2. 在使用RGB灯前需要初始化,也就是把RGB灯的软件GPIO设置为输出模式。
3.然后关闭RGB灯,同样是设置RGB灯的GPIO为高电平则可以让RGB灯熄灭。
4.初始化串口3,并且设置串口串口波特率为115200,然后通过dma通道0发送“hello yahboom!”的欢迎语。
5.接下来是dma通道1读取串口的数据,并且经过协议过滤的过程,通过设置rec_flag的值,从而让数据必须是16进制的FFAA开头才可以进行解析,解析后,串口把接收到的真正数据通过dma通道0发出。
6.以下是串口通过DMA发送和接收数据的函数的源码,SDK已经包含了这两个函数,直接调用即可。
7.解析真正的数据,根据数据的不同数值,修改RGB灯的颜色和关闭状态。如果是0x11则亮红灯,0x22则红灯灭;0x33则亮绿灯,0x44则绿灯灭;0x55则亮蓝灯,0x66则蓝灯灭;
8.编译调试,烧录运行
把本课程资料中的dmac复制到SDK中的src目录下,然后进入build目录,运行以下命令编译。
cmake .. -DPROJ=dmac -G “MinGW Makefiles”
make
编译完成后,在build文件夹下会生成dmac.bin文件。
使用type-C数据线连接电脑与K210开发板,打开kflash,选择对应的设备,再将程序固件烧录到K210开发板上。
五、实验现象
烧录完成固件后,系统会弹出一个终端界面,如果没有弹出终端界面的可以打开串口助手显示调试内容。
打开电脑的串口助手,选择对应的K210开发板对应的串口号,波特率设置为115200,然后点击打开串口助手。注意还需要设置一下串口助手的DTR和RTS。在串口助手底部此时的4.DTR和7.RTS默认是红色的,点击4.DTR和7.RTS,都设置为绿色,然后按一下K210开发板的复位键。
由于通讯里使用的是uint8_t类型数据,所以我们需要把串口助手的发送和接收设置改为HEX,如果没有选择HEX类型的话,K210芯片会认为数据不正确而不进行解析数据。
在串口助手输入FF AA 11,红灯亮,FF AA 22,红灯熄灭;输入FF AA 33,绿灯亮,FF AA 44,绿灯熄灭;输入FF AA 55,蓝灯亮,FF AA 66,蓝灯熄灭。
然后点击发送就可以看到效果了。
六、实验总结
1.直接内存存取控制器DMAC需要搭配其他的设备,如串口、I2C或者I2S通讯来使用。
2.DMAC是可以提高CPU效率,直接通过DMA在设备和内存之间传输数据,而CPU只需要启动dma传输就可以,等待完成即可。
附:API对应的头文件 dmac.h
dmac_init
描述
初始化DMA。
函数原型
void dmac_init(void)
参数
无。
返回值
无。
dmac_set_single_mode
描述
设置单路DMA参数。
函数原型
void dmac_set_single_mode(dmac_channel_number_t channel_num, const void *src, void *dest, dmac_address_increment_t src_inc, dmac_address_increment_t dest_inc, dmac_burst_trans_length_t dmac_burst_size, dmac_transfer_width_t dmac_trans_width, size_t block_size)
参数
参数名称 | 描述 | 输入输出 |
channel_num | DMA 通道号 | 输入 |
src | 源地址 | 输入 |
dest | 目标地址 | 输出 |
src_inc | 源地址是否自增 | 输入 |
dest_inc | 目标地址是否自增 | 输入 |
dmac_burst_size | 突发传输数量 | 输入 |
dmac_trans_width | 单次传输数据位宽 | 输入 |
block_size | 传输数据的个数 | 输入 |
返回值
无。
dmac_is_done
描述
用于DMAC启动后判断是否完成传输。用于DMAC启动传输后,如果在启动前判断会不准确。
函数原型
int dmac_is_done(dmac_channel_number_t channel_num)
参数
参数名称 | 描述 | 输入输出 |
channel_num | DMA 通道号 | 输入 |
返回值
返回值 | 描述 |
0 | 未完成 |
1 | 已完成 |
dmac_wait_done
描述
等待DMA完成工作。
函数原型
void dmac_wait_done(dmac_channel_number_t channel_num)
参数
参数名称 | 描述 | 输入输出 |
channel_num | DMA 通道号 | 输入 |
返回值
无。
dmac_set_irq
描述
设置DMAC中断的回调函数
函数原型
void dmac_set_irq(dmac_channel_number_t channel_num , plic_irq_callback_t dmac_callback, void *ctx, uint32_t priority)
参数
参数名称 | 描述 | 输入输出 |
channel_num | DMA 通道号 | 输入 |
dmac_callback | 中断回调函数 | 输入 |
ctx | 回调函数的参数 | 输入 |
priority | 中断优先级 | 输入 |
返回值
无。
dmac_set_src_dest_length
描述
设置DMAC的源地址、目的地址和长度,然后启动DMAC传输。如果src为NULL则不设置源地址,dest为NULL则不设置目的地址,len<=0则不设置长度。
该函数一般用于DMAC中断中,使DMA继续传输数据,而不必再次设置DMAC的所有参数以节省时间。
函数原型
void dmac_set_src_dest_length(dmac_channel_number_t channel_num, const void *src, void *dest, size_t len)
参数
参数名称 | 描述 | 输入输出 |
channel_num | DMA 通道号 | 输入 |
src | 中断回调函数 | 输入 |
dest | 回调函数的参数 | 输入 |
len | 传输长度 | 输入 |
返回值
无。
dmac_is_idle
描述
判断DMAC当前通道是否空闲,该函数在传输前和传输后都可以用来判断DMAC状态。
函数原型
int dmac_is_idle(dmac_channel_number_t channel_num)
参数
参数名称 | 描述 | 输入输出 |
channel_num | DMA 通道号 | 输入 |
返回值
返回值 | 描述 |
0 | 忙 |
1 | 空闲 |
dmac_wait_idle
描述
等待DMAC进入空闲状态。
参数
参数名称 | 描述 | 输入输出 |
channel_num | DMA 通道号 | 输入 |
返回值
无。
举例
/* I2C通过DMA发送128个int数据 */uint32_t buf[128];dmac_wait_idle(SYSCTL_DMA_CHANNEL_0);sysctl_dma_select(SYSCTL_DMA_CHANNEL_0, SYSCTL_DMA_SELECT_I2C0_TX_REQ);dmac_set_single_mode(SYSCTL_DMA_CHANNEL_0, buf, (void*)(&i2c_adapter->data_cmd), DMAC_ADDR_INCREMENT, DMAC_ADDR_NOCHANGE, DMAC_MSIZE_4, DMAC_TRANS_WIDTH_32, 128);dmac_wait_done(SYSCTL_DMA_CHANNEL_0);
数据类型
相关数据类型、数据结构定义如下:
· dmac_channel_number_t:DMA通道编号。
· dmac_address_increment_t:地址增长方式。
· dmac_burst_trans_length_t:突发传输数量。
· dmac_transfer_width_t:单次传输数据位数。
dmac_channel_number_t
描述
DMA通道编号。
定义
typedef enum _dmac_channel_number{ DMAC_CHANNEL0 = 0, DMAC_CHANNEL1 = 1, DMAC_CHANNEL2 = 2, DMAC_CHANNEL3 = 3, DMAC_CHANNEL4 = 4, DMAC_CHANNEL5 = 5, DMAC_CHANNEL_MAX} dmac_channel_number_t;
成员
成员名称 | 描述 |
DMAC_CHANNEL0 | DMA通道 0 |
DMAC_CHANNEL1 | DMA通道 1 |
DMAC_CHANNEL2 | DMA通道 2 |
DMAC_CHANNEL3 | DMA通道 3 |
DMAC_CHANNEL4 | DMA通道 4 |
DMAC_CHANNEL5 | DMA通道 5 |
dmac_address_increment_t
描述
地址增长方式。
定义
typedef enum _dmac_address_increment{ DMAC_ADDR_INCREMENT = 0x0, DMAC_ADDR_NOCHANGE = 0x1} dmac_address_increment_t;
成员
成员名称 | 描述 |
DMAC_ADDR_INCREMENT | 地址自动增长 |
DMAC_ADDR_NOCHANGE | 地址不变 |
dmac_burst_trans_length_t
描述
突发传输数量。
定义
typedef enum _dmac_burst_trans_length{ DMAC_MSIZE_1 = 0x0, DMAC_MSIZE_4 = 0x1, DMAC_MSIZE_8 = 0x2, DMAC_MSIZE_16 = 0x3, DMAC_MSIZE_32 = 0x4, DMAC_MSIZE_64 = 0x5, DMAC_MSIZE_128 = 0x6, DMAC_MSIZE_256 = 0x7} dmac_burst_trans_length_t;
成员
成员名称 | 描述 |
DMAC_MSIZE_1 | 单次传输数量乘1 |
DMAC_MSIZE_4 | 单次传输数量乘4 |
DMAC_MSIZE_8 | 单次传输数量乘8 |
DMAC_MSIZE_16 | 单次传输数量乘16 |
DMAC_MSIZE_32 | 单次传输数量乘32 |
DMAC_MSIZE_64 | 单次传输数量乘64 |
DMAC_MSIZE_128 | 单次传输数量乘128 |
DMAC_MSIZE_256 | 单次传输数量乘256 |
dmac_transfer_width_t
描述
单次传输数据位数。
定义
typedef enum _dmac_transfer_width{ DMAC_TRANS_WIDTH_8 = 0x0, DMAC_TRANS_WIDTH_16 = 0x1, DMAC_TRANS_WIDTH_32 = 0x2, DMAC_TRANS_WIDTH_64 = 0x3, DMAC_TRANS_WIDTH_128 = 0x4, DMAC_TRANS_WIDTH_256 = 0x5} dmac_transfer_width_t;
成员
成员名称 | 描述 |
DMAC_TRANS_WIDTH_8 | 单次传输8位 |
DMAC_TRANS_WIDTH_16 | 单次传输16位 |
DMAC_TRANS_WIDTH_32 | 单次传输32位 |
DMAC_TRANS_WIDTH_64 | 单次传输64位 |
DMAC_TRANS_WIDTH_128 | 单次传输128位 |
DMAC_TRANS_WIDTH_256 | 单次传输256位 |