├── 20Keys_Example.c ├── README.md ├── key.c ├── key.h └── main.c /20Keys_Example.c: -------------------------------------------------------------------------------- 1 | //这里再举一个4*4矩阵键盘另加直联4个方向键,共20个按键的范例 2 | 3 | //设PD3、PD4、PD5、PD6为普通直联端口的四个键(上下左右方向键) 4 | 5 | //4*4行列式矩阵键盘硬件连接: 6 | //设PD8、PD9、PD10、PD11为行列式键盘的按键输入(为方便读取,这4个IO口尽量分配为连续引脚号) 7 | //设PD12、PD13、PD14、PD15为行列式键盘的扫描输出(这4个IO口随意) 8 | 9 | #define KeyNumMax 20 //硬件实体按键数量4*4+4=20个 10 | typedef u32 KeyS_Type;//定义状态字为32位数据类型 11 | 12 | #define KEY_OUT_LINE_NULL GPIOD->BRR = 0x0f<<12 /*清除所有输出*/ 13 | #define KEY_OUT_LINE1 GPIOD->BSRR = 0x01<<12 /*扫描输出第一行*/ 14 | #define KEY_OUT_LINE2 GPIOD->BSRR = 0x02<<12 /*扫描输出第二行*/ 15 | #define KEY_OUT_LINE3 GPIOD->BSRR = 0x04<<12 /*扫描输出第三行*/ 16 | #define KEY_OUT_LINE4 GPIOD->BSRR = 0x08<<12 /*扫描输出第四行*/ 17 | 18 | #define KEY_IN (GPIOD->IDR&(GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11))/*扫描输入值*/ 19 | #define KEY_IN0 (KEY_IN>>8) /*获取输入移位到低端(假如是PD0-3的话就更简单,不用移位了)*/ 20 | 21 | 22 | //按键硬件读端口位置 23 | #define KB_RIGHT_IN PDin(3)//读取按键0 24 | #define KB_DOWN_IN PDin(4)//读取按键1 25 | #define KB_LEFT_IN PDin(5)//读取按键2 26 | #define KB_UP_IN PDin(6)//读取按键3 27 | 28 | 29 | //20个硬件实体按键的编号,键态字依此顺序按位组合 30 | #define KB_RIGHT 0 31 | #define KB_DOWN 1 32 | #define KB_LEFT 2 33 | #define KB_UP 3 34 | #define KB_NUM1 4 35 | #define KB_NUM2 5 36 | #define KB_NUM3 6 37 | #define KB_BACK 7//退格键 38 | #define KB_NUM4 8 39 | #define KB_NUM5 9 40 | #define KB_NUM6 10 41 | #define KB_SPACE 11//空格键 42 | #define KB_NUM7 12 43 | #define KB_NUM8 13 44 | #define KB_NUM9 14 45 | #define KB_ESC 15 46 | #define KB_XING 16//*号键 47 | #define KB_NUM0 17 48 | #define KB_JING 18//#号键 49 | #define KB_ENTER 19//回车键 50 | //***************以上内容可写在key.h 文件中******** 51 | 52 | 53 | //*************** key.c 文件相应内容 ******** 54 | //按键初始化函数 55 | void KEY_Init(void) //IO初始化 56 | { 57 | GPIO_InitTypeDef GPIO_InitStructure; 58 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE); 59 | 60 | /* PD8,9,10,11按键输入*/ 61 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11; 62 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 下拉输入 63 | GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //50M时钟速度 64 | GPIO_Init(GPIOD, &GPIO_InitStructure); 65 | /* PD12,13,14,15按键扫描输出*/ 66 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; 67 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 68 | GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //50M时钟速度 69 | GPIO_Init(GPIOD, &GPIO_InitStructure); 70 | 71 | /* PD3,4,5,6按键输入,对应四个方向键*/ 72 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6; 73 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 74 | GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //50M时钟速度 75 | GPIO_Init(GPIOD, &GPIO_InitStructure); 76 | } 77 | 78 | 79 | //硬件按键编码 80 | //以上述20键为例(最大暂支持32键,对于键少的系统 KeyS_Type可定义为u16或u8) 81 | KeyS_Type GetHalKeyCode(void) 82 | { 83 | KeyS_Type ktmp=0; 84 | if(!KB_RIGHT_IN) ktmp|=1<>4 92 | 93 | KEY_OUT_LINE_NULL; 94 | KEY_OUT_LINE2; 95 | ktmp |= KEY_IN0<<8; //或者直接KEY_IN 96 | 97 | KEY_OUT_LINE_NULL; 98 | KEY_OUT_LINE3; 99 | ktmp |= KEY_IN0<<12; //或者直接KEY_IN<<4 100 | 101 | KEY_OUT_LINE_NULL; 102 | KEY_OUT_LINE4; 103 | ktmp |= KEY_IN0<<16; //或者直接KEY_IN<<8 104 | 105 | return ktmp; 106 | } 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # STM32 2 | STM32 CM3 Project 3 | 本键盘扫描模块的特点: 4 | 5 | 一、使用灵活:一体实现按键的普通、单击、双击、长按、保持以及组合等功能,无须事前为每个按键每种键值逐一进行宏定义,也无须逐一编写各事件的条件判断, 6 | 只须为需要的按键事件编写相应的响应代码即可,同时留有特殊键组合等的扩展接口; 7 | 可以选择每一按键事件的处理实时性,从而能够使强实时性的紧急按键优先得到处理,可自由选择中断处理及查询处理或二者混合的处理方式, 8 | 灵活适配使应用项目能够兼备按键的强实时性要求以及超长(主循环执行一遍的时间长达1秒以上的)程序的适应性。 9 | 10 | 二、注重通用:模块设计时注重通用性,按键事件(键值)依简单易懂的标准事件格式编写;除能满足几乎所有按键应用需求外,在按键数量上, 11 | 从少到2-4个按键直到最大32个按键(包括端口直联、行列式矩阵、矩阵加直联混合)都可适用。(注:新写了一个4*4矩阵加4键直联混合共20个键的例子参见153楼) 12 | 13 | 14 | 三、稳定可靠:后台智能抖动消除、按键干扰杂波滤除措施有力,获取按键稳定可靠,不会产生重复按键,即使在CPU非常繁忙时也不会漏失按键。 15 | 16 | 17 | 四、移植简便:所有可调整参数(数量不多)均以宏定义列出,除与硬件相关(按键个数及连接端口)的部分须根据具体系统修改外,其它均无须变化,很容易移植。 18 | 程序可读性强,注释详尽丰富,其中包括函数调用关系及详细运用修改说明,如有未尽事宜,可提出探讨,本人尽量解答修改。 19 | 20 | 21 | 五、高效节能:消抖无须延时等待,同时采取自适应变频扫键、键盘闲置检测、消抖读键双进程周期差异等多项智能措施尽量减少占用CPU的计算资源。 22 | 23 | (完整工程示例分享请移步http://www.openedv.com/forum.php?mod=viewthread&tid=277263&extra=page%3D1,如果对您有所助宜,请赐星星鼓励一下,谢谢。) 24 | -------------------------------------------------------------------------------- /key.c: -------------------------------------------------------------------------------- 1 | #include "key.h" 2 | #include "led.h" 3 | #include "sys.h" 4 | #include "delay.h" 5 | ////////////////////////////////////////////////////////////////////////////////// 6 | //本程序只供学习使用,未经作者许可,不得用于其它任何用途 7 | //ALIENTEK战舰STM32开发板 8 | //按键驱动代码 9 | //正点原子@ALIENTEK 10 | //技术论坛:www.openedv.com 11 | //修改日期:2018/8/30 12 | //版本:V2.2 13 | //Made by warship 14 | ////////////////////////////////////////////////////////////////////////////////// 15 | 16 | //按键初始化函数 17 | void KEY_Init(void) //IO初始化 18 | { 19 | GPIO_InitTypeDef GPIO_InitStructure; 20 | 21 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);//使能PORTA,PORTE时钟 22 | 23 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;//KEY0-KEY2 24 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入 25 | GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE2,3,4 26 | 27 | //初始化 WK_UP-->GPIOA.0 下拉输入 28 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; 29 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉 30 | GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0 31 | } 32 | 33 | //硬件按键编码(为应用三行读键程序而准备) 34 | //以战舰版的四键为例(最大暂支持16键,KeyS_Type定义为u32则可支持32键) 35 | KeyS_Type GetHalKeyCode(void) 36 | { 37 | KeyS_Type ktmp=0; 38 | if(!KEY0_IN) ktmp|=1<=LONG_TICKS && KEY_RELEASED) 136 | {//键盘长时间闲置,直接返回(绝大部分时间基本都是这种状态,此举将大大节省CPU资源) 137 | KeyTime=LONG_TICKS;//此句防止KeyTime溢出(KeyTime由扫键程序累增) 138 | return; 139 | } 140 | Trg=KeyStable & (KeyStable ^ Cont); //调用三行读键方法,其实核心只有此行,使得Trg在某按键被按下后有且只有一次读取到对应位为1; 141 | Cont=KeyStable; 142 | newkeytmp=Key_PrePro();//从键预处理程序中读键值 143 | if(newkeytmp)//如果有新的键值 144 | { 145 | New_KeyBuff[pNewKey++]=newkeytmp;//存入按键缓冲区(pNewKey永远指向下一空位置) 146 | if(pNewKey==KEYBUFFSIZE)pNewKey=0;//按键缓冲区循环使用 147 | } 148 | } 149 | 150 | //读取按键值:由主循环调用。 151 | //从按键缓存中读取按键值,无键则返回0 152 | u8 Read_A_Key(void) 153 | { 154 | static u8 pReadKey=0;//读键指针 155 | if(pReadKey==KEYBUFFSIZE)pReadKey=0;//按键缓冲区循环使用 156 | if(pReadKey==pNewKey) return 0;//键已经取尽,返回0 157 | return New_KeyBuff[pReadKey++]; 158 | } 159 | 160 | 161 | //按键扫描函数:一般由Systick中断服务程序以5ms一次的时间节拍调用此函数 162 | //采用了键盘自适应变频扫描措施,在键盘正常稳定期间(非消抖期间)扫描频率降低以减少CPU资源占用 163 | //该函数将影响全局变量:消除抖动后的稳定键态值KeyStable及累计时长KeyTime 164 | void Key_Scan_Stick(void) 165 | { 166 | KeyS_Type KeyValTemp; 167 | static KeyS_Type KeyValTempOld=0; 168 | static u16 debounce_cnt=0; 169 | static u16 debouncing=0; 170 | 171 | KeyTime++;//在稳定键态(包括无键)状态下,全局变量KeyTime是持续增加的 172 | if((!debouncing) && (KeyTime%NORMAL_SCAN_FREQ))//非消抖期间且累计计时不是6的倍数(即6*5=30ms才扫描一次) 173 | return; //则不扫描键盘直接返回,这里可调整NORMAL_SCAN_FREQ为其它数,估计最大到40即120ms扫描一次都问题不大的。 174 | 175 | KeyValTemp=GetHalKeyCode();//扫描键盘,得到实时键值(合并),可存16个键值(按下相应位为1松开为0); 176 | 177 | if(KeyValTemp!=KeyStable) //如果当前值不等于旧存值,即键值有变化 178 | { 179 | debouncing=1;//标示为消抖期 180 | if(!(KeyValTemp^KeyValTempOld))//如果临时值不稳定(即新键值有变化) 181 | { 182 | debounce_cnt=0; 183 | KeyValTempOld=KeyValTemp; 184 | } 185 | else//临时值稳定 186 | { 187 | if(++debounce_cnt >= DEBOUNCE_TICKS) 188 | { 189 | KeyStable = KeyValTemp;//键值更新为当前值. 190 | debounce_cnt = 0;//并复位消抖计数器. 191 | KeyTime=1; //新键值累计时长复位为1个时间单位 192 | debouncing=0;//消抖期结束 193 | } 194 | } 195 | } 196 | else //如果键值仍等于旧存值: 197 | { //则复位消抖计数器(注意:只要消抖中途读到一次键值等于旧存值,消抖计数器均从0开始重新计数). 198 | debounce_cnt = 0; 199 | KeyValTempOld=KeyValTemp; 200 | } 201 | } 202 | 203 | //*************************************************************** 204 | // 无须单击、双击、长按、连续保持等功能键的可以删除以下函数 205 | //*************************************************************** 206 | /** 多功能按键状态机 207 | * 入口参数:实体按键编号(参数为KEYCLR用于复位状态机) 208 | * 返回:键值(按键事件值)=(KeyNum+2)*10+键事件值; 其它返回0. 209 | */ 210 | #define THE_KEY_IS_OFF (!(Cont & KeyOnCode)) 211 | #define THE_KEY_IS_ON (Cont & KeyOnCode) 212 | #define THE_KEY_PRESSED ((Trg & KeyOnCode) && (Cont & KeyOnCode)) 213 | 214 | u8 Get_Key_State(u8 KeyNum) 215 | { 216 | //按键记忆状态(每字节低四位存state,高4位存repeat) 217 | static u8 KeyState[KeyNumMax]; 218 | 219 | KeyS_Type KeyOnCode; 220 | u8 i,state,repeat,event=0; 221 | if(KeyNum==KB_CLR) //参数为KB_CLR时,则消除所有按键记忆状态 222 | { 223 | for(i=0;i>4; 229 | 230 | if(Trg && (Trg!=KeyOnCode)) state=0; //出现其它键,则状态清0 231 | 232 | switch (state) 233 | { 234 | case 0://状态0:键完全松开 235 | if(THE_KEY_PRESSED) 236 | { //初次按键触发并有效 237 | event = (u8)PRESS_DOWN; 238 | repeat = 1; 239 | state = 1;//初次按键有效,变成状态1 240 | } 241 | else //无效电平,即按键为松开状态 242 | event = (u8)NONE_PRESS; 243 | break; 244 | 245 | case 1://状态1:初次按键触发并有效 246 | if(THE_KEY_IS_OFF) { //检测到按键松开 247 | event = (u8)PRESS_UP; 248 | state = 2;//按键按下后松开,变成状态2 249 | } 250 | else if(KeyTime > LONG_TICKS) {//按键未松开,且持续时间已经超过LONG_TICKS 251 | event = (u8)LONG_RRESS_START; 252 | state = 5;//即长按触发启动,变成状态5 253 | } 254 | break; 255 | 256 | case 2://状态2:按键按下后已松开 257 | if(THE_KEY_PRESSED) { //再次检测到按下 258 | event = (u8)PRESS_DOWN; 259 | repeat++;//重按次数累计 260 | if(repeat == 2) state = 3;//如果重按次数等于2,则变成状态3 261 | } 262 | else //持续松开 263 | { 264 | if(KeyTime > SHORT_TICKS) 265 | {//如果松开时间超过SHORT_TICKS,即一次按键结束 266 | state = 0;//因按键松开时间超过SHORT_TICKS,则复位成状态0 267 | if(repeat==1) event=(u8)SINGLE_CLICK;//次数为1的情况下触发单击事件 268 | else if(repeat==2) event=(u8)DOUBLE_CLICK;//重按次数为2的情况下触发双击事件 269 | } 270 | } //隐含:如果松开时间还没有超过SHORT_TICKS,仍然维持状态2,有待后续判断 271 | break; 272 | 273 | case 3://状态3:按下、松开、又按下(即第二次按下) 274 | if(THE_KEY_IS_OFF) //检测到按键松开 275 | { 276 | event = (u8)PRESS_UP; 277 | if(KeyTime < SHORT_TICKS) state = 2; //松开时间小于SHORT_TICKS,回到状态2 278 | else state = 0;//松开时间大于SHORT_TICKS,则变成状态0 279 | }//隐含:如果仍按下则停留在状态3等待松开(第二次按下没有长按之说) 280 | break; 281 | 282 | case 5://状态5:长按触发已经启动 283 | if(THE_KEY_IS_ON) //如果按键仍持续按下 284 | event = (u8)LONG_PRESS_HOLD;//长按并保持按键事件成立 285 | else { //如果按键松开 286 | event = (u8)PRESS_UP; 287 | state = 0; //则恢复到状态0 288 | } 289 | break; 290 | } 291 | KeyState[KeyNum]=state; //保存相应的记忆状态值 292 | KeyState[KeyNum]+= repeat<<4; 293 | if(event>=(u8)PRESS_DOWN) //设定只输出特殊功能键(修改此处可输出按下/松开等一般事件) 294 | // if(event) //输出所有事件 295 | return KEYOUT_BASE_DEF+event; 296 | else return 0; 297 | } 298 | 299 | /******************************************************************************* 300 | * Function Name : SysTickHandler 301 | * Description : 系统每1毫秒产生一次中断,进入此服务程序 302 | 注意: 本函数与延时函数不冲突 303 | *******************************************************************************/ 304 | //nTicks为一个32位的静态全局变量,用于累计SysTick总次数 305 | static u32 nTicks = 0; //静态全局变量只在本文件有作用 306 | u32 GetTicks() 307 | { 308 | return nTicks; 309 | } 310 | 311 | extern u8 Flag300ms; 312 | /* SysTick中断服务函数 */ 313 | void SysTick_Handler(void) 314 | { 315 | nTicks++; 316 | if(!(nTicks%300)) Flag300ms=1; 317 | 318 | if (( nTicks % TICKS_INTERVAL) == 0 ) 319 | { 320 | Key_Scan_Stick(); //每5ms扫键一次 321 | if ( nTicks % (TICKS_INTERVAL*12) == 0 ) 322 | GetAndSaveKey();//每60ms分析一次键值 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /key.h: -------------------------------------------------------------------------------- 1 | #ifndef __KEY_H 2 | #define __KEY_H 3 | #include "sys.h" 4 | ////////////////////////////////////////////////////////////////////////////////// 5 | //本扫描键盘模块的特点: 6 | //一、使用灵活:一体实现按键的普通、单击、双击、长按、保持以及组合等功能,无须事前为每个按键每种键值逐一进行宏定义,也无须逐一编写各事件的条件判断, 7 | // 只须为需要的按键事件编写相应的响应代码即可,同时留有特殊键组合等的扩展接口; 8 | // 可以选择每一按键事件的处理实时性,从而能够使强实时性的紧急按键优先得到处理,可自由选择中断处理及查询处理或二者混合的处理方式, 9 | // 灵活适配使应用项目能够兼备按键的强实时性要求以及超长(主循环执行一遍的时间长达1秒以上的)程序的适应性。 10 | //二、注重通用:模块设计时注重通用性,按键事件(键值)依简单易懂的标准事件格式编写;除能满足几乎所有按键应用需求外,在按键数量上, 11 | // 从少到2-4个按键直到最大32个按键(包括端口直联、行列式矩阵、矩阵加直联混合)都可适用。 12 | //三、稳定可靠:后台智能抖动消除、按键干扰杂波滤除措施有力,获取按键稳定可靠,不会产生重复按键,即使在CPU非常繁忙时也不会漏失按键。 13 | //四、移植简便:所有可调整参数(数量不多)均以宏定义列出,除与硬件相关(按键个数及连接端口)的部分须根据具体系统修改外,其它均无须变化,很容易移植。 14 | // 程序可读性强,注释详尽丰富,其中包括函数调用关系及详细运用修改说明,如有未尽事宜,可提出探讨,本人尽量解答修改。 15 | //五、高效节能:消抖无须延时等待,同时采取自适应变频扫键、键盘闲置检测、消抖读键双进程周期差异等多项智能措施尽量减少占用CPU的计算资源。 16 | 17 | //本程序只供学习使用,未经作者许可,不得用于其它任何用途 18 | //测试平台:ALIENTEK战舰STM32开发板 19 | //按键驱动代码 20 | //正点原子@ALIENTEK 21 | //技术论坛:http://www.openedv.com/forum.php?mod=viewthread&tid=277263,有问题可在本帖中提出讨论,谢谢。 22 | //修改日期:2018/9/1 23 | //版本:V2.2 24 | //Made by warship 25 | ////////////////////////////////////////////////////////////////////////////////// 26 | 27 | //根据需求可修改定义如下参数. 28 | #define TICKS_INTERVAL 5 //ms, 后台调用间隔时间 29 | #define DEBOUNCE_TICKS 4 //消除抖动次数,即时长至少为5ms*4 30 | #define NORMAL_SCAN_FREQ 6 //正常情况下扫键频率因子,如为6则表示稳定后扫键周期为6*TICKS_INTERVAL=30ms 31 | #define SHORT_TICKS (300 /TICKS_INTERVAL) //短按时间定义300ms 32 | #define LONG_TICKS (1200 /TICKS_INTERVAL) //长按时间定义1200ms 33 | #define KEYBUFFSIZE 16 //按键缓存FIFO深度,定义保存16个键值 34 | 35 | //***************** 以下与具体系统的硬件相关 ******************************************** 36 | #define KeyNumMax 4 //硬件实体按键数量 37 | typedef u16 KeyS_Type; //键态字类型定义(根据按键总数定义足够长度的数据类型, 38 | //本例程只有4个键,用u8足矣,但为扩充方便这里用了u16,最大可满足16键,大于16键时请定义为u32) 39 | //按键硬件读端口位置 40 | #define KEY0_IN PEin(4) //按键0输入端口 41 | #define KEY1_IN PEin(3) //按键1输入端口 42 | #define KEY2_IN PEin(2) //按键2输入端口 43 | #define WKUP_IN PAin(0) //按键3输入端口(WK_UP) 44 | 45 | //硬件实体按键编号,键态字KeyS_Type依此顺序按位组合,每BIT位对应一个实体按键 46 | #define KB_KEY0 0 //规定KEY0键用键态字的第0号bit位表示,该位为1表示KEY0键处于按下状态 47 | #define KB_KEY1 1 //规定KEY1键用键态字的第1号bit位表示,该位为1表示KEY1键处于按下状态 48 | #define KB_KEY2 2 //规定KEY2键用键态字的第2号bit位表示,该位为1表示KEY2键处于按下状态 49 | #define KB_WKUP 3 //规定KEY2键用键态字的第3号bit位表示,该位为1表示KEY3键处于按下状态 50 | 51 | 52 | 53 | //************ 以下基本与硬件无关(除增删组合键码定义外一般无须修改) ********************* 54 | 55 | //定义一个特殊值用于复位状态机 56 | #define KB_CLR 44 57 | 58 | 59 | //这里可以定义一些特殊键码(如组合键等) 60 | #define WKUP_PLUSKEY0_PRES 1 //示例:WKUP+KEY0组合按键(先按下WKUP再按下KEY0) 61 | 62 | 63 | //功能键值输出: 64 | #define KEYOUT_BASE_DEF 10*(KeyNum+2) //为保留组合键值等空间,这里定义为20以上,可视情修改) 65 | 66 | #define KEY_EVENT(m,n) (u8)(10*(m+2)+n) //按键事件(即键值)宏定义 67 | //有了上述宏定义后,无须再为各个按键单独写宏定义,使用KEY_EVENT(键编号,键值事件)就可以代表特定按键事件了。 68 | //例如:用KEY_EVENT(KB_WKUP,DOUBLE_CLICK)就表示了WKUP键双击的键值(或称事件值) 69 | 70 | 71 | //状态机键值事件宏定义如下: 72 | #define NONE_PRESS 0 73 | #define PRESS_UP 1 //每当按键释放时即触发一次该事件 74 | #define PRESS_DOWN 2 //每当按键按下时即触发一次该事件 75 | #define SINGLE_CLICK 3 //短按并松开时即触发一次该事件 76 | #define DOUBLE_CLICK 4 //连续两次短按并松开时即触发一次该事件 77 | #define LONG_RRESS_START 5 //初次满足长按时间要求时即触发一次该事件 78 | #define LONG_PRESS_HOLD 6 //满足长按时间要求后,之后每次扫描检测到按键仍未释放均会触发一次该事件 79 | 80 | 81 | //按键已经全部释放 82 | #define KEY_RELEASED (Cont==0) //简化的条件 83 | //#define KEY_RELEASED (Cont==0 && Trg==0) //严格的条件 84 | 85 | 86 | 87 | /**************数据和函数接口声明*******************/ 88 | extern KeyS_Type Trg; 89 | extern KeyS_Type Cont; 90 | extern u16 KeyTime; 91 | 92 | 93 | void KEY_Init(void);//键硬件IO端口初始化,由主函数调用 94 | void GetAndSaveKey(void);//本函数由SYSTICK调用,在后台读键,如果有键值则存入按键缓冲区 95 | u8 Read_A_Key(void);//读取按键值:由系统主循环调用。 96 | void Key_Scan_Stick(void);//本函数由SYSTICK调用,在后台扫描按键获取消除抖动后的稳定键值 97 | u8 Get_Key_State(u8 KeyNum);//按键状态机,本函数一般由Get_Key()内部调用 98 | 99 | //函数的调用关系:由SYSTICK自动启动两个进程: 100 | //进程一:SysTick_Handler ->自动调用Key_Scan_Stick();该进程比较简单,扫描按键获取消除抖动后的稳定键值,无须修改。 101 | //进程二:SysTick_Handler ->自动调用GetAndSaveKey() ->调用Key_PrePro() ->调用Get_Key() ->调用Get_Key_State() 102 | // 该进程中间的两个函数,可根据具体需求进行修改实现: 103 | // 1是Get_Key()函数,定义系统中起作用的键事件,大部分键已通过调用Get_Key_State()自动完成,如有需要,可增加定义一些组合键事件; 104 | // 2是Key_PrePro()函数,键预处理,实质是截获部分要求强实时性的紧急按键优先处理;剩下的键则由GetAndSaveKey()自动存入FIFO队列待主循环查询处理。 105 | // 106 | //系统主循环 ->调用Read_A_Key():系统主循环中通过调用Read_A_Key(),对FIFO队列中的按键进行查询处理。 107 | //总之,运用本代码模块的主要工作是三件事: 108 | //一:硬件接口初始化:包括必要的GPIO初始化,实体硬按键的排序等。 109 | //二:键值(或称事件)生成:对Get_Key()函数进行改写,使之能输出所有有效的键值。所有按键的单击、双击、长按、保持等键值输出已经实现,如果有需要则在该函数中增加组合键等键值。 110 | //三:键值(或称事件)处理:对键值的具体响应处理可在两个地方实现。如有强实时性的紧急按键需要优先处理的键值请在Key_PrePro()函数编写代码, 111 | // 其它按键的响应请在系统主循环调用Read_A_Key()得到键值后编写代码。 112 | // 根据具体项目需求,也可全部响应代码都在主循环中编写,也可以全部响应代码都在Key_PrePro()函数中编写。 113 | // 如果全部响应代码都在Key_PrePro()函数中编写,则主循环中无须再处理按键(也无须调用Read_A_Key函数)。 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShuifaHe/STM32/2e44ed8575612968152b129eb407dcd9b344906a/main.c --------------------------------------------------------------------------------