Rigo的胡言乱语录

Rigo's Stupid Blogs Archives

View on GitHub

RP2040 DMA 初始化踩坑

如果你要初始化 RP2040 的 DMA,是需要用

    cfg = dma_channel_get_default_config(ejtag_ctx.tdi_chan);

这样的代码获取一个默认的配置结构体,然后用

    channel_config_set_read_increment(&cfg, true);
    channel_config_set_write_increment(&cfg, false);

这样的代码修改设置,最后再

    dma_channel_configure(ejtag_ctx.tdi_chan, &cfg, ejtag_ctx.tdi_write_addr, NULL, 0, false);

应用到 DMA 通道的。

然后呢,由于自己偷懒不想给所有的通道都写一遍,于是给配置 3 个通道的时候都复用了通道 0 的配置结构体。但是这样就踩上了 RP2040 DMA 里面这个坑:

RP2040 的 DMA 是支持 scatter-gather 的,也就是从分散的多段内存中读取然后把它们变成一个连续的流,或者把一个连续的流分段写入到多段内存中。支持 scatter-gather 的方式是 RP2040 的每个 DMA 通道都有一个 chain_to 字段,可以通过上述结构体配置,具体效果是如果一个通道的传输完成了,那么就立即启动 chain_to 指定的下一通道,这其实就是把对分散的多段内存进行 DMA 的操作自动化了,使之不需要 CPU 进行干预。而禁用一个通道的 chain_to 能力的方式就是向该通道的该字段中写入该通道本身的通道号。那么,

static inline dma_channel_config dma_channel_get_default_config(uint channel) {
    dma_channel_config c = {0};
    channel_config_set_read_increment(&c, true);
    channel_config_set_write_increment(&c, false);
    channel_config_set_dreq(&c, DREQ_FORCE);
    channel_config_set_chain_to(&c, channel);
    channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
    channel_config_set_ring(&c, false, 0);
    channel_config_set_bswap(&c, false);
    channel_config_set_irq_quiet(&c, false);
    channel_config_set_enable(&c, true);
    channel_config_set_sniff_enable(&c, false);
    channel_config_set_high_priority( &c, false);
    return c;
}

dma_channel_get_default_config 函数其实是给出了默认关闭某通道的 chain_to 功能的,但是如果要复用这个配置结构体,那就没办法用了,要对每个通道都设置一遍 chain_to 才行,如果忘了的话,DMA 就会被其他通道意外激活,导致各种各样奇怪的问题。

其实最终解法还是建议老老实实每个通道都获取一下 default config,以免以后又加了新的和通道号强相关的字段。

本案例来自于 Pico Loongson EJTAG此版本