EXTI外部中断之对射式红外传感器计次应用案例

news2024/11/15 17:32:41

系列文章目录

STM32中断系统之EXTI外部中断


文章目录

  • 系列文章目录
  • 前言
  • 一、应用案例简介
  • 二、电路接线图
  • 三、应用案例代码
  • 四、应用案例分析
    • 4.1 配置外部中断
      • 4.1.1 配置RCC时钟
      • 4.1.2 配置GPIO
      • 4.1.3 配置AFIO
      • 4.1.4 配置EXTI
      • 4.1.5 配置NVIC
    • 4.2 编写中断函数


前言

提示:本文主要用作在学习江科大自化协STM32入门教程后做的归纳总结笔记,旨在学习记录,如有侵权请联系作者
本文主要探讨利用EXTI外部中断实现一个对射式红外传感器计次的功能。


一、应用案例简介

本案例利用EXTI外部中断实现了一个对射式红外传感器计次的功能。当使用挡光片挡在红外对管的中间,此时输出指示灯熄灭(输出高电平),然后再拿开挡光片,此时输出指示灯亮了(输出低电平)。在灭到亮之间会产生一个下降沿,这个下降沿触发单片机引脚的外部中断,然后执行数字加1的中断程序。如此类推,实现了对挡光次数的统计并显示在OLED显示屏上。

二、电路接线图

对射式红外传感器模块VCC、GND分别接电源正负极,DO数字输出端接到PB14口。当使用挡光片在这个对射式红外对管中间经过时,DO口就会输出电平变化的信号,这个电平跳变的信号就会触发STM32 PB14号口的中断,这就是对射式红外传感器计次的原理。

在这里插入图片描述

三、应用案例代码

传感器头文件CountSensor.h:

#ifndef __COUNT_SENSOR_H
#define __COUNT_SENSOR_H

void CountSensor_Init(void);
uint16_t CountSensor_Get(void);

#endif

传感器实现文件CountSensor.c:

#include "stm32f10x.h"                  // Device header

uint16_t CountSensor_Count;

void CountSensor_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
	
	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_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);
}

uint16_t CountSensor_Get(void)
{
	return CountSensor_Count;
}

void EXTI15_10_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line14) == SET)
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
		{
			CountSensor_Count ++;
		}
		EXTI_ClearITPendingBit(EXTI_Line14);
	}
}

主程序文件main.c:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"

int main(void)
{
	OLED_Init();
	CountSensor_Init();
	
	OLED_ShowString(1, 1, "Count:");
	
	while (1)
	{
		OLED_ShowNum(1, 7, CountSensor_Get(), 5);
	}
}

四、应用案例分析

4.1 配置外部中断

首先,在CountSensor_Init函数里完成外部中断的初始化配置。那要如何配置外部中断呢?我们可以看一下下面这个外部中断整体结构图。简单来说,我们只需要把这个外部中断从GPIO到NVIC这一路中出现的外设模块都配置好,把这条信号电路给打通就行了。

在这里插入图片描述

外部中断配置步骤如下:
1. 配置RCC: 把涉及的外设的时钟都打开。
2. 配置GPIO: 选择端口为输入模式。
3. 配置AFIO: 选择我们用的这一路GPIO并将它连接到后面的EXTI。
4. 配置EXTI: 选择边沿触发方式,比如上升沿、下降沿或者双边沿。选择触发响应方式,可以选择中断响应或事件响应,当然我们一般都是中断响应。
5. 配置NVIC: 给中断选择一个合适的优先级

最后,通过NVIC,外部中断信号就能进入CPU了,这样CPU才能收到中断信号,才能跳转到中断函数里执行中断程序,那接下来让我们一个一个来分析。

首先第一步,配置RCC时钟。

4.1.1 配置RCC时钟

  • 调用RCC_APB2PeriphClockCmd函数分别开启GPIOB和AFIO(AFIO也是APB2的外设)的时钟。
  • 接着还有EXTI和NVIC两个外设的时钟,这两个外设的时钟是一直都打开着的,所以不需要我们再开启时钟了。

