STM32CUBEMX 使用文章:
STM32CUBEMX 使用教程4 — 串口 (USART) 配置、重定向 printf 输出
STM32CUBEMX 使用教程3 — 外部中断(EXTI)的使用
STM32CUBEMX 使用教程2 — GPIO的使用、输入/输出
STM32CUBEMX 使用教程 1 — 配置环境、新建工程
一、DMA简介
DMA(Direct Memory Access),直接存储器访问(或数据搬运),是STM32中很实用的一个外设。
DMA 传输数据方式不需要 CPU 参与控制传输,也不用中断处理的方式进行数据搬运处理,纯粹通过硬件为 RAM 与 I/O 设备之间开辟一条直接传送数据的通路,使 CPU 的效率大大提高。
由于我手头上使用的是STM32F103ZET6,所以按照这个进行讲解,同型号不同规格的有些差异,具体的请参考指导手册。
STM32F103ZET6 有两个 DMA 控制器,DMA1 和 DMA2,其中,DMA1 有 7 个通道,DMA2 有 5 个通道, 不同的 DMA 控制器映射不同的的外设请求。映射表如下:
DMA控制器的框图如下:
从上图中可以看到:
(1)DMA传输数据请求
外设想要通过 DMA 来传输数据,必须先给 DMA 控制器发送 DMA 请求,DMA 收到 请求信号之后,控制器会给外设一个应答信号,当外设应答后且 DMA 控制器收到应答信号之后,就会启动 DMA 的传输,直到传输完毕。
(2)DMA通道映射
DMA1 有 7 个通道,DMA2 有 5 个通道,每个通道对应不同的外设的 DMA 请求。
虽然每个通道可以接收多个外设的请求,但是同一时间只能接收一个,不能同时接收多个。
(3)多请求仲裁机制
当有多个 DMA 通道请求传输时,由仲裁器管理先后响应的顺序问题。
仲裁器管理 DMA 通道请求分为两个阶段:第一阶段属于软件阶段,可以在 DMA_CCRx 寄存器中设置,有 4 个等级:非常高,高,中和低四个优先级。
第二阶段属于硬件阶段,如果两个或以上的 DMA 通道请求设置的优先级一样,则他们优先级取决于通道编号,编号越低优先权越高,比如通道 0 高于通道 1。
本文通过 DMA1 和 USART1 演示,将内存缓存区的数据(Data_buffer)通过DMA搬运到 USART1 输出。
从上面可以知道,USART1_TX 映射到 DMA1 的 4 通道。
二、STM32CUBEMX 配置DMA
(1)打开上期的USART 的配置工程,并开启DMA功能。
USART的配置参考我上一期的文章:
STM32CUBEMX 使用教程4 — 串口 (USART) 配置、重定向 printf 输出
选择 USART1 的 TX 并增加 DMA ,如下图:
(2)增加 DMA 通道映射到 USART1_TX,如下图:
(3)DMA的通道映射方向为 Memory --> Peripheral,其他的设置如下:
说明一下:
Direction:传输方向。有两种:
Memory to Periphral:内存到外设;
Periphral to Memory:外设到内存;
Mode:模式,有两种:
Normal:正常模式,DMA初始化完成以后传输一次,后面想要再次传输时要重新初始化一次DMA。
Circular:循环模式,DMA初始化完成以后会一直传输。
DMA 数据宽度:
Byte:字节
Half Word:半字
Word:字
代码生成之后,DMA初始化代码如下:
void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel4_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
}
USART 与 DMA 的相关代码配置如下:
/* USART1 DMA Init */
/* USART1_TX Init */
hdma_usart1_tx.Instance = DMA1_Channel4;
hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart1_tx.Init.Mode = DMA_NORMAL;
hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx);
因为本文配置的是从内存通过DMA把数据搬运到USART,所以需要定义发送数据的区域。如下:
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t Tx_data[] = {"DMA Test, 微信公众号:嵌入式之入坑笔记!\r\n"};
/* USER CODE END 0 */
调用API接口发送数据:
HAL_UART_Transmit_DMA(&huart1, (uint8_t*)Tx_data, sizeof(Tx_data));
实验现象如下: