stm32 用中断实现按键点灯和蜂鸣器鸣叫(固件库版)

引言

这是这篇博文的升级版本
https://blog.csdn.net/qq_44885018/article/details/103050388
上面轮询的方法,今天主要讲中断的方法。为了方便不要开两个网页,我尽量把主要不一样的代码弄一下,原先的代码,电路图和任务说明就不写了,需要的跳转看一下上一篇。

按键中断代码

与原先的代码多了两个中断初始化函数(也就是有interrupt的),中断函数我没有写在这个子文件,因为中断函数随时可以变,所有写在main函数前面。

#include"key.h"
#include<stdio.h>
#include"stm32f4xx.h"
#include"stm32f4xx_gpio.h"

//GEC_key_init:初始化key所使用的端口
void GEC_key_init(void)
{
	//使能GPIOA、GPIOC端口时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);
	//设置模式
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN;//设置为输入模式
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;//设置相应端口
	GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;//输入模式为悬空
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//速率为50MHz
	GPIO_Init(GPIOA,&GPIO_InitStruct);//初始化端口
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13;//切换引脚
	GPIO_Init(GPIOC,&GPIO_InitStruct);//切换组号,并且初始化
}
/*
	GEC_key_ctl:实现对按键的状态的监控
	@key_n:用于保存键返回状态的主源
	例如:
		GEC_F429_K2_K3:表示主源为两个键
		GEC_F429_K2:表示主源是键K2
		GEC_F429_K3:表示主源是键k3
	返回值:返回主源键的状态
	例如:
		GEC_KEY_DOWN:表示主源键按下
		GEC_KEY_UP:表示主源键松开
		(注意当主源键为GEC_F429_K2_K3表示两个键按下或者松开,当主源键为一个时表示另一个为松开状态)
*/
int  GEC_key_ctl(int *key_n)
{
	//读取键的电平状态
	uint8_t status_a=GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0);//获取键k2的电平
	uint8_t status_c=GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13);//获取键k3的电平
	//将键k2的电平左移1位和键k3的电平组合去满足宏的范围
	/*
		KEY_ALL_DOWN:表示键k2、k3按下
		KEY_K2_DOWN:表示键k2按下,k3松开
		KEY_K3_DOWN:表示键k3按下,k2松开
		KEY_ALL_UP:表示键k2、k3松开
	*/
	status_c+=status_a<<1;
	switch (status_c)
	{
		case KEY_ALL_DOWN:
		{
			*key_n=GEC_F429_K2_K3;
			return GEC_KEY_DOWN;
		}
		case KEY_K2_DOWN:
		{
			*key_n=GEC_F429_K2;
			return GEC_KEY_DOWN;
		}
		case KEY_K3_DOWN:
		{
			*key_n=GEC_F429_K3;
			return GEC_KEY_DOWN;
		}
		case KEY_ALL_UP:
		{
			*key_n=GEC_F429_K2_K3;
			return GEC_KEY_UP;
		}
		default:;
	}
	return GEC_KEY_UP;
}

/*
在启动汇编的.s文件进行替换,并且声明
IMPORT GEC_key2_interrupt;
IMPORT GEC_key3_interrupt
*/
//GEC_key2_interrupt_init:主要实现键k2的初始化
void GEC_key2_interrupt_init(void)
{
	//GPIO口的基本初始化配置,这里就不多说了,看上面链接的博客
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN;//需要注意的就是中断的串口必须是输入模式,因为要获取而不是修改
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);

	//SYSCFG:系统配置管理的设置
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//其也是一个外设,需要使能时钟
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);//确定需要中断的是哪个引脚

	//外部中断控制器的配置
	EXTI_InitTypeDef EXTI_InitStruct;
	EXTI_InitStruct.EXTI_Line=EXTI_Line0;//中断的组号
	EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;//模式的选择,还有一种可以是一种事件
	EXTI_InitStruct.EXTI_LineCmd=ENABLE;//允许中断
	EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Rising_Falling;//中断触发方式为:双边沿触发
	EXTI_Init(&EXTI_InitStruct);

	//NVIC的配置,NVIC:嵌套中断控制器(简单来说就是优先级问题)
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel=EXTI0_IRQn;//中断组号
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级的设置
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;//子优先级设置(也就是在抢占优先级相同且同时情况下使用)
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;//允许中断
	NVIC_Init(&NVIC_InitStruct);
	
}

