EXTI外部中断

中断系统介绍

中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得 CPU 暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
中断优先级:当有多个中断源同时申请中断时, CPU 会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断, CPU 再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回

EXTI外部中断_第1张图片

STM32中断

68 个可屏蔽中断通道,包含 EXTI TIM ADC USART SPI I2C RTC 等多个外设
使用 NVIC 统一管理中断,每个中断通道都拥有 16 个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级

 NVIC

EXTI外部中断_第2张图片

NVIC优先级分组 

NVIC 的中断优先级由优先级寄存器的 4 位( 0~15 )决定,这 4 位可以进行切分,分为高 n 位的抢占优先级和低 4-n 位的响应优先级
抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队

EXTI外部中断_第3张图片 响应优先级:即当有中断正在执行,执行完毕后,响应优先级高的,先执行。(排队排第一)

抢占优先级:即当有中断正在执行,无论有没有执行完,抢占优先级高的立马执行。(插队)

EXTI简介

EXTI Extern Interrupt )外部中断
EXTI 可以监测指定 GPIO 口的电平信号,当其指定的 GPIO 口产生电平变化时, EXTI 将立即向 NVIC 发出中断申请,经过 NVIC 裁决后即可中断 CPU 主程序,使 CPU 执行 EXTI 对应的中断程序
支持的触发方式:上升沿 / 下降沿 / 双边沿 / 软件触发
支持的 GPIO 口:所有 GPIO 口,但相同的 Pin不能同时触发中断(即PA1和PB1)
通道数: 16 GPIO_Pin ,外加 PVD 输出、 RTC 闹钟、 USB 唤醒、以太网唤醒
触发响应方式:中断响应 / 事件响应(不会触发中断,而是触发别的外设操作,是外设之间的联合工作)

 EXTI外部中断_第4张图片

AFIO中断引脚选择: 它可以在前面的GPIO外设的16个引脚中选择其中一个连接到后面的EXTI的通道里,所以相同的Pin不可以同时触发中断。

AFIO复用IO口

AFIO 主要用于引脚复用功能的选择和重定义
STM32 中, AFIO 主要完成两个任务:复用功能引脚重映射、中断引脚选择

EXTI外部中断_第5张图片

EXTI框图

EXTI外部中断_第6张图片

到底什么样的设备需要用到外部中断呢,外部中断又有什么好处呢?

对于STM32来说,想要获取的信号是外部驱动的很快的突发信号,就应该用到外部中断。

比如旋转编码器的输出信号,当我们不使用旋转编码器时,这时我们不需要STM32做出任何反应,但是一旦我们拧动了旋转编码器,就会发出很多脉冲波形需要被接收,这个信号是突发的,STM32是不知道什么时候会来的,且它是外部驱动的,STM32只能被动读取,信号也非常快,当STM32稍微晚接收一点点时间都会造成数据的丢失。这种情况就应该使用外部中断。

旋转编码器

旋转编码器:用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向
类型:机械触点式 / 霍尔传感器式 / 光栅式

EXTI外部中断_第7张图片 EXTI外部中断_第8张图片EXTI外部中断_第9张图片

硬件电路 

EXTI外部中断_第10张图片EXTI外部中断_第11张图片

实操部分

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

首先先编写初始化函数

这里我们需要搞懂需要配置哪些函数

EXTI外部中断_第12张图片

由上图可知:

初始化函数

1、配置RCC时钟(即涉及到的外设需要用到的时钟),再配置GPIO,选择为输入模式;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

当不知道配置GPIO为什么输入模式时,可以查看参考手册中的GPIO介绍中的外设GPIO配置

EXTI外部中断_第13张图片

EXTI外部中断_第14张图片

 这里我们选择上拉输入

    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);
2、配置AFIO,选择我们将会用到的GPIO口,连接到EXTI;

这和配置对射式红外传感器的GPIO口时不一样,需要用到新的函数,在GPIO的头文件中可以找到

首先是

void GPIO_AFIODeInit(void);

 会把AFIO外设的配置全部清除

第二个:

void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

 这个函数可以配置AFIO的数据选择器,来选择我们想要的中断引脚。

打开函数定义就可得知参数应该填什么

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
3、配置EXTI,选择边沿触发方式(上升沿、下降沿和双边沿),选择触发响应方式(中断响应和事件响应)

打开EXTI头文件,查看EXTI有什么函数

a、清除EXTI的配置

void EXTI_DeInit(void);

b、 初始化EXTI

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

c、 调用这个函数,可以把参数传递的结构体变量附一个默认值

void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);

 d、软件触发外部中断,调用这个函数,参数给一个指定的中断线,就能外部软件触发一次中断

void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);

 在外设运行过程中,会产生一些状态标志位(比如外部中断来了,会置标志位;串口受到数据,会置标志位;定时器时间到,也会置标志位;这些标志位都是存放在标志寄存器上的,当我们想要阅读这些标志位时就可以使用到以下四个函数)

 e、第一个:可以获取指定标志位是否被置1,;第二个:可以对置1的标志位进行清除(倾向于在主程序中使用)

FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
void EXTI_ClearFlag(uint32_t EXTI_Line);

f、在中断函数中,使用以下两个函数

第一个:获取中断标志位是否被置1,;第二个:清除中断挂起标志位(倾向于在中断函数中使用)

ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

初始化

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

需要用到结构体,右键查看结构体成员

    EXTI_InitTypeDef EXTI_InitStructure;
		

	EXTI_InitStructure.EXTI_Line = ;
	EXTI_InitStructure.EXTI_Mode = ;
	EXTI_InitStructure.EXTI_Trigger = ;
	EXTI_InitStructure.EXTI_LineCmd = ;

	EXTI_Init(&EXTI_InitStructure);

 然后依次右键查看各成员的定义

EXTI外部中断_第15张图片

在注释中搜索关键词的位置,例如EXTI_Lines;

EXTI外部中断_第16张图片

    //配置EXTI
	EXTI_InitTypeDef EXTI_InitStructure;
		
	//指定要配置的中断线
	EXTI_InitStructure.EXTI_Line = EXTI_Line14;
	//指定外部中断线的模式(枚举类型的中断模式和事件模式)
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	//指定触发信号的有效边缘(上升沿,下降沿,双向沿)
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	//指定选择中断线的新状态(ENABLE/DISABLE)
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
 4、配置NVIC,给中断选择一个合适的优先级,再通过NVIC就可以进入到CPU了。

NVIC的函数被发配到了杂项头文件中misc.h

1、用于中断分组,参数是中断分组的方式

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

2、初始化

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

 在配置中断之前,先指定中断的分组,再初始化NVIC即可。

中断分组:

EXTI外部中断_第17张图片

 在中断不多的情况下,不用太在意如何分组,这里我们选择比较平均的第二个分组,两位抢占(pre-emption),两位响应(subpriority)。

    //配置NVIC
	//分组方式整个芯片只能用一种,则这个分组的代码只要执行一次即可
	//如果是放在模块内使用该分组函数,则一定要保证每个模块的分组方式相同
	//或许可以直接放在main函数中
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

 NVIC初始化

	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = ;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = ;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = ;

	NVIC_Init(&NVIC_InitStructure);

再一个个仔细的查找该填入的值

第一个是选择中断通道,我们芯片时MD中等密度的,所以只需要展开MD的条件编译即可

EXTI外部中断_第18张图片

EXTI外部中断_第19张图片

STM32的EXTI10到EXTI15都合并到了这个通道里。

再根据下图赋值

EXTI外部中断_第20张图片

	//配置NVIC
	//NVIC的响应优先级和抢断优先级分组
	//分组方式整个芯片只能用一种,则这个分组的代码只要执行一次即可
	//如果是放在模块内使用该分组函数,则一定要保证每个模块的分组方式相同
	//或者可以直接放在main函数中
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	//中断通道
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
	//指定中断通道是使能还是失能
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	//抢断优先级和响应优先级
	//根据之前NVIC的分组函数,可以确定一下两个元素的范围
	//因为选择了分组2,则范围是0~3,且该工程只有这一个中断,所以随意设置为1
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

	NVIC_Init(&NVIC_InitStructure);

中断函数

名字是固定的,可以在启动文件中参考

EXTI外部中断_第21张图片

