4.0EXTI外部中断

中断 = CPU正在干自己的事情,突然发生了一个需要处理的事件,CPU暂时停下当前工作,去处理这个事件,处理完再回来。




68个可屏蔽中断通道是什么
STM32里面有很多东西都可以产生中断。
1. 按键/外部引脚
GPIO外部中断:
EXTI
PA0上的按键按下
↓
EXTI产生中断
↓
CPU处理
2. 定时器
定时器计时到指定时间,可以产生中断。
TIM2
↓
计时1000ms
↓
产生中断
↓
执行中断函数
这样你就不用:
Delay_ms(1000);
一直傻等了。
3. 串口
比如 USART 收到一个字节:
USART
↓
收到数据
↓
产生中断
↓
CPU处理
例如电脑给 STM32 发:
'A'
STM32可以在收到 A 的时候自动进入串口中断。
4. ADC
ADC转换完成以后:
ADC开始转换
↓
转换完成
↓
产生中断
↓
CPU处理
所以你看到:
EXTI
TIM
ADC
USART
SPI
I2C
RTC
...
不同的硬件事件,都可以“叫醒”CPU,让CPU去处理。
什么叫“可屏蔽中断”?
可屏蔽中断 = 可以通过配置,让CPU暂时不理它。
按键中断
你可以配置:
允许 → 按键来了,处理
禁止 → 按键来了,不处理
所以叫:
可屏蔽(Maskable)
你可以把它理解成:
“这个电话我可以接,也可以先不接。”
什么是 NVIC?
这个就是你这张图第二条最重要的东西。
使用NVIC统一管理中断
NVIC 全名:
Nested Vectored Interrupt Controller
中文一般叫:
嵌套向量中断控制器
NVIC就是STM32里面专门负责管理中断的“大管家”。
为什么需要 NVIC?
假设同时发生:
按键中断
定时器中断
串口中断
CPU不可能三个同时执行。
所以必须决定:
谁先处理?
谁后处理?
这就是中断优先级。
什么叫“优先级”?
假设:
按键中断:优先级 2
定时器中断:优先级 1
串口中断:优先级 3
一般可以理解成:
1:最高
2:其次
3:最低
那么同时来的时候:
定时器
↓
先处理
↓
按键
↓
再处理
↓
串口
什么叫“嵌套”?
正在执行普通程序
↓
低优先级中断来了
↓
进入低优先级中断
↓
突然又来了一个更高优先级中断
↓
暂停当前这个低优先级中断
↓
去执行高优先级中断
↓
高优先级中断结束
↓
回来继续低优先级中断
↓
结束
↓
回到主程序
这就是:
中断嵌套
可以理解成:
主程序
└── 中断A
└── 中断B
每个中断通道都拥有16个可编程的优先级
这意味着:
优先级
0
1
2
3
...
15
一共16级。
但这里有一个非常容易踩坑的地方:
数字越小,优先级越高
也就是说:
0 > 1 > 2 > 3
不是:
15最高
而是:
0最高
15最低
可对优先级进行分组,进一步设置抢占优先级和响应优先级
这句话非常重要。
STM32的中断优先级实际上可以进一步拆成两部分:
抢占优先级
+
响应优先级
你现在可以先建立这张脑图:
STM32中断
│
┌──────────┴──────────┐
↓ ↓
中断来源 NVIC
│ │
┌──────┼──────┐ 管理中断
↓ ↓ ↓ │
EXTI TIM USART ↓
↓ ↓ ↓ 优先级
按键 定时器 串口 │
┌─┴─┐
↓ ↓
抢占 响应
外设发生事件
↓
产生中断请求
↓
NVIC管理
↓
判断优先级
↓
CPU响应中断
↓
进入中断服务函数
↓
处理事情
↓
清除中断标志
↓
返回原来的程序
STM32的优先级总空间是4 bit,因此总共有 24=16 种优先级组合;“优先级分组”只是把这4 bit拆分成抢占优先级和响应优先级,两者的位数加起来仍然是4。


