6

YIE002开发探索03-外部中断

 3 years ago
source link: http://yiiyee.cn/blog/2021/07/19/yie002%e5%bc%80%e5%8f%91%e6%8e%a2%e7%b4%a203-%e5%a4%96%e9%83%a8%e4%b8%ad%e6%96%ad/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】

上一篇中,实现了按键的控制功能。本篇准备使用外部中断的方式,来实现对按键的控制。

1 STM32的外部中断

在跑马灯的实验中,简要的介绍过GPIO的设置。对于本篇来说,使用GPIO作为外部输入中断,所需要关注的知识点有两块:
1) 如何配置外部中断,以及与之相关的中断程序的编写;
2) 中断管理分组以及中断的优先级。

1.1 外部中断/事件控制器(EXTI)

STM32的每个IO都可以作为外部中断的中断输入口。STM32F103C8T6有19个能产生事件/中断请求的边沿检测器,每个输入线可以独立配置成输入类型和对应的触发事件(上升沿、下降沿或双边沿触发)。

这19个外部中断为:
线 0~15:对应外部 IO 口的输入中断。
线 16:连接到 PVD 输出。
线 17:连接到 RTC 闹钟事件。
线 18:连接到 USB 唤醒事件。

也就是说,供IO口使用的中断线只有16个。而STM32的IO口数目是超过16个的,因此,必然产生共用的问题。

在设计上,STM32的管脚GPIOx.0GPIOx.15(x=A/B/C/D/E/F/G)分别对应中断线015,也即每个中断线最多对应7个IO口。比如,线1对应GPIOA.1、GPIOB.1、GPIOC.1…GPIOG.1。中断线每次只能连接到1个IO口上,在平常编程中,需要通过配置决定中断线连接到哪个GPIO上。

如图1,给出了GPIO与中断线的映射关系图。

图1 外部中断通用I/O映像

当然,由于我们使用Cube MX编程,这些配置工作,通过图形配置以及自动生成代码,就可以完成了。对于中断你配置的细节,可以参考相应的文档(RM0008)。

另外一个需要注意的问题是,STM32的IO外部中断函数只有6个(在启动文件startup_stm32f103xb.s中可以看到)。

EXPORT EXTI0_IRQHandler
EXPORT EXTI1_IRQHandler
EXPORT EXTI2_IRQHandler
EXPORT EXTI3_IRQHandler
EXPORT EXTI4_IRQHandler
EXPORT EXTI9_5_IRQHandler
EXPORT EXTI15_10_IRQHandler

从函数字面上也可以看出,中断线0-4分别对应一个中断函数;中断线5-9共用中断函数EXPORT EXTI9_5_IRQHandler;中断线10-15共用中断函数EXPORT EXTI15_10_IRQHandler。因此,对于共用函数的中断,在中断函数中还需要分别判断是哪个引脚引发的中断,然后再进行相应的动作。

1.2 中断管理和优先级

ARM Cortex-M3内核支持256个中断,包括16个内核中断和240个外部中断。STM32只是使用了其中一部分,它有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。

对于STM32F103系列的单片机,又只有60个可屏蔽中断。中断的控制,在单片机内部提供了ICER(中断除能寄存器组)、ISPR(中断挂起控制寄存器组)、ICPR(中断解挂控制寄存器组)、IABR(中断激活标志位寄存器组)和IP(中断优先级控制寄存器组)等来进行操作。

对于中断相关寄存器的操作,可以参考《Cortex M3权威指南》,我们在编程时,主要关注的是中断优先级的处理。

优先级的分组是通过AIRCR(应用程序中断及复位控制寄存器)实现的,其bit 8~10用来定义分组。如表1所示。

表1 AIRCR的中断分组设置表

组AIRCR[10:8]分配结果01110 位抢占优先级, 4 位子优先级11101 位抢占优先级, 3 位子优先级21012 位抢占优先级, 2 位子优先级31003 位抢占优先级, 1 位子优先级40114 位抢占优先级, 0 位子优先级

