目录
前言
一、DMA Driver介绍
1. 常用API介绍
1.1 dma_config
1.2 dma_start
1.3 dma_stop
2. 结构体
2.1 dma_config
2.2 dma_block_config
三、DMA Driver 示例
1. 内部Memory之间传输数据(SRAM to SRAM)
DMA模式注意事项
前言
本文涉及到理论知识可以参考以下文章:
SRAM/DRAM:ROM、RAM存储器原理详解以及DRAM、SRAM、SDRAM 、FLASH存储器的介绍_17岁boy的博客-CSDN博客_ram和rom的区别
DMA:DMA控制器原理详解_17岁boy的博客-CSDN博客_dma是什么意思
一、DMA Driver介绍
1. 常用API介绍
1.1 dma_config
1.1.1 函数介绍
函数原型
static inline int dma_config(const struct device *dev, uint32_t channel, struct dma_config *config);
函数介绍
作用
返回值
配置dma指定通道 0成功,非0失败
参数介绍
参数名
类型
作用
dev const struct device * 指向实例化DMA驱动指针句柄
channel uint32_t 要配置的通道编号
config struct dma_config * 指向配置属性结构体的指针
1.2 dma_start
1.2.1 函数介绍
函数原型
int dma_start(const struct device *dev, uint32_t channel);
函数介绍
作用
返回值
启动DMA指定通道 0成功,非0失败
参数介绍
参数名
类型
作用
dev const struct device * 指向实例化DMA驱动指针句柄
channel uint32_t 通道编号
1.3 dma_stop
1.3.1 函数介绍
函数原型
int dma_stop(const struct device *dev, uint32_t channel);
函数介绍
函数作用
返回值
停止DMA指定通道传输 0成功,非0失败
参数介绍
参数名
类型
作用
dev const struct device * 指向实例化DMA驱动指针句柄
2. 结构体
2.1 dma_config
结构体原型
struct dma_config {
uint32_t dma_slot : 7;
uint32_t channel_direction : 3;
uint32_t complete_callback_en : 1;
uint32_t error_callback_en : 1;
uint32_t source_handshake : 1;
uint32_t dest_handshake : 1;
uint32_t channel_priority : 4;
uint32_t source_chaining_en : 1;
uint32_t dest_chaining_en : 1;
uint32_t linked_channel : 7;
uint32_t reserved : 5;
uint32_t source_data_size : 16;
uint32_t dest_data_size : 16;
uint32_t source_burst_length : 16;
uint32_t dest_burst_length : 16;
uint32_t block_count;
struct dma_block_config *head_block;
void *user_data;
dma_callback_t dma_callback;
};
结构体作用
作用
负责配置DMA通道
成员介绍
成员名
类型
作用
成员名
类型
作用
dma_slot uint32_t 外围设备和方向,只针对特定硬件
channel_direction uint32_t 工作模式,0代表内存对内存,1代表内存对外设,2代表外设对内存,3代表外设对外设
complete_callback_en uint32_t 回调触发条件,0-完成时调用的回调仅,1-每个块完成时调用的回调
error_callback_en uint32_t 回调触发条件,0-启用错误回调,1-禁用错误回调
source_handshake uint32_t 源地址解码方式, 0-HW(硬件解码), 1-SW(软件解码)
dest_handshake uint32_t 目标地址解码方式,0-HW(硬件解码), 1-SW(软件解码) 0-H
channel_priority uint32_t 通道优先级
source_chaining_en uint32_t 启用/禁用源块链接0-禁用,1-启用
dest_chaining_en uint32_t 启用/禁用目标块链接。0-禁用,1-启用
linked_channel uint32_t 通道结束后下一个请求通道的编号,即当前通道工作完成后要请求的下一个通道编号
reserved uint32_t 保留
source_data_size uint32_t 源地址数据总线的位宽即内存芯片数据总线位宽,以字节为单位
dest_data_size uint32_t 目标地址数据总线的位宽即内存芯片数据总线位宽,以字节为单位
source_burst_length uint32_t 多少字节为一块,如8字节为1块则每8字节为一个传输单位,分块传输,源块与目标块要一致
dest_burst_length uint32_t 多少字节为一块,如8字节为1块则每8字节为一个传输单位,分块传输,源块与目标块要一致
head_block struct dma_block_config * 块配置结构体
user_data void * 回调时传递的参数
dma_callback_t dma_callback 回调函数指针
2.2 dma_block_config
结构体原型
struct dma_block_config { #ifdef CONFIG_DMA_64BIT
uint64_t source_address;
uint64_t dest_address;
#else
uint32_t source_address;
uint32_t dest_address;
#endif
uint32_t source_gather_interval;
uint32_t dest_scatter_interval;
uint16_t dest_scatter_count;
uint16_t source_gather_count;
uint32_t block_size;
struct dma_block_config *next_block;
uint16_t source_gather_en : 1;
uint16_t dest_scatter_en : 1;
uint16_t source_addr_adj : 2;
uint16_t dest_addr_adj : 2;
uint16_t source_reload_en : 1;
uint16_t dest_reload_en : 1;
uint16_t fifo_mode_control : 4;
uint16_t flow_control_mode : 1;
uint16_t reserved : 3;
};
结构体介绍
作用
块传输配置
主要成员介绍
成员名
类型
作用
成员名
类型
作用
source_address uint64_t/uint32_t 传输块的起始源地址
source_gather_interval uint32_t 地址调整是否在聚集边界
dest_address uint64_t/uint32_t 传输块的目标地址
dest_scatter_interval uint32_t 是否分散边界的地址调整
dest_scatter_count uint16_t 散布边界之间的连续转移计数
source_gather_count uint16_t 聚集边界之间的连续传输计数
block_size uint32_t 传输块大小
三、DMA Driver 示例
1. 内部Memory之间传输数据(SRAM to SRAM)
我的程序就运行在SRAM里,因为是内存对内存所以不需要额外指定地址,SRAM在我的板子里偏移是0x20010000,这个地址为起始地址,你可以通过打印你变量地址来查看你当前运行在哪个内存
在pro.conf文件中开启dma驱动
CONFIG_DMA=y
包含基本头文件
#include
#include
#include
#include
基本BUFF大小与写入BUFF
#define RX_BUFF_SIZE 1024
static const char tx_data[] = "It is harder to be kind than to be wise.......\0";
static char rx_data[RX_BUFF_SIZE] = { 0 };
回调函数,当error_code为0代表传输完成
static void test_done(const struct device *dma_dev, void *arg,
uint32_t id, int error_code)
{
if (error_code == 0) {
printk("DMA transfer done\n");
printk("ImFu:%s\n", rx_data);
} else {
printk("DMA transfer met an error\n"); }
}
配置代码
static void test_task(uint32_t chan_id, uint32_t blen)
{
struct dma_config dma_cfg = { 0 };
struct dma_block_config dma_block_cfg = { 0 };
const struct device *dma = device_get_binding("DMA_2");
if (!dma) {
printk("error\n");
}
//Set working mode
dma_cfg.channel_direction = MEMORY_TO_MEMORY;
//Set the bus bit width to 8 bits
dma_cfg.source_data_size = 1U;
dma_cfg.dest_data_size = 1U;
//Blocks per transmission
dma_cfg.source_burst_length = blen;
dma_cfg.dest_burst_length = blen;
//Callback
dma_cfg.dma_callback = test_done;
dma_cfg.complete_callback_en = 0U;
dma_cfg.error_callback_en = 1U;
dma_cfg.block_count = 1U;
//Block attribute
dma_cfg.head_block = &dma_block_cfg;
//Configure transport block properties
dma_block_cfg.block_size = sizeof(tx_data);
dma_block_cfg.source_address = (uint32_t)tx_data;
dma_block_cfg.dest_address = (uint32_t)rx_data;
//config channel
if (dma_config(dma, chan_id, &dma_cfg)!=0) {
printk("Unable to configure channel\n");
}
//start
if (dma_start(dma, chan_id)!=0) {
printk("Unable to start channel\n");
}
//strcmp(tx_data,tx_data);
}
main函数里调用
void main(void)
{
test_task(1, 8);
while(0);
}
完整代码
#include
#include
#include
#include
#define RX_BUFF_SIZE 1024
static const char tx_data[] = "It is harder to be kind than to be wise.......\0";
static char rx_data[RX_BUFF_SIZE] = { 0 };
static void test_done(const struct device *dma_dev, void *arg,
uint32_t id, int error_code)
{
if (error_code == 0) {
printk("DMA transfer done\n");
printk("ImFu:%s\n", rx_data);
} else {
printk("DMA transfer met an error\n"); }
}
static void test_task(uint32_t chan_id, uint32_t blen)
{
struct dma_config dma_cfg = { 0 };
struct dma_block_config dma_block_cfg = { 0 };
const struct device *dma = device_get_binding("DMA_2");
if (!dma) {
printk("error\n");
}
//Set working mode
dma_cfg.channel_direction = MEMORY_TO_MEMORY;
//Set the bus bit width to 8 bits
dma_cfg.source_data_size = 1U;
dma_cfg.dest_data_size = 1U;
//Blocks per transmission
dma_cfg.source_burst_length = blen;
dma_cfg.dest_burst_length = blen;
//Callback
dma_cfg.dma_callback = test_done;
dma_cfg.complete_callback_en = 0U;
dma_cfg.error_callback_en = 1U;
dma_cfg.block_count = 1U;
//Block attribute
dma_cfg.head_block = &dma_block_cfg;
//Configure transport block properties
dma_block_cfg.block_size = sizeof(tx_data);
dma_block_cfg.source_address = (uint32_t)tx_data;
dma_block_cfg.dest_address = (uint32_t)rx_data;
//config channel
if (dma_config(dma, chan_id, &dma_cfg)!=0) {
printk("Unable to configure channel\n");
}
//start
if (dma_start(dma, chan_id)!=0) {
printk("Unable to start channel\n");
}
//strcmp(tx_data,tx_data);
}
void main(void)
{
test_task(1, 8);
while(0);
}
运行结果:
DMA transfer done
ImFu:It is harder to be kind than to be wise.......
DMA模式注意事项
在开发过程中需要注意一些事情,如记得看好你的原理图中你的DMA总线是否连在SRAM或DRAM上,如果没有连上即便这个通道支持ME2ME那么也无法访问这些内存
其次在开发过程中,即便你的DMA和CPU使用的是两个总线,那么也需要注意当DMA在对特定内存地址进行访问时,CPU不要对这个内存进行访问,否则会造成传输失败的情况,因为对于内存芯片来说,同一时间下一个内存单元只能被一个设备选取,当然你可以访问其它地址