引言
这是这篇博文的升级版本
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
注意中断函数为没有返回值和参数列表,因为,中断具有不可控,你不知道什么时候发生。
。
结语
如果有什么疑问,或者观点不一致的欢迎一起讨论。
如果有什么函数不懂的可以自己稍微查一下,也可以比较两个的不同,了解。