Driver: DMA M2M¶
1 功能概述¶
dma_m2m sample
以 Memory to Memory 传输为例,演示了 Zephyr DMA Driver 在 PAN1080 SoC 上的使用方法。
2 环境准备¶
PAN1080 EVB一块
Micro USB线一条(用于供电和查看串口打印Log)
硬件接线:
使用USB线,将PC USB与EVB MicroUSB(USB->UART)相连
使用杜邦线将EVB上的:
UART1 TX与P06相连
UART1 RX与P07相连
PC软件: 串口调试助手(UartAssist)或终端工具(SecureCRT),波特率921600
3 编译和烧录¶
例程位置:zephyr\samples_panchip\drivers\dma_m2m
目前可使用ZAL工具或quick build脚本进行编译和下载。
脚本位置:quick_build_samples\drivers\dma_m2m.bat
打开脚本后默认会编译项目,编译完成时,可输入字符进行后续下载等操作:
Input the keyword to continue:
'b' build 编译项目
'r' make clean and rebuild 重新编译项目
'f' flash download 下载
'e' erase chip 擦除芯片
'o' open project by VS Code 打开 `VS Code`,可查看源码,执行编译下载等
others exit 退出
wait input:
4 演示说明¶
本例程中,进行了2次 DMA Block 传输:
首先从 Flash Address 1 中读取一个大数组,将其通过 DMA 的方式搬移的 SRAM 上,并确认搬移成功;
然后使用
dma_reload()
接口,重新配置源地址为 Flash Address 2,并将此地址对应的大数组通过 DMA 的方式搬移到同一块 SRAM 上,再确认是否搬移成功。
本例程涉及如下的 DMA Driver APIs:
dma_request_channel()
dma_release_channel()
dma_config()
dma_start()
dma_reload()
dma_get_status()
例程执行成功的 Log 如下所示:
*** Booting Zephyr OS build zephyr-v2.7.0-481-ge520fced9ef8 ***
[00:00:00.000,000] <inf> dma_dw: Device DMA_0 initialized
[00:00:00.000,000] <inf> app: DMA M2M (Memory to Memory) demo start..
[00:00:00.001,000] <inf> app: Configure and start the 1st DMA transfer procedure, chan_id=0
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.003,000] <inf> app: dma_transfer_cb: DMA transfer done
[00:00:00.003,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.003,000] <inf> app: DMA status: direction=0, busy=0
[00:00:00.009,000] <inf> app: Reload and start the 2nd DMA transfer procedure, chan_id=0
[00:00:00.009,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.009,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.009,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.009,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.009,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.009,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.009,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.011,000] <inf> app: dma_transfer_cb: DMA transfer done
[00:00:00.011,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.011,000] <inf> app: DMA status: direction=0, busy=0
[00:00:00.017,000] <inf> app: DMA Data transfer OK, and channel 0 is now ready again for later use.
[00:00:00.017,000] <inf> app: DMA M2M (Memory to Memory) demo end.
5 开发者说明¶
5.1 PAN1080 DMA Controller 硬件特性介绍¶
PAN1080 SoC 内置一个 DMA Controller (DMAC),其具有 3 个可用的通道(通道编号为 0 ~ 2),并支持通道优先级配置;
PAN1080 DMAC 仅支持单 Block 传输,一个 Block 最大可传输 4095 个 Item,每个 Item 最大支持 4 字节数据(即 32-Bits,与系统总线宽度一致),换句话说,在一次典型的 DMA Memory-to-Memory 传输过程中,最大可以搬移 4095 * 4 = 16380 字节的数据(Peripheral-to-Memory 传输方式达不到此值);
注:PAN1080 DMAC 不支持 Chaining Multi-Block 传输和 Scatter/Gather 传输
PAN1080 DMAC 支持 Memory-to-Memory、Memory-to-Peripheral、Peripheral-to-Memory、Peripheral-to-Peripheral 等四种传输方式;
PAN1080 DMAC 支持寄存器配置数据传输源地址和目的地址,并支持地址递增、递减或不变;
PAN1080 DMAC 每个通道均有一个深度为 4、宽度为 32-bit 的内部 FIFO,其内部逻辑支持自动包装或拆解数据以适应 FIFO 位宽;
PAN1080 DMAC 支持硬件握手和软件握手接口,并支持 single transaction 和 burst transaction;
PAN1080 DMAC 支持流控,但其只支持 DMA 作为流控,不支持 Peripheral 作为流控;
PAN1080 DMAC 支持组合或分开的中断请求,支持中断使能与屏蔽,可在以下事件触发时产生中断:DMA Transfer 结束、Block Transfer 结束、Single/Burst Transaction 结束、传输出错;
5.2 Zephyr DMA Driver 配置项详解¶
Zephyr DMA Driver API (zephyr/include/drivers/dma.h
) 中,有两个重要的结构体(dma_config
和dma_block_config
)用于 DMA 配置,其为了做到通用,提供了非常多的配置参数,而对于 PAN1080 DMA 来说,只需用到其中的一部分即可。
struct dma_config
和 struct dma_block_config
在 dma.h
中的定义如下:
/**
* @struct dma_block_config
* @brief DMA block configuration structure.
*
* @param source_address is block starting address at source
* @param source_gather_interval is the address adjustment at gather boundary
* @param dest_address is block starting address at destination
* @param dest_scatter_interval is the address adjustment at scatter boundary
* @param dest_scatter_count is the continuous transfer count between scatter
* boundaries
* @param source_gather_count is the continuous transfer count between gather
* boundaries
*
* @param block_size is the number of bytes to be transferred for this block.
*
* @param config is a bit field with the following parts:
*
* source_gather_en [ 0 ] - 0-disable, 1-enable.
* dest_scatter_en [ 1 ] - 0-disable, 1-enable.
* source_addr_adj [ 2 : 3 ] - 00-increment, 01-decrement,
* 10-no change.
* dest_addr_adj [ 4 : 5 ] - 00-increment, 01-decrement,
* 10-no change.
* source_reload_en [ 6 ] - reload source address at the end of
* block transfer
* 0-disable, 1-enable.
* dest_reload_en [ 7 ] - reload destination address at the end
* of block transfer
* 0-disable, 1-enable.
* fifo_mode_control [ 8 : 11 ] - How full of the fifo before transfer
* start. HW specific.
* flow_control_mode [ 12 ] - 0-source request served upon data
* availability.
* 1-source request postponed until
* destination request happens.
* reserved [ 13 : 15 ]
*/
struct dma_block_config {
#ifdef CONFIG_DMA_64BIT // PAN1080地址总线为32位,因此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; // * 必须配置(固定为0)
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; // 不用配置(硬件支持,但Driver未实现)
uint16_t dest_reload_en : 1; // 不用配置(硬件支持,但Driver未实现)
uint16_t fifo_mode_control : 4; // 不用配置(硬件支持,但用户无需配置)
uint16_t flow_control_mode : 1; // 不用配置(硬件不支持)
uint16_t reserved : 3;
};
/**
* @struct dma_config
* @brief DMA configuration structure.
*
* @param dma_slot [ 0 : 6 ] - which peripheral and direction
* (HW specific)
* @param channel_direction [ 7 : 9 ] - 000-memory to memory,
* 001-memory to peripheral,
* 010-peripheral to memory,
* 011-peripheral to peripheral,
* ...
* @param complete_callback_en [ 10 ] - 0-callback invoked at completion only
* 1-callback invoked at completion of
* each block
* @param error_callback_en [ 11 ] - 0-error callback enabled
* 1-error callback disabled
* @param source_handshake [ 12 ] - 0-HW, 1-SW
* @param dest_handshake [ 13 ] - 0-HW, 1-SW
* @param channel_priority [ 14 : 17 ] - DMA channel priority
* @param source_chaining_en [ 18 ] - enable/disable source block chaining
* 0-disable, 1-enable
* @param dest_chaining_en [ 19 ] - enable/disable destination block
* chaining.
* 0-disable, 1-enable
* @param linked_channel [ 20 : 26 ] - after channel count exhaust will
* initiate a channel service request
* at this channel
* @param reserved [ 27 : 31 ]
* @param source_data_size [ 0 : 15 ] - width of source data (in bytes)
* @param dest_data_size [ 16 : 31 ] - width of dest data (in bytes)
* @param source_burst_length [ 0 : 15 ] - number of source data units
* @param dest_burst_length [ 16 : 31 ] - number of destination data units
* @param block_count is the number of blocks used for block chaining, this
* depends on availability of the DMA controller.
* @param source_peripheral_id is the ID of a hardware peripheral which
* acts as DMA source of current channel, note this is only valid when
* the source_handshake bit is 0 (means HW handshake).
* @param dest_peripheral_id is the ID of a hardware peripheral which
* acts as DMA destination of current channel, note this is only valid when
* the dest_handshake bit is 0 (means HW handshake).
* @param user_data private data from DMA client.
* @param dma_callback see dma_callback_t for details
*/
struct dma_config {
uint32_t dma_slot : 7; // 不用配置(Driver不支持)
uint32_t channel_direction : 3; // * 必须配置
uint32_t complete_callback_en : 1; // # 可选配置(根据实际需要决定是否配置)
uint32_t error_callback_en : 1; // 不用配置(Driver不支持)
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; // * 必须配置(固定为1)
uint16_t source_peripheral_id; // # 可选配置(根据实际需要决定是否配置)
uint16_t dest_peripheral_id; // # 可选配置(根据实际需要决定是否配置)
struct dma_block_config *head_block; // * 必须配置
void *user_data; // # 可选配置(根据实际需要决定是否配置)
dma_callback_t dma_callback; // # 可选配置(根据实际需要决定是否配置)
};
上述两个结构体定义中的参数,在 DMA 配置过程中关注以下原则即可:
标记为 必须配置 的参数,是必须正确填写的,否则 DMA 将无法正常工作;
标记为 可选配置 的参数,需要根据实际需求决定是否需要进行配置;
标记为 不用配置 的参数,无需关注之;
下面两个表格中详细列出了 struct dma_config
和 struct dma_block_config
中各个参数的详细含义及 PAN1080 DMAC 的使用说明:
参数名称 |
重要程度 |
参数含义 |
PAN1080 DMA 说明 |
---|---|---|---|
source_address |
必须配置 |
DMA 源端地址 |
需要注意:
|
dest_address |
必须配置 |
DMA 目的端地址 |
需要注意:
|
source_gather_interval |
不用配置 |
DMA Gather 源端地址到达边界后的调整值 |
PAN1080 DMAC 不支持 Gather 特性,此参数无意义 |
dest_scatter_interval |
不用配置 |
DMA Scatter 目的端地址到达边界后的调整值 |
PAN1080 DMAC 不支持 Scatter 特性,此参数无意义 |
dest_scatter_count |
不用配置 |
DMA Scatter 目的端地址调整次数 |
PAN1080 DMAC 不支持 Scatter 特性,此参数无意义 |
source_gather_count |
不用配置 |
DMA Gather 源端地址调整次数 |
PAN1080 DMAC 不支持 Gather 特性,此参数无意义 |
block_size |
必须配置 |
当前 DMA Block 预期传输的数据长度(字节) |
需要注意此处待传输的数据长度必须同时满足以下几个条件:
|
next_block |
必须配置 |
指向下一个 DMA Block 的配置参数 |
PAN1080 DMAC 不支持 Chaining Multi-Block 特性,所以此参数必须配置为0 |
source_gather_en |
不用配置 |
DMA Source Gather 使能控制 |
PAN1080 DMAC 不支持 Gather 特性,此参数无意义 |
dest_scatter_en |
不用配置 |
DMA Destination Scatter 使能控制 |
PAN1080 DMAC 不支持 Scatter 特性,此参数无意义 |
source_addr_adj |
不用配置 |
DMA 源端地址步进规则(递增、递减、或不变) |
PAN1080 DMAC 支持此特性,但用户无需在此处配置,原因是 DMA Driver 会根据实际传输类型,自动按如下规则配置步进:
|
dest_addr_adj |
不用配置 |
DMA 目的端地址步进规则(递增、递减、或不变) |
PAN1080 DMAC 支持此特性,但用户无需在此处配置,原因是 DMA Driver 会根据实际传输类型,自动按如下规则配置步进:
|
source_reload_en |
不用配置 |
DMA 源端地址自动重新加载使能控制 |
PAN1080 DMAC 支持此特性,但 PAN1080 DMA Driver 未实现此特性,因此无需配置 |
dest_reload_en |
不用配置 |
DMA 目的端地址自动重新加载使能控制 |
PAN1080 DMAC 支持此特性,但 PAN1080 DMA Driver 未实现此特性,因此无需配置 |
fifo_mode_control |
不用配置 |
决定 DMA 执行一个 Burst Transaction 之前,DMA FIFO 中需要有多少空间或数据 |
PAN1080 DMAC 支持此特性,但用户无需在此处配置,原因是 DMA Driver 强制将此特性配置为“只要 FIFO 空间或数据足够一次 Single Transfer,就可以传输” |
flow_control_mode |
不用配置 |
决定目的端 Peripheral 作为流控时,源外设如何给数据 |
PAN1080 DMAC 不支持 Peripheral 作为流控,自然也不支持此特性,此参数无意义 |
参数名称 |
重要程度 |
参数含义 |
PAN1080 DMA 说明 |
---|---|---|---|
dma_slot |
不用配置 |
DMA Peripheral 传输方式下的外设 ID |
PAN1080 DMAC 支持此特性,但 DMA Driver 使用另外两个单独的参数来分别指示源端和目的端外设 ID:
|
channel_direction |
必须配置 |
DMA 传输类型 |
PAN1080 DMAC 支持以下 4 种传输类型:
|
complete_callback_en |
可选配置 |
决定 |
PAN1080 DMA Driver 支持此特性,但由于 PAN1080 DMAC 硬件仅支持单个 Block 传输,因此在概念上,“每个 Block 传输完成”与“所有数据传输完成”是相同的,在实际应用中,建议直接将此参数配置为0即可 |
error_callback_en |
不用配置 |
DMA 传输出错的 Callback 函数使能控制 |
PAN1080 DMAC 支持Error中断,但 PAN1080 DMA Driver 未实现此特性,因此无需配置 |
source_handshake |
不用配置 |
决定 DMA 源端握手方式为硬件握手还是软件握手 |
PAN1080 DMAC 支持此特性,但用户无需在此处配置,原因是 DMA Driver 强制将此特性配置为“硬件握手” |
dest_handshake |
不用配置 |
决定 DMA 目的端握手方式为硬件握手还是软件握手 |
PAN1080 DMAC 支持此特性,但用户无需在此处配置,原因是 DMA Driver 强制将此特性配置为“硬件握手” |
channel_priority |
可选配置 |
当前 DMA 通道的优先级 |
PAN1080 DMAC 支持此特性,3个通道的优先级可以独立配置,优先级可选范围为 0 ~ 3,其中数字越大表示优先级越高,在实际应用中,如果没有特殊需求,建议直接将此参数配置为0即可 |
source_chaining_en |
不用配置 |
DMA 源端 Block-Chaining (Multi-Block) 特性使能控制 |
PAN1080 DMAC 硬件不支持,此参数无意义 |
dest_chaining_en |
不用配置 |
DMA 目的端 Block-Chaining (Multi-Block) 特性使能控制 |
PAN1080 DMAC 硬件不支持,此参数无意义 |
linked_channel |
不用配置 |
DMA 衔接通道 |
PAN1080 DMAC 硬件不支持,此参数无意义 |
source_data_size |
必须配置 |
DMA 源端数据宽度(字节) |
PAN1080 DMAC 支持的源端数据宽度有 1、2、4 字节:
|
dest_data_size |
必须配置 |
DMA 目的端数据宽度(字节) |
PAN1080 DMAC 支持的目的端数据宽度有 1、2、4 字节:
|
source_burst_length |
必须配置 |
DMA 源端 Burst 长度 |
PAN1080 DMAC 支持的源端 Burst 长度有 1、4、8 字节:
|
dest_burst_length |
必须配置 |
DMA 目的端 Burst 长度 |
PAN1080 DMAC 支持的目的端 Burst 长度有 1、4、8 字节:
|
block_count |
必须配置 |
DMA 待传输的 Block 数量 |
PAN1080 DMAC 不支持 Chaining Multi-Block 特性,所以此参数必须配置为1 |
source_peripheral_id |
可选配置 |
DMA 源端 Peripheral ID |
当 DMA 源端被配置为 Peripheral 时(即 Peripheral-to-Memory 或 Peripheral-to-Peripheral 方式),此处用于指定对应外设的 Peripheral ID,PAN1080 所有支持 DMA 功能的外设 Periphal ID 定义详见:
|
dest_peripheral_id |
可选配置 |
DMA 目的端 Peripheral ID |
当 DMA 目的端被配置为 Peripheral 时(即 Memory-to-Peripheral 或 Peripheral-to-Peripheral 方式),此处用于指定对应外设的 Peripheral ID,PAN1080 所有支持 DMA 功能的外设 Periphal ID 定义详见:
|
head_block |
必须配置 |
指向第一个 DMA Block 的配置参数 |
需要先定义一个 |
user_data |
可选配置 |
DMA Callback 的用户数据, |
此参数会作为函数参数传进 |
dma_callback |
可选配置 |
DMA Callback 函数的指针, |
当 DMA 传输完成(或 Block 传输完成)后,会自动触发此参数指向的函数,在实际应用中,建议实现callback函数,以方便确认DMA传输成功 |