全功能按键程序,支持消抖,长按,组合按键,按两次,三次,四次等

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

******************* main.c

while(1)
{
if (key_mode == '0')
cur_key = get_key(); // 支持延迟消抖,可重复输入按键
else
cur_key = get_key_func(); // 按键并等待按键释放,支持长按和多次按键,需打开key.c中的函数定义
// cur_key = get_key_release();// 支持延迟消抖,并等待按键释放,支持长按
if (cur_key != NO_KEY)
{
PutChar(cur_key);
PutString("\n\r");
PutString("This Key's ID is: ");
}
}
}



******************* key.h

#ifndef __DEF_H
#define __DEF_H

typedef unsigned char INT8U;
typedef unsigned int INT16U;
typedef unsigned long INT32U;
typedef signed char INT8S;
typedef signed int INT16S;
typedef signed long INT32S;
typedef float FP32; /* Single precision floating point */
typedef double FP64; /* Double precision floating point */

typedef unsigned char BOOL;
typedef unsigned char BYTE;
typedef unsigned int WORD;

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE 1
#endif

void delay_N_ns(INT16U n);

#endif /* #ifndef __DEF_H */


#ifndef __KEY_H
#define __KEY_H

#include

//#define __KEY_IDPT
#define __KEY_ARRAY
//#define __KEY_USE_TIMER // 使用定时器定时检测按键

#define NO_KEY 0 // 无按键
#define KEY_DELAY_BASE 30 // 消抖延迟时间基数
#define KEY_DITHERLESS_DELAY 100 // 按键消抖延迟时间
#define KEY_HOLD_TIME 30 // 按键按住多少时间开始重复
#define KEY_REPEAT_TIME 5 // 按键长按后的重复时间
#define KEY_LONGHOLD_TIME 50 // 按键按住多少时间开始算长按

#define KEY_HOLD_MAX 200 // 按键按住算超长按
#define KEY_TIME_MAX 8000 // 总计数次数
//#define KEY_TIME_MAX KEY_DITHERLESS_DELAY*100

#define KEY_DELAY_CHECK 0
#define KEY_LOOP_CHECK 1

// 按键状态定义
#define FSM_NOKEY 0 // 按键状态,无按键
#define FSM_KEY_DOWN 1 // 按键状态,按下
#define FSM_KEY_SURE_HOLD 2 // 按键状态,一直按住
#define FSM_KEY_UP 3 // 按键状态,松开

// ---------------------------- __KEY_IDPT ---------------------------
// Independent key defines and functions
#ifdef __KEY_IDPT

#define KEY_PORT_NUM 4 // 按键数 4
#define KEY_PORT_VAL 0x0F // 对应的IO有效位置
#define KEY_PORT_MAX 0x08 // 最高有效位
#define KEY_POR

T_MIN 0x01 // 最低有效位
#define KEY_PORT_FUNC {P1SEL &= ~KEY_PORT_VAL;} // 设置为IO模式
#define KEY_PORT_DIR {P1DIR |= KEY_PORT_VAL;} // 设置为输入
#define KEY_PORT_IN (~P1IN & KEY_PORT_VAL) // 读取输入值,低电平有效

void init_key();

#endif // #ifdef __KEY_IDPT
// ---------------------------- end of __KEY_IDPT --------------------


// ---------------------------- __KEY_ARRAY ---------------------------
// Key array defines and functions
#ifdef __KEY_ARRAY
// Key Row defines
#define KEY_ROW_NUM 4 // 行数
#define KEY_ROW_VAL 0xF0 // 行对应的IO有效位置
#define KEY_ROW_MAX 0x80 // 行最高有效位
#define KEY_ROW_MIN 0x10 // 行最低有效位
#define KEY_ROW_FUNC {P1SEL &= ~KEY_ROW_VAL;} // P5.4~P5.7设置为IO模式
#define KEY_ROW_DIR {P1DIR |= KEY_ROW_VAL;} // P5.4~P5.7设置为行扫描输出
#define KEY_ROW P1OUT // 行扫描输出端口

