一般的按鍵功能都需要做防抖處理,中間就涉及到了阻塞延遲,但是在實際項目開發中應當儘量避免不必的阻塞。可以使用計時功能去處理按鍵防抖,在系統滴答時鐘中斷中實現按鍵功能檢測,按鍵功能處理是在main裏實現。
#define KEY_NR 2
/* 短按 */
#define KV_KEY1 0x01
#define KV_KEY2 0x02
/* 長按 */
#define LKV_KEY1 0x10
#define LKV_KEY2 0x20
#define KV_NULLKEY 0x00
#define KEY_PRESS 0x01
#define KEY_NO_PRESS 0x00
#define KEYBUF_MAX 32 /* 記錄按鍵狀態最大值 */
static unsigned char g_key_buf[KEYBUF_MAX];
static unsigned char g_key_inidx, g_key_rdidx;
static unsigned char g_key_cnt[KEY_NR];
static unsigned char g_key_inv[KEY_NR]; /* 表示按鍵按下狀態 */
static unsigned int g_key_count = 0;
static unsigned int g_lkey_count = 0; /* 長按計數 */
static unsigned char g_keylast_val = KV_NULLKEY;
#define KEY_CNT 3
#define KEYOUT_CNT 600
#define KEYLONG_CNT 300
typedef struct {
GPIO_TypeDef *port;
unsigned short pin;
unsigned char kv_index; /* 定義短按按鍵索引 */
unsigned char lkv_index; /* 定義長按按鍵索引 */
}key_def_t;
static const key_def_t key_tab[] = {
{KEY1_PORT, KEY1_PIN, KV_KEY1, LKV_KEY1},
{KEY2_PORT, KEY2_PIN, KV_KEY2, LKV_KEY2},
{NULL, KV_NULLKEY, KV_NULLKEY},
};
首先定義按鍵結構體便於處理,定義了短按( kv_index)與長按(lkv_index)兩個元素,如果需要其他實現的功能可以另外定義。關於短按與長按的時間後面講到具體實現方法時再說明。
int bsp_key_init(void)
{
int i;
const key_def_t *pkey = key_tab;
GPIO_InitTypeDef GPIO_InitStructure;
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;
while(NULL != pkey->port) {
GPIO_InitStructure.Pin = pkey->pin;
HAL_GPIO_Init(pkey->port, &GPIO_InitStructure);
pkey++;
}
g_key_inidx = g_key_rdidx = 0;
for(i = 0; i < KEY_NR; i++)
g_key_cnt[i] = g_key_inv[i] = 0;
g_key_count = 0;
g_lkey_count = 0;
return 0;
}
void bsp_key_proc(void)
{
int i;
const key_def_t *pkey = key_tab;
g_key_count++;
if(g_key_count >= KEYOUT_CNT) {
g_key_count = 0;
if(KV_NULLKEY != g_keylast_val) {
g_keylast_val = KV_NULLKEY;
g_key_buf[g_key_inidx] = KV_NULLKEY;
g_key_inidx = (g_key_inidx + 1) % KEYBUF_MAX;
}
}
for(i = 0; i < KEY_NR; i++) {
if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(pkey[i].port, pkey[i].pin)) {
if(g_lkey_count < KEYLONG_CNT) g_lkey_count++;
if(g_key_cnt[i] < KEY_CNT) g_key_cnt[i]++;
else g_key_inv[i] = 1;
} else { /* 起到防抖作用 */
if(g_key_cnt[i]) g_key_cnt[i]--;
}
if(g_key_inv[i] && (0 == g_key_cnt[i])) { /* 按鍵按下並鬆開 */
if(g_lkey_count >= KEYLONG_CNT) g_key_buf[g_key_inidx] = pkey[i].lkv_index;
else g_key_buf[g_key_inidx] = pkey[i].kv_index;
g_keylast_val = g_key_buf[g_key_inidx];
g_key_inidx = (g_key_inidx + 1) % KEYBUF_MAX;
g_key_inv[i] = 0;
g_lkey_count = 0;
g_key_count = 0;
}
}
return 0;
}
/* 獲取按鍵功能 */
uint8_t bsp_key_readv(void)
{
unsigned char key = KV_NULLKEY;
if(g_key_rdidx != g_key_inidx) {
key = g_key_buf[g_key_rdidx];
g_key_rdidx = (g_key_rdidx + 1) % KEYBUF_MAX;
}
return key;
}
/* 檢測按鍵是否按下 */
int bsp_key_status(void)
{
if(g_key_rdidx != g_key_inidx) return KEY_PRESS;
return KEY_NO_PRESS;
}
真正的核心是bsp_key_proc()函數,通過計數的方法去檢測按鍵的狀態,用戶也可以自行定義多種不同的按鍵狀態。可以在系統滴答中斷裏面調用該函數,這種按鍵處理無阻塞,方便添加按鍵功能,隨便按鍵按下時間長短。系統滴答中斷爲1ms,間隔10ms檢測按鍵狀態。
__weak void systick_handler_callback(void)
{
static int cnt = 0;
cnt++;
if (cnt >= 10) {
cnt = 0;
bsp_key_proc();
}
}
void SysTick_Handler(void)
{
HAL_IncTick();
systick_handler_callback();
}
int main(void)
{
unsigned char key;
bsp_key_init();
while (1)
{
if (bsp_key_status() != KV_NULLKEY) {
key = bsp_key_readv();
switch (key) {
case KV_KEY1:
break;
case KV_KEY2:
break;
case LKV_KEY1:
break;
case LKV_KEY2:
break;
default:
break;
}
}
}
}