STM32-中断

一、中断的基本概念

  1. 中断(Interrupt)

    • 中断是CPU在执行当前程序时,被外部或内部事件打断的过程。
    • 中断发生后,CPU会暂停当前任务,转而执行中断服务例程(ISR),处理中断事件。
  2. 中断源(Interrupt Source)

    • 中断源是触发中断的事件来源,可以是外部信号(如GPIO引脚状态变化)、内部事件(如定时器溢出)或系统事件(如错误)。
  3. 中断向量(Interrupt Vector)

    • 中断向量是中断服务例程的入口地址。
    • STM32的中断向量表存储在Flash的起始地址(通常是0x08000000),包含所有中断的入口地址。
  4. 中断地址

    • 中断地址是中断向量表中存储的地址,指向具体的中断服务例程。
    • 例如,外部中断0的中断地址是中断向量表中对应EXTI0的地址。

二、中断优先级

  1. 中断优先级的作用

    • 当多个中断同时发生时,CPU需要根据优先级决定先处理哪个中断。
    • STM32的中断优先级由NVIC(Nested Vectored Interrupt Controller)管理。
  2. 优先级分组

    • STM32支持中断优先级分组,将优先级分为抢占优先级和子优先级。
    • 抢占优先级高者可以打断优先级低的中断。
    • 子优先级用于同一抢占优先级下的中断排序。
  3. 优先级配置

    • NVIC的优先级寄存器(IPR)用于设置中断的优先级。
    • 优先级数值越小,优先级越高。

三、外部中断模块(EXTI)的使用场景

  1. 按键输入

    • 当按键按下或释放时,GPIO引脚状态变化触发外部中断,进入中断服务例程处理按键事件。
  2. 传感器信号检测

    • 例如,使用外部中断检测传感器的脉冲信号或状态变化。
  3. 通信信号同步

    • 在串行通信中,外部中断可用于同步接收信号的起始位或停止位。
  4. 紧急事件处理

    • 当检测到异常信号(如过流、过压)时,通过外部中断快速响应。

三、模块知识点

一、NVIC中断优先级分组概述

NVIC是STM32的中断控制器,用于管理中断的优先级和响应顺序。STM32支持中断优先级分组,允许开发者根据实际需求分配抢占优先级和响应优先级的位数。这种分组机制可以优化中断的响应效率和系统的实时性能。

1、中断优先级分组的配置
  1. 中断优先级分组的作用

    • 资源分配:通过分组,可以为不同的中断源分配不同的优先级,从而区分哪些中断更为重要。
    • 响应时间管理:合理的分组可以优化中断的响应时间,提高系统的实时性能。
    • 系统稳定性:避免优先级倒置的问题,保证系统运行的稳定性。
  2. 优先级分组的配置方式

    • STM32的中断优先级分组通过NVIC的应用中断与复位控制寄存器(AIRCR)的高4位进行配置。

    • 根据配置的不同,优先级分组可以分为以下几种:

      分组AIRCR[10:8]抢占优先级位数响应优先级位数抢占优先级级别响应优先级级别
      01110位4位1级16级
      11101位3位2级8级
      21012位2位4级4级
      31003位1位8级2级
      40114位0位16级1级
    • 说明

      • 抢占优先级越高(数值越小),中断可以打断其他低优先级中断的执行。
      • 响应优先级在抢占优先级相同时起作用,决定中断的执行顺序。
  3. 配置函数

    • NVIC的优先级分组可以通过NVIC_PriorityGroupConfig()函数设置。例如:
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置为分组2
      
2、中断优先级的设置
  1. 中断优先级寄存器(IPR)

    • 每个中断源都有一个对应的IPR寄存器,用于设置中断的优先级。
    • 优先级数值越小,优先级越高。
  2. 设置函数

    • 使用HAL_NVIC_SetPriority()函数设置中断优先级。例如:
      HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 1); // 抢占优先级为2,响应优先级为1
      
  3. 使能中断

    • 使用HAL_NVIC_EnableIRQ()函数使能中断。例如:
      HAL_NVIC_EnableIRQ(EXTI0_IRQn); // 使能EXTI0中断
      
3、中断优先级的使用场景
  1. 实时性要求高的中断
    • 高抢占优先级的中断可以打断低优先级中断的执行,适合处理实时性要求高的任务。
  2. 多中断系统
    • 在多中断场景下,合理的优先级分配可以避免优先级倒置,确保系统稳定运行。
