STM32、STM32L4xx,HAL库使用IDLE空闲中断+DMA接收不定长数据帧

1.定义初始化句柄

UART_HandleTypeDef USART1_InitStruct;  //USART1初始化句柄
DMA_HandleTypeDef hdma_usart1_rx;      //USART1_Rx DMA初始化句柄

2.定义串口1相关变量

uint8_t USART1_Rxbuffer[152] = {0};    //接收数据缓存区
uint8_t USART1_Rxflag = 0;             //接收数据标志位
uint16_t USART1_RxLength = 0;          //接收到数据的长度

3.串口1初始化

void USART1_Init(uint32_t Baud)   
{
  /* USART Ports Clock Enable */
    __HAL_RCC_USART1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

  GPIO_InitTypeDef GPIO_InitStruct;

  GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10;                     //TX AND RX
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;                             //复用输出
  GPIO_InitStruct.Pull = GPIO_NOPULL;                                        //不设置上下拉
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;                          //设置速度
  GPIO_InitStruct.Alternate = GPIO_AF7_USART1;                                            //GPIO复用到USART1
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);  
    
    /* USART1 Init */

    USART1_InitStruct.Instance =USART1;                                                           //配置串口1
    USART1_InitStruct.Init.BaudRate=Baud;                                                            //波特率
    USART1_InitStruct.Init.Mode = USART_MODE_TX_RX;                                       //收发
    USART1_InitStruct.Init.WordLength = USART_WORDLENGTH_8B;            //数据长度
    USART1_InitStruct.Init.Parity=USART_PARITY_NONE;                    //NO校验位
    USART1_InitStruct.Init.StopBits= USART_STOPBITS_1;                  //1停止位
    USART1_InitStruct.Init.HwFlowCtl= UART_HWCONTROL_NONE;              //不使用硬件流控
    USART1_InitStruct.Init.OverSampling = UART_OVERSAMPLING_16;         //设置过采样率
    USART1_InitStruct.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;    
    HAL_UART_Init(&USART1_InitStruct);
    
    HAL_NVIC_SetPriority(USART1_IRQn,1, 1);                             //初始化中断[抢占][响应]
    HAL_NVIC_EnableIRQ(USART1_IRQn);                                    //启用串口中断
    
    
    __HAL_UART_ENABLE_IT(&USART1_InitStruct, UART_IT_IDLE);             // 使能 IDLE中断,用于接收不定长数据
    __HAL_UART_CLEAR_IDLEFLAG(&USART1_InitStruct);                      //清除IDLE标志位用于下一次接收
    
    /*DMA初始化*/
    __HAL_RCC_DMA1_CLK_ENABLE();
    hdma_usart1_rx.Instance = DMA1_Channel5;      //对应DMA通道5
    hdma_usart1_rx.Init.Request = DMA_REQUEST_2;                    //串口1接收信号为2
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;    //外设to内存
    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_NORMAL;
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_HIGH;
    HAL_DMA_Init(&hdma_usart1_rx);
    
    __HAL_LINKDMA(&USART1_InitStruct, hdmarx, hdma_usart1_rx);
    
    HAL_UART_Receive_DMA(&USART1_InitStruct, USART1_Rxbuffer, sizeof(USART1_Rxbuffer));  //使能DMA接收,数据存放在USART1_Rxbuffer
}

4.IDLE空闲中断服务函数

/* 串口空闲回调函数 [接收完一帧数据]*/
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART1)   //串口1接收完一帧数据
    {
        USART1_RxLength = sizeof(USART1_Rxbuffer) - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);    //计算数据长度
        if(USART1_RxLength > 1)
            {
                USART1_Rxflag = 1;         //置标志位
                __NOP()//自己编写
                __HAL_UART_CLEAR_IDLEFLAG(&USART1_InitStruct);    //清除Usart1IDLE标志位								
	            __HAL_UART_ENABLE_IT(&USART1_InitStruct, UART_IT_IDLE);   //使能 IDLE中断
	            USART1_RxLength = 0;       //数据长度清0		
				HAL_DMA_Abort(&hdma_usart1_rx);                               //停止DMA
	            __HAL_DMA_DISABLE(&hdma_usart1_rx); 	//先禁用DMA传输												
	            hdma_usart1_rx.Instance->CNDTR = sizeof(USART1_Rxbuffer); 		//重新设置传输数据的数量
	            __HAL_DMA_ENABLE(&hdma_usart1_rx); //使能DMA													
	            HAL_UART_Receive_DMA(&USART1_InitStruct, USART1_Rxbuffer, sizeof(USART1_Rxbuffer));  //重新启动DMA数据接收	
                USART1_Rxflag = 0;         //数据处理完清标志位												           
            }
    }

