> 文章列表 > Zephry DMA驱动使用教程

Zephry DMA驱动使用教程

Zephry DMA驱动使用教程

目录

前言

一、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 <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不要对这个内存进行访问,否则会造成传输失败的情况,因为对于内存芯片来说,同一时间下一个内存单元只能被一个设备选取,当然你可以访问其它地址