HAL库USART中断接收的相关问题

news/2025/2/13 15:05:50/

文章目录

  • 一、使用中断的步骤
  • 二、相关函数分析
    • 1、HAL_UART_IRQHandler
    • 2、UART_Receive_IT
    • 3、HAL_UART_Receive_IT
    • 4、UART_Start_Receive_IT
    • 5、总结
  • 三、HAL库使用心得

一、使用中断的步骤

1、配置GPIO
2、配置USART1
3、设置UART1中断优先级(不开启手动中断,中断由HAL库给好的函数开启)
4、设置USART1中断服务函数(调用HAL给好的中断服务函数)
5、设置回调函数(HAL定义了一个弱函数,需要用户自己重定义)
6、开启USART1接收中断

二、相关函数分析

1、HAL_UART_IRQHandler

在中断服务函数USART1_IRQHandler中调用,进行USART中断的处理。

void USART1_IRQHandler(void)
{HAL_UART_IRQHandler(&huart1);
}

查看函数源码

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{uint32_t isrflags   = READ_REG(huart->Instance->SR); //#define READ_REG(REG)         ((REG))uint32_t cr1its     = READ_REG(huart->Instance->CR1);uint32_t cr3its     = READ_REG(huart->Instance->CR3);uint32_t errorflags = 0x00U;uint32_t dmarequest = 0x00U;/* If no error occurs */errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));//USART_SR_PE 32位,PE位为1,其余为0,是一个掩码  #define USART_SR_PE   USART_SR_PE_Msk  //  isrflags 时SR的值,与这几位的掩码与,若这些位无置位则结果位0.表示错误位都没有置为,无错误发生if (errorflags == RESET)// == 0 无错误{/* UART in mode Receiver -------------------------------------------------*/if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET)){//接收数据寄存器非空 且 接收中断使能UART_Receive_IT(huart);//中断接收函数return;}}
//错误发生时/* If some errors occur */if ((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET)|| ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET))){/* UART parity error interrupt occurred ----------------------------------*/if (((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET)){huart->ErrorCode |= HAL_UART_ERROR_PE;}/* UART noise error interrupt occurred -----------------------------------*/if (((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET)){huart->ErrorCode |= HAL_UART_ERROR_NE;}/* UART frame error interrupt occurred -----------------------------------*/if (((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET)){huart->ErrorCode |= HAL_UART_ERROR_FE;}/* UART Over-Run interrupt occurred --------------------------------------*/if (((isrflags & USART_SR_ORE) != RESET) && (((cr1its & USART_CR1_RXNEIE) != RESET)|| ((cr3its & USART_CR3_EIE) != RESET))){huart->ErrorCode |= HAL_UART_ERROR_ORE;}/* Call UART Error Call back function if need be --------------------------*/if (huart->ErrorCode != HAL_UART_ERROR_NONE){/* UART in mode Receiver -----------------------------------------------*/if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET)){UART_Receive_IT(huart);}/* If Overrun error occurs, or if any error occurs in DMA mode reception,consider error as blocking */dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);if (((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest){/* Blocking error : transfer is abortedSet the UART state ready to be able to start again the process,Disable Rx Interrupts, and disable Rx DMA request, if ongoing */UART_EndRxTransfer(huart);/* Disable the UART DMA Rx request if enabled */if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)){ATOMIC_CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);/* Abort the UART DMA Rx channel */if (huart->hdmarx != NULL){/* Set the UART DMA Abort callback :will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;if (HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK){/* Call Directly XferAbortCallback function in case of error */huart->hdmarx->XferAbortCallback(huart->hdmarx);}}else{/* Call user error callback */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)/*Call registered error callback*/huart->ErrorCallback(huart);
#else/*Call legacy weak error callback*/HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */}}else{/* Call user error callback */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)/*Call registered error callback*/huart->ErrorCallback(huart);
#else/*Call legacy weak error callback*/HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */}}else{/* Non Blocking error : transfer could go on.Error is notified to user through user error callback */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)/*Call registered error callback*/huart->ErrorCallback(huart);
#else/*Call legacy weak error callback*/HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */huart->ErrorCode = HAL_UART_ERROR_NONE;}}return;} /* End if some error occurs *//* Check current reception Mode :If Reception till IDLE event has been selected : */if ((huart->ReceptionType == HAL_UART_RECEPTION_TOIDLE)&& ((isrflags & USART_SR_IDLE) != 0U)&& ((cr1its & USART_SR_IDLE) != 0U)){__HAL_UART_CLEAR_IDLEFLAG(huart);/* Check if DMA mode is enabled in UART */if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)){/* DMA mode enabled *//* Check received length : If all expected data are received, do nothing,(DMA cplt callback will be called).Otherwise, if at least one data has already been received, IDLE event is to be notified to user */uint16_t nb_remaining_rx_data = (uint16_t) __HAL_DMA_GET_COUNTER(huart->hdmarx);if ((nb_remaining_rx_data > 0U)&& (nb_remaining_rx_data < huart->RxXferSize)){/* Reception is not complete */huart->RxXferCount = nb_remaining_rx_data;/* In Normal mode, end DMA xfer and HAL UART Rx process*/if (huart->hdmarx->Init.Mode != DMA_CIRCULAR){/* Disable PE and ERR (Frame error, noise error, overrun error) interrupts */ATOMIC_CLEAR_BIT(huart->Instance->CR1, USART_CR1_PEIE);ATOMIC_CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);/* Disable the DMA transfer for the receiver request by resetting the DMAR bitin the UART CR3 register */ATOMIC_CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);/* At end of Rx process, restore huart->RxState to Ready */huart->RxState = HAL_UART_STATE_READY;huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;ATOMIC_CLEAR_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);/* Last bytes received, so no need as the abort is immediate */(void)HAL_DMA_Abort(huart->hdmarx);}/* Initialize type of RxEvent that correspond to RxEvent callback execution;In this case, Rx Event type is Idle Event */huart->RxEventType = HAL_UART_RXEVENT_IDLE;#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)/*Call registered Rx Event callback*/huart->RxEventCallback(huart, (huart->RxXferSize - huart->RxXferCount));
#else/*Call legacy weak Rx Event callback*/HAL_UARTEx_RxEventCallback(huart, (huart->RxXferSize - huart->RxXferCount));
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */}return;}else{/* DMA mode not enabled *//* Check received length : If all expected data are received, do nothing.Otherwise, if at least one data has already been received, IDLE event is to be notified to user */uint16_t nb_rx_data = huart->RxXferSize - huart->RxXferCount;if ((huart->RxXferCount > 0U)&& (nb_rx_data > 0U)){/* Disable the UART Parity Error Interrupt and RXNE interrupts */ATOMIC_CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));/* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */ATOMIC_CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);/* Rx process is completed, restore huart->RxState to Ready */huart->RxState = HAL_UART_STATE_READY;huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;ATOMIC_CLEAR_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);/* Initialize type of RxEvent that correspond to RxEvent callback execution;In this case, Rx Event type is Idle Event */huart->RxEventType = HAL_UART_RXEVENT_IDLE;#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)/*Call registered Rx complete callback*/huart->RxEventCallback(huart, nb_rx_data);
#else/*Call legacy weak Rx Event callback*/HAL_UARTEx_RxEventCallback(huart, nb_rx_data);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */}return;}}/* UART in mode Transmitter ------------------------------------------------*/if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET)){UART_Transmit_IT(huart);return;}/* UART in mode Transmitter end --------------------------------------------*/if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET)){UART_EndTransmit_IT(huart);return;}
}

