博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
GPIO外部中断
阅读量:4954 次
发布时间:2019-06-12

本文共 3081 字,大约阅读时间需要 10 分钟。

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 之间的关系,用下图简单表示:

 

因此,对于编程而言,需要对GPIO、EXTI、NVIC 3个模块分别进行配置和操作。所幸,Cube HAL 以及 CubeMX 工具大大地降低了开发的复杂度。

Nucleo 开发板上有一个用户按钮B1和一个用户LED LD2,可以用它们来实现一个简单的 GPIO 外部中断 Demo。Nucleo 原理图显示,B1 进行了 RC de-bouncing,因此可以作为外部中断源。未经 de-bouncing 的按钮,是不应该触发中断的。B1接在 PC13 口,已经设计了上拉电阻

 

 

 

使用CubeMX,将B1口模式设置为 GPIO_EXIT13。可见,PC13 使用了 EXTI line 13。另外,由于使用了上拉电阻,选择中断为下降沿触发:

 

 

这样,GPIO和 EXTI 就配置好了。别忘了还需要配置 NVIC。这里只需要简单地启用它对应的 IRQ即可,其余保持默认:

 

简要分析一下 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);

  

 

转载于:https://www.cnblogs.com/vinccc/p/8273559.html

你可能感兴趣的文章
fegin 调用源码分析
查看>>
Linux的基本命令
查看>>
02号团队-团队任务3:每日立会(2018-12-05)
查看>>
SQLite移植手记1
查看>>
C# windows程序应用与JavaScript 程序交互实现例子
查看>>
HashMap详解
查看>>
js05-DOM对象二
查看>>
mariadb BINLOG_FORMAT = STATEMENT 异常
查看>>
C3P0 WARN: Establishing SSL connection without server's identity verification is not recommended
查看>>
iPhone在日本最牛,在中国输得最慘
查看>>
动态方法决议 和 消息转发
查看>>
WPF自定义搜索框代码分享
查看>>
js 基础拓展
查看>>
SpringBoot访问html访问不了的问题
查看>>
{width=200px;height=300px;overflow:hidden}
查看>>
C#生成随机数
查看>>
CSS基础学习 20.CSS媒体查询
查看>>
2019春季第十一周作业
查看>>
洛谷P4591 [TJOI2018]碱基序列 【KMP + dp】
查看>>
iOS CoreData介绍和使用(以及一些注意事项)
查看>>