STM32学习笔记之--EXTI(原理+代码+Debug验证)

1–EXTI简介

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

  • 中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行

  • 中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源

  • 中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回

    中断源(例如串口收到了数据)

2–STM32的中断

  • 68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设(具体可以查手册儿)
  • 使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级

灰色的是内核的中断 其他的就是中断触发信号(触发源)

最右边是中断的地址 因为我们程序中的中断函数 他的地址 是有编译器来分配的 是不固定的 但是我们的中断跳转 只能跳到对应的位置进行执行程序 所以就有了地址 这些固定的地址存放这一条长跳转指令 当发生中断 就会由这个长跳转指令找到我们对应的中断处理函数

  • NVIC(嵌套中断向量控制表)Nested Vectored Interrupt Controller

    NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级

    抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队

    分组方式抢占优先级响应优先级
    分组00位,取值为04位,取值为0~15
    分组11位,取值为0~13位,取值为0~7
    分组22位,取值为0~32位,取值为0~3
    分组33位,取值为0~71位,取值为0~1
    分组44位,取值为0~150位,取值为0
  • 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断

AFIO主要有2个作用 中断引脚选择 复用功能引脚重映射

3–EXTI框图详解

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

图中红色的部分 是一个产生中断1的线路 最终流行NVIC控制器内

编号1是输入线 EXTI控制器内有19个中断/事件输入线 这些输入线可以通过寄存器任意一个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 来实现是否产生中断的目的。编号 4 电路输出的信号会被保存到挂起寄存器(EXTI_PR) 内,如果确定编号 4 电路输出为 1 就会把 EXTI_PR 对应位置 1。

编号 5 是将 EXTI_PR 寄存器内容输出到 NVIC 内,从而实现系统中断事件控制。

下面绿色的部分 他是一个产生事件的的电路流程 最终输出一个脉冲信号

123部分都是共用的 一路到了编号6 编号6是一个与门 他有2个输入端 一个软件中断事件寄存器 还有一个事件屏蔽寄存器 (EXTI_EMR )如果这一位设置为0 那么不管3是不是1 都输出0 如果 EXTI_EMR 设置为 1 时,最终编号 6 电路输出的信号才由编号 3 电路的输出信号决定,这样我们可以简单的控制 EXTI_EMR 来实现是否产生事件的目的。

编号7是一个脉冲发生器电路,当它的输入端,即编号 6 电路的输出端,是一个有效信号 1 时就会产生一个脉冲;如果输入端是无效信号就不会输出脉冲

编号8 是一个脉冲信号,就是产生事件的线路最终的产物,这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC 等等,这样的脉冲信号一般用来触发 TIM 或者 ADC开始转换

  • 产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是
    软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号
    传输,属于硬件级的。

4–程序分析

void Exti_Init(void)
{
    /*****开启时钟*************/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO  , ENABLE);
    
    /******配置GPIO口**********/
    GPIO_InitTypeDef GPIO_InitStructuer;
    GPIO_InitStructuer.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructuer.GPIO_Pin = GPIO_Pin_14;
    GPIO_InitStructuer.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB , &GPIO_InitStructuer);
    
    /*******配置AFIO 中断引脚选择********/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB , GPIO_PinSource14);
    
    /*****配置EXTI********/
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_InitStructure.EXTI_Line = EXTI_Line14;                     //选择中断线
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;                       //使能
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;             //中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;         //选择下降沿触发
    EXTI_Init(&EXTI_InitStructure);
    
    /*****配置NVIC*****/
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);    //设置优先级
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;                        使能所在的外部中断通道
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                             使能外部中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;                   //抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;                          //响应优先级
    NVIC_Init(&NVIC_InitStructure);
    
}
  • 中断函数
void EXTI15_10_IRQHandler(void)     //外部中断函数
{
    if(EXTI_GetITStatus(EXTI_Line14) == SET)    //判断是否通道14来的中断
    {
        if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)         //如果14号脚为低电平
		{
            Count ++;
		}
        EXTI_ClearITPendingBit(EXTI_Line14);        //清除中断请求
    }
    
}

5–程序验证

  • 首先将硬件电路搭建好 我这里采用的是STM32C8T6最小系统板 + 对射红外传感器 这里将对射红外传感器的DO口接到STM32核心板上的PB14口 当有物体遮挡对射光电传感器时 DO口会输出一个高电平 因为设置的外部中断时下降沿触发 PB14引脚会捕捉到一个高电平到低电平的跳变信号 就会触发中断

  • 进入Keil的Debug模式 在中断函数处打断点 后点击全速运行

用卡片遮挡我们的对射红外传感器 如果黄色箭头出现在我们打断点处 说明我们成功啦!!!

6 – 总结

在中断中 讲究块进快出 所以最好不要在中断里使用 Delay函数

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是基于STM32F103的HC-SR501红外传感器驱动代码,带有注释: ```c #include "stm32f10x.h" // 定义HC-SR501传感器连接的GPIO口和引脚 #define SENSOR_GPIO GPIOA #define SENSOR_PIN GPIO_Pin_0 // 初始化GPIO口和引脚,配置为输入模式 void GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟 GPIO_InitStructure.GPIO_Pin = SENSOR_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(SENSOR_GPIO, &GPIO_InitStructure); } // 配置外部中断 void EXTI_Config(void) { EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); // 配置外部中断线路 EXTI_InitStructure.EXTI_Line = EXTI_Line0; // 中断线路0 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; // 中断触发方式,上升沿和下降沿都触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; // 配置中断通道为0 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; // 抢占优先级为0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; // 子优先级为1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } // 外部中断0的中断处理函数 void EXTI0_IRQHandler(void) { uint8_t sensor_value = GPIO_ReadInputDataBit(SENSOR_GPIO, SENSOR_PIN); // 读取传感器的输出信号 if (sensor_value == Bit_SET) { // 传感器检测到运动 // 处理传感器检测到运动后的操作 } else { // 传感器未检测到运动 // 处理传感器未检测到运动后的操作 } EXTI_ClearITPendingBit(EXTI_Line0); // 清除中断标志位 } int main(void) { GPIO_Config(); // 初始化GPIO口和引脚 EXTI_Config(); // 配置外部中断 while (1) { // 在主循环中处理其他操作 // ... } } ``` 以上代码是基于STM32F103的HC-SR501红外传感器驱动代码,包括初始化GPIO口和引脚、配置外部中断和中断处理函数。您可以根据自己的实际情况进行修改和完善。同时,您也需要根据HC-SR501传感器的规格书来设置正确的参数,以确保传感器的正确工作。 希望这些代码对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值