GPIO外部中断

STM32之“中断”机制很复杂,看了PM(Cortex-m4)和RM,对它们就询问了一个光景。首先,与“中断”相关的术语就闹
exception, interrupt, event
三独。Cortex-m4审批中涵盖一个NVIC控制器,用于拍卖 exception。而 interrupt
是属 exception 之同种植,其它 exception 类型包括 SysTick等。interrupt
又吃作IRQ。

STM32内、Cortex-m4按之外的“中断”,即为
interrupt/IRQ。STM32经 IRQ Channel 向 NVIC 请求处理 IRQ,而 NVIC
处理包括 IRQ 在内的各种 exception,例如:优先级…等等。对于 IRQ,NVIC
将调用其“中断处理程序” ISR。

稍许
STM32 外围接口直接通过 IRQ Channel 与 NVIC 接口,而 GPIO
外部中断则使经另外一个控制器–EXTI–与NVIC接口。GPIO及 EXTI
之间的接口称为 EXTI line;而 EXTI 与 NVIC之间虽然为 IRQ Channel。GPIO
pin与EXTI line之间是n:1底涉嫌,而EXTI line与 IRQ
Channel之间吧是n:1的涉及。基本上,PXn 对应 EXTI line n,这里X=A,
B, … H,n=0, 1, 2 … 15。例如,PX2(PA2, PB2 …)都针对承诺给 EXTI
line 2。

EXTI
line同 IRQ Channel之间的对应关系则小复杂,16个 EXTI line 占用7单
IRQ:

  • EXTI
    line 0 – 4 分别对应一个IRQ,因此,共有5独 IRQ
  • EXTI
    line 5 – 9 一并用一个IRQ
  • EXTI
    line 10 – 15 共用一个IRQ

另外,EXTI
line 上除支持 interrupt 之外,还支持 event。event
被点之后,并无传递让 NVIC 去处理(像 IRQ
那样),而是发送一个脉冲给电源管理模块,似乎是因此来兑现唤醒功能的。

GPIO、EXTI
与 NVIC 之间的涉,用生图简单表示:

图片 1

 

用,对于编程而言,需要对GPIO、EXTI、NVIC
3个模块分别展开布置以及操作。所幸,Cube HAL 以及 CubeMX
工具大大地回落了付出的复杂度。

Nucleo
开发板上闹一个用户按钮B1以及一个用户LED LD2,可以就此它们来实现一个简便的
GPIO 外部中断 Demo。Nucleo 原理图展示,B1 进行了 RC
de-bouncing,因此好当做外部中断源。未经 de-bouncing
的按钮,是勿应有接触中断的。B1通在 PC13
口,已经计划了上拉电阻:

 

图片 2

 

 

使用CubeMX,将B1丁模式设置也
GPIO_EXIT13。可见,PC13 使用了 EXTI line
13。另外,由于采用了上拉电阻,选择暂停为减低沿触发:

图片 3

图片 4 

 

这般,GPIO以及
EXTI 就安排好了。别忘了还索要配置 NVIC。这里就待简单地启用其对应的
IRQ即可,其余保持默认:

图片 5

 

简分析一下
CubeMX 生成的代码。首先,中断向量表定义在启动代码 startup_stm32f303xe.s
中,在此文件被好看看有着 exception 处理程序(函数叫作),包括 EXTI
ISR:

g_pfnVectors:
    .word   _estack
    .word   Reset_Handler
    .word   NMI_Handler
    ...
    .word   SysTick_Handler
    ...
    .word   EXTI0_IRQHandler
    .word   EXTI1_IRQHandler
    .word   EXTI2_TSC_IRQHandler
    .word   EXTI3_IRQHandler
    .word   EXTI4_IRQHandler
    ...
    .word   EXTI9_5_IRQHandler
    ....
    .word   EXTI15_10_IRQHandler
    ....

  

其中,EXTI15_10_IRQHandler
就是按钮B1底中止处理程序。这个函数的贯彻在 stm32f3xx_it.c
中,它实在就调用了 Cube 库的 HAL_GPIO_EXTI_IRQHandler()
函数,将捧口号作为参数传递进去:

/**
* @brief This function handles EXTI line[15:10] interrupts.
*/
void EXTI15_10_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI15_10_IRQn 0 */

  /* USER CODE END EXTI15_10_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
  /* USER CODE BEGIN EXTI15_10_IRQn 1 */

  /* USER CODE END EXTI15_10_IRQn 1 */
}

  

检查 HAL_GPIO_EXTI_IRQHandler()
函数的实现,发现它座落 GPIO HAL
模块内,它而调用了一个回调函数 HAL_GPIO_EXTI_Callback(),而拖欠回调函数的默认实现声明也
__weak 属性,我们可覆盖:

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
   ...

  

因此,我们在 stm32f3xx_it.c
增加 HAL_GPIO_EXTI_Callback() 的实现,每当B1按下,开/关LD2:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
}

  

另外,在gpio.c
中的 MX_GPIO_Init() 函数惨遭,看到了 NVIC 的部署,但并无看出与 EXTI
有关的布局。其实,EXTI 配置都由 HAL_GPIO_Init()
函数处理,不劳我们辛苦。也就是说,对 GPIO 的外部中断的处理,要动用 GPIO
和 NVIC 2单Cube 模块:

  ...
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
  ...
  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);