STM32 外部中断实验

实验内容

  • 编程写程序使用PA4管脚(S2按键)的外部输入中断功能。当S2按键按下后,在相应的外部中断服务函数中切换LED2灯的状态。

实验目的

  • 复习LED和按键的原理和编程方法;
  • 了解中断、中断向量、中断优先级和中断服务函数等概念;
  • 掌握外部中断的编程方法。

实验环境

实验所需硬件

序号 名称 数量 备注
1 电脑 1台 系统Windows7及以上
2 STM32底座模块 1个 ·
3 LED模块 1个 ·
4 ST-Link下载器 1个 ·
5 ST-Link下载器连接线 1根 ·
6 配套外部中断实验代码 1份 ·

实验所需软件

  • MDK5 安装步骤

  • ST-LINK 驱动安装步骤

  • Git 软件下载(可选)

ST-Link下载器 & ST-Link下载器连接线

STLink下载器

STM32底座:HIVE PRO STM32是一种基于STM32F103C8T6芯片的蜂巢底座。

STM32底座

LED模块:LED模块共有四个按键,4个LED灯,可供完成流水灯、按键处理等相关实验。按键的触发为低电平,LED灯低电平点亮。

LED模块

实验要求

  • 熟悉STM32的中断系统,能够自行编写程序配置外部中断。
  • 理解中断发生的整个过程,理解抢占优先级和响应优先级在多个中断发生时候的作用。

实验原理

中断介绍

中断

ARM Coetex-M3内核共支持256个中断,其中16个内部中断,240个外部中断和可编程的256级中断优先级的设置。

STM32目前支持的中断共84个(16个内部+68个外部),还有16级可编程的中断优先级的设置,仅使用中断优先级设置8bit中的高4位。

STM32可支持68个中断通道,已经固定分配给相应的外部设备,每个中断通道都具备自己的中断优先级控制字节PRI_n(8位,但是STM32中只使用4位,高4位有效),每4个通道的8位中断优先级控制字构成一个32位的优先级寄存器。68个通道的优先级控制字至少构成17个32位的优先级寄存器。

其中4bit的中断优先级可以分成2组,从高位看,前面定义的是抢占式优先级,后面是响应优先级。按照这种分组,4bit一共可以分成5组:

  • 第0组:所有4bit用于指定响应优先级;
  • 第1组:最高1位用于指定抢占式优先级,后面3位用于指定响应优先级;
  • 第2组:最高2位用于指定抢占式优先级,后面2位用于指定响应优先级;
  • 第3组:最高3位用于指定抢占式优先级,后面1位用于指定响应优先级;
  • 第4组:所有4位用于指定抢占式优先级。

所谓抢占式优先级和响应优先级,他们之间的关系是:具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套。

当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。每一个中断源都必须定义2个优先级。

其中有几点需要注意的是:

  • 如果指定的抢占式优先级别或响应优先级别超出了选定的优先级分组所限定的范围,将可能得到意想不到的结果;
  • 抢占式优先级别相同的中断源之间没有嵌套关系;
  • 如果某个中断源被指定为某个抢占式优先级别,又没有其它中断源处于同一个抢占式优先级别,则可以为这个中断源指定任意有效的响应优先级别。

外部中断

STM32中,每一个GPIO都可以触发一个外部中断,但是,GPIO的中断是以组为一个单位的,同组间的外部中断同一时间只能使用一个。比如说,PA0、PB0、PC0、PD0、PE0、PF0、PG0这些为1组,如果我们使用PA0作为外部中断源,那么其它的PB0、PC0、PD0、PE0、PF0、PG0就不能够再使用了,在此情况下,只能使用类似于PB1、PC2这种末端序号不同的外部中断源。每一组使用一个中断标志EXTIx。EXTI0–EXTI4这5个外部中断有着自己的单独的中断响应函数,EXTI5-9共用一个中断响应函数,EXTI10-15共用一个中断响应函数,如图所示。对于中断的控制,STM32有一个专用的管理机构:NVIC。

GPIO与中断线的映射关系图

中断配置步骤

要把IO口配置为外部中断输入,配置步骤如下:

  • 初始化IO口为输入。

将IO口设置为外部中断输入,可以设置为上拉/下拉输入,也可以设置为浮空输入,但浮空的时候外部一定要带上拉,或者下拉电阻。否则可能导致中断不停的触发。在干扰较大的地方,就算内部设置了上拉/下拉,也建议使用外部上拉/下拉电阻,这样可以一定程度防止外部干扰带来的影响。

  • 开启IO口复用时钟,设置IO口与中断线的映射关系。

STM32的IO口与中断线的对应关系需要配置外部中断配置寄存器EXTICR,先开启复用时钟,然后配置IO口与中断线的对应关系。再把外部中断与中断线连接起来。

  • 开启与该IO口相对的线上中断/事件,设置触发条件。

配置中断所产生的条件,STM32可以配置成上升沿触发,下降沿触发,或者任意电平变化触发,但是不能配置成高电平触发和低电平触发。这里根据实际情况来配置。同时要开启中断线上的中断,这里需要注意的是:如果使用外部中断,并设置该中断的EMR位的话,会引起软件仿真不能跳到中断,而硬件上是可以的。而不设置EMR,软件仿真就可以进入中断服务函数,并且硬件上也是可以的。建议不要配置EMR位。

  • 配置中断分组(NVIC),并使能中断。

