- 概要
- RXNE中断响应过程方法(2)
(一)概要
中断响应过程需要实现的关键代码是由两部分组成:
使能中断标志位+完善中断服务入口函数,这就像一个固定的套餐组合,缺一不可;
在上一篇//www.greatytc.com/p/f2d4095c9def介绍RXNE的中断响应过程,是用HAL库函数
__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);使能中断,配合中断响应服务入口函数USER_UART_IRQHandler(&huart1);共同实现了接收到一个字符的中断响应过程;
(二)RXNE中断响应过程方法(2)
2.1 简介
第二种方法,是我们继续沿用HAL库自定义的回调函数来实现中断服务入口程序,依然是使能中断 标志位+补充中断响应服务函数,方法二提供的套餐组合为HAL_UART_Receive_IT() 和HAL_UART_RxCpltCallback()
使能RXNE中断标志位的函数声明是:
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
中断响应服务函数是HAL库定义的回调函数( Callback):
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
回调函数是一种弱函数,
weak 顾名思义是“弱”的意思,如果函数名称前面加上__weak 修饰符,我们一般称这个函数为“弱函数”。加上了__weak 修饰符的函数,用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没重新定义这个函数,那么编译器就会执行__weak 声明的函数,并且编译器不会报错。
具备weak属性的函数,就像是穿了一件隐身衣,当用户没有在文件中定义时,它对于编译器是不可见的隐身状态,只有当被用户重新定义之后,它作为中断响应的服务函数,会按照用户定义实现其功能;回调函数在HAL库中非常常见,它既可以被全局结构体变量句柄调用,也可以成为中断响应的服务函数;用户需要清晰理解不同外设的回调函数实现功能,才能方便高效的应用;
2.2 代码实现步骤
在USART1串口控制流水灯的实验中,//www.greatytc.com/p/48817b329231,串口助手发送“mode_1#”命令字后,STM32的USART1 pRxBuffPtr连续接收到9个字符之后,就会进入到中断服务回调函数; HAL_UART_RxCpltCallback(huart);我们在main.c中重新定义回调函数,即可实现同样的效果;接下来分析中断使能到中断响应的全过程;
中断使能:HAL_UART_Receive_IT
中断响应:SART1_IRQHandler()——HAL_UART_IRQHandler—— UART_Receive_IT——当RxXferCount计数器自减到0,调用回调函数HAL_UART_RxCpltCallback();
(1)使能中断标志位
1.1 调用函数HAL_UART_Receive_IT允许RXNE标志位产生中断,该函数
在mian.c主函数USART1初始化操作之后,调用此函数,目的是开启RXNE标志位,因为命令字“mode_1#\r\n”是9个字符,当DR数据寄存器接收到来自串口助手发送的9个字符之后,就会产生响应中断服务函数;
HAL_UART_Receive_IT(&huart1,uart1RxBuff,9);
从该函数定义可以得知:该函数功能是:
1.使能PE(奇偶校验错误)中断标志位,ERR(错误数据)中断标志位,RXNE(数据寄存器不为空)中断标志位;
2.给串口句柄huart1的三个全局变量*pRxBuffPtr,RxXferSize和RxXferCount赋初值;
pRxBuffPtr 指向用户自定义的接收数组的首地址;
RxXferSize; 接收数据的长度;
RxXferCount:接收数据的计数器;
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)
{
if ((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->pRxBuffPtr = pData;//指向用户自定义的接收数组
huart->RxXferSize = Size;//设定接收到的字符串个数
huart->RxXferCount = Size;//设定计数器的初值
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->RxState = HAL_UART_STATE_BUSY_RX;
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Parity Error Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_PE);//使能PE标志位
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
__HAL_UART_ENABLE_IT(huart, UART_IT_ERR);//使能ERR标志位
/* Enable the UART Data Register not empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);//使能RXNE标志位
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
(2)中断响应的执行过程
2.1 中断入口函数: 在文件stm32f1xx_it.c中,找到IRQHandler函数
void USART1_IRQHandler(void)//中断响应的入口
{
HAL_UART_IRQHandler(&huart1);
}
2.2 执行 HAL_UART_IRQHandler(&huart1);: 从函数定义可知,当使能RXNE标志位之后,条件语句成立,则调用UART_Receive_IT(huart);
void HAL_UART_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;
/* If no error occurs */
errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
if (errorflags == RESET)
{
/* UART in mode Receiver -------------------------------------------------*/
if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
{
UART_Receive_IT(huart);
return;
}
}

2.3 执行函数 UART_Receive_IT(huart);。该函数功能
1.判断一次接收的有效数据位是8个字节还是9个字节;是否必须包含奇偶校验位;
2.从DR数据寄存器中读取有效数据位存放到用户自定义的数组中;
3.移动指针pRxBuffPtr;
4判断uarRxXferCount计数器自减为0之后,就调用回调函数 HAL_UART_RxCpltCallback(huart);
5.条件成立,函数返回HAL_OK;
函数 UART_Receive_IT()的作用是把每次中断接收到的字符保存在串口句柄的缓存指针 pRxBuffPtr 中,同时每次接收一个字符,其计数器 RxXferCount 减 1,直到接收完成 RxXferSize 个字符之后 RxXferCount 设置为0,同时调用接收完成回调函数 HAL_UART_RxCpltCallback 进行处理;
2.4 用户在main.c中重写回调函数HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart),在回调函数中只需要判断是否USART1的产生的中断,用户自定义的全局变量 uart1RxState赋值1,则说明数据已经接收;此外,因为上一步在调用回调函数之前,已经关闭了中断__HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);所以需要重新开启中断,调用函数HAL_UART_Receive_IT(&huart1,uart1RxBuff,9);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//当RXcounter自减到0 ,才进入回调函数
{
if(huart->Instance==USART1)
{
uart1RxState =1;
}
HAL_UART_Receive_IT(&huart1,uart1RxBuff,9);//重新开启中断
}
总结:
串口中断是我们总结了第二种方法。与第一种方法不同,我们采用了HAL自定义的中断服务函数,且通过重新定义回调函数的方法。实现了与方法一相同的功能;无论是采取之前库函数的思想,重写中断服务响应函数;还是采取HAL的逻辑,运用HAL帮我们自定义好的函数;我们都需要充分理解中断执行过程;通过单步运行反复调试代码,阅读参考手册和英文文档注释,帮助我们更好的理解串口异步通信;