5./*串口错误回调函数*/

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
        if(HAL_UART_GetError(huart) & HAL_UART_ERROR_PE){        /*!< Parity error            */
        //奇偶校验错误
            __HAL_UART_CLEAR_PEFLAG(huart);
        }else if(HAL_UART_GetError(huart) & HAL_UART_ERROR_NE){ /*!< Noise error             */
        //噪声错误
            __HAL_UART_CLEAR_NEFLAG(huart);
        }else if(HAL_UART_GetError(huart) & HAL_UART_ERROR_FE){ /*!< Frame error             */
        //帧格式错误
        __HAL_UART_CLEAR_FEFLAG(huart);
        }else if(HAL_UART_GetError(huart) & HAL_UART_ERROR_ORE){ /*!< Overrun error           */
        //数据太多串口来不及接收错误
        __HAL_UART_CLEAR_OREFLAG(huart);
        }
    //当这个串口发生了错误,一定要在重新使能接收中断
    if(huart ->Instance == USART1) {
        HAL_UART_Receive_IT(&USART1_InitStruct, USART1_Txbuffer, 1);
        }
    //其他串口......
}

6.完整源码usart.c

#include "stm32l4xx_hal.h"


UART_HandleTypeDef USART1_InitStruct;  //USART1初始化句柄
DMA_HandleTypeDef hdma_usart1_rx;      //USART1_Rx DMA初始化句柄

/****************** 串口1变量定义 *********************/
uint8_t USART1_Txbuffer[1];            //发送数据缓存区
uint8_t USART1_Rxbuffer[128] = {0};    //接收数据缓存区
uint8_t USART1_Rxflag = 0;             //接收数据标志位
uint16_t USART1_RxLength = 0;          //接收到数据的长度

/**
  * 函    数:串口1初始化函数
  * 参    数:无
  * 参    数:无
  * 返 回 值:无
	* 注    意:无
  */
void USART1_Init(uint32_t Baud)   
{
  /* USART Ports Clock Enable */
    __HAL_RCC_USART1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
 
		GPIO_InitTypeDef GPIO_InitStruct;
	 
		GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10;                     //TX AND RX
		GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;                             //复用输出
		GPIO_InitStruct.Pull = GPIO_NOPULL;                                        //不设置上下拉
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;                          //设置速度
		GPIO_InitStruct.Alternate = GPIO_AF7_USART1;                                            //GPIO复用到USART1
		HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);  
    
    /* USART1 Init */
 
    USART1_InitStruct.Instance =USART1;                                                           //配置串口1
    USART1_InitStruct.Init.BaudRate=Baud;                                                            //波特率
    USART1_InitStruct.Init.Mode = USART_MODE_TX_RX;                                       //收发
    USART1_InitStruct.Init.WordLength = USART_WORDLENGTH_8B;            //数据长度
    USART1_InitStruct.Init.Parity=USART_PARITY_NONE;                    //NO校验位
    USART1_InitStruct.Init.StopBits= USART_STOPBITS_1;                  //1停止位
    USART1_InitStruct.Init.HwFlowCtl= UART_HWCONTROL_NONE;              //不使用硬件流控
    USART1_InitStruct.Init.OverSampling = UART_OVERSAMPLING_16;         //设置过采样率
    USART1_InitStruct.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;    
    HAL_UART_Init(&USART1_InitStruct);
    
    HAL_NVIC_SetPriority(USART1_IRQn,1, 1);                             //初始化中断[抢占][响应]
    HAL_NVIC_EnableIRQ(USART1_IRQn);                                    //启用串口中断
    
    
    __HAL_UART_ENABLE_IT(&USART1_InitStruct, UART_IT_IDLE);             // 使能 IDLE中断,用于接收不定长数据
    __HAL_UART_CLEAR_IDLEFLAG(&USART1_InitStruct);                      //清除IDLE标志位用于下一次接收
    
    /*DMA初始化*/
    __HAL_RCC_DMA1_CLK_ENABLE();
    hdma_usart1_rx.Instance = DMA1_Channel5;      //对应DMA通道5
    hdma_usart1_rx.Init.Request = DMA_REQUEST_2;                    //串口1接收信号为2
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;    //外设to内存
    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_NORMAL;
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_HIGH;
    HAL_DMA_Init(&hdma_usart1_rx);
    
    __HAL_LINKDMA(&USART1_InitStruct, hdmarx, hdma_usart1_rx);
    
    HAL_UART_Receive_DMA(&USART1_InitStruct, USART1_Rxbuffer, sizeof(USART1_Rxbuffer));  //使能DMA接收,数据存放在USART1_Rxbuffer
}

/**
  * 函    数:串口1发送数据
  * 参    数:
						@Data:要发送的数据
  * 返 回 值:无
	* 注    意:无
  */
