STM32串口DMA接收双缓冲

STM32高端MCU(F4、F7等)才支持DMA双缓冲,低端MCU(F1)不支持DMA双缓冲,不过有替代方案可实现类型效果。

一、MCU支持DMA双缓冲的情形

不再赘述,参见博客 STM32 串口DMA发送+DMA接收+硬件双缓冲区切换功能实现

二、MCU不支持DMA双缓冲,但可通过DMA传输半完成中断替代,以下代码已在F103上验证通过。

1.先通过STM32CubeMX生成串口初始化代码

串口接收DMA一定要选择Circular模式,并且使能串口接收中断

串口接收DMA一定要选择Circular模式

使能串口接收中断

2.使能空闲中断,空闲中断中取出接收数据

3.在DAM接收半完成、完成中断中取出接收数据

usart.c:

/* Includes ------------------------------------------------------------------*/
#include "usart.h"

/* USER CODE BEGIN 0 */

#define DE_UART_DMA_BUF_LEN    0x1E
static uint8_t RecvDMABuf[DE_UART_DMA_BUF_LEN];

/* USER CODE END 0 */

UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_tx;
DMA_HandleTypeDef hdma_usart1_rx;

/* USART1 init function */

void MX_USART1_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_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART1 DMA Init */
    /* USART1_TX Init */
    hdma_usart1_tx.Instance = DMA1_Channel4;
    hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_tx.Init.Mode = DMA_NORMAL;
    hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx);

    /* USART1_RX Init */
    hdma_usart1_rx.Instance = DMA1_Channel5;
    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;
    if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx);

    /* USART1 interrupt Init */
    HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspInit 1 */

    /*Enable the IDLE Interrupt*/
    __HAL_UART_ENABLE_IT(uartHandle,UART_IT_IDLE);
    __HAL_UART_CLEAR_IDLEFLAG(uartHandle);
    
  /* USER CODE END USART1_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspDeInit 0 */

  /* USER CODE END USART1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART1_CLK_DISABLE();

    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);

    /* USART1 DMA DeInit */
    HAL_DMA_DeInit(uartHandle->hdmatx);
    HAL_DMA_DeInit(uartHandle->hdmarx);

    /* USART1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspDeInit 1 */

  /* USER CODE END USART1_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */

/*
 * This function is called on DMA TC or HT events, and on UART IDLE (if enabled) event.
 */
void  UsartProcessData(const void* data, size_t len) 
{
     uint8_t* pData = (uint8_t*)data;
//    DebugPrintf("RecvLen:%d\n",len);
//    DebugHexPrint(pData,len);
}

void  UsartRxCheck(UART_HandleTypeDef *huart) 
{
    static uint16_t OldPos = 0;
    /* Calculate current position in buffer and check for new data available */
    uint16_t NewPos = DE_UART_DMA_BUF_LEN - __HAL_DMA_GET_COUNTER(huart->hdmarx);

    if (NewPos != OldPos)     /* Check change in received data */
    {                       
        if (NewPos > OldPos)  /* Current position is over previous one */
        {                    
            /*
             * Processing is done in "linear" mode.
             *
             * Application processing is fast with single data block,
             * length is simply calculated by subtracting pointers
             */
            UsartProcessData(&RecvDMABuf[OldPos], NewPos - OldPos);
        }
        else
        {
            /*
             * Processing is done in "overflow" mode..
             *
             * Application must process data twice,
             * since there are 2 linear memory blocks to handle
             */
            UsartProcessData(&RecvDMABuf[OldPos], DE_UART_DMA_BUF_LEN - OldPos);
            if (NewPos > 0) 
            {
                UsartProcessData(&RecvDMABuf[0], NewPos);
            }
        }
        OldPos = NewPos;   /* Save current position as old for next transfers */
    }
}

/**
  * @brief  Rx Half Transfer completed callbacks.
  * @param  huart  Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval None
  */
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == huart1.Instance)
    {
        //DebugPrintf("HAL_UART_RxHalfCpltCallback\n");
        UsartRxCheck(huart);
    }
}

