stm32LL库串口空闲中断+DMA接收

stm32LL库串口空闲中断+DMA接收

俺用的STM32F407VGT6,cubemx生成的代码。

cubemx的串口配置串口配置
这里就按默认参数配就行,主要是要打开后面的DMA
在这里插入图片描述
在这里点添加就完事了,如果只用串口接收,那只开RX就行,我把TX也开了是为了方便以后拓展功能。
在这里插入图片描述
然后把串口的全局中断开了,好像到这里就完事了,这个是俺很久以前配的,可能有遗漏,不管那么多了。

配完了之后就是生成代码了,这里选用了LL库,之前用HAL库,拿DAC生成个正弦波都有一段会抽一下,换了LL库之后就没问题了。
在这里插入图片描述
然后打开工程,首先来到usart.c,可以看到,cubemx还是很贴心的帮我们生成好了代码,但是就是没帮我们初始化,所谓万事俱备只欠东风。

在这里插入图片描述
在这里把代码贴上来,需要注意的是这里的DMAmode得改成循环模式,不然数据接收到一次之后,你后面的数据再怎么变它都不会刷新。。。

  LL_DMA_SetChannelSelection(DMA2, LL_DMA_STREAM_2, LL_DMA_CHANNEL_4);

  LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_STREAM_2, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);

  LL_DMA_SetStreamPriorityLevel(DMA2, LL_DMA_STREAM_2, LL_DMA_PRIORITY_HIGH);

  LL_DMA_SetMode(DMA2, LL_DMA_STREAM_2, LL_DMA_MODE_CIRCULAR);

  LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_STREAM_2, LL_DMA_PERIPH_NOINCREMENT);

  LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_STREAM_2, LL_DMA_MEMORY_INCREMENT);

  LL_DMA_SetPeriphSize(DMA2, LL_DMA_STREAM_2, LL_DMA_PDATAALIGN_BYTE);

  LL_DMA_SetMemorySize(DMA2, LL_DMA_STREAM_2, LL_DMA_MDATAALIGN_BYTE);

  LL_DMA_DisableFifoMode(DMA2, LL_DMA_STREAM_2);

对了,顺便附带一下LL库实现printf的操作,弄个这个函数就完事了

在这里插入图片描述

int fputc(int ch, FILE *f)

{

	LL_USART_TransmitData8(USART1,ch);
	while(!LL_USART_IsActiveFlag_TC(USART1))
	{
	}

	return ch;

}

咳咳,言归正传,下面来到stm32f4xx_it.c,加入这个函数,也就是空闲中断的回调函数。

在这里插入图片描述

void USART_RxIdleCallback(void)
{

		uint8_t i;

		if(LL_USART_IsActiveFlag_IDLE(USART1))
			
		{
			
			//先清标志位
			
			LL_USART_ClearFlag_IDLE(USART1); 					
						
			if(UartRxDmaBuf[2] == 0xEE)
				
			{
				
				NVIC_SystemReset();
				
			}
			
			//先停止UART流DMA,暂停接收
			
			LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_2); 
			
			
			/* 3.搬移数据进行其他处理 */
			
			for(i=0; i<8; i++)
			{
				
				UartRxBuf[i] = UartRxDmaBuf[i];

			}
									
			UartRxflag = 1; //标志已经成功接收到一包等待处理
			
			LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_2, 8);
			
			/* 4.开启新的一次DMA接收 */
			
			LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_2);
									
		}
		
}

中间有个串口发送特定数据reset单片机的操作,不需要的可以去掉。

在这里插入图片描述
然后就是改写串口中断处理函数啦,把之前写的函数往里面一丢,然后再清一下标志位,就完事了。

    USART_RxIdleCallback();
	
	LL_USART_ClearFlag_RXNE(USART1);

额额,这里的DMA中断处理函数应该是没有用到,我反正是没开DMA的中断,但是不知道不写会不会有啥影响,也贴出来吧,个人感觉不写是不会有问题的,如果拿这篇文章做验证的话可以先不写。

在这里插入图片描述
OK,事已至此,木已成舟,一切准备工作皆已就绪,相当于导弹造好了,就差摁按钮了。

首先先搞个变量出来接收DMA的数据,对了,忘记说了,这玩意迁移数据好像没多大意义,两个好像是一起刷新的,算了不管了,反正F4内存大,也不差这点空间。
在这里插入图片描述

uint8_t UartRxBuf[8];							 //迁移的上位机数据
uint8_t UartRxDmaBuf[8];           //上位机传输的原始数据
uint8_t UartRxflag = 0;            //数据待处理标志