优先级分为抢占优先级和子优先级,抢占优先级在前,子优先级在后,数值越小优先级越高。每个外部中断都有一个优先级寄存器,占用8位来表示优先级。 STM32F103用了60个中断,其优先级相关的位也只用了4位。也就是表1中,抢占优先级和子优先级共用的4位。

举例来说,当抢占优先级占了3位时,它可以取值0至7,此时子优先级只能取值0和1;当抢占优先级占了4位,则可取值0至15,子优先级只能取值0了。

需要注意的是:如果抢占优先级和子优先级一样,哪个中断先发生就先执行;高抢占优先级可以打断正在进行的低抢占优先级的中断,而相同抢占优先级的中断,高子优先级中断不可以打断低子优先级中断。

总结来说,对于GPIO的外部中断,一般的步骤如下:
1) 初始化IO口,开启时钟;
2) 设置IO口与中断线的映射关系;
3) 设置触发条件;
4) 配置中断分组(NVIC),使能中断;
5) 编写中断服务函数;

实际上,使用Cube MX编程,除了第5步编写中断服务函数外,其他步骤都可以自动生成代码。下面进入实际的操作环节。

2 YIE002-STM32型的外部中断编程

编程步骤包括Cube MX的图形配置,生成代码,以及编写中断服务函数。

2.1 Cube MX的图形配置

使用上一篇的代码,在Cube MX上重新配置引脚参数。如图2所示。

图2 GPIO配置

将按键相关的PA1、PA5、PA6都配置成外部中断,其他引脚的配置不变。另外,在GPIO的配置页面,将PA1、PA5和PA6均配置为下降沿触发中断(缺省是上升沿触发)。

对于中断优先级的配置,可在System Core中,选择NVIC,在配置界面中修改。如图3所示。

图3 中断优先级配置

首先,在Pritority Group的下拉菜单中,选择为抢占优先级分配2个位。也就是说,抢占优先级可以取值0至3;子优先级占2位,取值也是0至3。

然后,分别为中断函数设置相应的优先级,并使能中断就可以了。本例中,两个外部中断的抢占优先级都是2,子优先级分别设为了0和1。

完成上述设置后,自动生成代码,就完成了前期工作了。

剩下的工作,只需要编写中断服务程序就可以了。

2.2 中断服务程序的编写

在生成的代码中,MX_GPIO_Init()用来初始化GPIO,与LED灯和按键相关的GPIO,都在此完成配置。从这个函数中,可以看到外部中断的配置过程。

我们所关注的两个中断函数,均位于stm32f1xx_it.c中。分别为:

void EXTI1_IRQHandler(void)
{
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
}

void EXTI9_5_IRQHandler(void)
{
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);
}

这两个函数都调用了HAL_GPIO_EXTI_IRQHandler(),它位于stm32f1xx_hal_gpio.c中,内容为:

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(),这就是Cube Library提供的,供用户修改的中断函数。框架代码中,帮助我们处理了清中断的操作。编码时所要做的,是关注应用需求。

在main.c的 “USER CODE BEGIN 4”和“USER CODE END 4”之间,添加如下代码:

/* USER CODE BEGIN 4 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if (GPIO_Pin == GPIO_PIN_1)
  {
		HAL_Delay(10);
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_12);
  }
	if (GPIO_Pin == GPIO_PIN_5)
  {
		HAL_Delay(10);
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_13);
  }
	if (GPIO_Pin == GPIO_PIN_6)
  {
		HAL_Delay(10);
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_14);
  }
}
/* USER CODE END 4 */

就完成了类似上篇博客的按键功能。

在HAL_GPIO_EXTI_Callback()中,对引发中断的引脚进行了判断。不同的按键按下,将对不同的LED灯进行操作。

当然,由于使用了中断函数,主函数main()中什么代码都不用添加。

141 total views, 2 views today


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK