API GUIDE / DMA / HAL
G32S10 DMA HAL 接口
HAL 层接口:hal_dma_init() 是 controller 级、幂等的初始化:打开 PDMA0/PDMA1 时钟并建立 HAL 软件状态。
1. 资源模型与公开类型
1.1 HAL 全局生命周期
hal_dma_init() 是 controller 级、幂等的初始化:打开 PDMA0/PDMA1 时钟并建立 HAL
软件状态。hal_dma_open() 会自动调用它。
DMA controller 是全局共享资源,因此 HAL 没有 hal_dma_deinit():不能由一个模块在不
知道其他 channel 状态的情况下关闭 controller 时钟或撤销全局状态。hal_dma_stop() 归还
活动物理 channel,hal_dma_close() 释放一个 idle handle;controller 初始化会一直保留到
系统复位。上层模块必须自行确保多个使用者不会同时持有同一 PDMA controller
的 IRQ 或硬件资源。
1.2 HAL 运行状态
typedef enum {
HAL_DMA_STATE_UNUSED = 0,
HAL_DMA_STATE_READY,
HAL_DMA_STATE_BUSY,
} hal_dma_state_t;说明:
HAL_DMA_STATE_UNUSED:句柄未打开或已被清理。HAL_DMA_STATE_READY:句柄已打开,可以启动传输。HAL_DMA_STATE_BUSY:句柄正在执行 DMA 传输。
1.3 DMA 总线宽度
typedef enum {
HAL_DMA_BUS_WIDTH_32BIT = 0,
HAL_DMA_BUS_WIDTH_8BIT = 1,
HAL_DMA_BUS_WIDTH_16BIT = 2,
} hal_dma_bus_width_t;说明:
bus_width同时用于源和目的端口宽度字段。外设 DMA 场景下应与外设 FIFO 或数据寄存器访问宽度匹配。
1.4 DMA 回调类型
typedef enum {
HAL_DMA_CALLBACK_DESCRIPTOR_COMPLETE = 0,
HAL_DMA_CALLBACK_TRANSFER_COMPLETE,
HAL_DMA_CALLBACK_ERROR,
HAL_DMA_CALLBACK_COUNT,
} hal_dma_callback_id_t;
typedef void (*hal_dma_callback_t)(hal_dma_handle_t *handle, void *user_ctx);| 参数 | 说明 |
|---|---|
handle | 完成传输的 DMA 句柄。 |
user_ctx | hal_dma_user_data_set() 设置的上下文;未设置时为 NULL。 |
说明:
回调按 descriptor complete、transfer complete、error 三个事件槽注册,使用
hal_dma_callback_register();回调本身不携带 event/status 参数。hal_dma_user_data_set()设置三个事件槽共享的上下文;地址错误和 halt 状态由 HAL 记录日志。回调运行在 PDMA ISR context,不能阻塞。
事件快照使用以下公开类型,通过 hal_dma_get_last_event() 读取:
typedef enum {
HAL_DMA_EVENT_NONE = 0,
HAL_DMA_EVENT_DESCRIPTOR_COMPLETE,
HAL_DMA_EVENT_TRANSFER_COMPLETE,
HAL_DMA_EVENT_ERROR,
} hal_dma_event_t;
typedef struct {
hal_dma_event_t type;
uint32_t descriptor_index;
uint32_t descriptor_advance;
bool descriptor_index_valid;
bool descriptor_events_coalesced;
bool normal_irq_pending;
bool descriptor_irq_pending;
} hal_dma_event_info_t;HAL_DMA_EVENT_NONE 表示当前没有已记录的事件。descriptor_advance 表示根据前后观测到的 descriptor index 计算出的最小推进数量,不保证等于实际完成数量;无法确认有效 index 时为 0,首次有效观测时为 1。其他字段及单 descriptor cyclic 的特殊规则见 4.5.1。
1.5 DMA 打开配置
typedef struct {
hal_dma_request_t request;
hal_dma_bus_width_t bus_width;
uint32_t unit_size;
void *descriptor;
uint32_t descriptor_capacity;
} hal_dma_config_t;| 字段 | 说明 |
|---|---|
request | DMA 请求源,例如内存 auto-request、UART TX/RX、I2C TX/RX、CAN RX、AIC 等。 |
bus_width | 源和目的共享的 DMA 访问宽度。 |
unit_size | 每次 DMA 请求搬运的数据单元大小,单位为字节;HAL_DMA_UNIT_SIZE_AUTO 表示自主传输大小模式。 |
descriptor | descriptor 模式使用的调用者提供 DMA descriptor buffer;只使用非 descriptor 模式时可为 NULL。 |
descriptor_capacity | descriptor buffer 中可用的 descriptor 数量;descriptor 为 NULL 时必须为 0。 |
关键约束:
request必须是 HAL 当前支持的合法请求源。bus_width必须是HAL_DMA_BUS_WIDTH_8BIT/16BIT/32BIT之一。unit_size当前支持HAL_DMA_UNIT_SIZE_AUTO(0)、1/2/4/8/16/32/64或>=128字节;固定单元大小编码时,8会按硬件语义归一到4,>=128归一到128。HAL_DMA_UNIT_SIZE_AUTO要求bus_width为HAL_DMA_BUS_WIDTH_32BIT;传输长度及 linked/cyclic 每段长度必须至少为 8 字节,且为 8 的倍数。descriptor为NULL时,descriptor_capacity必须为 0,此时只能使用hal_dma_start_nondescriptor()。使用
hal_dma_start()、hal_dma_start_linked()或hal_dma_start_cyclic()时,descriptor必须按HAL_DMA_DESCRIPTOR_ALIGN对齐,buffer 大小可用HAL_DMA_DESCRIPTOR_BUFFER_SIZE(count)计算。linked/cyclic 传输还要求本次使用的 descriptor 数组位于同一 4 KiB DOA 编码窗口内;如果希望支持较大的
count,建议 descriptor buffer 使用 4 KiB 对齐。descriptor的生命周期由调用者保证,必须覆盖hal_dma_open()到hal_dma_close()期间的 DMA 使用。
1.6 DMA 句柄
typedef struct hal_dma_handle hal_dma_handle_t;
struct hal_dma_handle {
hal_dma_request_t request;
hal_dma_bus_width_t bus_width;
uint32_t unit_size;
hal_dma_callback_t callbacks[HAL_DMA_CALLBACK_COUNT];
void *user_ctx;
uint32_t controller_id;
uint32_t channel_id;
hal_dma_state_t state;
void *descriptor;
uint32_t descriptor_count;
uint32_t descriptor_capacity;
uintptr_t src_start;
uintptr_t dst_start;
uintptr_t src_end;
uintptr_t dst_end;
uint8_t last_event;
uint8_t last_completed_descriptor;
uint8_t descriptor_event_advance;
uint8_t descriptor_event_flags;
};说明:
调用者提供
hal_dma_handle_t存储空间,并通过hal_dma_open()初始化。request、bus_width、unit_size和 descriptor storage 来自hal_dma_config_t;callback slot 和user_ctx只能通过显式注册接口配置。descriptor指向调用者提供的 descriptor buffer;descriptor_capacity保存该 buffer 容量,descriptor_count保存当前传输实际使用数量。controller_id、channel_id、state、descriptor_count、descriptor_capacity、地址范围以及末尾四个事件状态字段由 HAL 管理。HAL 内部字段不应由调用者直接修改或释放。
2. HAL 与 IRQ 集成接口规范
2.1 HAL 层实现边界
HAL 层公开接口使用
hal_dma_handle_t *handle或const hal_dma_handle_t *handle传递软件句柄。HAL 层使用
IM_HAL_PARAM_CHECK_RETURN做参数合法性检查。HAL 层普通可失败接口返回
error_code_t。DMA HAL 负责 DMA 控制器侧通道、descriptor 填充、启动、停止、IRQ service 和回调。
DMA HAL 不注册、使能或释放 SoC IRQ;调用方必须为使用的 controller 接入对应 IRQ。
DMA HAL 不负责配置 UART、SSI、I2C、CAN、AIC、ADC、PWM 等外设自身的 DMA 使能位、FIFO 阈值或协议状态。
数据 buffer 的 cache flush/invalidate 由调用方或外设驱动负责;HAL 只维护 descriptor cache。
2.2 HAL 初始化、通道生命周期和 IRQ 集成
2.2.1 hal_dma_init
error_code_t hal_dma_init(void);
功能说明:
初始化 PDMA0 和 PDMA1 两个 DMA 控制器,打开控制器时钟并清理 HAL 内部控制器状态。本接口不注册或使能任何 SoC IRQ。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
| 无 | - | - | - |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 初始化成功,或已经初始化。 |
| 其他错误码 | 时钟使能失败。 |
补充说明:
本接口可重复调用;已经初始化后直接返回
IM_OK。hal_dma_open()内部会自动调用本接口。若传输需要回调,调用方还必须接入对应 PDMA SoC IRQ,并在自己的 SoC IRQ entry 中调用
hal_dma_irq_handler()。
2.2.2 hal_dma_irq_handler
void hal_dma_irq_handler(hal_dma_controller_t controller);
功能说明:
服务指定 controller 的 normal 与 descriptor pending DMA interrupt。HAL 读取DIRQP/DIP,定位 channel/handle,维护 event snapshot、通道状态和 callback。
补充说明:
该接口由调用方的 SoC IRQ entry 调用;对于同一 controller,normal 和 descriptor IRQ 都必须路由到它。
HAL 只处理自己管理的 channel;不同调用方不能共享同一个 controller 的 channel 管理权。
callback 处于 PDMA ISR context,不能阻塞;terminal event callback 前 HAL 已释放通道,callback 可以启动下一笔传输。
2.2.3 hal_dma_open
error_code_t hal_dma_open(hal_dma_handle_t *handle, const hal_dma_config_t *config);
功能说明:
初始化 DMA 句柄,保存请求源、总线宽度、单元大小和调用者提供的 descriptor buffer,并根据请求源确定控制器。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
handle | hal_dma_handle_t * | 输出 | DMA 句柄存储空间,不能为 NULL。 |
config | const hal_dma_config_t * | 输入 | DMA 打开配置,不能为 NULL。 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 打开成功。 |
IM_ERR_INVALID_PARAM | handle/config 为空,请求源、总线宽度、单元大小、descriptor buffer 或 descriptor 对齐非法。 |
| 其他错误码 | hal_dma_init() 失败返回的错误码。 |
补充说明:
本接口只初始化句柄,不立即占用硬件通道。
硬件通道在
hal_dma_start*()启动传输时分配。HAL 分配通道时只依据内部软件占用位判断空闲状态;通道会在完成中断、
hal_dma_stop()或启动失败清理路径中释放。上层模块应在
open()成功、第一次start()前使用hal_dma_callback_register()和hal_dma_user_data_set()设置所需事件槽。
2.2.4 hal_dma_close
error_code_t hal_dma_close(hal_dma_handle_t *handle);
功能说明:
关闭一个 DMA 句柄,清理运行时状态。HAL 不释放调用者提供的 descriptor buffer。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
handle | hal_dma_handle_t * | 输入/输出 | DMA 句柄,不能为 NULL。 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 关闭成功。 |
IM_ERR_INVALID_PARAM | handle 为空。 |
IM_ERR_BUSY | 句柄仍处于忙状态。 |
补充说明:
调用
hal_dma_close()前应确认传输已完成,或先调用hal_dma_stop()。关闭后句柄恢复为未使用状态,descriptor buffer 生命周期仍由调用者管理。
2.2.5 hal_dma_callback_register
error_code_t hal_dma_callback_register(hal_dma_handle_t *handle, hal_dma_callback_id_t callback_id, hal_dma_callback_t callback);
功能说明:
注册或更新一个 DMA 事件槽的回调。只能在已打开且处于 READY 状态的句柄上调用;传入 NULL callback 可取消该事件槽的回调。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
handle | hal_dma_handle_t * | 输入/输出 | 已打开的空闲 DMA 句柄,不能为 NULL。 |
callback_id | hal_dma_callback_id_t | 输入 | descriptor complete、transfer complete 或 error 事件槽;不能为 HAL_DMA_CALLBACK_COUNT。 |
callback | hal_dma_callback_t | 输入 | ISR context 中调用的回调,或用于取消注册的 NULL。 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 回调槽更新成功。 |
IM_ERR_INVALID_PARAM | handle 为空或 callback_id 非法。 |
IM_ERR_NOT_READY | 句柄处于 UNUSED 状态。 |
IM_ERR_BUSY | 句柄已打开,但当前不是 READY 状态。 |
2.2.6 hal_dma_user_data_set
error_code_t hal_dma_user_data_set(hal_dma_handle_t *handle, void *user_ctx);
功能说明:
设置三个 DMA 回调槽共享的调用者上下文。只能在已打开且处于 READY 状态的句柄上调用。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
handle | hal_dma_handle_t * | 输入/输出 | 已打开的空闲 DMA 句柄,不能为 NULL。 |
user_ctx | void * | 输入 | 传递给回调的上下文,允许为 NULL。 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 上下文更新成功。 |
IM_ERR_INVALID_PARAM | handle 为空。 |
IM_ERR_NOT_READY | 句柄处于 UNUSED 状态。 |
IM_ERR_BUSY | 句柄已打开,但当前不是 READY 状态。 |
2.3 启动传输
2.3.1 hal_dma_start
error_code_t hal_dma_start(hal_dma_handle_t *handle, void *src, void *dst, uint32_t len);
功能说明:
启动一次普通 DMA 传输。该接口使用单个 8-word descriptor 完成传输,适用于一次性内存到内存、内存到外设或外设到内存传输。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
handle | hal_dma_handle_t * | 输入/输出 | 已通过 hal_dma_open() 打开的 DMA 句柄。 |
src | void * | 输入 | 源地址,不能为 NULL。 |
dst | void * | 输入 | 目的地址,不能为 NULL。 |
len | uint32_t | 输入 | 传输长度,单位为字节。 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 启动成功。 |
IM_ERR_INVALID_PARAM | 句柄、地址、长度、descriptor buffer 或传输参数非法。 |
IM_ERR_BUSY | 无空闲通道或句柄状态不允许启动。 |
补充说明:
len必须大于 0;固定单元大小模式须满足unit_size和bus_width对齐约束,AUTO 模式须至少为 8 字节且为 8 的倍数;计数范围见 7.3。该接口要求
hal_dma_open()时提供有效 descriptor buffer。启动后句柄状态切为
HAL_DMA_STATE_BUSY。完成中断到来后,HAL 将状态切回
HAL_DMA_STATE_READY并调用回调。
2.3.2 hal_dma_start_nondescriptor
error_code_t hal_dma_start_nondescriptor(hal_dma_handle_t *handle, void *src, void *dst, uint32_t len);
功能说明:
启动一次非 descriptor DMA 传输。该接口直接配置通道 DSA/DTA/DTC/DRT/DCM/DSD 寄存器,并通过 DCS_NDES_MASK | DCS_CTE_MASK 启动传输,不使用 descriptor buffer。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
handle | hal_dma_handle_t * | 输入/输出 | 已通过 hal_dma_open() 打开的 DMA 句柄。 |
src | void * | 输入 | 源地址,不能为 NULL。 |
dst | void * | 输入 | 目的地址,不能为 NULL。 |
len | uint32_t | 输入 | 传输长度,单位为字节。 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 启动成功。 |
IM_ERR_INVALID_PARAM | 句柄、地址、长度或传输参数非法。 |
IM_ERR_BUSY | 无空闲通道或句柄状态不允许启动。 |
IM_ERR_UNSUPPORTED | 当前请求源不支持非 descriptor 模式,例如 CAN RX。 |
补充说明:
len必须大于 0;固定单元大小模式须满足unit_size和bus_width对齐约束,AUTO 模式须至少为 8 字节且为 8 的倍数;计数范围见 7.3。该接口不要求
hal_dma_open()时提供 descriptor buffer。当前非 descriptor 模式暂不支持
HAL_DMA_REQUEST_CAN0_RX/HAL_DMA_REQUEST_CAN1_RX。完成中断到来后,HAL 将状态切回
HAL_DMA_STATE_READY并调用回调。
2.3.3 hal_dma_start_linked
error_code_t hal_dma_start_linked(hal_dma_handle_t *handle, void *src, void *dst, uint32_t len, uint32_t count);
功能说明:
启动一次 linked descriptor DMA 传输。HAL 将总长度拆分为 count 个 descriptor,并按顺序链接执行。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
handle | hal_dma_handle_t * | 输入/输出 | 已通过 hal_dma_open() 打开的 DMA 句柄。 |
src | void * | 输入 | 源地址,不能为 NULL。 |
dst | void * | 输入 | 目的地址,不能为 NULL。 |
len | uint32_t | 输入 | 总传输长度,单位为字节。 |
count | uint32_t | 输入 | descriptor 数量。 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 启动成功。 |
IM_ERR_INVALID_PARAM | 参数非法、descriptor buffer 容量不足或跨 4 KiB DOA 窗口、count 为 0、count > 128、len 不能被 count 整除或分段长度不满足约束。 |
IM_ERR_BUSY | 无空闲通道或句柄状态不允许启动。 |
补充说明:
len必须能被count整除。固定单元大小模式下,每段长度必须不小于
unit_size,且能被unit_size整除;AUTO 模式下,每段长度必须至少为 8 字节且为 8 的倍数;计数范围见 7.3。HAL 使用调用者提供的 descriptor buffer,并校验 buffer 容量。
本次使用的 descriptor 数组必须位于同一 4 KiB DOA 编码窗口内,避免
DESC_DTC_DOA8-bit offset wrap;代码使用DESC_DTC_DOA_MASK构造该字段;若不满足该约束,接口返回IM_ERR_INVALID_PARAM。最后一个 descriptor 使能传输完成中断并清除 LINK 位。
2.3.4 hal_dma_start_cyclic
error_code_t hal_dma_start_cyclic(hal_dma_handle_t *handle, void *src, void *dst, uint32_t len, uint32_t count);
功能说明:
启动一次 cyclic descriptor DMA 传输。HAL 将多个 descriptor 首尾相连形成循环链,适用于持续接收或环形缓冲区场景。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
handle | hal_dma_handle_t * | 输入/输出 | 已通过 hal_dma_open() 打开的 DMA 句柄。 |
src | void * | 输入 | 源地址,不能为 NULL。 |
dst | void * | 输入 | 目的地址,不能为 NULL。 |
len | uint32_t | 输入 | 总传输长度,单位为字节。 |
count | uint32_t | 输入 | descriptor 数量。 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 启动成功。 |
IM_ERR_INVALID_PARAM | 参数非法、descriptor buffer 容量不足或跨 4 KiB DOA 窗口、count 为 0、count > 128、len 不能被 count 整除或分段长度不满足约束。 |
IM_ERR_BUSY | 无空闲通道或句柄状态不允许启动。 |
补充说明:
参数约束与
hal_dma_start_linked()相同。cyclic 使用的 descriptor 数组必须与 linked 一样位于同一 4 KiB DOA 编码窗口内;建议使用 4 KiB 对齐的 descriptor buffer。
最后一个 descriptor 的 DESC_DTC_DOA 指回第一个 descriptor,并保持 LINK 位。
cyclic 的每个 descriptor 都启用 TIE。每次 descriptor 完成时,HAL 记录
HAL_DMA_EVENT_DESCRIPTOR_COMPLETE并调用已注册的 descriptor callback,但保持 handle 为HAL_DMA_STATE_BUSY。循环传输通常需要配合
hal_dma_stop_after_current_transfer()或hal_dma_stop()停止。
2.4 停止传输
2.4.1 hal_dma_stop_after_current_transfer
error_code_t hal_dma_stop_after_current_transfer(hal_dma_handle_t *handle, void *mem);
功能说明:
用于源地址递增的 linked/cyclic 传输场景。HAL 根据传入的源侧内存地址定位 descriptor,并清除该 descriptor 的 LINK 位,使 DMA 在完成该 descriptor 后停止。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
handle | hal_dma_handle_t * | 输入/输出 | 正在执行 linked/cyclic 传输的 DMA 句柄。 |
mem | void * | 输入 | 源侧内存地址,用于选择需要停止链接的 descriptor。 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 设置成功。 |
IM_ERR_INVALID_PARAM | handle/mem 为空,或 mem 不在当前源地址范围内。 |
IM_ERR_NOT_READY | 句柄未处于忙状态,或当前不是 linked/cyclic 多 descriptor 传输。 |
补充说明:
该接口只适用于 linked/cyclic 多 descriptor 传输;普通单 descriptor 传输会返回
IM_ERR_NOT_READY。mem使用源侧地址语义,而不是目的地址语义。固定源地址的外设 RX 不适用:此时
src_start == src_end,没有可接受的mem地址,接口返回IM_ERR_INVALID_PARAM;应使用hal_dma_stop()停止。HAL 会将
mem转换为 DMA/物理地址后与handle->src_start/src_end比较。修改 descriptor 后会 flush descriptor cache。
2.4.2 hal_dma_stop
error_code_t hal_dma_stop(hal_dma_handle_t *handle);
功能说明:
停止当前 DMA 传输,释放当前硬件通道记录,并将句柄状态恢复为 READY。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
handle | hal_dma_handle_t * | 输入/输出 | DMA 句柄,不能为 NULL。 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 停止成功,或句柄当前并非忙状态。 |
IM_ERR_INVALID_PARAM | handle 为空。 |
补充说明:
忙状态下会读取当前源/目的地址并更新结束地址记录。
本接口用于强制停止,外设 DMA 场景下外设驱动应先关闭外设侧 DMA 请求。
2.5 事件和当前地址查询
2.5.1 hal_dma_get_last_event
error_code_t hal_dma_get_last_event(const hal_dma_handle_t *handle, hal_dma_event_info_t *event_out);
功能说明:
读取 HAL 在最近一次 DMA callback 前记录的事件。callback 已按事件槽分发;此接口 用于读取 descriptor index、pending snapshot 和诊断信息。
HAL_DMA_EVENT_DESCRIPTOR_COMPLETE 只用于仍在运行的 descriptor 事件,尤其是
cyclic DMA;此时 handle 保持 HAL_DMA_STATE_BUSY。HAL_DMA_EVENT_TRANSFER_COMPLETE和 HAL_DMA_EVENT_ERROR 的 callback 到来前,handle 已转为 HAL_DMA_STATE_READY,
并且 channel_id 已失效。接口在 IRQ 临界区中复制完整 event snapshot;最推荐在
DMA callback 中读取。
一般情况下,descriptor_index 是由 DCS.CDOA、descriptor 物理 4 KiB 窗口基址和 16-byte
offset 编码还原,并且按实际 descriptor 数组起点和 32-byte stride 校验后的 index。
当 descriptor_index_valid 为 false 时,该值为 UINT32_MAX。
单 descriptor cyclic 是例外:HAL 直接报告 descriptor_index=0、descriptor_advance=1、descriptor_index_valid=true,不依赖 CDOA 解码。
descriptor_events_coalesced 仅表示 HAL 能从 index 跳跃观察到事件合并;由于 DIP
是每通道 pending bit 而不是计数器,false 不能证明中断逐个送达。CDOA 无法通过
物理地址/stride 校验时只会使 descriptor_index_valid 为 false,不会被误报为
coalesced。
normal_irq_pending 与 descriptor_irq_pending 是 IRQ handler 入口时的 DIRQP/DIP
快照,可用于验证两个 pending 同时出现时仍只生成一个 callback。
2.5.2 hal_dma_get_src_addr
error_code_t hal_dma_get_src_addr(hal_dma_handle_t *handle, uintptr_t *addr_out);
功能说明:
读取当前 DMA 硬件正在访问的源地址。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
handle | hal_dma_handle_t * | 输入/输出 | DMA 句柄,不能为 NULL。 |
addr_out | uintptr_t * | 输出 | 当前源地址输出指针,不能为 NULL。 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 读取成功。 |
IM_ERR_INVALID_PARAM | 参数为空,或忙状态句柄的控制器/通道号非法。 |
IM_ERR_HARDWARE | 硬件地址在 1024 次重试内始终未落入当前地址范围。 |
补充说明:
忙状态下最多重试 1024 次读取硬件 DSA,读取到当前源地址范围内的地址即返回成功。
非忙状态下返回
handle->src_end。重试耗尽仍未读取到合法地址时返回
IM_ERR_HARDWARE,不写入addr_out;不使用基于时间的超时等待。
2.5.3 hal_dma_get_dst_addr
error_code_t hal_dma_get_dst_addr(hal_dma_handle_t *handle, uintptr_t *addr_out);
功能说明:
读取当前 DMA 硬件正在访问的目的地址。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
handle | hal_dma_handle_t * | 输入/输出 | DMA 句柄,不能为 NULL。 |
addr_out | uintptr_t * | 输出 | 当前目的地址输出指针,不能为 NULL。 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 读取成功。 |
IM_ERR_INVALID_PARAM | 参数为空,或忙状态句柄的控制器/通道号非法。 |
IM_ERR_HARDWARE | 硬件地址在 1024 次重试内始终未落入当前地址范围。 |
补充说明:
忙状态下最多重试 1024 次读取硬件 DTA,读取到当前目的地址范围内的地址即返回成功。
非忙状态下返回
handle->dst_end。重试耗尽仍未读取到合法地址时返回
IM_ERR_HARDWARE,不写入addr_out;不使用基于时间的超时等待。
2.6 调试诊断
2.6.1 hal_dma_dump_regs
error_code_t hal_dma_dump_regs(const hal_dma_handle_t *handle);
功能说明:
打印指定 DMA 句柄对应通道的寄存器信息,用于芯片调试、bring-up 或现场问题诊断。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
handle | const hal_dma_handle_t * | 输入 | DMA 句柄,不能为 NULL。 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | dump 成功。 |
IM_ERR_INVALID_PARAM | handle 为空或句柄内部控制器/通道信息非法。 |
IM_ERR_UNSUPPORTED | 未同时启用 CONFIG_SDK_ENABLE_DEBUG_HELPERS 和 CONFIG_DMA_ENABLE_DEBUG_HELPERS。 |
补充说明:
dump 内容包括通道
DSA/DTA/DTC/DRT/DCS/DCM/DDA/DSD,以及SACIDX0/1、DACIDX0/1。接口声明始终存在,功能体由 debug helper 双开关控制。
2.6.2 hal_dma_dump_desc
error_code_t hal_dma_dump_desc(const hal_dma_handle_t *handle);
功能说明:
打印指定 DMA 句柄当前持有的 descriptor 信息,用于检查 descriptor 链、地址、传输长度和控制位配置。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
handle | const hal_dma_handle_t * | 输入 | DMA 句柄,不能为 NULL。 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | dump 成功。 |
IM_ERR_INVALID_PARAM | handle 为空或句柄未持有 descriptor。 |
IM_ERR_NOT_READY | 当前没有已配置的有效传输 descriptor。 |
IM_ERR_UNSUPPORTED | 未同时启用 CONFIG_SDK_ENABLE_DEBUG_HELPERS 和 CONFIG_DMA_ENABLE_DEBUG_HELPERS。 |
补充说明:
dump 内容包括每个 descriptor 的
dcm/dsa/dta/dtc/sd/drt。descriptor_count为 0 时,表示当前没有已配置的有效传输 descriptor,dump 返回IM_ERR_NOT_READY。
3. 当前实现约束与行为说明
3.1 Descriptor 模式
8-word descriptor 模式:单次传输使用 1 个 descriptor,linked/cyclic 传输使用 descriptor 数组。
非 descriptor 模式:单次传输直接配置通道寄存器,不使用 descriptor buffer。
descriptor 路径和非 descriptor 路径分别在
hal_dma_init_desc()与hal_dma_init_nondescriptor_regs()中构造DCM和 transfer count。linked/cyclic descriptor 数组必须位于同一 4 KiB DOA 编码窗口内,避免
DESC_DTC_DOA8-bit offset wrap;代码使用DESC_DTC_DOA_MASK构造该字段。
3.2 CAN RX SACIDX 行为
HAL_DMA_REQUEST_CAN0_RX使用SACIDX0。HAL_DMA_REQUEST_CAN1_RX使用SACIDX1。HAL 只在 linked/cyclic CAN RX 路径配置 SACIDX。
普通 UART/SPI/I2C/ADC/AIC RX 不使用 SACIDX。
4. 典型用法
本节给出 HAL 的高级 bring-up/reference 用法。直接使用 HAL 的调用方必须确定实际使用
controller 的 normal/DIP IRQ 对的唯一 owner,并在自己的 SoC IRQ entry 中调用hal_dma_irq_handler();不得重复注册同一 IRQ。
4.1 外设 TX DMA
以外设发送为例,数据流通常为“内存 -> 外设 FIFO/数据寄存器”。外设驱动需要配置:
src:发送缓冲区地址。dst:外设 TX FIFO 或 TX 数据寄存器地址。request:对应外设 TX DMA 请求源,例如HAL_DMA_REQUEST_UART1_TX。外设侧寄存器:TX FIFO 阈值、TX DMA 请求使能位、必要的错误中断策略。
外设驱动对象中应为每个可能并发使用的 DMA handle 准备独立 descriptor buffer:
typedef struct {
hal_dma_handle_t tx_dma;
hal_dma_handle_t rx_dma;
uint8_t tx_dma_desc[HAL_DMA_DESCRIPTOR_BUFFER_SIZE(HAL_DMA_DESCRIPTOR_COUNT)]
__attribute__((aligned(HAL_DMA_DESCRIPTOR_ALIGN)));
uint8_t rx_dma_desc[HAL_DMA_DESCRIPTOR_BUFFER_SIZE(HAL_DMA_DESCRIPTOR_COUNT)]
__attribute__((aligned(HAL_DMA_DESCRIPTOR_ALIGN)));
} uart_driver_t;hal_dma_config_t dma_config = {
.request = HAL_DMA_REQUEST_UART1_TX,
.bus_width = HAL_DMA_BUS_WIDTH_8BIT,
.unit_size = 1U,
.descriptor = uart->tx_dma_desc,
.descriptor_capacity = HAL_DMA_DESCRIPTOR_COUNT,
};
hal_dma_open(&uart->tx_dma, &dma_config);
hal_dma_user_data_set(&uart->tx_dma, uart);
hal_dma_callback_register(&uart->tx_dma,
HAL_DMA_CALLBACK_TRANSFER_COMPLETE,
uart_dma_tx_done);
hal_dma_callback_register(&uart->tx_dma, HAL_DMA_CALLBACK_ERROR, uart_dma_tx_done);
/* UART 驱动配置 TX FIFO 阈值,并打开 UART TX DMA 请求。 */
hal_dma_start(&uart->tx_dma,
tx_buffer,
(void *)uart_tx_fifo_addr,
tx_len);说明:
uart_tx_fifo_addr应由 UART 驱动根据 UART 实例计算或定义。uart->tx_dma_desc应按HAL_DMA_DESCRIPTOR_BUFFER_SIZE(HAL_DMA_DESCRIPTOR_COUNT)分配,并按HAL_DMA_DESCRIPTOR_ALIGN对齐。DMA HAL 不应猜测或配置 UART 寄存器。
4.2 外设 RX DMA
以外设接收为例,数据流通常为“外设 FIFO/数据寄存器 -> 内存”。外设驱动需要配置:
src:外设 RX FIFO 或 RX 数据寄存器地址。dst:接收缓冲区地址。request:对应外设 RX DMA 请求源,例如HAL_DMA_REQUEST_UART1_RX。外设侧寄存器:RX FIFO 阈值、RX DMA 请求使能位、超时/错误中断策略。
hal_dma_config_t dma_config = {
.request = HAL_DMA_REQUEST_UART1_RX,
.bus_width = HAL_DMA_BUS_WIDTH_8BIT,
.unit_size = 1U,
.descriptor = uart->rx_dma_desc,
.descriptor_capacity = HAL_DMA_DESCRIPTOR_COUNT,
};
hal_dma_open(&uart->rx_dma, &dma_config);
hal_dma_user_data_set(&uart->rx_dma, uart);
hal_dma_callback_register(&uart->rx_dma,
HAL_DMA_CALLBACK_TRANSFER_COMPLETE,
uart_dma_rx_done);
hal_dma_callback_register(&uart->rx_dma, HAL_DMA_CALLBACK_ERROR, uart_dma_rx_done);
/* UART 驱动配置 RX FIFO 阈值,并打开 UART RX DMA 请求。 */
hal_dma_start(&uart->rx_dma,
(void *)uart_rx_fifo_addr,
rx_buffer,
rx_len);说明:
DMA 完成只表示 FIFO/寄存器与内存之间的数据搬运完成,不一定表示外设协议层完全结束。
uart->rx_dma_desc应按HAL_DMA_DESCRIPTOR_BUFFER_SIZE(HAL_DMA_DESCRIPTOR_COUNT)分配,并按HAL_DMA_DESCRIPTOR_ALIGN对齐。外设驱动需要在 DMA 完成后检查协议状态和错误状态。