引子
在前面的串口例子中,我们可以看如果要通过串口发送一个字符串,需要CPU把每个字符一个一个的发送出去,整个数据传输的过程都需要CPU的参与。可以想象如果传输的数据量较大,那么CPU大部分时间都忙于数据的传输了,然而,我们希望CPU能去做其他更重要的事情,那么数据的传输有没有更好的办法呢?
有,就是DMA(直接存储器访问)一个可以实现数据在存储器和外设或存储器和存储器之间直接传输,而不需要CPU参与的功能,通称DMA控制器。
因此,如果我们设计一个DMA,可以这么做。假设CPU大哥有批货(数据)需要从杭州运到上海,那么可以招一个快递员(DMA小弟),告诉他收货地址、发货地址、货物大小和紧急程度,如果有多批货需要运到不同的城市,那就多招几个快递员,并成立一个快递公司管理。
上面的例子中,快递公司可以看做是DMA控制器,DMA通道可以看做是快递员,收货地址是数据目的地址,发货地址是数据源地址,货物大小是数据长度,紧急程度是软件优先级,快递员编号是硬件优先级。
GD32的DMA管理
DMA不是Cortex-M3的内核的一部分,都是厂家自己设计的。如图,GD32 MDA1支持7通道,并和CPU共用系统总线,因此,和CPU是存在竞争关系的,只是总线仲裁比较偏心CPU,保证CPU至少有一半的总线带宽。
DMA的配置步骤:
1. 配置外设地址
2. 配置存储器地址
3. 配置传输数据总数
4. 配置软件优先级,传输方向,模式类型,数据尺寸和中断类型
5. 使能DMA
这些配置中,大部分在引子里已经说明了,理解不难,重点说下,优先级、模式类型和中断类型
优先级:
-
优先级分两个层次,软件优先级和硬件优先级。软件优先级高于硬件优先级。
- 软件优先级又分4个等级
- 硬件优先级是固定的,就是通道号,如下图,通道编号越小,优先级越高。
模式类型:
循环模式和普通模式
中断类型:
每个通道都有专门的中断,中断事件只有三个:传输完成,传输完成一半和传输错误。如下图,
代码配置
static VOID UART1_DmaRxConfig(IN U8 *buf, IN U32 len)
{
DMA_InitPara DMA_InitStructure;
DMA_Enable(DMA1_CHANNEL5, DISABLE);
/* USART1 RX DMA1 Channel (triggered by USART1 Rx event) Config */
DMA_DeInit(DMA1_CHANNEL5);
DMA_InitStructure.DMA_PeripheralBaseAddr = (U32) &(USART1->DR);
DMA_InitStructure.DMA_MemoryBaseAddr = (U32)buf;
DMA_InitStructure.DMA_DIR = DMA_DIR_PERIPHERALSRC;
DMA_InitStructure.DMA_BufferSize = len;
DMA_InitStructure.DMA_PeripheralInc = DMA_PERIPHERALINC_DISABLE;
DMA_InitStructure.DMA_MemoryInc = DMA_MEMORYINC_ENABLE;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PERIPHERALDATASIZE_BYTE;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MEMORYDATASIZE_BYTE;
DMA_InitStructure.DMA_Mode = DMA_MODE_NORMAL;
DMA_InitStructure.DMA_Priority = DMA_PRIORITY_VERYHIGH;
DMA_InitStructure.DMA_MTOM = DMA_MEMTOMEM_DISABLE;
DMA_Init(DMA1_CHANNEL5, &DMA_InitStructure);
DMA_Enable(DMA1_CHANNEL5, ENABLE);
}
代码路径
https://github.com/YaFood/GD32F103/tree/master/TestUART
https://gitee.com/YaFOOD/GD32F103/tree/master/TestUART