STM32G4 DMA的使用(寄存器开发)

下面以STM32G474为例,使用DMA来存储USART1的接收数据。

1. 查看硬件支持

首先查看要使用的DMA支持的通道数,在手册中有如下说明。

根据上图可以看到,对于不同的设备类型有不同的DMA通道数量。设备类型分类如下图所示。

我使用的是STM32G474因此是属于Category 3,也就是说,DMA1有8个通道,DMA2也有8个通道。

2. DMA框图

由上图可以看到,DMA请求是通过 DMA_MUX 过来的,然后通过仲裁器对来的请求源进行仲裁,根据优先级来确定先处理那些请求,然后再返回DMA_ack。每个DMA有分别有两个接口为 AHB主接口 和 AHB子接口,数据由子接口传递到主接口。

3. DMA_MUX(DMA矩阵)

跟GPIO矩阵一样,DMA也有矩阵,通过矩阵来选择请求源。

框图如下图所示:

3.1 DMA requests from peripherals 外设请求源

左侧 DMA requests from peripherals 是外设请求源,在 DMAMUX_CxCR中配置 DMAREQ_ID[6:0] 进行选择,其映射表如下表所示。

如:我要将DMA_MUX的ch0选择USART1_RX为DMA请求输出,则应该将 DMAMUX_C0CR 的 DMAREQ_ID 配置为24(上表USART1_RX映射为24)。

3.2 DMA请求生成器

在DMA_MUX框图的左侧有一个 DMA请求生成器,可通过外部信号或者其他信号来触发 DMA请求生成器 来产生DMA请求给DMA_MUX选择。

我认为可以这么理解:

  • 外设过来的信号直接到DMA_MUX的叫DMA请求
  • 外部引脚或者一些其他信号 要通过 DMA请求生成器 来生成DMA请求的(生成后传递给DMA_MUX),触发DMA请求生成器的信号叫 触发信号

G474的有4个DMA触发生成器(在3.1章节中映射表的前4个请求源就是由 DMA请求生成器 生成的DMA请求源),通过DMAMUX_RGxCR寄存器配置选择触发源。

因为我没有使用外部触发功能,因此没有做测试,大概的思路应该是这样的,因此对DMA请求生成器不做过多介绍,如有错误欢迎讨论。

3.3 同步功能

同步功能可以认为是门控,当 DMAMUX_CxCR 中的 SE 置位,代表同步功能有效,当检测到同步信号有效,则会将请求源的信号传递给框图末端的 DMA request out,即 请求 给了 DMA 。
因为我没有使用DMA同步功能,因此没有做测试,大概的思路应该是这样的,如有错误欢迎讨论。

3.4 输出信号

框图右侧有两种输出信号,一个就是DMA请求输出信号,另一个是DMA事件信号。

DMA_MUX请求输出信号与DMA通道一 一对应,即:

DMA_MUX通道 DMA通道
DMAMUX1_Channel0 DMA1_Channel1
DMAMUX1_Channel1 DMA1_Channel2
DMAMUX1_Channel2 DMA1_Channel3
DMAMUX1_Channel3 DMA1_Channel4
DMAMUX1_Channel4 DMA1_Channel5
DMAMUX1_Channel5 DMA1_Channel6
DMAMUX1_Channel6 DMA1_Channel7
DMAMUX1_Channel7 DMA1_Channel8
DMAMUX1_Channel8 DMA2_Channel1
DMAMUX1_Channel9 DMA2_Channel2
DMAMUX1_Channel10 DMA2_Channel3
DMAMUX1_Channel11 DMA2_Channel4
DMAMUX1_Channel12 DMA2_Channel5
DMAMUX1_Channel13 DMA2_Channel6
DMAMUX1_Channel14 DMA2_Channel7
DMAMUX1_Channel15 DMA2_Channel8

由于没有使用事件,因此也不介绍,其大概也就是置位标志位,或者几个请求后置位标志位或者中断,因为不使用,因此不介绍。

3.5 DMA_MUX中断