这些以IRQHandler结尾的都是中断函数的名字

其中

DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10

就是我们EXTI的中断函数

在我们编写的中断函数的下面写下中断函数的基本框架

void EXTI15_10_IRQHandler(void)
{
	
	
}

一定是无返回值和输入值的!

到这步我们还需要添加一个判断语句——是否是PB14口引起的中断,这就需要用到我们之前所解释过的EXTI函数,读取中断标志位的函数

ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);

同时还需要在判断后加上清除中断标志位的函数

void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

把中断状态清除,以防一直卡在中断函数中

整体

void EXTI15_10_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line14) == SET)//判断是否是指定端口产生的中断
	{
		//内容写在这
		
		
		//清除中断标志位,回归主函数,以防一直卡在中断函数中
		EXTI_ClearITPendingBit(EXTI_Line14);
	}
}

记得一定要在中断函数执行完后清除中断标志位,否则中断标志位一直为SET,程序就会一直响应中断,一直执行中断函数。

这样一来,再在main函数中初始化中断函数就写好代码了,可以用Keil自带的调试模式查看能不能跳到中断函数中。

在中断函数中设置一个停止点,然后全速运行,再试着使PB14产生中断(实验中是挡住红外传感器的光)

EXTI外部中断_第22张图片

 当出现

时,就说明中断函数写得没错 

我们就可以定义一个数来计算中断触发的次数,即挡光片挡住光线的次数。

uint16_t Counts;

void EXTI15_10_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line14) == SET)//判断是否是指定端口产生的中断
	{
		//内容写在这
		Counts++;
		
		//清除中断标志位,回归主函数,以防一直卡在中断函数中
		EXTI_ClearITPendingBit(EXTI_Line14);
	}
}

uint16_t GetCounts(void)
{
	return Counts;
}

在主函数中:

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

uint16_t Sensor_Counts;

int main(void)
{
	OLED_Init();
	CountSensor_Init();
	
	while(1)
	{
		Sensor_Counts = GetCounts();
		OLED_ShowString(1,1,"Counts:");
		OLED_ShowNum(1,8,Sensor_Counts,2);
	}
}

就可以实现我们想要的功能了

总体:

CountSensor.c

#include "stm32f10x.h"                  // Device header

uint16_t Counts;

void CountSensor_Init(void)
{
	//配置时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	//EXTI和NVIC的时钟是一直开着的,不用使用函数配置
	
	
	//配置对射式红外传感器的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);
	
	//配置AFIO
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
	
	//配置EXTI
	EXTI_InitTypeDef EXTI_InitStructure;
		
	//指定要配置的中断线
	EXTI_InitStructure.EXTI_Line = EXTI_Line14;
	//指定外部中断线的模式(枚举类型的中断模式和事件模式)
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	//指定触发信号的有效边缘(上升沿,下降沿,双向沿)
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	//指定选择中断线的新状态(ENABLE/DISABLE)
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
	
	//配置NVIC
	//NVIC的响应优先级和抢断优先级分组
	//分组方式整个芯片只能用一种,则这个分组的代码只要执行一次即可
	//如果是放在模块内使用该分组函数,则一定要保证每个模块的分组方式相同
	//或者可以直接放在main函数中
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	//中断通道
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
	//指定中断通道是使能还是失能
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	//抢断优先级和响应优先级
	//根据之前NVIC的分组函数,可以确定一下两个元素的范围
	//因为选择了分组2,则范围是0~3,且该工程只有这一个中断,所以随意设置为1
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

	NVIC_Init(&NVIC_InitStructure);
}

void EXTI15_10_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line14) == SET)//判断是否是指定端口产生的中断
	{
		//内容写在这
		Counts++;
		
		//清除中断标志位,回归主函数,以防一直卡在中断函数中
		EXTI_ClearITPendingBit(EXTI_Line14);
	}
}

uint16_t GetCounts(void)
{
	return Counts;
}

main.c

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

uint16_t Sensor_Counts;

int main(void)
{
	OLED_Init();
	CountSensor_Init();
	
	while(1)
	{
		Sensor_Counts = GetCounts();
		OLED_ShowString(1,1,"Counts:");
		OLED_ShowNum(1,8,Sensor_Counts,2);
	}
}

