stm32 笔记(HAL库) 外部中断

重新整理一下笔记。

  来自CPU外部的外设产生的紧急事件叫做中断,比如GPIO引脚电平变化;来自CPU内部产生的紧急事件叫做异常。 异常和中断的效果基本一致,都是暂停当前任务,优先执行紧急事件,因此一般将中断和异常统称为中断

中断优先级(16个可编程的优先等级  4位中断优先级  2的4次方=16)

stm32 有20个外部中断线,其中常用的有16个外部中断线

常用的16个外部中断线

 配置过程如下:

                        

以按键外部中断控制灯和蜂鸣器为例,在STM32cubemx 创建工程配置RCC选择外部晶振

修改时钟频率

配置GPIO 这里只放了按键的配置(外部中断)

                                

配置输入模式   上升沿触发  上拉输入

                ​​​​​​​        

使能中断向量

保存工程  。工程创建完毕

在keil 中打开工程。代码分析如下:

gpio.c文件中的 GPIO 初始化函数  使能GPIOA、GPIOB时钟

void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

E2、E3引脚为按键外部中断

配置引脚触发模式   上升沿触发

上拉输入

LED、Beep 为普通GPIO

配置为推挽输出模式

没有和上拉和下拉

HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(Beep_GPIO_Port, Beep_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pins : PE2 PE3 */
  GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;//上升沿触发
  GPIO_InitStruct.Pull = GPIO_PULLUP;//上拉输入
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  /*Configure GPIO pins : PBPin PBPin */
  GPIO_InitStruct.Pin = LED_Pin|Beep_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出
  GPIO_InitStruct.Pull = GPIO_NOPULL;//没有上拉和下拉
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

配置NVIC对应的中断线

/* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI2_IRQn, 4, 0);
  HAL_NVIC_EnableIRQ(EXTI2_IRQn);//使能中断线

  HAL_NVIC_SetPriority(EXTI3_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(EXTI3_IRQn);//使能中断线

}
/**
  * @brief  Sets the priority of an interrupt.
  * @param  IRQn: External interrupt number.
  *         This parameter can be an enumerator of IRQn_Type enumeration
  *         (For the complete STM32 Devices IRQ Channels list, please refer to the appropriate CMSIS device file (stm32f10xx.h))
  * @param  PreemptPriority: The preemption priority for the IRQn channel.
  *         This parameter can be a value between 0 and 15
  *         A lower priority value indicates a higher priority 
  * @param  SubPriority: the subpriority level for the IRQ channel.
  *         This parameter can be a value between 0 and 15
  *         A lower priority value indicates a higher priority.          
  * @retval None
  */
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
//参数1 中断号
//参数2 抢占优先级
//参数3 子优先级

在stm32f1xx hal_gpio.c 文件HAL_GPIO_Init()函数中,进行配置外部中断,这里初始化了GPIO的输入输出模式的选择、外部中断的配置(使能了AFIO复用时钟)。

 /*--------------------- EXTI Mode Configuration ------------------------*/
      /* Configure the External Interrupt or event for the current IO */
      if ((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
      {
        /* Enable AFIO Clock */
        __HAL_RCC_AFIO_CLK_ENABLE();
        temp = AFIO->EXTICR[position >> 2u];
        CLEAR_BIT(temp, (0x0Fu) << (4u * (position & 0x03u)));
        SET_BIT(temp, (GPIO_GET_INDEX(GPIOx)) << (4u * (position & 0x03u)));
        AFIO->EXTICR[position >> 2u] = temp;


        /* Enable or disable the rising trigger */
        if ((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
        {
          SET_BIT(EXTI->RTSR, iocurrent);
        }
        else
        {
          CLEAR_BIT(EXTI->RTSR, iocurrent);
        }

        /* Enable or disable the falling trigger */
        if ((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
        {
          SET_BIT(EXTI->FTSR, iocurrent);
        }
        else
        {
          CLEAR_BIT(EXTI->FTSR, iocurrent);
        }

        /* Configure the event mask */
        if ((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
        {
          SET_BIT(EXTI->EMR, iocurrent);
        }
        else
        {
          CLEAR_BIT(EXTI->EMR, iocurrent);
        }

        /* Configure the interrupt mask */
        if ((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
        {
          SET_BIT(EXTI->IMR, iocurrent);//使能外部中断
        }
        else
        {
          CLEAR_BIT(EXTI->IMR, iocurrent);
        }
      }
    }

在stm32f1xx_it.c 中生成中断触发函数,但在HAL库中我们并不能在这里面进行相关中断处理,在HAL库有一个callback回调函数。

回调函数是一种常见的编程技术,它可以在异步操作完成后调用一个预定义的函数来处理结果。回调函数通常用于处理事件、执行异步操作或响应用户输入等场景。回调函数的作用是将代码逻辑分离出来,使得代码更加模块化和可维护。使用回调函数可以避免阻塞程序的运行,提高程序的性能和效率。另外,回调函数还可以实现代码的复用,因为它们可以被多个地方调用。

/**
  * @brief This function handles EXTI line2 interrupt.
  */
void EXTI2_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI2_IRQn 0 */

  /* USER CODE END EXTI2_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
  /* USER CODE BEGIN EXTI2_IRQn 1 */

  /* USER CODE END EXTI2_IRQn 1 */
}

/**
  * @brief This function handles EXTI line3 interrupt.
  */
void EXTI3_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI3_IRQn 0 */

  /* USER CODE END EXTI3_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);
  /* USER CODE BEGIN EXTI3_IRQn 1 */

  /* USER CODE END EXTI3_IRQn 1 */
}

 打开HAL_GPIO_EXTI_IRQHandler函数的定义,在这里我们发现这个函数调用了HAL_GPIO_EXTI_Callback()函数,而这个函数前面有一个_weak弱声明。    用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数,那么编译器就会执行__weak声明的函数,并且编译器不会报错。所以我们可以在别的地方定义一个相同名字的函数,而不必也尽量不要修改之前的函数。那么我们需要在用户文件(main.c)中,自己再定义一个一模一样的函数,只是我们自己定义的函数,不需要指明是弱函数。

/**
  * @brief  This function handles EXTI interrupt request.
  * @param  GPIO_Pin: Specifies the pins connected EXTI line
  * @retval None
  */
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

/**
  * @brief  EXTI line detection callbacks.
  * @param  GPIO_Pin: Specifies the pins connected EXTI line
  * @retval None
  */
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(GPIO_Pin);
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_GPIO_EXTI_Callback could be implemented in the user file
   */
}

所以在main.c中,我们直接复制这个回调函数,在这里我们进行中断处理。

/* USER CODE BEGIN 4 */
 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
 {
	if(GPIO_Pin == GPIO_PIN_2)
	{
		HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
	}
	
	if(GPIO_Pin == GPIO_PIN_3 )
	{
		HAL_GPIO_TogglePin(Beep_GPIO_Port, Beep_Pin);
	}
 
 }
/* USER CODE END 4 */

外部中断相关寄存器

这些寄存器(除了PR寄存器)都在HAL_GPIO_Init()函数中进行配置,EXTI_PR寄存器在

/**
  * @brief  This function handles EXTI interrupt request.
  * @param  GPIO_Pin: Specifies the pins connected EXTI line
  * @retval None
  */
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

函数中配置,打开__HAL_GPIO_EXTI_GET_IT()发现这是宏。所以在这个函数中进行外部中断标志进行检查、检查是否有边沿事件,若有系统会把PR寄存器置零,需要我们清除标志,所以,置1清除标志,等待下一个边沿事件。这里我们可以对比51单片机的串口中断的RI和TI标志位的检测和清除。

/**
  * @brief  Checks whether the specified EXTI line is asserted or not.
  * @param  __EXTI_LINE__: specifies the EXTI line to check.
  *          This parameter can be GPIO_PIN_x where x can be(0..15)
  * @retval The new state of __EXTI_LINE__ (SET or RESET).
  */
#define __HAL_GPIO_EXTI_GET_IT(__EXTI_LINE__) (EXTI->PR & (__EXTI_LINE__))

/**
  * @brief  Clears the EXTI's line pending bits.
  * @param  __EXTI_LINE__: specifies the EXTI lines to clear.
  *          This parameter can be any combination of GPIO_PIN_x where x can be (0..15)
  * @retval None
  */
#define __HAL_GPIO_EXTI_CLEAR_IT(__EXTI_LINE__) (EXTI->PR = (__EXTI_LINE__))

总结

在这里我虽然使用了HAL库进行学习,但是HAL库与标准库本质上都是通过配置寄存器来是单片机工作的,只是它们的API不同。配置普通GPIO需要使能时钟、配置引脚的输入输出模式,同样外部中断也是通过GPIO,但是需要我们复用为外部中断,所以还要使能复用时钟AFIO。

  • 12
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32G431的HAL库中,可以使用外部中断来处理外部信号的触发。下面是使用HAL库进行外部中断的步骤: 1. 配置IO口为外部中断输入源:在GPIO初始化函数中,使用`GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING/FALLING/RISING_FALLING`来设置IO口的触发方式,可以选择上升沿触发、下降沿触发或者上升下降沿都触发。 2. 在NVIC中使能外部中断,并分配优先级:使用`HAL_NVIC_SetPriority(EXTI_IRQn, PreemptPriority, SubPriority)`函数设置外部中断的优先级,其中`EXTI_IRQn`是外部中断的中断号,`PreemptPriority`是抢占优先级,`SubPriority`是子优先级。 3. 实际的中断执行函数:在中断执行函数中,可以编写具体的中断处理代码。在HAL库中,中断执行函数的命名规则为`void EXTIx_IRQHandler(void)`,其中`x`是对应的外部中断线号。 4. 使能中断时:使用`HAL_NVIC_EnableIRQ(EXTI_IRQn)`函数使能外部中断。 下面是一个使用HAL库进行外部中断的示例代码: ```c // 配置IO口为外部中断输入源 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_PIN_0; // 设置为对应的IO口 GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; // 上升沿触发 GPIO_InitStruct.Pull = GPIO_PULLDOWN; // 默认拉低 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 在NVIC中使能外部中断,并分配优先级 HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); // 实际的中断执行函数 void EXTI0_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); } // 使能中断 HAL_NVIC_EnableIRQ(EXTI0_IRQn); ``` 请注意,以上代码仅为示例,具体的配置和使用方法可能会根据实际情况有所不同。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值