1. 前言

STM32中断:

参考资料:《STM32F10X-中文参考手册》中断和事件章节
《野火STM32手册》

2. NVIC

NVIC是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。

2.1 NVIC寄存器简介

NVIC寄存器包括以下几种功能:

  1. 中断优先级:NVIC允许针对每个可能的中断源设置优先级,通过设置优先级来确定中断的响应顺序。通常,较低的数值表示更高的优先级。
  2. 中断使能:可以通过NVIC寄存器来使能或禁用特定的中断源,以控制中断请求的触发。
  3. 中断向量表偏移寄存器:用于指定中断服务程序(ISR)的地址,当特定中断触发时,处理器会跳转到相应的中断服务程序开始执行。
typedef struct {
    __IO uint32_t ISER[8];       // 中断使能寄存器
    uint32_t RESERVED0[24];
    __IO uint32_t ICER[8];       // 中断清除寄存器
    uint32_t RSERVED1[24];
    __IO uint32_t ISPR[8];       // 中断使能悬起寄存器
    uint32_t RESERVED2[24];
    __IO uint32_t ICPR[8];       // 中断清除悬起寄存器
    uint32_t RESERVED3[24];
    __IO uint32_t IABR[8];       // 中断有效位寄存器
    uint32_t RESERVED4[56];
    __IO uint8_t  IP[240];       // 中断优先级寄存器(8Bit wide)
    uint32_t RESERVED5[644];
    __O  uint32_t STIR;          // 软件触发中断寄存器
}  NVIC_Type;

一般只用ISER、ICER和IP这三个寄存器,ISER用来使能中断,ICER用来失能中断,IP用来设置中断优先级

2.2 中断优先级的定义

数值越小,优先级越高, 在F103系列中,只用到了0-4,但是原则上有0-255,

STM32中断看这一篇就够了-LMLPHP
用于表达优先级的这4bit,又被分组成抢占优先级和子优先级。

2.3 优先级分组

STM32中断看这一篇就够了-LMLPHP

2.3 中断编程

三个步骤:

  1. 使能外部中断
  2. 初始化NVIC_InitTypeDef结构体
typedef struct {
    uint8_t NVIC_IRQChannel;                    // 中断源
    uint8_t NVIC_IRQChannelPreemptionPriority;  // 抢占优先级
    uint8_t NVIC_IRQChannelSubPriority;         // 子优先级
    FunctionalState NVIC_IRQChannelCmd;         // 中断使能或者失能
} NVIC_InitTypeDef;
  1. 编写中断服务函数,一般写在stm32f10x_it.c这个库中

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

外部中断/事件控制器由20个产生事件/中断请求的边沿检测器组成,每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以独立地被屏蔽。挂起寄存器保持着状态线的中断请求

3.1 EXTI功能框图

STM32中断看这一篇就够了-LMLPHP
斜杠20,表示在控制器内部类似的信号线路有20个, EXTI总共有20个中断/事件线
STM32中断看这一篇就够了-LMLPHP
红色代表产生中断,绿色代表产生事件

3.2 中断事件线

EXTI有20个中断/事件线,每个GPIO都可以被设置为输入线,占用EXTI0至EXTI15, 还有另外七根用于特定的外设事件,见表 EXTI中断_事件线 。

4根特定外设中断/事件线由外设触发,具体用法参考《STM32F10X-中文参考手册》中对外设的具体说明。
STM32中断看这一篇就够了-LMLPHP

EXTI0至EXTI15用于GPIO,通过编程控制可以实现任意一个GPIO作为EXTI的输入源。由表 EXTI中断_事件线 可知, EXTI0可以通过AFIO的外部中断配置寄存器1(AFIO_EXTICR1)的EXTI0[3:0]位选择配置为PA0、 PB0、PC0、PD0、PE0、PF0、PG0、PH0或者PI0

3.3 EXTI初始化结构体

