STM32F103C8T6中断配置
作为一个STM32新手,当初第一次接触中断配置的时候,真的是一头雾水。拿着STM32F103C8T6的最小系统板,想做个按键外部中断翻转LED的小实验,结果折腾了大半天,LED就是纹丝不动。后来一点点排查,才发现是自己漏了关键的步骤。今天就把STM32F103C8T6的中断配置经验分享出来,希望能帮到和我当初一样的新手。
一、STM32中断的分类
在STM32中,中断是指CPU在执行当前任务时,由于某种事件(外部或内部)发生,暂停当前任务,转而执行中断服务程序(ISR),完成后再返回原任务。STM32的中断主要分为以下几种类型:
硬中断硬中断是由外部硬件设备触发的中断信号,例如GPIO外部中断、定时器中断等。硬中断可以进一步分为以下两类:
不可屏蔽中断(NMI):用于处理紧急情况,例如RAM奇偶校验错误。NMI无法被屏蔽,CPU必须立即响应。可屏蔽中断(INTR):用于一般外设的中断请求,例如网卡或串口通信。CPU可以选择响应或忽略。
软中断软中断是由软件触发的中断,通常用于系统调用或访问受保护的资源。软中断的主要作用是将复杂的任务分为两部分:一部分在硬中断中快速处理,另一部分在软中断中完成,以提高系统实时性。
异常异常是CPU在运行过程中由于错误或特殊指令触发的中断,例如:
Fault:如除零错误或无效操作码。
Trap:如调试断点或溢出。
Abort:如总线错误或内存访问错误。
外部中断(EXTI)外部中断是由GPIO引脚的电平变化触发的中断。STM32支持多达23条外部中断线,包括GPIO中断、RTC闹钟事件、USB唤醒等。每条中断线可以独立配置触发方式(上升沿、下降沿或双边沿)。
系统中断系统中断是由内核触发的特殊中断,例如SysTick定时器中断、PendSV中断等。这些中断通常用于操作系统的任务调度或系统管理。
中断优先级STM32的中断优先级分为抢占优先级和响应优先级。抢占优先级决定中断是否可以打断其他中断,而响应优先级用于决定同级抢占优先级的中断处理顺序。
通过合理配置中断优先级和触发条件,可以实现高效的实时控制和资源管理。
二、先聊点基础:STM32中断的核心概念
在动手之前,先简单提两个核心玩意儿,不用死记硬背,知道是干啥的就行:
- NVIC(嵌套向量中断控制器):相当于STM32的“中断管家”,负责管理所有中断的优先级、开启/关闭中断通道,所有外设的中断最终都要经过它的调度。
- EXTI(外部中断/事件控制器):专门管外部引脚的中断,比如按键触发的中断,它能把GPIO引脚和NVIC连接起来,还能设置触发方式(上升沿、下降沿、双边沿)。
STM32F103C8T6的中断配置本质上就是:让外设(比如GPIO)产生中断请求,通过EXTI(外部中断)或外设自身的中断源,告诉NVIC,最后由NVIC触发对应的中断服务函数。
三、实战环境准备
先交代下我的实验环境,新手可以直接照搬:
- 硬件:STM32F103C8T6最小系统板、LED灯(接PB0)、按键(接PA0)
- 软件:Keil MDK5.36、STM32标准外设库3.5.0(F1系列,别用HAL库,新手先从标准库入手更易理解)
实验目标很简单:按下PA0的按键,触发外部中断,翻转PB0的LED灯状态。
四、库函数配置外部中断
我推荐新手先用库函数,不用跟寄存器死磕,先把流程跑通再说。
步骤1:开启相关时钟(最容易漏的一步!)
STM32的外设要工作,必须先开时钟,这是铁律。这里需要开三个时钟:
- GPIOA(PA1按键)、GPIOB(PB0 LED)的时钟(APB2总线)
- AFIO(复用功能IO)的时钟(重点!外部中断的引脚映射必须靠AFIO,不开这个时钟,中断绝对触发不了)
代码如下:
// 1. 开启时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);步骤2:配置GPIO引脚模式
PA1作为按键输入,我选上拉输入(这样按键没按下时是高电平,按下是低电平);PB0作为LED输出,选推挽输出。
代码:
// 2. 配置GPIOGPIO_InitTypeDef GPIO_InitStructure;// PA1配置(根据实际电路选择模式)// 如果按键接在PA0和GND之间,使用上拉输入GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;// 上拉输入GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;// 输入模式可忽略SpeedGPIO_Init(GPIOA,&GPIO_InitStructure);// PB0配置GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);GPIO_SetBits(GPIOB,GPIO_Pin_0);// 初始高电平GPIO_InitTypeDef GPIO_InitStructure;步骤3:配置AFIO的外部中断引脚映射
STM32的EXTI线和GPIO引脚是“一对多”的关系,比如EXTI0线可以对应GPIOA0、GPIOB0、GPIOC0等,但需要通过AFIO指定具体用哪个GPIO的引脚。
我们要把PA1和EXTI1线绑定,用库函数GPIO_EXTILineConfig就行:
// 3. 配置AFIO映射GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);步骤4:配置EXTI中断参数
这里要设置中断的触发方式、开启中断线:
// 4. 配置EXTIEXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line=EXTI_Line1;EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;// 下降沿触发EXTI_InitStructure.EXTI_LineCmd=ENABLE;EXTI_Init(&EXTI_InitStructure);步骤5:配置NVIC中断优先级
NVIC是“中断管家”,必须告诉它:哪个中断通道要开启?优先级是多少?
首先要配置优先级分组(整个程序只能调用一次!),我选分组2(2位抢占优先级,2位响应优先级),然后配置EXTI0的优先级:
// 5. 配置NVICNVIC_InitTypeDef NVIC_InitStructure;NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_Init(&NVIC_InitStructure);步骤6:编写中断服务函数(必须清标志位!)
中断服务函数的名字是固定的,要和启动文件(startup_stm32f10x_md.s)里的中断向量表一致,比如EXTI0的服务函数名就是EXTI0_IRQHandler。
重点中的重点:执行完中断操作后,必须清除中断标志位,否则MCU会认为中断还在触发,一直进入服务函数,导致程序卡死!
// 中断服务函数定义voidEXTI1_IRQHandler(void){if(EXTI_GetITStatus(EXTI_Line1)!=RESET){// 简单延时消抖(可选,根据需求)// for(int i = 0; i < 10000; i++);// 读取按键状态if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==0){// 翻转LED状态if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_0))GPIO_ResetBits(GPIOB,GPIO_Pin_0);elseGPIO_SetBits(GPIOB,GPIO_Pin_0);}EXTI_ClearITPendingBit(EXTI_Line1);// 必须清除中断标志}}步骤7:整合main函数
把上面的代码整合到main函数里,死循环里啥都不用做,等待中断即可:
#include"stm32f10x.h"// STM32标准外设库头文件// 中断服务函数声明voidEXTI1_IRQHandler(void);/** * @brief 主函数 * @param 无 * @retval int 程序返回值(通常不会返回) */intmain(void){// 系统初始化(配置系统时钟、Flash延迟等)SystemInit();/******************** 第一步:开启外设时钟 ********************/// 使能GPIOA、GPIOB和AFIO(复用功能IO)的时钟// 注意:STM32外设使用前必须开启对应的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);/******************** 第二步:配置GPIO引脚 ********************/GPIO_InitTypeDef GPIO_InitStructure;// GPIO初始化结构体// 配置PA1为输入模式(连接按键)// 假设按键一端接PA1,另一端接GND,使用内部上拉电阻GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;// 选择引脚1GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;// 上拉输入模式GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;// 输入模式下此参数可忽略GPIO_Init(GPIOA,&GPIO_InitStructure);// 应用配置到GPIOA// 配置PB0为输出模式(连接LED)GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;// 选择引脚0GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;// 推挽输出模式GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;// 输出速度50MHzGPIO_Init(GPIOB,&GPIO_InitStructure);// 应用配置到GPIOB// 设置PB0初始状态为高电平(LED灭,假设低电平点亮LED)GPIO_SetBits(GPIOB,GPIO_Pin_0);/******************** 第三步:配置AFIO映射 ********************/// 将GPIOA的Pin1映射到外部中断线1// 注意:每个外部中断线可以映射到多个GPIO引脚,需通过AFIO选择具体引脚GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);/******************** 第四步:配置外部中断(EXTI) ********************/EXTI_InitTypeDef EXTI_InitStructure;// EXTI初始化结构体EXTI_InitStructure.EXTI_Line=EXTI_Line1;// 选择外部中断线1EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;// 中断模式(非事件模式)EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;// 下降沿触发(按键按下时产生下降沿)EXTI_InitStructure.EXTI_LineCmd=ENABLE;// 使能该中断线EXTI_Init(&EXTI_InitStructure);// 应用配置/******************** 第五步:配置嵌套向量中断控制器(NVIC) ********************/NVIC_InitTypeDef NVIC_InitStructure;// NVIC初始化结构体// 配置中断优先级分组(2位抢占优先级,2位响应优先级)// 注意:整个系统只能调用一次优先级分组函数NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 配置EXTI1中断通道NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn;// 中断通道:EXTI线1NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;// 抢占优先级为2NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;// 响应优先级为2NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;// 使能该中断通道NVIC_Init(&NVIC_InitStructure);// 应用配置/******************** 第六步:主循环 ********************/while(1){// 主循环可以添加其他后台任务// 中断处理会在中断服务函数中进行,不会影响主循环运行// 可以在这里添加低功耗模式、系统状态监测等代码}}/** * @brief EXTI线1中断服务函数 * @param 无 * @retval 无 * @note 当PA1引脚检测到下降沿时,此函数会被自动调用 */voidEXTI1_IRQHandler(void){// 检查是否发生了EXTI线1中断(中断标志位是否置位)if(EXTI_GetITStatus(EXTI_Line1)!=RESET){// ---------- 可选:按键消抖处理 ----------// 机械按键在按下/释放时会产生抖动,可能导致多次触发// 简单的软件消抖方法:延时后再次检测引脚状态// for(int i = 0; i < 10000; i++); // 简单延时// ---------- 确认按键状态 ----------// 再次读取PA1引脚状态,确保是有效的按键按下// 上拉模式下,按键按下时引脚为低电平(0)if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==0){// ---------- 执行中断处理逻辑 ----------// 读取PB0当前输出状态并翻转if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_0)){// 当前为高电平,设置为低电平(点亮LED)GPIO_ResetBits(GPIOB,GPIO_Pin_0);}else{// 当前为低电平,设置为高电平(熄灭LED)GPIO_SetBits(GPIOB,GPIO_Pin_0);}// 可选:添加按键释放等待,避免单次按下触发多次// while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0);}// ---------- 清除中断标志位 ----------// 非常重要!如果不清除标志位,会连续触发中断EXTI_ClearITPendingBit(EXTI_Line1);}}五、容易踩的坑
- 忘记开启AFIO时钟:这是我当初最致命的坑,配置完所有步骤,按键按烂了LED都不亮,后来查手册才发现外部中断映射需要AFIO支持。
- 没清中断标志位:第一次成功触发中断后,LED闪了一下就卡住了,原因是没清标志位,MCU一直重复进入中断。
- GPIO模式配错:比如把PA0配成浮空输入,又没外接上拉电阻,导致引脚电平不稳定,中断乱触发。
- 优先级分组调用多次:
NVIC_PriorityGroupConfig只能调用一次,否则会导致优先级配置混乱。
六、总结:中断配置的核心逻辑
其实不管是外部中断、定时器中断还是串口中断,配置流程都大同小异:
开时钟 → 配外设 → 配中断源(EXTI/外设自身) → 配NVIC → 写服务函数(清标志位)
新手只要把这个流程刻在脑子里,再结合实际外设的特点稍作调整,就能搞定大部分中断配置了。
最后说句心里话:学习STM32,光看教程没用,一定要动手。遇到问题别慌,查手册、单步调试、逐行排查,那些踩过的坑,最后都会变成你的经验。
江协科技中断学习笔记
对射式传感器的应用
main.c 代码如下:
#include"stm32f10x.h"// Device header#include"Delay.h"#include"OLED.h"#include"CountSensor.h"intmain(){OLED_Init();CountSensor_Init();OLED_ShowCHinese(0,0,0);//计OLED_ShowCHinese(0,16,1);//次OLED_ShowString(1,5,":");while(1){OLED_ShowNum(1,6,CountSensor_Get(),5);}}CountSensor.c 代码如下:
#include"stm32f10x.h"// Device headeruint16_tCountSensor_Count;voidCountSensor_Init(void){/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启GPIOB的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO的时钟,外部中断必须开启AFIO的时钟/*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);//将PB14引脚初始化为上拉输入/*AFIO选择中断引脚*///将外部中断的14号线映射到GPIOB,即选择PB14为外部中断引脚GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);/*EXTI初始化*/EXTI_InitTypeDef EXTI_InitStructure;//定义结构体变量EXTI_InitStructure.EXTI_Line=EXTI_Line14;//选择配置外部中断的14号线EXTI_InitStructure.EXTI_LineCmd=ENABLE;//指定外部中断线使能EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//指定外部中断线为中断模式EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;//指定外部中断线为下降沿触发EXTI_Init(&EXTI_InitStructure);//将结构体变量交给EXTI_Init,配置EXTI外设/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置NVIC为分组2//即抢占优先级范围:0~3,响应优先级范围:0~3//此分组配置在整个工程中仅需调用一次//若有多个中断,可以把此代码放在main函数内,while循环之前//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure;//定义结构体变量NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;//选择配置NVIC的EXTI15_10线NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure);//将结构体变量交给NVIC_Init,配置NVIC外设}/** * 函 数:获取计数传感器的计数值 * 参 数:无 * 返 回 值:计数值,范围:0~65535 */uint16_tCountSensor_Get(void){returnCountSensor_Count;}voidEXTI15_10_IRQHandler(void){if(EXTI_GetFlagStatus(EXTI_Line14)==SET){/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0)//判断是否是外部中断14号线触发的中断{CountSensor_Count++;//计数值自增一次}EXTI_ClearITPendingBit(EXTI_Line14);//清除外部中断14号线的中断标志位//中断标志位必须清除//否则中断将连续不断地触发,导致主程序卡死}}CountSensor.h 代码如下:
#ifndef__COUNT_SENSOR_H#define__COUNT_SENSOR_HvoidCountSensor_Init(void);uint16_tCountSensor_Get(void);#endif旋转编码器计数
main.c 代码如下:
#include"stm32f10x.h"// Device header#include"Delay.h"#include"OLED.h"#include"Encoder.h"int16_tNum;intmain(){/*模块初始化*/OLED_Init();//OLED初始化Encoder_Init();//旋转编码器初始化OLED_ShowCHinese(0,0,0);//计OLED_ShowCHinese(0,16,1);//次OLED_ShowString(1,5,":");while(1){Num+=Encoder_Get();OLED_ShowSignedNum(1,6,Num,5);}}Encoder.c 代码如下:
#include"stm32f10x.h"// Device headerint16_tEncoder_Count;voidEncoder_Init(void){/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启GPIOB的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO的时钟,外部中断必须开启AFIO的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);//将PB14引脚初始化为上拉输入/*AFIO选择中断引脚*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);//将外部中断的0号线映射到GPIOB,即选择PB0为外部中断引脚GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);//将外部中断的1号线映射到GPIOB,即选择PB1为外部中断引脚/*EXTI初始化*/EXTI_InitTypeDef EXTI_InitStructure;//定义结构体变量EXTI_InitStructure.EXTI_Line=EXTI_Line0|EXTI_Line1;//选择配置外部中断的14号线EXTI_InitStructure.EXTI_LineCmd=ENABLE;//指定外部中断线使能EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//指定外部中断线为中断模式EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;//指定外部中断线为下降沿触发EXTI_Init(&EXTI_InitStructure);//将结构体变量交给EXTI_Init,配置EXTI外设/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置NVIC为分组2//即抢占优先级范围:0~3,响应优先级范围:0~3//此分组配置在整个工程中仅需调用一次//若有多个中断,可以把此代码放在main函数内,while循环之前//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure;//定义结构体变量NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;//选择配置NVIC的EXTI0线NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure);//将结构体变量交给NVIC_Init,配置NVIC外设NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn;//选择配置NVIC的EXTI1线NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;//指定NVIC线路的响应优先级为2NVIC_Init(&NVIC_InitStructure);//将结构体变量交给NVIC_Init,配置NVIC外设}int16_tEncoder_Get(void){/*使用Temp变量作为中继,目的是返回Encoder_Count后将其清零*//*在这里,也可以直接返回Encoder_Count 但这样就不是获取增量值的操作方法了 也可以实现功能,只是思路不一样*/int16_tTemp;Temp=Encoder_Count;Encoder_Count=0;returnTemp;}/** * 函 数:EXTI0外部中断函数 * 参 数:无 * 返 回 值:无 * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行 * 函数名为预留的指定名称,可以从启动文件复制 * 请确保函数名正确,不能有任何差异,否则中断函数将不能进入 */voidEXTI0_IRQHandler(void){if(EXTI_GetITStatus(EXTI_Line0)==SET)//判断是否是外部中断0号线触发的中断{/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0){//PB0的下降沿触发中断,此时检测另一相PB1的电平,目的是判断旋转方向if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0){Encoder_Count--;//此方向定义为反转,计数变量自减}}EXTI_ClearITPendingBit(EXTI_Line0);//清除外部中断0号线的中断标志位//中断标志位必须清除//否则中断将连续不断地触发,导致主程序卡死}}/** * 函 数:EXTI1外部中断函数 * 参 数:无 * 返 回 值:无 * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行 * 函数名为预留的指定名称,可以从启动文件复制 * 请确保函数名正确,不能有任何差异,否则中断函数将不能进入 */voidEXTI1_IRQHandler(void){if(EXTI_GetITStatus(EXTI_Line1)==SET)//判断是否是外部中断1号线触发的中断{/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0){//PB1的下降沿触发中断,此时检测另一相PB0的电平,目的是判断旋转方向if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0){Encoder_Count++;//此方向定义为正转,计数变量自增}}EXTI_ClearITPendingBit(EXTI_Line1);//清除外部中断1号线的中断标志位//中断标志位必须清除//否则中断将连续不断地触发,导致主程序卡死}}Encoder.h 代码如下:
#ifndef__ENCODER_H#define__ENCODER_HvoidEncoder_Init(void);int16_tEncoder_Get(void);#endif