EXTI 是什么
EXTI(External Interrupt/Event Controller,外部中断 / 事件控制器),是 STM32 单片机里的一个外设。 它的核心作用是监测指定 GPIO 引脚的电平变化,当引脚电平发生符合要求的跳变时,不需要 CPU 循环查询引脚状态,就能主动向 CPU 发起中断请求,让 CPU 暂停当前主程序,去执行对应的中断处理函数;也可以不经过 CPU,直接触发其他硬件外设的动作。
简单说:它是 GPIO 的 “电平变化报警器”,用来替代 CPU 轮询,大幅节省 CPU 资源。
EXTI 基本结构拆解
1. 输入层:GPIO 端口
-
所有 GPIO 端口(GPIOA、GPIOB、GPIOC……)的 16 个引脚(Pin0~Pin15),都可以作为 EXTI 的信号输入源。
-
关键限制:相同引脚编号不能同时使用。比如 PA0、PB0、PC0 不能同时触发 EXTI0 中断,同一时刻只能从所有端口的 0 号引脚里,选一个接入 EXTI0 通道。
2. 路由层:AFIO 中断引脚选择
AFIO(复用功能 IO)在这里的核心功能是中断引脚路由(多选一开关):
-
它接收所有 GPIO 端口的引脚信号,对每个引脚编号(Pin0~Pin15)做一次 “二选一 / 多选一”:比如 Pin0 通道,选择是 PA0、PB0 还是 PC0 接入后续的 EXTI。
-
输入是多组 GPIO 的 16 根引脚线,输出仍然是 16 根线,对应 EXTI 的 16 个 GPIO 中断通道。
3. 核心层:EXTI 边沿检测及控制
这是 EXTI 外设的本体,主要完成 3 件事:
-
边沿检测与触发配置 可以配置触发规则:上升沿(电平从低变高)、下降沿(电平从高变低)、双边沿(任意跳变都触发)、软件触发(代码手动触发,无需外部信号)。只有符合配置的电平变化,才会触发后续动作。
-
通道管理 一共管理 20 个通道:
-
前 16 个通道(EXTI0 ~ EXTI15):对应 GPIO 的 Pin0~Pin15,是最常用的外部引脚中断。
-
后 4 个内部通道:PVD(电源电压监测)、RTC(实时时钟闹钟)、USB 唤醒、ETH(以太网唤醒),属于芯片内部外设的唤醒 / 中断信号,也挂载在 EXTI 上。
-
-
双模式输出
-
中断模式:把中断信号发给 NVIC,最终打断 CPU,执行中断服务函数(最常用)。
-
事件模式:不经过 CPU,直接输出一个硬件事件脉冲给其他外设(比如触发 DMA、启动 ADC),响应更快、不占用 CPU,适合低功耗场景。
-
4. 裁决层:NVIC(嵌套向量中断控制器)
-
接收 EXTI 发来的中断请求,负责中断优先级配置、中断屏蔽、最终裁决是否打断 CPU 当前程序。
-
注意一个硬件设计细节:
-
EXTI0 ~ EXTI4:每个通道都有独立的 NVIC 中断通道,各自对应一个中断函数。
-
EXTI5 ~ EXTI9:共用一个 NVIC 中断通道(图中 EXTI9_5)。
-
EXTI10 ~ EXTI15:共用一个 NVIC 中断通道(图中 EXTI15_10)。 共用通道的引脚触发中断时,会进入同一个中断服务函数,需要在函数里通过标志位手动判断是哪个引脚触发的。
-
最下方的 “其它外设”,则对应事件模式的输出,EXTI 直接把事件信号发给其他硬件外设,不经过 CPU 和 NVIC。
EXTI 核心特性
-
触发方式:支持上升沿触发、下降沿触发、双边沿触发、软件触发四种。
-
引脚覆盖:所有 GPIO 引脚都能配置为外部中断,但同编号 Pin 不能同时使用。
-
通道总数:16 个 GPIO 引脚通道 + 4 个内部外设通道,共 20 个通道。
-
两种响应方式
-
中断响应:CPU 跳转执行中断函数,适合需要软件处理的场景。
-
事件响应:纯硬件联动,不占用 CPU,适合高速、低功耗的硬件触发场景。
-
GPIO 引脚电平发生跳变 → AFIO 路由将信号传入对应 EXTI 通道 → EXTI 检测到符合配置的边沿 → 置起中断标志位,向 NVIC 发送中断请求 → NVIC 根据优先级判断是否响应 → CPU 暂停主程序,跳转到对应中断服务函数执行 → 执行完毕后返回主程序。


