STM32LL库编程系列番外——DMA常用编程

前言

前面与很多工程都有使用到DMA,DMA在传输大量数据时有非常好的效果,但是DMA本人也是边接触,边学习,不断使用不断总结,这里给出本人认为一般情况下非常好用DMA发送与接收LL库配置代码。


本人目前只使用了DMA在串口外设和内存之间的传输,所以基于该任务编写代码,但是我相信在其他的引用上也殊途同归。
首先在cubeMX中配置好DMA的参数与中断,不熟悉得可以去看往期文章。

在dma.c文件编写DMA初始化函数如下:
这里说明一下,创建接收和发送数组,一般我喜欢接收数组比接收的数据位多一位,用来存储该数据格式是否正确。设置的接受数据长度也比传输的多一位。发送数组和数据长度按照要发送的数据一样,可以对需要发送的数组把帧头和帧尾直接初始化掉,避免其他程序每次的写入,更加精炼。如下所示:我需要接收数据的字节长度为14个字节,发送数据的字节长度为6字节。大家使用时按照这个模板套就好了。

//定义变量
uint8_t RS422_R_data[15];
uint8_t RS422_R_date_size = 15;
uint8_t RS422_T_data[6]={0xff,0xfe,0,0,0,0x0f};
uint8_t RS422_T_date_size = 6;
void USART2_DMA_init(void)
{
	//DMA接收配置
	LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_5, (uint32_t)&(USART2->DR));//设置外设地址
	LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_5, (uint32_t)RS422_R_data);//设置内存地址
	LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_5, RS422_R_date_size);//设置内存的数据长度

	LL_DMA_ClearFlag_TC5(DMA1);
	
	LL_USART_EnableDMAReq_RX(USART2);//启用串口DMA接收模式
	LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_5);//启动USART2_RX的DMA接收
	//DMA发送配置
	LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_6, (uint32_t)&(USART2->DR));//设置外设地址
	LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_6, (uint32_t)RS422_T_data);//设置内存地址
	LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_6, RS422_T_date_size);//设置内存的数据长度

		//清除中断标志位
	LL_USART_ClearFlag_TC(USART2);//按手册要求清除此标志位
	LL_DMA_ClearFlag_TC6(DMA1);
	LL_DMA_EnableIT_TC(DMA1,LL_DMA_STREAM_6);
	LL_USART_EnableDMAReq_TX(USART2);//启用串口DMA发送模式
	//LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_6);//启动USART2_RX的DMA发送
}

接收和发送配置按照上述配置一般情况下完全足够。注意在mian.c里添加上面函数。
在usart.c文件配置串口的空闲中断如下:

  /* USER CODE BEGIN USART2_Init 2 */
	LL_USART_ClearFlag_IDLE(USART2);
	LL_USART_EnableIT_IDLE(USART2);

  /* USER CODE END USART2_Init 2 */

接着就可以编写中断处理函数了
stm32f4xx_it.c中USART2的串口处理函数如下:
首先先判断是否是空闲中断,然后清除中断标志位,接着关闭DMA传输,接着的if-else判断是用来判断接收数据的帧头和帧尾是否正确,把标志位存储到数组最后一位,这也是为什么我前面说把接收数组比接收的数据位多一位的用处。然后清除DMA完全中断标志位,接着重新设置内存地址,再开启DMA传输。这样可以不断把数据存入刷新该数据。大家按这个模板套就行。

void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */
	if(LL_USART_IsActiveFlag_IDLE(USART2))//RS422数据接收空闲中断
	{
		LL_USART_ClearFlag_IDLE(USART2);
		LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_5);
		if((RS422_R_data[0]==0XFF)&&(RS422_R_data[1]==0xFE)&&(RS422_R_data[13]==0x0F))
		{
			RS422_R_data[14]=0xe0;//数据帧正确
		}
		else
		{
			RS422_R_data[14]=0x0e;//数据帧错误
		}
		LL_DMA_ClearFlag_TC5(DMA1);
		LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_5, (uint32_t)RS422_R_data);//设置内存地址
		LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_5);//启动UART5_RX的DMA传输
	}

  /* USER CODE END USART2_IRQn 0 */
  /* USER CODE BEGIN USART2_IRQn 1 */

  /* USER CODE END USART2_IRQn 1 */
}

这里我解释一下重要操作的意义:
1.明明DMA接收配置没有打开TC中断为什么还要清除TC中断标志位
如下图所示,每次传输完,DMA的TC标志都会硬件置1,无论你是否开启TC中断,都需要软件清0才可以开启下一次DMA传输(这也就是为什么清除TC中断标志位)。如果开启了TC中断,那么此时还会跳入到DMA中断服务函数。
在这里插入图片描述
在这里插入图片描述
2.为什么要重新设置内存地址
因为这里是USART的空闲中断,如图,空闲中断就是在空闲帧处触发,如果我传输的数据错乱了,比应该传输的字节少,那么重新设置内存地址可以保证下次传输的首地址正确,从而这次错误不影响下一次的数据传输,如果不这么干,下次传输的首地址错了,就会一直错下去。如果比应该传输的字节多,那么传输一轮后,下一轮就是比应该传输的字节少了,继续改正。
在这里插入图片描述
这个是DMA发送配置传输完成的TC中断服务函数,将发送的数据发送完后就会进入该中断,在中断清除标志位就可以等待下一次发送传输了。