注意:EXTI作为一个独立的外设,按理来说应该是需要开启时钟的,但是寄存器里面却没有EXTI时钟的控制位,这个原因手册里也没有找到,网上也没有确切的答案。可能是和EXTI唤醒有关,或者是其他一些电路上设计的考虑,那我们暂时就不用管了,知道它不需要开启时钟就行了。另外NVIC也不需要开启时钟,因为NVIC是内核的外设,内核的外设都是不需要开启时钟的。人家跟CPU一起都是住“皇宫”里的,而RCC管的都是内核外的外设,所以RCC管不着NVIC。

配置RCC时钟代码如下:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

好,那到这里时钟就配置完了,接着我们就来进行第二步,配置GPIO。

4.1.2 配置GPIO

GPIO的配置在前面GPIO的章节已经讲过了,这里就不再累述了,在这里只讲一下一些需要注意的点。

第一个GPIO_Mode 参数,对于外部中断来说,要选择浮空输入、上拉输入或者下拉输入其中的一个模式。当然,像这种其他外设使用GPIO口的情况,如果你不清楚该配置为什么模式,可以看一下参考手册。找到GPIO这一章,有一个外设的GPIO配置表,里面有写每个外设的各个引脚都需要配置为什么模式。

在这里插入图片描述

那在最后,就有EXTI输入线,他给的推荐配置就是浮空、上拉或者下拉。所以在这里,我就给一个GPIO_Mode_IPU,上拉输入,默认为高电平的输入方式。

在这里插入图片描述

GPIO初始化配置部分代码如下:

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);

这样GPIO部分我们就配置好了,然后我们就来进行第三步,配置AFIO。

4.1.3 配置AFIO

简单介绍一下AFIO相关函数。这个AFIO外设,ST公司并没有给它分配专门的库函数文件,它的库函数是和GPIO在一个文件里的,那我们找一下GPIO的.h文件,找到stm32f10x_gpio.h文件,然后拉到最后。

void GPIO_DeInit(GPIO_TypeDef* GPIOx);
void GPIO_AFIODeInit(void);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);

首先是 GPIO_AFIODeInit 函数,这个函数是用来复位AFIO外设的,调用一下这个函数,AFIO外设的配置就会全部清除。

然后下面这个 GPIO_PinLockConfig 函数,这个函数是用来锁定GPIO配置的,调用这个函数,参数指定某个引脚,那这个引脚的配置就会被锁定,防止意外更改。这个也是GPIO外设的函数,用的不多,了解一下即可。

然后是 GPIO_EventOutputConfigGPIO_EventOutputCmd 函数,这两个函数是用来配置AFIO的事件输出功能的,用得也不多,了解一下即可。

接下来 GPIO_PinRemapConfigGPIO_EXTILineConfig 函数,这两个函数就比较重要了。

void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);

GPIO_PinRemapConfig 函数可以用来进行引脚重映射。第一个参数可以选择你要重映射的方式,第二个参数是新的状态。

然后 GPIO_EXTILineConfig 函数就是我们本章节外部中断需要用到的函数了。调用这个函数,就可以配置AFIO的数据选择器,来选择我们想要中断的引脚。

void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

那最后一个,GPIO_ETH_MediaInterfaceConfig 函数,这个是和以太网有关的。

先回到这个主线任务上来,我们现在想要配置AFIO外部中断引脚选择,也就是调用 GPIO_EXTILineConfig 函数即可,以下是该函数的定义及说明。

**
  * @brief  Selects the GPIO pin used as EXTI Line.
  * @param  GPIO_PortSource: selects the GPIO port to be used as source for EXTI lines.
  *   This parameter can be GPIO_PortSourceGPIOx where x can be (A..G).
  * @param  GPIO_PinSource: specifies the EXTI line to be configured.
  *   This parameter can be GPIO_PinSourcex where x can be (0..15).
  * @retval None
  */
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
{
  uint32_t tmp = 0x00;
  /* Check the parameters */
  assert_param(IS_GPIO_EXTI_PORT_SOURCE(GPIO_PortSource));
  assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource));
  
  tmp = ((uint32_t)0x0F) << (0x04 * (GPIO_PinSource & (uint8_t)0x03));
  AFIO->EXTICR[GPIO_PinSource >> 0x02] &= ~tmp;
  AFIO->EXTICR[GPIO_PinSource >> 0x02] |= (((uint32_t)GPIO_PortSource) << (0x04 * (GPIO_PinSource & (uint8_t)0x03)));
}