还用到了OLED模块

这里是移开挡光片+1,只需更改

    //指定触发信号的有效边缘(上升沿,下降沿,双向沿)
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;

即可改为挡住+1,或者挡住+1和移开+1 

5-2 旋转编码器计次

在之前的代码上稍作修改即可,把向左转和向右转改为两个EXTI中断。

配置时钟不用改,配置GPIO口时

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_0;

配置APIO需要分开配置

	//配置AFIO
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);

EXTI中断可以一起配置

	EXTI_InitStructure.EXTI_Line = EXTI_Line1 | EXTI_Line0;

配置NVIC时需要分开配置

EXTI外部中断_第23张图片

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	//EXTI0
	NVIC_InitTypeDef NVIC_InitStructure;
	//中断通道
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
	//指定中断通道是使能还是失能
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	//抢断优先级和响应优先级
	//根据之前NVIC的分组函数,可以确定一下两个元素的范围
	//因为选择了分组2,则范围是0~3,且该工程只有这一个中断,所以随意设置为1
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

	NVIC_Init(&NVIC_InitStructure);
	
	//EXTI1
	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_Init(&NVIC_InitStructure);

这里需要稍稍把EXTI1的中断优先级调后

再分别写出两个EXTI的中断函数

EXTI外部中断_第24张图片

 

void EXTI0_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line0) == SET)
	{
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
		{
			Counts++;
		}
		EXTI_ClearITPendingBit(EXTI_Line0);
	}
	
}

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

 这样再把原先显示数字的Counts改为有符号的就行了

主体

CountSensor.c

#include "stm32f10x.h"                  // Device header

int16_t Counts;

void CountSensor_Init(void)
{
	//配置时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	//EXTI和NVIC的时钟是一直开着的,不用使用函数配置
	
	
	//配置旋转编码器的GPIO口
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	//配置AFIO
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);

	//配置EXTI
	EXTI_InitTypeDef EXTI_InitStructure;
		
	//指定要配置的中断线
	EXTI_InitStructure.EXTI_Line = EXTI_Line1 | EXTI_Line0;
	//指定外部中断线的模式(枚举类型的中断模式和事件模式)
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	//指定触发信号的有效边缘(上升沿,下降沿,双向沿)
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	//指定选择中断线的新状态(ENABLE/DISABLE)
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
	
	//配置NVIC
	//NVIC的响应优先级和抢断优先级分组
	//分组方式整个芯片只能用一种,则这个分组的代码只要执行一次即可
	//如果是放在模块内使用该分组函数,则一定要保证每个模块的分组方式相同
	//或者可以直接放在main函数中
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	//EXTI0
	NVIC_InitTypeDef NVIC_InitStructure;
	//中断通道
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
	//指定中断通道是使能还是失能
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	//抢断优先级和响应优先级
	//根据之前NVIC的分组函数,可以确定一下两个元素的范围
	//因为选择了分组2,则范围是0~3,且该工程只有这一个中断,所以随意设置为1
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

	NVIC_Init(&NVIC_InitStructure);
	
	//EXTI1
	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_Init(&NVIC_InitStructure);
}

//旋转编码器旋转时会产生两道波形,设为A和B,
//正转时,B相比A,慢90°,反转则快90°
//以此特性,只有A处于低电平,B处于下降沿时,判断为正转
//A为下降沿,B为低电平时判断为反转
所以以判断旋转编码器的两个输出端的值,来设置两个旋转方向的中断函数

void EXTI0_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line0) == SET)
	{
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
		{
			Counts++;
		}
		EXTI_ClearITPendingBit(EXTI_Line0);
	}
	
}

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

int16_t GetCounts(void)
{
	return Counts;
}

main.c

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

int16_t Sensor_Counts;

int main(void)
{
	OLED_Init();
	CountSensor_Init();
	
	while(1)
	{
		Sensor_Counts = GetCounts();
		OLED_ShowString(1,1,"Counts:");
		OLED_ShowSignedNum(1,8,Sensor_Counts,2);
	}
}

你可能感兴趣的:(单片机,嵌入式硬件)