stm32串口采用循环队列+DMA方式接收数据调试总结

本文分享了在项目中使用DMA进行串口接收时遇到的问题及解决方法。重点介绍了DMA通道选择的重要性,以及如何正确配置DMA传输数量寄存器DMA_CNDTRx,确保数据能够准确地从串口搬运到内存。

最近在项目中给串口的接收添加DMA,遇到的问题:

1、“配置好”DMA后,但是DMA不工作

初始化串口1为接收、DMA1的通道3,并使能相应的外设,外设的时钟也全部打开,但是通过调试发现,DMA就是不传输数据。

问题点:忽视了DMA的请求映像

每个DMA控制器都分管着不同的外设DMA请求,通过查表发现串口1的接收只能映射到DMA1的通道5.因此DMA的通道选择错误。现粘贴串口一的中断处理程序如下:(以下程序并没有采用循环队列的方式接受数据)

static unsigned char APP_ComBuf[100];
unsigned char Receive_Message[100] = {0};
static int APP_iConter = 0;
void USART1_IRQHandler(void)                	
{
	u8 Res,i;
	OS_ERR err;
#ifdef SYSTEM_SUPPORT_OS	 	
	OSIntEnter();    
#endif
	if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)   
  {
		   
       DMA_Cmd(DMA1_Channel5,DISABLE);    
       APP_iConter=USART1->DR;//
       APP_iConter=USART1->SR;
       APP_iConter=100-DMA_GetCurrDataCounter(DMA1_Channel5);//  
       APP_ComBuf[APP_iConter]=0;//
       DMA1_Channel5->CNDTR=100;//重新设置传输量,这一步是必须的。
			 if(APP_iConter>=6)
			 {
					for(i=0;i<APP_ComBuf[2];i++)
					{
						Receive_Message[i] = APP_ComBuf[i];
					}
				  memset(APP_ComBuf,0,sizeof(APP_ComBuf));					
				  OSTaskQPost((OS_TCB*	)&APPTCB,	
								     (void*		)Receive_Message,
								     (OS_MSG_SIZE)Receive_Message[2],
								     (OS_OPT		)OS_OPT_POST_FIFO,
				             (OS_ERR*	)&err);
			 }
       DMA_Cmd(DMA1_Channel5,ENABLE);  
  }
	 NVIC_Clear(USART1);

#ifdef SYSTEM_SUPPORT_OS	 
	OSIntExit();  											 
#endif
} 	

在设置好DMA的通道后,每收到数据后,使能DMA后,DMA会自动将串口接收到的数据搬运到存储区App_ComBuff中,如果没有DMA1_Channel5->CNDTR=100;这一步,或者该步配置错误,那么每当串口接收到的数据都会向存储区App_ComBuff中存储,App_ComBuff的地址递增。于此同时APP_iConter也在累积,APP_ComBuf[2]也不能被解析到,因为数据随着APP_ComBuf地址的递增,数据都往后存储,帧长度并未存储到APP_ComBuf[2]中,因此自己认为的APP_ComBuf[2]存储的是帧长度,但是APP_ComBuf[2]始终是0。再次理解一下 DMA1_Channel5->CNDTR=100;这句话的必要性,串口采用的是空闲中断接收数据,并非采用的是接收中断,因此当串口处于空闲中断时,这时候已经传输完成了一帧数据。因此,将 DMA1_Channel5->CNDTR=100;便可以在下次空闲中断时,将串口接收数据寄存器中的数据搬运到存储器中的首地址。这样for(i=0;i<APP_ComBuf[2];i++)这个for循环也才可以得以执行。另:DMA通道x的传输数量寄存器DMA_CNDTRx,当这个寄存器的内容为0时,无论通道是否开启,都不会发生任何数据传输。以上是本人通过DMA接收数据的运用的肤浅理解。

关于采用队列的方式接收串口数据,后面继续更新,谢谢。

### 实现 STM32 串口 DMA 循环缓冲区无丢包 为了确保在高波特率下(如921600bps)实现稳定的数据传输并防止丢包,可以采用循环缓冲区配合DMA方式。这种方法不仅提高了效率还简化了编程逻辑。 #### 配置 UART 和 DMA 参数 设置UART接口时需特别注意参数的选择以匹配高速通信需求。对于STM32来说,在初始化阶段要指定使用DMA模式,并开启相应的中断服务函数用于处理接收完成事件[^1]。 ```c // 初始化USART外设结构体 static void MX_USARTx_UART_Init(void) { huart->Instance = USARTx; /* 设置波特率为921600 */ huart->Init.BaudRate = 921600; // 启用硬件流控制(HW Flow Control)如果需要的话 // 开启DMA模式 huart->AdvancedInit.AdvFeatureInit |= UART_ADVFEATURE_DMADISABLEONERROR_INIT; HAL_UART_DeInit(huart); if (HAL_UART_Init(huart) != HAL_OK){ Error_Handler(); } } ``` #### 创建循环缓冲区 创建两个独立的缓冲区分别作为发送和接收用途。每个缓冲区大小应根据实际应用情况设定,考虑到最坏情况下可能接收到的最大数据量加上一定的冗余空间。这里假设每边分配了一个4KB大小的空间。 ```c #define BUFFER_SIZE 4096 uint8_t rx_buffer[BUFFER_SIZE]; volatile uint16_t rx_head,rx_tail; ``` #### 处理 DMA 完成中断 当DMA完成了当前批次的数据转移后会触发此中断。此时应该更新指向下一个待填充位置的索引值,并重新启动新的DMA事务继续监听后续到来的信息片段。同时也要在此处检查是否有未处理完毕的有效载荷等待应用程序层去消费掉它们。 ```c void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USARTx){ // 更新头指针到下一个可用槽位 rx_head += huart->hdmarx->Instance->NDTR; while(rx_head >= BUFFER_SIZE){rx_head -= BUFFER_SIZE;} // 如果有新消息到达则唤醒上层任务进行解析 if ((rx_head + 1) % BUFFER_SIZE != rx_tail){ osThreadFlagsSet(TaskHandle, DATA_READY_FLAG); } // 继续准备接受下一帧数据 __HAL_DMA_CLEAR_FLAG(&hdma_usart_rx, HDMA_FLAG_TC); HAL_UART_Receive_DMA(huart, &rx_buffer[rx_head], RX_BUFFER_SIZE); } } ``` #### 超时机制设计 引入定时器监控整个过程中的异常状况比如长时间未能成功获取有效字符或者出现了连续错误等情况。一旦检测到上述情形发生,则采取适当措施恢复正常的通讯状态,例如重置连接或是发出警告信号给用户界面提示存在潜在问题。 ```c void TIMx_IRQHandler(void) { if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE)){ static uint32_t last_time=0; uint32_t current_time=__HAL_TIM_GetCounter(htim); if((current_time-last_time)>TIMEOUT_LIMIT){ Handle_Timeout_Error(); }else{ last_time=current_time; } __HAL_TIM_CLEAR_IT(htim,TIM_IT_UPDATE); } } ``` 通过以上方法可以在STM32平台上构建起一套高效可靠的基于DMA技术的全双工异步串行链路解决方案,满足大容量快速交换的需求的同时保持较低CPU占用率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值