DMAMUX的中断都是 生成器 或者 同步模式 的,我没用,因此不管。

4.DMA的使用

4.1 DMA的配置步骤

DMA通道配置步骤

根据以下顺序配置DMA通道:

  1. 配置外设寄存器地址在 DMA_CPARx寄存器
  2. 配置内存地址在DMA_CMARx寄存器
  3. 配置传输数据数量在DMA_CNDTRx寄存器
  4. 在DMA_CCRx寄存器中配置以下参数:
    • 通道优先级
    • 数据传输方向
    • 循环模式
    • 外设地址 和 内存地址 增长模式
    • 外设 和 内存 数据格式大小
    • 中断使能一半 和/或 完全传输 和/或传输错误

4.2 配置示例

以 USART1 外设为例,将 USART1 的 RDR 数据(接收数据,波特率38400)通过 DMA2_CH2 存储到 RX_Buffer 变量中,详细代码如下。

4.2.1 USART1 初始化配置

c 复制代码
#define USART1_SendByte(byte)					USART1->TDR = byte

#define USART1_EnableReceive()					USART1->CR1 |= USART_CR1_RE;
#define USART1_DisableReceive()					USART1->CR1 &= ~(USART_CR1_RXNEIE | USART_CR1_RE)

#define USART1_EnableSend()						USART1->CR1 |= USART_CR1_TE;\
												USART1->CR1 |= (USART_CR1_TXEIE | USART_CR1_TCIE)
#define USART1_DisableSend()					USART1->CR1 &= ~(USART_CR1_TXEIE | USART_CR1_TE | USART_CR1_TCIE)
#define USART1_DisableTXEInt()					USART1->CR1 &= ~USART_CR1_TXEIE