// Key col defines
#define KEY_COL_NUM 4 // 列数
#define KEY_COL_VAL 0x0F // 列对应的IO有效位置
#define KEY_COL_MAX 0x08 // 列最高有效位
#define KEY_COL_MIN 0x01 // 列最低有效位
#define KEY_COL_FUNC {P1SEL &= ~KEY_COL_VAL;} // P1.0~P1.3设置为IO模式
#define KEY_COL_DIR {P1DIR &= ~KEY_COL_VAL;} // P1.0~P1.3设置为列扫描输入
#define KEY_COL P1IN // 列扫描输入端口

#define KEY_COL_1 0x01 // 1列对应的IO位置
#define KEY_COL_2 0x02 // 2列对应的IO位置
#define KEY_COL_3 0x04 // 3列对应的IO位置
#define KEY_COL_4 0x08 // 4列对应的IO位置

//如果按键的行列在同一个IO口组,则可以用一句话进行端口初始化
#define KEY_PORT_VAL (KEY_ROW_VAL | KEY_COL_VAL) // 也可以直接修改定义值为数字
#define KEY_PORT_FUNC {P5SEL &= ~KEY_VAL;} // P1.0~P1.7设置为IO模式

void init_keypad();
#define key_pressed keypad_pressed //单独使用阵列键盘时,函数直接等同
// 同时使用独立按键和阵列键盘,需再定义以下函数(修改对应函数中key_pressed())
//INT8U keypad_pressed();
//INT8U check_keypad();
//INT8U get_keypad();

#endif /* #ifdef __KEY_ARRAY */
// ---------------------------- end of __KEY_ARRAY -------------------


// ---------------------------- __KEY_USE_TIMER ----------------------
// 使用定时器支持按键长按,定时(或者循环)消抖,组合按键
#ifdef __KEY_USE_TIMER
// 使用定时器支持按

键长按,定时(或者循环)消抖,组合按键

typedef struct
{
INT8U key_check_mode; // 是否在循环中检测按键的标志KEY_DELAY_CHECK或KEY_LOOP_CHECK
INT8U key_pre_value; // 时间计数
INT8U key_state; // 按键状态机状态
// INT16U key_dly_cnt; // 按键消抖延迟计数
INT16U key_hold_cnt; // 按键按下后保持计数
INT16U key_repeat_cnt; // 按键按下后重复次数计数
} struct_KeyInfo;
/*
#define _KEY_NONE 0

#define _KEY_REENTER 1
#define _KEY_NO_REENTER 2

typedef struct
{
WORD PreKey; //上次检测到的键
BYTE KeyState; //按键状态机状态
WORD SameKeyCntr; //同一键检测到按下的次数
WORD CurKey; //当前检测到的键, 用于处理长按的情况
BYTE (*KeyDownCallBack)(WORD, WORD); //键确认按下的回调函数指针
void (*KeyUpCallBack)(WORD); //键抬起的回调函数指针
} struct_KeyInfo;
*/
// ---------------------------- else of __KEY_USE_TIMER
#else
// 不使用定时器检测按键,但支持按键按下和松开,按键长按等功能
typedef struct
{
INT8U key_check_mode; // 是否在循环中检测按键的标志KEY_DELAY_CHECK或KEY_LOOP_CHECK
INT8U key_pre_value; // 时间计数
INT8U key_state; // 按键状态机状态
INT16U key_dly_cnt; // 按键消抖延迟计数
INT16U key_hold_cnt; // 按键按下后保持计数
INT16U key_repeat_cnt; // 按键按下后重复次数计数
} struct_KeyInfo;

#endif // #ifdef __KEY_USE_TIMER
// ---------------------------- end of __KEY_USE_TIMER ---------------

extern struct_KeyInfo key_info1; // 可以定义多套按键

INT8U read_key(); // 读键,并根据映射返回键值
BOOL key_pressed(); // 检测是否有按键按下
INT8U check_key(INT8U, struct_KeyInfo *); // 检测按键键值
INT8U get_key(); // 支持延迟消抖,可重复输入按键
INT8U get_key_release(); // 支持延迟消抖,并等待按键释放,支持长按
INT8U get_key_func(); // 按键并等待按键释放,支持长按和多次按键