/**
  * @brief  Rx Transfer completed callbacks.
  * @param  huart  Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval None
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == huart1.Instance)
    {
        //DebugPrintf("HAL_UART_RxCpltCallback\n");
        UsartRxCheck(huart);
    }
}

/**
  * @brief  Rx IDLE callback
  * @param  huart: UART handle. 
  * @note   This example shows a simple way to report end of DMA Tx transfer, and 
  *         you can add your own implementation. 
  * @retval None
  */
static void UART_RxIdleCallBack(UART_HandleTypeDef *huart)
{
    if(huart->Instance == huart1.Instance)
    {
        //DebugPrintf("UART_IDLE_CallBack\n");
        UsartRxCheck(huart);
    }
}

/**
  * @brief  UART_IDLE_IRQHandler
  * @param  huart  Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval HAL status
  */
void UART_IDLE_IRQHandler(UART_HandleTypeDef *huart)
{
    if(__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET)
    {
         __HAL_UART_CLEAR_IDLEFLAG(huart);
         __HAL_UART_FLUSH_DRREGISTER(huart);
         UART_RxIdleCallBack(huart);
    }
}

void UART_StartReceive(void)
{
    HAL_UART_Receive_DMA(&huart1,RecvDMABuf,sizeof(RecvDMABuf));
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

1、此时DMA缓冲区类似一个循环buffer,接收满后自动从头部接收。
2、在DMA接收半完成中断和DMA接收完成中断之后的空闲中断中不能重复取接收数据。
3、该方法也适用于F4、F7等高端MCU。

  • 9
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 18
    评论
使用STM32的DMA(直接内存访问)功能可以实现串口数据的快速接收。下面是一个简单的示例代码: ```c #include "stm32f10x.h" #define UART_RX_DMA_CH DMA1_Channel5 #define UART_RX_DMA_FLAG_TC DMA1_FLAG_TC5 uint8_t uart_rx_buf[64]; void uart_init(void) { USART_InitTypeDef usart_init; // 串口配置 usart_init.USART_BaudRate = 115200; usart_init.USART_WordLength = USART_WordLength_8b; usart_init.USART_StopBits = USART_StopBits_1; usart_init.USART_Parity = USART_Parity_No; usart_init.USART_Mode = USART_Mode_Rx; usart_init.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Init(USART1, &usart_init); // 使能DMA接收模式 USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); DMA_Cmd(UART_RX_DMA_CH, DISABLE); DMA_DeInit(UART_RX_DMA_CH); DMA_InitTypeDef dma_init; dma_init.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; dma_init.DMA_MemoryBaseAddr = (uint32_t)uart_rx_buf; dma_init.DMA_DIR = DMA_DIR_PeripheralSRC; dma_init.DMA_BufferSize = sizeof(uart_rx_buf); dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable; dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable; dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; dma_init.DMA_Mode = DMA_Mode_Circular; dma_init.DMA_Priority = DMA_Priority_High; dma_init.DMA_M2M = DMA_M2M_Disable; DMA_Init(UART_RX_DMA_CH, &dma_init); DMA_Cmd(UART_RX_DMA_CH, ENABLE); } int main(void) { uart_init(); while (1) { // 检查DMA接收是否完成 if (DMA_GetFlagStatus(DMA1_FLAG_TC5)) { DMA_ClearFlag(DMA1_FLAG_TC5); // DMA接收完成,处理接收到的数据 for (int i = 0; i < sizeof(uart_rx_buf); i++) { // 处理接收到的数据 } // 重新使能DMA接收 DMA_Cmd(UART_RX_DMA_CH, DISABLE); DMA_SetCurrDataCounter(UART_RX_DMA_CH, sizeof(uart_rx_buf)); DMA_Cmd(UART_RX_DMA_CH, ENABLE); } } } ``` 上述代码中,我们使用了USART1作为串口,并使能了DMA接收模式。在主循环中,我们检查DMA接收是否完成,如果完成,则处理接收到的数据,并重新使能DMA接收。注意,这里我们使用了循环模式的DMA,即DMA接收缓冲区满后会重新从头开始接收数据。如果需要处理较长的数据包,建议使用双缓冲模式的DMA

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值