Zephry DMA驱动使用教程(内存到内存)

Posted 17岁boy想当攻城狮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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失败
  • 参数介绍

参数名

类型

作用

devconst struct device *指向实例化DMA驱动指针句柄
channeluint32_t要配置的通道编号
configstruct dma_config *指向配置属性结构体的指针

1.2 dma_start

1.2.1 函数介绍

  • 函数原型

int dma_start(const struct device *dev, uint32_t channel);
  • 函数介绍

作用

返回值

启动DMA指定通道0成功,非0失败
  • 参数介绍

参数名

类型

作用

devconst struct device *指向实例化DMA驱动指针句柄
channeluint32_t通道编号

1.3 dma_stop

1.3.1 函数介绍

  • 函数原型

int dma_stop(const struct device *dev, uint32_t channel);
  • 函数介绍

函数作用

返回值

停止DMA指定通道传输0成功,非0失败
  • 参数介绍

参数名

类型

作用

devconst 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_slotuint32_t外围设备和方向,只针对特定硬件
channel_directionuint32_t工作模式,0代表内存对内存,1代表内存对外设,2代表外设对内存,3代表外设对外设
complete_callback_enuint32_t回调触发条件,0-完成时调用的回调仅,1-每个块完成时调用的回调
error_callback_enuint32_t回调触发条件,0-启用错误回调,1-禁用错误回调
source_handshakeuint32_t源地址解码方式, 0-HW(硬件解码), 1-SW(软件解码)
dest_handshakeuint32_t目标地址解码方式,0-HW(硬件解码), 1-SW(软件解码) 0-H
channel_priorityuint32_t通道优先级
source_chaining_enuint32_t启用/禁用源块链接0-禁用,1-启用
dest_chaining_enuint32_t启用/禁用目标块链接。0-禁用,1-启用
linked_channeluint32_t通道结束后下一个请求通道的编号,即当前通道工作完成后要请求的下一个通道编号
reserveduint32_t保留
source_data_sizeuint32_t源地址数据总线的位宽即内存芯片数据总线位宽,以字节为单位
dest_data_sizeuint32_t目标地址数据总线的位宽即内存芯片数据总线位宽,以字节为单位
source_burst_lengthuint32_t多少字节为一块,如8字节为1块则每8字节为一个传输单位,分块传输,源块与目标块要一致
dest_burst_lengthuint32_t多少字节为一块,如8字节为1块则每8字节为一个传输单位,分块传输,源块与目标块要一致
head_blockstruct dma_block_config *块配置结构体
user_datavoid *回调时传递的参数
dma_callback_tdma_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_addressuint64_t/uint32_t传输块的起始源地址
source_gather_intervaluint32_t地址调整是否在聚集边界
dest_addressuint64_t/uint32_t传输块的目标地址
dest_scatter_intervaluint32_t是否分散边界的地址调整
dest_scatter_countuint16_t散布边界之间的连续转移计数
source_gather_countuint16_t聚集边界之间的连续传输计数
block_sizeuint32_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驱动使用教程(内存到内存)的主要内容,如果未能解决你的问题,请参考以下文章

用于 DMA 的快速映射内存缓冲区

DMA32映射问题

驱动移植过程中DMA内存相关接口替换

[架构之路-47]:目标系统 - 系统软件 - Linux OS硬件设备驱动 - CPU内存管理单元MMUDMA与IO内存管理单元IOMMU

内存映射(Linux设备驱动程序)

Linux内存从0到1学习笔记(8.7 DMA-BUF代码解读)