中断 = 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 件事:

  1. 边沿检测与触发配置 可以配置触发规则:上升沿(电平从低变高)、下降沿(电平从高变低)、双边沿(任意跳变都触发)、软件触发(代码手动触发,无需外部信号)。只有符合配置的电平变化,才会触发后续动作。

  2. 通道管理 一共管理 20 个通道:

    • 前 16 个通道(EXTI0 ~ EXTI15):对应 GPIO 的 Pin0~Pin15,是最常用的外部引脚中断。

    • 后 4 个内部通道:PVD(电源电压监测)、RTC(实时时钟闹钟)、USB 唤醒、ETH(以太网唤醒),属于芯片内部外设的唤醒 / 中断信号,也挂载在 EXTI 上。

  3. 双模式输出

    • 中断模式:把中断信号发给 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 核心特性

  1. 触发方式:支持上升沿触发、下降沿触发、双边沿触发、软件触发四种。

  2. 引脚覆盖:所有 GPIO 引脚都能配置为外部中断,但同编号 Pin 不能同时使用。

  3. 通道总数:16 个 GPIO 引脚通道 + 4 个内部外设通道,共 20 个通道。

  4. 两种响应方式

    • 中断响应: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 的两大核心任务

对应图中文字说明:

  1. 复用功能引脚重映射 很多外设的引脚有默认位置,通过 AFIO 的重映射寄存器,可以把外设的引脚切换到另一组备用引脚。比如 USART1 默认使用 PA9/PA10,开启重映射后可以换到 PB6/PB7,方便 PCB 布线和引脚资源调配。

  2. 中断引脚选择(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,表示该通道有中断请求待处理。我们在中断服务函数里清除中断标志,操作的就是这个寄存器。

  2. 中断屏蔽寄存器 相当于每个通道的中断独立开关。对应位为 1 时中断放行,为 0 时即使有触发也不会向 NVIC 发送请求。

  3. 与门 + 输出至 NVIC 只有 “挂起标志置 1” 且 “中断未被屏蔽” 同时满足时,与门才会输出有效信号,最终把中断请求送到 NVIC,由 NVIC 裁决是否打断 CPU 主程序。


4. 路径二:事件响应(输出到其它外设)

这是纯硬件联动路径,不经过 CPU:

  1. 事件屏蔽寄存器 和中断屏蔽寄存器功能类似,单独控制每个通道的事件输出是否开启。

  2. 与门 + 脉冲发生器 事件未被屏蔽且触发信号有效时,信号进入脉冲发生器,输出一个固定宽度的硬件脉冲。 这个脉冲直接送到芯片内部其它外设(如 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=1count++

  • 左转(-)PA1 先变成低,然后 PA0 才下降,中断里读到 PA1=0count--

//主函数
#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



Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