• STM32-Project17-18:NVIC中断应用概述;EXTI外部中断控制实验;


    一.异常类型

    中断就是异常,32支持为数众多的系统异常和外部中断。
    其中系统异常有 8 个(如果把 Reset HardFault 也算上的话就是 10 个),外部中断有 60
    个,如下表所示部分的异常。

    EXTI外设的异常只是NVIC异常处理里面的一种。

    ...............

    二. NVIC简介

    NVIC 是嵌套向量中断控制 器,控制着整个芯片中断相关的功能,是内核里面的一个外设。
    ,其寄存器部分,具体 使用了多少可参考《 Cortex-M3 内核编程手册》 -4.3.11:NVIC 寄存器映射。
    1   
    代码 17-1 NVIC 寄存器 结构体定义,来自固件库头文件: core_cm3.h
    1 typedef struct {
    2 __IO uint32_t ISER[8]; // 中断使能寄存器
    3 uint32_t RESERVED0[24];
    4 __IO uint32_t ICER[8]; // 中断清除寄存器
    5 uint32_t RSERVED1[24];
    6 __IO uint32_t ISPR[8]; // 中断使能悬起寄存器
    7 uint32_t RESERVED2[24];
    8 __IO uint32_t ICPR[8]; // 中断清除悬起寄存器
    9 uint32_t RESERVED3[24];
    10 __IO uint32_t IABR[8]; // 中断有效位寄存器
    11 uint32_t RESERVED4[56];
    12 __IO uint8_t IP[240]; // 中断优先级寄存器 (8Bit wide)
    13 uint32_t RESERVED5[644];
    14 __O uint32_t STIR; // 软件触发中断寄存器
    15 } NVIC_Type;
    在配置中断的时候我们一般只用 ISER ICER IP 这三个寄存器, ISER 用来使能中
    断, ICER 用来失能中断, IP 用来设置中断优先级。
    NVIC 中断配置固件库 固件库文件 core_cm3.h 的最后,还提供了 NVIC 的一些函数
    void NVIC_EnableIRQ(IRQn_Type IRQn) 使能中断
    void NVIC_DisableIRQ(IRQn_Type IRQn) 失能中断
    void NVIC_SetPendingIRQ(IRQn_Type IRQn) 设置中断悬起位
    void NVIC_ClearPendingIRQ(IRQn_Type IRQn) 清除中断悬起位
    uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) 获取悬起中断编号
    void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) 设置中断优先级
    uint32_t NVIC_GetPriority(IRQn_Type IRQn) 获取中断优先级
    void NVIC_SystemReset(void) 系统复位
    具体函数操作哪一个NVIC寄存器,需要看函数体怎么实现的。
    三 中断优先级定义
         在 NVIC 有一个专门的寄存器:中断优先级寄存器 NVIC_IPRx ,用来配置外部中断的
    优先级, IPR 宽度为 8bit ,原则上每个外部中断可配置的优先级为 0~255 ,数值越小,优先
    级越高。

    这个分组的库函数是由内核外设SCB里面写的,不在NVIC库函数里面

    四 中断编程

    在配置每个中断的时候一般有 3 个编程要点:
    1 、使能对应外设某个中断,这个具体由每个外设的相关中断使能位控制。比如串口有发送
    完成中断,接收完成中断,这两个中断都由串口控制寄存器的相关中断使能位控制。相当于打开小门。
    2 、初始化 NVIC_InitTypeDef 结构体,配置中断优先级分组,设置抢占优先级和子优
    先级,使能中断请求。 NVIC_InitTypeDef 结构体在固件库头文件 misc.h 中定义。相当于打开大门。
    代码 17-3 NVIC 初始化结构体
    1 typedef struct {
    2 uint8_t NVIC_IRQChannel; // 中断源
    3 uint8_t NVIC_IRQChannelPreemptionPriority; // 抢占优先级
    4 uint8_t NVIC_IRQChannelSubPriority; // 子优先级
    5 FunctionalState NVIC_IRQChannelCmd; // 中断使能或者失能
    6 } NVIC_InitTypeDef;
    有关 NVIC 初始化结构体的成员我们一一解释下:
    1 NVIC_IROChannel :用来设置中断源,不同的中断中断源不一样,且不可写错,即
    使写错了程序也不会报错,只会导致不响应中断。具体的成员配置可参考 stm32f10x.h 头文
    件里面的 IRQn_Type 结构体定义,这个结构体包含了所有的中断源。
    2 NVIC_IRQChannelPreemptionPriority :抢占优先级,具体的值要根据优先级分组来
    确定,具体参考表格 17-5 优先级分组真值表 。
    3 NVIC_IRQChannelSubPriority :子优先级,具体的值要根据优先级分组来确定,具
    体参考表格 17-5 优先级分组真值表 。
    4 NVIC_IRQChannelCmd :中断使能( ENABLE )或者失能( DISABLE )。操作的
    NVIC_ISER NVIC_ICER 这两个寄存器。
    3 、编写中断服务函数
    在启动文件 startup_stm32f10x_hd.s 中我们预先为每个中断都写了一个中断服务函数,
    只是这些中断函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要我
    们重新编写,为了方便管理我们把中断服务函数统一写在 stm32f10x_it.c 这个库文件中。
    关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就
    在中断向量表中找不到中断服务函数的入口,直接跳转到启动文件里面预先写好的空函数,
    并且在里面无限循环,实现不了中断。
    五 .EXTI 简介
    EXTI External interrupt/event controller )—外部中断 / 事件控制器,管理了控制器的 20
    个中断 /事件线。
    每个中断 / 事件线都对应有一个边沿检测器,可以实现输入信号的上升沿
    检测和下降沿的检测。 EXTI 可以实现对每个中断 / 事件线进行单独配置,可以单独配置为
    中断或者事件,以及触发事件的属性。
    EXTI 的功能框图包含了 EXTI 最核心内容,掌握了功能框图,对 EXTI 就有一个整体
    的把握,在编程时思路就非常清晰。 可以看到很多在信号线上打一个斜杠并标注“ 20 ”字样,这个表示在控制器 内部类似的信号线路有 20 个,这与 EXTI 总共有 20 个中断 /事件线是吻合的。

    EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件
    上就有所不同。
    首先我们来看图 18-1 中红色虚线指示的电路流程。它是一个产生中断的线路,最终信
    号流入到 NVIC 控制器内。
    编号 1 是输入线, EXTI 控制器有 20   个中断 / 事件输入线,这些输入线可以通过寄存器
    设置为任意一个 GPIO ,也可以是一些外设的事件,这部分内容我们将在后面专门讲解。
    输入线一般是存在电平变化的信号。
    编号 2 是一个边沿检测电路,它会根据上升沿触发选择寄存器 (EXTI_RTSR) 和下降沿
    触发选择寄存器 (EXTI_FTSR) 对应位的设置来控制信号触发。边沿检测电路以输入线作为
    信号输入端,如果检测到有边沿跳变就输出有效信号 1 给编号 3 电路,否则输出无效信号
    0 。而 EXTI_RTSR EXTI_FTSR 两个寄存器可以控制器需要检测哪些类型的电平跳变过
    程,可以是只有上升沿触发、只有下降沿触发或者上升沿和下降沿都触发。
    编号 3 电路实际就是一个或门电路,它一个输入来自编号 2 电路,另外一个输入来自
    软件中断事件寄存器 (EXTI_SWIER) EXTI_SWIER 允许我们通过程序控制就可以启动中
    / 事件线,这在某些地方非常有用。我们知道或门的作用就是有 1 就为 1 ,所以这两个输
    入随便一个有有效信号 1 就可以输出 1 给编号 4 和编号 6 电路。
    编号 4 电路是一个与门电路,它一个输入是编号 3 电路,另外一个输入来自中断屏蔽
    寄存器 (EXTI_IMR) 。与门电路要求输入都为 1 才输出 1 ,导致的结果是如果 EXTI_IMR
    置为 0 时,那不管编号 3 电路的输出信号是 1 还是 0 ,最终编号 4 电路输出的信号都为 0
    如果 EXTI_IMR 设置为 1 时,最终编号 4 电路输出的信号才由编号 3 电路的输出信号决定,
    这样我们可以简单的控制 EXTI_IMR 来实现是否产生中断的目的。编号 3 电路输出的信号
    会被保存到挂起寄存器 (EXTI_PR) 内,如果确定编号 3   电路输出为 1 就会把 EXTI_PR 对应
    位置 1
    编号 5 是将 EXTI_PR 寄存器内容输出到 NVIC 内,从而实现系统中断事件控制。
    接下来我们来看看绿色虚线指示的电路流程。它是一个产生事件的线路,最终输出一
    个脉冲信号。
    产生事件线路是在编号 3 电路之后与中断线路有所不同,之前电路都是共用的。
    编号 6 电路是一个与门,它一个输入来自编号 3 电路,另外一个输入来自事件屏蔽寄存器
    (EXTI_EMR) 。如果 EXTI_EMR 设置为 0 时,那不管编号 3 电路的输出信号是 1 还是 0
    最终编号 6 电路输出的信号都为 0 ;如果 EXTI_EMR 设置为 1 时,最终编号 6 电路输出的
    信号才由编号 3 电路的输出信号决定,这样我们可以简单的控制 EXTI_EMR 来实现是否产
    生事件的目的。
    编号 7 是一个脉冲发生器电路,当它的输入端,即编号 6 电路的输出端,是一个有效
    信号 1 时就会产生一个脉冲;如果输入端是无效信号就不会输出脉冲。
    编号 8 是一个脉冲信号,就是产生事件的线路最终的产物,这个脉冲信号可以给其他
    外设电路使用,比如定时器 TIM 、模拟数字转换器 ADC 等等,这样的脉冲信号一般用来
    触发 TIM 或者 ADC 开始转换。
    产生中断线路目的是把输入信号输入到 NVIC ,进一步会运行中断服务函数,实现功
    能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且
    是电路级别的信号传输,属于硬件级的。
    另外, EXTI 是在 APB2 总线上的,在编程时候需要注意到这点。
    中断 / 事件线
    EXTI 20 个中断 / 事件线,每个 GPIO 都可以被设置为输入线,占用 EXTI0
    EXTI15,还有另外七根用于特定的外设事件,
    EXTI0 EXTI15 用于 GPIO ,通过编程控制可以实现任意一个 GPIO 作为 EXTI 的输
    入源。由表 18-1 可知, EXTI0 可以通过 AFIO 的外部中断配置寄存器 1(AFIO_EXTICR1)
    EXTI0[3:0] 位选择配置为 PA0 PB0 PC0 PD0 PE0 PF0 PG0 PH0 或者 PI0,
    EXTI 初始化结构体详解
    标准库函数对每个外设都建立了一个初始化结构体,比如 EXTI_InitTypeDef ,结构体
    成员用于设置外设工作参数,并由外设初始化配置函数,比如 EXTI_Init() 调用,这些设定
    参数将会设置外设相应的寄存器,达到配置外设工作环境的目的。
    初始化结构体和初始化库函数配合使用是标准库精髓所在,理解了初始化结构体每个
    成员意义基本上就可以对该外设运用自如了。初始化结构体定义在 stm32f4xx_exti.h 文件中,
    初始化库函数定义在 stm32f4xx_exti.c 文件中,编程时我们可以结合这两个文件内注释使用。
     EXTI 初始化结构体
    1 typedef struct {
    2 uint32_t EXTI_Line; // 中断 / 事件线
    3 EXTIMode_TypeDef EXTI_Mode; // EXTI 模式
    4 EXTITrigger_TypeDef EXTI_Trigger; // 触发类型
    5 FunctionalState EXTI_LineCmd; // EXTI 使能
    6 } EXTI_InitTypeDef;
    1) EXTI_Line EXTI 中断 / 事件线选择,可选 EXTI0 EXTI19
    2) EXTI_Mode EXTI 模式选择,可选为产生中断 (EXTI_Mode_Interrupt) 或者产生事
    (EXTI_Mode_Event)
    3) EXTI_Trigger EXTI 边沿触发事件,可选上升沿触发 (EXTI_Trigger_Rising) 、下
    降沿触发 ( EXTI_Trigger_Falling) 或者上升沿和下降沿都触发
    ( EXTI_Trigger_Rising_Falling)
    4) EXTI_LineCmd :控制是否使能 EXTI 线,可选使能 EXTI 线 (ENABLE) 或禁用
    (DISABLE)
    六  外部中断控制实验
    中断在嵌入式应用中占有非常重要的地位,几乎每个控制器都有中断功能。中断对保
    证紧急事件得到第一时间处理是非常重要的。
    我们设计使用外接的按键来作为触发源,使得控制器产生中断,并在中断服务函数中
    实现控制 灯的任务。
    软件设计
    中断服务函数放在 stm32f10x_it.h 文件中。
    编程要点
    1) 初始化用来产生中断的 GPIO
    2) 初始化 EXTI
    3) 配置 NVIC
    4) 编写中断服务函数;
    1. #include "exti.h"
    2. #include "led.h"
    3. #include "key.h"
    4. static void EXTI_NVIC_Config(void){
    5. NVIC_InitTypeDef NVIC_InitStruct;
    6. NVIC_PriorityGroupConfig( NVIC_PriorityGroup_1);//分组
    7. NVIC_InitStruct.NVIC_IRQChannel = EXTI4_IRQn;//中断源
    8. NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;//主优先级
    9. NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;//子优先级;
    10. NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//打开大门
    11. NVIC_Init(&NVIC_InitStruct);
    12. }
    13. void EXTI_Key0_Config(void){//中断外设事件线初始化
    14. EXTI_InitTypeDef EXTI_InitStructure;//变量定义放最前面
    15. //0初始化NVIC,用于处理中断
    16. EXTI_NVIC_Config();
    17. //1初始化要链接到exti的GPIO
    18. KEY0_GPIO_Config();//浮空输入:电平完全由外部决定
    19. //2初始化exti用于产生中断
    20. RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO, ENABLE );//使用 GPIO 之前必须开启 GPIO 端口的时钟;用到 EXTI 必须开启 AFIO 时钟。EXIT外设的小时钟,APB2线上的是大时钟
    21. GPIO_EXTILineConfig( GPIO_PortSourceGPIOE, GPIO_PinSource4);//功能框图输入线源配置:对应ARIO外设
    22. EXTI_InitStructure.EXTI_Line = EXTI_Line4;
    23. EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//选择中断而非事件
    24. EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //假设默认高电平,按键按下拉低。注意这里引脚跳接线自己接高电平一下
    25. EXTI_InitStructure.EXTI_LineCmd = ENABLE;//打开小门
    26. EXTI_Init(&EXTI_InitStructure);
    27. //3编写中断服务函数,在it文件里面
    28. }

    1. #include "stm32f10x.h"
    2. #include "led.h"
    3. #include "key.h"
    4. #include "rcc.h"
    5. #include "exti.h"
    6. //key0按键按下检测到中断,LED0就反转一次
    7. void delay(uint32_t count){
    8. for(;count!=0;count--);
    9. }
    10. int main(void)
    11. {
    12. LED0_GPIO_Config();
    13. EXTI_Key0_Config();
    14. while (1){
    15. }
    16. }

  • 相关阅读:
    Hippo4J v1.3.1 发布,增加 Netty 监控上报、SpringCloud Hystrix 线程池监控等特性
    使用Grunt shell方式交互处理数据
    微前端框架 qiankun 配置使用【基于 vue/react脚手架创建项目 】
    C语言程序设计(第五版)书上习题及课后习题与答案汇总(只涉及程序编写部分)--持续更新
    数据结构——红黑树
    可达矩阵-邻接矩阵-以及有向图的python绘制
    mysql增加字段语句转为Oracle语句
    元类的使用
    git还原过程中如何显示更详细的日志
    [leetcode hot 150]第十一题,盛水最多的容器
  • 原文地址:https://blog.csdn.net/qq_45871216/article/details/126456390