ARM32开发——(十五)DMA内存到内存

1. 重点

  • 理解DMA数据传输过程
  • 掌握DMA的初始化流程
  • 理解源和目标的配置
  • 理解数据传输特点
  • 能够动态配置源数据
  • 能够实现DMA中断

2. 主要内容

2.1 需求

#define ARR_LEN 1024
char src[ARR_LEN] = "hello\0";
char dst[ARR_LEN] = {0};

将src这个数组的值,赋值到dst这个数组中,不可以采取直接赋值的方式,需要通过DMA将数据进行传递。

2.2 数据交互流程

ARM32开发——(十五)DMA内存到内存_第1张图片

  • CPU配置好DMA
  • CPU通知DMA干活
  • DMA请求源数据
  • DMA获取源数据
  • DMA将获取的源数据交给目标

2.3 开发流程

2.3.1 依赖引入

添加标准库中的gd32f4xx_dma.c文件

2.3.2 DMA初始
/***************** DMA m2m *******************/
// 时钟
rcu_periph_clock_enable(RCU_DMA1);
// 重置dma
dma_deinit(DMA1, DMA_CH0);

 dma 配置
dma_single_data_parameter_struct dsdps;
dma_single_data_para_struct_init(&dsdps);
// 方向
dsdps.direction = DMA_MEMORY_TO_MEMORY;	
// 外设(作为内存到内存拷贝的源)
dsdps.periph_addr = (uint32_t)src;
dsdps.periph_inc = DMA_PERIPH_INCREASE_ENABLE;
// 内存
dsdps.memory0_addr = (uint32_t)dst;
dsdps.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
// 数据长度
dsdps.number = ARR_LEN;
// 数据宽度
dsdps.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
dma_single_data_mode_init(DMA1, DMA_CH0, &dsdps);
  • 配置时钟
  • 初始化dma通道
2.3.3 DMA传输请求
dma_channel_enable(DMA1, DMA_CH0);
while(RESET == dma_flag_get(DMA1, DMA_CH0, DMA_FLAG_FTF));
dma_flag_clear(DMA1, DMA_CH0, DMA_FLAG_FTF);
  • dma_channel_enable: 请求dma数据传输
  • DMA_FLAG_FTF:为传输完成标记

3. 数据传输理解

ARM32开发——(十五)DMA内存到内存_第2张图片

3.1 源地址和目标地址

源地址和目标地址都是提前配置好的,当传输时,就会从源地址将数据传递给目标地址。

3.2 自增长

每传输一个字节数据,地址是否需要增长。这里包含了源地址是否需要增长,也包含了目标地址是否需要增长。

3.3 数据宽度

数据宽度表示一次传递多上个bit

3.4 数据长度

传输了多少个数据宽度的数据。

4. 源的数据未定

这里主要指的是在配置DMA的源和目标的时候,我们无法给出一个具体的源地址和源的数据长度。

ARM32开发——(十五)DMA内存到内存_第3张图片

通常我们要解决的也是初始化和数据传输请求。

4.1 DMA初始化
uint32_t dmax = DMA1;
uint32_t dmax_rcu = RCU_DMA1;
uint32_t dmax_ch = DMA_CH0;

uint32_t dmax_dirction = DMA_MEMORY_TO_MEMORY;

//uint32_t dmax_src = (uint32_t)src;
uint32_t dmax_src_inc = DMA_PERIPH_INCREASE_ENABLE;
uint32_t dmax_src_width = DMA_PERIPH_WIDTH_8BIT;
//uint32_t dmax_src_len = ARR_LEN;

uint32_t dmax_dst = (uint32_t)dst;
uint32_t dmax_dst_inc = DMA_MEMORY_INCREASE_ENABLE;
/***************** DMA m2m *******************/
// 时钟
rcu_periph_clock_enable(dmax_rcu);
// 重置dma
dma_deinit(dmax, dmax_ch);

 dma 配置
dma_single_data_parameter_struct dsdps;
dma_single_data_para_struct_init(&dsdps);
// 方向
dsdps.direction = dmax_dirction;	
// 内存
dsdps.memory0_addr = dmax_dst;
dsdps.memory_inc = dmax_dst_inc;
// 外设
//dsdps.periph_addr = dmax_src;
dsdps.periph_inc = dmax_src_inc;
// 数据长度
//dsdps.number = dmax_src_len;
// 数据宽度
dsdps.periph_memory_width = dmax_src_width;
dma_single_data_mode_init(dmax, dmax_ch, &dsdps);

初始化时,不再初始化源地址和要传输的长度。

4.2 DMA数据传输请求
dma_periph_address_config(DMA1, DMA_CH0,(uint32_t)data);
dma_transfer_number_config(DMA1, DMA_CH0, len);

dma_channel_enable(DMA1, DMA_CH0);
while(RESET == dma_flag_get(DMA1, DMA_CH0, DMA_FLAG_FTF));
dma_flag_clear(DMA1, DMA_CH0, DMA_FLAG_FTF);

请求数据传输前,进行动态配置:

  • 在此需要注意的是,要分清楚当前是否是调用源的地址配置完整代码DMA中断

通常,dma数据传输完成,我们也是可以知道的。可以通过中断的方式获取。

4.3 开启中断
// 中断配置
nvic_irq_enable(DMA1_Channel0_IRQn, 2, 2);
// 中断使能
dma_interrupt_enable(DMA1, DMA_CH0, DMA_INT_FTF);
// 开启DMA通道
dma_channel_enable(DMA1, DMA_CH0);
  • DMA_CHXCTL_FTFIE: 表示中断数据传输完成标记
4.4 中断函数实现
void DMA1_Channel0_IRQHandler() {
    // 判断中断标记
    if(SET == dma_interrupt_flag_get(DMA1, DMA_CH0, DMA_INT_FLAG_FTF)) {
        printf("INT dst: %s\n", dst);

        // 清理标记位
        dma_interrupt_flag_clear(DMA1, DMA_CH0, DMA_INT_FLAG_FTF);
    }
}
  • DMA_INT_FLAG_FTF: 表示中断传输完成标记,需要在标记触发时做业务逻辑
  • 同时,需要配合寄存器,清除标记

你可能感兴趣的:(ARM32开发,嵌入式硬件,arm开发)