STM32——中断

发布于:2025-09-02 ⋅ 阅读:(15) ⋅ 点赞:(0)

总:STM32——学习总纲

一、什么是中断

1.1 作用与意义

1.2 STM32 GPIO 外部中断简图

二、NVIC

2.1 NVIC 基本概念

Nested vectored interrupt controller,嵌套向量中断控制器,属于内核(M3、M4、M7)

用不到很多的优先级,允许厂商裁剪 

内核中断、外部中断,都有其对应的中断服务函数,是中断的入口。被定义在中断向量表中。

 

2.1.1 中断向量表

STM32 是 32位单片机每一次取地址32位,也就是4byte。

main函数优先级最低。

STM32F1参考手册

2.2 NVIC 相关寄存器 

2.3 NVIC 工作原理

  

2.4 STM32 中断优先级基本概念

 

2.5 STM32 中断优先级分组

IPR寄存器只是用高四位 [7:4]分配优先级。x位表示 2^x 个优先级。

举例:

2.6 STM32 NVIC使用

步骤 操作寄存器 HAL库
1 设置中断分组 AIRCR[10:8] HAL_NVIC_SetPriorityGrouping
2 设置中断优先级 IPRx [7:4] HAL_NVIC_SetPriority
3 使能中断 ISERx HAL_NVIC_EnableIRQ

除了这三个HAL库函数,还有其他不常用的NVIC函数。

一般在HAL_Init() 函数中设置分组2:NVIC_PRIORITYGROUP_2。

/**
  * @brief  This function is used to initialize the HAL Library; it must be the first
  *         instruction to be executed in the main program (before to call any other
  *         HAL function), it performs the following:
  *           Configure the Flash prefetch.
  *           Configures the SysTick to generate an interrupt each 1 millisecond,
  *           which is clocked by the HSI (at this stage, the clock is not yet
  *           configured and thus the system is running from the internal HSI at 16 MHz).
  *           Set NVIC Group Priority to 4.
  *           Calls the HAL_MspInit() callback function defined in user file
  *           "stm32f1xx_hal_msp.c" to do the global low level hardware initialization
  *
  * @note   SysTick is used as time base for the HAL_Delay() function, the application
  *         need to ensure that the SysTick time base is always set to 1 millisecond
  *         to have correct HAL operation.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_Init(void)
{
  /* Configure Flash prefetch */
#if (PREFETCH_ENABLE != 0)
#if defined(STM32F101x6) || defined(STM32F101xB) || defined(STM32F101xE) || defined(STM32F101xG) || \
    defined(STM32F102x6) || defined(STM32F102xB) || \
    defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG) || \
    defined(STM32F105xC) || defined(STM32F107xC)

  /* Prefetch buffer is not available on value line devices */
  __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif
#endif /* PREFETCH_ENABLE */

  /* Set Interrupt Group Priority */
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);

  /* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */
  HAL_InitTick(TICK_INT_PRIORITY);

  /* Init the low level hardware */
  HAL_MspInit();

  /* Return function status */
  return HAL_OK;
}
/**
  * @brief  Sets the priority grouping field (preemption priority and subpriority)
  *         using the required unlock sequence.
  * @param  PriorityGroup: The priority grouping bits length. 
  *         This parameter can be one of the following values:
  *         @arg NVIC_PRIORITYGROUP_0: 0 bits for preemption priority
  *                                    4 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_1: 1 bits for preemption priority
  *                                    3 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_2: 2 bits for preemption priority
  *                                    2 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_3: 3 bits for preemption priority
  *                                    1 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_4: 4 bits for preemption priority
  *                                    0 bits for subpriority
  * @note   When the NVIC_PriorityGroup_0 is selected, IRQ preemption is no more possible. 
  *         The pending IRQ priority will be managed only by the subpriority. 
  * @retval None
  */
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
/**
  * @brief  Sets the priority of an interrupt.
  * @param  IRQn: External interrupt number.
  *         This parameter can be an enumerator of IRQn_Type enumeration
  *         (For the complete STM32 Devices IRQ Channels list, please refer to the appropriate CMSIS device file (stm32f10xx.h))
  * @param  PreemptPriority: The preemption priority for the IRQn channel.
  *         This parameter can be a value between 0 and 15
  *         A lower priority value indicates a higher priority 
  * @param  SubPriority: the subpriority level for the IRQ channel.
  *         This parameter can be a value between 0 and 15
  *         A lower priority value indicates a higher priority.          
  * @retval None
  */
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)