那上面这里简介写的是,选择GPIO pin作为外部中断线。第一个参数是GPIO_PortSource,选择某个GPIO外设作为外部中断源,这个参数可以是GPIO_PortSourceGPIOx,其中x可以是A到G。我们用的是PB14号引脚,所以就是GPIO_PortSourceGPIOB。第二个参数是GPIO_PinSource,指定要配置的外部中断线,这个参数可以是GPIO_PinSourcex,其中x可以是0到15。所以我们这里就是GPIO_PinSource14,代表连接PB14号口的第14个中断线路。

到这里,AFIO外部中断引脚选择配置就完成了,直接调用 GPIO_EXTILineConfig 函数即可。当执行完这个函数之后,AFIO的第14个数据选择器就拨好了,其中输入端被拨到了GPIOB外设上,对应的就是PB14号引脚,输出端固定连接的是EXTI的第14个中断线路。这样,PB14号引脚的电平信号就可以顺利通过AFIO进入到后级电路EXTI了。

AFIO配置代码如下:

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);

那接下来,我们就可以进入第四步,配置EXTI了。

4.1.4 配置EXTI

我们先来看一下EXTI的库函数文件,看一下EXTI都有哪些库函数可以用。我们找到stm32f10x_exti.h文件,拖到最后,这些就是EXTI的所有库函数了。

void EXTI_DeInit(void);
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
void EXTI_ClearFlag(uint32_t EXTI_Line);
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

首先第一个,EXTI_DeInit函数,调用它就可以把EXTI的配置都清除,恢复成上电默认的状态。

然后第二个,EXTI_Init函数,调用这个函数就可以根据这个结构体里的参数配置EXTI外设,我们初始化EXTI主要用到的就是这个函数。

接着第三个,EXTI_StructInit函数,调用这个函数,可以把参数传递的结构体变量赋一个默认值。

那接着看第四个,EXTI_GenerateSWInterrupt函数,这个函数是用来软件触发外部中断的,调用这个函数,参数给一个指定的中断线,就能软件触发一次这个外部中断。

然后剩下的这四个函数也是库函数的模版函数,很多模块都有这四个函数。因为在外设运行的过程中会产生一些状态标志位,比如外部中断来了,是不是会有一个挂起寄存器置了一个标志位?对于其他外设来说,比如串口收到数据会置标志位,定时器时间到也会置标志位。这些标志位都是放在状态寄存器里的,当程序想要获取这些标志位时,就可以用到这四个函数。

其中,这前两个函数。EXTI_GetFlagStatus函数可以获取指定的标志位是否被置1了,EXTI_ClearFlag函数可以对置1的标志位进行清除。那对于这些标志位,有的比较紧急,在置标志位后会触发中断。在中断函数里,如果你想查看标志位和清除标志位那就用下面两个函数。EXTI_GetITStatus函数可以获取中断标志位是否被置1了,EXTI_ClearITPendingBit函数可以清除挂起标志位。

所以总结一下就是,如果你想在主程序里查看和清除标志位,就用前两个函数EXTI_GetFlagStatus和EXTI_ClearFlag,如果你想在中断函数里查看和清除标志位,就用下面这两个函数。其实本质上,这四个函数都是对状态寄存器的读写。上面两个和下面两个是类似的功能,都是读写状态寄存器,只不过是下面这两个函数只能读写与中断有关的标志位,并且对中断是否允许做出了判断,而上面这两个函数只是一般的读写标志位,没有额外的处理,能不能触发中断的标志位都能读取。所以建议在主程序里用上面两个,中断程序里用下面两个。当然你如果非要在中断里用上面两个那其实也是没有问题的,只不过是库函数针对这两种情景,区分了这两类读写函数。

ok,那我们进入正题,完成EXTI的配置调用EXTI_Init函数即可,我们先来看一下该函数的声明。

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)

再来看一下EXTI_InitTypeDef结构体的声明

** 
  * @brief  EXTI Init Structure definition  
  */

