STM32电子按键设计指南
一、电子按键的工作原理
按键开关内部构造主要就是金属弹簧,当按下按键时弹簧片与电路短接,当按键弹起时弹簧片与电路为开路状态。
1.1、独立按键
一端接GPIOx一端接地。GPIOx设置为上拉输入模式,当按键按下时GPIOx读取的为低电平,当按键弹起时GPIOx读取的为高电平。
1.2、矩阵按键
二、编码
2.1、GPIO引脚配置
// GPIO时钟
#define GPIOx_RCC_APB2Periph RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC \
RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOF \
RCC_APB2Periph_GPIOG
void GPIOx_config(st_u32 GPIOx_RCC,GPIO_TypeDef *GPIOxPORT,st_u16 GPIOPin,GPIOMode_TypeDef GPIOMode)
{
RCC_APB2PeriphClockCmd(GPIOx_RCC, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
/* Configure PD0 and PD2 in output pushpull mode */
GPIO_InitStructure.GPIO_Pin =GPIOPin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIOMode;
GPIO_Init(GPIOxPORT, &GPIO_InitStructure);
}
2.2、引脚初始化
void KeyInit(void)
{
unsigned char i;
for(;i<7;i++)
{
KeyNumber[i]=0x00;
}
GPIOx_Config(RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_6,GPIO_Mode_Out_PP);
GPIOx_Config(RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_4,GPIO_Mode_Out_PP);
GPIOx_Config(RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_5,GPIO_Mode_Out_PP);
GPIOx_Config(RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_8,GPIO_Mode_Out_PP);
// GPIOx_Config(RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_9,GPIO_Mode_IN_FLOATING);
// GPIOx_Config(RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_12,GPIO_Mode_IN_FLOATING);
// GPIOx_Config(RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_13,GPIO_Mode_IN_FLOATING);
//
// GPIO_SetBits(GPIOB, GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_12|GPIO_Pin_13);
GPIO_SetBits(GPIOB, GPIO_Pin_6);
GPIO_SetBits(GPIOB, GPIO_Pin_4);
GPIO_SetBits(GPIOB, GPIO_Pin_5);
GPIO_SetBits(GPIOB, GPIO_Pin_8);
// KEYA = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7);
}
2.2.1、测试
void KeyPortBitTest(void)
{
if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_3)==0)
{
printf("F\r\n");
}
// 读取输入引脚电平
ReadLed=GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13); // LED
ReadGP9=KEY_A;
ReadGP1=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6);
ReadGP2=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_4);
ReadGP3=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_5);
ReadGP4=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8);
// 读取输出引脚电平
ReadGP5=GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_6);
ReadGP6=GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_4);
ReadGP7=GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5);
ReadGP8=GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_8);
// 测试未初始化配置引脚电平
// 输出
ReadGP10=GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_9);
ReadGP11=GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_12);
// 输入
ReadGP12=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9);
ReadGP13=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12);
}
加点量
#ifndef KEY_H
#define KEY_H
extern void KeyInit(void);
extern void Get_Key(void);
//extern uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
extern void KeyPortBitTest(void);
#define KEY_ON 1 // 按键按下与GND短接
#define KEY_OFF 0 // 按键弹起与GND断开
#define KEY_RCC RCC_APB2Periph_GPIOB
#define KEY_PORT GPIOB
#define KEY_A GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6)
#define KEY_B GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_4)
#define KEY_C GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_5)
#define KEY_D GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8)
#define KEY_E GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)
#define KEY_F GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)
#define Remote_Sensing_Key GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)
//GPIO_ReadOutputDataBit
//#define KEY_A GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_3)
//#define KEY_B GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_4)
//#define KEY_C GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5)
//#define KEY_D GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_8)
//#define KEY_E GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_9)
//#define KEY_F GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_12)
//#define Remote_Sensing_Key GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_13)
#define KeyDown_Null 0x00
#define KeyDown_Forward 0x01
#define KeyDown_Back 0x02
#define KeyDown_Right 0x03
#define KeyDown_Left 0x04
#define KeyDown_Accelerate 0x05
#define KeyDown_Moderate 0x06
#define KeyDown_Stop 0x07
enum key_NUMBER
{KEYNULL,KEYA,KEYB,KEYC,KEYD,KEYE,KEYF,KEYyg,};
ext unsigned char KeyScanfTime;
ext unsigned short int KeySelect;
ext unsigned char KeyNumber[7];
ext unsigned char ReadGP1;
ext unsigned char ReadGP2;
ext unsigned char ReadGP3;
ext unsigned char ReadGP4;
ext unsigned char ReadGP5;
ext unsigned char ReadGP6;
ext unsigned char ReadGP7;
ext unsigned char ReadGP8;
ext unsigned char ReadGP9;
ext unsigned char ReadGP10;
ext unsigned char ReadGP11;
ext unsigned char ReadGP12;
ext unsigned char ReadGP13;
#endif
void Get_Key(void)
{
if(KeyScanfTime<10)
{
KeyScanfTime++;
}
else
{
KeyScanfTime=0;
switch(Number1) // 确保只有一个按键按下,使用枚举类型
{
case KEYNULL:
Number1=KEYA;
break;
case KEYA:
// KeyNumber[0]=KEY_A&0xFF; // 1111 1110 按下
KeyNumber[0]=KEY_A&0x01; // 0000 0001
Number1=KEYB;
break;
case KEYB:
//KeyNumber
//KeyNumber[1]=(KEY_B<<0x01)&0xFF; // 1111 1101
KeyNumber[1]=KEY_B<<0x01; // 0000 0010
Number1=KEYC;
break;
case KEYC:
//KeyNumber[2]=(KEY_C<<0x02)&0xFF; // 1111 1011
KeyNumber[2]=KEY_C<<0x02;
Number1=KEYD;;
break;
case KEYD:
// KeyNumber[3]=(KEY_D<<0x03)&0xFF; // 1111 0111
KeyNumber[3]=KEY_D<<0x03;
Number1=KEYE;
break;
case KEYE:
//KeyNumber[4]=(KEY_E<<0x04)&0xFF; // 1110 1111
KeyNumber[4]=KEY_E<<0x04;
Number1=KEYF;
break;
case KEYF:
//KeyNumber[5]=(KEY_F<<0x05)&0xFF; // 1101 1111
KeyNumber[5]=KEY_F<<0x05;
Number1=KEYyg;
break;
case KEYyg:
// KeyNumber[6]=(Remote_Sensing_Key<<0x06)&0xFF; // 1011 1111
KeyNumber[6]=Remote_Sensing_Key<<0x06;
Number1=KEYNULL;
break;
default:
break;
}
}
}
按键弹起
按键按下
三、按键算法模板
3.1、使用枚举定义按键列表
typedef enum // 按键列表
{
KEY_S1,
KEY_S2,
KEY_S3,
KEY_S4,
KEY_S5,
KEY_S6,
KEYNUM
}KEY_TYPEDEF;
typedef enum // 检测按键流程 通过switch遍历数据
{
KEY_STEP_WAIT, // 等待按键
KEY_STEP_CLICK, // 按键按下
KEY_STEP_LOGN_PRESS, // 长按
KEY_STEP_CONTINOUS_PRESS, // 持续按下
}KEY_STEP_TYPEDEF;
extern unsigned char ScanfStep[KEYNUM]; // 按键
3.2、初始化按键IO
static void KeyConfig(void)
{
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// key1 PB3
GPIOConfig( RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_3,GPIO_Speed_50MHz,GPIO_Mode_IPU);
// key2 PB5
GPIOConfig( RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_5,GPIO_Speed_50MHz,GPIO_Mode_IPU);
// key3 PB6
GPIOConfig( RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_6,GPIO_Speed_50MHz,GPIO_Mode_IPU);
// key4 PB7
GPIOConfig( RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_7,GPIO_Speed_50MHz,GPIO_Mode_IPU);
// key5 PB10
GPIOConfig( RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_10,GPIO_Speed_50MHz,GPIO_Mode_IPU);
// key6 PB11
GPIOConfig( RCC_APB2Periph_GPIOB,GPIOB,GPIO_Pin_11,GPIO_Speed_50MHz,GPIO_Mode_IPU);
}
3.3、IO口封装函数
// 时钟 端口 引脚 输入输出速度 输出输入模式
void GPIOConfig(uint32_t RCC_APB2Periph,GPIO_TypeDef* GPIOx,uint16_t GPIOPIN,GPIOSpeed_TypeDef GPIOSPEED,GPIOMode_TypeDef GPIOMODE)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIOPIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIOMODE; ;
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
3.4、获取按键的状态函数
// 根据原理图知按键按下为低电平,按键未按下为高电平 取反后按键按下为IO口高电平 按键为按下IO为低电平
static unsigned char GetKey1State(void)
{
return (!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_3));
}
static unsigned char GetKey2State(void)
{
return (!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_5));
}
static unsigned char GetKey3State(void)
{
return (!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6));
}
static unsigned char GetKey4State(void)
{
return (!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7));
}
static unsigned char GetKey5State(void)
{
return (!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10));
}
static unsigned char GetKey6State(void)
{
return (!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11));
}
3.5、定义数组保存按键状态
unsigned char (*GetKeysState[KEYNUM])()={GetKey1State,GetKey2State,GetKey3State,GetKey4State,GetKey5State,GetKey6State};
3.6、按键值与按键回掉函数的定义
typedef enum // 按键值定义
{
KEY_IDLE_VAL, //0
KEY1_CLICK, //1 KEY1 单击确认
KEY1_CLICK_RELEASE, //2 KEY1 单击释放
KEY1_LONG_PRESS, //3 KEY1 长按确认
KEY1_LONG_PRESS_CONTINUOUS, //4 KEY1 长按持续
KEY1_LONG_PRESS_RELEASE, //5 KEY1 长按释放
KEY2_CLICK, //6
KEY2_CLICK_RELEASE,
KEY2_LONG_PRESS,
KEY2_LONG_PRESS_CONTINUOUS,
KEY2_LONG_PRESS_RELEASE,
KEY3_CLICK, //11
KEY3_CLICK_RELEASE,
KEY3_LONG_PRESS,
KEY3_LONG_PRESS_CONTINUOUS,
KEY3_LONG_PRESS_RELEASE,
KEY4_CLICK, //16
KEY4_CLICK_RELEASE,
KEY4_LONG_PRESS,
KEY4_LONG_PRESS_CONTINUOUS,
KEY4_LONG_PRESS_RELEASE,
KEY5_CLICK, //21
KEY5_CLICK_RELEASE,
KEY5_LONG_PRESS,
KEY5_LONG_PRESS_CONTINUOUS,
KEY5_LONG_PRESS_RELEASE,
KEY6_CLICK, //26
KEY6_CLICK_RELEASE,
KEY6_LONG_PRESS,
KEY6_LONG_PRESS_CONTINUOUS,
KEY6_LONG_PRESS_RELEASE,
}KEY_VALUE_TYPEDEF;
// 指针函数 返回值类型 指针函数名(参数一,参数二)
typedef void (*KeyEvent_CallBack_t)(KEY_VALUE_TYPEDEF keys);
KeyEvent_CallBack_t KeyScanCBS;
void hal_keyScanCBSRegister(KeyEvent_CallBack_t pCBS)
{
if(KeyScanCBS==0)
{
KeyScanCBS = pCBS;
}
}
3.7、按键扫描流程
unsigned char KeyStep[KEYNUM]; // 按键检查流程
unsigned char KeyScanTimer[KEYNUM]; // 消抖延时
unsigned char KeyPressLongTimer[KEYNUM]; // 长按延时
unsigned char KeyContPressTimer[KEYNUM]; // 连续长按延时
void hal_KeyProc(void)
{
unsigned char i,KeyState[KEYNUM];
for(i=0;i<KEYNUM;i++) // 遍历按键
{
keys=0;
KeyState[i]=GetKeysState[i](); // 获取按键状态 1~6
switch(KeyStep[i])
{
case KEY_STEP_WAIT: // 等待按键
if(KeyState[i]) // 获取到按键状态
{
KeyStep[i]=KEY_STEP_CLICK;
}
break;
case KEY_STEP_CLICK: // 按键单击按下
if(KeyState[i])
{
if(!(--KeyScanTimer[i]))
{
KeyScanTimer[i]=KEY_SCANTIME;
KeyStep[i]=KEY_STEP_LOGN_PRESS;
keys=(i*5)+1;
}
}
else
{
KeyScanTimer[i]=KEY_SCANTIME;
KeyStep[i]=KEY_STEP_WAIT;
}
break;
case KEY_STEP_LOGN_PRESS: // 长按按键
if(KeyState[i])
{
if(!(--KeyPressLongTimer[i])) // 计数结束 等于0
{
KeyPressLongTimer[i]=KEY_PRESS_CONINUE_TIME;
KeyStep[i]=KEY_STEP_CONTINOUS_PRESS;
keys=(i*5)+3; // 长按确认
}
}
else
{
KeyPressLongTimer[i]=KEY_PRESS_CONINUE_TIME;
KeyStep[i]=KEY_STEP_WAIT;
keys=(i*5)+2; // 单击释放
}
break;
case KEY_STEP_CONTINOUS_PRESS: // 持续长按
if(KeyState[i])
{
if(!(--KeyContPressTimer[i])) // 先自减 再判断值是否等于0
{
KeyContPressTimer[i]=KEY_PRESS_CONINUE_TIME; // 连续长按延时
keys=(i*5)+4;
}
}
else
{
KeyStep[i]=KEY_STEP_WAIT;
KeyContPressTimer[i]=KEY_PRESS_CONINUE_TIME; // 连续长按延时
keys=(i*5)+5;
}
break;
default:
break;
}
// 扫描按键
if(keys)// 按键ID 0~5
{
if(KeyScanCBS)
{
KeyScanCBS((KEY_VALUE_TYPEDEF)keys);
}
}
}
}
事件回调时间
static void KeyEventHandle(KEY_VALUE_TYPEDEF keys);
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))
{
}
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))
{
}
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))
{
}
else if(keys==KEY1_LONG_PRESS_CONTINUOUS
||keys==KEY2_LONG_PRESS_CONTINUOUS
||keys==KEY3_LONG_PRESS_CONTINUOUS
||keys==KEY4_LONG_PRESS_CONTINUOUS
||keys==KEY5_LONG_PRESS_CONTINUOUS
||keys==KEY6_LONG_PRESS_CONTINUOUS) // 长按持续
{
}
else if(keys==KEY1_LONG_PRESS_RELEASE
||keys==KEY2_LONG_PRESS_RELEASE
||keys==KEY3_LONG_PRESS_RELEASE
||keys==KEY4_LONG_PRESS_RELEASE
||keys==KEY5_LONG_PRESS_RELEASE
||keys==KEY6_LONG_PRESS_RELEASE) // 长按释放
{
}
}
作者:旭日初扬