HAL库的学习 —— 串口DMA配合空闲中断接收数据

一、简介

      串口采用DMA方式接收,MCU可以不用干预就实现外设到RAM传输,相较于中断方式大大提高MCU的运行效率,使得MCU可以腾出空来去处理其它重要事情。以下主要介绍HAL库关于串口DMA方式的配置以及代码实现,包括接收和发送。

二、CubeMx配置

首先将引脚配置为串口功能:

接着配置串口的参数:

点击ADD将UART5的接收设为DMA接收方式

开启串口和DMA中断:

 

三、代码实现

/*******数据结构定义*******/

typedef struct
{
  uint8_t flg;
  uint8_t buf[256];
  uint16_t len
} DMA_PRO;

#define DMA_REC_LEN  100

DMA_PRO dmaTx = {0};
DMA_PRO dmaRx = {0};

/*******DMA使能/空闲中断使能*******/

//在串口初始化后加入
HAL_UART_Receive_DMA(&huart5, dmaRx.buf, DMA_REC_LEN);  
__HAL_UART_ENABLE_IT(&huart5, UART_IT_IDLE);

/*******空闲中断处理*******/

void UsartReceive_IDLE(UART_HandleTypeDef *huart)  
{  
  uint32_t tmpFlg = 0;
  uint32_t temp;
  tmpFlg =__HAL_UART_GET_FLAG(&huart5,UART_FLAG_IDLE); //获取IDLE标志位

  if((tmpFlg != RESET))//idle标志被置位
  { 
    __HAL_UART_CLEAR_IDLEFLAG(&huart5);//清除标志位
    HAL_UART_DMAStop(&huart5); 
    temp  =  __HAL_DMA_GET_COUNTER(&hdma_huart5_rx);// 获取DMA中未传输的数据个数   
    dmaRx.len =  100 - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
    dmaRx.flg = 1;	// 接受完成标志位置1	
  }
} 

/*******主循环打印接收内容*******/

/***main函数while循环*******/

while (1)
{
   if (dmaRx.flg == 1)
   {
     dmaRx.flg = 0;

     //避免其它异常触发空闲中断,误以为接收完成。如首次上电没收到数据空闲标志会被置位
     if (dmaRx.len == 0)
     {
       if (HAL_UART_Receive_DMA(&huart5, dmaRx.buf, DMA_REC_LEN) != HAL_OK)
       {
         printf("DMA UART REC OPEN ERR\r\n");
       }
     }
     else
     {
       //注DMA发送函数调完后,并没有完成发送,发送完成会触发DMA中断,调用串口中断回调函数
       dmaTx.flg = 1;
       memcpy(dmaTx.buf, dmaRx.buf, dmaRx.len);
       dmaTx.len = dmaRx.len;

       if (HAL_UART_Transmit_DMA(&huart5, dmaTx.buf, dmaTx.len) != HAL_OK)
       {
         printf("DMA UART TRANSFER OPEN ERR\r\n");
       }

       while (dmaTx.flg);

       if (HAL_UART_Receive_DMA(&huart5, dmaRx.buf, DMA_REC_LEN) != HAL_OK)
       {
         printf("DMA OPEN ERR\r\n");
       }    
     }
   }
}

        

/*******发送回调函数复位发送标志*******/

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart == &huart5)
  {
    dmaTx.flg = 0;
  }
}

 

使用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传输完成和空闲中断的事件。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值