stm32 uart dma idle 接收 不定个数字符

传说中的stm32 idle dma来做串口接收, stm32f405rg的uart1测了一下. 。

1.可能开机时, 如果rx一直是高,会置idle 为高,如要依次读取sr, dr来清掉,

   以后只有当uart接收到一个正常的byte之后, 有一个持续一个byte的空闲高电平,就会触发idle中断

 (典型的场景是一个连续发送的帧的最后一个字节接收完成后)

2.dma在预先设置的传输量传输完成的时候,会触发dma完成中断

   如果在其他地方,对已经开始但尚未完成的dma,软件设置disable的时候,也会触发dma完成中断。

   所以在uart空闲中断isr里面, 可以读取dma已经传输的部分数据,  可以只 设置diable dma, 让dma中断去处理未完成的接收。

3.dma一次传输大小的选择。

   根据cpu的频率, (168M)

   和isr处理的时长,  (大致认为每条命令等周期, 看反汇编,得出isr的长度, 里面有个循环来处理dma缓冲的大小)

   和波特率  (115200)

   大致算出dma缓冲为30字节, 否则长了, 在一次dma传输正常完成中断里搬迁的时候, 花的时间长了,很多字节就无法接收了。

   实际测试30字节是不行的, 20字节连续收发300k,字节无差错。

 

main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

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

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "ringbuff.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
ringbuff rb_uart1;
char rx_ring_buff[1000];

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern uint8_t uart_dma_rx_buff[20];
typedef struct
{
  __IO uint32_t ISR;   /*!< DMA interrupt status register */
  __IO uint32_t Reserved0;
  __IO uint32_t IFCR;  /*!< DMA interrupt flag clear register */
} DMA_Base_Registers;
extern void DMA_SetConfig(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength);


/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */



int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */
  

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  /*
  1.读取寄存器, 清除idle中断
  2.设置启用dma读取
  3.写到fifo
  4.循环读取fifo处理
  5.dma中断和idle中断,类似
     如果是dma完成中断,清除中断,读取数据写到fifo, 重新开始dma
     如果是idle中断,清除idle, 停止dma传输, 读取都fifo, 重新开始dma


   huart1

     
  */
     ringbuff_init(&rb_uart1, rx_ring_buff, sizeof(rx_ring_buff));
     __HAL_DMA_DISABLE(&hdma_usart1_rx);
     DMA_Base_Registers *regs = (DMA_Base_Registers *)hdma_usart1_rx.StreamBaseAddress;
     //hdma_usart1_rx.Instance->NDTR = sizeof(uart_dma_rx_buff);

     
     DMA_SetConfig(&hdma_usart1_rx, (uint32_t)(&(huart1.Instance->DR)), (uint32_t)(uart_dma_rx_buff), sizeof(uart_dma_rx_buff));
     
     regs->IFCR = 0x3FU << hdma_usart1_rx.StreamIndex;
     
     /* Enable Common interrupts*/
     hdma_usart1_rx.Instance->CR  |= DMA_IT_TC | DMA_IT_DME;

     __HAL_DMA_ENABLE(&hdma_usart1_rx);
     
     READ_REG(huart1.Instance->SR);
     READ_REG(huart1.Instance->DR);
     READ_REG(huart1.Instance->CR1);
     READ_REG(huart1.Instance->CR3);
     
     SET_BIT(huart1.Instance->CR3, USART_CR3_DMAR);
     SET_BIT(huart1.Instance->CR1, USART_CR1_IDLEIE);
     SET_BIT(huart1.Instance->CR3, USART_CR3_EIE);


  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    static char temp[100];
    int n = 0;
    int i = 0;
    char ch = 0;
    n = ringbuff_get(&rb_uart1, temp, sizeof(temp));
    for (i = 0; i < n; i++)
    {
        ch = temp[i];
        while((huart1.Instance->SR & UART_FLAG_TXE) == 0);
        huart1.Instance->DR = ch;
    }

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage 
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  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();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

/** 
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void) 
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA2_Stream2_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{ 
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

 

stm32f4xx_it.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    stm32f4xx_it.c
  * @brief   Interrupt Service Routines.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f4xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "ringbuff.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */

/* USER CODE END TD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
 
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void UART1_IRQHandler(UART_HandleTypeDef *huart);
void DMA2_UART_IRQHandler(DMA_HandleTypeDef *hdma);


/* USER CODE END 0 */

/* External variables --------------------------------------------------------*/
extern DMA_HandleTypeDef hdma_usart1_rx;
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN EV */

/* USER CODE END EV */

/******************************************************************************/
/*           Cortex-M4 Processor Interruption and Exception Handlers          */ 
/******************************************************************************/
/**
  * @brief This function handles Non maskable interrupt.
  */
void NMI_Handler(void)
{
  /* USER CODE BEGIN NonMaskableInt_IRQn 0 */

  /* USER CODE END NonMaskableInt_IRQn 0 */
  /* USER CODE BEGIN NonMaskableInt_IRQn 1 */

  /* USER CODE END NonMaskableInt_IRQn 1 */
}

/**
  * @brief This function handles Hard fault interrupt.
  */