void USART1_SendData(uint8_t *Data,uint8_t length)
{
	HAL_UART_Transmit(&USART1_InitStruct,Data,length,1000);
}

/**
  * 函    数:串口1中断服务函数
  * 参    数:
  * 返 回 值:无
	* 注    意:无
  */
void USART1_IRQHandler(void)
{
   HAL_UART_IRQHandler(&USART1_InitStruct);   //串口中断公共接口函数
}

/* 串口数据接收完成回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
   if(huart->Instance == USART1&& USART1_Rxflag==0)
   {
		 USART1_Rxbuffer[USART1_RxLength] = USART1->RDR;  //将数据存到串口1接收缓存区
		 HAL_UART_Receive_IT(&USART1_InitStruct,USART1_Txbuffer,1);
		 USART1_RxLength++;
   }
}

/*串口1空闲中断标清除*/
void Usart1_Clear_IdleFlag(void)
{
	__HAL_UART_CLEAR_IDLEFLAG(&USART1_InitStruct);    //清除Usart1IDLE标志位								
	__HAL_UART_ENABLE_IT(&USART1_InitStruct, UART_IT_IDLE);   //使能 IDLE中断	
	HAL_DMA_Abort(&hdma_usart1_rx);                               //停止DMA
	__HAL_DMA_DISABLE(&hdma_usart1_rx); 	//先禁用DMA传输												
	hdma_usart1_rx.Instance->CNDTR = sizeof(USART1_Rxbuffer); 		//重新设置传输数据的数量
	__HAL_DMA_ENABLE(&hdma_usart1_rx); //使能DMA													
	HAL_UART_Receive_DMA(&USART1_InitStruct, USART1_Rxbuffer, sizeof(USART1_Rxbuffer));  //重新启动DMA数据接收
}

/* 串口空闲回调函数 [接收完一帧数据]*/
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
{
	USART1_RxLength = sizeof(USART1_Rxbuffer) - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);    //计算数据长度
	if(huart->Instance == USART1)   //串口1接收完一帧数据
	{
		if(USART1_RxLength > 1)
			{
				USART1_Rxflag = 1;          //置串口1接收数据包标志位
			}
	}
}
/*串口错误回调函数*/
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
		if(HAL_UART_GetError(huart) & HAL_UART_ERROR_PE){		/*!< Parity error            */
		//奇偶校验错误
			__HAL_UART_CLEAR_PEFLAG(huart);
		}else if(HAL_UART_GetError(huart) & HAL_UART_ERROR_NE){ /*!< Noise error             */
		//噪声错误
			__HAL_UART_CLEAR_NEFLAG(huart);
		}else if(HAL_UART_GetError(huart) & HAL_UART_ERROR_FE){ /*!< Frame error             */
		//帧格式错误
		__HAL_UART_CLEAR_FEFLAG(huart);
		}else if(HAL_UART_GetError(huart) & HAL_UART_ERROR_ORE){ /*!< Overrun error           */
		//数据太多串口来不及接收错误
		__HAL_UART_CLEAR_OREFLAG(huart);
		}
    //当这个串口发生了错误,一定要在重新使能接收中断
    if(huart ->Instance == USART1 || huart ->Instance == USART2){
		HAL_UART_Receive_IT(&USART1_InitStruct, USART1_Txbuffer, 1);
		}
    //其他串口......
}

 usart.h

#ifndef  __USART_H
#define  __USART_H
#include "string.h"

extern uint8_t USART1_Rxbuffer[];    //数据缓存区
extern uint8_t USART1_Rxflag; 
extern uint16_t USART1_RxLength;     // 接收到数据的长度

void USART1_Init(uint32_t Baud) ;  
void USART1_SendData(uint8_t *Data,uint8_t length);
void Usart1_Clear_IdleFlag(void);
#endif

 main.c

#include "stm32l4xx_hal.h"
#include "SysClk.h"
#include "LED.h"
#include "USART.h"
int main(void)
{
  HAL_Init();
	My_SystemClock_Config();  //配置系统时钟80M
	My_GPIO_Init();               //LED的GPIO初始化 
	USART1_Init(115200);          //初始化串口1波特率为115200负责与上位机通信
  while (1)
  {
		if(USART1_Rxflag==1)       //串口1接收1到1帧数据
		{
				USART1_SendData(USART1_Rxbuffer,USART1_RxLength);//这里只做转发
				memset(USART1_Rxbuffer,0x00,128);   //处理完数据,清空接收缓存区,准备下一次接收
				USART1_RxLength = 0;         //长度清为0
				USART1_Rxflag = 0;           //标志位清0
				Usart1_Clear_IdleFlag();     //清除标志位
		}
		HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
  }
}

6.看看效果

由于HAL库没有标配空闲中断

配置IDLE空闲中断参考我另一篇博客STM32HAL库空闲中断配置

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值