HAL-DMA中断空闲接受不定长数据


title: HAL-DMA中断空闲接受不定长数据
tags:

  • STM32
  • Hal
  • Cubemax

  面对无规律长度的数据帧如何处理?

  • 不定长数据接收可以使用每帧数据发送完成后会有一定的空闲时间"帧的时间间隔?"
    如果你想每帧都要可以采用dma加空闲中断的方式空闲中断一次就是一帧数据接受完成再去做其它数据解析和其他业务出来不过需要我们注意的是这里面有个小坑

__HAL_UART_CLEAR_IDLEFLAG(&huart1);

我先说下如何处理:
if (__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)== SET)
{
 uint16_t temp = 0;
 __HAL_UART_CLEAR_IDLEFLAG(&huart1);
 HAL_UART_DMAStop(&huart1);
 temp = huart1.Instance->SR;
 temp = huart1.Instance->DR;
 temp = hdma_usart1_rx.Instance->CNDTR;
 Usart1type.UsartDMARecLen  = USART1_DMA_REC_SIE - temp;
 HAL_UART_RxCpltCallback(&huart1);
}

  这个函数网上说什么有bug还是啥

  •  temp = huart1.Instance->SR;

  •  temp = huart1.Instance->DR;

  • 还要读一下这两寄存器

    原理很简单就是打开空闲中断函数

    触发中断后dma接收

    接收完后搬到接收数组里面清除dma数组

    做业务逻辑处理 处理完就全部数组清除

    操作步骤如下:

1.开启中断函数

void EnableUsart_IT(void){

  __HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);

  __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);

  __HAL_UART_CLEAR_IDLEFLAG(&huart1);

  HAL_UART_Receive_DMA(&huart1,Usart1type.Usart1DMARecBuffer,USART1_DMA_REC_SIE);

}

2.触发中断后dma接收

void USART1_IRQHandler(void)
{

 /* USER CODE BEGIN USART1_IRQn 0 */

if (__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)== SET)

{

 uint16_t temp = 0;

 __HAL_UART_CLEAR_IDLEFLAG(&huart1);

 HAL_UART_DMAStop(&huart1);

 temp = huart1.Instance->SR;

 temp = huart1.Instance->DR;

 temp = hdma_usart1_rx.Instance->CNDTR;

 Usart1type.UsartDMARecLen  = USART1_DMA_REC_SIE - temp;

 HAL_UART_RxCpltCallback(&huart1);


}



 /* USER CODE END USART1_IRQn 0 */

 HAL_UART_IRQHandler(&huart1);

 /* USER CODE BEGIN USART1_IRQn 1 */

 HAL_UART_Receive_DMA(&huart1,Usart1type.Usart1DMARecBuffer,USART1_DMA_REC_SIE); //再次打开DMA接收

 /* USER CODE END USART1_IRQn 1 */

}

3.接收完后搬到接收数组里面清除dma数组

/* USER CODE BEGIN 1 */

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){

 if (huart->Instance==USART1)

 {
  memcpy(&Usart1type.Usart1RecBuffer[Usart1type.UsartRecLen],Usart1type.Usart1DMARecBuffer,Usart1type.UsartDMARecLen);

   Usart1type.UsartRecLen=Usart1type.UsartDMARecLen;
   memset(Usart1type.Usart1DMARecBuffer,0x00,sizeof(Usart1type.Usart1DMARecBuffer));

   Usart1type.UsartRecFlag=1;

 }
    }
    

4.业务逻辑处理

我就简单的串口打印测试

/* Infinite loop */

 /* USER CODE BEGIN WHILE */

 while (1)

 {

   if (Usart1type.UsartRecFlag==1 )

   {

​    printf("re:%s,len%d\r\n",Usart1type.Usart1RecBuffer,Usart1type.UsartDMARecLen);

​    memset(Usart1type.Usart1RecBuffer,0x00,sizeof(Usart1type.Usart1RecBuffer));

​    Usart1type.UsartRecFlag=0;

​    Usart1type.UsartRecLen=0;

   

   }

  /* USER CODE END WHILE */



  /* USER CODE BEGIN 3 */

 }

 /* USER CODE END 3 */

}

 

参考代码:就一积分 给我点

串口空闲中断cubemax任意长度数据资源-CSDN文库

使用DMA空闲中断接收不定数据的步骤如下: 1. 初始化串口和DMA:配置串口为接收模式,启用DMA传输;配置DMA为循环模式,传输大小为1字节,传输方向为从串口接收数据到内存。 2. 开启DMA传输:调用DMA启动函数启动DMA传输。 3. 开启串口接收中断:调用串口中断使能函数,开启空闲中断。 4. 在空闲中断中处理数据:当DMA传输完成并且串口没有接收到新数据时,说明接收完成,可以在空闲中断中处理接收到的数据。 下面是一个简单的例子: ```c #include "stm32f4xx_hal.h" #define RX_BUF_SIZE 256 UART_HandleTypeDef huart1; DMA_HandleTypeDef hdma_usart1_rx; uint8_t rx_buf[RX_BUF_SIZE]; uint8_t rx_len = 0; uint8_t rx_flag = 0; void UART_Init(void) { huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } hdma_usart1_rx.Instance = DMA2_Stream2; hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4; hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart1_rx.Init.Mode = DMA_CIRCULAR; hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW; hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(&huart1, hdmarx, hdma_usart1_rx); HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn); HAL_UART_Receive_DMA(&huart1, rx_buf, RX_BUF_SIZE); } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { rx_len += RX_BUF_SIZE - hdma_usart1_rx.Instance->NDTR; HAL_UART_Receive_DMA(huart, rx_buf, RX_BUF_SIZE); } } void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { rx_len += RX_BUF_SIZE / 2 - hdma_usart1_rx.Instance->NDTR; } } void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { __HAL_UART_CLEAR_PEFLAG(&huart1); HAL_UART_Receive_DMA(huart, rx_buf, RX_BUF_SIZE); } } void DMA2_Stream2_IRQHandler(void) { HAL_DMA_IRQHandler(&hdma_usart1_rx); } void HAL_UART_IdleCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { if (hdma_usart1_rx.Instance->NDTR == RX_BUF_SIZE) { rx_len = RX_BUF_SIZE; rx_flag = 1; } else { rx_len = RX_BUF_SIZE - hdma_usart1_rx.Instance->NDTR; } } } int main(void) { HAL_Init(); UART_Init(); while (1) { if (rx_flag) { // 处理接收到的数据 rx_flag = 0; } } } ``` 在上面的例子中,我们使用了循环DMA传输模式,当接收到一定数量的数据后,将触发空闲中断,并在空闲中断中处理接收到的数据。同时,在DMA传输完成和空闲中断中,我们使用了两个不同的回调函数,分别处理DMA传输完成和空闲中断的事件。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值