中断详解

一. NVIC要点分析

1. 为什么需要中断

  • 程序中有很多不确定何时会发生的事情需要处理, 假如没有中断cpu就需要一直去等待, 这样会极大的消耗Cup的资源. 如图举例中断的优点以及应用场景

2. NVIC分组和响应优先级

  • 由于中断的优点, 在STM32中预留了256个中断事件位, 不过在F103中只使用了70个. 其中10个是系统内部异常, 剩余60个是外部中断.这么多中断假如同时发送CUP该如何处理, 这时就引进了NVIC的概念, 用于控制所用中断是否响应以及响应优先级.
  • 优先级分组将响应优先级分为抢占优先级和响应优先级, F103只使用了高4位用于优先级分组. 根据排列组合, 二者可以列出五种组合方式. 0000, 0001, 0011, 0111, 1111.
  • 抢占优先级顾名思义拥有CPU抢夺权, 当CUP在执行A(抢占优先级2)中断事件时, B(抢占优先级1)中断来临CPU会优先执行B中断. 而响应优先级没有抢夺CPU的权利, 只有等待被优先选择的权利. 假如A, B的抢占优先级都为1, A的响应优先级为2, B为1. 当AB中断同时来临时会优先处理B. 但是如果A已经在执行, B不能将其打断. 如果AB抢占和响应优先级都一样, 当AB中断同时来临将比较二者的自然优先级, 参见中断向量表的先后顺序.

3. NVIC结构体分析

  • 参考手册对NVIC描述很少, 有关寄存器详细需要参考M3权威指南. 对于NVIC我们只需要 设置分组, 配置好结构体并将其初始化即可.

*值得注意的是: 对于分组的设置整个程序只需要设置一次即可. 因此可以将这条函数放在main中.

/*
typedef struct {
    uint8_t NVIC_IRQChannel;                    // 中断源
    uint8_t NVIC_IRQChannelPreemptionPriority;  // 抢占优先级
    uint8_t NVIC_IRQChannelSubPriority;         // 子优先级
    FunctionalState NVIC_IRQChannelCmd;         // 中断使能或者失能
} NVIC_InitTypeDef;
*/

//初始化示例
    NVIC_InitTypeDef NVIC_InitStructures;
    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //最好放在main中配置.
    
    NVIC_InitStructures.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStructures.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructures.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructures.NVIC_IRQChannelCmd = ENABLE;
    
    NVIC_Init(&NVIC_InitStructures);

二. 外部中断/事件控制器

1. EXTI简介以及功能框图

  • EXTI可以由软件触发(设置触发条件), 或者由外部触发(外部设备为引脚提供信号). 每个EXTI支持20个中断/事件请求(每次只支持一个中断/事件). 分别为(EXTI0-EXTI19)

  • 无论是由软件触发还是硬件触发, 经过或门后会使得请求挂起寄存器置1. 此时中断屏蔽寄存器和事件屏蔽寄存器控制着, 是否给到响应给NVIC或者发生脉冲.

2. EXTI结构体详解

  • EXTI结构体主要是配置图中关键位. 通道选择, 上升/下降沿触发, 中断/事件模式选着, 使能. 值得注意的是这里用到的是GPIO的复用功能, 需要使能AFIO.
/*
typedef struct {
    uint32_t EXTI_Line;                 // 中断/事件线
    EXTIMode_TypeDef EXTI_Mode;         // EXTI模式
    EXTITrigger_TypeDef EXTI_Trigger;   // 触发类型
    FunctionalState EXTI_LineCmd;       // EXTI使能
} EXTI_InitTypeDef;
*/

void Key_EXIT_Config(void)
{
    EXTI_InitTypeDef EXTI_InitStructures;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //!!!注意这里不是使能EXTI, EXTI只是一个功能不算外设

    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
    EXTI_InitStructures.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
    EXTI_InitStructures.EXTI_Line = EXTI_Line0;
    EXTI_InitStructures.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructures.EXTI_LineCmd = ENABLE;
    
    EXTI_Init(&EXTI_InitStructures);
    
}

三. 外部中断控制实验

1. 按键控制外部中断

  • 这个实验通过按键产生外部中断信号来控制灯的亮灭
  • 值得注意的两个点是: 由于EXTI需要用到GPIO所以需要先使能并初始化GPIO.(使用任何外设都要先使能时钟再初始化) 中断服务函数尽量写在stm32f10x_it.c中.
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_key.h"
#include "bsp_exit.h"

static void Key_NVIC_Config(void);
void Key_EXIT_Config(void);

int main()
{
    Led_GPIO_Config();
    LED_OFF;
    Key_EXIT_Config();
    
    while(1){}
    
}

static void Key_NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructures;
    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    NVIC_InitStructures.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStructures.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructures.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructures.NVIC_IRQChannelCmd = ENABLE;
    
    NVIC_Init(&NVIC_InitStructures);

}

void Key_EXIT_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructures;
    EXTI_InitTypeDef EXTI_InitStructures;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    Key_NVIC_Config();
    
    GPIO_InitStructures.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructures.GPIO_Mode = GPIO_Mode_IPU;
    
    GPIO_Init(GPIOB, &GPIO_InitStructures);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
    EXTI_InitStructures.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
    EXTI_InitStructures.EXTI_Line = EXTI_Line0;
    EXTI_InitStructures.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructures.EXTI_LineCmd = ENABLE;
    
    EXTI_Init(&EXTI_InitStructures);
    
}

//为了统一管理, 中断服务函数一般写在stmf10x_it.c中
void EXTI0_IRQHandler (void)
{

    if(EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        LED_TOGGLE;
        
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

posted @ 2023-09-20 00:16  烙铁666  阅读(247)  评论(0)    收藏  举报