将中断进行分组配置,以及使能,对STM32的中断来说,只有配置了NVIC的设置,并开启才能被执行,否则是不会执行到中断服务函数里面去的。

  • 编写中断服务函数。

这是中断设置的最后一步,中断服务函数,是必不可少的,如果在代码里面开启了中断,但是没编写中断服务函数,就可能引起硬件错误,从而导致程序崩溃!所以在开启了某个中断后,一定要记得为该中断编写服务函数。在中断服务函数里面编写你要执行的中断后的操作。

实验步骤

0

① LED模块安装于STM32底座上,ST-Link连接底座与计算机。如下图:
安装模块

② 访问github,进入github界面后点击Code,Clone HTTPS安全链接,如下图所示:
操作步骤

③ 打开电脑终端,进入工作目录workspace (workspace 为工程文件夹所在目录):

cd workspace

④ 运行clone命令:

git clone https://github.com/aiotcom/eps.git

下载目录至指定文件夹下。

如果提示“command not found”表示电脑没有安装Git,请至Git官网下载。
如果电脑没有安装 Git 软件,也可以进入Github,点击 Code -> DownLoad ZIP 下载所有工程代码。如下图所示:
下载代码
如果电脑没有公网,可以进:D盘\实验教程与代码选择相应的代码。

⑤ 打开 Keil 5 工程软件,点击工具栏: ` Project -> Open Project,选择工程文件:STM32基础实验\3.外部中断实验\USER\EXT1.uvprojx` 并打开。

打开工程
选择文件
如果没有安装该软件,请至Keil官网下载。

⑥ 工程启动后,点击 Rebuild 重新编译。如下图:

重新编译工程

⑦ 编译成功,如下图:

编译成功

⑧ 点击 Download 按钮下载程序,如下图所示:

下载程序

下载成功

⑨ 下载完成后,将USB线进行重连操作(即:将STLink的USB线从底座上取下,再重新接上)。

⑩ 如下图,第一次按下按键S2,LED2亮,再次按下按键S2,LED2灭,每次按都会改变LED2的状态。

实验效果图

代码讲解

① 程序目录结构,如下图。CORE文件夹为STM32内核代码,HALLIB文件文件夹为底层HAL库文件。我们主要关心,main.c及HARDWARE中的代码。

代码目录结构

② main.c中进行硬件的初始化及整个代码的逻辑控制。重点了解外部中断的中断服务函数中的代码。当按键S2按下后,程序就会运行到EXTI4_IRQHandler()中,每次按下后都会切换LED2的状态。

int main(void)
{
    HAL_Init();//初始化HAL库    
    LED_Init();//初始化LED	
    EXTI4_Init();//初始化PA4外部中断,PA4->按键S2
    while(1);
}
//==========================================================
//函数名称:EXTI4_IRQHandler()
//
//函数功能:中断服务函数
//
//入口参数:无
//
//返回参数:无
//
//说明:EXTI4_IRQHandler 为外部中断4中断处理函数。函数名字不可修改。
//==========================================================
void EXTI4_IRQHandler(void)
{
    static uint8_t state = 0;
    state = 1 - state;
    state?LED2_ON():LED2_OFF();

    __HAL_GPIO_EXTI_CLEAR_IT(S2_PIN);//清除中断
}

③ LED.c为LED驱动代码,代码实现对LED灯控制的IO进行初始化设置IO口为推挽上拉模式。重点不要忘记使用能IO接口的时钟及设置输出的速度。

    void LED_Init(void)
    {
        GPIO_InitTypeDef GPIO_Initure;

        __HAL_RCC_GPIOA_CLK_ENABLE();//开启GPIOA时钟
            
        GPIO_Initure.Pin=LED1_PIN|LED2_PIN|LED3_PIN|LED4_PIN;
        GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;//推挽输出
        GPIO_Initure.Pull=GPIO_PULLUP;//上拉
        GPIO_Initure.Speed=GPIO_SPEED_HIGH;//高速
        HAL_GPIO_Init(LED_PORT,&GPIO_Initure);
            
        LED_OFF();
    }

④ exit.c 对按键S1的IO接口进行初始化,设置中断抢占优先级,使用中断。

void EXTI4_Init(void)
{
    GPIO_InitTypeDef GPIO_Initure;
        
    __HAL_RCC_GPIOA_CLK_ENABLE();//开启GPIOA时钟

    GPIO_Initure.Pin  = S2_PIN;//按键S1
    GPIO_Initure.Mode = GPIO_MODE_IT_FALLING;//下降沿触发,按键无按下时,IO口为高电平。
    GPIO_Initure.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(S2_PORT,&GPIO_Initure);

    HAL_NVIC_SetPriority(EXTI4_IRQn,1,1);//抢占优先级为1,子优先级为1
    HAL_NVIC_EnableIRQ(EXTI4_IRQn);//使能中断线	
}

常见问题

  1. 弹出警告窗口,不能下载程序。

    • 请确认STLink驱动、STM32F103C8的DFP包是否安装。
    • STLink仿真器是否正常接入。
  2. 下载代码后程序没观察到实验现象。

    • 请重新上电,或者按下底座上的复位按键。
    • 模块没有安装稳妥。
  3. 按键按下后无反应。

    • 手指的发力点要在按键表面上。

实验思考

  1. 利用外部中断的方式实现按键S1按下后,改变LED1的状态。

  2. 实现按键S2按下两次LED2亮,按S2一次LED2灭。