Demo:DMA

来自Bouffalo Lab Docs
跳转到导航 跳转到搜索

Demo介绍:

这部分代码保存在SDK下的:/examples/peripherals/dma/ 中。展示了不同模式下的dma运行情况,测试dma外设的功能完整性。

在该SDK中,dma的搬运行为被构建成一个LLI,系统根据LLI不断向下搬运直到最终节点。

dma_normal:

该部分代码测试了内存到内存的基本dma运送。共搬运数据三组,字节对齐为32位。搬运模式为一次性搬运。

数据字节对齐式初始化:

这里这里以32位对齐是为了配合后面DMA读取。

static __attribute((aligned(32))) uint8_t src1_buffer[DMA_BUFFER_LENGTH];
static __attribute((aligned(32))) uint8_t src2_buffer[DMA_BUFFER_LENGTH];
static __attribute((aligned(32))) uint8_t src3_buffer[DMA_BUFFER_LENGTH];

static __attribute((aligned(32))) uint8_t dst1_buffer[DMA_BUFFER_LENGTH];
static __attribute((aligned(32))) uint8_t dst2_buffer[DMA_BUFFER_LENGTH];
static __attribute((aligned(32))) uint8_t dst3_buffer[DMA_BUFFER_LENGTH];

DMA配置解析:

方向配置为:内存到内存;

优先级都配置为: 0,是最高优先级;

目标和源数据指针都为递增;

突发传输次数配置都为 1;

数据宽度都为 32位置。

    config.direction = DMA_MEMORY_TO_MEMORY;
    config.src_req = 0;
    config.dst_req = 0;
    config.src_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
    config.dst_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
    config.src_burst_count = DMA_BURST_INCR1;
    config.dst_burst_count = DMA_BURST_INCR1;
    config.src_width = DMA_DATA_WIDTH_32BIT;
    config.dst_width = DMA_DATA_WIDTH_32BIT;

突发次数:

突发次数就是突发传输,是指在DMA占用总线时,一个周期里读或者写的次数。注意,虽然双方都配置了突发次数,但是并不是同时进行而是先读后写

比如,源数据的突发传输次数设定为4,目标写的突发次数为2,则是先读取四次,然后再写入两次。这里的突发读/写每次都会出发指针的递增

部分输出:

===========================
uart  sig1:ffffffff, sig2:0000f32f
clock gen1:9ffffffd, gen2:0fff0c11
xtal:40000000Hz(crystal)
board init done
===========================
dma memory case:
tc done
tc done
tc done
copy finished with time=353us
case end

dma_normal_cycle:

该部分代码和上述代码的配置一致,但是该代码为循环搬运。

不同的地方在于:

    int used_count = bflb_dma_channel_lli_reload(dma0_ch0, lli, 20, transfers, 3);
    bflb_dma_channel_lli_link_head(dma0_ch0, lli, used_count);      //该函数将链表进行了头尾相接
    bflb_dma_channel_start(dma0_ch0);

链表头尾相接变成了环,所以程序将会重复循环搬运构建的链表内的所有item。

部分输出:

tc done
tc done
tc done
tc done
tc done
tc done
tc done
tc done
tc done
tc done
tc done
tc done
tc done
tc done
tc done
tc done
tc done
tc done
end

按照demo,应该触发一百次中断函数,所以应该有100次 tc done

dma_reduce_or_add:

这部分代码主要展示了两个模式在不同的情况下的应用,并且展示了普通模式下当目标数据和源数据宽度不同时应该怎么传输数据和当应该使用模式但没有使用时会产生什么样的负面效果。

Add与Reduce模式说明:

Add和Reduce模式从狭义来判断是当目标数据宽度和源数据宽度不匹配时使用。

但事实并不仅仅如此,本demo在第一段代码的配置中就展示了即使数据宽度不匹配也可以在normal模式下通过设置配置来运行

    config.direction = DMA_MEMORY_TO_MEMORY;
    config.src_req = 0;
    config.dst_req = 0;
    config.src_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
    config.dst_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
    config.src_burst_count = DMA_BURST_INCR1;
    config.dst_burst_count = DMA_BURST_INCR4;
    config.src_width = DMA_DATA_WIDTH_32BIT;
    config.dst_width = DMA_DATA_WIDTH_8BIT;

通过设定 burst 突发次数来配合宽度,最终使得:源宽度*突发次数 = 目标宽度*突发次数 则可以完整的将数据正确的传输过去

那什么情况下使用Add和Reduce?

答案是要参考数据总量,比如:

    /* Disable reduce mode and enable add mode, src_width = 8bit, dst_width = 32bit, nbytes = 65, transfer length = nbytes + 3 = 68 byte, multiple of dst_width */
    bflb_dma_feature_control(dma0_ch0, DMA_CMD_SET_REDUCE_MODE, 0);
    bflb_dma_feature_control(dma0_ch0, DMA_CMD_SET_ADD_MODE, 1);

    config.src_burst_count = DMA_BURST_INCR4;
    config.dst_burst_count = DMA_BURST_INCR1;
    config.src_width = DMA_DATA_WIDTH_8BIT;
    config.dst_width = DMA_DATA_WIDTH_32BIT;
    bflb_dma_channel_init(dma0_ch0, &config);

    bflb_dma_channel_tcint_mask(dma0_ch0, false);

    transfers[0].nbytes = DMA_TRANSFER_LENGTH + 1;  //这里更改设置总传输量为65字节
    bflb_dma_channel_lli_reload(dma0_ch0, lli, 20, transfers, 1);

    bflb_dma_channel_start(dma0_ch0);

当目标数据宽度是32位4字节,而源数据的宽度是8位一字节,拿65/4 = 16 余下 1,也就是说最后一次传输会仅仅传输一字节数据向一个宽度为四字节的变量。假设这里不是变量而是寄存器,那么就有可能写不进去(可能识别错误)。那么Add在这里的作用就是:字节对齐。它会自动补全后面的数据,也就是补出来3字节的数据,这就是什么注释中说最后的实际传输数据是65 + 3= 68 byte。

而Reduce模式,在本demo中仅仅是测试,没有给出实际的需求场景。

在实际场景中的应用一般是在如下情况:假设当目标宽度为32位,数据源宽度为8位,读取总量设定为61字节。 61 / 4 = 15 余下 1。此时必须再次读取,但是读取的四字节中只有一字节要使用,假设寄存器读取后就会复位,那么这里就会错误操作。所以reduce模式就可以在这种情况下使用,它会规定用户设定的传输总量为最大读取字节数,reduce的作用就是:防止过量读取。

在配置上可以参考这个:

模式 适用场景 配置公式 效果
Reduce 源宽度 > 目标宽度 || 不希望过度读取 nbytes = 期望 + 目标宽度 实际传输 = 期望
Add 目标宽度 > 源宽度 || 需要字节对齐写入数据 nbytes = 期望 实际传输 = 对齐(期望)

部分输出:

===========================
uart  sig1:ffffffff, sig2:0000f32f
clock gen1:9ffffffd, gen2:0fff0c11
xtal:40000000Hz(crystal)
board init done
===========================
dma memory case:
tc done
Check over

tc done
Check over

tc done
Correct, meaningless data. index: 65, src: 0x41, dst: 0x31
Correct, meaningless data. index: 66, src: 0x42, dst: 0x32
Correct, meaningless data. index: 67, src: 0x43, dst: 0x33
Check over

tc done
Check over

end

可以看到在最终的没有添加add模式的情况下,出现了无意义数据。