typedef struct
{
  uint32_t EXTI_Line;               /*!< Specifies the EXTI lines to be enabled or disabled.
                                         This parameter can be any combination of @ref EXTI_Lines */
   
  EXTIMode_TypeDef EXTI_Mode;       /*!< Specifies the mode for the EXTI lines.
                                         This parameter can be a value of @ref EXTIMode_TypeDef */

  EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines.
                                         This parameter can be a value of @ref EXTIMode_TypeDef */

  FunctionalState EXTI_LineCmd;     /*!< Specifies the new state of the selected EXTI lines.
                                         This parameter can be set either to ENABLE or DISABLE */ 
}EXTI_InitTypeDef;

首先是第一个参数EXTI_Line,这个是指定我们要配置的中断线。那我们需要用PB14所在的第14个线路,所以选择EXTI_Line14。

接下来看第二个参数EXTI_Mode,这个是指定外部中断线的模式。有中断模式和事件模式,那我们这里选中断模式,选择EXTI_Mode_Interrupt。

接着再看第三个参数EXTI_Trigger,这个是指定触发信号的有效边沿。有上升沿触发、下降沿触发和双边沿触发。我这里选一个下降沿触发EXTI_Trigger_Falling。

最后看第四个参数EXTI_LineCmd,这个是指定选择的中断线的新状态。我们选择开启中断,所以选择ENABLE。

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);

这样我们的外部中断就配置完成了,我们当前的配置是,将EXTI的第14个线路配置为中断模式,下降沿触发,然后开启中断,这样PB14的电平信号就能通过EXTI通向下一级NVIC了。

那最后,我们就来执行第五步,配置NVIC。

4.1.5 配置NVIC

我们还是先看一下库函数文件里的函数,因为NVIC是内核外设,所以它的库函数是被ST发配到杂项这里来了,我们打开misc.h文件,拖到最后,这些就是NVIC相关函数了。

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);

我们先来看一下第一个函数NVIC_PriorityGroupConfig,这个函数是用来中断分组的,参数是中断分组的方式。

然后第二个函数NVIC_Init,根据结构体里面指定的参数初始化NVIC。

接着看第三个函数NVIC_SetVectorTable,设置中断向量表。第四个函数NVIC_SystemLPConfig,系统低功耗配置。这两个函数用的不多,了解即可。

在配置中断之前,先指定一下中断的分组然后再使用NVIC_Init函数初始化NVIC就行了,大概的思路就是这样。

先来看一下NVIC_PriorityGroupConfig函数的定义及相关说明。

**
  * @brief  Configures the priority grouping: pre-emption priority and subpriority.
  * @param  NVIC_PriorityGroup: specifies the priority grouping bits length. 
  *   This parameter can be one of the following values:
  *     @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority
  *                                4 bits for subpriority
  *     @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority
  *                                3 bits for subpriority
  *     @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority
  *                                2 bits for subpriority
  *     @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority
  *                                1 bits for subpriority
  *     @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority
  *                                0 bits for subpriority
  * @retval None
  */
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
  /* Check the parameters */
  assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
  
  /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
  SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}

这个简介是配置优先级分组:先占优先级和从占优先级,这里先占优先级就是抢占优先级,从占优先级就是响应优先级,我们这里选择NVIC_PriorityGroup_2。

好,那我们进入最后一步,配置NVIC,先来看一下NVIC_Init函数的声明。

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

NVIC_InitTypeDef结构体声明如下:

** 
  * @brief  NVIC Init Structure definition  
  */

typedef struct
{
  uint8_t NVIC_IRQChannel;                    /*!< Specifies the IRQ channel to be enabled or disabled.
                                                   This parameter can be a value of @ref IRQn_Type 
                                                   (For the complete STM32 Devices IRQ Channels list, please
                                                    refer to stm32f10x.h file) */

  uint8_t NVIC_IRQChannelPreemptionPriority;  /*!< Specifies the pre-emption priority for the IRQ channel
                                                   specified in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref NVIC_Priority_Table */

  uint8_t NVIC_IRQChannelSubPriority;         /*!< Specifies the subpriority level for the IRQ channel specified
                                                   in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref NVIC_Priority_Table */

  FunctionalState NVIC_IRQChannelCmd;         /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
                                                   will be enabled or disabled. 
                                                   This parameter can be set either to ENABLE or DISABLE */   
} NVIC_InitTypeDef;

