1. 概述
DMA(Direct Memory Access)是YTM32B1M微控制器中的高效数据传输系统,能够在不占用CPU资源的情况下,在内存与内存、内存与外设之间进行高速数据传输。本文档详细解析YTM32B1M SDK中DMA驱动的实现,包括传输配置、通道管理、中断处理等功能。
2. 文件组织结构
2.1 头文件
-
dma_driver.h: DMA驱动的主要头文件,定义了所有公共接口和数据结构
-
dma_irq.h: DMA中断处理相关头文件
-
dma_hw_access.h: DMA硬件访问层头文件
2.2 源文件
-
dma_driver.c: DMA驱动的主要实现文件
-
dma_irq.c: DMA中断服务程序实现
-
dma_hw_access.c: DMA硬件抽象层实现
2.3 架构层次
应用层 ↓ dma_driver.h/c (高层API) ↓ dma_hw_access.h/c (硬件抽象层) ↓ DMA寄存器定义 ↓ DMA硬件控制器
3. 核心数据结构解析
3.1 传输大小枚举
typedef enum { DMA_TRANSFER_SIZE_1B = 0x0U, // 1字节传输 DMA_TRANSFER_SIZE_2B = 0x1U, // 2字节传输 DMA_TRANSFER_SIZE_4B = 0x2U, // 4字节传输 DMA_TRANSFER_SIZE_8B = 0x3U, // 8字节传输 DMA_TRANSFER_SIZE_16B = 0x4U, // 16字节传输 DMA_TRANSFER_SIZE_32B = 0x5U, // 32字节传输 DMA_TRANSFER_SIZE_64B = 0x6U, // 64字节传输 } dma_transfer_size_t;
3.2 传输类型枚举
typedef enum { DMA_TRANSFER_PERIPH2MEM = 0U, // 外设到内存 DMA_TRANSFER_MEM2PERIPH, // 内存到外设 DMA_TRANSFER_MEM2MEM, // 内存到内存 DMA_TRANSFER_PERIPH2PERIPH // 外设到外设 } dma_transfer_type_t;
3.3 地址模运算枚举
typedef enum { DMA_MODULO_OFF = 0U, // 关闭模运算 DMA_MODULO_2B, // 2字节模运算 DMA_MODULO_4B, // 4字节模运算 DMA_MODULO_8B, // 8字节模运算 DMA_MODULO_16B, // 16字节模运算 DMA_MODULO_32B, // 32字节模运算 DMA_MODULO_64B, // 64字节模运算 // ... 更多模运算选项 DMA_MODULO_2GB // 2GB模运算 } dma_modulo_t;
3.4 通道中断类型
typedef enum { DMA_CHN_ERR_INT = 0U, // 错误中断 DMA_CHN_HALF_MAJOR_LOOP_INT, // 半主循环中断 DMA_CHN_MAJOR_LOOP_INT // 完整主循环中断 } dma_channel_interrupt_t;
3.5 通道状态枚举
typedef enum { DMA_CHN_NORMAL = 0U, // 通道正常状态 DMA_CHN_ERROR // 通道错误状态 } dma_chn_status_t;
3.6 DMA用户配置结构
typedef struct { bool haltOnError; // 错误时停止所有通道 } dma_user_config_t;
3.7 通道状态结构
typedef struct { uint8_t virtChn; // 虚拟通道号 dma_callback_t callback; // 回调函数指针 void *parameter; // 回调函数参数 volatile dma_chn_status_t status; // 通道状态 } dma_chn_state_t;
3.8 通道配置结构
typedef struct { uint8_t virtChnConfig; // DMA虚拟通道号 dma_request_source_t source; // DMA请求源选择 dma_callback_t callback; // 通道回调函数 void *callbackParam; // 回调函数参数 } dma_channel_config_t;
3.9 循环传输配置结构
typedef struct { uint32_t triggerLoopIterationCount; // 触发循环迭代次数 bool srcOffsetEnable; // 源地址偏移使能 bool dstOffsetEnable; // 目标地址偏移使能 int32_t triggerLoopOffset; // 触发循环偏移量 bool transferLoopChnLinkEnable; // 传输循环通道链接使能 uint8_t transferLoopChnLinkNumber; // 传输循环链接通道号 bool triggerLoopChnLinkEnable; // 触发循环通道链接使能 uint8_t triggerLoopChnLinkNumber; // 触发循环链接通道号 } dma_loop_transfer_config_t;
3.10 传输配置结构
typedef struct { uint32_t srcAddr; // 源地址 uint32_t destAddr; // 目标地址 dma_transfer_size_t srcTransferSize; // 源传输大小 dma_transfer_size_t destTransferSize; // 目标传输大小 int16_t srcOffset; // 源地址偏移 int16_t destOffset; // 目标地址偏移 int32_t srcLastAddrAdjust; // 源地址最后调整 int32_t destLastAddrAdjust; // 目标地址最后调整 dma_modulo_t srcModulo; // 源地址模运算 dma_modulo_t destModulo; // 目标地址模运算 uint32_t transferLoopByteCount; // 传输循环字节数 bool ramReloadEnable; // RAM重载使能 uint32_t ramReloadNextDescAddr; // 下一个描述符地址 bool interruptEnable; // 中断使能 dma_loop_transfer_config_t *loopTransferConfig; // 循环传输配置指针 } dma_transfer_config_t;
3.11 软件CTS结构
typedef struct { uint32_t SADDR; // 源地址 int16_t SOFF; // 源偏移 uint16_t ATTR; // 传输属性 uint32_t BCNT; // 单次循环传输字节数 int32_t STO; // 源触发循环偏移 uint32_t DADDR; // 目标地址 int16_t DOFF; // 目标偏移 uint16_t TCNT; // 触发循环传输计数 int32_t RAM_LOAD_ADDR; // RAM加载地址 uint16_t CSR; // 控制/状态寄存器 uint16_t TCNTRV; // 触发循环计数器值 } dma_software_cts_t;
3.12 DMA状态结构
typedef struct { dma_chn_state_t *volatile virtChnState[FEATURE_DMA_VIRTUAL_CHANNELS]; // 通道状态指针数组 } dma_state_t;
4. DMA系统架构图
DMA请求源 DMA控制器 目标 ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 外设A │────────────→│ 通道0 │────────────→│ 内存 │ │ (UART) │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 内存 │────────────→│ 通道1 │────────────→│ 外设B │ │ │ │ │ │ (SPI) │ └─────────────┘ └─────────────┘ └─────────────┘ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 内存A │────────────→│ 通道2 │────────────→│ 内存B │ │ │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ ┌─────────────┐ │ 中断控制 │ │ │ └─────────────┘ │ ┌─────────────┐ │ CPU │ │ │ └─────────────┘
5. 核心API接口详解
5.1 DMA模块初始化
status_t DMA_DRV_Init(dma_state_t *dmaState, const dma_user_config_t *userConfig, dma_chn_state_t *const chnStateArray[], const dma_channel_config_t *const chnConfigArray[], uint32_t chnCount);
功能: 初始化DMA模块和指定的通道 参数:
-
dmaState
: DMA状态结构指针 -
userConfig
: 用户配置结构指针 -
chnStateArray[]
: 通道状态结构数组 -
chnConfigArray[]
: 通道配置结构数组 -
chnCount
: 要初始化的通道数量
5.2 DMA模块去初始化
status_t DMA_DRV_Deinit(void);
功能: 去初始化DMA模块,释放所有资源
5.3 通道管理接口
// 初始化DMA通道 status_t DMA_DRV_ChannelInit(dma_chn_state_t *dmaChannelState, const dma_channel_config_t *dmaChannelConfig); // 释放DMA通道 status_t DMA_DRV_ReleaseChannel(uint8_t virtualChannel); // 启动DMA通道 status_t DMA_DRV_StartChannel(uint8_t virtualChannel); // 停止DMA通道 status_t DMA_DRV_StopChannel(uint8_t virtualChannel);
5.4 传输配置接口
// 配置单块传输 status_t DMA_DRV_ConfigSingleBlockTransfer(uint8_t virtualChannel, dma_transfer_type_t type, uint32_t srcAddr, uint32_t destAddr, dma_transfer_size_t transferSize, uint32_t dataBufferSize); // 配置多块传输 status_t DMA_DRV_ConfigMultiBlockTransfer(uint8_t virtualChannel, dma_transfer_type_t type, uint32_t srcAddr, uint32_t destAddr, dma_transfer_size_t transferSize, uint32_t blockSize, uint32_t blockCount, bool disableReqOnCompletion); // 配置循环传输 status_t DMA_DRV_ConfigLoopTransfer(uint8_t virtualChannel, const dma_transfer_config_t *transferConfig); // 配置RAM重载传输 status_t DMA_DRV_ConfigRamReloadTransfer(uint8_t virtualChannel, dma_software_cts_t *scts, dma_transfer_size_t transferSize, uint32_t bytesOnEachRequest, const dma_ram_reload_list_t *srcList, const dma_ram_reload_list_t *destList, uint8_t ctsCount);
5.5 地址和参数配置接口
// 设置源地址 void DMA_DRV_SetSrcAddr(uint8_t virtualChannel, uint32_t address); // 设置源地址偏移 void DMA_DRV_SetSrcOffset(uint8_t virtualChannel, int16_t offset); // 设置目标地址 void DMA_DRV_SetDestAddr(uint8_t virtualChannel, uint32_t address); // 设置目标地址偏移 void DMA_DRV_SetDestOffset(uint8_t virtualChannel, int16_t offset); // 设置传输大小 void DMA_DRV_SetSrcReadChunkSize(uint8_t virtualChannel, dma_transfer_size_t size); void DMA_DRV_SetDestWriteChunkSize(uint8_t virtualChannel, dma_transfer_size_t size);
5.6 状态查询接口
// 获取通道状态 dma_chn_status_t DMA_DRV_GetChannelStatus(uint8_t virtualChannel); // 获取剩余字节数 uint32_t DMA_DRV_GetRemainingMajorIterationsCount(uint8_t virtualChannel); // 检查通道是否完成 bool DMA_DRV_ChannelIsDone(uint8_t virtualChannel);
6. 外设应用描述
6.1 DMA的主要应用场景
-
串口数据传输: UART接收/发送大量数据时使用DMA减少CPU负担
-
SPI/I2C通信: 高速串行通信中的数据缓冲传输
-
ADC数据采集: 连续采样数据的自动存储
-
内存拷贝: 大块内存数据的高效复制
-
外设间数据传输: 不经过CPU的外设间直接数据传输
6.2 典型应用示例
6.2.1 UART接收DMA配置
// DMA状态和配置 dma_state_t dmaState; dma_chn_state_t dmaChannelState; uint8_t rxBuffer[256]; // DMA用户配置 dma_user_config_t dmaUserConfig = { .haltOnError = false }; // DMA通道配置 dma_channel_config_t dmaChannelConfig = { .virtChnConfig = 0, // 使用通道0 .source = DMA_REQ_UART0_RX, // UART0接收请求 .callback = uartRxDmaCallback, // 完成回调 .callbackParam = NULL }; // 初始化DMA DMA_DRV_Init(&dmaState, &dmaUserConfig, &dmaChannelStatePtr, &dmaChannelConfigPtr, 1); // 配置UART接收传输 DMA_DRV_ConfigSingleBlockTransfer( 0, // 通道0 DMA_TRANSFER_PERIPH2MEM, // 外设到内存 (uint32_t)&UART0->DATA, // 源地址:UART数据寄存器 (uint32_t)rxBuffer, // 目标地址:接收缓冲区 DMA_TRANSFER_SIZE_1B, // 1字节传输 sizeof(rxBuffer) // 缓冲区大小 ); // 启动DMA传输 DMA_DRV_StartChannel(0);
6.2.2 内存到内存传输
uint32_t srcData[1024]; uint32_t destData[1024]; // 配置内存到内存传输 DMA_DRV_ConfigSingleBlockTransfer( 1, // 通道1 DMA_TRANSFER_MEM2MEM, // 内存到内存 (uint32_t)srcData, // 源地址 (uint32_t)destData, // 目标地址 DMA_TRANSFER_SIZE_4B, // 4字节传输 sizeof(srcData) // 数据大小 ); // 启动传输 DMA_DRV_StartChannel(1);
6.2.3 循环缓冲区传输
// 循环传输配置 dma_loop_transfer_config_t loopConfig = { .triggerLoopIterationCount = 10, // 10次触发循环 .srcOffsetEnable = true, // 源地址偏移使能 .dstOffsetEnable = false, // 目标地址偏移禁用 .triggerLoopOffset = 0, // 循环完成后偏移量 .transferLoopChnLinkEnable = false, // 传输循环链接禁用 .triggerLoopChnLinkEnable = false // 触发循环链接禁用 }; // 传输配置 dma_transfer_config_t transferConfig = { .srcAddr = (uint32_t)circularBuffer, .destAddr = (uint32_t)&ADC0->DATA, .srcTransferSize = DMA_TRANSFER_SIZE_2B, .destTransferSize = DMA_TRANSFER_SIZE_2B, .srcOffset = 2, // 每次传输后源地址+2 .destOffset = 0, // 目标地址不变 .transferLoopByteCount = 2, // 每次传输2字节 .interruptEnable = true, // 使能中断 .loopTransferConfig = &loopConfig }; // 配置循环传输 DMA_DRV_ConfigLoopTransfer(2, &transferConfig);
6.2.4 DMA中断处理
void uartRxDmaCallback(void *parameter, dma_chn_status_t status) { if (status == DMA_CHN_NORMAL) { // 传输完成,处理接收到的数据 processReceivedData(rxBuffer, sizeof(rxBuffer)); // 重新启动DMA传输 DMA_DRV_StartChannel(0); } else { // 传输错误处理 handleDmaError(); } }
7. 与数据手册的对应关系
根据YTM32B1ME0数据手册中的DMA章节:
-
DMA控制器: 支持多通道并行传输,每个通道独立配置
-
传输控制结构(CTS): 对应SDK中的dma_software_cts_t结构
-
请求路由: 通过DMAMUX模块将外设请求路由到DMA通道
-
中断系统: 支持传输完成、半完成和错误中断
-
地址生成: 支持线性地址、模运算地址等多种寻址模式
8. 最佳实践建议
-
内存对齐: 确保传输数据按照传输大小正确对齐
-
缓存一致性: 在有缓存的系统中注意数据一致性问题
-
中断优先级: 合理设置DMA中断优先级,避免影响实时性
-
错误处理: 实现完善的错误处理机制,包括超时和重试
-
资源管理: 及时释放不用的DMA通道,避免资源浪费
-
性能优化: 选择合适的传输大小和模式以获得最佳性能
9. 调试技巧
-
状态监控: 使用DMA_DRV_GetChannelStatus()监控通道状态
-
进度跟踪: 通过剩余计数器跟踪传输进度
-
寄存器检查: 在调试时检查DMA寄存器配置是否正确
-
中断调试: 确认中断服务程序正确注册和执行
-
时序分析: 使用逻辑分析仪分析DMA请求和响应时序
10. 总结
YTM32B1M的DMA系统提供了强大而灵活的数据传输能力,支持多种传输模式和丰富的配置选项。通过合理使用DMA,可以显著提高系统的数据处理效率,降低CPU负载,实现更好的实时性能。掌握DMA的配置和使用方法对于开发高性能嵌入式应用至关重要。
本文档基于YTM32B1M SDK v1.0,如有更新请参考最新版本文档。