国产MCU学习Day21——CW32F030C8T6 DMA技术详解与应用指南

每日更新教程,评论区答疑解惑,小白也能变大神!"

目录

一.直接内存访问(DMA)

二.寄存器列表

三.案例-通过软件触发DMA方式实现内存到内存的DMA传输

预处理器定义与全局变量

内存操作函数

错误处理与DMA中断

NVIC配置

主函数

一.直接内存访问(DMA)

1.1 概述

  • CW32x030 支持直接内存访问(DMA),无需 CPU 干预,即可实现外设和存储器之间、外设和外设之间、存储器 和存储器之间的高速数据传输。DMA 控制器内部的优先级仲裁器,可实现 DMA 和 CPU 对外设总线控制权的仲裁, 以及多 DMA 通道之间的调度执行。

1.2 主要特性

  • 5 条独立 DMA 通道
  • 3 种数据传输宽度:8bit、16bit、32bit
  • 4 种传输模式:软件 BLOCK、软件 BULK、硬件 BLOCK、硬件 BULK
  • 源地址及目的地址类型:外设、存储器
  • 地址增量方式:固定、自增 ● 待传输数据块数量:1 ~ 65535
  • 寻址范围:0x0000 0000 ~ 0xFFFF FFFF

1.3 功能框图

  • DMA 控制器的 AHB 总线通过总线矩阵和外设的 AHB 总线相连,实现 DMA 与外设间的数据互联,包括 FLASH、 SRAM、CRC、GPIO 以及各 APB 设备等。DMA 控制器的功能框图如下图所示:
  • DMA 控制器
  • 5 条独立 DMA 通道,通道优先级和通道号绑定,通道号越小优先级越高,通道号越大优先级越低。
  • Bus Matrix 总线矩阵
  • CPU 系统总线和 DMA 总线接口均连接到总线矩阵上,通过总线矩阵切换和外设的连接。当 CPU 与 DMA 访 问不同的 AHB 总线设备或 AHB 到 APB 桥时,数据传输可以同时进行;当 CPU 和 DMA 同时访问同一个 AHB 设备(AHB 到 APB 桥接器下不同设备也被认为是同一 AHB 设备)时,CPU 的优先级高于 DMA。
  • AHB to APB 桥接器
  • DMA 可以通过总线矩阵及 AHB 到 APB 桥接器实现对 APB 设备的访问。同一个 AHB 到 APB 桥接器下的所有 APB 设备共享同一 AHB 总线,因此被看作是同一个 AHB 设备。当 CPU 和 DMA 同时访问同一 AHB 到 APB 桥接器下的 APB 设备(无论是访问同一个 APB 设备还是不同的 APB 设备)时,CPU 的优先级高于 DMA。

1.4 DMA 传输模式

  • 支持 4 种传输模式:软件触发 BLOCK 传输模式,软件触发 BULK 传输模式,硬件触发 BLOCK 传输模式, 硬件触发 BULK 传输模式。
  • 软件和硬件触发 部分外设支持硬件 DMA,当它们被配置为 DMA 通道的触发源时,可以产生 DMA 请求(DMA request),硬 件触发启动 DMA 传输,无需 CPU 参与。不支持硬件 DMA 的外设,只能配置为软件触发启动 DMA 传输。 触发方式由 DMA 触发源控制寄存器 DMA_TRIGy(y 是 DMA 通道号,y=1~5,下同)的 TYPE 位域来控制, TYPE 为 0 为软件触发方式,TYPE 为 1 则为硬件触发方式。
  • BLOCK 和 BULK 传输模式 BLOCK 和 BULK 传输模式的区别是,在传输过程中是否允许被更高优先级的传输请求(高优先级 DMA 或 CPU)打断。DMA 控制及状态寄存器 DMA_CSRy 的 TRANS 位域为 1 为 BLOCK 传输模式,为 0 则为 BULK 传输模式。 BULK 传输模式时,会一次性完成所有数据的传输。 数据传输过程中不会插入传输间隙,传输过程也不会被 CPU 或者其它高优先级 DMA 通道传输请求打断。 BLOCK 传输模式时,每传输完成 1 个数据块后会插入一个传输间隙,在该传输间隙内,由仲裁器进行传输 优先级仲裁。如果有 CPU 或更高优先级的 DMA 通道要访问当前 DMA 通道所占用的外设,仲裁器会释放当 前 DMA 通道对该设备的控制权,让 CPU 或者更高优先级的 DMA 通道优先访问,等完成后再继续之前被打 断的 DMA 通道传输。
  • DMA 传输模式,如下表所示:

1.5 DMA 传输过程

  • 一次完整的 DMA 传输过程,用户需做以下设置:
  • 步骤 1:设置传输模式,DMA_CSRy.TRANS 设置 1 为 BLOCK 传输模式,设置 0 则为 BULK 传输模式;
  • 步骤 2:设置触发方式,DMA_TRIGy.TYPE 设置 0 为软件触发,设置 1 则为硬件触发方式;
  • 步骤 3:配置 DMA 通道数据传输位宽,DMA_CSRy.SIZE,选择 8、16、32bit;
  • 步骤 4:配置 DMA 通道传输数据块数量,DMA_CNTy.CNT,有效范围 1 ~ 65535;
  • 步骤 5:配置 DMA 通道传输数据块大小,DMA_CNTy.REPEAT,请写入 1;
  • 步骤 6:配置 DMA 传输源地址,DMA_SRCADDRy;
  • 步骤 7:配置 DMA 传输目的地址,DMA_DSTADDRy;
  • 步骤 8:配置 DMA 源地址增量方式,DMA_CSRy.SRCINC,固定或自动增加;
  • 步骤 9:配置 DMA 目的地址增量方式,DMA_CSRy.DSTINC,固定或自动增加;
  • 步骤 10:设置 DMA_CSRy.EN 为 1,使能对应的 DMA 通道;
  • 步骤 11:对于软件触发 DMA 传输,设置 DMA_TRIGy.SOFTSRC 启动 DMA 传输,对于硬件触发 DMA 传输不需设置, 而只需正确设置用于启动 DMA 的外设。

1.6 软件触发 BLOCK 传输模式

  • 设置 DMA_TRIGy.SOFTSRC 为 1,将立即触发 DMA 通道进行数据传输。
  • DMA 通道每传输完成 1 个数据块,DMA 插入一个传输间隙,仲裁器在该传输间隙内进行传输优先级仲裁。如果 在该传输间隙内,有 CPU 或更高优先级的 DMA 请求要访问当前 DMA 通道所占用的 AHB 设备,仲裁器会释放当 前 DMA 通道对该设备的控制权,让 CPU 或者更高优先级的 DMA 通道优先访问;当 CPU 或更高优先级的 DMA 通道释放设备访问权后,本 DMA 通道未完成的数据传输将继续进行。
  • 传输完成后 DMA_TRIGy.SOFTSRC 自动清零。
  • 软件触发 BLOCK 传输模式,传输 3 个数据位宽为 16bit 的数据(DMA_CNTy.REPEAT = 1,DMA_CNTy.CNT = 3), 其中,SA 代表源地址,DA 代表目标地址,源地址和目的地址均为地址自增模式,传输过程如下图所示:

1.7软件触发 BULK 传输模式

  • 设置 DMA_TRIGy.SOFTSRC 为 1,将立即触发 DMA 进行数据传输,DMA 通道传输完 DMA_CNTy.CNT 个数据块 后停止传输并释放外设占用权,传输完成后 DMA_TRIGy.SOFTSRC 自动清零。
  • 软件触发 BULK 传输模式没有传输间隙,在 DMA_CNTy.CNT 个数据块传输完成之前,该 DMA 通道将一直占用外 设,CPU 或更高优先级的 DMA 通道只能等待该 DMA 传输完成后才能访问该外设。 在用户应用中,建议每次不要传输太多的数据,否则将可能造成 CPU 在一段时间内完全不能访问被该 DMA 通道 占用的外设。
  • 软件触发 BULK 传输模式,传输 3 个数据位宽为 16bit 的数据(DMA_CNTy.REPEAT = 1,DMA_CNTy.CNT = 3), 其中,SA 代表源地址,DA 代表目标地址,源地址和目的地址均为地址自增模式,传输过程如下图所示:

1.7硬件触发 BLOCK 传输模式

  • DMA 通道对应的外设产生硬件触发请求信号时,DMA 会传输 1 个数据块,然后等待下一次硬件触发请求信号, 直到 DMA 控制器中配置的数据量全部传输完毕。DMA 通道支持的硬件触发请求信号,请参考触发源控制寄存器 DMA_TRIGy 的 HARDSRC 位域。

二.寄存器列表

三.案例-通过软件触发DMA方式实现内存到内存的DMA传输

预处理器定义与全局变量

#define BUFFER_SIZE 32U  // 定义缓冲区大小为32个32位无符号整数
uint32_t aSRC_Const_Buffer[BUFFER_SIZE] =  // 源数据缓冲区(常量数据)
{
    0x01020304, 0x05060708, 0x090A0B0C, 0x0D0E0F10,
    0x11121314, 0x15161718, 0x191A1B1C, 0x1D1E1F20,
    0x21222324, 0x25262728, 0x292A2B2C, 0x2D2E2F30,
    0x31323334, 0x35363738, 0x393A3B3C, 0x3D3E3F40,
    0x41424344, 0x45464748, 0x494A4B4C, 0x4D4E4F50,
    0x51525354, 0x55565758, 0x595A5B5C, 0x5D5E5F60,
    0x61626364, 0x65666768, 0x696A6B6C, 0x6D6E6F70,
    0x71727374, 0x75767778, 0x797A7B7C, 0x7D7E7F80
};

uint32_t aDST_Buffer[BUFFER_SIZE];  // 目标数据缓冲区(用于DMA传输)
uint8_t TransOverFlag;  // DMA传输完成标志位

内存操作函数

/**
 * @brief 比较两段内存内容
 * @param mem1 内存区域1指针
 * @param mem2 内存区域2指针
 * @param bytesize 比较的字节数
 * @return 0表示相同,-1表示不同或参数错误
 */
int32_t myMemcmp(uint8_t *mem1, uint8_t *mem2, uint32_t bytesize)
{
    int i = 0;
    uint8_t *p = mem1;
    uint8_t *q = mem2;
    if (p == NULL || q == NULL)  // 空指针检查
    {
        return -1;
    }
    for (i = 0; i < bytesize; i++, p++, q++)  // 逐字节比较
    {
        if (*p != *q)
        {
            return -1;
        }
    }
    return 0;
}

/**
 * @brief 清零指定内存区域
 * @param buf 待清零内存指针
 * @param size 清零的字节数
 */
void ZeroMemory(uint8_t *buf, uint32_t size)
{
    uint32_t i = 0;
    for (i = 0; i < size; i ++)
    {
        *buf = 0x0;
    }
}

错误处理与DMA中断

/**
 * @brief 错误处理函数(死循环)
 */
void Error_Handle()
{
    while (1);  // 进入死循环表示错误状态
}

/**
 * @brief DMA通道1中断服务函数
 * 处理传输完成和传输错误中断
 */
void DMA_CHANNEL1_IRQ_FUNCTION(void)
{
    if (DMA_GetITStatus(DMA_IT_TC1))  // 传输完成中断
    {
        DMA_ClearITPendingBit(DMA_IT_TC1);  // 清除中断标志
        TransOverFlag = 1;  // 设置传输完成标志
    }
    if (DMA_GetITStatus(DMA_IT_TE1))  // 传输错误中断
    {
        DMA_ClearITPendingBit(DMA_IT_TE1);  // 清除中断标志
        Error_Handle();  // 进入错误处理
    }
}

NVIC配置

