15. DMA Manager 组件
15.1. 概述
DMA Manager 组件在底层 DMA 驱动之上提供了一套统一的资源管理层,负责 DMA 通道的分配、配置与生命周期管理。支持多 DMA 实例(HDMA、XDMA 等),并提供中断回调注册与通道控制能力。以下是该组件的主要特征:
集中式 DMA 通道资源管理
自动分配和释放 DMA 通道,避免手动管理冲突。
支持从指定 DMA 实例请求通道。
统一的通道配置接口
通过
dma_mgr_chn_conf_t结构体封装 DMA 通道配置(源/目的地址、传输宽度、突发大小、工作模式等),提供一次性配置 APIdma_mgr_setup_channel。
灵活的中断回调机制
支持分别注册传输完成(TC)、半传输完成(Half TC)、错误(Error)和终止(Abort)的回调函数。
支持 DMAV2 高级特性
无限循环传输、自定义突发大小、握手选项、字节序交换等。
15.2. 相关枚举和结构体介绍
枚举 / 宏介绍
DMA 传输宽度:定义每次传输的数据宽度。
#define DMA_MGR_TRANSFER_WIDTH_BYTE DMA_TRANSFER_WIDTH_BYTE /* 8位 */ #define DMA_MGR_TRANSFER_WIDTH_HALF_WORD DMA_TRANSFER_WIDTH_HALF_WORD /* 16位 */ #define DMA_MGR_TRANSFER_WIDTH_WORD DMA_TRANSFER_WIDTH_WORD /* 32位 */ #define DMA_MGR_TRANSFER_WIDTH_DOUBLE_WORD DMA_TRANSFER_WIDTH_DOUBLE_WORD /* 64位 */
DMA 每次突发传输的大小:
#define DMA_MGR_NUM_TRANSFER_PER_BURST_1T DMA_NUM_TRANSFER_PER_BURST_1T #define DMA_MGR_NUM_TRANSFER_PER_BURST_2T DMA_NUM_TRANSFER_PER_BURST_2T #define DMA_MGR_NUM_TRANSFER_PER_BURST_4T DMA_NUM_TRANSFER_PER_BURST_4T #define DMA_MGR_NUM_TRANSFER_PER_BURST_8T DMA_NUM_TRANSFER_PER_BURST_8T #define DMA_MGR_NUM_TRANSFER_PER_BURST_16T DMA_NUM_TRANSFER_PER_BURST_16T #define DMA_MGR_NUM_TRANSFER_PER_BURST_32T DMA_NUM_TRANSFER_PER_BURST_32T #define DMA_MGR_NUM_TRANSFER_PER_BURST_64T DMA_NUM_TRANSFER_PER_BURST_64T #define DMA_MGR_NUM_TRANSFER_PER_BURST_128T DMA_NUM_TRANSFER_PER_BURST_128T #define DMA_MGR_NUM_TRANSFER_PER_BURST_256T DMA_NUM_TRANSFER_PER_BURST_256T #define DMA_MGR_NUM_TRANSFER_PER_BURST_512T DMA_NUM_TRANSFER_PER_BURST_512T #define DMA_MGR_NUM_TRANSFER_PER_BURST_1024T DMA_NUM_TRANSFER_PER_BURST_1024T
DMA 握手模式:普通模式(软件触发)或握手模式(硬件触发)。
#define DMA_MGR_HANDSHAKE_MODE_NORMAL DMA_HANDSHAKE_MODE_NORMAL #define DMA_MGR_HANDSHAKE_MODE_HANDSHAKE DMA_HANDSHAKE_MODE_HANDSHAKE
DMA 地址控制模式:递增、递减或固定。
#define DMA_MGR_ADDRESS_CONTROL_INCREMENT DMA_ADDRESS_CONTROL_INCREMENT #define DMA_MGR_ADDRESS_CONTROL_DECREMENT DMA_ADDRESS_CONTROL_DECREMENT #define DMA_MGR_ADDRESS_CONTROL_FIXED DMA_ADDRESS_CONTROL_FIXED
DMA 中断掩码:
#define DMA_MGR_INTERRUPT_MASK_NONE DMA_INTERRUPT_MASK_NONE #define DMA_MGR_INTERRUPT_MASK_ERROR DMA_INTERRUPT_MASK_ERROR #define DMA_MGR_INTERRUPT_MASK_ABORT DMA_INTERRUPT_MASK_ABORT #define DMA_MGR_INTERRUPT_MASK_TC DMA_INTERRUPT_MASK_TERMINAL_COUNT #define DMA_MGR_INTERRUPT_MASK_ALL DMA_INTERRUPT_MASK_ALL
DMA 通道状态:
#define DMA_MGR_CHANNEL_STATUS_ONGOING DMA_CHANNEL_STATUS_ONGOING #define DMA_MGR_CHANNEL_STATUS_ERROR DMA_CHANNEL_STATUS_ERROR #define DMA_MGR_CHANNEL_STATUS_ABORT DMA_CHANNEL_STATUS_ABORT #define DMA_MGR_CHANNEL_STATUS_TC DMA_CHANNEL_STATUS_TC
DMA 通道优先级:
#define DMA_MGR_CHANNEL_PRIORITY_LOW DMA_CHANNEL_PRIORITY_LOW #define DMA_MGR_CHANNEL_PRIORITY_HIGH DMA_CHANNEL_PRIORITY_HIGH
结构体介绍
DMA 资源结构体:封装一个 DMA 通道资源,包含 DMA 实例、通道号及中断号。
typedef struct _dma_resource { DMA_Type *base; /**< 所属的 DMA 实例 */ uint32_t channel; /**< 通道索引 */ int32_t irq_num; /**< DMA 中断号 */ } dma_resource_t;
DMA 通道配置结构体:用于配置 DMA 通道的各个参数。
typedef struct hpm_dma_mgr_chn_conf { bool en_dmamux; /**< DMAMUX 使能 */ uint8_t dmamux_src; /**< DMAMUX 源 */ uint8_t priority; /**< 通道优先级 */ uint8_t src_burst_size; /**< 源端突发大小 */ uint8_t src_mode; /**< 源端工作模式: 0-普通, 1-握手 */ uint8_t dst_mode; /**< 目的端工作模式: 0-普通, 1-握手 */ uint8_t src_width; /**< 源端数据位宽 */ uint8_t dst_width; /**< 目的端数据位宽 */ uint8_t src_addr_ctrl; /**< 源端地址控制: 0-递增, 1-递减, 2-固定 */ uint8_t dst_addr_ctrl; /**< 目的端地址控制: 0-递增, 1-递减, 2-固定 */ uint16_t interrupt_mask; /**< 中断掩码 */ uint32_t src_addr; /**< 源地址 */ uint32_t dst_addr; /**< 目的地址 */ uint32_t linked_ptr; /**< 下一个链表描述符 */ uint32_t size_in_byte; /**< 传输总字节数 */ bool en_infiniteloop; /**< 无限循环传输。注意:仅 DMAV2 支持 */ uint8_t handshake_opt; /**< 握手选项。注意:仅 DMAV2 支持 */ uint8_t burst_opt; /**< 突发大小选项。注意:仅 DMAV2 支持 */ } dma_mgr_chn_conf_t;
DMA 回调函数类型:
typedef void (*dma_mgr_chn_cb_t)(DMA_Type *base, uint32_t channel, void *cb_data_ptr);
15.3. API 调用流程介绍
15.3.1. 初始化 DMA Manager
调用
dma_mgr_initAPI 初始化 DMA Manager 上下文。在进行任何 DMA 操作之前必须执行此步骤。dma_mgr_initAPI 原型:void dma_mgr_init(void);
15.3.2. 请求 DMA 资源
调用
dma_mgr_request_resourceAPI 请求一个可用的 DMA 通道。DMA Manager 会在所有 DMA 实例中自动查找未被占用的通道。dma_mgr_request_resourceAPI 原型:hpm_stat_t dma_mgr_request_resource(dma_resource_t *resource);
返回值:
status_success:DMA 通道分配成功。status_invalid_argument:resource参数为 NULL。status_dma_mgr_no_resource:所有 DMA 通道已被占用。
如果需要从**指定**的 DMA 实例(如 HPM_HDMA 或 HPM_XDMA)请求通道,使用:
hpm_stat_t dma_mgr_request_specified_resource(dma_resource_t *resource, DMA_Type *base);
举例: 请求一个 DMA 通道。
dma_resource_t resource; if (dma_mgr_request_resource(&resource) != status_success) { printf("Failed to request DMA resource\n"); return; } printf("DMA channel allocated: instance=%p, channel=%d\n", resource.base, resource.channel);
15.3.3. 获取缺省通道配置
调用
dma_mgr_get_default_chn_configAPI 为dma_mgr_chn_conf_t结构体填充默认值,之后可根据需要修改各个字段。dma_mgr_get_default_chn_configAPI 原型:void dma_mgr_get_default_chn_config(dma_mgr_chn_conf_t *config);
默认值:
DMAMUX 关闭,DMAMUX 源 = 0
低优先级
源端突发大小 = 1 次传输
源端/目的端工作模式 = 普通模式
源端/目的端位宽 = 字节
源端/目的端地址控制 = 递增
源端/目的端地址 = 0
传输大小 = 0,链表指针 = 0
所有中断屏蔽
无限循环关闭,突发选项 = 标准大小,握手选项 = 单次突发
15.3.4. 配置 DMA 通道
调用
dma_mgr_setup_channelAPI 将配置一次性写入 DMA 通道的硬件寄存器,并可选配置 DMAMUX。dma_mgr_setup_channelAPI 原型:hpm_stat_t dma_mgr_setup_channel(const dma_resource_t *resource, dma_mgr_chn_conf_t *config);
返回值:
status_success:通道配置成功。status_invalid_argument:无效参数。
15.3.5. 注册 DMA 传输完成回调
调用
dma_mgr_install_chn_tc_callbackAPI 为 DMA 通道注册传输完成回调函数,该回调在 DMA 中断服务程序中调用。dma_mgr_install_chn_tc_callbackAPI 原型:hpm_stat_t dma_mgr_install_chn_tc_callback(const dma_resource_t *resource, dma_mgr_chn_cb_t callback, void *user_data);
返回值:
status_success:回调注册成功。status_invalid_argument:无效参数。
类似地,还可以注册半传输完成、错误和终止回调:
hpm_stat_t dma_mgr_install_chn_half_tc_callback(const dma_resource_t *resource, dma_mgr_chn_cb_t callback, void *user_data); hpm_stat_t dma_mgr_install_chn_error_callback(const dma_resource_t *resource, dma_mgr_chn_cb_t callback, void *user_data); hpm_stat_t dma_mgr_install_chn_abort_callback(const dma_resource_t *resource, dma_mgr_chn_cb_t callback, void *user_data);
15.3.6. 使能 DMA 通道并启动传输
调用
dma_mgr_enable_chn_irqAPI 为通道使能所需的中断源。dma_mgr_enable_chn_irqAPI 原型:hpm_stat_t dma_mgr_enable_chn_irq(const dma_resource_t *resource, uint32_t irq_mask);
irq_mask可为DMA_MGR_INTERRUPT_MASK_TC、DMA_MGR_INTERRUPT_MASK_HALF_TC、DMA_MGR_INTERRUPT_MASK_ERROR、DMA_MGR_INTERRUPT_MASK_ABORT的组合。
调用
dma_mgr_enable_dma_irq_with_priorityAPI 使能全局 DMA 中断并设置优先级。dma_mgr_enable_dma_irq_with_priorityAPI 原型:hpm_stat_t dma_mgr_enable_dma_irq_with_priority(const dma_resource_t *resource, uint32_t priority);
调用
dma_mgr_enable_channelAPI 启动 DMA 传输。dma_mgr_enable_channelAPI 原型:hpm_stat_t dma_mgr_enable_channel(const dma_resource_t *resource);
返回值:
status_success:通道使能成功,传输已启动。status_invalid_argument:无效参数。
15.3.7. 完整 DMA 传输示例
以下是一个使用 DMA Manager 组件的典型 DMA 传输流程:
#include "hpm_dma_mgr.h"
#include "hpm_uart_drv.h"
ATTR_PLACE_AT_NONCACHEABLE static char tx_buff[256];
static volatile bool tx_dma_complete;
/* DMA 传输完成回调 */
static void tx_dma_complete_cb(DMA_Type *base, uint32_t channel, void *user_data)
{
(void)base;
(void)channel;
(void)user_data;
tx_dma_complete = true;
}
void uart_dma_send_example(void)
{
dma_mgr_chn_conf_t chn_config;
dma_resource_t tx_dma_resource;
/* 1. 初始化 DMA Manager */
dma_mgr_init();
/* 2. 请求一个 DMA 通道 */
if (dma_mgr_request_resource(&tx_dma_resource) != status_success) {
printf("Failed to request DMA resource\n");
return;
}
/* 3. 获取缺省通道配置 */
dma_mgr_get_default_chn_config(&chn_config);
/* 4. 配置 DMA 通道用于 UART 发送 */
chn_config.src_width = DMA_MGR_TRANSFER_WIDTH_BYTE;
chn_config.dst_width = DMA_MGR_TRANSFER_WIDTH_BYTE;
chn_config.src_addr = core_local_mem_to_sys_address(HPM_CORE0, (uint32_t)tx_buff);
chn_config.dst_addr = (uint32_t)&HPM_UART0->DATA;
chn_config.src_mode = DMA_MGR_HANDSHAKE_MODE_NORMAL;
chn_config.dst_mode = DMA_MGR_HANDSHAKE_MODE_HANDSHAKE;
chn_config.src_addr_ctrl = DMA_MGR_ADDRESS_CONTROL_INCREMENT;
chn_config.dst_addr_ctrl = DMA_MGR_ADDRESS_CONTROL_FIXED;
chn_config.size_in_byte = sizeof(tx_buff);
chn_config.en_dmamux = true;
chn_config.dmamux_src = HPM_DMA_SRC_UART0_TX;
chn_config.interrupt_mask = DMA_MGR_INTERRUPT_MASK_NONE;
dma_mgr_setup_channel(&tx_dma_resource, &chn_config);
/* 5. 注册传输完成回调 */
dma_mgr_install_chn_tc_callback(&tx_dma_resource, tx_dma_complete_cb, NULL);
/* 6. 使能通道中断和全局 DMA 中断 */
dma_mgr_enable_chn_irq(&tx_dma_resource, DMA_MGR_INTERRUPT_MASK_TC);
dma_mgr_enable_dma_irq_with_priority(&tx_dma_resource, 1);
/* 7. 启动 DMA 传输 */
dma_mgr_enable_channel(&tx_dma_resource);
/* 8. 等待传输完成 */
while (!tx_dma_complete) {
}
printf("UART DMA transfer complete\n");
}
15.3.8. 通道控制 API
禁能通道: 停止 DMA 通道传输。
hpm_stat_t dma_mgr_disable_channel(const dma_resource_t *resource);
终止传输: 强制终止 DMA 通道传输。
hpm_stat_t dma_mgr_abort_chn_transfer(const dma_resource_t *resource);
查询传输状态: 查询 DMA 通道的当前状态。
hpm_stat_t dma_mgr_check_chn_transfer_status(const dma_resource_t *resource, uint32_t *status);
获取剩余传输字节: 查询 DMA 通道尚未传输的字节数。
hpm_stat_t dma_mgr_get_chn_remaining_transize(const dma_resource_t *resource, uint32_t *size);
设置传输大小: 修改当前通道的传输大小。
hpm_stat_t dma_mgr_set_chn_transize(const dma_resource_t *resource, uint32_t size);
释放 DMA 资源: 将 DMA 通道归还给管理器。
hpm_stat_t dma_mgr_release_resource(const dma_resource_t *resource);
15.3.9. 逐字段配置 API
如果不想使用一次性配置 dma_mgr_setup_channel,也可以对各字段逐一配置:
dma_mgr_set_chn_src_addr(&resource, src_addr);
dma_mgr_set_chn_dst_addr(&resource, dst_addr);
dma_mgr_set_chn_src_width(&resource, width);
dma_mgr_set_chn_dst_width(&resource, width);
dma_mgr_set_chn_src_work_mode(&resource, mode);
dma_mgr_set_chn_dst_work_mode(&resource, mode);
dma_mgr_set_chn_src_addr_ctrl(&resource, addr_ctrl);
dma_mgr_set_chn_dst_addr_ctrl(&resource, addr_ctrl);
dma_mgr_set_chn_src_burst_size(&resource, burstsize);
dma_mgr_set_chn_transize(&resource, size);
dma_mgr_set_chn_priority(&resource, priority);
dma_mgr_set_chn_infinite_loop_mode(&resource, enable);
dma_mgr_set_chn_handshake_option(&resource, handshake_opt);
Note
DMA Manager 是 SDK 中的基础组件,被以下上层组件使用:
components/spi:SPI 组件通过 DMA Manager 管理 SPI DMA 传输。components/i2c:I2C 组件通过 DMA Manager 管理 I2C DMA 传输。components/serial_nor:Serial NOR 组件通过 DMA Manager 管理 Flash DMA 传输。components/i2s_over_spi:I2S over SPI 组件通过 DMA Manager 管理音频 DMA 传输。