我们先来看一下第一个参数NVIC_IRQChannel,这个是指定中断通道来开启或关闭,我们来看一下关于这个参数的描述。

/*!< Specifies the IRQ channel to be enabled or disabled.This parameter can be a value of @ref IRQn_Type (For the complete STM32 Devices IRQ Channels list, please refer to stm32f10x.h file) */

这里有个括号,写的是对于完整的STM32中断通道列表,请参考stm32f10x.h文件,这个意思是IRQn_Type的定义不在这个文件,需要到stm32f10x.h文件里找。

在这里插入图片描述

可以看到这里有非常多的中断通道,因为这个库函数可以兼容所有的F1系列芯片,但是不同的芯片中断通道列表是不一样的,所以这里有很多条件编译用来选择你使用芯片的中断通道列表。由于我们的芯片是MD中等密度的,所以只需要展开这个MD的条件编译即可。

然后找到这个EXTI15_10_IRQn,STM32的EXTI10到EXTI15都是合并到这个通道里,这样通道就指定好了。

接下来两个参数NVIC_IRQChannelPreemptionPriority、NVIC_IRQChannelSubPriority,就是指定所选通道的抢占优先级和响应优先级了。

我们跳转到它的定义,然后找到这张表,我们在前面选择了分组2 NVIC_PriorityGroup_2,那抢占优先级和响应优先级的取值范围都是0 ~ 3,这里我将抢占优先级和响应优先级都设置为1。

** @defgroup NVIC_Priority_Table 
  * @{
  */

**
@code  
 The table below gives the allowed values of the pre-emption priority and subpriority according
 to the Priority Grouping configuration performed by NVIC_PriorityGroupConfig function
  ============================================================================================================================
    NVIC_PriorityGroup   | NVIC_IRQChannelPreemptionPriority | NVIC_IRQChannelSubPriority  | Description
  ============================================================================================================================
   NVIC_PriorityGroup_0  |                0                  |            0-15             |   0 bits for pre-emption priority
                         |                                   |                             |   4 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------
   NVIC_PriorityGroup_1  |                0-1                |            0-7              |   1 bits for pre-emption priority
                         |                                   |                             |   3 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------    
   NVIC_PriorityGroup_2  |                0-3                |            0-3              |   2 bits for pre-emption priority
                         |                                   |                             |   2 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------    
   NVIC_PriorityGroup_3  |                0-7                |            0-1              |   3 bits for pre-emption priority
                         |                                   |                             |   1 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------    
   NVIC_PriorityGroup_4  |                0-15               |            0                |   4 bits for pre-emption priority
                         |                                   |                             |   0 bits for subpriority                       
  ============================================================================================================================
@endcode
*/

最后一个参数NVIC_IRQChannelCmd,指定中断通道是使能还是失能,我们选择ENABLE。

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);

那到这里,整个外部中断的配置也就结束了。外部中断的信号从GPIO到AFIO,再到EXTI,再到NVIC,最终通向CPU,这样才能让CPU由主程序跳转到中断程序里执行。

4.2 编写中断函数

在STM32中,中断函数的名字都是固定的,每个中断通道都对应一个中断函数。中断函数的名字我们可以参考一下启动文件,这里面以IRQHandler结尾的字符串就是中断函数的名字,我们可以找到这个EXTI15_10_IRQHandler这一项,这就是EXTI10到EXTI15的中断函数(中断函数都是无参数无返回值的)。

中断函数代码如下:

void EXTI15_10_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line14) == SET)
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
		{
			CountSensor_Count ++;
		}
		EXTI_ClearITPendingBit(EXTI_Line14);
	}
}

在中断函数里,一般都是先进行一个中断标志位的判断,确保是我们想要的中断源触发的这个函数。因为这个函数是EXTI10到EXTI15都能进来,所以要先判断一下是不是我们想要的EXTI14进来的。最后,中断结束后一定要再调用一下清除中断标志位的函数,因为只要中断标志位置1了,程序就会跳转到中断函数。如果你不清除中断标志位,那它就会一直申请中断,这样程序就会不断响应中断,执行中断函数,那程序就会卡死在中断函数里了。所以我们每次中断程序结束后,都应该清除一下中断标志位。