2、UART_Receive_IT

中断接收函数,被HAL_UART_IRQHandler 调用

static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{uint8_t  *pdata8bits;uint16_t *pdata16bits;/* Check that a Rx process is ongoing */if (huart->RxState == HAL_UART_STATE_BUSY_RX)//判断接收过程是否任然在进行{if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE)){//设置为9位数据而且无校验时的处理pdata8bits  = NULL;pdata16bits = (uint16_t *) huart->pRxBuffPtr;*pdata16bits = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);huart->pRxBuffPtr += 2U;}else{pdata8bits = (uint8_t *) huart->pRxBuffPtr;//pdata16bits  = NULL;if ((huart->Init.WordLength == UART_WORDLENGTH_9B) || ((huart->Init.WordLength == UART_WORDLENGTH_8B) && (huart->Init.Parity == UART_PARITY_NONE))){//设置为8位数据时无校验或9位数据位时的处理*pdata8bits = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);//从数据寄存器接收8位数据}else{*pdata8bits = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);//从数据寄存器接收7位数据}huart->pRxBuffPtr += 1U;//buff指针加一}if (--huart->RxXferCount == 0U)//此时已经接收了最后一位数据 huart->RxXferCount = 1;{//关闭所有中断,设置usart1 的句柄的各个标志位/* Disable the UART Data Register not empty Interrupt */__HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);/* Disable the UART Parity Error Interrupt */__HAL_UART_DISABLE_IT(huart, UART_IT_PE);/* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */__HAL_UART_DISABLE_IT(huart, UART_IT_ERR);/* Rx process is completed, restore huart->RxState to Ready */huart->RxState = HAL_UART_STATE_READY;/* Initialize type of RxEvent to Transfer Complete */huart->RxEventType = HAL_UART_RXEVENT_TC;/* Check current reception Mode :If Reception till IDLE event has been selected : */if (huart->ReceptionType == HAL_UART_RECEPTION_TOIDLE){//开启了空闲中断,关闭空闲中断/* Set reception type to Standard */huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;/* Disable IDLE interrupt */ATOMIC_CLEAR_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);/* Check if IDLE flag is set */if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE)){/* Clear IDLE flag in ISR */__HAL_UART_CLEAR_IDLEFLAG(huart);}
//接收完成,且开启了空闲中断时调用此函数
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)/*Call registered Rx Event callback*/huart->RxEventCallback(huart, huart->RxXferSize);
#else/*Call legacy weak Rx Event callback*/HAL_UARTEx_RxEventCallback(huart, huart->RxXferSize);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */}else{/* Standard reception API called *///接收完成,没有开启空闲中断时选择处理的回调函数
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)/*Call registered Rx complete callback*/huart->RxCpltCallback(huart);
#else/*Call legacy weak Rx complete callback*/HAL_UART_RxCpltCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */}return HAL_OK;}return HAL_OK;}else{return HAL_BUSY;}
}