typedef struct {
    uint32_t EXTI_Line;                 // 中断/事件线
    EXTIMode_TypeDef EXTI_Mode;         // EXTI模式
    EXTITrigger_TypeDef EXTI_Trigger;   // 触发类型
    FunctionalState EXTI_LineCmd;       // EXTI使能
} EXTI_InitTypeDef;

3.4 按键中断

  1. 初始化用来产生中断的GPIO;
  2. 初始化EXTI;
  3. 配置NVIC;
  4. 编写中断服务函数;

宏定义:

//引脚定义
#define KEY1_INT_GPIO_PORT         GPIOA
#define KEY1_INT_GPIO_CLK          (RCC_APB2Periph_GPIOA\
                                    |RCC_APB2Periph_AFIO)
#define KEY1_INT_GPIO_PIN          GPIO_Pin_0
#define KEY1_INT_EXTI_PORTSOURCE   GPIO_PortSourceGPIOA
#define KEY1_INT_EXTI_PINSOURCE    GPIO_PinSource0
#define KEY1_INT_EXTI_LINE         EXTI_Line0
#define KEY1_INT_EXTI_IRQ          EXTI0_IRQn

#define KEY1_IRQHandler            EXTI0_IRQHandler


#define KEY2_INT_GPIO_PORT         GPIOC
#define KEY2_INT_GPIO_CLK          (RCC_APB2Periph_GPIOC\
                                    |RCC_APB2Periph_AFIO)
#define KEY2_INT_GPIO_PIN          GPIO_Pin_13
#define KEY2_INT_EXTI_PORTSOURCE   GPIO_PortSourceGPIOC
#define KEY2_INT_EXTI_PINSOURCE    GPIO_PinSource13
#define KEY2_INT_EXTI_LINE         EXTI_Line13
#define KEY2_INT_EXTI_IRQ          EXTI15_10_IRQn

NVIC配置

static void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    /* 配置NVIC为优先级组1 */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

    /* 配置中断源:按键1 */
    NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;
    /* 配置抢占优先级:1 */
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    /* 配置子优先级:1 */
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    /* 使能中断通道 */
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* 配置中断源:按键2,其他使用上面相关配置 */
    NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;
    NVIC_Init(&NVIC_InitStructure);
}

EXIT中断配置

void EXTI_Key_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;

    /*开启按键GPIO口的时钟*/
    RCC_APB2PeriphClockCmd(KEY1_INT_GPIO_CLK,ENABLE);

    /* 配置 NVIC 中断*/
    NVIC_Configuration();

    /*--------------------------KEY1配置---------------------*/
    /* 选择按键用到的GPIO */
    GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;
    /* 配置为浮空输入 */
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);

    /* 选择EXTI的信号源 */
    GPIO_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE, \
                        KEY1_INT_EXTI_PINSOURCE);
    EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;

    /* EXTI为中断模式 */
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    /* 上升沿中断 */
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    /* 使能中断 */
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    /*--------------------------KEY2配置------------------*/
    /* 选择按键用到的GPIO */
    GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;
    /* 配置为浮空输入 */
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);

    /* 选择EXTI的信号源 */
    GPIO_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE, \
                        KEY2_INT_EXTI_PINSOURCE);
    EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;

    /* EXTI为中断模式 */
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    /* 下降沿中断 */
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    /* 使能中断 */
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
}

中断服务函数

void KEY1_IRQHandler(void)
{
    //确保是否产生了EXTI Line中断
    if (EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) {
        // LED1 取反
        LED1_TOGGLE;
        //清除中断标志位
        EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);
    }
}

void KEY2_IRQHandler(void)
{
    //确保是否产生了EXTI Line中断
    if (EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET) {
        // LED2 取反
        LED2_TOGGLE;
        //清除中断标志位
        EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);
    }
}

最后

如果本文对你有所帮助,还请三连支持一下博主!
STM32中断看这一篇就够了-LMLPHP

11-17 04:49