void USART1_Function_Init(void)
{
/*
在RCC_CFGR寄存器中配置USART时钟源fCK
当前配置为SYSCLK 168MHz
*/
	//步骤1  配置CR1寄存器的M标志位,即配置数据长度
	USART1->CR1 = (USART1->CR1 & 0x00000000)
					// |USART_CR1_RXFFIE			//bit31:RXFIFO Full interrupt enable
					// |USART_CR1_TXFEIE			//bit30:TXFIFO empty interrupt enable
					// |USART_CR1_FIFOEN			//bit29:FIFO mode enable
					// |USART_CR1_M1				//bit28:【00|1起始位,8数据位】【01|1起始位,9数据位】【10|1起始位,7数据位】
					// |USART_CR1_EOBIE			//bit27:【1|EOBF标志位中断使能】
					// |USART_CR1_RTOIE			//bit26:【1|RTOF标志位中断使能】
					// |(0 << USART_CR1_DEAT_Pos)				//bit[25:21]:DE时间
					// |(0 << USART_CR1_DEDT_Pos)				//bit[20:16]:DE时间
					// |USART_CR1_OVER8			//bit15:【0|过采样/16】【1|过采样/8】
					// |USART_CR1_CMIE				//bit14:字符匹配中断使能(CMF标志位)
					// |USART_CR1_MME				//bit13:静音模式使能【0|一直活动】【1|静音模式】配合WAKE唤醒
					// |USART_CR1_M0				//bit12:【00|1起始位,8数据位】【01|1起始位,9数据位】【10|1起始位,7数据位】
					// |USART_CR1_WAKE				//bit11:唤醒方法配置【0|空闲总线唤醒】【1|地址唤醒】
					// |USART_CR1_PCE				//bit10:奇偶校验使能
					// |USART_CR1_PS				//bit09:【0|偶校验】【1|奇校验】
					// |USART_CR1_PEIE				//bit08:PE中断使能位
					// |USART_CR1_TXEIE			//bit07:TXE中断使能位
					// |USART_CR1_TCIE				//bit06:TC中断使能位
					// |USART_CR1_RXNEIE			//bit05:RXNE/ORE中断使能位
					// |USART_CR1_IDLEIE			//bit04:IDLE中断使能位
					// |USART_CR1_TE				//bit03:发送使能
					// |USART_CR1_RE				//bit02:接收使能
					// |USART_CR1_UESM				//bit01:【0|USART不能在停止模式唤醒】【1|当配置USART的RCC时钟,可以在停止模式被唤醒】
					// |USART_CR1_UE				//bit00:使能USART模块
					;
	
	//步骤2 设置波特率(16bit)
	USART1->BRR = 4375;			//bit[15:4]:USARTDIV[15:4]//168000000/38400 = 4375
								//bit[3:0]:【OVER8=0| USARTDIV[3:0]】【OVER8=1| USARTDIV[3:0]右移1位,此时BIT3必须为0】
	
	//步骤3 配置停止位长度
	USART1->CR2 = (USART1->CR2 & 0x00000086)
					// |(0x00 << USART_CR2_ADD_Pos)				//bit[31:24]:地址匹配或者数据匹配的数据
					// |USART_CR2_RTOEN			//bit23:接收超时使能
					// |USART_CR2_ABRMODE_1		//bit[22:21]:【00|起始位检测】【01|start->10检测】
					// |USART_CR2_ABRMODE_0		//自动波特率检测	【10|0x7f检测】【11|0x55检测】
					// |USART_CR2_ABREN			//bit20:自动波特率检测使能【0|禁止】【1|使能】
					// |USART_CR2_MSBFIRST			//bit19:【0|先发送低位】【1|先发送高位】
					// |USART_CR2_DATAINV			//bit18:【0|正常】【1|二进制取反(1对应低电平)】
					// |USART_CR2_TXINV			//bit17:【0|正常(VDD为空闲)】【1|TX引脚认为VDD为忙,GND为空闲】
					// |USART_CR2_RXINV			//bit16:【0|正常(VDD为空闲)】【1|RX引脚认为VDD为忙,GND为空闲】
					// |USART_CR2_SWAP				//bit15:【0|正常】【1|TX和RX引脚功能调换】
					// |USART_CR2_LINEN			//bit14:LIN总线使能
					// |USART_CR2_STOP_1			//bit[13:12]:【00|1停止位】【01|0.5停止位】
					// |USART_CR2_STOP_0			//停止位选择	【10|2停止位】【11|1.5停止位】
					// |USART_CR2_CLKEN			//bit11:CK引脚使能标志位
					// |USART_CR2_CPOL				//bit10:【0|低值稳定】【1|高值稳定】
					// |USART_CR2_CPHA 			//bit09:【第一个时钟沿发送或接受数据】【第二个时钟沿发送或接受数据】
					// |USART_CR2_LBCL				//bit08:【0|最后一个数据位的时钟脉冲不输出到CK引脚】【1|最后一个数据位的时钟脉冲输出到CK引脚】
					// |USART_CR2_LBDIE			//bit06:LBDF标志位中断使能
					// |USART_CR2_LBDL				//bit05:该位用于在 11 位或 10 位中断检测之间进行选择。【0|10位断点检测】【1|11位断点检测】
					// |USART_CR2_ADDM7			//bit04:【0|4位地址检测】【1|7位地址检测】
					// |USART_CR2_DIS_NSS			//bit03:0: SPI slave selection depends on NSS input pin. 1: SPI slave is always selected and NSS input pin is ignored.
					// |USART_CR2_SLVEN			//bit00:Synchronous Slave mode enable
					;

	//步骤4 通过置位CR1的UE标志位使能USART模块
	USART1->CR1 |= USART_CR1_UE;

	//步骤5 配置DMA
	USART1->CR3 = (USART1->CR3 & 0x00010000)
					// |(0 << USART_CR3_TXFTCFG_Pos)	//bit[29:31]:
					// 									// 000:TXFIFO reaches 1/8 of its depth
					// 									// 001:TXFIFO reaches 1/4 of its depth
					// 									// 010:TXFIFO reaches 1/2 of its depth
					// 									// 011:TXFIFO reaches 3/4 of its depth
					// 									// 100:TXFIFO reaches 7/8 of its depth
					// 									// 101:TXFIFO becomes empty
					// |USART_CR3_RXFTIE				//bit28:RXFIFO阈值中断启用
					// |(0 << USART_CR3_RXFTCFG_Pos)	//bit[25:27]:
					// 									// 000:Receive FIFO reaches 1/8 of its depth
					// 									// 001:Receive FIFO reaches 1/4 of its depth
					// 									// 010:Receive FIFO reaches 1/2 of its depth
					// 									// 011:Receive FIFO reaches 3/4 of its depth
					// 									// 100:Receive FIFO reaches 7/8 of its depth
					// 									// 101:Receive FIFO becomes full
					// 									// Remaining combinations: Reserved
					// |USART_CR3_TCBGTIE				//bit24:传输完成,中断启用
					// |USART_CR3_TXFTIE				//bit23:TXFIFO阈值中断启用
					// |USART_CR3_WUFIE				//bit22:WUF标志位中断使能(停止模式唤醒)
					// |USART_CR3_WUS_1				//bit[21:20]:【00|当地址相同置位】【01|Reserve】
					// |USART_CR3_WUS_0				//唤醒停止中断标志位选择【WUF在起始位置位】【WUF在RXNE置位】
					// |USART_CR3_SCARCNT				//bit[19:17]:智能卡自动重复计数
					// |USART_CR3_DEP					//bit15:【0|DE信号高电平有效】【1|DE信号低电平有效】Driver Enable -> DE
					// |USART_CR3_DEM					//bit14:【0|DE信号禁止】【1|DE信号使能】
					// |USART_CR3_DDRE					//bit13:接收错误时禁止DMA【0|接收错误后仅置位标志位,不禁止DMA】【1|接收错误后禁止DMA】
					// |USART_CR3_OVRDIS				//bit12:【0|溢出后,置位ORE】【1|溢出后不置位ORE】
					// |USART_CR3_ONEBIT				//bit11:【0|3bit采样法】【1|1bit采样法】
					// |USART_CR3_CTSIE				//bit10:CTS中断使能
					// |USART_CR3_CTSE					//bit09:CTS使能
					// |USART_CR3_RTSE					//bit08:RTS使能
					// |USART_CR3_DMAT					//bit07:DMA使能发送
					|USART_CR3_DMAR					//bit06:DMA使能接收
					// |USART_CR3_SCEN					//bit05:智能卡模式使能【1|使能】
					// |USART_CR3_NACK					//bit04:【0|就校验错误NACK不传输】【1|奇偶校验错误NACK传输】
					// |USART_CR3_HDSEL				//bit03:单线半双工模式选择【1|半双工模式】
					// |USART_CR3_IRLP					//bit02:【1|IrDA低功耗模式】
					// |USART_CR3_IREN					//bit01:【1|IrDA模式使能】
					// |USART_CR3_EIE					//bit00:错误中断使能标志位
					;


	USART1->GTPR = 0x0000
					// |(0 << 8)					//bit[15:8]:Guard Time 守护时间	GT[7:0]
					// |0x00						//bit[7:0]:分频值(page946)		PSC[7:0]
					;

	USART1->RTOR = 0x00000000
					// |(0 << 24)					//bit[31:24]:块长度
					// |0x000000					//bit[23:0]:接收超时值
					;

	USART1->RQR = (USART1->RQR & 0xffffffe0)
					// |USART_RQR_TXFRQ			//bit04:发送数据刷新请求,置位TXE
					// |USART_RQR_RXFRQ			//bit03:接收数据刷新请求,清除RXNE
					// |USART_RQR_MMRQ				//bit02:静音模式刷新请求,置位RWU,同时USART进入静音模式
					// |USART_RQR_SBKRQ			//bit01:截止请求,置位SBKF
					// |USART_RQR_ABRRQ			//bit00:自动模特率请求,置位ABRF标志位
					;

	// USART1->ISR = 
	// USART_ISR_REACK				//bit22:接收标志
	// |USART_ISR_TEACK			//bit21:发送标志
	// |USART_ISR_WUF				//bit20:停止模式唤醒标志
	// |USART_ISR_RWU				//bit19:静音模式唤醒标志
	// |USART_ISR_SBKF				//bit18:发送截止标志
	// |USART_ISR_CMF				//bit17:ADD字符匹配标志
	// |USART_ISR_BUSY				//bit16:忙标志
	// |USART_ISR_ABRF				//bit15:自动波特率错误标志
	// |USART_ISR_ABRE				//bit14:自动波特率状态错误标志

	// |USART_ISR_EOBF				//bit12:块结束标志
	// |USART_ISR_RTOF				//bit11:接收超时标志
	// |USART_ISR_CTS				//bit10:CTS标志
	// |USART_ISR_CTSIF			//bit09:CTS中断标志
	// |USART_ISR_LBD				//bit08:LIN截止侦查标志
	// |USART_ISR_TXE				//bit07:发送数据寄存器空标志
	// |USART_ISR_TC				//bit06:发送完成标志
	// |USART_ISR_RXNE				//bit05:读数据寄存器不空标志
	// |USART_ISR_IDLE				//bit04:总线空标志
	// |USART_ISR_ORE				//bit03:溢出标志
	// |USART_ISR_NE				//bit02:起始位检测标志
	// |USART_ISR_FE				//bit01:帧错误标志
	// |USART_ISR_PE				//bit00:校验错误标志

	USART1->ICR = (USART1->ICR & 0xffede4a0)
					// |USART_ICR_WUCF					//bit20:写1清除标志位
					// |USART_ICR_CMCF					//bit17:写1清除标志位
					// |USART_ICR_EOBCF				//bit12:写1清除标志位
					// |USART_ICR_RTOCF				//bit11:写1清除标志位
					// |USART_ICR_CTSCF				//bit09:写1清除标志位
					// |USART_ICR_LBDCF				//bit08:写1清除标志位
					// |USART_ICR_TCCF					//bit06:写1清除标志位
					// |USART_ICR_ORECF				//bit04:写1清除标志位
					// |USART_ICR_IDLECF				//bit03:写1清除标志位
					// |USART_ICR_NCF					//bit02:写1清除标志位
					// |USART_ICR_FECF					//bit01:写1清除标志位
					// |USART_ICR_PECF					//bit00:写1清除标志位
					;

	// USART1->RDR
	// USART1->TDR

	//步骤6 置位CR1寄存器的RE标志位,RX开始寻找起始帧。
	USART1_EnableReceive();
}

4.2.2 RX_Buffer变量声明

c 复制代码
#define RS232_RXBUFFER_SIZE			128
typedef struct RS232_Var_Str
{
	char				RX_Buffer[RS232_RXBUFFER_SIZE];
}RS232_Var_Struct;
extern RS232_Var_Struct RS232_Var;

4.2.3 DMA初始化

这里我使用DMA2的CH2,对应DMA_MUX是CH9

(DMA1占用8个DMA_MUX通道,DMA2占用8个DMA_MUX通道,DMA_MUX通道是从0开始,因此排到DMA2的CH2就是DMA_MUX的CH9)

DMA通道配置步骤

根据以下顺序配置DMA通道:

  1. 配置外设寄存器地址在 DMA_CPARx寄存器
  2. 配置内存地址在DMA_CMARx寄存器
  3. 配置传输数据数量在DMA_CNDTRx寄存器
  4. 在DMA_CCRx寄存器中配置以下参数:
    • 通道优先级
    • 数据传输方向
    • 循环模式
    • 外设地址 和 内存地址 增长模式
    • 外设 和 内存 数据格式大小
    • 中断使能一半 和/或 完全传输 和/或传输错误
c 复制代码
void DMA_Function_Init(void)
{
	DMA2_Channel2->CCR &= ~DMA_CCR_EN;							//步骤0:禁止使能DMA,准备配置
	DMA2_Channel2->CPAR = (uint32_t)(&USART1->RDR);				//步骤1:配置外设地址
	DMA2_Channel2->CMAR = (uint32_t)(&RS232_Var.RX_Buffer[0]);	//步骤2:配置内存地址
	DMA2_Channel2->CNDTR = RS232_RXBUFFER_SIZE;					//步骤3:传输数据数量 这里配置128个,也就是Rx_Buffer的大小
	//步骤4: 在DMA_CCRx寄存器中配置需要的参数
	DMA2_Channel2->CCR =	0x00000000
							// |DMA_CCR_MEM2MEM			// Bit14: memory-to-memory mode	【1: enabled】
							|(0 << DMA_CCR_PL_Pos)		// Bit[13:12]: 优先级配置。【00: low  01: medium  10: high  11: very high】
							|(0 << DMA_CCR_MSIZE_Pos)	// Bit[11:10]: 内存地址对应大小 size【00: 8 bits  01: 16 bits  10: 32 bits  11: reserved】
							|(0 << DMA_CCR_PSIZE_Pos)	// Bit[09:08]: 外设地址对应大小 size【00: 8 bits  01: 16 bits  10: 32 bits  11: reserved】
							|DMA_CCR_MINC				// Bit07: 内存指针自加【1: enabled】(由于想法是USART接收到1字节,就存到Rxbuffer中,因此需要对内存地址自加)
							// |DMA_CCR_PINC			// Bit06: 外设指针自加【1: enabled】
							|DMA_CCR_CIRC				// Bit05: 循环模式【1: enabled】使能后当传输数量达到,CNDTR变成0后,自动重装CNDTR寄存器,重新使能DMA
							// |DMA_CCR_DIR				// Bit04: 数据传输方向 【0:外设->内存】【1:内存->外设】
							// |DMA_CCR_TEIE			// Bit03: transfer error interrupt enable
							// |DMA_CCR_HTIE			// Bit02: half transfer interrupt enable
							// |DMA_CCR_TCIE			// Bit01: transfer complete interrupt enable(就是CNDTR变成0后,如果置位该位会触发中断)
							// |DMA_CCR_EN				// Bit00: DMA使能
							;
	//步骤x:配置DMA源矩阵,DMA2的通道2对应DMA_MUX的通道9(DMA1占用8个DMA_MUX通道,DMA2占用8个DMA_MUX通道,DMA_MUX通道是从0开始,因此排到DMA2的CH2就是DMA_MUX的CH9)
	//对于MUX通道的介绍在下面
	DMAMUX1_Channel9->CCR = (24 << DMAMUX_CxCR_DMAREQ_ID_Pos) | (0 << DMAMUX_CxCR_NBREQ_Pos) | (0 << DMAMUX_CxCR_SYNC_ID_Pos);
	DMA2_Channel2->CCR |= DMA_CCR_EN;					//步骤5:使能DMA
}

至此,USART1接收的数据就可以通过DMA存储到 RS232_Var.RX_Buffer 变量中去了。

相关推荐
LN花开富贵1 小时前
stm32g431rbt6芯片中VREF+是什么?在电路中怎么设计?
笔记·stm32·单片机·嵌入式硬件·学习
qq21084629531 小时前
【stm32笔记】使用rtt-studio与stm32CubeMx联合创建项目
笔记·stm32·嵌入式硬件
CV金科1 小时前
蓝桥杯—STM32G431RBT6按键的多方式使用(包含软件消抖方法精讲)从原理层面到实际应用(一)
stm32·单片机·嵌入式硬件·蓝桥杯
2021.091 小时前
五、CAN总线
嵌入式硬件
luckyluckypolar1 小时前
STM32——输入捕获
stm32·单片机·嵌入式硬件·物联网
hong1616881 小时前
嵌入式硬件基础知识
嵌入式硬件
hai405871 小时前
单片机(Microcontroller)原理及应用
单片机·嵌入式硬件
jun7788952 小时前
嵌入式硬件基础知识
嵌入式硬件
Projectsauron3 小时前
STM32 芯片启动过程
stm32·单片机·芯片启动过程
CDialog3 小时前
arduino ide开发esp32-wroom-32E
单片机·嵌入式硬件