-
Notifications
You must be signed in to change notification settings - Fork 50
/
key.h
115 lines (91 loc) · 8.41 KB
/
key.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#ifndef __KEY_H
#define __KEY_H
#include "sys.h"
//////////////////////////////////////////////////////////////////////////////////
//本扫描键盘模块的特点:
//一、使用灵活:一体实现按键的普通、单击、双击、长按、保持以及组合等功能,无须事前为每个按键每种键值逐一进行宏定义,也无须逐一编写各事件的条件判断,
// 只须为需要的按键事件编写相应的响应代码即可,同时留有特殊键组合等的扩展接口;
// 可以选择每一按键事件的处理实时性,从而能够使强实时性的紧急按键优先得到处理,可自由选择中断处理及查询处理或二者混合的处理方式,
// 灵活适配使应用项目能够兼备按键的强实时性要求以及超长(主循环执行一遍的时间长达1秒以上的)程序的适应性。
//二、注重通用:模块设计时注重通用性,按键事件(键值)依简单易懂的标准事件格式编写;除能满足几乎所有按键应用需求外,在按键数量上,
// 从少到2-4个按键直到最大32个按键(包括端口直联、行列式矩阵、矩阵加直联混合)都可适用。
//三、稳定可靠:后台智能抖动消除、按键干扰杂波滤除措施有力,获取按键稳定可靠,不会产生重复按键,即使在CPU非常繁忙时也不会漏失按键。
//四、移植简便:所有可调整参数(数量不多)均以宏定义列出,除与硬件相关(按键个数及连接端口)的部分须根据具体系统修改外,其它均无须变化,很容易移植。
// 程序可读性强,注释详尽丰富,其中包括函数调用关系及详细运用修改说明,如有未尽事宜,可提出探讨,本人尽量解答修改。
//五、高效节能:消抖无须延时等待,同时采取自适应变频扫键、键盘闲置检测、消抖读键双进程周期差异等多项智能措施尽量减少占用CPU的计算资源。
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//测试平台:ALIENTEK战舰STM32开发板
//按键驱动代码
//正点原子@ALIENTEK
//技术论坛:https://www.openedv.com/forum.php?mod=viewthread&tid=277263,有问题可在本帖中提出讨论,谢谢。
//修改日期:2018/9/1
//版本:V2.2
//Made by warship
//////////////////////////////////////////////////////////////////////////////////
//根据需求可修改定义如下参数.
#define TICKS_INTERVAL 5 //ms, 后台调用间隔时间
#define DEBOUNCE_TICKS 4 //消除抖动次数,即时长至少为5ms*4
#define NORMAL_SCAN_FREQ 6 //正常情况下扫键频率因子,如为6则表示稳定后扫键周期为6*TICKS_INTERVAL=30ms
#define SHORT_TICKS (300 /TICKS_INTERVAL) //短按时间定义300ms
#define LONG_TICKS (1200 /TICKS_INTERVAL) //长按时间定义1200ms
#define KEYBUFFSIZE 16 //按键缓存FIFO深度,定义保存16个键值
//***************** 以下与具体系统的硬件相关 ********************************************
#define KeyNumMax 4 //硬件实体按键数量
typedef u16 KeyS_Type; //键态字类型定义(根据按键总数定义足够长度的数据类型,
//本例程只有4个键,用u8足矣,但为扩充方便这里用了u16,最大可满足16键,大于16键时请定义为u32)
//按键硬件读端口位置
#define KEY0_IN PEin(4) //按键0输入端口
#define KEY1_IN PEin(3) //按键1输入端口
#define KEY2_IN PEin(2) //按键2输入端口
#define WKUP_IN PAin(0) //按键3输入端口(WK_UP)
//硬件实体按键编号,键态字KeyS_Type依此顺序按位组合,每BIT位对应一个实体按键
#define KB_KEY0 0 //规定KEY0键用键态字的第0号bit位表示,该位为1表示KEY0键处于按下状态
#define KB_KEY1 1 //规定KEY1键用键态字的第1号bit位表示,该位为1表示KEY1键处于按下状态
#define KB_KEY2 2 //规定KEY2键用键态字的第2号bit位表示,该位为1表示KEY2键处于按下状态
#define KB_WKUP 3 //规定KEY2键用键态字的第3号bit位表示,该位为1表示KEY3键处于按下状态
//************ 以下基本与硬件无关(除增删组合键码定义外一般无须修改) *********************
//定义一个特殊值用于复位状态机
#define KB_CLR 44
//这里可以定义一些特殊键码(如组合键等)
#define WKUP_PLUSKEY0_PRES 1 //示例:WKUP+KEY0组合按键(先按下WKUP再按下KEY0)
//功能键值输出:
#define KEYOUT_BASE_DEF 10*(KeyNum+2) //为保留组合键值等空间,这里定义为20以上,可视情修改)
#define KEY_EVENT(m,n) (u8)(10*(m+2)+n) //按键事件(即键值)宏定义
//有了上述宏定义后,无须再为各个按键单独写宏定义,使用KEY_EVENT(键编号,键值事件)就可以代表特定按键事件了。
//例如:用KEY_EVENT(KB_WKUP,DOUBLE_CLICK)就表示了WKUP键双击的键值(或称事件值)
//状态机键值事件宏定义如下:
#define NONE_PRESS 0
#define PRESS_UP 1 //每当按键释放时即触发一次该事件
#define PRESS_DOWN 2 //每当按键按下时即触发一次该事件
#define SINGLE_CLICK 3 //短按并松开时即触发一次该事件
#define DOUBLE_CLICK 4 //连续两次短按并松开时即触发一次该事件
#define LONG_RRESS_START 5 //初次满足长按时间要求时即触发一次该事件
#define LONG_PRESS_HOLD 6 //满足长按时间要求后,之后每次扫描检测到按键仍未释放均会触发一次该事件
//按键已经全部释放
#define KEY_RELEASED (Cont==0) //简化的条件
//#define KEY_RELEASED (Cont==0 && Trg==0) //严格的条件
/**************数据和函数接口声明*******************/
extern KeyS_Type Trg;
extern KeyS_Type Cont;
extern u16 KeyTime;
void KEY_Init(void);//键硬件IO端口初始化,由主函数调用
void GetAndSaveKey(void);//本函数由SYSTICK调用,在后台读键,如果有键值则存入按键缓冲区
u8 Read_A_Key(void);//读取按键值:由系统主循环调用。
void Key_Scan_Stick(void);//本函数由SYSTICK调用,在后台扫描按键获取消除抖动后的稳定键值
u8 Get_Key_State(u8 KeyNum);//按键状态机,本函数一般由Get_Key()内部调用
//函数的调用关系:由SYSTICK自动启动两个进程:
//进程一:SysTick_Handler ->自动调用Key_Scan_Stick();该进程比较简单,扫描按键获取消除抖动后的稳定键值,无须修改。
//进程二:SysTick_Handler ->自动调用GetAndSaveKey() ->调用Key_PrePro() ->调用Get_Key() ->调用Get_Key_State()
// 该进程中间的两个函数,可根据具体需求进行修改实现:
// 1是Get_Key()函数,定义系统中起作用的键事件,大部分键已通过调用Get_Key_State()自动完成,如有需要,可增加定义一些组合键事件;
// 2是Key_PrePro()函数,键预处理,实质是截获部分要求强实时性的紧急按键优先处理;剩下的键则由GetAndSaveKey()自动存入FIFO队列待主循环查询处理。
//
//系统主循环 ->调用Read_A_Key():系统主循环中通过调用Read_A_Key(),对FIFO队列中的按键进行查询处理。
//总之,运用本代码模块的主要工作是三件事:
//一:硬件接口初始化:包括必要的GPIO初始化,实体硬按键的排序等。
//二:键值(或称事件)生成:对Get_Key()函数进行改写,使之能输出所有有效的键值。所有按键的单击、双击、长按、保持等键值输出已经实现,如果有需要则在该函数中增加组合键等键值。
//三:键值(或称事件)处理:对键值的具体响应处理可在两个地方实现。如有强实时性的紧急按键需要优先处理的键值请在Key_PrePro()函数编写代码,
// 其它按键的响应请在系统主循环调用Read_A_Key()得到键值后编写代码。
// 根据具体项目需求,也可全部响应代码都在主循环中编写,也可以全部响应代码都在Key_PrePro()函数中编写。
// 如果全部响应代码都在Key_PrePro()函数中编写,则主循环中无须再处理按键(也无须调用Read_A_Key函数)。
#endif