【STM32】串口DMA之双缓冲

5 篇文章 2 订阅
3 篇文章 1 订阅

【STM32】串口DMA之双缓冲

前言

在前两章节中已经讲述了串口的通常用法, 【STM32】CubeMX+HAL库之串口
以及串口DMA空闲中断不定长接收与发送, 【STM32】串口DMA空闲中断不定长收发配自定义装包与解包

在本章节我们将介绍DMA双缓冲这一技术。在很多时候数据的发送频率与数据接收使用频率并不一致,数据来的太快,接收者还没来得及对其进行处理,下一帧的数据就到了,并将其覆盖,致使信息的有效性大大减弱。这时我们增加一个缓冲区来存放来不及处理的数据,就能更好地完成任务。双缓冲还有一个重要的应用是在图像部分,可以更好的解决显示图像时的闪烁延迟问题。

所用工具:

  1. 开发板:野火挑战者STM32H743IIT6
  2. STM32CubeMX
  3. IDE: Keil-MDK

满足怎样的需求

  1. 利用DMA传输方式节约CPU资源
  2. 利用串口空闲中断来拉起处理函数
  3. 提供掉线重启DMA的能力,保证热插拔的稳定性

总述流程

  1. 在主函数中调用初始化函数
    main.c
/* USER CODE BEGIN PFP */
extern void uartInit(UART_HandleTypeDef *huart);
/* USER CODE END PFP */
 
  /* USER CODE BEGIN 2 */
  uartInit(&huart1);
  /* USER CODE END 2 */
  1. bsp_uart.c中的定义
uint8_t rx_buf[2][RX_BUFLEN]; //双缓冲Buffer

DMA_Stream_TypeDef *DMA_DBuf;
  1. 初始化函数实体
    此部分初始化两个Buffer,与前部分的区别十分明显。
    bsp_uart.c
void uartInit(UART_HandleTypeDef *huart)
{
	DMA_DBuf = huart->hdmarx->Instance;
	//enable the DMA transfer for the receiver request
  //使能DMA串口接收
  SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);
  //enalbe idle interrupt
  //使能空闲中断
  __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);

  //disable DMA
  //失效DMA
  __HAL_DMA_DISABLE(huart->hdmarx);
  while(DMA_DBuf->CR & DMA_SxCR_EN)
  {
    __HAL_DMA_DISABLE(huart->hdmarx);
  }

  DMA_DBuf->PAR = (uint32_t) & (USART1->RDR); //F4中为DR
  //memory buffer 1
  //内存缓冲区1
  DMA_DBuf->M0AR = (uint32_t)(rx_buf[0]);
  //memory buffer 2
  //内存缓冲区2
  DMA_DBuf->M1AR = (uint32_t)(rx_buf[1]);
  //data length
  //数据长度
	huart->RxXferSize = RX_BUFLEN * 2;
  DMA_DBuf->NDTR = RX_BUFLEN * 2;
  //enable double memory buffer
  //使能双缓冲区
  SET_BIT(DMA_DBuf->CR, DMA_SxCR_DBM);

  //enable DMA
  //使能DMA
  __HAL_DMA_ENABLE(huart->hdmarx);
}
  1. 回调函数实体
    在处理函数中也分别对两个Buffer进行处理。
    bsp_uart.c
void BSP_UART_Callback(UART_HandleTypeDef *huart)
{
	DMA_DBuf = huart->hdmarx->Instance;
	
	if(huart->Instance == USART1)
  {
    if(huart->Instance->ISR & UART_FLAG_RXNE)//接收到数据  F4 为SR
    {
      __HAL_UART_CLEAR_PEFLAG(huart);
    }
    else if(huart->Instance->ISR & UART_FLAG_IDLE) 
    {
      static uint16_t this_time_rx_len = 0;

      __HAL_UART_CLEAR_PEFLAG(huart);
      //disable DMA
      //失效DMA
      __HAL_DMA_DISABLE(huart->hdmarx);
      //get receive data length, length = set_data_length - remain_length
      //获取接收数据长度,长度 = 设定长度 - 剩余长度
      this_time_rx_len = huart->RxXferSize - DMA_DBuf->NDTR;
      //reset set_data_lenght
      //重新设定数据长度
      DMA_DBuf->NDTR = 36;

      if ((DMA_DBuf->CR & DMA_SxCR_CT) == RESET)
      {
        //set memory buffer 1
        //设定缓冲区1
        DMA_DBuf->CR |= DMA_SxCR_CT;
        //enable DMA
        //使能DMA
        __HAL_DMA_ENABLE(huart->hdmarx);
        if(this_time_rx_len == 18)
        {
          //LoadData(0);  此部分写自己的解包程序
        }
      }
      else
      {
        //设定缓冲区2
        DMA1_Stream1->CR &= ~(DMA_SxCR_CT);
        //enable DMA
        //使能DMA
        __HAL_DMA_ENABLE(huart->hdmarx);
        if(this_time_rx_len == 18)
        {
          //LoadData(1);    此部分写自己的解包程序
        }
      }
    }
  }
}```

 5. 在stm32h7xx_it.c中加入自定义回调函数

```c
/* USER CODE BEGIN PFP */
extern void BSP_UART_Callback(UART_HandleTypeDef *huart);
/* USER CODE END PFP */

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	BSP_UART_Callback(&huart1);
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

双缓冲的使用到这里就结束了,H7与其他芯片略有不同,需要自行找下问题,在代码中基本也都有注释,此部分学的还不够透彻,还望大佬指教更加官方的写法。

  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值