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;
  }
}

 

  • 3
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
串口空闲中断DMA中断接收是两种不同的接收方式。 串口空闲中断接收是指当串口接收到数据后,数据传输完成并且串口空闲时,触发中断来进行数据处理。这种方式比较简单,适用于数据量较小的情况。 DMA中断接收是指使用DMA控制器来进行数据传输,当DMA传输完成后触发中断进行数据处理。这种方式适用于数据量较大的情况,可以提高数据传输的效率。 下面是一个基于STM32的串口空闲中断+DMA中断接收的例子: ```c #include "stm32f4xx.h" #include "stm32f4xx_conf.h" #define USARTx USART2 #define DMAx DMA1 #define DMAx_Streamx DMA1_Stream5 #define DMAx_Streamx_IRQn DMA1_Stream5_IRQn #define DMAx_Streamx_IRQHandler DMA1_Stream5_IRQHandler #define BUFFER_SIZE 1024 uint8_t buffer[BUFFER_SIZE]; volatile uint16_t bufferIndex = 0; void initUSART() { USART_InitTypeDef USART_InitStruct; GPIO_InitTypeDef GPIO_InitStruct; NVIC_InitTypeDef NVIC_InitStruct; DMA_InitTypeDef DMA_InitStruct; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); USART_InitStruct.USART_BaudRate = 115200; USART_InitStruct.USART_WordLength = USART_WordLength_8b; USART_InitStruct.USART_StopBits = USART_StopBits_1; USART_InitStruct.USART_Parity = USART_Parity_No; USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USARTx, &USART_InitStruct); USART_ITConfig(USARTx, USART_IT_IDLE, ENABLE); DMA_InitStruct.DMA_Channel = DMA_Channel_4; DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)buffer; DMA_InitStruct.DMA_BufferSize = BUFFER_SIZE; DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USARTx->DR; DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMAx_Streamx, &DMA_InitStruct); NVIC_InitStruct.NVIC_IRQChannel = DMAx_Streamx_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); USART_Cmd(USARTx, ENABLE); DMA_Cmd(DMAx_Streamx, ENABLE); } void DMAx_Streamx_IRQHandler() { if (DMA_GetITStatus(DMAx_Streamx, DMA_IT_TCIF5) != RESET) { DMA_ClearITPendingBit(DMAx_Streamx, DMA_IT_TCIF5); DMA_Cmd(DMAx_Streamx, DISABLE); bufferIndex = BUFFER_SIZE - DMA_GetCurrDataCounter(DMAx_Streamx); } } void USART2_IRQHandler() { if (USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) { USART_ClearITPendingBit(USART2, USART_IT_IDLE); DMAx_Streamx->CR &= ~(1 << 0); // Disable DMA Stream DMA_ClearFlag(DMAx_Streamx, DMA_FLAG_TCIF5 | DMA_FLAG_HTIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_FEIF5); DMA_Cmd(DMAx_Streamx, ENABLE); } } int main() { initUSART(); while (1) { if (bufferIndex > 0) { // 处理接收到的数据 bufferIndex = 0; } } } ``` 该例子中使用DMA控制器进行数据传输,并通过串口空闲中断触发DMA传输完成中断。在DMA传输完成中断中,通过DMA_GetCurrDataCounter函数获取当前未传输的数据长度,即接收到的数据长度。在串口空闲中断中,通过禁用并重新启用DMA Stream来触发DMA传输完成中断
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值