STM32CubeMX教程12 DMA 直接内存读取


读者可访问 GitHub - lc-guo/STM32CubeMX-Series-Tutorial 获取原始工程代码


1、准备材料

开发板(正点原子stm32f407探索者开发板V2.4

STM32CubeMX软件(Version 6.10.0

keil µVision5 IDE(MDK-Arm

ST-LINK/V2驱动

野火DAP仿真器

XCOM V2.6串口助手

2、实验目标

使用STM32CubeMX软件配置STM32F407开发板上串口USART1以DMA方式传输数据,然后实现与实验“STM32CubeMX教程9 USART/UART 异步通信”相同的目标

3、实验流程

3.0、前提知识

直接存储器访问(DMA)是实现存储器与外设、存储器与存储器之间高效传输数据的一种方法,其拥有① 从外设到存储器、② 从存储器到外设和③ 从存储器到存储器 三种传输模式

STM32F407有DMA1和DMA2两个DMA控制器,其中DMA2可以实现上述三种传输模式,DMA1仅可以实现前两种传输模式

每个DMA均有8个流(stream),每个流又有8个通道,但是每个流只能同时使用8个通道中的一个,支持DMA的外设一般可以选择8个流共64个通道中的某2个通道发起DMA请求

当多个流共同发起DMA请求时,由DMA仲裁器来决定谁先发送(软件可以配置DMA流的优先级),如下图所示为STM32F407的DMA1/2具体的外设请求映射表 (注释1)

以USART1_TX为例举个例子

当使用USART1_TX发起DMA请求时,USART1_TX可以选择的DMA通道只有DMA2 Stream7 CH4,因此DMA请求从DMA2 Stream7 CH4发起,然后经过仲裁器到存储器端口处取要发送的数据,然后经过FIFO(可以配置不使用),最后将数据送到外设端口USART1,上述描述如下图中黄色标注所示 (注释1)

在使用DMA传输数据时一般需要设置① DMA请求和② DMA流两个主要参数,其中DMA请求就是存储器或者外设发起的传输需求,而DMA流就是进行DMA传输的数据链路(上图黄色链路)

使用某个外设的DMA传输时,一般流程为“添加外设具体的DMA请求 -> 选择该DMA请求的流Stream ->设置DMA优先级 -> 设置DMA请求模式 -> 设置地址递增(一般为外设地址不变,内存地址递增) -> 配置FIFO -> 配置传输数据的数据宽度 -> 启动DMA流全局中断 -> 在程序中以DMA方式启动外设”

先入先出(FIFO)可以想象成一个缓存区,启用FIFO之后,可以将输出暂存在该缓存区中,然后当数据量一旦达到了FIFO设置的阈值才会从缓存区中取出来发送出去,当DMA传输的源和目标的数据宽度不同时,FIFO将变得非常有用(可以在FIFO中改变传输数据的宽度)

3.1、CubeMX相关配置

3.1.0、工程基本配置

打开STM32CubeMX软件,单击ACCESS TO MCU SELECTOR选择开发板MCU(选择你使用开发板的主控MCU型号),选中MCU型号后单击页面右上角Start Project开始工程,具体如下图所示

开始工程之后在配置主页面System Core/RCC中配置HSE/LSE晶振,在System Core/SYS中配置Debug模式,具体如下图所示

详细工程建立内容读者可以阅读“STM32CubeMX教程1 工程建立

3.1.1、时钟树配置

系统时钟使用8MHz外部高速时钟HSE,HCLK、PCLK1和PCLK2均设置为STM32F407能达到的最高时钟频率,具体如下图所示

3.1.2、外设参数配置

以下标号与USART1 DMA配置图片中的标号对应

① 在Pinout & Configuration页面左边功能分类栏目Connectivity中单击其中USART1,配置串口模式及参数,具体的USART1基本参数配置与实验“STM32CubeMX教程9 USART/UART 异步通信”相同,在页面下方的参数配置栏上面会有很多标签,当前处于的位置为Parameter Settings(参数设置)

② 单击DMA Settings进入USART1的DMA Settings(DMA配置)页面,支持DMA功能的外设在这里都会有DMA Settings页面

③ 单击ADD按键来增加外设的DMA,如果有可以使用的DMA流则会以列表形式供用户选择,这里可以选择发送TX和接收RX两个串口的DMA请求

④ 选择可用的DMA流,DMA的方向会根据用户选择的DMA请求自动推断,一般只有一个选项,接着选择DMA流的优先级(注意与DMA中断优先级区分)

⑤ USART1_RX DMA请求 Mode (模式)设置为循环模式,并设置内存地址递增,由于外设USART1的地址固定,因此不需要地址递增,而接收的数据存储到内存里需要地址递增

⑦ USART1_TX DMA请求 Mode (模式)设置为普通模式,其他参数与USART1_RX DMA请求一致‘

还以一些未标号的其他参数如下配置

Use Fifo (使用FIFO配置):如果使用则需要配置阈值 Threshold

Data Width (传输数据宽度):用于配置外设和内存的数据宽度,由于串口传输和存储数据均是以字节方式传输,因此这里均选择Byte

Burst Size (突发传输设置):当不使用FIFO时只能为单次传输,而当使用FIFO时,突发传输可以配置为4、8或16增量突发传输

具体配置如下图所示

3.1.3、外设中断配置

在Pinout & Configuration页面左边System Core/NVIC中勾选USART1和DMA2 stream2/7全局中断,然后选择合适的中断优先级即可,步骤如下图所示

3.2、生成代码

3.2.0、配置Project Manager页面

单击进入Project Manager页面,在左边Project分栏中修改工程名称、工程目录和工具链,然后在Code Generator中勾选“Gnerate peripheral initialization as a pair of 'c/h' files per peripheral”,最后单击页面右上角GENERATE CODE生成工程,具体如下图所示

详细Project Manager配置内容读者可以阅读“STM32CubeMX教程1 工程建立”实验3.4.3小节

3.2.1、外设初始化调用流程

在生成的工程主函数main()中调用MX_DMA_Init()函数对USART1 RX/TX用到的DMA时钟及其流的中断进行了配置

在MX_USART1_UART_Init()函数中相比较于“STM32CubeMX教程9 USART/UART 异步通信”实验增加了USART1 RX/TX DMA相关的参数配置及初始化

3.2.2、外设中断调用流程

勾选DMA2 stream2/7全局中断后,会在生成的工程文件stm32f4xx_it.c中新增全局中断函数DMA2_Stream2_IRQHandler()和DMA2_Stream7_IRQHandler()

这两个中断服务函数均调用了HAL库的DMA中断统一管理函数HAL_DMA_IRQHandler(),该函数中根据各种标志判断DMA传输完成/失败/一半完成等事件,然后根据不同的事件调用不同的回调函数,这里DMA传输完成之后调用了hdma->->XferCpltCal1back(),这是个函数指针

上述过程如下图所示

这个函数指针在以DMA方式启动USART1发送或者接收数据时被指向DMA传输完成回调UART_DMATransmitCpl()函数

在该DMA传输完成回调UART_DMATransmitCplt()函数中最终调用了USART1_TX传输完成回调HAL_UART_TxCpltCallback()函数,该函数在串口实验中我们重新实现过,也就是说DMA最终的传输完成中断回调函数使用了外设的中断回调函数

其他的中断事件回调流程类似,上述过程如下图所示

3.2.3、添加其他必要代码

本实验与 “STM32CubeMX教程9 USART/UART 异步通信”实验代码一致,除以下三个方面需要修改

  1. 将主函数开始的以中断方式启动的串口接收函数修改为以DMA方式启动的HAL_UART_Receive_DMA()函数
  2. 主循环中将以中断方式启动的串口发送函数修改为以DMA方式启动的HAL_UART_Transmit_DMA()函数
  3. 删除空闲回调函数中的再次启动串口接收函数HAL_UART_Receive_IT()

其他所有代码无需修改,具体修改后的代码如下所示

为什么删除再次启动串口接收中断的函数?

在串口章节的实验中,我们提到当以中断方式启动串口接收之后,串口接收完毕一次就不能自动接收第二次了,用户必须手动再次调用中断接收函数才可以重新接收,因此我们才在串口空闲回调函数里重新启用,以此来实现无限次的自动接收

但是在本实验中,我们已经配置了串口USART1的DMA接收模式为循环模式,在该模式下其接收完毕一次之后能够继续接收,因此不需要我们在空闲回调函数中重新启用

如果将串口USART1的DMA发送模式配置为循环模式,当按下按键触发时,会出现一直发送的情况,读者可以尝试尝试,这显然不符合我们的预想,因此串口USART1_TX的DMA请求模式配置为了普通模式

4、常用函数

/*以DMA方式启动USART接收*/
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
 
/*以DMA方式启动USART传输*/
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)

5、烧录验证

实验现象与“STM32CubeMX教程9 USART/UART 异步通信”实验现象一致

6、注释详解

注释1:图片来自STM32F4xx中文参考手册 RM0090

参考资料

STM32Cube高效开发教程(基础篇)

更多内容请浏览 STM32CubeMX+STM32F4系列教程文章汇总贴

posted @ 2024-01-11 09:59  OSnotes  阅读(1100)  评论(1编辑  收藏  举报