void GEC_key3_interrupt_init(void)//和上面类似
{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13;
	GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOC,&GPIO_InitStruct);

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC,EXTI_PinSource13);
	
	EXTI_InitTypeDef EXTI_InitStruct;
	EXTI_InitStruct.EXTI_Line=EXTI_Line13;
	EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_LineCmd=ENABLE;
	EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Rising_Falling;
	EXTI_Init(&EXTI_InitStruct);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel=EXTI15_10_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStruct);
	
}

main函数

#include<stdio.h>
#include"led.h"
#include"key.h"
#include"stm32f4xx.h"
#include"stm32f4xx_gpio.h"
#include"beep.h"
/*
在启动汇编的.s文件进行替换,并且声明
IMPORT GEC_key2_interrupt;
IMPORT GEC_key3_interrupt
*/
void GEC_key2_interrupt(void)//k2中断函数
{
	if(SET==EXTI_GetFlagStatus(EXTI_Line0))//检测是否0组是否发生中断
	{
		if(GEC_KEY_DOWN==GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))//判断键2是否按下还是弹起
		{
			GEC_led_ctl(GEC_F429_D3,GEC_LED_ON);//控制灯D3亮
			if(GEC_KEY_DOWN==GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13))//判断键k3是否按下
			{
				GEC_beep_ctl(GEC_BEEP_ON);//k2,k3同时按下,蜂鸣器鸣叫
			}
		}
		else
		{
			GEC_led_ctl(GEC_F429_D3,GEC_LED_OFF);//k2松开时,灯熄灭
			GEC_beep_ctl(GEC_BEEP_OFF);//将蜂鸣器关闭
			
		}
		EXTI_ClearITPendingBit(EXTI_Line0);//清除中断标志
	}
}
void GEC_key3_interrupt(void)//k3中断函数
{
	if(SET==EXTI_GetFlagStatus(EXTI_Line13))//检测是否13组是否发生中断
	{
		if(GEC_KEY_DOWN==GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13))//判断键3是否按下还是弹起
		{
			GEC_led_ctl(GEC_F429_D4,GEC_LED_ON);//控制灯D4亮
			if(GEC_KEY_DOWN==GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))//判断键k2是否按下
			{
				GEC_beep_ctl(GEC_BEEP_ON);//k2,k3同时按下,蜂鸣器鸣叫
			}
		}
		else
		{
			GEC_led_ctl(GEC_F429_D4,GEC_LED_OFF);//k3松开时,灯熄灭
			GEC_beep_ctl(GEC_BEEP_OFF);//将蜂鸣器关闭
		}
		EXTI_ClearITPendingBit(EXTI_Line13);//清除中断标志
	}
}

int main()
{
	GEC_led_init();//初始化LED灯
	GEC_key_init();//初始化按键灯
	GEC_beep_init();//初始化蜂鸣器
	GEC_key2_interrupt_init();//初始化按键中断键k2
	GEC_key3_interrupt_init();//初始化按键中断键k3
	while(1);//等待中断
}

注意点

因为使用固件库,使用你需要在固件库的startup_stm32f429_439xx.s进行修改,以及声明。

DCD EXTI0_IRQHandler
修改成
DCD GEC_key2_interrupt;

DCD EXTI15_10_IRQHandler
修改成
DCD GEC_key3_interrupt;
由于这两个程序均是用c写的所以,需要在这个汇编启动代码加入声明
IMPORT GEC_key2_interrupt;
IMPORT GEC_key3_interrupt
注意中断函数为没有返回值和参数列表,因为,中断具有不可控,你不知道什么时候发生。
其次也有人会问,为什么没有延时去抖动?
实际上也没什么特别必要(当然你要加也很简单),为什么没什么必要呢?因为这不是测电平,实现函数发生器,产生方波之类的,仅仅只是按下就发光,就是有抖动,人的眼睛也看不出来,反应不过来,而且这里中断中使用的也不是将端口电平取反之类的,使用也是可控状态的。我也亲测实现和想的一至,完成任务需求。
其他的我基本上都写了备注,由于两个按键中断差不多,所以第二个,基本上没有写备注,不过流程差不多。

结语

如果有什么疑问,或者观点不一致的欢迎一起讨论。
如果有什么函数不懂的可以自己稍微查一下,也可以比较两个的不同,了解。

猜你喜欢

转载自blog.csdn.net/qq_44885018/article/details/103070394