前言
在项目中,按键是非常常用的外设,通过按键的单击、长按等,可以实现不同的互动操作。
这里,就通过按键单击、长按功能,分别实现LED点亮,LED闪烁,LED关闭三种状态。
一、原理图
根据原理图上的连线,可以得知,一共有6个按键,当按键未按下的时候,GPIO口处于高电平状态,当按键按下,GPIO则处于低电平状态。
值得注意的是,KEY1连接的GPIO口,复用状态下是JDIO,可以用重映射功能将其关闭,让PB3变为可用的GPIO口。
二、程序
按键驱动函数
#include "hal_key.h"
#include "stm32F10x.h"
static void hal_keyConfig(void);
static unsigned char hal_getKey1Sta(void);
static unsigned char hal_getKey2Sta(void);
static unsigned char hal_getKey3Sta(void);
static unsigned char hal_getKey4Sta(void);
static unsigned char hal_getKey5Sta(void);
static unsigned char hal_getKey6Sta(void);
KeyEvent_CallBack_t KeyScanCBS;
//函数指针数组,hal_getKeySta,分别是6个按键
unsigned char (*getKeysState[KEYNUM])(void)={ hal_getKey1Sta,hal_getKey2Sta,hal_getKey3Sta,hal_getKey4Sta,hal_getKey5Sta,hal_getKey6Sta};
unsigned char KeyStep[KEYNUM]; //按键检测
unsigned char KeyScanTime[KEYNUM]; //去抖时间
unsigned char KeyPressLongTimer[KEYNUM]; //长按延时
unsigned char KeyContPressTimer[KEYNUM]; //连续长按延时
//初始化
void hal_KeyInit(void)
{
unsigned char i;
hal_keyConfig();
KeyScanCBS=0;
for(i=0;i<KEYNUM;i++)
{
KeyStep[i]=KEY_STEP_WAIT;
KeyScanTime[i]=KEY_SCANTIME;
KeyPressLongTimer[i]=KEY_PRESS_LONG_TIME;
KeyContPressTimer[i]=KEY_PRESS_CONTINUE_TIME;
}
}
void hal_KeyScanCBSRegister(KeyEvent_CallBack_t pCBS) //传入回调函数
{
if(KeyScanCBS == 0)
{
KeyScanCBS = pCBS; //函数指针指向pCBS函数
}
}
void hal_KeyProc(void)
{
unsigned char i,KeyState[KEYNUM];
for(i=0;i<KEYNUM;i++)
{
unsigned char keys=0;
KeyState[i]=getKeysState[i]();//读取每个按键的高低电平
switch(KeyStep[i])//等待状态
{
case KEY_STEP_WAIT:
if(KeyState[i])
{
KeyStep[i]=KEY_STEP_CLICK;
}
break;
case KEY_STEP_CLICK://单击状态
if(KeyState[i])
{
if(!(--KeyScanTime[i]))
{
KeyScanTime[i]=KEY_SCANTIME;
KeyStep[i]=KEY_STEP_LONG_PRESS;
keys=(i*5)+1;//单击
}
}
else
{
KeyStep[i]=KEY_STEP_WAIT;
KeyScanTime[i]=KEY_SCANTIME;
}
break;
case KEY_STEP_LONG_PRESS://长按状态
if(KeyState[i])
{
if(!(--KeyPressLongTimer[i]))
{
KeyPressLongTimer[i]=KEY_PRESS_LONG_TIME;
KeyStep[i]=KEY_STEP_CONTINUOUS_PRESS;
keys=(i*5)+3;//长按
}
}
else
{
KeyStep[i]=KEY_STEP_WAIT;
KeyPressLongTimer[i]=KEY_PRESS_LONG_TIME;
keys=(i*5)+2;// 单击释放
}
break;
case KEY_STEP_CONTINUOUS_PRESS://持续长按状态
if(KeyState[i])
{
if(!(--KeyContPressTimer[i]))
{
KeyContPressTimer[i]=KEY_PRESS_CONTINUE_TIME;
keys=(i*5)+4;//持续长按
}
}
else
{
KeyStep[i]=KEY_STEP_WAIT;
KeyContPressTimer[i]=KEY_PRESS_CONTINUE_TIME;
keys=(i*5)+5;// 长按释放
}
break;
}
if(keys)
{
if(KeyScanCBS)
{
KeyScanCBS((KEY_VALUE_TYPEDEF)keys);//实现功能
}
}
}
}
static void hal_keyConfig(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);//复用功能时钟打开
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //禁用
GPIO_InitStructure.GPIO_Pin = K1_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; //上拉输入
GPIO_Init(K1_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = K2_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(K2_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = K3_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(K3_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = K4_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(K4_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = K5_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(K5_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = K6_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(K6_PORT, &GPIO_InitStructure);
}
//读取按键状态,按下返回1,未按下返回0
static unsigned char hal_getKey1Sta(void)
{
return (!GPIO_ReadInputDataBit(K1_PORT, K1_PIN));
}
static unsigned char hal_getKey2Sta(void)
{
return (!GPIO_ReadInputDataBit(K2_PORT, K2_PIN));
}
static unsigned char hal_getKey3Sta(void)
{
return (!GPIO_ReadInputDataBit(K3_PORT, K3_PIN));
}
static unsigned char hal_getKey4Sta(void)
{
return (!GPIO_ReadInputDataBit(K4_PORT, K4_PIN));
}
static unsigned char hal_getKey5Sta(void)
{
return (!GPIO_ReadInputDataBit(K5_PORT, K5_PIN));
}
static unsigned char hal_getKey6Sta(void)
{
return (!GPIO_ReadInputDataBit(K6_PORT, K6_PIN));
}
应用层调用函数
#include "app.h"
#include "hal_led.h"
#include "hal_key.h"
static void KeyEventHandle(KEY_VALUE_TYPEDEF keys);
void AppInit(void)
{
hal_KeyScanCBSRegister(KeyEventHandle );
}
void AppProc(void)
{}
static void KeyEventHandle(KEY_VALUE_TYPEDEF keys)
{
if((keys==KEY1_CLICK)
|| (keys==KEY2_CLICK)
|| (keys==KEY3_CLICK)
|| (keys==KEY4_CLICK)
|| (keys==KEY5_CLICK)
|| (keys==KEY6_CLICK))
{
LedMsgInput(LED1,LED_LIGHT,1);//调用LED长亮接口
}else if((keys==KEY1_CLICK_RELEASE)
|| (keys==KEY2_CLICK_RELEASE)
|| (keys==KEY3_CLICK_RELEASE)
|| (keys==KEY4_CLICK_RELEASE)
|| (keys==KEY5_CLICK_RELEASE)
|| (keys==KEY6_CLICK_RELEASE))
{
LedMsgInput(LED1,LED_BLINK4,1);//LED闪烁
}else if((keys==KEY1_LONG_PRESS)
|| (keys==KEY2_LONG_PRESS)
|| (keys==KEY3_LONG_PRESS)
|| (keys==KEY4_LONG_PRESS)
|| (keys==KEY5_LONG_PRESS)
|| (keys==KEY6_LONG_PRESS))
{
LedMsgInput(LED1,LED_DARK,1);//LED熄灭
}
}
这段代码中,为了实现按键的三种状态,实际上采用了一种类似状态机的编程方法。
用switch选择的方式,创建了四种状态,当条件成立后,会转入相应的状态中,执行相应代码。
版权声明:本文为jiangfengfeng222原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。