STM32标准库教程——第五章:使用EXTI外部中断功能

1.中断系统

中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行

中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源

中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回

2.中断执行流程

3.STM32中断

  • 68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设
  • 使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级
    1. WWDG:窗口看门狗 用来监测程序运行状态的中断 比如程序卡死了 没有及时喂狗 窗口看门狗就会申请中断 让程序调到窗口看门狗的中断程序里 此时可以在中断程序里进行一些错误检查 看看出现什么问题
    2. PVD电源电压检测:如果供电电压不足 PVD电路就会申请中断 在中断里可知供电不足 此时便需要保存重要数据
    3. 地址:程序中的中断函数其地址是由编译器来分配的 是不固定的 但中断跳转由于硬件的限制 只能跳到固定的地址执行程序 所以为了能让硬件跳转到一个不固定的中断函数里 这里就需要在内存中定义一个地址的列表 这个列表地址是固定的 中断发生后 就跳到这个固定位置 在这个固定位置上 由编译器 再加上一条跳转到中断函数的代码 此时中断跳转就可以跳转到任意位置了 这个中断地址的列表 就是中断向量表 相当于中断跳转的一个跳板 但我们是用C语言编程的 不需要去管这个中断向量表

    4.NVIC基本结构(嵌套中断向量控制器)

    5.NVIC优先级分组

  • NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级

  • 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队

  • 6.EXIT简介

    1. 相同的Pin不能同时触发中断:比如PA0和PB0不能同时使用
    2. 通道数:加起来总共有20个中断线路 16个GPIO_Pin是外部中断的主要功能 后面四个主要是来外部中断”蹭网“的 外部中断有个功能 就是从低功耗模式的停止模式下唤醒STM32 对于PVD电源电压监测 当电源从电压过低恢复时 就需要PVD借助一下外部中断退出停止模式 对于RTC闹钟来说 有时候为了省电 RTC定了个闹钟后 STM32会进入停止模式 等到闹钟响的时候再唤醒 这也需要借助外部中断 USB唤醒、以太网唤醒也都是类似作用
    3. 中断响应:申请中断 让CPU执行中断函数
    4. 事件响应:是STM32对外部中断增加的一种额外的功能 当外部中断检测到引脚电平变化时 正常的流程是选择触发中断 但在STM32中 也可以选择触发一个事件 如果选择触发事件 那外部中断的信号就不会通向CPU 而是通向其他外设 用来触发其他外设的操作 比如触发ADC转换、触发DMA等
    5. 3、4对比总结:中断响应是正常流程 引脚电平变化触发中断 事件响应不会触发中断 而是触发别的外设操作 属于外设之间的联合工作

    7.EXIT基本结构

    1. AFIO:数据选择器 可以在前面3个GPIO外设的16个引脚里选择其中一个连接到后面的EXIT的通道里
    2. 输入信号经过EXIT电路后分为了两种输出 上面的接到了NVIC 是用来触发中断的 本来20路输入 应该有20路中断的输出 可能开发板公司觉得20输出太多 比较占用NVIC的通道资源 所以把其中外部中断的9~5、15~10分到一个通道里 意思是外部中断的9~5会触发同一个中断函数 15~10也会触发同一个中断函数 在编程时 在这两个中断函数里  需要再根据标志位来区别到底是哪个中断进来的 下面有20条输出线路到了其他外设 这就是用来触发其他外设操作的 也就是我们上面说的事件响应

    8.AFIO复用IO口

  • AFIO主要用于引脚复用功能的选择和重定义

  • 在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择

  • 9.EXIT框图

  • 触发信号通过或门之后 兵分两路 上一路是触发中断的 下一路是触发事件的 中断输出部分:触发中断首先会置一个挂起寄存器 相当于是一个中断标志位 可以读取这个寄存器判断是哪个通道触发的中断 如果中断挂起寄存器置1 继续向左走 和中断屏蔽寄存器共同进入一个与门 然后至NVIC中断控制器 这个与门相当于一个开关 中断屏蔽寄存器给1 那另一个就是直接输出 也就是允许中断 中断屏蔽寄存器给0 另一个无论输入什么 输出都是0 相当于屏蔽了这个中断 事件输出部分:事件屏蔽寄存器进行开关控制 最后通过一个脉冲发生器到其他外设 这个脉冲发生器就是给一个电平脉冲 用来触发其他外设的动作  
  • 10.相关API

    10.1 GPIO_AFIODeInit

    void GPIO_AFIODeInit(void)(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
    功能:
    	用来复位AFIO外设 调用该函数后AFIO外设的配置就会全部清除
    参数:
        无    
    返回值:
    	无    
    

    10.2 GPIO_PinLockConfig

    void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
    功能:
    	锁定GPIO引脚配置寄存器
    参数:
        GPIOx:GPIOx:其中x可以为(A..G)选择GPIO外设
        GPIO_Pin:要写的端口位,该参数可以是GPIO_Pin_x的任意组合,其中x可以是(0..15)
    返回值:
    	无    

    10.3 GPIO_PinRemapConfig

    void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
    功能:
    	更改指定引脚的映射
    参数:
        GPIO_Remap: 选择要重新映射的引脚
        NewState:端口引脚重新映射的新状态,取值为:ENABLE或DISABLE 
    返回值:
    	无    
    

    10.4 GPIO_EXTILineConfig

    void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
    功能:
    	选择 GPIO 管脚用作外部中断线路
    参数:
        GPIO_PortSource: 选择用作外部中断线源的 GPIO 端口,取值为GPIO_PortSourceGPIOx,其中x为(A..G)
        GPIO_PinSource:待设置的外部中断线路,该参数可以是GPIO_PinSourcex,其中x可以是(0..15)  
    返回值:
    	无    
    

    10.5 EXTI_Init

    void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
    功能:
    	根据 EXTI_InitStruct 中指定的参数初始化外设 EXTI 寄存器
    参数:
        EXTI_InitStruct:指向结构 EXTI_InitTypeDef 的指针,包含了外设 EXTI 的配置信息 
    返回值:
    	无    
    

    10.6 EXTI_GetITStatus

    ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)
    功能:
    	检查指定的 EXTI 线路触发请求发生与否
    参数:
        EXTI_Line:待检查 EXTI 线路的挂起位 
    返回值:
    	EXTI_Line 的新状态(SET 或者 RESET) 
            
        
    

    10.7 EXTI_ClearITPendingBit

    void EXTI_ClearITPendingBit(uint32_t EXTI_Line)
    功能:
    	清除 EXTI 线路挂起位
    参数:
        EXTI_Line:待清除 EXTI 线路的挂起位 
    返回值:
    	无 
            
           
    

    10.8 NVIC_PriorityGroupConfig

    void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
    功能:
    	设置优先级分组:先占优先级和从优先级(先占就是抢先优先级,从站就是响应优先级)
    参数:
        NVIC_PriorityGroup:优先级分组位长度(本次代码选择的是2为抢占2位响应 比较平均)
    返回值:
    	无
    
    
    PriorityGroup类型
    /** @defgroup Preemption_Priority_Group 
      * @{
      */
    
    #define NVIC_PriorityGroup_0         ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
                                                                4 bits for subpriority */
    #define NVIC_PriorityGroup_1         ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
                                                                3 bits for subpriority */
    #define NVIC_PriorityGroup_2         ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
                                                                2 bits for subpriority */
    #define NVIC_PriorityGroup_3         ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
                                                                1 bits for subpriority */
    #define NVIC_PriorityGroup_4         ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
                                                                0 bits for subpriority */
    
    
    

    10.9 NVIC_Init

    void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
    功能:
    	根据 NVIC_InitStruct 中指定的参数初始化外设 NVIC 寄存器
    参数:
        NVIC_InitStruct:指向结构 NVIC_InitTypeDef 的指针,包含了外设 GPIO 的配置信息     
    返回值:
    	无    
    

    11.对射式红外传感器工程

    11.1 对射式红外传感器模块

    11.1.1 简介

    该产品采用FTR9606高灵敏度槽型光耦器件,槽宽5mm。它由一个红外发光二极管和NPN光电三极管组成,M3固定安装孔,有输出状态指示灯,输出高电平灯灭,输出低电平灯亮。有遮挡,输出高电平。无遮挡,输出低电平。使用3.3-5VDC 宽电压LM393比较器输出,信号干净,波形好,驱动能力强,超过15mA。输出形式:数字开关量输出(0和1)。广泛用于电机转速检测,脉冲计数,位置限位等。

    11.1.2 特点

    1、使用进口ITR9606高灵敏度槽型光耦传感器,槽宽度5mm。
    2、有输出状态指示灯,输出高电平灯灭,输出低电平灯亮。
    3、有遮挡,输出高电平;无遮挡,输出低电平。
    4、比较器输出,信号干净,波形好,驱动能力强,超过15mA。
    5、工作电压3.3V-5V
    6、输出形式:数字开关量输出(0和1)
    7、设有M3固定螺栓孔,方便安装
    8、小板PCB尺寸:3.2cm x 1.4cm
    9、使用宽电压LM393比较器
    10、广泛用于电机转速检测,脉冲计数,位置限位等。

    11.1.3 引脚说明

    1、VCC 接电源正极3.3-5V

    2、GND 接电源负极

    3、DO TTL开关信号输出

    4、AO 此模块不起作用

    11.1.4 操作说明

    1、接好VCC和GND,模块电源指示灯会亮
    2、模块槽中无遮挡时,接收管导通,模块DO输出低电平,开关指示灯亮;遮挡时,DO输出高电平,开关指示灯灭。
    3、模块DO可与继电器相连,组成限位开关等功能,也可以与有源蜂鸣器模块相连,组成报警器。
    4、DO输出接口可以与单片机10口直接相连,一般接外部中断,检测传感器是否有遮档,如用电机码盘则可检测电机的转速。

    11.1.5 电路原理图

    11.2 接线图

    11.3 相关代码

    CountSensor.c
    #include "stm32f10x.h"                  // Device header
    
    uint16_t CountSensor_Count;				//全局变量,用于计数
    
    /**
      * 函    数:计数传感器初始化
      * 参    数:无
      * 返 回 值:无
      */
    void CountSensor_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选择中断引脚*/
    	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//将外部中断的14号线映射到GPIOB,即选择PB14为外部中断引脚
    	
    	/*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线路的抢占优先级为1
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
    	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
    }
    
    /**
      * 函    数:获取计数传感器的计数值
      * 参    数:无
      * 返 回 值:计数值,范围:0~65535
      */
    uint16_t CountSensor_Get(void)
    {
    	return CountSensor_Count;
    }
    
    /**
      * 函    数:EXTI15_10外部中断函数
      * 参    数:无
      * 返 回 值:无
      * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
      *           函数名为预留的指定名称,可以从启动文件复制
      *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
      */
    void EXTI15_10_IRQHandler(void)
    {
    	if (EXTI_GetITStatus(EXTI_Line14) == SET)		//判断是否是外部中断14号线触发的中断
    	{
    		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
    		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
    		{
    			CountSensor_Count ++;					//计数值自增一次
    		}
    		EXTI_ClearITPendingBit(EXTI_Line14);		//清除外部中断14号线的中断标志位
    													//中断标志位必须清除
    													//否则中断将连续不断地触发,导致主程序卡死
    	}
    }
    
    CountSensor.h
    #ifndef __COUNT_SENSOR_H
    #define __COUNT_SENSOR_H
    
    void CountSensor_Init(void);
    uint16_t CountSensor_Get(void);
    
    #endif
    
    main.c
    #include "stm32f10x.h"                  // Device header
    #include "Delay.h"
    #include "OLED.h"
    #include "CountSensor.h"
    
    int main(void)
    {
    	/*模块初始化*/
    	OLED_Init();			//OLED初始化
    	CountSensor_Init();		//计数传感器初始化
    	
    	/*显示静态字符串*/
    	OLED_ShowString(1, 1, "Count:");	//1行1列显示字符串Count:
    	
    	while (1)
    	{
    		OLED_ShowNum(1, 7, CountSensor_Get(), 5);		//OLED不断刷新显示CountSensor_Get的返回值
    	}
    }
    

    现象:这里给外部中断配置的是下降沿 所以每次遮挡传感器 在遮挡的那一瞬间计次加1 拿起的说话不变 再次遮挡计次再次加1

    12.旋转编码器工程

    12.1 旋转编码器模块

    12.1.1 简介
  • 旋转编码器:用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向

  • 类型:机械触点式/霍尔传感器式/光栅式

  • 12.2 硬件电路

    1. 左边接了一个10的上拉电阻 默认没旋转的情况下 这个点被上拉为高电平 通过R3输出到A端口就是高电平 当旋转时 内部触电导通 这个点直接被拉低到GND 再通过R3输出 A端口就是低电平 
    2. R3:输出限流电阻 防止模块引脚电流过大
    3. C1:输出滤波电容 防止一些输出信号抖动 

    12.2 接线图

    12.3 相关代码

    Encoder.c
    #include "stm32f10x.h"                  // Device header
    
    int16_t Encoder_Count;					//全局变量,用于计数旋转编码器的增量值
    
    /**
      * 函    数:旋转编码器初始化
      * 参    数:无
      * 返 回 值:无
      */
    void Encoder_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);						//将PB0和PB1引脚初始化为上拉输入
    	
    	/*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;		//选择配置外部中断的0号线和1号线
    	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线路的抢占优先级为1
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
    	NVIC_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线路的抢占优先级为1
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;			//指定NVIC线路的响应优先级为2
    	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
    }
    
    /**
      * 函    数:旋转编码器获取增量值
      * 参    数:无
      * 返 回 值:自上此调用此函数后,旋转编码器的增量值
      */
    int16_t Encoder_Get(void)
    {
    	/*使用Temp变量作为中继,目的是返回Encoder_Count后将其清零*/
    	/*在这里,也可以直接返回Encoder_Count
    	  但这样就不是获取增量值的操作方法了
    	  也可以实现功能,只是思路不一样*/
    	int16_t Temp;
    	Temp = Encoder_Count;
    	Encoder_Count = 0;
    	return Temp;
    }
    
    /**
      * 函    数:EXTI0外部中断函数
      * 参    数:无
      * 返 回 值:无
      * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
      *           函数名为预留的指定名称,可以从启动文件复制
      *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
      */
    void EXTI0_IRQHandler(void)
    {
    	if (EXTI_GetITStatus(EXTI_Line0) == SET)		//判断是否是外部中断0号线触发的中断
    	{
    		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
    		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
    		{
    			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)		//PB0的下降沿触发中断,此时检测另一相PB1的电平,目的是判断旋转方向
    			{
    				Encoder_Count --;					//此方向定义为反转,计数变量自减
    			}
    		}
    		EXTI_ClearITPendingBit(EXTI_Line0);			//清除外部中断0号线的中断标志位
    													//中断标志位必须清除
    													//否则中断将连续不断地触发,导致主程序卡死
    	}
    }
    
    /**
      * 函    数:EXTI1外部中断函数
      * 参    数:无
      * 返 回 值:无
      * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
      *           函数名为预留的指定名称,可以从启动文件复制
      *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
      */
    void EXTI1_IRQHandler(void)
    {
    	if (EXTI_GetITStatus(EXTI_Line1) == SET)		//判断是否是外部中断1号线触发的中断
    	{
    		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
    		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
    		{
    			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)		//PB1的下降沿触发中断,此时检测另一相PB0的电平,目的是判断旋转方向
    			{
    				Encoder_Count ++;					//此方向定义为正转,计数变量自增
    			}
    		}
    		EXTI_ClearITPendingBit(EXTI_Line1);			//清除外部中断1号线的中断标志位
    													//中断标志位必须清除
    													//否则中断将连续不断地触发,导致主程序卡死
    	}
    }
    Encoder.h
    #ifndef __ENCODER_H
    #define __ENCODER_H
    
    void Encoder_Init(void);
    int16_t Encoder_Get(void);
    
    #endif
    
    main.c
    #include "stm32f10x.h"                  // Device header
    #include "Delay.h"
    #include "OLED.h"
    #include "Encoder.h"
    
    int16_t Num;			//定义待被旋转编码器调节的变量
    
    int main(void)
    {
    	/*模块初始化*/
    	OLED_Init();		//OLED初始化
    	Encoder_Init();		//旋转编码器初始化
    	
    	/*显示静态字符串*/
    	OLED_ShowString(1, 1, "Num:");			//1行1列显示字符串Num:
    	
    	while (1)
    	{
    		Num += Encoder_Get();				//获取自上此调用此函数后,旋转编码器的增量值,并将增量值加到Num上
    		OLED_ShowSignedNum(1, 5, Num, 5);	//显示Num
    	}
    }
    
    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32标准库教程——第五章:使用EXTI外部中断功能

    发表评论