/**
 * @brief NVIC嵌套向量中断控制器配置
 * 配置DMA通道1的中断
 */
void NVIC_Configuration(void)
{
    __disable_irq();  // 全局禁用中断
    NVIC_ClearPendingIRQ(DMACH1_IRQn);  // 清除DMA通道1中断挂起位
    NVIC_EnableIRQ(DMACH1_IRQn);  // 使能DMA通道1中断
    __enable_irq();  // 全局启用中断
}

主函数

/**
 * @brief 主函数
 * 初始化硬件并启动DMA传输
 * @return 程序返回值(未使用)
 */
int32_t main(void)
{
    DMA_InitTypeDef   DMA_InitStruct;
    // 启用DMA和GPIOB的AHB总线时钟
    RCC_AHBPeriphClk_Enable(RCC_AHB_PERIPH_DMA | RCC_AHB_PERIPH_GPIOB, ENABLE);
    
    // 配置GPIOB的8/9引脚为推挽输出
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.Pins = GPIO_PIN_8 | GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
    GPIO_Init(CW_GPIOB, &GPIO_InitStruct);
    PB08_SETLOW();
    PB09_SETLOW();
    
    TransOverFlag = 0;  // 初始化传输完成标志
    DMA_StructInit(&DMA_InitStruct);  // DMA初始化结构体默认值
    ZeroMemory((uint8_t *)&aDST_Buffer[0], sizeof(aDST_Buffer));  // 清空目标缓冲区
    
    // 配置DMA参数
    DMA_InitStruct.DMA_Mode = DMA_MODE_BLOCK;  // 块传输模式
    DMA_InitStruct.DMA_TransferWidth = DMA_TRANSFER_WIDTH_32BIT;  // 32位传输宽度
    DMA_InitStruct.DMA_SrcInc = DMA_SrcAddress_Increase;  // 源地址递增
    DMA_InitStruct.DMA_DstInc = DMA_DstAddress_Increase;  // 目标地址递增
    DMA_InitStruct.DMA_TransferCnt = BUFFER_SIZE;  // 传输数据量
    DMA_InitStruct.DMA_SrcAddress = (uint32_t)&aSRC_Const_Buffer[0];  // 源地址
    DMA_InitStruct.DMA_DstAddress = (uint32_t)&aDST_Buffer[0];  // 目标地址
    DMA_InitStruct.TrigMode = DMA_SWTrig;  // 软件触发模式
    
    DMA_Init(CW_DMACHANNEL1, &DMA_InitStruct);  // 初始化DMA通道1
    DMA_ClearITPendingBit(DMA_IT_ALL);  // 清除所有DMA中断标志
    DMA_ITConfig(CW_DMACHANNEL1, DMA_IT_TC | DMA_IT_TE, ENABLE);  // 使能传输完成和错误中断
    NVIC_Configuration();  // 配置NVIC中断
    DMA_Cmd(CW_DMACHANNEL1, ENABLE);  // 使能DMA通道1
    DMA_SWTrigCmd(CW_DMACHANNEL1);  // 软件触发DMA传输
    
    // 主循环
    while (1)
    {
        if (TransOverFlag == 1)  // 传输完成
        {
            TransOverFlag = 0;
            // 比较源和目标缓冲区内容
            if (myMemcmp((uint8_t *)&aDST_Buffer[0], (uint8_t *)&aSRC_Const_Buffer[0], BUFFER_SIZE) == -1)
            {
                Error_Handle();  // 内容不一致则进入错误处理
            }
            else  // 传输验证成功
            {
                while (1)  // 进入指示成功的闪烁循环
                {
                    PB08_TOG();  // 切换PB8状态
                    PB09_TOG();  // 切换PB9状态
                    FirmwareDelay(1000000);  // 延时
                }
            }
        }
        else  // 传输未完成时的等待状态
        {
            PB08_TOG();  // 切换PB8状态
            PB09_TOG();  // 切换PB9状态
            FirmwareDelay(100000);  // 较短延时
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值