#endif /* #ifndef __KEY_H */






*******************key.c


//--------------------------------------------------------------------------
//独立按键和阵列式按键,不支持同时按键
//返回按键ASCII码
//支持按下一次有效,get_key()
//按下后重复有效,get_key()
//按下后释放按键才有效,get_key_release()或get_key_func()
//长按有效,get_key_release()或get_key_func()
//连续按下多次有效(1、2、3、4),get_key_func()
//-------------

-------------------------------------------------------------
#include "key.h"

//----------------------------------全局变量--------------------------------
#ifndef __KEY_USE_TIMER
struct_KeyInfo key_info1 = // 可以定义多套按键
{
KEY_LOOP_CHECK, // INT8U key_check_mode; // 检测按键的标志KEY_DELAY_CHECK或KEY_LOOP_CHECK
NO_KEY, // INT8U key_pre_value; // 时间计数
FSM_NOKEY, // INT8U key_state; // 按键状态机状态
0, // INT16U key_dly_cnt; // 按键消抖延迟计数
0, // INT16U key_hold_cnt; // 按键按下后保持计数
0 // INT16U key_repeat_cnt; // 按键按下后重复次数计数
};
#endif // #ifndef __KEY_USE_TIMER

#ifdef __KEY_IDPT
//键值映射
INT8U key_map[] = {0, '1', '2', 0, '3', 0, 0, 0, '4',
0, 0, 0, 0, 0, 0, 0};
#endif /* #ifdef __KEY_IDPT */

#ifdef __KEY_ARRAY
//键值映射
INT8U keypad_map[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'A', 'B', 'C', 'D', 'E', 'F'};
#endif /* #ifdef __KEY_ARRAY */


//--------------------------------------------------------------------------
//函数名称:delay_key
//功 能:延时
//参 数:struct_KeyInfo * pKey_Info: 按键组信息
//返回值 :无
//--------------------------------------------------------------------------
void delay_key(struct_KeyInfo * pKey_Info)
{
INT8U i, j;

if (pKey_Info->key_check_mode)
{
if (pKey_Info->key_dly_cnt < KEY_DITHERLESS_DELAY)
{
pKey_Info->key_dly_cnt++;
}
else
{
pKey_Info->key_dly_cnt = 0;
}
// key_dly_over = (key_dly_cnt == KEY_DITHERLESS_DELAY);
}
else
{
pKey_Info->key_dly_cnt = 0;
j = KEY_DITHERLESS_DELAY;
while (j-- > 0)
for (i = KEY_DELAY_BASE; i > 0; i--);
}
}


#ifdef __KEY_IDPT
//--------------------------------------------------------------------------
//函数名称:init_key
//功 能:初始化扫描键盘的IO端口
//参 数:无
//返回值 :无
//--------------------------------------------------------------------------
void init_key()
{
KEY_PORT_FUNC;
KEY_PORT_DIR;
}

//--------------------------------------------------------------------------
//函数名称:key_pressed
//功 能:检测按键
//参 数:无
//返回值 :0:无按键 1:有按键
//--------------------------------------------------------------------------
BOOL key_pressed()
{
return (KEY_PORT_IN != 0); //获取输入
// 返回0无按键按下,否则有键按下
}

INT8U read_key()
{
return (key_map[KEY_PORT_IN]);
}

#endif /* #ifdef __KEY_IDPT */

#ifdef __KEY_ARRAY

//-------------------------------------------------

-------------------------
//函数名称:init_keypad
//功 能:初始化扫描键盘的IO端口
//参 数:无
//返回值 :无
//--------------------------------------------------------------------------
void init_keypad()
{
KEY_COL_FUNC;
KEY_COL_DIR;
KEY_ROW_FUNC;
KEY_ROW_DIR;
KEY_ROW |= KEY_ROW_VAL; // 输出高电平
}

//--------------------------------------------------------------------------
//函数名称:key_pressed
//功 能:检测按键
//参 数:无
//返回值 :0:无按键 1:有按键
//--------------------------------------------------------------------------
BOOL keypad_pressed()
{
KEY_ROW &= ~KEY_ROW_VAL; //行输出全0,判断有无按键输入
return ((~KEY_COL & KEY_COL_VAL) != 0); //获取列输入,低有效
// 返回0无按键按下,否则有键按下
}

//--------------------------------------------------------------------------
//函数名称:read_key
//功 能:扫描键盘的IO端口,获得键值
//参 数:无
//返回值 :INT8U 按键的ASCII码
//--------------------------------------------------------------------------
INT8U read_key()
{
// 使用循环扫描和循环查询的方式
INT8U key_val;
INT8U row_cnt, col_cnt, row_scanout, col_scanin;

key_val = 0;
row_scanout = KEY_ROW_MAX; // 行从高到低按row_scanout扫描
// row_scanout = KEY_ROW_MIN; // 行从低到高按row_scanout扫描
for (row_cnt = 0; row_cnt < KEY_ROW_NUM; row_cnt++) //--行扫描
{
KEY_ROW |= KEY_ROW_VAL; //行输出全1
KEY_ROW &= (~row_scanout); //行输出中有一个为0
row_scanout >>= 1; // 行从高到低按row_scanout扫描
// row_scanout <<= 1; // 行从低到高按row_scanout扫描
if ((~KEY_COL & KEY_COL_VAL) != 0) //是否列中有一位有效,低有效
{
// col_scanin = KEY_COL_MAX; // 列从高到低col_scanin用于检测哪一位为0
col_scanin = KEY_COL_MIN; // 列从低到高col_scanin用于检测哪一位为0
for (col_cnt = 0; col_cnt < KEY_COL_NUM; col_cnt++) //--列检测
{
if ((KEY_COL & col_scanin) == 0) // 是否是该列, 等于0为是
{
// 查表获取键值
key_val = keypad_map[row_cnt * KEY_COL_NUM + col_cnt];
return (key_val); // 返回值,退出循环
}
// col_scanin >>= 1; // 列从高到低col_scanin右移1位
col_scanin <<= 1; // 列从低到高col_scanin左移1位
}
}
}

// 使用直接扫描和case查询的方式
/*
INT8U key_val;
INT8U row_cnt, row_scanout, col_scanin;

key_val = 0;
row_cnt = 0;
//行扫描
for (row_scanout = KEY_ROW_MAX; row_cnt >= K

EY_ROW_MIN; row_cnt >>= 1)
// for (row_scanout = KEY_ROW_MIN; row_cnt <= KEY_ROW_MAX; row_cnt <<= 1)
{
KEY_ROW |= KEY_ROW_VAL; //行输出全1
KEY_ROW &= (~row_scanout); //行输出四位中有一个为0

col_scanin = ~KEY_COL & KEY_COL_VAL; // 列col_scanin用于检测出哪一位为0
switch (col_scanin) // 列检测,同一时刻只能有一个按键按下
{
case KEY_COL_1:
key_val = keypad_map[row_cnt * KEY_COL_NUM]; // 查表获取键值
break;
case KEY_COL_2:
key_val = keypad_map[row_cnt * KEY_COL_NUM + 1]; // 查表获取键值
break;
case KEY_COL_3:
key_val = keypad_map[row_cnt * KEY_COL_NUM + 2]; // 查表获取键值
break;
case KEY_COL_4:
key_val = keypad_map[row_cnt * KEY_COL_NUM + 3]; // 查表获取键值
break;
case default:
key_val = 0;
break;
}
row_cnt++;
}
*/
return (key_val); // 返回值
}

#endif /* #ifdef __KEY_ARRAY */

//--------------------------------------------------------------------------
//函数名称:key_callback
//功 能:按键回调函数,用于长按和重复计数
//参 数:INT8U key_holdtime: 保持计数次数
// struct_KeyInfo * pKey_Info: 按键组信息
//返回值 :无
//--------------------------------------------------------------------------
void key_callback(INT8U key_holdtime, struct_KeyInfo * pKey_Info)
{
if (pKey_Info->key_hold_cnt <= key_holdtime)
pKey_Info->key_hold_cnt++;
else
{
if (pKey_Info->key_hold_cnt <= KEY_HOLD_MAX) // 计算超长按
pKey_Info->key_hold_cnt++;
if (pKey_Info->key_repeat_cnt <= KEY_REPEAT_TIME) // 进入重复
pKey_Info->key_repeat_cnt++;
else
pKey_Info->key_repeat_cnt = 0;
}
}

//--------------------------------------------------------------------------
//函数名称:key_fsm
//功 能:检测按键状态转换机转换函数
//参 数:INT8U key_value: 读到的键值
// INT8U key_holdtime: 保持计数次数
// struct_KeyInfo * pKey_Info: 按键组信息
//返回值 :无
//--------------------------------------------------------------------------
void key_fsm(INT8U key_value, INT8U key_holdtime, struct_KeyInfo * pKey_Info)
{
switch (pKey_Info->key_state) // 根据新获得的键值转换按键状态,需区分两种检测方式
{
case FSM_NOKEY: // 无按键
pKey_Info->key_pre_value = NO_KEY;
if (key_value != NO_KEY) // key_value有值
{
if (pKey_Info->key_check_mode) // 循环检测模式,进入消抖
{
pKey_Info->key_state = FSM_KEY_DOWN; // 进

入消抖
}
else // 延迟检测模式(已经消抖)
{
pKey_Info->key_hold_cnt = 0;
pKey_Info->key_repeat_cnt = 0;
pKey_Info->key_pre_value = key_value; // 备份上一次键值
pKey_Info->key_state = FSM_KEY_SURE_HOLD; // 延迟检测模式,按键有效
}
}
else
{
pKey_Info->key_state = FSM_NOKEY;
}
break;

case FSM_KEY_DOWN: // 循环检测模式按键可能按下,用于消抖
if (!pKey_Info->key_check_mode || // 延迟检测模式或者循环满足延迟
(pKey_Info->key_dly_cnt == KEY_DITHERLESS_DELAY))
if (key_value != NO_KEY) // key_value有值
{
pKey_Info->key_hold_cnt = 0;
pKey_Info->key_repeat_cnt = 0;
pKey_Info->key_pre_value = key_value; // 备份上一次键值
pKey_Info->key_state = FSM_KEY_SURE_HOLD; // 按键有效
}
else
{
pKey_Info->key_state = FSM_NOKEY; // 按键无效
}
else ; // 循环检测模式未满足延迟,保持状态
break;

case FSM_KEY_SURE_HOLD: // 按键确定按下,长按后可检测连续有效几次
if (key_value != NO_KEY)
{
pKey_Info->key_state = FSM_KEY_SURE_HOLD;
if (!pKey_Info->key_check_mode ||
(pKey_Info->key_dly_cnt == KEY_DITHERLESS_DELAY))
{
pKey_Info->key_pre_value = key_value; // 备份上一次键值
key_callback(key_holdtime, pKey_Info);
}
}
else
{
pKey_Info->key_state = FSM_KEY_UP;
}
break;

case FSM_KEY_UP: // 释放按键瞬间,可用于按键有效一次的情况
if (key_value != NO_KEY) // key_value有值
{
if (key_value == pKey_Info->key_pre_value)
{
pKey_Info->key_hold_cnt = 0;
pKey_Info->key_repeat_cnt = 0;
pKey_Info->key_state = FSM_KEY_SURE_HOLD;
}
else
pKey_Info->key_state = FSM_KEY_DOWN;
}
else
{
pKey_Info->key_state = FSM_NOKEY;
}
break;

default:
pKey_Info->key_state = FSM_NOKEY;
break;
}
}

//--------------------------------------------------------------------------
//函数名称:check_key
//功 能:检测按键(带消抖),并获取键值
//参 数:INT8U key_holdtime: 保持计数次数
// struct_KeyInfo * pKey_Info: 按键组信息
//返回值 :INT8U 按键的ASCII码
//----------------

----------------------------------------------------------
INT8U check_key(INT8U key_holdtime, struct_KeyInfo * pKey_Info)
{
INT8U key_value;

key_value = key_pressed(); // 获取按键按下情况
if (key_value != NO_KEY) // 如果有键按下
{
delay_key(pKey_Info); // 消除抖动
key_value = read_key(); // 再次检测按键,并转换键值
}
else
pKey_Info->key_dly_cnt = 0;

key_fsm(key_value, key_holdtime, pKey_Info);
return (key_value);
}

