按键检测说起来比较简单,实际上也不难,只需要注意下细节就行。
理想条件下按键按下和抬起时都是一个标准的上升沿火下降沿,但是由于机械原因按键按下和抬起的过程波形会如下图所示一样有很多抖动跳变。
此时如果按理想情况检测沿跳变或高低电平会导致明明只按了一次缺检测到两次甚至多次按下的错误状况,为了解决这个BUG就需要我们进行“消抖”了。
消抖方式一般分两种:硬件消抖和软件消抖。
硬件消抖:在按键两端并入一个小电容,可以吸收掉按下和抬起时的小抖动,使电平变化更平稳,此时软件上可以按理想情况进行检测。优点就是软件上可以简单许多,缺点就是增加了硬件成本和复杂度,另外电容选择不好的话消抖效果还可能达不到预期
软件消抖:在软件上检测到第一次沿变化时延时一小段时间后再检测高低电平,使用延时跳过硬件上的抖动时间。优点是硬件上简单,缺点是软件上需要注意硬件的抖动时间。
显然屏幕板上的按键没有进行硬件消抖,我们就只能使用软件进行消抖,测试好的消抖按键扫描代码如下
static void (*key_pressed_cb[KEY_NUM])(void) = {NULL}; /** * @brief 按键扫描函数,在扫描到按键按下时执行相应回调,以1毫秒调用 * 一次为准设计,修改调用频率需相应修改按下判断条件。 * @param void 无入口参数 * @return 无返回 */ void key_scan(void) { static int key_stat[KEY_NUM] = {0}; for(char i=0; i<KEY_NUM; i++){ if(gpio_get(get_key_pin(i))==0){ key_stat[i]++; } else key_stat[i]=0; } for(char i=0; i<KEY_NUM; i++){ //连续检测到10ms都按下则认为是按下 if(key_stat[i]==10){ if(NULL != key_pressed_cb[i])(*key_pressed_cb[i])(); } } } /** * @brief 按键按下回调函数注册 * @param[in] key 需要注册的按键序号,合法范围:[0,KEY_NUM) * @param[in] cb 相应的回调函数 * @return 无返回 */ void key_pressed_cb_register(uint8_t key, void(* cb)(void)) { if(key<0 || key>KEY_NUM){ printf("key_pressed_cb_register argument error!!!"); return; } key_pressed_cb[key] = cb; }
每1毫秒调用一次函数key_scan,每次调用时检测一次按键是否按下,按下时按键按下状态值自增1,未按下时则清零,当状态值增加到10则说明按键至少已经连续按下了10 毫秒,一般抖动10毫秒是可以覆盖的。另外可以看到我判断按下是使用"key_stat == 10"判断的,这样不支持连按的,如果要支持连按可以增加判断当key_stat大于1000(1秒)时对100取余判断是否为0,这样就可以达成长按1秒后每秒触发10次的连按效果,根据实际情况增减时间和频率即可。
另外还可以看到我的代码中还使用了函数指针,这样做主要是考虑到后面开发贪吃蛇游戏时可能还需要开发几个UI界面,不同界面按键完成的操作是不一样的,使用函数指针的话就可以切换到不同界面是将相应的按键操作给连接到函数指针上。
接下来写一个简单的测试程序测试下我们的按键模块
void key_a_pressed(void) { printf("KEY A PRESSED!!"); gpio_put(PICO_DEFAULT_LED_PIN, 1); } void key_b_pressed(void) { printf("KEY B PRESSED!!"); gpio_put(PICO_DEFAULT_LED_PIN, 0); }
int main() { bool lv = false; stdio_init_all(); gpio_init(PICO_DEFAULT_LED_PIN); gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); for (int n = 0; n < KEY_NUM; n++) { dev_key_init(n); } key_pressed_cb_register(KEY_A,key_a_pressed); key_pressed_cb_register(KEY_B,key_b_pressed); uint64_t time_to_wait_1s = 0; uint64_t time_to_wait_keyscan = 0; while (true) { if(try_to_wait(&time_to_wait_1s,1000000)==0){ printf("pico try_to_wait running!!!"); time_to_wait_1s = 0; // gpio_put(PICO_DEFAULT_LED_PIN, lv); // lv = !lv; } if(try_to_wait(&time_to_wait_keyscan,1000)==0) { key_scan(); time_to_wait_keyscan = 0; } } }
编译成功!下载到开发板,当按下A键时可以看到板载LED亮起,按下B键时熄灭。同时接上串口可以在串口助手中看到相应数据打印
可以看到每次按下都只会收到一条按下的打印数据,说明我们的消抖程序正常运行!