一、AFIO 复用 IO 口
1. AFIO 是什么
AFIO 全称 Alternate Function I/O(复用功能输入输出端口),是 STM32 中负责引脚功能分配的 “硬件开关矩阵”。 STM32 的单个物理引脚通常同时具备 GPIO、串口、定时器、SPI 等多种功能,AFIO 的核心作用就是决定引脚最终连接到哪一个外设功能,同时负责外部中断的引脚路由选择。
2. AFIO 的两大核心任务
对应图中文字说明:
-
复用功能引脚重映射 很多外设的引脚有默认位置,通过 AFIO 的重映射寄存器,可以把外设的引脚切换到另一组备用引脚。比如 USART1 默认使用 PA9/PA10,开启重映射后可以换到 PB6/PB7,方便 PCB 布线和引脚资源调配。
-
中断引脚选择(EXTI 路由) 这是和 EXTI 强相关的核心功能,对应右侧的「外部中断通用 I/O 映像」框图。
3. 右侧框图:外部中断引脚选择逻辑
右侧的图直观展示了 AFIO 如何把 GPIO 连接到 EXTI 通道:
-
一共 16 个 EXTI 通道(EXTI0 ~ EXTI15),每个通道固定对应一个引脚编号(Pin0 ~ Pin15)。
-
每个 EXTIx 通道都是一个多选一多路开关:
-
以 EXTI0 为例,它的输入端是 PA0、PB0、PC0、PD0、PE0、PF0、PG0 所有端口的 0 号引脚。
-
通过
AFIO_EXTICR1寄存器的EXTI0[3:0]这 4 位配置,选择其中一个端口的 0 号引脚,接入到 EXTI0 通道。
-
-
以此类推:EXTI1 由
AFIO_EXTICR1的对应位配置,EXTI15 由AFIO_EXTICR4的对应位配置。
关键结论:这就是 “相同 Pin 号不能同时触发中断” 的硬件原因 —— 每个 EXTIx 通道同一时刻只能选一个端口的对应引脚,比如选了 PA0 做外部中断,PB0、PC0 就不能同时使用 EXTI0。
二、EXTI 内部详细框图
这张图是 EXTI 外设的完整内部硬件结构,我们按信号从右到左的流向拆解,整体分为输入检测、中断输出、事件输出三大部分。
1. 输入级:边沿检测与触发配置
最右侧是 20 根输入线(16 路 GPIO + 4 路内部外设),信号从这里进入 EXTI:
-
边沿检测电路:负责识别引脚的电平跳变。
-
上升沿触发选择寄存器:配置哪些通道开启「低电平→高电平」的上升沿触发。
-
下降沿触发选择寄存器:配置哪些通道开启「高电平→低电平」的下降沿触发。
-
两个寄存器同时开启,即为双边沿触发,任意电平跳变都能触发。
-
检测到符合配置的跳变后,电路输出有效信号到下一级。
2. 触发汇总:硬件触发 + 软件触发
中间的或门是触发信号的汇总点,两个输入任意一个有效,就会输出触发信号:
-
一路来自前面的边沿检测电路,对应外部硬件触发。
-
另一路来自软件中断事件寄存器,对应软件触发:直接写寄存器就能手动模拟触发,不需要外部电平变化。
-
这一级的输出信号会同时分成两条路径:中断响应路径和事件响应路径。
3. 路径一:中断响应(输出到 NVIC)
这是最常用的路径,最终会打断 CPU 执行中断函数:
-
请求挂起寄存器 触发信号到来时,对应通道的挂起标志位会被硬件置 1,表示该通道有中断请求待处理。我们在中断服务函数里清除中断标志,操作的就是这个寄存器。
-
中断屏蔽寄存器 相当于每个通道的中断独立开关。对应位为 1 时中断放行,为 0 时即使有触发也不会向 NVIC 发送请求。
-
与门 + 输出至 NVIC 只有 “挂起标志置 1” 且 “中断未被屏蔽” 同时满足时,与门才会输出有效信号,最终把中断请求送到 NVIC,由 NVIC 裁决是否打断 CPU 主程序。
4. 路径二:事件响应(输出到其它外设)
这是纯硬件联动路径,不经过 CPU:
-
事件屏蔽寄存器 和中断屏蔽寄存器功能类似,单独控制每个通道的事件输出是否开启。
-
与门 + 脉冲发生器 事件未被屏蔽且触发信号有效时,信号进入脉冲发生器,输出一个固定宽度的硬件脉冲。 这个脉冲直接送到芯片内部其它外设(如 DMA、ADC、定时器),可以直接触发外设工作,全程不占用 CPU。
5. 顶部:寄存器访问总线
所有配置寄存器(屏蔽、触发选择、软件触发、挂起等)都通过APB 总线由 CPU 读写配置,工作时钟由 PCLK2 提供。我们写代码配置 EXTI,本质就是通过 APB 总线操作这些寄存器。
中断 vs 事件的核心区别
-
中断:需要 CPU 响应,进入中断服务函数执行代码,有软件处理过程,会打断主程序,适合需要逻辑处理的场景。
-
事件:纯硬件动作,直接产生脉冲联动外设,不经过 CPU,响应速度极快,适合低功耗唤醒、高速硬件触发场景。