然后在主函数里加上这些初始化的东西就大功告成啦,记得放在串口初始化函数后面哈,因为串口初始化函数里面还有着一些DMA初始化的操作,所以要先完成。

在这里插入图片描述

	LL_DMA_SetPeriphAddress(DMA2, LL_DMA_STREAM_2, (uint32_t)(&USART1->DR));
    LL_DMA_SetMemoryAddress(DMA2, LL_DMA_STREAM_2, (uint32_t)UartRxDmaBuf);
    LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_2, 8);
	LL_USART_EnableIT_RXNE(USART1);   //使能串口1的中断
	LL_USART_EnableIT_IDLE(USART1);   //使能串口1的空闲中断
	
	LL_USART_EnableDMAReq_RX(USART1);
	LL_DMA_EnableStream(DMA2,LL_DMA_STREAM_2);

额额,稍微解释一下,第一个是设置外设的地址,因为之前在串口初始化里已经将DMA设置为外设到内存了,就是下面那段代码,在串口初始化里面可以找到,这里就不用再写一次了。

 LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_STREAM_2, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);

第二个是设置内存地址,就不用多说了。

第三个是传输的长度,之前的数组设置成多大的这里参数就给多少。

这里讲一下,F4和F1两者LL库的区别,之前在网上查,F1的初始化函数,都不是设置STREAM,而是设置CHANNEL,我想是因为F4有DMA控制器的原因,在F4上多了个这么玩意(请忽视我原谅色的护眼背景)
在这里插入图片描述
而F1应该就是直接CHANNEL就接到仲裁器了吧,手头上没有F1的参考手册,姑且就发挥主观能动性臆测一下。

好了,行文至此,就此搁笔。

具体的还是需要花时间去参透,只有理解了其如何运行,才能得心应手地来配置。

这波,这波是在大气层。在这里插入图片描述

  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个简单的STM32串口空闲中断DMA接收程序的示例: ``` #include "stm32f4xx.h" // 定义DMA缓冲区大小 #define BUFFER_SIZE 256 // 定义DMA接收缓冲区 uint8_t dma_buffer[BUFFER_SIZE]; int main(void) { // 初始化串口 USART_InitTypeDef usart_init_struct; usart_init_struct.USART_BaudRate = 115200; usart_init_struct.USART_WordLength = USART_WordLength_8b; usart_init_struct.USART_StopBits = USART_StopBits_1; usart_init_struct.USART_Parity = USART_Parity_No; usart_init_struct.USART_Mode = USART_Mode_Rx; USART_Init(USART1, &usart_init_struct); USART_Cmd(USART1, ENABLE); // 初始化DMA DMA_InitTypeDef dma_init_struct; dma_init_struct.DMA_Channel = DMA_Channel_4; dma_init_struct.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR); dma_init_struct.DMA_Memory0BaseAddr = (uint32_t)dma_buffer; dma_init_struct.DMA_DIR = DMA_DIR_PeripheralToMemory; dma_init_struct.DMA_BufferSize = BUFFER_SIZE; dma_init_struct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; dma_init_struct.DMA_MemoryInc = DMA_MemoryInc_Enable; dma_init_struct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; dma_init_struct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; dma_init_struct.DMA_Mode = DMA_Mode_Circular; dma_init_struct.DMA_Priority = DMA_Priority_High; dma_init_struct.DMA_FIFOMode = DMA_FIFOMode_Disable; dma_init_struct.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; dma_init_struct.DMA_MemoryBurst = DMA_MemoryBurst_Single; dma_init_struct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream2, &dma_init_struct); // 启动DMA传输 DMA_Cmd(DMA2_Stream2, ENABLE); // 配置串口空闲中断 USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 启动串口 USART_Cmd(USART1, ENABLE); while (1) { // 空闲中断触发后处理接收到的数据 if (USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) { USART_ClearITPendingBit(USART1, USART_IT_IDLE); uint16_t length = BUFFER_SIZE - DMA_GetCurrDataCounter(DMA2_Stream2); // 处理接收到的数据 // ... // 重新启动DMA传输 DMA_Cmd(DMA2_Stream2, ENABLE); } } } // 串口中断处理函数 void USART1_IRQHandler(void) { if (USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) { // 清除中断标志 USART_ClearITPendingBit(USART1, USART_IT_IDLE); } } ``` 该程序使用了DMA接收串口数据,并使用了空闲中断来触发数据处理。在空闲中断处理函数中,首先需要获取接收到的数据的长度,然后进行数据处理。处理完毕后,再重新启动DMA传输。注意,在空闲中断处理函数中,需要清除中断标志。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值