蓝桥杯嵌入式比赛所使用的是CT117E嵌入式竞赛板,板子上共有4个独立按键和一个复位按键,在进行按键配置前,我们需要先查看板子的原理图,知道这四个按键所连接的引脚。
由原理图可知,B1、B2、B3、B4四个按键分别对应PA0、PA8、PB1、PB2四个引脚。四个按键都连接了上拉电阻,所以都是低电平有效。
了解了按键的连接,接下来我们就可以对按键进行配置了,配置代码如下:
void Key_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB , ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; //上拉输入
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
这段代码就是用来初始化按键输入的IO口的,实现PA0、PA8、PB1、PB2的输入设置。
按键的配置步骤和LED的步骤相同
第一步:不管使用任何外设,永远都是先使能相应的时钟,通过函数RCC_APB2PeriphClockCmd()来使能APB2总线上的GPIOA、GPIOB的时钟。
第二步就是对引脚的具体配置,按键输入,我们把引脚配置为上拉输入。
接下来我们看看头文件里面的代码。
#ifndef _KEY_H
#define _KEY_H
#include "stm32f10x.h"
//key
void Key_Init(void);
#define KEY1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)
#define KEY2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)
#define KEY3 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)
#define KEY4 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_2)
#endif
这段代码里面最关键就是四个宏定义:
采用库函数GPIO_ReadInputDataBit读取IO口的值,它的返回值就是所读取IO口的电平状态,高电平返回1,低电平返回0。
配置完毕,我们还需要知道哪个按键被按下,所以接下来就要写按键读取函数,之前我一直都使用原子点的方法来处理按键,进入蓝桥杯备赛后,查阅了很多资料,偶然发现了一种简单实用的方法,代码如下:
//key
void Key_Read(void)
{
static u16 key1_sum=0,key2_sum=0,key3_sum=0,key4_sum=0;
//key1
if(KEY1==0)
{
key1_sum++;
if(key1_sum==1) //短按
{
//需要实现的功能
}
if(key1_sum==20) //长按
{
//需要实现的功能
key1_sum=10;
}
}
else
key1_sum=0;
}
4个按键的代码相同,可以直接复制粘贴。
代码第4行定义了4个静态变量,这是用来计数的,其实就是区分长短按,根据长按触发时间的长短,可以调整变量类型的大小。
按键为低电平有效,当读取到按键按下时,key1_sum就自加1。
如果是短按,按键会及时弹起,就只会进入短按程序执行(第9行),按键弹起后key1_sum就会清零。
如果是长按,key1_sum就会根据按键扫描的时间不断加1,直到松开按键,计数值才会清零。第17行给key1_sum赋值,是为了不断的触发长按。
长按计数值=长按触发时间 / 按键扫描时间
写完按键读取函数,接下来就要不断的扫描这个函数,就需要使用到嘀嗒定时器中断。中断中不宜放置太多代码,所以我们使用标志位来实现按键扫描。首先在main.c中定义一个标志位Key_Flag,中断中的代码如下:
extern u8 Key_Flag; //按键扫描标志
void SysTick_Handler(void)
{
static u16 ms1=0;
TimingDelay--;
if(++ms1==50) //50ms
{
ms1=0;
Key_Flag=1;
}
}
主函数代码:
//Main Body
int main(void)
{
SysTick_Config(SystemCoreClock/1000);
Led_Init();
Key_Init();
while(1)
{
if(Key_Flag) //按键扫描
{
Key_Flag=0; //标志位清零
Key_Read();
}
}
}
这个中断可看做1ms定时器,使用静态变量ms1来计数,计数值为50,按键扫描时间就为50ms。每50ms就把计数器清零,按键标志位置1,在主函数中,就会执行按键读取函数(也就是每50ms执行一次)。按键扫描时间定为50ms是为了让按键充分消抖
到此,按键的配置就写完了,我们写个简单的小程序来试试。
#include "stm32f10x.h"
#include "io.h"
u32 TimingDelay = 0;
u8 Key_Flag=0; //按键扫描标志
extern u16 LED;
//key
void Key_Read(void)
{
static u16 key1_sum=0,key2_sum=0,key3_sum=0,key4_sum=0;
//key1
if(KEY1==0)
{
key1_sum++;
if(key1_sum==1) //短按
{
LED ^= (1<<8);
Led_Set();
}
if(key1_sum==20) //长按
{
LED ^= (1<<9);
Led_Set();
key1_sum=10;
}
}
else
key1_sum=0;
}
//Main Body
int main(void)
{
SysTick_Config(SystemCoreClock/1000);
Led_Init();
Key_Init();
while(1)
{
if(Key_Flag) //按键扫描
{
Key_Flag=0;
Key_Read();
}
}
}
这个程序中,短按B1实现LED1的翻转;长按B1(长按触发时间为1s)就会让LED2不停地闪烁,26行key1_sum=10,只要只是按着B1不松开,LED2就每隔500ms翻转一次。