void HardFault_Handler(void)
{
  /* USER CODE BEGIN HardFault_IRQn 0 */

  /* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
    /* USER CODE END W1_HardFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Memory management fault.
  */
void MemManage_Handler(void)
{
  /* USER CODE BEGIN MemoryManagement_IRQn 0 */

  /* USER CODE END MemoryManagement_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
    /* USER CODE END W1_MemoryManagement_IRQn 0 */
  }
}

/**
  * @brief This function handles Pre-fetch fault, memory access fault.
  */
void BusFault_Handler(void)
{
  /* USER CODE BEGIN BusFault_IRQn 0 */

  /* USER CODE END BusFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_BusFault_IRQn 0 */
    /* USER CODE END W1_BusFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Undefined instruction or illegal state.
  */
void UsageFault_Handler(void)
{
  /* USER CODE BEGIN UsageFault_IRQn 0 */

  /* USER CODE END UsageFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_UsageFault_IRQn 0 */
    /* USER CODE END W1_UsageFault_IRQn 0 */
  }
}

/**
  * @brief This function handles System service call via SWI instruction.
  */
void SVC_Handler(void)
{
  /* USER CODE BEGIN SVCall_IRQn 0 */

  /* USER CODE END SVCall_IRQn 0 */
  /* USER CODE BEGIN SVCall_IRQn 1 */

  /* USER CODE END SVCall_IRQn 1 */
}

/**
  * @brief This function handles Debug monitor.
  */
void DebugMon_Handler(void)
{
  /* USER CODE BEGIN DebugMonitor_IRQn 0 */

  /* USER CODE END DebugMonitor_IRQn 0 */
  /* USER CODE BEGIN DebugMonitor_IRQn 1 */

  /* USER CODE END DebugMonitor_IRQn 1 */
}

/**
  * @brief This function handles Pendable request for system service.
  */
void PendSV_Handler(void)
{
  /* USER CODE BEGIN PendSV_IRQn 0 */

  /* USER CODE END PendSV_IRQn 0 */
  /* USER CODE BEGIN PendSV_IRQn 1 */

  /* USER CODE END PendSV_IRQn 1 */
}

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

/******************************************************************************/
/* STM32F4xx Peripheral Interrupt Handlers                                    */
/* Add here the Interrupt Handlers for the used peripherals.                  */
/* For the available peripheral interrupt handler names,                      */
/* please refer to the startup file (startup_stm32f4xx.s).                    */
/******************************************************************************/

/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  //HAL_UART_IRQHandler(&huart1);
  UART1_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

/**
  * @brief This function handles DMA2 stream2 global interrupt.
  */
void DMA2_Stream2_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream2_IRQn 0 */

  /* USER CODE END DMA2_Stream2_IRQn 0 */
  DMA2_UART_IRQHandler(&hdma_usart1_rx);
  /* USER CODE BEGIN DMA2_Stream2_IRQn 1 */

  /* USER CODE END DMA2_Stream2_IRQn 1 */
}

/* USER CODE BEGIN 1 */
typedef struct
{
  __IO uint32_t ISR;   /*!< DMA interrupt status register */
  __IO uint32_t Reserved0;
  __IO uint32_t IFCR;  /*!< DMA interrupt flag clear register */
} DMA_Base_Registers;

uint8_t uart_dma_rx_buff[20];
void UART1_IRQHandler(UART_HandleTypeDef *huart)
{
    /*
    
    */
  uint32_t isrflags   = READ_REG(huart->Instance->SR);
  uint32_t cr1its     = READ_REG(huart->Instance->CR1);
  uint32_t cr3its     = READ_REG(huart->Instance->CR3);
  uint32_t errorflags = 0x00U;
  uint32_t dmarequest = 0x00U;

  /*clear the error and idle interrupt status by read sr and dr
    idle interrupt will not triggered until recv next good byte.
  */
  READ_REG(huart1.Instance->DR);
  
  errorflags = (isrflags & (uint32_t)(USART_SR_IDLE|USART_SR_PE | USART_SR_NE|USART_SR_FE | USART_SR_ORE));
  if (errorflags)
  {
    /*in the error status or idle, disable dma, after the dma really stopped,
    it will trigger the dma compelete intrrupt(need to check how many bytes
    transfered in the dma isr), */
     __HAL_DMA_DISABLE(&hdma_usart1_rx);
  }
}
extern ringbuff rb_uart1;