IRQn_Type:是中断号枚举类型,对于中断向量表中断位置(STM32f1参考手册)。

PreemptPriority:抢占优先级

SubPriority:响应优先级

/**
  * @brief  Enables a device specific interrupt in the NVIC interrupt controller.
  * @note   To configure interrupts priority correctly, the NVIC_PriorityGroupConfig()
  *         function should be called before. 
  * @param  IRQn External interrupt number.
  *         This parameter can be an enumerator of IRQn_Type enumeration
  *         (For the complete STM32 Devices IRQ Channels list, please refer to the appropriate CMSIS device file (stm32f10xxx.h))
  * @retval None
  */
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)

三、EXTI

3.1 EXTI 基本概念


3.1.1 简介

External interrupt/event Controller,外部中断事件控制器

在H7中,称为 Extended interrupt/event Controller,扩展中断事件控制器。

包含20个产生 事件/中断请求 的边沿检测器,即总共:20条 EXTI 线(F1)

 3.1.2 中断和事件的理解:

中断:要进入NVIC,有相应的中断服务函数,需要CPU处理。

事件:不进入NVIC,仅用于内部硬件自动控制,如 :TIM、DMA、ADC。

中断是计算机或单片机在执行程序时,因突发事件(如硬件信号、紧急任务等)暂停当前操作,转去处理新事件,完成后自动返回原程序继续执行的过程。

事件是 STM32 中实现硬件级快速响应的机制,适用于无需 CPU 干预的自动控制场景。

3.1.3 EXTI 支持的外部中断/事件请求

互联性的F1系列才包含以太网,所以非互联性STM32只有19条 EXTI 线(F1),不包含EXTI线19,以太网唤醒事件。

EXTI线16~...是内部中断/事件

学习中断的主要任务是学习 EXTI线 0~15。

3.2 EXTI 主要特性

 挂起状态位是针对 中断 来说的,有一个寄存器进行选择。

  

 3.3 EXTI 工作原理(F1)

主要使用 四个寄存器,一般不使用 软件触发

STM31F1xxx参考手册

 EXTI_RTSR 上升沿触发选择寄存器

(Rise Trigger Select Register)

EXTI_FTSR 下降沿触发选择寄存器

(Fall Trigger Select Register)

双边沿触发设置

双边沿则控制对应位的Fall&Rise寄存器位都置1,允许EXTI输入线x双边沿触发。

EXTI_IMR    中断屏蔽寄存器

(Interrupt Mask Register)

EXTI_PR      请求挂起寄存器

(Pending Request Register)

一般使用写入 ‘1’的方式清除这个寄存器,后边一个办法不常用。

3.4 F4/F7参考F1,H7 EXTI工作原理见B站视频

四、EXTI 与 IO 的映射关系

4.1 AFIO 简介(F1)

内容 寄存器 说明
1 调试IO配置 AFIO_MAPR[26:24] 配置JTAG/SWD的开关状态。可不关注
2 重映射配置 AFIO_MAPR 部分外设IO重映射配置,F1中并不是所有的IO都有重映射功能。可不关注
3 外部中断配置* AFIO_EXTICR 1~4 配置EXTI中断线 0~15 对应到具体那个IO。重点

特别注意:

配置AFIO寄存器之前要使能AFIO时钟,方法如下:

__HAL_RCC_AFIO_CLK_ENABLE();  对应RCC_APB2ENR寄存器位0。参考手册8.4.3、8.4.6

4.2 SYSCFG 简介(F4/F7/H7 使用,见B站)

【【正点原子】手把手教你学STM32 HAL库开发全集【真人出镜】STM32入门教学视频教程 单片机 嵌入式】第60讲

4.3 EXTI 与 IO 对应关系*

4.3.1 理论对应关系

右列 0 称为引脚号,中间A~K称为 IO分组号。

EXTI0 表示对应 引脚号为0 的 IO。

也就是说,EXTI0 如果被控制对应 PA0,那么别的 Px0 的IO就不能与EXTI0 对应了

EXTIx 输入线只能一对一对应 IO。

参考手册 9.2.5

4.3.2 AFIO寄存器配置关系(F1)

一个AFIO外部中断配置寄存器可控制4条 EXTI线。

这样的寄存器有四个,,总共可配置 16条 EXTI线对应 16个 IO。

五、如何使用中断*

5.1 中断一图流

在本节中,主要学习GPIO外部中断,外设中断由外设本身配置,不由STM32寄存器配置。

