Zephry DMA驱动使用教程(内存到内存)
Posted 17岁boy想当攻城狮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Zephry DMA驱动使用教程(内存到内存)相关的知识,希望对你有一定的参考价值。
目录
1. 内部Memory之间传输数据(SRAM to SRAM)
前言
本文涉及到理论知识可以参考以下文章:
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 <zephyr.h>
#include <drivers/dma.h>
#include <sys/printk.h>
#include <string.h>
基本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 <zephyr.h>
#include <drivers/dma.h>
#include <sys/printk.h>
#include <string.h>
#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不要对这个内存进行访问,否则会造成传输失败的情况,因为对于内存芯片来说,同一时间下一个内存单元只能被一个设备选取,当然你可以访问其它地址
以上是关于Zephry DMA驱动使用教程(内存到内存)的主要内容,如果未能解决你的问题,请参考以下文章
[架构之路-47]:目标系统 - 系统软件 - Linux OS硬件设备驱动 - CPU内存管理单元MMUDMA与IO内存管理单元IOMMU