1+X 传感网中级备考实例解析:异步串行通信的接收中断(2)

  • 概要
  • 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;
    }
  }
图1HAL_UART_IRQHandler函数定义

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帮我们自定义好的函数;我们都需要充分理解中断执行过程;通过单步运行反复调试代码,阅读参考手册和英文文档注释,帮助我们更好的理解串口异步通信;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容