DMA知识简介:
DMA 控制器提供了一种硬件的方式在外设和存储器之间或者存储器和存储器之间传输数据,而无需 CPU 的介入,从而使 CPU 可以专注在处理其他系统功能上。
项目中需要采用DMA方式传输数据的原因:如果仅仅采用SPI接口进行寄存器通信,那么大可不必采用DMA通信,直接让CPU利用SPI接口通信即可。但是项目中往往会出现传输大量的数据包的情况,此时直接传输和以DMA传输的时间上的差异就体现出来了,并且采用直接传输的时候由于MCU需要不停的搬运数据,因此在传输的两帧数据中间会有很大的延时,导致传输效率极低。而采用DMA方式情况进行传输的话,每个数据帧之间的延时极低。
该图展示的就是采用直接传输的方式所截取的spi通信过程,中间有很大的延时。
该图为SPI采用DMA通信方式的SPI的波形图
程序分析
第一步:配置SPI的基本功能,可以参考GD32f103系列教程—(SPI非DMA方式软件实现篇)
第二步:配置DMA的功能
查看GD32F10x 用户手册,找到DMA那节,可以看出,如果我们想采用SPI0发送数据,则需要配置SPI0 TX端为DM0 的通道2
所以,首先,配置DMA的时钟,然后配置DMA的一些功能,比如存储器的宽度,传输方向,内存的宽度等
void hal_dma_spi0_config(u8_t *sram_buffer, u16_t sram_buffer_legth)
{
rcu_periph_clock_enable(RCU_DMA0); // 使能dma0的时钟
dma_parameter_struct dma_init_struct;
// 停用DMA0的DMA_CH2通道
dma_deinit(DMA0, DMA_CH2);
// 配置DMA初始化结构体
dma_init_struct.periph_addr = (uint32_t)&SPI_DATA(SPI0); // 配置外设地址为SPI0数据寄存器地址
dma_init_struct.memory_addr = (uint32_t)sram_buffer; // 配置存储器地址为sram_buffer的地址
dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL; // 配置传输方向为存储器到外设
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT; // 配置存储器宽度为8位
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT; // 配置外设宽度为8位
dma_init_struct.priority = DMA_PRIORITY_LOW; // 配置DMA优先级为低
dma_init_struct.number = sram_buffer_legth/2; // 配置传输数据个数,这里应根据实际情况填写
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; // 禁止外设地址增量
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; // 允许存储器地址增量
// 初始化DMA0的DMA_CH2通道
dma_init(DMA0, DMA_CH2, &dma_init_struct);
// 配置DMA模式
dma_circulation_disable(DMA0, DMA_CH2); // 禁止循环模式
dma_memory_to_memory_disable(DMA0, DMA_CH2); // 禁止内存到内存模式
}
第三步:配置DMA传输启动
在需要传输数据的时候调用此函数,此时DMA模块会自动的将数据传输完成,知道检测到DMA CH2通道传输完成。
u8_t hal_dma_spi0_start_transmit()
{
/* SPI0_Tx DMA channel */
dma_channel_enable(DMA0, DMA_CH2);
spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);
/* wait DMA transmit completed */
while (!dma_flag_get(DMA0, DMA_CH2, DMA_FLAG_FTF))
{
}
return _SUCCESS;
}
完整程序关注微信公众号“软件喵”回复GD32F103SPIDMA