4、完整示例

以下是一个完整的代码示例,展示如何配置NVIC的优先级分组和中断优先级:

#include "stm32f10x.h"

void NVIC_Config(void)
{
    // 设置优先级分组为分组2:2位抢占优先级,2位响应优先级
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    // 配置EXTI0中断优先级:抢占优先级为1,响应优先级为1
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

int main(void)
{
    // 初始化NVIC配置
    NVIC_Config();

    while (1)
    {
        // 主循环
    }
}
总结
  1. NVIC的优先级分组:通过AIRCR寄存器的高4位配置,支持5种分组方式。
  2. 中断优先级设置:通过IPR寄存器设置抢占优先级和响应优先级。
  3. 配置函数:使用NVIC_PriorityGroupConfig()设置分组,使用HAL_NVIC_SetPriority()设置优先级。

二. EXTI功能概述

EXTI(外部中断/事件控制器)是STM32中用于检测GPIO引脚电平变化并触发中断或事件的模块。它支持以下功能:

  • 支持所有GPIO口:任意GPIO引脚都可以配置为外部中断。
  • 触发方式:支持上升沿、下降沿、双边沿或软件触发。
  • 通道数:支持16个GPIO引脚(EXTI0~EXTI15),外加PVD输出、RTC闹钟、USB唤醒等。
  • 响应方式:中断响应(执行中断服务函数)或事件响应(触发其他外设操作)。
1. 为什么相同的Pin不能同时触发中断?

在STM32中,EXTI模块通过AFIO(复用功能引脚选择器)将GPIO引脚连接到中断通道。由于EXTI只有16个通道(EXTI0~EXTI15),而STM32有多个GPIO端口(如GPIOA、GPIOB、GPIOC等),因此每个通道只能连接一个引脚。

例如:

  • PA0、PB0、PC0等引脚都对应EXTI0通道,但只能选择其中一个引脚连接到EXTI0。
  • 如果需要多个中断引脚,必须选择不同Pin号的引脚(如PA0和PB1),否则会产生冲突。

AFIO的作用是选择GPIO引脚并将其映射到EXTI通道,因此在配置中断时,需要通过AFIO指定具体引脚。

2. EXTI配置步骤
步骤1:使能时钟

使能GPIO和AFIO的时钟:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
步骤2:配置GPIO

将GPIO引脚配置为输入模式(如浮空输入、上拉输入):

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
步骤3:连接GPIO到EXTI

通过AFIO将GPIO引脚映射到EXTI通道:

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); // 将PA0映射到EXTI0
步骤4:配置EXTI

设置EXTI的触发方式(上升沿、下降沿等)并使能中断:

EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
步骤5:配置NVIC

设置中断优先级并使能中断:

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
步骤6:编写中断服务函数

在中断服务函数中处理中断事件并清除中断标志:

void EXTI0_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        // 处理中断事件
        EXTI_ClearITPendingBit(EXTI_Line0); // 清除中断标志
    }
}
4. 注意事项
  1. 相同Pin冲突:相同的Pin(如PA0和PB0)不能同时配置为EXTI中断。
  2. 中断优先级:合理配置中断优先级,避免优先级倒置。
  3. 中断标志清除:在中断服务函数中必须清除中断标志。
  4. 共享中断线:对于EXTI9_5和EXTI15_10等共享中断线,需要在中断服务函数中判断具体引脚。

四、中断的配置流程

中断配置涉及多个模块:RCC(时钟)、GPIO(通用输入输出)、AFIO(复用功能)、EXTI(外部中断控制器)和NVIC(中断控制器)。以下是详细步骤:

1. 时钟配置(RCC)
  • 使能外设时钟:通过RCC模块使能GPIO、AFIO和EXTI的时钟。
  • 示例代码:
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
    
2. GPIO配置
  • 配置GPIO引脚为中断模式
    • 设置GPIO引脚为输入模式。
    • 配置引脚的上拉/下拉电阻(根据需要)。
  • 示例代码:
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
3. AFIO配置
  • 映射中断线:通过AFIO模块将GPIO引脚映射到EXTI的中断线。
  • 示例代码:
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); // 将PA0映射到EXTI0
    