对射式红外传感器计次工程代码:EXTI外部中断之对射式红外传感器计次应用案例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2043417.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

泛微OA系统走进腾讯大厦

企业信息化、数字化、网络化、智能化的快速发展带来了无限可能&#xff0c;但同时也带来了系统安全的严峻挑战。您准备好应对了吗? 上月由腾讯安全部、泛微联合举办的“OA 系统安全防护与腾讯iOA 零信任安全策略客户会”在腾讯滨海大厦成功举办&#xff0c;本次活动邀请了60位…

拆开一个断了FPC的墨水屏,是不是像OLED一样驱动芯片在里面

可对比查看一个OLED的屏幕拆解 拆解理由 第一次焊接驱动板时的fpc上下接问题&#xff0c;但焊接到板子上并没有达到正常的显示效果。PI补强也被撕下来。后来拔下来后发现金手指断裂。本来想用一个fpc排线连接在一起&#xff0c;但后来发现并没有达到理想效果&#xff0c;飞线…

【Python学习-UI界面】PyQt5 小部件14-QDock 子窗口

可停靠窗口是一个子窗口&#xff0c;可以保持浮动状态或附加到主窗口的指定位置。 QMainWindow类的主窗口对象保留了一块区域供可停靠窗口使用。该区域位于中央窗口部件周围。 可停靠窗口可以在主窗口内移动&#xff0c;也可以被取消停靠并由用户移动到新的区域。 样式如下: …

MinIO DataPOD 目标锁定 GPU Direct 并行文件系统

MinIO 推出针对 AI 应用的 DataPOD 参考架构 MinIO 设计了一种旨在为 AI 训练提供数据的 exascale DataPOD 参考架构。这家开源对象存储软件供应商正将其可扩展至100 PiB&#xff08;即大约112.6 PB&#xff09;的单元定位为一种替代方案&#xff0c;以取代使用 GPU Direct 技…

新中地2402期GIS特训营学员圆满结业,解锁GIS开发的无限可能!

GIS开发了解 24年8月5日&#xff0c;新中地GIS开发特训营2402期学员迎来了属于自己的结业典礼。 初入特训营&#xff0c;教与学双向奔赴 从24年3月4日开班&#xff0c;面对全新的领域&#xff0c;大家新中既有对未知的忐忑&#xff0c;更有对掌握GIS开发技术的期待 在本期学员…

车辆车载客流统计系统解决方案

车辆车载客流统计系统是一种用于实时监测和分析乘客流量的技术解决方案&#xff0c;它可以帮助公交公司、地铁运营商等交通管理部门优化运营计划、提高服务效率和乘客满意度。以下是一个详细的车载客流统计系统解决方案&#xff1a; 一、系统组成 传感器与设备 摄像头&#xf…

C库函数signal()信号处理

signal()是ANSI C信号处理函数&#xff0c;原型如下&#xff1a; #include <signal.h>typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); signal()将信号signum的处置设置为handler&#xff0c;该handler为SIG_IGN&#xff…

脊髓损伤治疗方法和需要那些营养

脊髓损伤作为一种严重的神经系统损伤&#xff0c;其治疗与康复一直是医学界关注的重点。在中医领域&#xff0c;针对脊髓损伤的治疗有着独特的理论和方法&#xff0c;旨在通过调节人体内部环境&#xff0c;促进受损神经的修复与再生。以下将从中医缓解方法与营养支持两个方面进…

Velero 快速上手:使用 Velero 实现 Kubernetes 集群备份与迁移

一、veloro 简介 Velero 是vmware开源的一个云原生的灾难恢复和迁移工具&#xff0c;它本身也是开源的,采用Go语言编写&#xff0c;可以安全的备份、恢复和迁移Kubernetes集群资源数据&#xff1b;Velero 是西班牙语意思是帆船&#xff0c;非常符合Kubernetes社区的命名风格&a…

【Python快速入门和实践017】Python常用脚本-根据文件后缀对其进行分类保存