void DMA1_Stream6_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream6_IRQn 0 */
	if(LL_DMA_IsActiveFlag_TC6(DMA1))//RS422控制板数据发送完成
	{
		LL_DMA_ClearFlag_TC6(DMA1);
	}

  /* USER CODE END DMA1_Stream6_IRQn 0 */

  /* USER CODE BEGIN DMA1_Stream6_IRQn 1 */

  /* USER CODE END DMA1_Stream6_IRQn 1 */
}

可以看到在DMA配置函数了,发送配置的DMA传输使能我是注释掉了,在把需要发送的数据处理好了,保存在发送数组里再开启发送使能。如下图每次传输完成时会自动关闭DMA,所以中断服务函数里不用再关闭了,但是需要清除中断标志位,不然下次没法发送也没法退出中断。
在这里插入图片描述

总结

到这里DMA在USART外设和内存之间的传输就完成了,大家使用时可以直接套用上述模板即可。该模板的优点是具有数据接收格式判断和数据错误纠正,在高速,大量的数据传输方面很实用。

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: STM32 LL是一种低级别的驱动,提供了对STM32微控制器底层寄存器的访问。SPI DMA(直接内存访问)是一种高效的数据传输方式,可以在通信过程中减少CPU的负担。 STM32 LL中提供了SPI和DMA的驱动函数,用户可以使用这些函数来实现SPI DMA传输。具体步骤如下: 1. 配置SPI口,设置传输速度、数据位数、时钟极性、时钟相位等参数。 2. 配置DMA,设置数据缓冲区、数据长度、传输方向等参数。接收数据时需要将DMA的传输方向设置为从外设到内存。 3. 启动DMA传输,并等待传输完成。传输完成后,可以通过DMA中断或查询方式来获取传输结果。 使用STM32 LL的SPI DMA传输可以提高数据传输的效率,并减少CPU的负担。在实际应用中,用户需要根据实际需求进行配置和调试,以实现更好的性能和稳定性。 ### 回答2: STM32系列芯片是一款广泛应用于嵌入式系统中的微控制器,而LL则是ST公司开发的一套轻量级固件,提供了一系列的低级驱动,并结合HAL使用,方便开发者进行芯片驱动的编写。 SPI (Serial Peripheral Interface)总线是一种同步串行通信协议,在通信中分为一主多从的结构,可同时连接多个设备。SPI总线传输的数据速率快,适合短距离以高速率进行数据传输。SPI总线传输协议中有一种DMA模式,即在传输时使用DMA进行高速且低开销的数据传输。 在STM32芯片中使用LL编写SPI DMA的驱动程序,首先要初始化所需的GPIO引脚,设置SPI的时序及工作模式。LM中提供了LL_SPI_Init()函数,方便控制SPI总线通信的初始化。接着,设置DMA请求的方向与传输模式,即将SPI的数据缓存区作为数据的源头或数据的接收端。 在使用STM32 LL编写SPI DMA的驱动程序时,需要首先学习一些关于LL中SPI和DMA的相关知识。SPI总线传输速度快、传输距离短,因此在嵌入式系统控制中应用广泛。同时,使用DMA模式进行数据传输可以减少CPU的使用率,提高数据传输效率,提高嵌入式系统的性能。 总之,LL提供了开发SPI DMA功能的方便工具,可以帮助开发者减轻编写驱动程序的负担,提高系统调试与运行的效率和可靠性。 ### 回答3: STM32是一系列由ST公司推出的ARM Cortex-M处理器,LL则是其官方提供的低级驱动,用于管理STM32芯片的外设。其中,SPI和DMA是两个重要的外设,下面将介绍STM32 LL中SPI DMA的应用。 SPI,全称Serial Peripheral Interface,是一种串行接口协议,它的主要特点是简单易用、高效可靠、传输速率快等。在STM32中,SPI是一种重要的外设,它可以进行多种模式的数据传输,比如全双工、半双工、主从等。SPI通常用于与外部器件进行数据交互,比如存储器、传感器、LCD等。 而DMA,全称Direct Memory Access,是一种直接存储器访问技术,它可以实现数据的高速传输,减轻CPU的负担。在STM32中,DMA是一种独立的硬件模块,可以提高数据传输效率,尤其在处理大批量数据时更为明显。 在STM32中使用SPI+DMA,可以提高数据传输的效率,减少CPU的占用率,提高系统的稳定性。在LL中,进行SPI+DMA传输可以分为以下步骤: 1. 初始化SPI和DMA模块:包括SPI的时钟配置、传输速率、工作模式,以及DMA的通道配置、数据长度等。 2. 启动DMA传输:首先将SPI的数据发送寄存器与DMA的内存地址关联,然后通过启动DMA传输,实现大批量数据的快速传输。 3. 等待传输完成:在DMA传输完成前,可以通过中断方式或轮询方式判断传输是否完成,等待传输完成后,即可进行下一步操作。 在使用SPI DMA传输时,需要注意以下几点: 1. SPI的数据传输模式必须是DMA模式,可以通过LL的SPI_ClearFlag_DMATransfer完成。 2. DMA传输完成后,必须及时禁用DMA传输中断和DMA通道,以确保数据不会重复传输。 3. 在初始化SPI DMA时,要根据实际情况选择合适的传输模式和缓冲区大小,以确保数据传输的稳定性和可靠性。 总之,SPI和DMASTM32中重要的外设,在LL的支持下,可以实现快速、高效、稳定的数据传输,提高系统的性能和可靠性。同时,在使用过程中,需要合理配置、正确使用,才能发挥它们的优势。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值