4. EXTI配置
  • 配置中断触发条件:设置EXTI的中断触发方式(上升沿、下降沿或双边沿)。
  • 示例代码:
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_InitStructure.EXTI_Line = EXTI_Line0; // 配置EXTI0
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
    
5. NVIC配置
  • 使能中断:通过NVIC使能对应的外部中断。
  • 设置中断优先级:根据需求配置中断优先级。
  • 示例代码:
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; // 配置EXTI0中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
6. 编写中断服务例程(ISR)
  • 在对应的中断服务例程中编写处理逻辑。
  • 示例代码:
    void EXTI0_IRQHandler(void)
    {
        if (EXTI_GetITStatus(EXTI_Line0) != RESET) // 检查EXTI0是否触发
        {
            // 处理中断事件
            EXTI_ClearITPendingBit(EXTI_Line0); // 清除中断标志
        }
    }
    

五、总结

  1. 中断的基本概念:中断源、中断向量、中断地址。
  2. 中断优先级:抢占优先级和子优先级,通过NVIC配置。
  3. 外部中断模块(EXTI)的使用场景:按键检测、传感器信号、通信同步等。
  4. 中断配置流程
    • 使能时钟(RCC)。
    • 配置GPIO为中断模式。
    • 映射GPIO到EXTI(通过AFIO)。
    • 配置EXTI的中断触发方式。
    • 通过NVIC使能中断并设置优先级。
    • 编写中断服务例程(ISR)。

案例:使用外部中断控制LED状态

硬件需求
  1. STM32开发板(如STM32F103C8T6)。
  2. 一个按键。
  3. 一个LED灯。
  4. 限流电阻(如330Ω)。
  5. 连接线。
电路连接
  1. 将按键的一端连接到STM32的一个GPIO引脚(如PA0)。
  2. 将按键的另一端连接到地(GND)。
  3. 将LED的正极连接到另一个GPIO引脚(如PB0)。
  4. 将LED的负极通过限流电阻连接到地(GND)。

软件目标

  1. 配置PA0为外部中断输入。
  2. 配置PB0为普通GPIO输出。
  3. 当按键按下时,通过外部中断触发ISR,切换LED的状态。

代码实现

1. 初始化GPIO和外部中断
#include "stm32f10x.h"

void GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    // 使能GPIOA和GPIOB的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);

    // 配置PA0为浮空输入(按键连接到PA0)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置PB0为推挽输出(LED连接到PB0)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // 确保LED初始状态为关闭
    GPIO_SetBits(GPIOB, GPIO_Pin_0);
}

void EXTI_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    // 使能AFIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

    // 配置PA0为中断线
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);

    // 配置EXTI0为下降沿触发
    EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    // 配置NVIC
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
2. 中断服务例程(ISR)
void EXTI0_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line0) != RESET) // 检查是否EXTI0触发
    {
        // 切换LED状态
        if (GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_0) == Bit_SET)
        {
            GPIO_ResetBits(GPIOB, GPIO_Pin_0); // 打开LED
        }
        else
        {
            GPIO_SetBits(GPIOB, GPIO_Pin_0); // 关闭LED
        }

        // 清除中断标志
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}
3. 主函数
int main(void)
{
    // 初始化GPIO和外部中断
    GPIO_Config();
    EXTI_Config();

    while (1)
    {
        // 主循环为空,所有操作在中断中完成
    }
}

代码说明

  1. GPIO配置

    • PA0配置为浮空输入,用于按键检测。
    • PB0配置为推挽输出,用于控制LED。
  2. 外部中断配置

    • 将PA0映射到EXTI0。
    • 配置EXTI0为下降沿触发(按键按下时触发)。
    • 通过NVIC使能EXTI0中断。
  3. 中断服务例程(ISR)

    • 检测是否为EXTI0触发。
    • 切换LED的状态(通过读取PB0的状态并取反)。
    • 清除中断标志,避免重复触发。

运行结果

  1. 当按键按下时,LED状态会切换(从亮变灭,或从灭变亮)。
  2. 主循环为空,所有操作都在中断服务例程中完成。

拓展

  1. 防抖处理:按键可能会产生抖动,可以通过软件延时或硬件滤波解决。
  2. 多按键检测:可以扩展到多个按键,通过配置多个外部中断线(如EXTI1、EXTI2等)。
  3. 组合功能:结合定时器中断,实现按键长按和短按的区分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值