一、功能介绍 这段代码的功能是将源文件夹中的文件按照它们的文件扩展名分类并移动到不同的子文件夹中。步骤如下&#xff1a; 定义函数&#xff1a;move_files_by_extension函数接收两个参数&#xff1a; source_folder&#xff1a;源文件夹路径。destination_folder&#xff…

LLM + GraphRAG技术,赋能教育培训行业数字化创新

随着人工智能大模型时代的到来&#xff0c;LLM大语言模型、RAG增强检索、Graph知识图谱、Prompt提示词工程等技术的发展日新月异&#xff0c;也让各行各业更加期待技术带来的产业变革。 比如&#xff0c;教育培训行业&#xff0c;教师数量相对有限、学生个体差异较大&#xff…

数据结构第一天

数据结构基础知识 1.1 什么是数据结构 数据结构就是数据的逻辑结构以及存储操作 (类似数据的运算) 数据结构就教会你一件事&#xff1a;如何更有效的存储数据 1.2 数据 数据&#xff1a;不再是单纯的数字&#xff0c;而是类似于集合的概念。 数据元素&#xff1a;是数据的基本单…

怎样卸载python

python卸载干净的具体操作步骤如下&#xff1a; 1、首先打开电脑左下角开始菜单&#xff0c;点击“运行”选项&#xff0c;输入“cmd”。 2、输入“python --version”&#xff0c;得到一个程序的版本&#xff0c;按回车键。 3、点击下图程序。 4、然后在该页面中点击“uninst…

【投融界-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

8 自动类型转换、强制类型转换、整数数据溢出与模运算、浮点数精度丢失、类型转换值截断

目录 1 自动类型转换&#xff08;隐式转换&#xff09; 1.1 运算过程中的自动类型转换 1.1.1 转换规则 1.1.2 转换方向 1.1.3 案例演示 1.2 赋值时的自动类型转换 1.2.1 案例演示 2 强制类型转换&#xff08;显式转换&#xff09; 2.1 介绍 2.2 转换格式 2.3 转换规…

案例分享—国外毛玻璃效果UI设计案例

毛玻璃效果通过模糊和半透明特性&#xff0c;显著增强了UI界面的层次感和深度&#xff0c;使得元素之间界限清晰&#xff0c;同时赋予界面一种现代、高级的质感&#xff0c;提升了整体视觉吸引力。 该效果不仅美观&#xff0c;还通过柔和的色彩和光照效果营造出清新、轻松的氛围…

回归分析系列1-多元线性回归

03 多元线性回归 3.1 简介 多元线性回归是简单线性回归的扩展&#xff0c;允许我们同时研究多个自变量对因变量的影响。多元回归模型可以表示为&#xff1a; 其中&#xff0c;x1,x2,…,xp是 p 个自变量&#xff0c;β0 是截距&#xff0c;β1,β2,…,βp是对应的回归系数&…

【STM32项目】在FreeRtos背景下的实战项目的实现过程(一)

个人主页~ 这篇文章是我亲身经历的&#xff0c;在做完一个项目之后总结的经验&#xff0c;虽然我没有将整个项目给放出来&#xff0c;因为这项目确实也是花了米让导师指导的&#xff0c;但是这个过程对于STM32的实战项目开发都是非常好用的&#xff0c;可以说按照这个过程&…

Layout 布局组件快速搭建

文章目录 设置主题样式变量封装公共布局组件封装 Logo 组件封装 Menu 菜单组件封装 Breadcrumb 面包屑组件封装 TabBar 标签栏组件封装 Main 内容区组件封装 Footer 底部组件封装 Theme 主题组件 经典布局水平布局响应式布局搭建 Layout 布局组件添加 Layout 路由配置启动项目 …

关于Idea中的debug模式只能执行一次的问题

希望文章能给到你启发和灵感&#xff5e; 如果觉得文章对你有帮助的话&#xff0c;点赞 关注 收藏 支持一下博主吧&#xff5e; 阅读指南 开篇说明一、基础环境说明1.1 硬件环境1.2 软件环境 二、为什么debug模式只有生效一次三、补充说明其他调试功能四、最后 开篇说明 记录一…