STM32中断控制


一、什么是中断

1.1 基本概念

中断,在单片机中占有非常重要的地位,几乎任何一款单片机都会有中断。。代码默认地从上向下执行,遇到条件或者其他语句,会按照指定的地方跳转。而在单片机执行代码的过程中,难免会有一些突发的情况需要处理,这样就会打断当前的代码,待处理完突发情况之后,程序会回到被打断的地方继续执行。

1.2 关于STM32的中断

STM32具有十分强大的中断系统,将中断分为了两个类型:内核异常和外部中断。并将所有中断通过一个表编排起来,下面是stm32中断向量表的部分内容:

 

 

 

 

 

 上图-3到6这个区域被标黑了,这个区域就是内核异常。内核异常不能够被打断,不能被设置优先级(也就是说优先级是凌驾于外部中断之上的)。常见的内核异常有以下几种:复位(reset),不可屏蔽中断(NMI),硬错误(Hardfault),其他的也可以在表上找到。

从第7个开始,后面所有的中断都是外部中断。外部中断是我们必须学习掌握的知识,包含线中断,定时器中断,IIC,SPI等所有的外设中断,可配置优先级。外部中断的优先级分为两种:抢占优先级和响应优先级。

什么是抢占优先级?

抢占优先级比较霸道,一言不和就插队。抢占优先级高的,能够打断优先级低的任务,等优先级较高的任务执行完毕后,再回来继续执行之前的任务。所以当存在多个抢占优先级不同的任务时,很有可能会产生任务的嵌套。

什么是响应优先级?

响应优先级则稍微谦逊些,比较有礼貌。响应优先级又被称为次优先级,若两个任务的抢占式优先级一样,那么响应优先级较高的任务则先执行,且在执行的同时不能被下一个响应优先级更高的任务打断,所以我说它比较有有礼貌

1.3中断发生的过程

 

 

 1.4中断的作用

  • 速度匹配:可以解决快速的CPU与慢速的外部设备之间传送数据的矛盾。
  • 分时操作:CPU可以分时为多个外部设备服务,提高计算机的利用率。
  • 实时响应:CPU能够及时处理应用系统的随机事件,增强系统的实时性。
  • 可靠性高:CPU可以处理设备故障及掉电等突发事件,提高系统可靠性。

2 HAL库

2.1HAL的中断

例如51单片机中的中断函数,之前使用标准库编写程序时,中断程序(函数)由我们自己实现。

而STM32的HAL库的中断处理函数是按照HAL处理机制来实现,如USART1,统一由HAL_UART_IRQHandler来进行处理,如下图:

 

 

 

其它大部分外设(TIM、SPI、CAN...)中断都类似,HAL进行统一处理。

也就是说,HAL已经帮我们把中断处理函数写好了,我们只需要调用相应函数来编写应用程序就行了。

HAL_xxx_IRQHandler里面做了哪些处理? 我们以STM32F1的HAL_UART_IRQHandler为例:

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;
    }
  }
 
 
  /* If some errors occur */
  if((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))
  {
  /*
  ·
  ·删减了部分代码
  ·
  */
  } /* End if some error occurs */
 
 
  /* 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;
  }
}

这些和我们之前编写的中断处理函数是不是有类似之处?

这是无非就是接收中断、发送中断、错误中断等一系列处理。只是这里又进行了再次封装,比如接收中断UART_Receive_IT。

当然,这个UART_Receive_IT接收中断实现方式又可能存在不同。像F0、F1...就是直接调用这个接收中断函数来进一步处理。

像L0、G0...是通过执行指针函数RxISR来进一步处理。G0的接收中断处理为:huart->RxISR(huart);

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
  //删除了前面代码
  /* If no error occurs */
  errorflags = (isrflags & (uint32_t)(USART_ISR_PE | USART_ISR_FE | USART_ISR_ORE | USART_ISR_NE));
  if (errorflags == 0U)
  {
    /* UART in mode Receiver ---------------------------------------------------*/
    if (((isrflags & USART_ISR_RXNE_RXFNE) != 0U)
        && (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U)
            || ((cr3its & USART_CR3_RXFTIE) != 0U)))
    {
      if (huart->RxISR != NULL)
      {
        huart->RxISR(huart);
      }
      return;
    }
  }
  //删除了后面代码
}

2.2回调函数

在HAL库中存在大量类似HAL_XXX_XXXCallback这样的函数,这些都是回调函数。

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

三、进行一些尝试(控制LED灯)

在STMCUBEX,新建一个项目,例如图中用的单片机是STM32103C8

点击sys,将debug选项改为Serial Wire

 

 然后在Rcc里的High Speed Clock 中选择Crystal/Ceramic Resonator

 

 

 将PB0选为外部中断触发器(GPIO_EXTI0),PA1是控制led灯的,和将它选择为GPIO_output

 

选择PLLCLK,然后将后面的晶振频率最大值改为72M赫兹

 

 

 project里把toolchain那里改为MDK-ARM,版本选择最新

 

 选择生成初始化文件,然后选择生成代码

 

 创建过程

 

创建成功

 

 打开项目,来到keil5,进入到mian.c里,接下来就是刚刚提到的在回调函数里写代码了,将回调函数重写一遍就行了,代码如下:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
    GPIO_PinState b0_pin = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);  // 读取b0的状态
    b0_pin=1-b0_pin;
    switch (GPIO_Pin){//判断引脚
        case GPIO_PIN_0:
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1,1-b0_pin);  // 将a1写入与b0相同的电位
            break;
    }
    
}

结果展示:

 

四、中断实现串口通信

设置大多数和上述相同 下面只展示不同的部分

 

 

project manager里的步骤也和上一个是一样的,接下来就是在keil5里打开项目,进入main.c文件,在里面定义如下数据,注意不是在main函数里,而是在头文件后

uint8_t aRxBuffer;//接收缓冲中断
uint8_t Uart1_RxBuff[256];//接收缓冲
uint8_t Uart1_Rx_Cnt=0;//接收缓冲计数
uint8_t cAlmStr[]="DataOverflow(>256)";

在定义了

 

 这个数据类型后插入代码

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(Uart1_Rx_Cnt >= 255)  //溢出判断
    {
        Uart1_Rx_Cnt = 0;
        memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff));
        HAL_UART_Transmit(&huart1, (uint8_t *)&cAlmStr, sizeof(cAlmStr),0xFFFF);    
    }
    else
    {
        Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer;   //接收数据转存
    
        if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)||(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
        {
            HAL_UART_Transmit(&huart1, (uint8_t *)&Uart1_RxBuff, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去
            Uart1_Rx_Cnt = 0;
            memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff)); //清空数组
        }
    }
    
    HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);   //再开启接收中断

}

然后在主函数写一个接收中断函数

int main(void)
{
 //初始化
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
 
    
    
    //接收中断函数
    HAL_UART_Receive_IT(&huart1,(uint8_t*)&aRxBuffer,1);
    
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

结果如下:

 

 我们发送什么 就会接受到什么。

五、总结

中断函数的学习过程是一次很大的自我挑战,但是中断函数在单片机中的运用可以说是无比的重要,他极大程度的节约了单片机的使用资源,学会了使用中断单片机可以不同再像之前一样,傻傻的等待我们的指令再去进行操作,这次的学习过程让我受益匪浅。

六、参考链接

https://blog.csdn.net/m0_58414679/article/details/121060073?spm=1001.2014.3001.5501

https://blog.csdn.net/DP29syM41zyGndVF/article/details/113804917

https://blog.csdn.net/as480133937/article/details/104827639

https://www.cnblogs.com/breezy-ye/articles/12157442


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM