40.1 初学者重要提示
BDMA只能操作D3域的存储器和外设,这点比较重要,操作的时候容易被遗忘。详情看本章2.6小节。
BDMA支持8路通道。虽然是8路,但这8路不是并行工作的,而是由BDMA的仲裁器决定当前处理哪一路。
BDMA不支持硬件FIFO,但是支持双缓冲。
BDMA不支持突发模式。
BDMA最大传输次数65535次,每次传输单位可以是字节、半字和字。
BDMA的循环模式不可用于存储器到存储器模式。
HAL库没有配套BDMA的双缓,当前的HAL库V1.3.0版本没有对双缓冲进行支持
40.2 BDMA基础知识
BDMA的几个关键知识点放在开头说:
由于总线矩阵的存在,各个主控的道路四通八达,从而可以让DMA和CPU同时开工,但是注意一点,如果他们同时访问的同一个外设,会有一点性能影响的。
BDMA支持存储器到外设,外设到存储器,存储器到存储器和外设到外设的传输,其中外设到外设的传输,DMA1和DMA2是不支持的,这个模式在低功耗模式下比较有用。
BDMA只有一个AHB总线主控,而DMA1和DMA2是有两个的,可以分别用于源地址和目的地址的传输。
源地址和目的地址的数据宽度可以不同,但是数据地址必须要跟其数据类型对齐。比如源地址是uint32类型的,那么此数组的地址必须4字节对齐。
BDMA主要有两种模式,一个是Normal正常模式,传输一次后就停止传输;另一种是Circular循环模式,会一直循环的传输下去,即使有DMA中断,传输也是一直在进行的。
BDMA的通道请求(Channel0 – Channel7)的优先级可编程,分为四级Very high priority,High priority,Medium priority和Low priority。通道的优先级配置相同的情况下,如果同时产生请求,会优先响应编号低的,即Channel0优先响应。
40.2.1 BDMA硬件框图
认识一个外设,最好的方式就是看他的框图,方便我们快速的了解BDMA的基本功能,然后再看手册了解细节。框图如下所示:
(注:ST做的DMA框图没有其它外设做的好,不够详细)
通过这个框图,我们可以得到如下信息:
bdma_tcif[0:7]接口
通道0 – 通道7的传输完成标志。
bdma_it[0:7]接口
通道0 – 通道7的中断触发。
bdma_req[0:7]接口
通道0 –通道7的请求信号接口。
Arbiter仲裁器
用于仲裁当期要处理的DMA请求。通过这里我们可以看出虽然是8路,但这8路不是并行工作的,而是由BDMA的仲裁器决定当前处理哪一路。
AHB总线接口
BDMA只有一个AHB总线主控,而DMA1和DMA2是有两个的,可以分别用于源地址和目的地址的传输。
DMA1和DMA2:
40.2.2 BDMA传输
BDMA支持如下几种传输模式:
存储器到外设。
外设到存储器。
存储器到存储器。
外设到外设的传输。
其中外设到外设的传输,DMA1和DMA2是不支持的,这个模式在低功耗模式下比较有用。
关于这几种传输方式要注意以下两个问题:
源地址和目的地址的数据宽度可以不同,但是数据地址必须要跟其数据类型对齐。比如源地址是uint32类型的,那么此数组的地址必须4字节对齐。
BDMA不可以操作TCM区,其它的SRAM区均可操作,在第25章专门讲解过这个问题。
拓展知识
MDK中全局变量的数据对齐问题说明:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=13511 。
40.2.3 BDMA的循环模式和正常模式
BDMA主要有两种模式,一个是Normal正常模式,传输一次后就停止传输;另一种是Circular循环模式,会一直循环的传输下去,即使有DMA中断,传输也是一直在进行的。
这两种模式各有用途。
Normal正常模式
适合用于单次传输,比如存储器到存储器的数据复制粘贴,又比如串口的数据单次发送,下次还需要发送的时候,使能下即可。
Circular循环模式
适合用于需要连续传输的场合,比如定时器触发BDMA实现任意IO的PWM输出。
另外特别注意,循环模式不可用于存储器到存储器模式。
40.2.4 BDMA数据封装和解封
独立的源和目标传输宽度(字节、半字、字):源和目标的数据宽度不相等时, DMA 自动封装/解封必要的传输数据来优化带宽。无需像F1系列那样强行要求数据缓冲的4字节对齐。下面是各种源地址和目的地址数据宽度传输4次的效果,可以帮助大家更好的理解。
40.2.5 BDMA双缓冲
BDMA也是支持双缓冲模式的,双缓冲的含义是源地址或者目的地址可以设置两个缓冲区,这种方式的好处是一个缓冲区在接收或者发送数据的时候,另一个缓冲区可以动态更新数据或者处理已经接收到的数据。
当用户开启了BDMA传输完成中断后,通过寄存器CCRx的CT位判断当前使用的是哪个缓冲区
如果CT = 1表示当前正在使用缓冲区1,即寄存器BDMA_CM1ARx记录的地址。
如果CT = 0表示当前正在使用缓冲区0,即寄存器BDMA_CM0ARx记录的地址。
另外注意,存储器到存储器的BDMA传输不支持双缓冲模式,仅可以用于存储器到外设或者外设到存储器。
40.2.6 BDMA可以操作的区域
根据第3章的总线互联方式,BDMA仅可以操作:AHB4,APB4的外设以及SRAM4,Backup RAM。
实际应用的时候要特别注意,防止操作错误。
40.3 BDMA的HAL库用法
BDMA的HAL库用法其实就是几个结构体变量成员的配置和使用,然后配置GPIO、时钟,并根据需要配置NVIC、中断和DMA。下面我们逐一展开为大家做个说明。
40.3.1 BDMA寄存器结构体
BDMA相关的寄存器是通过HAL库中的结构体DMA_TypeDef和DMA_Stream_TypeDef定义的,在stm32h743xx.h中可以找到这个类型定义:
typedef struct
{
__IO uint32_t ISR; /*!< DMA interrupt status register, Address offset: 0x00 */
__IO uint32_t IFCR; /*!< DMA interrupt flag clear register, Address offset: 0x04 */
} BDMA_TypeDef;
typedef struct
{
__IO uint32_t CCR; /*!< DMA channel x configuration register Addr offset: 0x08 + 0x14 * x,x = 0 to 7 */
__IO uint32_t CNDTR;/*!< DMA channel x number of data register Addr offset: 0x0C + 0x14 * x,x = 0 to 7 */
__IO uint32_t CPAR; /*!< DMA channel x peripheral address register Addr offset: 0x10 + 0x14 * x, x = 0 to 7*/
__IO uint32_t CMAR; /*!< DMA channel x memory address register, Addr offset: 0x14 + 0x14 * x, x = 0 to 7 */
} BDMA_Channel_TypeDef;
__IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m7.h 文件定义了这个宏:
#define __O volatile /*!< Defines 'write only' permissions */
#define __IO volatile /*!< Defines 'read / write' permissions */
与其它外设的的定义方式不同,BDMA有8组通道,每个通道都有一组BDMA_Channel_TypeDef结构体所定义的寄存器。
解决这个问题的办法就是定义一套BDMA_Channel0 - BDMA_Channel7来解决,定义在stm32h743xx.h文件。
#define PERIPH_BASE ((uint32_t)0x40000000)
#define D3_AHB1PERIPH_BASE (PERIPH_BASE + 0x18020000)
#define BDMA_BASE (D3_AHB1PERIPH_BASE + 0x5400)
#define BDMA ((BDMA_TypeDef *) BDMA_BASE)
#define BDMA_Channel0_BASE (BDMA_BASE + 0x0008)
#define BDMA_Channel1_BASE (BDMA_BASE + 0x001C)
#define BDMA_Channel2_BASE (BDMA_BASE + 0x0030)
#define BDMA_Channel3_BASE (BDMA_BASE + 0x0044)
#define BDMA_Channel4_BASE (BDMA_BASE + 0x0058)
#define BDMA_Channel5_BASE (BDMA_BASE + 0x006C)
#define BDMA_Channel6_BASE (BDMA_BASE + 0x0080)
#define BDMA_Channel7_BASE (BDMA_BASE + 0x0094)
#define BDMA_Channel0 ((BDMA_Channel_TypeDef *) BDMA_Channel0_BASE)
#define BDMA_Channel1 ((BDMA_Channel_TypeDef *) BDMA_Channel1_BASE)
#define BDMA_Channel2 ((BDMA_Channel_TypeDef *) BDMA_Channel2_BASE)
#define BDMA_Channel3 ((BDMA_Channel_TypeDef *) BDMA_Channel3_BASE)
#define BDMA_Channel4 ((BDMA_Channel_TypeDef *) BDMA_Channel4_BASE)
#define BDMA_Channel5 ((BDMA_Channel_TypeDef *) BDMA_Channel5_BASE)
#define BDMA_Channel6 ((BDMA_Channel_TypeDef *) BDMA_Channel6_BASE)
#define BDMA_Channel7 ((BDMA_Channel_TypeDef *) BDMA_Channel7_BASE)
我们访问BDMA的ISR寄存器可以采用这种形式:BDMA->ISR = 0,而访问通道0的CCR就可以采用这种形式BDMA_Channel0->CCR = 0。
40.3.2 BDMA句柄结构体DMA_HandleTypeDef
HAL库在DMA_TypeDef的基础上封装了一个结构体DMA_HandleTypeDef,定义如下:
typedef struct __DMA_HandleTypeDef
{
void *Instance;
DMA_InitTypeDef Init;
HAL_LockTypeDef Lock;
__IO HAL_DMA_StateTypeDef State;
void *Parent;
void (* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma);
void (* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);
void (* XferM1CpltCallback)( struct __DMA_HandleTypeDef * hdma);
void (* XferM1HalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);
void (* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma);
void (* XferAbortCallback)( struct __DMA_HandleTypeDef * hdma);
__IO uint32_t ErrorCode;
uint32_t StreamBaseAddress;
uint32_t StreamIndex;
DMAMUX_Channel_TypeDef *DMAmuxChannel;
DMAMUX_ChannelStatus_TypeDef *DMAmuxChannelStatus;
uint32_t DMAmuxChannelStatusMask;
DMAMUX_RequestGen_TypeDef *DMAmuxRequestGen;
DMAMUX_RequestGenStatus_TypeDef *DMAmuxRequestGenStatus;
uint32_t DMAmuxRequestGenStatusMask;