单片机按键扫描
按键扫描
前言
最近因为工作原因使用了CA51的触摸按键功能,看了他们的按键扫描函数觉得函数比较好使用的,所以以他的框架写了一个使用32单片机的按键扫描函数。本代码也可以在51中使用,但如果你使用不到复合按键或者复合按键不超过2的话不建议使用,因为我使用的键值是32位的并且有更好的方式实现可以节省一些ram。后续如果有时间的话,再去为51优化这个代码。
一些知识点
因为我使用的是STM32F407的芯片,按键是共地的。我的开发板上只有三个按键,K0到K2分别连接的PE2到PE4。
为方便读取gpio口电平,根据位段算出的三个IO口的IDR的位段地址。


源码
头文件
#ifndef __KEY_SCANF_H__
#define __KEY_SCANF_H__
//需要自己定义uint8_t uint16_t uint32_t,其他地方已经定义了就注释掉并加入头文件
#define TYPEDEF_UINT_ENABLE
#ifdef TYPEDEF_UINT_ENABLE
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
#else
#error "加入定义uint8_t uint16_t uint32_t的头文件"
#endif
#define K1 (1UL)
#define K2 (2UL)
#define K3 (3UL)
#define K4 (4UL)
#define K5 (5UL)
#define K6 (6UL)
#define K7 (7UL)
#define K8 (8UL)
#define K9 (9UL)
#define K10 (10UL)
#define K11 (11UL)
#define K12 (12UL)
#define K13 (13UL)
#define K14 (14UL)
#define K15 (15UL)
#define K16 (16UL)
#define K17 (17UL)
#define K18 (18UL)
#define K19 (19UL)
#define K20 (20UL)
#define K21 (21UL)
#define K22 (22UL)
#define K23 (23UL)
#define K24 (24UL)
#define K25 (25UL)
#define K26 (26UL)
#define K27 (27UL)
#define K28 (28UL)
#define K29 (29UL)
#define K30 (30UL)
#define K31 (31UL)
#define KEY_LONG_BREAK (1UL<<28)//BIT28
#define KEY_LONG_START (1UL<<29)//BIT29
#define KEY_LONG (1UL<<30)//BIT30
#define KEY_BREAK (1UL<<31)//BIT31
#define COMPOUND_KEY_ENABLE //如果使用复合按键则打开此宏 打开后按键数量不能超过15个,复合键最多支持6个
#define KEY_NUM 3 //按键个数,必须和实际相同
#ifdef COMPOUND_KEY_ENABLE
#define OFFSET_SIZE (4)
#else
#define OFFSET_SIZE (5)
#endif
enum
{
NONE_KEY_PRESSED = 0,
KEY_PRESSED,
};
void key_scanf_init(void);
uint32_t key_scanf_handler(void);
#endif /* __KEY_SCANF_H__ */
源文件
#include "key_scanf.h"
#include "string.h"
//#include "stm32f4xx.h"
/*
按键驱动要自己写,如初始化函数等
key_value_update函数要实现的功能
1.更新key_dev的key_pressed_buf数组,记录按键是否按下,1按下,0松开
2.pressed_key_num记录按下的按键数量
*/
//#error "根据按键不同,驱动的不同,实现对下面按键数据的更新,实现key_value_update函数"
typedef struct
{
uint8_t key_pressed_buf[KEY_NUM]; //按键是否被按下,按键状态数组
uint8_t pressed_key_num; //记录按键按下的个数,复合键最多支持3个按键符合键
}key_pressed_t;
static key_pressed_t key_dev; //按键扫描结构体
// void key_gpio_init()
// {
// GPIO_InitTypeDef GPIO_InitStructure;
// RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOA, ENABLE);
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 |GPIO_Pin_9;
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
// GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
// GPIO_Init(GPIOB,&GPIO_InitStructure);
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
// GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
// GPIO_Init(GPIOA,&GPIO_InitStructure);
// }
// enum
// {
// KEY_STATE_RELEASE = 0,
// KEY_STATE_DEBOUNCE,
// KEY_STATE_PRESS,
// };
// #define KEY3_PA0IDR_ADDR (0x42400200)
// #define KEY1_PB8IDR_ADDR (0x42408220)
// #define KEY2_PB9IDR_ADDR (0x42408224)
void key_value_update(void)
{
//#error "根据按键不同,驱动的不同,实现对下面按键数据的更新,实现key_value_update函数"
// static uint8_t key_state[KEY_NUM]; //按键状态
// static const uint32_t key_pin[KEY_NUM] = {KEY1_PB8IDR_ADDR,KEY2_PB9IDR_ADDR,KEY3_PA0IDR_ADDR};
// #define KEY_PIN(I) *((uint32_t *)(key_pin[I]))
// //因为我打算10ms运行一次所以这么写
// uint8_t i;
// for(i=0;i<KEY_NUM-1;i++)
// {
// switch(key_state[i])
// {
// case KEY_STATE_RELEASE:
// {
// if(KEY_PIN(i) == 0)
// {
// key_dev.key_pressed_buf[i] = 0;
// key_state[i] = KEY_STATE_DEBOUNCE;
// }
// break;
// }
// case KEY_STATE_DEBOUNCE:
// {
// if(KEY_PIN(i) == 0)//按键按下
// {
// key_dev.pressed_key_num++;
// key_dev.key_pressed_buf[i] = 1;
// key_state[i] = KEY_STATE_PRESS;
// }
// break;
// }
// case KEY_STATE_PRESS:
// {
// if(KEY_PIN(i) == 1)
// {
// key_dev.key_pressed_buf[i] = 0;
// key_dev.pressed_key_num--;
// key_state[i] = KEY_STATE_RELEASE;
// }
// break;
// }
// default:key_state[i] = KEY_STATE_RELEASE;break;
// }
// }
// i = 2;
// switch(key_state[i])
// {
// case KEY_STATE_RELEASE:
// {
// if(KEY_PIN(i) == 1)
// {
// key_dev.key_pressed_buf[i] = 0;
// key_state[i] = KEY_STATE_DEBOUNCE;
// }
// break;
// }
// case KEY_STATE_DEBOUNCE:
// {
// if(KEY_PIN(i) == 1)//按键按下
// {
// key_dev.pressed_key_num++;
// key_dev.key_pressed_buf[i] = 1;
// key_state[i] = KEY_STATE_PRESS;
// }
// break;
// }
// case KEY_STATE_PRESS:
// {
// if(KEY_PIN(i) == 0)
// {
// key_dev.key_pressed_buf[i] = 0;
// key_dev.pressed_key_num--;
// key_state[i] = KEY_STATE_RELEASE;
// }
// break;
// }
// default:key_state[i] = KEY_STATE_RELEASE;break;
// }
// #undef KEY_PIN
}
/*
函数功能:按键扫描初始化
参数:无
返回值:无
*/
void key_scanf_init(void)
{
memset(&key_dev,0,sizeof(key_pressed_t));
//加入你的按键初始化代码
//#error "加入你的按键初始化代码"
key_gpio_init();
}
/* key_press_TimeCnt 根据函数调用频率来确定计时时间,我打算10ms运行一次所以这样时间常量如下设置 */
#define LONG_START_TIMECNT (100)
#define LONG_KEY_TIMECNT (25)
/*
函数功能:按键扫描处理函数
参数:无
返回值:键值
注意!
若按键符合按键按下后,得所有按键松开后才产生松开事件。
*/
uint32_t key_scanf_handler(void)
{
static uint32_t key_press_TimeCnt,key_value_old=0;
static uint8_t key_scanf_state = NONE_KEY_PRESSED;
static uint8_t key_num_old = 0,longflag = 0;
uint32_t key_value_reply = 0;
uint8_t i,offset=0;
key_value_update();
/* 将key_dev.key_pressed_buf数据转换成键值 */
if(key_dev.pressed_key_num != 0)
{
for(i=0;i<KEY_NUM;i++)
{
if(key_dev.key_pressed_buf[i] == 1)
{
key_value_reply |= ((uint32_t)(i+1)<<offset);
offset += OFFSET_SIZE;
}
}
}
else
{
if(key_scanf_state == NONE_KEY_PRESSED)
return 0;
}
switch(key_scanf_state)
{
case NONE_KEY_PRESSED://无按键按下,等待按键按下
{
key_num_old = key_dev.pressed_key_num;
key_scanf_state = KEY_PRESSED;
key_value_old = key_value_reply;
key_press_TimeCnt = 0;
longflag = 0;
return key_value_reply;
}
case KEY_PRESSED://按键按下中,等待按键松开或者另外按键按下形成复合键,超时到长按状态
{
if(key_dev.pressed_key_num == 0)
{
if(key_press_TimeCnt >= LONG_START_TIMECNT)
key_value_reply = key_value_old | KEY_LONG_BREAK;
else
key_value_reply = key_value_old | KEY_BREAK;
key_scanf_state = NONE_KEY_PRESSED;
return key_value_reply;
}else if(key_dev.pressed_key_num < key_num_old)
{
//null
}else if(key_dev.pressed_key_num == key_num_old)
{
if(key_value_old == key_value_reply)
{
key_press_TimeCnt++;
if(longflag == 0)
{
if(key_press_TimeCnt >= LONG_START_TIMECNT)
{
longflag = 1;
key_value_reply = key_value_old | KEY_LONG_START;
return key_value_reply;
}
}
else
{
if((key_press_TimeCnt%LONG_KEY_TIMECNT)==0)
{
key_value_reply = key_value_old | KEY_LONG;
return key_value_reply;
}
}
}
else
{
if(key_press_TimeCnt >= LONG_START_TIMECNT)
key_value_reply = key_value_old | KEY_LONG_BREAK;
else
key_value_reply = key_value_old | KEY_BREAK;
key_scanf_state = NONE_KEY_PRESSED;
return key_value_reply;
}
}else if(key_dev.pressed_key_num <= KEY_NUM)
{
key_value_old = key_value_reply;
key_num_old = key_dev.pressed_key_num;
longflag = 0;
key_press_TimeCnt = 0;
return key_value_reply;
}
}
}
return 0;
}
使用介绍
结构体介绍
/*
按键驱动要自己写,如初始化函数等
key_value_update函数要实现的功能
1.更新key_dev的key_pressed_buf数组,记录按键是否按下,1按下,0松开
2.pressed_key_num记录按下的按键数量
*/
//#error "根据按键不同,驱动的不同,实现对下面按键数据的更新,实现key_value_update函数"
typedef struct
{
uint8_t key_pressed_buf[KEY_NUM]; //按键是否被按下,按键状态数组
uint8_t pressed_key_num; //记录按键按下的个数,复合键最多支持3个按键符合键
}key_pressed_t;
key_pressed_buf数组索引对应K1~K28,pressed_key_num记录当前按键按下的个数。
移植使用
-
KEY_NUM 此宏应为你的按键数量
-
如果需要使用复合按键则打开宏#define COMPOUND_KEY_ENABLE
-
你得自己实现key_value_update函数,此函数需要更新key_dev结构体的值。key_scanf_handler定期调用此函数,调用频率关系到长按和持续长按事件的发生。并且此函数会调用key_value_update函数,所以你实现时也可利用此调用周期完成按键消抖等逻辑。LONG_START_TIMECNT和LONG_KEY_TIMECNT因为我的调用周期是10ms所以我长按1s产生长按事件并且从这后每250ms产生一次持续长按事件。
函数逻辑
我的key_value_update函数
此函数只适用于我的开发版,但可以提供参考。我的调用周期是10ms,所以我的消抖直接就是转换按键状态了。
下面是代码逻辑图

key_scanf_handler函数
此函数不用修改,可直接使用。不过若是51可以修改逻辑以节省ram。
代码逻辑图


浙公网安备 33010602011771号