旋转编码器简介
旋转编码器是一种测量旋转状态的传感器,核心作用是检测转轴的「旋转位置、转速、旋转方向」。 它的工作逻辑很简单:转轴转动时,编码器会输出两路方波电信号;我们读取方波的频率就能算出转速(频率越快,转速越高),读取两路方波的相位先后顺序就能判断正转还是反转,统计脉冲总数就能算出旋转的角度和当前位置。
三种常见类型
-
机械触点式:靠内部金属弹片 / 触点的通断输出信号,成本很低,常见于旋钮调节(比如音量旋钮、菜单选择旋钮);缺点是有机械磨损,寿命短,转动时会有触点抖动。
-
霍尔传感器式:靠磁场变化触发霍尔元件输出信号,无机械接触,寿命长、耐震动,很多小型减速电机自带的编码器大多是这种。
-
光栅式(光电式):内部有带缝隙的光栅盘,靠透光 / 遮光触发光电管输出信号,精度最高、支持高转速,工业伺服电机、数控机床的高精度编码器基本都是这类。
配图从左到右,大致对应:光栅码盘结构、旋钮式机械编码器、带编码器的减速电机、工业级光电编码器。
硬件电路原理
这是单片机外接旋转编码器最经典、最常用的接口电路
1. 右侧模块框图
这是简化后的接线示意:
-
编码器模块供电:
VCC接 3.3V 电源,GND接地。 -
信号输出:一共引出 3 个引脚
A、B、C,其中 A、B 是两相脉冲信号,分别接到单片机的PB0、PB1引脚;C 是公共端,直接接地。
2. 左侧详细电路(A/B 相对称设计)
这是完整的信号调理电路,作用是给编码器供电、滤除干扰抖动,保证单片机读到稳定的信号,每一路的功能如下:
-
上拉电阻 R1、R2(10KΩ):机械编码器本身是无源的触点开关,没有上拉就输出不了稳定的高低电平。接上拉电阻到 VCC 后,触点断开时,A/B 引脚被拉成高电平;触点接通时,A/B 引脚通过内部触点连到 C(地),变成低电平,由此产生高低变化的方波。
-
RC 滤波电路(R3/C1、R4/C2):R3/R4(10KΩ)+ C1/C2(0.1uF)组成低通滤波。机械编码器的触点在通断瞬间会产生 “弹跳抖动”,出现很多杂乱的窄脉冲;霍尔 / 光电编码器也可能有电源干扰毛刺。RC 滤波可以把这些抖动、干扰滤掉,让信号变平滑,避免单片机误判脉冲数、计错角度。
-
C 引脚:是编码器内部所有触点的公共端,这里直接接地,作为电平的参考零点。
补充:A/B 相怎么判断方向和速度
编码器正常转动时,A、B 两路方波会保持90° 相位差(也叫正交信号):
-
正转时:A 相信号的上升沿 / 下降沿,总是比 B 相提前出现;
-
反转时:B 相信号的上升沿 / 下降沿,总是比 A 相提前出现。
单片机通过检测两路信号的跳变顺序,就能判断旋转方向;统计脉冲总数就能计算旋转角度 / 位置;统计 1 秒内的脉冲个数,就能算出转速。
对射式红外传感器计次:
硬件代码按这个流程来写驱动:

//主函数
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "sensor.h"
int main(void)
{
sensorinit();
OLED_Init();
OLED_ShowString(1,1,"COUNT:");
while (1)
{
OLED_ShowNum(2,1,count,8);
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6)==0)//如果没东西,解锁
{
lock=0;
}
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0)==0)//按键按下时归0
{
count=0;
}
}
}
//传感器驱动
#include "stm32f10x.h" // Device headerV
void sensorinit()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启GPIOB+AFIO时钟,EXTI和NVIC无需手动开启
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource6);//AFIO:把P6接到EXTI
//AFIO每个芯片只能规定一次,如果重复规定,分组必须相同
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line6;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);//EXTI配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
NVIC_InitTypeDef NVIC_InitSturcture;
NVIC_InitSturcture.NVIC_IRQChannel= EXTI9_5_IRQn;
NVIC_InitSturcture.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitSturcture.NVIC_IRQChannelSubPriority=0;
NVIC_InitSturcture.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitSturcture); //NVIC配置
}
volatile uint16_t count=0;
uint8_t lock=0;
uint16_t GetCount(void)
{
return count;
}
void EXTI9_5_IRQHandler (void)//中断是自行触发的。
{
if(EXTI_GetITStatus(EXTI_Line6)==SET)
{
if(lock==0)
{
lock=1;
count++;
}
EXTI_ClearITPendingBit(EXTI_Line6);//清除中断标记
}
}
//库文件
#ifndef __sensor_H
#define __sensor_H
void sensorinit(void);
uint16_t GetCount(void);
extern volatile uint16_t count;//外部计次用
extern uint8_t lock;//传感器式防止抖动。
#endif
//OLED的三个文件详情请看上期
旋转编码器计次:
1. 旋转编码器 AB 相的本质
旋转编码器输出两路方波信号,通常称为 A 相 和 B 相。
它们之间的相位差为 90°,也就是一个周期内,一路信号的变化总是比另一路超前或滞后四分之一周期。
-
静止时:由于你的 GPIO 配置为
GPIO_Mode_IPU(上拉输入),编码器内部开关断开,两个引脚都被上拉到高电平,所以:PA0 = 1(A 相)
PA1 = 1(B 相) -
旋转时:编码器内部触点依次接地(变为低电平),然后释放,形成方波。
由于机械结构或光栅排列,A 和 B 不会同时变化,而是有一个先、一个后,这就是相位差。
2. 根据你的代码反推电平变化
你的配置是:
-
PA0 下降沿触发中断
-
中断里读 PA1 电平:
-
如果 PA1 == 0 →
count-- -
如果 PA1 == 1 →
count++
-
-
你观察到:向右转是 +,向左转是 -
右转(正转,count++)
在 PA0 下降沿发生时,PA1 必须为 高电平(1),因为代码中 else count++。
也就是说,右转时,PA0 先于 PA1 变为低电平。
一个完整的右转“步进”(一个 detent)电平变化如下:
|
步骤 |
PA0 (A) |
PA1 (B) |
说明 |
|---|---|---|---|
|
静止 |
1 |
1 |
两相都高 |
|
1 |
0 |
1 |
A 先接地 → PA0 下降沿,此时 B=1,所以 count++ |
|
2 |
0 |
0 |
B 也跟着接地 |
|
3 |
1 |
0 |
A 释放,回到高电平(PA0 上升沿,不触发中断) |
|
4 |
1 |
1 |
B 释放,回到静止 |
波形示意(右转):
text
PA0: 1‾‾‾‾\_____/‾‾‾‾‾‾‾‾
↓下降沿
PA1: 1‾‾‾‾‾‾‾‾\_____/‾‾‾‾
↑此时B=1
左转(反转,count--)
在 PA0 下降沿发生时,PA1 必须为 低电平(0),因为代码中 if(PA1==0) count--。
也就是说,左转时,PA1 先于 PA0 变为低电平。
一个完整的左转“步进”电平变化如下:
|
步骤 |
PA0 (A) |
PA1 (B) |
说明 |
|---|---|---|---|
|
静止 |
1 |
1 |
两相都高 |
|
1 |
1 |
0 |
B 先接地(PA1 下降沿,但你没有配置 PA1 中断) |
|
2 |
0 |
0 |
A 跟着接地 → PA0 下降沿,此时 B=0,所以 count-- |
|
3 |
0 |
1 |
B 释放(PA1 上升沿) |
|
4 |
1 |
1 |
A 释放,回到静止 |
波形示意(左转):
text
PA0: 1‾‾‾‾‾‾‾‾\_____/‾‾‾‾
↓下降沿
PA1: 1‾‾‾‾\_____/‾‾‾‾‾‾‾‾
此时B=0
3. 为什么相位差能判断方向?
核心就是:在一个信号的边沿(例如 A 的下降沿)去观察另一个信号的电平,就能知道谁先谁后。
-
右转:A 先变化,所以在 A 下降沿时,B 还没变,保持原来的高电平 → 读到 1。
-
左转:B 先变化,所以在 A 下降沿时,B 已经变低了 → 读到 0。
这就是正交编码器方向判断的经典方法。
4. 总结你的编码器工作状态
-
静止:
PA0=1, PA1=1 -
右转(+):
PA1 先保持高,PA0 先产生下降沿,中断里读到PA1=1,count++ -
左转(-):
PA1 先变成低,然后 PA0 才下降,中断里读到PA1=0,count--
//主函数
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "rotaryencoding.h"
int main(void)
{
OLED_Init();
rotaryencoding();
OLED_ShowString(1, 1, "count");
while (1)
{
OLED_ShowSignedNum(2,1,count,8);
}
}
//驱动
#include "stm32f10x.h" // Device header
//当想配置两个中断时,GPIO和EXTI的配置可以使用|符号,其余不行。
void rotaryencoding(void) //时钟开启
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//配置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(GPIOA,&GPIO_Initstructure);
//配置AFIO
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
//配置EXTI
EXTI_InitTypeDef EXTI_Structure;
EXTI_Structure.EXTI_Line=EXTI_Line0;
EXTI_Structure.EXTI_LineCmd=ENABLE;
EXTI_Structure.EXTI_Trigger= EXTI_Trigger_Falling;
EXTI_Structure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_Init(&EXTI_Structure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
//配置EXTI0->NVIC
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel= EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_Init(&NVIC_InitStructure);
}
volatile int32_t count=0;
volatile uint8_t lock=0;
void EXTI0_IRQHandler(void) //中断0
{
if(EXTI_GetITStatus(EXTI_Line0)==SET)
{
if(lock==0)
{
lock=1;
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==0)
count--;
else
count++;
EXTI_ClearITPendingBit(EXTI_Line0);
}
lock=0;
}
}
//驱动
#ifndef __rotaryencoding_H
#define __rotaryencoding_H
void rotaryencoding(void);
extern int32_t count;
extern uint8_t lock;
#endif
更多推荐
所有评论(0)