• EXTI—外部中断/事件控制器


    外部中断/事件控制器(EXTI)管理了控制器的 23 个中断/事件线。每个中断/事件线都对
    应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI 可以实现对
    每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。

    EXTI功能框图
    EXTI 的功能框图包含了 EXTI 最核心内容,掌握了功能框图,对 EXTI 就有一个整体
    的把握,在编程时就思路就非常清晰。 EXTI 功能框图见图 17-1
    在图 17-1 可以看到很多在信号线上打一个斜杠并标注“ 23”字样,这个表示在控制器
    内部类似的信号线路有 23 个,这与 EXTI 总共有 23 个中断/事件线是吻合的。所以我们只
    要明白其中一个的原理,那其他 22 个线路原理也就知道了。


    EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件
    上就有所不同。

    产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功
    能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且
    是电路级别的信号传输,属于硬件级的。
    另外, EXTI 是在 APB2 总线上的,在编程时候需要注意到这点。

    EXTI 23 个中断/事件线,每个 GPIO 都可以被设置为输入线,占用 EXTI0
    EXTI15,还有另外七根用于特定的外设事件,见表 17-1
    七根特定外设中断/事件线由外设触发,具体用法参考《 STM32F4xx 中文参考手册》
    中对外设的具体说明。

     EXTI0 可以通过 SYSCFG 外部中断配置寄存器
    1(SYSCFG_EXTICR1)EXTI0[3:0]位选择配置为 PA0PB0PC0PD0PE0PF0
    PG0PH0 或者 PI0

    EXTI初始化结构体详解
    标准库函数对每个外设都建立了一个初始化结构体,比如 EXTI_InitTypeDef,结构体
    成员用于设置外设工作参数,并由外设初始化配置函数,比如 EXTI_Init()调用,这些设定
    参数将会设置外设相应的寄存器,达到配置外设工作环境的目的。
    初始化结构体和初始化库函数配合使用是标准库精髓所在,理解了初始化结构体每个
    成员意义基本上就可以对该外设运用自如了。初始化结构体定义在 stm32f4xx_exti.h 文件中,
    初始化库函数定义在 stm32f4xx_exti.c 文件中,编程时我们可以结合这两个文件内注释使用。

    下面说一下编程框架:

    使用 GPIO 之前必须开启 GPIO 端口的时钟;用到 EXTI 必须开启 SYSCFG 时钟。

     作为中断/时间输入线把 GPIO 配置为输入模式,这里不使用上拉或下拉,由外部电路
    完全决定引脚的状态。

     SYSCFG_EXTILineConfig 函数用来指定中断/事件线的输入源,它实际是设定
    SYSCFG 外部中断配置寄存器的值,该函数接收两个参数,第一个参数指定 GPIO 端口源,
    第二个参数为选择对应 GPIO 引脚源编号。

     1 int main(void)
     2 {
     3     /* LED 端口初始化 */
     4     LED_GPIO_Config();
     5       
     6     /* 初始化EXTI中断,按下按键会触发中断,
     7   *  触发中断会进入stm32f4xx_it.c文件中的函数
     8     *  KEY1_IRQHandler和KEY2_IRQHandler,处理中断,反转LED灯。
     9     */
    10     EXTI_Key_Config(); 
    11     
    12     /* 等待中断,由于使用中断方式,CPU不用轮询按键 */
    13     while(1)                            
    14     {
    15     }
    16 }

    主函数结构,先初始化硬件LED,用于按键中断控制的效果展示。

    然后是 EXTI_Key_Config(); 需要留意一点的函数或者配置加以红色标注:

     1  /**
     2   * @brief  配置嵌套向量中断控制器NVIC
     3   * @param  无
     4   * @retval 无
     5   */
     6 static void NVIC_Configuration(void)
     7 {
     8   NVIC_InitTypeDef NVIC_InitStructure;
     9   
    10   /* 配置NVIC为优先级组1 */
    11   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    12   
    13   /* 配置中断源:按键1 */
    14   NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;
    15   /* 配置抢占优先级:1 */
    16   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    17   /* 配置子优先级:1 */
    18   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    19   /* 使能中断通道 */
    20   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    21   NVIC_Init(&NVIC_InitStructure);
    22   
    23   /* 配置中断源:按键2,其他使用上面相关配置 */  
    24   NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;
    25   NVIC_Init(&NVIC_InitStructure);
    26 }
    27 
    28  /**
    29   * @brief  配置 PA0 为线中断口,并设置中断优先级
    30   * @param  无
    31   * @retval 无
    32   */
    33 void EXTI_Key_Config(void)
    34 {
    35     GPIO_InitTypeDef GPIO_InitStructure; 
    36     EXTI_InitTypeDef EXTI_InitStructure;
    37   
    38     /*开启按键GPIO口的时钟*/
    39     RCC_AHB1PeriphClockCmd(KEY1_INT_GPIO_CLK|KEY2_INT_GPIO_CLK ,ENABLE);
    40   
    41   /* 使能 SYSCFG 时钟 ,使用GPIO外部中断时必须使能SYSCFG时钟*/
    42   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
    43   
    44   /* 配置 NVIC */
    45   NVIC_Configuration();
    46   
    47     /* 选择按键1的引脚 */ 
    48   GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;
    49   /* 设置引脚为输入模式 */ 
    50   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;                
    51   /* 设置引脚不上拉也不下拉 */
    52   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    53   /* 使用上面的结构体初始化按键 */
    54   GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure); 
    55 
    56     /* 连接 EXTI 中断源 到key1引脚 */
    57   SYSCFG_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE,KEY1_INT_EXTI_PINSOURCE);
    58 
    59   /* 选择 EXTI 中断源 */
    60   EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;
    61   /* 中断模式 */
    62   EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    63   /* 下降沿触发 */
    64   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;  
    65   /* 使能中断/事件线 */
    66   EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    67   EXTI_Init(&EXTI_InitStructure);
    68   
    69   /* 选择按键2的引脚 */ 
    70   GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;  
    71   /* 其他配置与上面相同 */
    72   GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);      
    73 
    74     /* 连接 EXTI 中断源 到key2 引脚 */
    75   SYSCFG_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE,KEY2_INT_EXTI_PINSOURCE);
    76 
    77   /* 选择 EXTI 中断源 */
    78   EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;
    79   EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    80   /* 上升沿触发 */
    81   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  
    82   EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    83   EXTI_Init(&EXTI_InitStructure);
    84 }

    在stm32f4xx_it.c中编写中断服务函数,其中中断服务函数名要对应启动文件的中断向量表,为了可读性可以用宏给中断向量表取一个适合中断目的的名字。

    还有一个需要注意的:需要使能 SYSCFG 时钟 ,使用GPIO外部中断时必须使能SYSCFG时钟。

    上面是固件库的函数定义内部,这个是固件库编程的套路,都是这样定义某个功能的结构体,然后初始化这个结果体,最后调用一个init函数完成。

    固件库这样的套路明白之后,编写应用程序会轻松愉快地多。至于固件库内部到底是如何实现的,在达到高手水平了再去,毕竟做项目是以速度和质量说话的,固件库肯定快于寄存器版的,但是想自己写出库函数,又不得不去学习寄存器编程,所以都是相辅相成的,先学好固件库,然后学寄存器版,然后达到自己可以编写库,就达到学习目的了。

  • 相关阅读:
    腾讯精选50题算法【二叉搜索树的最近公共祖先】
    潜水一周,我精心整理了两个超级有用的职场生存之道
    全球用尽IPv4的一点思考
    Leetcode算法【114. 二叉树展开为链表】
    【翻译】全新16英寸MacBook Pro评测:开发人员的梦想成真
    Medium高赞系列,如何正确的在Stack Overflow提问
    Typora+PicGo+GitHub实现md自带图床效果
    SpringBoot输出日志到文件
    Mybatis用SQL做自连表查询
    IDEA实用插件推荐及使用方法详解
  • 原文地址:https://www.cnblogs.com/yangguang-it/p/6869428.html
Copyright © 2020-2023  润新知