RVMCU课堂「18」: 手把手教你玩转RVSTAR—DMA数据传输篇
扫描二维码
随时随地手机看文章
系统环境Windows 10-64bit
软件平台NucleiStudio IDE 202102版或 PlatformIO IDECoolTer
硬件需求RV-STAR开发板TTL-USB串口转换
DMA(直接内存访问)原理
DMA,全称Direct Memory Access,即直接存储器访问。DMA传输将数据从一个地址空间复制到另外一个地址空间。传输动作的初始化由CPU完成,而传输动作本身由DMA控制器来实行。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。DMA的方式并没有让处理器工作拖延,反而可以去处理其他的工作。DMA对于高效能嵌入式系统算法和网络传输是很重要的。在实现DMA传输时,是由DMA控制器直接掌管总线,因此,存在着一个总线控制权转移问题。即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。
GD32VF103的DMA控制器
GD32VF103的DMA控制器有12个通道(DMA0有7个通道,DMA1有5个通道)。每个通道都是专门用来处理一个或多个外设的存储器访问请求的。DMA控制器内部实现了一个仲裁器,用来仲裁多个DMA请求的优先级。DMA控制器和RISC-V内核共享系统总线,当DMA和CPU访问同样的地址空间时,DMA访问可能会阻挡CPU访问系统总线几个总线周期。总线矩阵中实现了循环仲裁算法来分配DMA与CPU的访问权,它可以确保CPU得到至少一半的系统总线带宽。
主要特征如下:
- 传输数据长度可编程配置,最大到65536
- 12个通道,并且每个通道都可配置(DMA0有7个通道,DMA1有5个通道)
- AHB和APB外设,片上闪存和SRAM都可以作为访问的源端和目的端
- 每个通道连接固定的硬件DMA请求
- 支持软件优先级(低、中、高、极高)和硬件优先级(通道号越低,优先级越高)
- 存储器和外设的数据传输宽度可配置:字节,半字,字
- 存储器和外设的数据传输支持固定寻址和增量式寻址
- 支持循环传输模式
- 支持外设到存储器,存储器到外设,存储器到存储器的数据传输
- 每个通道有3种类型的事件标志和独立的中断
- 支持中断的使能和清除
实验部分
原理部分已经介绍过,DMA的工作过程比较简单,开发起来也比较容易,用户只需要结合数据手册,明确微控制器DMA通道和外设间对应关系,明确DMA的源地址、目的地址、数据宽度等信息,在开发时对DMA控制器和外设进行相应的使能和配置即可。由于RV-STAR的USB串口(UART4)不支持DMA功能,本次的实验使用UART3进行:首先使用串口的DMA发送功能,让数据不经CPU直接从内存(txbuffer)传输到串口的发送端,然后使用串口的DMA接收功能接收10个字节的数据,保存到rxbuffer中,最后在主循环中(使用CPU)将rxbuffer接收到的数据再通过串口发送出去。
#include "nuclei_sdk_hal.h"
uint8_t rxbuffer[10];
uint8_t txbuffer[] = "\nUART DMA receive and transmit example, please input 10 bytes:\n";
#define ARRAYNUM(arr_name) (uint32_t)(sizeof(arr_name) / sizeof(*(arr_name)))
void uart_init();
void uart_send(int ch);
int main()
{
dma_parameter_struct dma_init_struct;
/* enable DMA1 */
rcu_periph_clock_enable(RCU_DMA1);
/* initialize UART3 */
uart_init();
/* deinitialize DMA Channel4(UART3_TX) */
dma_deinit(DMA1, DMA_CH4);
dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_addr = (uint32_t)txbuffer;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = ARRAYNUM(txbuffer) - 1;
dma_init_struct.periph_addr = (uint32_t)