1、打开正点原子的库函数源码可以看到关于按键的程序有两个,一个是按键输入实验,一个是外部中断实验。从最后体现的效果来看,这两个似乎是一样的,那么如果按键输入和外部中断冲突了,那么哪个优先级比较高呢,今天就来试试
2、首先还是简单介绍,硬件用的是野火的STM32F103ZET6开发板,软件框架用的是正点原子的。首先单独实现按键输入和外部中断来切换控制LED的亮灭状态。
首先打开原理图,可以看到按键SW2接到PA0,接了下拉电阻到GND,所以这个IO应该配置为下拉输入。
然后三个led分别接到了PB0、PB1、PB5,低电平点亮,高电平熄灭。
3、用按键扫描的方式点亮led
先初始化led
#include "led.h"
//初始化PB5和PE5为输出口.并使能这两个口的时钟
//LED IO初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); //使能PB,PE端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_5; //LED0-->PB.5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5
GPIO_SetBits(GPIOB,GPIO_Pin_0); //PB.5 输出高
GPIO_SetBits(GPIOB,GPIO_Pin_1); //PB.5 输出高
GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高
}
再初始化按键和编写按键扫描函数
#include "stm32f10x.h"
#include "key.h"
#include "sys.h"
#include "delay.h"
//按键初始化函数
void KEY_Init(void) //IO初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能PORTA,PORTE时钟
//初始化 WK_UP-->GPIOA.0 下拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0
}
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY3按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY2>KEY3!!
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(WK_UP==1))
{
delay_ms(10);//去抖动
key_up=0;
if(WK_UP==1)return WKUP_PRES;
}else if(WK_UP==0)key_up=1;
return 0;// 无按键按下
}
编写主函数,效果很简单,开机三个led不亮,按下SW2,三个led均点亮。
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "exti.h"
int main(void)
{
vu8 key=0;
delay_init(); //延时函数初始化
LED_Init(); //LED端口初始化
KEY_Init(); //初始化与按键连接的硬件接口
LED0=1;
LED1=1;
LED2=1;
while(1)
{
key=KEY_Scan(0); //得到键值
if(key==1)
{
LED0=0;
LED1=0;
LED2=0;
}
}
}
/**********************************************************/
4、用外部中断的方式点亮led
先初始化led,同上
#include "led.h"
//初始化PB5和PE5为输出口.并使能这两个口的时钟
//LED IO初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); //使能PB,PE端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_5; //LED0-->PB.5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5
GPIO_SetBits(GPIOB,GPIO_Pin_0); //PB.5 输出高
GPIO_SetBits(GPIOB,GPIO_Pin_1); //PB.5 输出高
GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高
}
中断配置和中断服务函数
#include "exti.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "usart.h"
#include "beep.h"
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
KEY_Init(); // 按键端口初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用功能时钟
//GPIOA.0 中断线以及中断初始化配置 上升沿触发 PA0 WK_UP
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按键WK_UP所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
}
//外部中断0服务程序
void EXTI0_IRQHandler(void)
{
delay_ms(10);//消抖
if(WK_UP==1) //WK_UP按键
{
LED0=0;
LED1=0;
LED2=0;
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位
}
编写主函数,可以看到,除了函数的初始化,while()循环里面什么都没有,因为只要有中断触发,就会执行中断服务函数里面的指令。实现的效果和上面一样,开机三个led都熄灭,按下SW2,三个led都点亮。
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "exti.h"
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
delay_init(); //延时函数初始化
LED_Init(); //LED端口初始化
EXTIX_Init();
LED0=1;
LED1=1;
LED2=1;
while(1)
{
}
}
/**********************************************************/
5、在上面的基础上,我用按键扫描的方式点亮led,然后用外部中断来熄灭led,那么会出现什么情况呢?首先肯定还是先初始化led,代码就不再重复。所有代码都和外部中断点亮led的一样,除了main函数有区别,代码如下
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "exti.h"
extern int a;
int main(void)
{
vu8 key=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
delay_init(); //延时函数初始化
LED_Init(); //LED端口初始化
KEY_Init(); //初始化与按键连接的硬件接口
EXTIX_Init();
LED0=1;
LED1=1;
LED2=1;
while(1)
{
key=KEY_Scan(0); //得到键值
if(key==1)
{
LED0=1;
LED1=1;
LED2=1;
}
}
}
可以看到,外部中断服务函数里面是将三个led都点亮。在主函数的while()里面是SW2按下,三个led都熄灭。实际效果是开机三个led都熄灭,按下SW2,三个led会闪一下,然后保持熄灭状态。分析原因在按下的一瞬间,触发了外部中断,所以led会熄灭,但是后续就进入了按键扫描函数,于是三个led保持熄灭状态。
回到标题的问题,按键扫描和外部中断的优先级哪个高。单纯从我这个代码而言,外部中断的优先级比按键扫描高,但是从实际效果而言,中断是一瞬间,一闪而过,而按键扫描是持续且长久的。通过这个也可以方便理解为什么说按键扫描比较耗费单片机资源,从此可见一斑。
百度云代码链接:
链接:https://pan.baidu.com/s/1IqjNq29yYTuU1TQjY9hV1g
提取码:2iwu