5.2 STM32 EXTI的配置步骤(GPIO外部中断)

步骤 作用 HAL库
1 使能GPIO时钟 使能GPIO时钟
2 设置GPIO输入模式 上、下拉/浮空输入 HAL_GPIO_Init
3 使能 AFIO(F1)/ SYSCFG(F4/F7/H7) 设置AFIO/SYSCFG 时钟开启寄存器
4 设置EXTI和IO对应关系 选择 PAx~PKx 一对一对应EXTI的x线,寄存器:AFIO_EXTICR/SYSCFG_EXTICR
5 设置EXTI屏蔽,上、下沿 设置EXTI对应通道的屏蔽和上升沿、下降沿触发,寄存器:EXTI_IMR、EXTI_RTSR、EXTI_FTSR
6 设置NVIC 见 2.6 STM32 NVIC使用
7 设计中断服务函数 编写对应中断的中断服务函数!需要清除中断标志!

5.3 SMT32 EXTI 的HAL库设置步骤(GPIO外部中断)

步骤 HAL库
1  使能GPIO时钟 __HAL_RCC_GPIOx_CLK_ENABLE
2 GPIO/AFIO(SYSCFG)/EXTI HAL_GPIO_Init 一步到位配置
3 设置中断分组

HAL_NVIC_SetPriorityGrouping 此函数只需要配置一次

4 设置中断优先级 HAL_NVIC_SetPriority
5 使能中断 HAL_NVIC_EnableIRQ
6 设计中断服务函数

编写 EXTIx_IRQHandler,清中断标志

STM32仅有7个外部中断服务函数,在.s文件中断向量表定义

EXTI 0~4:5条EXTI线单独的中断服务函数

EXTI 9_5:5~8EXTI线共用一个中断服务函数

EXTI 15_10: 10~15EXTI线共用一个中断服务函数

六、通用外设驱动模型

在此模型中,GPIO外部中断只使用了1,4两个步骤。

七、HAL库中断回调处理机制介绍

八、编程实战

通过外部中断控制一个灯亮灭

IO模式设置选择

PA0 按下VCC3.3V 高电平,上升沿触发,不按下需要一个下拉电阻,保持低电平,才有上升沿。

PE2.3.4 按下GND 低电平,下降沿触发,不按下需要一个上拉电阻,保持高电平,才有下降沿。

工程:【免费】stm32F1nvicexperiment资源-CSDN下载

控制不同的优先级

exti.c


#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/BEEP/beep.h"
#include "./BSP/KEY/key.h"
#include "./BSP/EXTI/exti.h"


/**
 * @brief       KEY0 外部中断服务程序
 * @param       无
 * @retval      无
 */
void KEY0_INT_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(KEY0_INT_GPIO_PIN);         /* 调用中断处理公用函数 清除KEY0所在中断线 的中断标志位 */
    __HAL_GPIO_EXTI_CLEAR_IT(KEY0_INT_GPIO_PIN);         /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}

/**
 * @brief       KEY1 外部中断服务程序
 * @param       无
 * @retval      无
 */
void KEY1_INT_IRQHandler(void)
{ 
    HAL_GPIO_EXTI_IRQHandler(KEY1_INT_GPIO_PIN);         /* 调用中断处理公用函数 清除KEY1所在中断线 的中断标志位,中断下半部在HAL_GPIO_EXTI_Callback执行 */
    __HAL_GPIO_EXTI_CLEAR_IT(KEY1_INT_GPIO_PIN);         /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}


/**
 * @brief       WK_UP 外部中断服务程序
 * @param       无
 * @retval      无
 */
void WKUP_INT_IRQHandler(void)
{ 
    HAL_GPIO_EXTI_IRQHandler(WKUP_INT_GPIO_PIN);        /* 调用中断处理公用函数 清除KEY_UP所在中断线 的中断标志位,中断下半部在HAL_GPIO_EXTI_Callback执行 */
    __HAL_GPIO_EXTI_CLEAR_IT(WKUP_INT_GPIO_PIN);        /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}