//--------------------------------------------------------------------------
//函数名称:get_key
//功 能:检测按下按键(带消抖),并获取键值,支持多次输入
// 需要多组按键时,可以定义多个struct_KeyInfo key_info1,
// 并定义多个get_key使用其即可
//参 数:无
//返回值 :INT8U 按键有效时的ASCII码,否则为NO_KEY
//--------------------------------------------------------------------------
INT8U get_key()
{
INT8U key_value;

key_value = check_key(KEY_HOLD_TIME, &key_info1);
if ( ( key_info1.key_state == FSM_KEY_SURE_HOLD ) &&
(!key_info1.key_check_mode ||
(key_info1.key_dly_cnt == KEY_DITHERLESS_DELAY)) &&
((key_info1.key_hold_cnt == 0) ||
(key_info1.key_hold_cnt == KEY_HOLD_TIME) ||
(key_info1.key_repeat_cnt == KEY_REPEAT_TIME)) )
{
return (key_value);
}
else
return (NO_KEY);
}

//--------------------------------------------------------------------------
//函数名称:get_key_release
//功 能:检松开按键(带消抖)和长按,并获取键值(没有支持超长按)
// 需要多组按键时,可以定义多个struct_KeyInfo key_info1,
// 并定义多个get_key_release使用其即可
//参 数:无
//返回值 :INT8U 按键有效时的ASCII码,否则为NO_KEY
//--------------------------------------------------------------------------
INT8U get_key_release()
{
// INT8U key_value;

// key_value = check_key();
check_key(KEY_LONGHOLD_TIME, &key_info1);
if (key_info1.key_hold_cnt < KEY_LONGHOLD_TIME)
if (key_info1.key_state == FSM_KEY_UP)
return (key_info1.key_pre_value);
else
return (NO_KEY);
else
if ( (key_info1.key_state == FSM_KEY_SURE_HOLD) &&
(!key_info1.key_check_mode ||
(key_info1.key_dly_cnt == KEY_DITHERLESS_DELAY)) &&
(key_info1.key_hold_cnt == KEY_LONGHOLD_TIME) )
{
#if 0 //这段是测试程序,可根据需要修改使用,打开 #if 开关即可
return (key_info1.key_pre_value | 0x80);
#else
if (key_info1.key_pre_value < '9')
return (key_info1.key_pre_

value - '0' + 'a');
else
return (key_info1.key_pre_value - 'A' + 'a' + 10);
#endif
}
else
return (NO_KEY);
}

#if 1 //这是一个测试程序,可根据需要修改使用,打开 #if 开关即可
//--------------------------------------------------------------------------
//函数名称:get_key_func
//功 能:检松开按键(带消抖)、长按和连续多次按键,并获取键值
//参 数:无
//返回值 :INT8U 按键有效时的ASCII码,否则为NO_KEY
//--------------------------------------------------------------------------
INT8U get_key_func()
{
static INT16U key_time_cnt = 0;
static INT8U key_cnt = 0;
static INT8U pre_key;
INT8U cur_key;

key_time_cnt ++;
if (key_time_cnt < KEY_TIME_MAX)
{
cur_key = get_key_release();
if ((cur_key != NO_KEY) && (key_cnt == 0 || cur_key == pre_key))
{
pre_key = cur_key;
key_cnt ++;
}
cur_key = NO_KEY;
}
else
{
if (pre_key > 'a') // 长按
cur_key = pre_key;
else if (key_cnt == 0) // 没按
cur_key = NO_KEY;
else if (key_cnt == 1) // 按1次
cur_key = pre_key;
else if (key_cnt == 2) // 按2次
cur_key = 'x';
else if (key_cnt == 3) // 按3次
cur_key = 'y';
else if (key_cnt == 4) // 按4次
cur_key = 'z';
else // 或者按5次以上
cur_key = '@';

pre_key = NO_KEY;
key_time_cnt = 0;
key_cnt = 0;
}
return (cur_key);
}
#endif


相关文档
最新文档