第1章 按键长短按实验(提升)
完整工程连接:http://download.csdn.net/download/jahol_fan/10175862
1.1 实验简介
按键长按与按键短按在很多产品中都需要应用到,在我们生活中,例如:手机开关机用到的就是按键长按,手机设置音量用的是按键的短按。在本实验平台的综合实验中,也需要用到按键的长短按,所以,我们很有必要学习如何实现按键的程序设计。
设计按键长短按的思路其实很简单,就是计数原理。假设,定时器定时10ms中断一次,在中断函数中,判断按键是否按下,如果按下,然后统计按键按下的时间长度是多少个10ms,如果按下了100个10ms,则表明长按了1秒;如果按下了300个10ms,表示按下了3秒。
1.2 硬件设计
1) KEY2连接到PA8,稳定按下是低电平,稳定松开是高电平。
2) KEY3连接到PB10,稳定按下是低电平,稳定松开是高电平。
1.3 软件设计
1.构造按键参数结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
typedef void (*keyCallBackFuction)(void); //定义回调函数指针
__packed typedef struct { uint8_t keyNum; //按键编号 uint32_t keyRccPeriph; //按键时钟 GPIO_TypeDef *keyPort; //按键所在端口 uint32_t keyGpio; //按键所在的IO口 keyCallBackFuction shortPress; //回调函数成员,短按回调函数 keyCallBackFuction longPress; //回调函数成员,长按回调函数 }keyTypedef_t;
__packed typedef struct { uint8_t keyTotolNum; //按键总数 keyTypedef_t *pSingleKey; //该指针单个按键的结构参数 }keysTypedef_t; |
首先,我们一起学习一下keyTypedef_t和keysTypedef_t这两个结构体。
keyTypedef_t的作用是:用于保存一个按键的结构参数,结构参数包括:按键的外设时钟、按键所在的端口、按键所对应的IO口、按键长按的回调函数指针、按键短按的回调函数指针。其中,需要注意的参数是:keyNum,keyNum的作用是为每个按键参数结构体提供一个“编号”,也就是说,每个按键都有自己唯一的“编号”。
本实验主要想实现识别按键的长按与短按,并执行相应的回调函数。回调函数的定义请参考上文代码块的“行1”的定义,该函数指针是无返回值且是无形参的。
keysTypedef_t的作用是:如果有多个按键,首先需要定义类型为keyTypedef_t的结构体数组,而定义一个keysTypedef_t变量来统一管理所有的按键结构体参数。其中16行的指针pSingleKey在初始化按键后,会指向“keyTypedef_t的结构体数组”,通过指针偏移来简介访问所有的按键结构参数,从而实现“统一管理所有的按键”。
在本章节,需要实现两个按键的长短按的识别,keyTypedef_t和keysTypedef_t的使用如下:
1 2 3 |
#define GPIO_KEY_NUM 2 //定义按键总数 keyTypedef_t singleKey[GPIO_KEY_NUM]; //定义单按键结构体数组 keysTypedef_t keys; //定义总体按键模型结构变量 |
行1:定义一个宏,决定按键的总数
行2:定义单按键结构参数数组,有多少个按键,则该数组需要有多少个成员。
行3:定义总体按键结构体参数变量,该结构体变量用于统一管理所有所有的按键。
2.初始化按键参数
定义好“单按键结构参数数组singleKey”以及“总体按键结构变量keys”后,需要初始化按键的结构参数,结构参数包括:编号、按键的外设时钟、按键所在的端口、按键所对应的IO口、按键长按的回调函数指针、按键短按的回调函数指针。用到的函数是keyInitOne,keyInitOne函数原形如下:
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 |
static uint8_t keyTotolNum = 0; //有与统计有多少个按键 /** * @brief key init function 按键初始化函数 * @param [in] keyRccPeriph :APB2外设时钟 * @param [in] keyPort:IO端口 * @param [in] keyGpio:IO管脚 * @param [in] short_press :按键短按回调函数 * @param [in] long_press :按键长按回到函数 * @return key structure pointer */ keyTypedef_t keyInitOne(uint32_t keyRccPeriph, GPIO_TypeDef * keyPort, uint32_t keyGpio, keyCallBackFuction shortPress, keyCallBackFuction longPress) { static uint8_t key_total = 0;
keyTypedef_t singleKey; //结构体变量,用于保存按键IO的参数
//平台定义IO口 singleKey.keyRccPeriph = keyRccPeriph; //保存按键IO口所在的外设时钟总线 singleKey.keyPort = keyPort; //保存按键所在的端口 singleKey.keyGpio = keyGpio; //保存按键所在的IO口 singleKey.keyNum = key_total++; //保存每个按键都有单独的编号 //回调函数定义,按键长按和短按各有自己的回调函数 singleKey.longPress = longPress; //保存按键长按函数指针 singleKey.shortPress = shortPress; //保存按键短按函数指针
keyTotolNum++; //全局静态变量,统计有多少个按键
return singleKey; //返回初始化的按键IO的参数的结构体变量 } |
首先,该keyInitOne函数的形参有:keyRccPeriph →APB2外设时钟, keyPort→IO端口,keyGpio→IO管脚,short_press →按键短按回调函数指针,long_press →按键长按回调函数指针。
假设,PA8管脚上挂在有一个按键,则需要将PA8的参数保存起来,则形参keyRccPeriph 需要传入的实参是RCC_APB2Periph_GPIOA, 形参keyPort需要传入的是GPIOA,形参keyGpio需要传入的实数是GPIO_Pin_8,形参short_press 需要传入的是按键短按回调函数指针,long_press →按键长按回调函数指针。
下面,我们一起学习一下上文的代码块:
行1:全局静态变量,用于统计总共有初始化了多少个按键
行17:静态变量,每初始化一个按键,该变量自增1。
行19:定义keyTypedef_t类型的局部变量 singleKey,用于保存按键的的结构参数(外设时钟、端口、IO管脚、按键短按回调函数指针、按键长按回调函数指针)。
行22~24:保存按键所对应的外设时钟总线、端口、IO管脚。
行25:保存按键的标号。
行27~28:保存按键所对应的长按函数指针和短按函数指针。
行30:初始化了的按键总数自增1
行32:返回定义keyTypedef_t类型的局部变量 singleKey,返回保存按键的的结构参数(外设时钟、端口、IO管脚、按键短按回调函数指针、按键长按回调函数指针)。
假设PA8挂有一个按键Key2、PB10挂有一个按键Key3,则Key2和Key3的初始化代码如下所示:
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 |
#define GPIO_KEY_NUM 2 //定义按键总数 keyTypedef_t singleKey[GPIO_KEY_NUM]; //定义单按键结构体数组 keysTypedef_t keys; //定义总体按键模型结构变量
/** * 按键3短按回调函数 * @param none * @return none */ void key3ShortPress(void) { Led_Reverse(1); //短按KEY3,则取反LED1 }
/** * 按键3长按回调函数 * @param none * @return none */ void key3LongPress(void) { Led_Reverse(3); //长按KEY3,则取反LED3 }
/** * KEY2短按回调函数 * @param none * @return none */ void key2ShortPress(void) { Led_Reverse(1); //短按KEY2,则取反LED1 }
/** * 按键2长按回调函数 * @param none * @return none */ void key2LongPress(void) { Led_Reverse(2); //长按KEY2,则取反LED2 }
/** * 按键初始化函数 * @param none * @return none */ void keyInit(void) { /*将按键端口参数保存到singleKey结构体数组中, 参数包括:外设端口、IO端口、IO管脚、长按函数指针、短按函数指针*/ singleKey[0] = keyInitOne(RCC_APB2Periph_GPIOB, GPIOB, GPIO_Pin_10, key3ShortPress, key3LongPress); singleKey[1] = keyInitOne(RCC_APB2Periph_GPIOA, GPIOA, GPIO_Pin_8, key2ShortPress, key2LongPress); keys.pSingleKey = (keyTypedef_t *)&singleKey; //pSingKey指针指向singleKey数组 keyParaInit(&keys); //初始化按键所在的IO口并初始化定时器 } |
2行:定义一个keyTypedef_t类型的数组 singleKey[GPIO_KEY_NUM],GPIO_KEY_NUM的值为2。一个按键对应一个数组元素。
11~15行:Key3短按回调函数。
22~25行:Key3长按回调函数。
32~35行:Key2短按回调函数。
42~45行:Key2长按回调函数。
3行:定义一个keysTypedef_t 类型的变量keys,用于统一管理所有的按键参数(外设时钟、端口、IO管脚、按键短按回调函数指针、按键长按回调函数指针)。
56~58行:keyInitOne函数原型在前文已经描述。调用keyInitOne函数,将Key3的按键参数保存到元素singleKey[0] 中。参数包括:外设时钟RCC_APB2Periph_GPIOB、端口GPIOB、IO管脚GPIO_Pin_10、按键短按回调函数指针key3ShortPress、按键长按回调函数指针 key3LongPress。
59~61行:keyInitOne函数原型在前文已经描述。调用keyInitOne函数,将Key2的按键参数保存到元素singleKey[1] 中。参数包括:外设时钟RCC_APB2Periph_GPIOA、端口GPIOA、IO管脚GPIO_Pin_8、按键短按回调函数指针key2ShortPress、按键长按回调函数指针key2LongPress。
本实验最终要实现的是,获知按键时长按还是短按,根据长按还是短按执行相应的回调函数,每个按键有单独的长按回调函数和短按回调函数。
62行:定义一个keysTypedef_t 类型的变量keys的指针成员pSingleKey指向keyTypedef_t类型的数组 singleKey[GPIO_KEY_NUM],作用是为了实现用变量keys的指针成员pSingleKey来管理所有按键的参数。
63行:按键IO口的初始化需要配置端口上下拉模式、端口速度等,前文讨论的keyInitOne函数只是用于保存按键IO口的参数,并不是用来初始化IO口,真正初始化IO口的是keyParaInit函数。
下面,我们一起来学习keyParaInit函数。
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 |
/** * @brief 按键参数初始化函数:按键GPIO初始化,并启动定时器来检测按键状态 * @param [in] pkeyS :按键全局结构体,改指针包含了所有的按键的参数 * @return none */ void keyParaInit(keysTypedef_t *pkeyS) { uint8_t i = 0;
if(NULL == pkeyS) //判断传入的指针是否有指向 { return ; //如果没有指向,则直接返回,不往下执行 }
pkeyS->keyTotolNum = keyTotolNum; //获取按键总数 /*误差判断,限制最多12个按键,可以通过修改宏KEY_MAX_NUMBER来实现支持更多的按键*/ if( pkeyS->keyTotolNum > KEY_MAX_NUMBER) { pkeyS->keyTotolNum = KEY_MAX_NUMBER; }
for(i = 0; i < pkeyS->keyTotolNum; i++) //取出所有的按键的参数,采用的是下标偏移法 { GPIO_InitTypeDef GPIO_InitStructure; //定义结构体,用于初始化IO参数 //取出某按键的外设端口时钟,并使能其时钟 RCC_APB2PeriphClockCmd(pkeyS->pSingleKey[i].keyRccPeriph, ENABLE);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //使能IO口速度 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //使能IO口的模式:上拉模式 GPIO_InitStructure.GPIO_Pin = pkeyS->pSingleKey[i].keyGpio; //取出某按键的管脚编号 GPIO_Init(pkeyS->pSingleKey[i].keyPort,&GPIO_InitStructure);//取出某按键的端口号,进行按键初始化 }
timer4Init(); //初始化定时器2,每隔1ms进入一次更新中断 } |
6行:keyParaInit(keysTypedef_t *pkeyS)函数的形参是keysTypedef_t类型的指针,该指针包含了所有按键的结构参数。
10~13行:判断传入的指针是不是空指针,如果是空指针,则返回,不继续往下执行。
15行: keyTotolNum记录了有多少个按键需要进行初始化,将 keyTotolNum的值保存到pkeyS->keyTotolNum的成员中。
17~20行:误差滤除,最多支持的按键不能超过12个。
22行:通过循环,改变下表i的值,来获得每个按键的结构体参数。
26~31行:取出按键的结构参数来进行按键IO口的初始化。
34行:定时器4初始化,使得1ms产生一次中断。
3.按键长短按管理
按键的长短的原理就是通过判断按键按下的时间长度,所以必须使用到定时器。本实验的做法是,通过定时器定时产生中断:每1ms触发一次定时器更新中断,然后在更新中断中进行进行按键长短按的管理。定时4的初始化代码如下:
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 |
/** * @brief 定时器2初始化函数1ms中断一次,TIM2的
* * @param none * @return none */ void timer4Init(void) { u16 arr = 7199; //自动重装载值 u16 psc = 9; //预分频值 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //定时器4时钟使能
/*定时器4初始化*/ TIM_TimeBaseStructure.TIM_Period = arr; //设置重载值 TIM_TimeBaseStructure.TIM_Prescaler = psc; //设置分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置分频因子 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //递增计数模式 TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //将结构参数用于初始化定时器4
TIM_ITConfig(TIM4, TIM_IT_Update,ENABLE ); //使能定时器4更新中断
/*设置中断参数*/ NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //定时器4中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //响应优先级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道 NVIC_Init(&NVIC_InitStructure); //初始化中断 TIM_Cmd(TIM4, ENABLE); //使能定时器 } |
定时器4的时钟频率是72MHZ,本实验对72MHZ的内部时钟进行10分频,计数器技术到7200则触发溢出中断,实现1ms触发一次更新中断。
关于定时器4的初始化代码,不作过多的分析,我们来学习一下定时器4的更新中断函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * @brief 定时器4中断函数,检测按键的状态 * @param none * @return none */ void TIM4_IRQHandler(void) { if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) //每隔1ms进入一次更新中断 { TIM_ClearITPendingBit(TIM4, TIM_IT_Update); //清除标志位 keyHandle((keysTypedef_t *)&keys); //每1ms检测一次按键的状态 } } |
11行:在定时器4更新中断函数中,每隔1ms,进行一次按键扫描管理,按键扫描管理工作包括:检测检测按键是否按下,检测按键按下的了多长的时间。接下来我们一起讨论一下 keyHandle函数,需要注意的是 keyHandle函数的实参是keys,该变量包含了所有按键的结构信息,包括按键编号、按键长按回调函数、按键短按回调函数。
首先我们要知道keyHandle函数调用了readKeyValue函数来获取按键是短按还是长按,而readKeyValue函数调用了getKey函数获知哪个按键按下,哪个按键松开。
为了更好的理解keyHandle函数,我们需要先理解getKey函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/** * @brief 读取按键的状态,按键按下都是低电平 * @param keys : 按键全局结构体指针Key * @return 按键的状态 */ uint16_t getKey(keysTypedef_t *keyS) { uint8_t i = 0; uint16_t readKey = 0; //使用一个16位的变量的低12位用来保存12个按键的状态值,如果按键按下,位置1
for(i = 0; i < keys.keyTotolNum; i++) //通过循环来读取所有的按键值 { if(!GPIO_ReadInputDataBit( (GPIO_TypeDef*)keyS->pSingleKey[i].keyPort, keyS->pSingleKey[i].keyGpio) ) { G_SET_BIT(readKey, keyS->pSingleKey[i].keyNum); //置位 } } return readKey; } |
getKey函数传入的实参是包含了所有的按键的结构信息(外设时钟、端口、IO管脚、按键短按回调函数指针、按键长按回调函数指针)。
9行:使用一个16位的变量的低12位用来保存12个按键的状态值,如果按键按下,位置1。
11行:使用初始化的按键总数keyTotolNum做为循环条件的上限控制,循环控制进行按键初始化。
13行:获取哪个按键按下,如果按键按下,则相应的IO口输入的是低电平,保存到寄存器的是数字信号0,所以13行的条件语句会成立。
18行:获知按下的是按个按键,进行置位操作。每个按键都有一个位域空间与其对应。如果按键按下,则位域置一,如果按键松开,位域清零。
21行:返回所有按键的位域值。
getKey函数是被readKeyValue函数用于获知所有的按键是被按下还是被松开。接下来我们来分析readKeyValue函数:
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 |
#define KEY_TIMER_MS 1 #define KEY_MAX_NUMBER 12 //最多12个按键 #define SCAN_TIME 30 #define PRESS_LONG_TIME 3000 /** * @brief Read the KEY value 读取所有按键的按键值 * @param [in] keys :Key global structure pointer * @return GPIO status */ uint16_t readKeyValue(keysTypedef_t *keyS) { static uint8_t keyScanFlag = 0; static uint8_t keyState = 0; static uint16_t keyLongPressCnt = 0; static uint16_t lastKeyValue = 0; //last key
uint16_t newKeyValue = 0; uint16_t keyReturnValue = 0;
keyCountTime ++; //每1ms加1
if(keyCountTime >= (SCAN_TIME / KEY_TIMER_MS)) //该条件语句是为了30ms扫描一次按键,改变宏的值可以实现灵活控制按键扫描频率 { keyCountTime = 0; keyScanFlag = 1; //标志位置1,需要进行一次按键扫描,使用标志位来控制按键的扫描速率 } if(1 == keyScanFlag) { keyScanFlag = 0; //按键扫描标志位清零 newKeyValue = getKey(keyS); //读取按键的值,还需要进行位域判断才能获知按下的按键是哪个 switch (keyState) { case 0: if(newKeyValue != 0) //如果这个值不为0,则表示有按键按下 { lastKeyValue = newKeyValue; //保存按下的值 keyState = 1; //切换到下一个case } break;
case 1: if(newKeyValue == lastKeyValue) //如果按键持续按下,则newKeyPressVal值和上次一样, { keyState = 2; //切换到下一个case keyReturnValue= lastKeyValue | KEY_PRESS_DOWN; //置位,表示按键按下了 } else //按键松开或者按键抖动,不做任何的响应,又回到case零 { keyState = 0; } break;
case 2: if(newKeyValue != lastKeyValue) //按键松开了 { keyState = 0; //回到case 0 keyLongPressCnt = 0; //清零 keyReturnValue = lastKeyValue | KEY_SHORT_PRESS; //按键按下,又松开了 return keyReturnValue; } if(newKeyValue == lastKeyValue) //如果按键仍然按下,则这两个值继续相等 { keyLongPressCnt++; //统计长按按下的次数 if(keyLongPressCnt >= (PRESS_LONG_TIME / SCAN_TIME)) //长按3秒有效 { keyLongPressCnt = 0;//按键统计次数需要进行清零 keyState = 3; //切换到case3 keyReturnValue= newKeyValue | KEY_LONG_PRESS; //按键长按 return keyReturnValue; //返回按键值 } } break;
case 3: if(newKeyValue != lastKeyValue) //长按过后,如果按键松开了,则清零,再回到case 0 { keyState = 0; } break; } } return NO_KEY_PRESS_DOWN; } |
readKeyValue函数传入的参数是:keysTypedef_t *keyS,该参数包含了所有按键的信息(编号、外设时钟、端口、IO管脚、按键短按回调函数指针、按键长按回调函数指针)。
readKeyValue函数每隔1毫秒会被调用一次。
3行:SCAN_TIME 用于控制按键扫描频率。
4行:PRESS_LONG_TIME用于控制按键长按的时间长度。
20~27行:keyCountTime静态变量每隔1毫秒会自增1,当如果(keyCountTime >= (SCAN_TIME / KEY_TIMER_MS)),则进行一次按键扫描,这样子写的目的是:为了控制按键的扫描频率。 keyScanFlag置1的表示,需要进行一次按键扫描。
31行:调用 getKey(keyS)函数来获取所有按键的按下与松开的状态, getKey(keyS)函数的返回值是一个16位的变量,该变量的低12位的每一个位域对应一个按键的状态。(位域值为1则表示按键按下,位域值为0则表示按键松开)。
本实验识别按键长短按的方法是“四状态法”:
34~39行:状态0。如果有按键按下,则newKeyValue值肯定不为0,如果有按键按下,则进入“状态1”。小结:状态0是用于判断按键是否按下。
42~52行:状态1。适用于判断按键经过抖动器后,按键是否按下的,如果还是按下的,则表示按键进入了“稳定按下”的状态,如果有按键按下,则:keyReturnValue = lastKeyValue | KEY_SHORT_PRESS,则keyReturnValue的值包含了按键位域值以及当前按键的属性:KEY_SHORT_PRESS。小结:“状态1”用于判断按键消抖后是否是稳定按下。
54~73行:状态2。改状态用于判断按键是长按还是短按,其中用变量keyLongPressCnt来统计按键按下的次数,从而知道按键按下的时间长度。
55~61行:如果最新按键位域值newKeyValue和最早的按键位域值lastKeyValue不相等,则表示按键是短按。
62~71行:如果最新按键位域值newKeyValue和最早的按键位域值lastKeyValue一直相等,则变量keyLongPressCnt递增,当如果变量keyLongPressCnt递增到大于(PRESS_LONG_TIME / SCAN_TIME),则表示按键是长按。则 keyReturnValue= newKeyValue | KEY_LONG_PRESS,则keyReturnValue的值包含了按键位域值以及当前按键的属性:KEY_LONG_PRESS。
75~80:“状态3”。如果最新按键位域值newKeyValue和最早的按键位域值lastKeyValue不相等,则表明按键松开了,则重新回到“状态0”。
分析完readKeyValue函数后,我们来一起分析keyHandle(keysTypedef_t *pkeyS)函数:
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 |
/** * @brief 检测按键是长按还是短按,并执行相应的回调函数 * @param [in] keys : 全局按键参数结构体 * @return none */ void keyHandle(keysTypedef_t *pkeyS) //定时处理函数,每个10ms执行一次 { uint8_t i = 0; uint16_t key_value = 0;
key_value = readKeyValue(pkeyS); //读取按键的值
if(!key_value) return; //如果按键值为0,则返回,不往下执行
/*检测是哪个按键短按*/ if(key_value & KEY_SHORT_PRESS) { for(i = 0; i < pkeyS->keyTotolNum; i++) //通过循环来判断所有的按键 { if(G_IS_BIT_SET(key_value, pkeyS->pSingleKey[i].keyNum)) //判断是不是置位了 { if(pkeyS->pSingleKey[i].shortPress) //如果有短按回调函数 { pkeyS->pSingleKey[i].shortPress(); //执行短按回调函数
} } } }
/*检测是哪个按键长按*/ if(key_value & KEY_LONG_PRESS) { for(i = 0; i < pkeyS->keyTotolNum; i++) //循环判断所有按键 { if(G_IS_BIT_SET(key_value, pkeyS->pSingleKey[i].keyNum)) //是不是置位了 { if(pkeyS->pSingleKey[i].longPress) //如果有按键长按回调函数 { pkeyS->pSingleKey[i].longPress(); //执行按键长按回调函数 } } } } } |
11行:读取所有按键的位域值和以及长按、短按的属性。
16~29行:判断按键是不是短按,如果是短按,则根据按键的位域,来判断是按个按键按下,并执行相应的短按回调函数。
32~43行:判断按键是不是长按,如果是短按,则根据按键的位域,来判断是按个按键按下,并执行相应的长按回调函数。
4.总结
1) 本实验按键最多支持12个,并且每个按键都有单独的长按回调函数和短按回调函数。
2) 只支持“按下是低电平、松开是高电平的按键”。
1.4 下载验证
短按KEY2、KEY3则取反LED1,长按KEY2则取反LED2,长按KEY3则取反LED3。