3、HAL_UART_Receive_IT

判断USART句柄的接收状态,调用UART_Start_Receive_IT 开启中断。

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{/* Check that a Rx process is not already ongoing */if (huart->RxState == HAL_UART_STATE_READY)//判断usart状态{if ((pData == NULL) || (Size == 0U))//判断参数是否有效{return HAL_ERROR;}/* Set Reception type to Standard reception */huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;//设置接收状态return (UART_Start_Receive_IT(huart, pData, Size));//调用接收中断开启函数}else{return HAL_BUSY;}
}

4、UART_Start_Receive_IT

HAL_StatusTypeDef UART_Start_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{huart->pRxBuffPtr = pData;//缓冲区指针初始化huart->RxXferSize = Size;//缓冲区大小初始化huart->RxXferCount = Size;//接收数据总大小初始化  会在接收时减小huart->ErrorCode = HAL_UART_ERROR_NONE;//错误码设置huart->RxState = HAL_UART_STATE_BUSY_RX;//接收状态设置if (huart->Init.Parity != UART_PARITY_NONE){/* Enable the UART Parity Error Interrupt */__HAL_UART_ENABLE_IT(huart, UART_IT_PE);}//开启错误中断和接收中断/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */__HAL_UART_ENABLE_IT(huart, UART_IT_ERR);/* Enable the UART Data Register not empty Interrupt */__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);return HAL_OK;
}

5、总结

