5.1 ~ 5.2 EXTI外部中断

视频选集(12/50)[5-2] 对射式红外传感器计次&旋转编码器计次

0. 库函数

0.1 AFIO配置

/**
  * @brief  配置AFIO数据选择器,以选择想要作为中断引脚的GPIO端口
  * @param  GPIO_PortSource: 选择某个GPIO作为中断源
  * @param  GPIO_PinSource: 指定端口
  * @retval None
  */
  void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

0.2 EXTI配置

/**
  * @brief  根据 EXTI_InitStruct 中指定的参数初始化 EXTI 外设。
  * @param  指向 EXTI_InitTypeDef 结构体的指针,该结构体包含 EXTI 的配置信息
  * @retval None
  */
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
typedef struct
{
  uint32_t EXTI_Line;               // 指定要配置的中断线
   
  EXTIMode_TypeDef EXTI_Mode;       // 指定EXTI模式

  EXTITrigger_TypeDef EXTI_Trigger; // 指定触发信号的有效边沿

  FunctionalState EXTI_LineCmd;     // 指定中断线的新状态
}EXTI_InitTypeDef;
/** @defgroup EXTI_Lines 
  * @{
  */

#define EXTI_Line0       ((uint32_t)0x00001)  /*!< External interrupt line 0 */
#define EXTI_Line1       ((uint32_t)0x00002)  /*!< External interrupt line 1 */
#define EXTI_Line2       ((uint32_t)0x00004)  /*!< External interrupt line 2 */
#define EXTI_Line3       ((uint32_t)0x00008)  /*!< External interrupt line 3 */
#define EXTI_Line4       ((uint32_t)0x00010)  /*!< External interrupt line 4 */
#define EXTI_Line5       ((uint32_t)0x00020)  /*!< External interrupt line 5 */
#define EXTI_Line6       ((uint32_t)0x00040)  /*!< External interrupt line 6 */
#define EXTI_Line7       ((uint32_t)0x00080)  /*!< External interrupt line 7 */
#define EXTI_Line8       ((uint32_t)0x00100)  /*!< External interrupt line 8 */
#define EXTI_Line9       ((uint32_t)0x00200)  /*!< External interrupt line 9 */
#define EXTI_Line10      ((uint32_t)0x00400)  /*!< External interrupt line 10 */
#define EXTI_Line11      ((uint32_t)0x00800)  /*!< External interrupt line 11 */
#define EXTI_Line12      ((uint32_t)0x01000)  /*!< External interrupt line 12 */
#define EXTI_Line13      ((uint32_t)0x02000)  /*!< External interrupt line 13 */
#define EXTI_Line14      ((uint32_t)0x04000)  /*!< External interrupt line 14 */
#define EXTI_Line15      ((uint32_t)0x08000)  /*!< External interrupt line 15 */
#define EXTI_Line16      ((uint32_t)0x10000)  /*!< External interrupt line 16 Connected to the PVD Output */
#define EXTI_Line17      ((uint32_t)0x20000)  /*!< External interrupt line 17 Connected to the RTC Alarm event */
#define EXTI_Line18      ((uint32_t)0x40000)  /*!< External interrupt line 18 Connected to the USB Device/USB OTG FS
                                                   Wakeup from suspend event */                                    
#define EXTI_Line19      ((uint32_t)0x80000)  /*!< External interrupt line 19 Connected to the Ethernet Wakeup event */
typedef enum
{
  EXTI_Mode_Interrupt = 0x00,
  EXTI_Mode_Event = 0x04
}EXTIMode_TypeDef;
typedef enum
{
  EXTI_Trigger_Rising = 0x08,
  EXTI_Trigger_Falling = 0x0C,  
  EXTI_Trigger_Rising_Falling = 0x10
}EXTITrigger_TypeDef;
// 在中断函数中查看和清除标志位
/**
  * @brief  检查指定的中断线是否被开启
  * @param  EXTI_Line: 指定要检查的中断线
  * @retval 中断线的状态 (SET or RESET).
  */
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);

/**
  * @brief  清除 EXTI 的标志位
  * @param  EXTI_Line: 指定要清除的中断线
  * @retval None
  */
