STM32 簡易按鍵KEY處理

一般的按鍵功能都需要做防抖處理,中間就涉及到了阻塞延遲,但是在實際項目開發中應當儘量避免不必的阻塞。可以使用計時功能去處理按鍵防抖,在系統滴答時鐘中斷中實現按鍵功能檢測,按鍵功能處理是在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;
			}
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章