void DMA2_UART_IRQHandler(DMA_HandleTypeDef *hdma)
{
    uint32_t tmpisr;
    __IO uint32_t count = 0U;
    uint32_t timeout = SystemCoreClock / 9600U;
    
    /* calculate DMA base and stream number */
    DMA_Base_Registers *regs = (DMA_Base_Registers *)hdma->StreamBaseAddress;
    
    tmpisr = regs->ISR;
    if (hdma != &hdma_usart1_rx)
    {
        regs->IFCR = 0x3FU << hdma->StreamIndex;
        return;
    }
    if((tmpisr & (DMA_FLAG_TCIF0_4 << hdma->StreamIndex)) != RESET)
    {
        CLEAR_BIT(huart1.Instance->CR3, USART_CR3_DMAR);
        __HAL_DMA_DISABLE(&hdma_usart1_rx);
        regs->IFCR = 0x3FU << hdma->StreamIndex;

        uint32_t len = sizeof(uart_dma_rx_buff) - hdma_usart1_rx.Instance->NDTR;
        ringbuff_put(&rb_uart1, uart_dma_rx_buff, len);

        hdma_usart1_rx.Instance->NDTR = sizeof(uart_dma_rx_buff);
        __HAL_DMA_ENABLE(&hdma_usart1_rx);
        SET_BIT(huart1.Instance->CR3, USART_CR3_DMAR);
    }
    else
    {
        regs->IFCR = 0x3FU << hdma->StreamIndex;
    }
}



/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

 

ringbuff.h

#ifndef RINGBUFF_H
#define RINGBUFF_H
#include "stdio.h"
typedef struct _ringbuff
{
    uint32_t rd;
    uint32_t wr;
    uint32_t size;
    char *buf;
    
} ringbuff;

void ringbuff_init(ringbuff *rb, char*buf, uint32_t size);
int ringbuff_put(ringbuff *rb, char*buf, uint32_t size);
int ringbuff_get(ringbuff *rb, char*buf, uint32_t size);

#endif

 

ringbuff.c

#include "ringbuff.h"
void ringbuff_init(ringbuff* rb, char*buf, uint32_t size)
{
    rb->rd = 0;
    rb->wr = 0;
    rb->buf = buf;
    rb->size = size;
}

int ringbuff_put(ringbuff* rb, char*buf, uint32_t size)
{

    int i = 0;
    if (size == 0)
        return 0;
    while((rb->wr + 1) % rb->size != rb->rd)
    {
        rb->buf[rb->wr] = buf[i];
        rb->wr = (rb->wr + 1) % rb->size;
        i++;
        if (i == size)
            break;
    }

    return i;
}

int ringbuff_get(ringbuff* rb, char*buf, uint32_t size)
{

    int i = 0;
    if (size == 0)
        return 0;
    while(rb->rd != rb->wr)
    {
        buf[i] = rb->buf[rb->rd];
        rb->rd = (rb->rd + 1) % rb->size;
        i++;
        if (i == size)
            break;
    }

    return i;
}

 

注意:上面每次uart idle中断时, disable dma时,会触发dma中断。 这样,当接收的字符每个间距时间很长的时候,就会先运行idle isr,再运行dma isr.

            所以, 可以直接在idle isr里完成dma已经部分接收完成的字符的搬迁。 然后清掉dma外设的中断请求和cpu里 已经pending的状态。

void UART1_IRQHandler(UART_HandleTypeDef *huart)
{
    /*
    
    */
  uart_int_times++;
  uint32_t isrflags   = READ_REG(huart->Instance->SR);
  READ_REG(huart1.Instance->DR);
  uint32_t cr1its     = READ_REG(huart->Instance->CR1);
  uint32_t cr3its     = READ_REG(huart->Instance->CR3);
  uint32_t errorflags = 0x00U;
  uint32_t dmarequest = 0x00U;

  /*clear the error and idle interrupt status by read sr and dr
    idle interrupt will not triggered until recv next good byte.
  */
  READ_REG(huart1.Instance->DR);
  if (isrflags & (USART_SR_PE | USART_SR_NE|USART_SR_FE | USART_SR_ORE))
      error_times++;
  errorflags = (isrflags & (uint32_t)(USART_SR_IDLE|USART_SR_PE | USART_SR_NE|USART_SR_FE | USART_SR_ORE));
  if (errorflags)
  {

  READ_REG(huart->Instance->SR);
  READ_REG(huart1.Instance->DR);
    /*in the error status or idle, disable dma, after the dma really stopped,
    it will trigger the dma compelete intrrupt(need to check how many bytes
    transfered in the dma isr), */
    // __HAL_DMA_DISABLE(&hdma_usart1_rx);
#if 1
  CLEAR_BIT(huart1.Instance->CR3, USART_CR3_DMAR);
  __HAL_DMA_DISABLE(&hdma_usart1_rx);
  
  DMA_Base_Registers *regs = (DMA_Base_Registers *)hdma_usart1_rx.StreamBaseAddress;
  
  
  uint32_t len = sizeof(uart_dma_rx_buff) - hdma_usart1_rx.Instance->NDTR;
  ringbuff_put(&rb_uart1, uart_dma_rx_buff, len);
  
  n_recv_test += len;
  
  hdma_usart1_rx.Instance->NDTR = sizeof(uart_dma_rx_buff);
  regs->IFCR = 0x3FU << hdma_usart1_rx.StreamIndex;
  __HAL_DMA_ENABLE(&hdma_usart1_rx);
  NVIC_ClearPendingIRQ(DMA2_Stream2_IRQn);
  SET_BIT(huart1.Instance->CR3, USART_CR3_DMAR);
#endif
  }
}

 

posted @ 2021-02-27 14:21  mbdkunkka  阅读(724)  评论(0编辑  收藏  举报