在使用标准库时需要我们先初始化GPIO再初始化USART,然后设置中断优先级并开启中断。在HAL库中,只需要我们初始化GPIO和USART并设置中断优先级,中断的开启和数据的接收都在HAL中封装好了函数,并使用回调函数的方式在接收完成后对数据进行处理。需要注意的是,USART的中断在HAL_UART_Receive_IT中调用UART_Start_Receive_IT开启相关中断,在ISR函数中只需调用HAL_UART_IRQHander函数。每次接收完成后相关的中断都会关闭,需要再次调用HAL_UART_Receive_IT函数开启中断,这也是刚开始用HAL库时常常疑惑的地方,为什么要在初始化时调用一次HAL_UART_Receive_IT函数,在接收完成后的处理函数中要调用HAL_UART_Receive_IT?
当然我们也可以自己调用相关的宏开启中断在ISR中写自己的处理函数,但是这样就不能够调用HAL库提供的函数接收数据了,因为在HAL库中,将外设抽象为句柄,在其中有许多的标志位,我们一旦自己自己调用中断操作后面又使用HAL库的API就会因为外设句柄中的标志位与实际不符而返回错误状态,导致程序卡死。

三、HAL库使用心得


http://www.ppmy.cn/news/1571734.html

相关文章

51单片机07 串口通信

串口是一种应用十分广泛的通讯接口&#xff0c;串口成本低、容易使用、通信线路简单&#xff0c;可实现两个设备的互相通信。单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信。51单片机内部自带UART&#xff08;Universal Asynchronous Recei…

从算法到落地:DeepSeek如何突破AI工具的同质化竞争困局

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;Linux网络编程 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 ​ Linux网络编程笔记&#xff1a; https://blog.cs…

LM Studio无设置代理,更改镜像源方法(MAC)

在macbook上使用LM Studio时发现总是加载失败&#xff0c;App也没有设置代理的地方&#xff0c;搜索了挺多解决方案&#xff0c;貌似官网再可以封补很多解决方案已经过时&#xff0c;最终找到一种替换镜像源的方法共享出来。 方便大家都能使用&#xff0c;不介绍命令行修改方式…

42.水果销售系统(springbootvue的Java项目[含微信小程序])

目录 1.系统的受众说明 2.开发环境与技术 2.1 MYSQL数据库 2.2 Java语言 2.3 微信小程序技术 2.4 SpringBoot框架 2.5 B/S架构 2.6 Tomcat 介绍 2.7 HTML简介 2.8 MyEclipse开发工具 3.系统分析 3.1 可行性分析 3.1.1 技术可行性 3.1.2 经济可行性 3.1.3 操作…

【鸿蒙开发】第二十九章 Stage模型-应用上下文Context、进程、线程

目录 1 Stage模型基本概念 1.1 开发流程 3 应用上下文Context的典型使用场景 3.1 获取应用文件路径 3.2 获取和修改加密分区 3.3 获取本应用中其他Module的Context 3.4 订阅进程内UIAbility生命周期变化 4 进程 4.1 概述 5 线程 5.1 线程类型 5.2 使用EventHub进行线…

【Linux】深入理解linux权限

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;Linux 目录 前言 一、权限是什么 二、用户和身份角色 三、文件属性 1. 文件属性表示 2. 文件类型 3. 文件的权限属性 四、修改文件的权限属性和角色 1. …

electron-vite 构建后路由失效问题

这个问题大概是这样的&#xff1a;使用 electron-vite 构建的应用&#xff0c;开发时候&#xff0c;用路由的窗口能正常显示&#xff0c;而打包后无法访问。 其实官网给出了说明&#xff1a;说明 也可以通过下面这段代码看出一些东西&#xff1a; 很明显他判断了环境&#xf…

ES 索引结构

ES 既不像 MySQL 这样有严格的 Schema&#xff0c;也不像 MongoDB 那样完全无 Schema&#xff0c;而是介于两者之间。 1️⃣ ES 的 Schema 模式 ES 默认是 Schema-less&#xff08;无模式&#xff09; 的&#xff0c;允许动态添加字段。 但 ES 也支持 Schema&#xff08;映射 …