/**
 * @brief       中断服务程序中需要做的事情
                在HAL库中所有的外部中断服务函数都会调用此函数
 * @param       GPIO_Pin:中断引脚号
 * @retval      无
 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    delay_ms(20);      /* 消抖 */
    switch(GPIO_Pin)
    {
        case KEY0_INT_GPIO_PIN:
            if (KEY0 == 0)
            {
                LED0_TOGGLE();  /* LED0 状态取反 */ 
                LED1_TOGGLE();  /* LED1 状态取反 */ 
            }
            break;
        case KEY1_INT_GPIO_PIN:
            if (KEY1 == 0)
            {
                LED0_TOGGLE();  /* LED0 状态取反 */ 
            }
            break;
        case WKUP_INT_GPIO_PIN:
            if (WK_UP == 1)
            {
                BEEP_TOGGLE();  /* 蜂鸣器状态取反 */ 
            }
            break;
    }
}

/**
 * @brief       外部中断初始化程序
 * @param       无
 * @retval      无
 */
void extix_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;

    KEY0_GPIO_CLK_ENABLE();                                  /* KEY0时钟使能 */
    KEY1_GPIO_CLK_ENABLE();                                  /* KEY1时钟使能 */
    WKUP_GPIO_CLK_ENABLE();                                  /* WKUP时钟使能 */

    gpio_init_struct.Pin  = KEY0_INT_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_IT_FALLING;            /* 下升沿触发 */
    gpio_init_struct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(KEY0_INT_GPIO_PORT, &gpio_init_struct);    /* KEY0配置为下降沿触发中断 */

    gpio_init_struct.Pin  = KEY1_INT_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_IT_FALLING;            /* 下升沿触发 */
    gpio_init_struct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(KEY1_INT_GPIO_PORT, &gpio_init_struct);    /* KEY1配置为下降沿触发中断 */
    
    gpio_init_struct.Pin  = WKUP_INT_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_IT_RISING;             /* 上升沿触发 */
    gpio_init_struct.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(WKUP_GPIO_PORT, &gpio_init_struct);        /* WKUP配置为下降沿触发中断 */

    HAL_NVIC_SetPriority(KEY0_INT_IRQn, 0, 2);               /* 抢占0,子优先级2 */
    HAL_NVIC_EnableIRQ(KEY0_INT_IRQn);                       /* 使能中断线4 */

    HAL_NVIC_SetPriority(KEY1_INT_IRQn, 1, 2);               /* 抢占1,子优先级2 */
    HAL_NVIC_EnableIRQ(KEY1_INT_IRQn);                       /* 使能中断线3 */

    HAL_NVIC_SetPriority(WKUP_INT_IRQn, 2, 2);               /* 抢占2,子优先级2 */
    HAL_NVIC_EnableIRQ(WKUP_INT_IRQn);                       /* 使能中断线0 */
}

exti.h


#ifndef __EXTI_H
#define __EXTI_H

#include "./SYSTEM/sys/sys.h"

/******************************************************************************************/
/* 引脚 和 中断编号 & 中断服务函数 定义 */ 

#define KEY0_INT_GPIO_PORT              GPIOE
#define KEY0_INT_GPIO_PIN               GPIO_PIN_4
#define KEY0_INT_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)   /* PE口时钟使能 */
#define KEY0_INT_IRQn                   EXTI4_IRQn
#define KEY0_INT_IRQHandler             EXTI4_IRQHandler

#define KEY1_INT_GPIO_PORT              GPIOE
#define KEY1_INT_GPIO_PIN               GPIO_PIN_3
#define KEY1_INT_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)   /* PE口时钟使能 */
#define KEY1_INT_IRQn                   EXTI3_IRQn
#define KEY1_INT_IRQHandler             EXTI3_IRQHandler

#define WKUP_INT_GPIO_PORT              GPIOA
#define WKUP_INT_GPIO_PIN               GPIO_PIN_0
#define WKUP_INT_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   /* PA口时钟使能 */
#define WKUP_INT_IRQn                   EXTI0_IRQn
#define WKUP_INT_IRQHandler             EXTI0_IRQHandler

/******************************************************************************************/


void extix_init(void);  /* 外部中断初始化 */

#endif

main.c


#include "./stm32f1xx_it.h"
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/BEEP/beep.h"
#include "./BSP/EXTI/exti.h"


int main(void)
{
    HAL_Init();                            /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);    /* 设置时钟, 72Mhz */
    delay_init(72);                        /* 延时初始化 */
    usart_init(115200);                    /* 串口初始化为115200 */
    led_init();                            /* 初始化LED */
    beep_init();                           /* 初始化蜂鸣器 */
    extix_init();                          /* 初始化外部中断输入 */
    LED0(0);                               /* 先点亮红灯 */

    while (1)
    {
        printf("OK\r\n");
        delay_ms(1000);
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到