使用DMA发送和接收数据

本文介绍DMA(直接存储器访问)的概念及其在STM32微控制器上的应用。文章详细讲解了如何利用DMA进行数据传输,包括初始化DMA结构体、配置USART1的DMA、编写中断服务函数等步骤,并给出了实现DMA打印功能的具体代码。

什么是DMA?DMA(直接存储器存储)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。其优点在于不占用CPU,可以无须CPU的干预让数据快速移动。

   

 

从图中可以看出共有两个DMA,为DMA1和DMA2,其中DMA1有7个通道,DMA2有5个通道,他们都挂接在AHB总线上,这就意味着使用他们需要使能AHB中的DMA时钟。

如果要使用DMA传输和接收数据,那么该怎么初始化结构体呢?首先,定义两个数组,用来储存器到存储器的数据和存储器到外设的数据,然后在一步一步进行DMA的结构体初始化,例如使用DMA1来接收USART1传来的数据。DMA的外设地址就需要指向USART1的DR数据寄存器,DMA的存储器地址就需要用到之前创建的数组地址,用来储存DR传输过来的数组;接下来设置数据的传输方向和传输数量,传输方向分为单向传输和双向传输,这里设置为单向传输PeripheralSRC((uint32_t)0x00000000),传输数目则是说每次通过DMA通道的数据长度,可自己设置数目。然后是DMA的外设递增模式设置,如果DMA选用的通道有多个外设连接,则需要用到这个模式,只需使能他就行了,这里不需要做使能,因为只用了一个usart1;DMA_MemoryInc(存储器地址递增模式)设置为DMA_MemoryInc_Enable,外设数据宽度和存储器数据局宽度都设置为BYTE模式,DMA模式选择为正常,优先级设为高优先级,最后的存储器到存储器的模式设置为DMA_M2M_Disable,用于两个存储器中的变量数据访问,最后就是使能了,接收USART1数据的通道为DMA1_Channel5,发送数据的通道为DMA1_Channel4,注意:这里一个做了两个通道的初始化,一个用于发送,一个用于接收;传输通道的传输数目配置为1,传输方向选择双向传输。在初始化函数最后将传输完成标志位清零。当然,在这之前还需要配置USART1的USART_DMACmd,开启串口发送DMA。配置代码如下:

     USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//配置为空闲中断 

     USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);

初始化完成之后就需要开始写USART1的中断服务函数,因为我们需要将USART1接收到的数据传送到DMA之中,所以在中断服务函数中需要在这个空闲中断中复制数据到DMA,首先是检查空闲中断标志位是否不为空,是则先关闭通道5,然后将两个数组初始化,最后打开通道5。

 在之后就是DMA打印功能了,其中分为两个函数,一个为打印函数,一个为数据处理函数,打印函数为带字符型指针参数和类型不确定的参数输入,函数名为void debug_printf(char* fmt,...),在函数中定义一个无符号的整形变量,使用va_list定义一个可变参数,使用va_start函数初始化这个参数,赋值为输入的字符指针,使用vsprintf函数将输入的值赋值给一个数组,最后使用va_end将指针置为无效,这就完成了输入形参的取值,再计算数组的长度,调用数据处理函数。数据处理函数为void debug_output(uint8_t* buff,uint16_t length),length为输入数据的长度,数据处理函数的功能是将收到的数据输出到外设,也就是实现打印功能的最后一步,首先失能DMA1的通道4,然后将length的值赋值给DMAI_Channel4->CNDTR,传输数量寄存器,将BUFF的值赋给CMAR寄存器(DMA1_Channel4->CMAR),最后使能DMA1的通道4,这样会使用DMA操控USART1来做打印功能。两个函数的代码如下:

void debug_output(uint8_t* buff,uint16_t length)
{
	DMA_Cmd(DMA1_Channel4, DISABLE);
	/* 设置DMA长度 和 地址 */
	DMA1_Channel4->CNDTR = length;
	DMA1_Channel4->CMAR = (uint32_t)buff;
	DMA_Cmd (DMA1_Channel4,ENABLE);		
}
/***********************************************************************************************************
* @描述	: 
***********************************************************************************************************/
void debug_printf(char* fmt,...) 
{  
	unsigned int length;
	/*可变参数输入*/
	va_list ap;
	va_start(ap,fmt);
	vsprintf(debug_buff,fmt,ap);
	va_end(ap);	
	/*可变参数输入*/
	length=strlen((const char*)debug_buff);
	debug_output((unsigned char*)debug_buff,length);	
}

 接下来只需要在主函数中直接调用debug_printf函数输出就可以实现打印功能了,这里借鉴了巍哥的代码哈,不要介意^_^。

在串口通信中使用DMA(直接内存访问)可以显著提高数据传输效率并减少CPU的负担。以下是在串口通信中实现DMA发送接收数据的基本方法: ### 串口DMA发送数据的实现 在发送数据时,DMA可以用来将数据从内存直接传输到串口的发送寄存器。这通常涉及到以下步骤: 1. 配置DMA通道以用于串口发送。这包括设置源地址(内存中的缓冲区)、目标地址(串口的数据寄存器)以及要传输的数据量。 2. 在DMA传输开始前,关闭DMA通道以便进行配置。 3. 设置DMA的数据传输计数器,指定需要发送的字节数。 4. 启用DMA通道,开始数据传输。 5. 当DMA传输完成后,可能会产生一个中断来通知CPU传输已经完成。 示例代码片段展示了如何初始化并启动DMA发送过程,假设使用的是STM32F103系列微控制器USART1: ```c void USART1_DMA_Begin_Send(uint8_t *send_buffer, uint16_t nSendBytes) { if (nSendBytes < USART1_DMA_TX_BUFFER_MAX_LENGTH) { memcpy(USART1_DMA_TX_Buffer, send_buffer, nSendBytes); DMA_Cmd(DMA1_Channel4, DISABLE); // 关闭DMA传输 DMA_SetCurrDataCounter(DMA1_Channel4, nSendBytes); // 设置数据传输量 DMA_Cmd(DMA1_Channel4, ENABLE); // 开启DMA传输 } } ``` ### 串口DMA接收数据的实现 对于接收数据DMA同样可以用来将数据从串口的接收寄存器直接传输到内存中的缓冲区。实现细节与发送类似,但方向相反: 1. 配置DMA通道以用于串口接收。设置源地址为串口的数据寄存器,目标地址为内存中的缓冲区,并且指定最大接收数据量。 2. 在DMA传输开始之前,同样需要先关闭DMA通道进行配置。 3. 设置DMA的数据传输计数器,指定可以接收的最大字节数。 4. 启用DMA通道,等待数据的到来。 5. 当DMA接收到指定数量的数据或检测到传输结束条件时,会触发一个中断来通知CPU处理接收到的数据。 为了处理不定长数据,通常会在DMA配置中使用循环模式或者在接收到特定的结束符后停止DMA传输。 ### 使用DMA的好处 - **减少CPU中断次数**:相比于每次发送或接收一个字节就触发一次中断,DMA可以在整个数据块传输完毕后仅触发一次中断。 - **提高数据吞吐量**:由于DMA可以在没有CPU干预的情况下完成数据传输,因此可以大大提高系统的数据处理能力。 通过上述方法,可以在串口通信中有效地利用DMA技术来优化数据传输性能[^2]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值