void EXTI_ClearITPendingBit(uint32_t EXTI_Line)
// 在主程序中查看和清除标志位
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
void EXTI_ClearFlag(uint32_t EXTI_Line);

本质上,这4个函数都是对状态寄存器的读写。上面的两个函数只能读取于中断有关的标志位,并且对中断是否允许做出了判断;下面的两个函数只是一般的读写标志位,没有额外的处理,能不能触发中断的标志位都能读取

0.3 NVIC配置

/**
  * @brief  配置优先级分组:抢占优先级和响应优先级。
  * @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);
/**
  * @brief  根据 NVIC_InitStruct 中指定的参数初始化 NVIC 外设。
  * @param  NVIC_InitStruct: 指向 NVIC_InitTypeDef 结构体的指针,该结构体包含指定 NVIC 外设的配置信息。
  * @retval None
  */
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
typedef struct
{
  uint8_t NVIC_IRQChannel;                    // 指定要启用或禁用的 IRQ 信道

  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;

1. 对射式红外传感器计次

1.1器件

  • STM32最小系统板
  • ST-LINK V2下载器
  • OLED显示屏
  • 对射式红外传感器

1.2 实现目标

遮挡模块槽一次,计数一次,累积计数实时显示在OLED屏幕上

1.3 接线图

注.我没有按照图中的IO口来接LED和按键,具体看程序代码

5-1 对射式红外传感器计次

1.4 程序

注.OLED部分我是直接用的资料里的程序,所以没有写笔记

main.c文件如下:

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Sensor_Dinf.h"

/*------------------------------主函数------------------------------*/
int main(void)
{
	/*-----硬件初始化-----*/
	OLED_Init();		// OLED初始化
	Sensor_Dinf_Init(); // 对射式红外传感器初始化
	
	/*-----OLED显示-----*/
	OLED_ShowString(1, 1, "COUNT:");
	while(1){
		OLED_ShowNum(1, 8, GetCOUNT(), 3);
	}
}

Sensor_Dinf.c文件如下:

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

uint8_t COUNT = 0; // 存储计数

/*------------------------------对射式红外传感器初始化函数------------------------------
	@DO A1
*/
void Sensor_Dinf_Init(void)
{
	/*-----变量声明-----*/
	GPIO_InitTypeDef GPIO_InitStruct; // 定义包含配置参数的结构体变量
	EXTI_InitTypeDef EXTI_InitStruc;
	NVIC_InitTypeDef NVIC_InitStruct;
	
	/*-----时钟使能-----*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 开启GPIOA的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 开启AFIO的时钟
	// EXTI和NVIC的时钟是一直开启的,不需要手动使能
	
	/*-----GPIO配置-----*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入:悬空时默认高电平
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化A1端口
	
	/*-----AFIO配置-----*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1); // 将PA1引脚连接到EXTI1中断线路
	
	/*-----EXTI配置-----*/
	EXTI_InitStruc.EXTI_Line = EXTI_Line1; // 指定EXTI1中断线路
	EXTI_InitStruc.EXTI_LineCmd = ENABLE; // 开启中断
	EXTI_InitStruc.EXTI_Mode = EXTI_Mode_Interrupt; // 选择中断模式
	EXTI_InitStruc.EXTI_Trigger = EXTI_Trigger_Falling; // 选择下降沿触发
	EXTI_Init(&EXTI_InitStruc); // 初始化EXTI1
	
	/*-----NVIC配置-----*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 优先级分组:2位抢占,2位响应
	NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn; // 指定通道
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 该工程中断只有一个,所以抢占和响应优先级可以随意配置
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStruct);
}


/*------------------------------读取计数值函数------------------------------
	@Return COUNT
*/
uint8_t GetCOUNT(void)
{
	return COUNT;
}


/*------------------------------EXTI1外部中断函数------------------------------
	@Brief 光线被遮挡时发生由低到高的跳变,遮挡物拿开时发生由高到低的跳变,进入中断。计次加1
	@Pin A1
*/
void EXTI1_IRQHandler(void)
{
	// Delay_ms(500); // 中断里面不要加延时。此处仅为测试
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
	{
		COUNT ++;
	}	
	
	EXTI_ClearITPendingBit(EXTI_Line1);// 清除中断标志位
}

Sensor_Dinf.h文件如下:

#ifndef __SENSOR_DINF_H
#define __SENSOR_DINF_H

void Sensor_Dinf_Init(void);
uint8_t GetCOUNT(void);

#endif

2. 旋转编码器计次

2.1 器件

  • STM32最小系统板
  • ST-LINK V2下载器
  • OLED显示屏
  • 旋转编码器

2.2 实现目标

  • 旋转编码器顺时针旋转时,编码盘每转过一格,计数值+1
  • 旋转编码器逆时针旋转时,编码盘每转过一格,计数值-1
  • 累计计数值为有符号数,可正可负,其数值实时显示在OLED屏上

2.3 接线图

5-2 旋转编码器计次

2.4 程序

main.c文件如下:

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Encoder.h"

/*------------------------------主函数------------------------------*/
int main(void)
{
	/*-----硬件初始化-----*/
	OLED_Init();		// OLED初始化
	Encoder_Init(); // 旋转编码器初始化
	
	/*-----OLED显示-----*/
	OLED_ShowString(1, 1, "COUNT:");
	while(1){
		OLED_ShowSignedNum(1, 8, GetCOUNT(), 5);
	}
}

Encoder.c文件如下:

#include "stm32f10x.h"                  // Device header

int16_t COUNT = 0; // 存储旋转编码器的计数值

/*------------------------------旋转编码器初始化函数------------------------------
	@A B0
	@B B1
*/
void Encoder_Init(void)
{
	/*-----变量声明-----*/
	GPIO_InitTypeDef GPIO_InitStruct; // 定义包含配置参数的结构体变量
	EXTI_InitTypeDef EXTI_InitStruc;
	NVIC_InitTypeDef NVIC_InitStruct;
	
	/*-----时钟使能-----*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 开启AFIO的时钟
	// EXTI和NVIC的时钟是一直开启的,不需要手动使能
	
	/*-----GPIO配置-----*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入:悬空时默认高电平
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct); // 初始化B0、B1端口
	
	/*-----AFIO配置-----*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0); // 将PB0引脚连接到EXTI0中断线路
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1); // 将PB1引脚连接到EXTI1中断线路
	
	/*-----EXTI配置-----*/
	EXTI_InitStruc.EXTI_Line = EXTI_Line0 | EXTI_Line1; // 指定EXTI0、1中断线路
	EXTI_InitStruc.EXTI_LineCmd = ENABLE; // 开启中断
	EXTI_InitStruc.EXTI_Mode = EXTI_Mode_Interrupt; // 选择中断模式
	EXTI_InitStruc.EXTI_Trigger = EXTI_Trigger_Falling; // 选择下降沿触发
	EXTI_Init(&EXTI_InitStruc); // 初始化EXTI0、1
	
	/*-----NVIC配置-----*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 优先级分组:2位抢占,2位响应
	
	NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn; // 指定通道
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStruct);
	
	NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn; // 指定通道
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
	NVIC_Init(&NVIC_InitStruct);	
}


/*------------------------------获取计数值函数------------------------------
	@Return COUNT
*/
int16_t GetCOUNT(void)
{
	return COUNT;
}


/*------------------------------EXTI0外部中断函数------------------------------
	@Brief A相下降沿进入该中断,若此时B相为高电平,旋转编码器正转,计数增加
	@Pin B0
*/
void EXTI0_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line0) == SET){
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1)){
			COUNT ++;
		}
		EXTI_ClearITPendingBit(EXTI_Line0);
	}
}


/*------------------------------EXTI1外部中断函数------------------------------
	@Brief B相下降沿进入该中断,若此时A相为高电平,旋转编码器反转,计数减少
	@Pin B1
*/

void EXTI1_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line1) == SET){
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0)){
			COUNT --;
		}
		EXTI_ClearITPendingBit(EXTI_Line1);
	}
}

Encoder.h文件如下:

#ifndef __ENCODER_H
#define __ENCODER_H

void Encoder_Init(void);
int16_t GetCOUNT(void);

#endif

posted @ 2025-08-08 22:09  LI,Yi-han  阅读(11)  评论(0)    收藏  举报