北屋教程网

专注编程知识分享,从入门到精通的编程学习平台

STM32CUBEMX 使用教程3 — 外部中断(EXTI)的使用

1、外部中断是什么?

通俗的来说,单片机的外部中断(External Interrupts, EXTI)是 MCU 中很重要的功能之一,它允许单片机对外部事件做出快速响应。

当连接到某个GPIO引脚的中断线检测到预设的电平变化或边沿变化时,就会产生一个中断请求,使CPU暂停当前任务,转而去执行相应的中断服务程序。这样,单片机就能够及时地处理外部事件,提高了系统的实时性和灵活性。

STM32 的外部中断/事件控制器(EXTI) 由20个产生事件/中断请求的边沿检测器组成,每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。

STM32的外部中断/事件控制器框图如下,实现外部事件触发的逻辑电路示意如图中的红色标线位置。

STM32F103 提供了多个外部中断线(EXTI),这些中断线可以连接到不同的GPIO引脚,也就是所有的IO中只要能作为输入/输出功能使用的都支持外部中断触发功能。

通用 I/O 端口以 EXTIn 中断控制的方式连接到16个外部中断/事件线上,分别为 EXTI0 ... EXTI15,如下:

配置使用外部中断的大致操作流程为:想要产生外部中断,必须先配置好并使能中断线,根据需要的边沿检测(上升沿/下降沿)设置触发寄存器,同时在中断屏蔽寄存器的相应位写‘1’允许中断请求。当外部中断线上发生了期待的边沿时,将产生一个中断请求,对应的挂起位也随之被置‘1’,通过挂起寄存器的对应位写‘1’,将清除该中断请求。

2、STM32CUBEMX 配置外部中断

本文使用实验板上的按键作为外部中断的演示,板载按键的连接原理图如下:

使用 WK_UP 和 KEY0 两个按键分别实现外部中断的下降沿和上升沿的触发外部中断配置。

对应连接的IO口如下:

STM32CUBEMX 的配置如下:

(1)打开 cubemx 选择 Pinout & Configuration,选择 PA0 和 PE4 配置为外部中断功能。

(2)选择 Pinout & Configuration 中的 GPIO 配置外部中断的属性。

(3)中断的触发方式选择。

WK_UP 连接 PA0 引脚,按键按下该引脚是高电平,所以可以把这个引脚默认为下拉,上升沿方式触发中断。

KEY0 连接 PE4 引脚,按键按下该引脚是低电平,所以可以把这个引脚默认为上拉,下降沿方式触发中断。

(4)选择 Pinout & Configuration 中的 NVIC 使能外部中断,并设置抢占优先级和子优先级。

(5)点 GENERATE CODE 生成代码。用 keil 打开代码,在 void MX_GPIO_Init(void) 函数中能找到相关的配置代码:

void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};


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


  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = KEY_0_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(KEY_0_GPIO_Port, &GPIO_InitStruct);


  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = KEY_WK_UP_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  HAL_GPIO_Init(KEY_WK_UP_GPIO_Port, &GPIO_InitStruct);


  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(EXTI0_IRQn);


  HAL_NVIC_SetPriority(EXTI4_IRQn, 3, 0);
  HAL_NVIC_EnableIRQ(EXTI4_IRQn);
}

(6)中断函数。

外部中断的函数入口在 stm32f1xx_it.c 中,如下:

/**
  * @brief This function handles EXTI line0 interrupt.
  */
void EXTI0_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI0_IRQn 0 */


  /* USER CODE END EXTI0_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(KEY_WK_UP_Pin);
  /* USER CODE BEGIN EXTI0_IRQn 1 */


  /* USER CODE END EXTI0_IRQn 1 */
}


/**
  * @brief This function handles EXTI line4 interrupt.
  */
void EXTI4_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI4_IRQn 0 */


  /* USER CODE END EXTI4_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(KEY_0_Pin);
  /* USER CODE BEGIN EXTI4_IRQn 1 */


  /* USER CODE END EXTI4_IRQn 1 */
}

进入中断函数以后,会再调用 HAL_GPIO_EXTI_IRQHandler();

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_Callback(); 处理中断事务。这个函数 HAL 库中定义了一个 weak 型的函数,这个需要自己定义一个函数用于执行外部中断事务,如果不定义则会使用库中已经定义的 weak 型的回调函数。

比如我这里在代码中其他地方重新定义这个中断回调函数,当中断被检测到的时候,翻转板子上的LED灯,每个按键对应一颗LED,如下:

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
   */
	
	if(GPIO_Pin == KEY_WK_UP_Pin)
	{
		HAL_GPIO_TogglePin(LED1_PORT_GPIO_Port, LED1_PORT_Pin);
	}
	
	if(GPIO_Pin == KEY_0_Pin)
	{
		HAL_GPIO_TogglePin(LED0_PORT_GPIO_Port, LED0_PORT_Pin);
	}
}


控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言