stm32F407按鍵例程安富萊

/*
*********************************************************************************************************
*
*	模塊名稱 : 獨立按鍵驅動模塊 (外部輸入IO)
*	文件名稱 : bsp_key.c
*	版    本 : V1.3
*	說    明 : 掃描獨立按鍵,具有軟件濾波機制,具有按鍵FIFO。可以檢測如下事件:
*				(1) 按鍵按下
*				(2) 按鍵彈起
*				(3) 長按鍵
*				(4) 長按時自動連發
*
*	修改記錄 :
*		版本號  日期        作者     說明
*		V1.0	2020.02.27  armfly	KEY0、KEY1 和 KEY2 是低電平有效的,
*									而 KEY_UP 是高電平有效的,並且外部
*									都沒有上下拉電阻,所以,需要在 STM32F4 內部設置上下拉。
*
*
*
*********************************************************************************************************
*/

#include "bsp.h"


/*
	正點原子探索者 按鍵口線分配:
		KEY0 鍵		: PE4   (低電平表示按下)
		KEY1 鍵		: PE3   (低電平表示按下)
		KEY2 鍵		: PE2   (低電平表示按下)
		KEY_UP 鍵	: PA0   (高電平表示按下)

*/
	
#define HARD_KEY_NUM	    4	   				/* 實體按鍵個數 */
#define KEY_COUNT   	 	(HARD_KEY_NUM + 2)	/* 4個獨立建 + 2個組合按鍵 */

/* 使能GPIO時鐘 */
#define ALL_KEY_GPIO_CLK_ENABLE() {	\
		__HAL_RCC_GPIOA_CLK_ENABLE();	\
		__HAL_RCC_GPIOE_CLK_ENABLE();	\
	};

/* 依次定義GPIO */
typedef struct
{
	GPIO_TypeDef* gpio;
	uint16_t pin;
	uint8_t ActiveLevel;	/* 激活電平 */
}X_GPIO_T;

/* GPIO和PIN定義 */
static const X_GPIO_T s_gpio_list[HARD_KEY_NUM] = {
	{GPIOE, GPIO_PIN_4, 0},	/* KEY0 */
	{GPIOE, GPIO_PIN_3, 0},	/* KEY1 */
	{GPIOE, GPIO_PIN_2, 0},	/* KEY2 */
	{GPIOA, GPIO_PIN_0, 1},	/* KEY_UP */
};	

/* 定義一個宏函數簡化後續代碼 
	判斷GPIO引腳是否有效按下
*/
static KEY_T s_tBtn[KEY_COUNT] = {0};  /* 按鍵對應的全局變量結構體 */
static KEY_FIFO_T s_tKey;		/* 按鍵FIFO變量,結構體 */

static void bsp_InitKeyVar(void);
static void bsp_InitKeyHard(void);
static void bsp_DetectKey(uint8_t i);

#define KEY_PIN_ACTIVE(id)	

/*
*********************************************************************************************************
*	函 數 名: KeyPinActive
*	功能說明: 判斷按鍵是否按下
*	形    參: 無
*	返 回 值: 返回值1 表示按下(導通),0表示未按下(釋放)
*********************************************************************************************************
*/
static uint8_t KeyPinActive(uint8_t _id)
{
	uint8_t level;
	
	if ((s_gpio_list[_id].gpio->IDR & s_gpio_list[_id].pin) == 0)
	{
		level = 0;
	}
	else
	{
		level = 1;
	}

	if (level == s_gpio_list[_id].ActiveLevel)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

/*
*********************************************************************************************************
*	函 數 名: IsKeyDownFunc
*	功能說明: 判斷按鍵是否按下。單鍵和組合鍵區分。單鍵事件不允許有其他鍵按下。
*	形    參: 無
*	返 回 值: 返回值1 表示按下(導通),0表示未按下(釋放)
*********************************************************************************************************
*/
static uint8_t IsKeyDownFunc(uint8_t _id)
{
	/* 實體單鍵 */
	if (_id < HARD_KEY_NUM)
	{
		uint8_t i;
		uint8_t count = 0;
		uint8_t save = 255;
		
		/* 判斷有幾個鍵按下 */
		for (i = 0; i < HARD_KEY_NUM; i++)
		{
			if (KeyPinActive(i)) 
			{
				count++;
				save = i;
			}
		}
		
		if (count == 1 && save == _id)
		{
			return 1;	/* 只有1個鍵按下時纔有效 */
		}		
		return 0;
	}
	
	/* 組合鍵 K0K1 */
	if (_id == HARD_KEY_NUM + 0)
	{
		if (KeyPinActive(KID_K0) && KeyPinActive(KID_K1))
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}

	/* 組合鍵 K1KU */
	if (_id == HARD_KEY_NUM + 1)
	{
		if (KeyPinActive(KID_K1) && KeyPinActive(KID_KU))
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}

	return 0;
}

/*
*********************************************************************************************************
*	函 數 名: bsp_InitKey
*	功能說明: 初始化按鍵. 該函數被 bsp_Init() 調用。
*	形    參:  無
*	返 回 值: 無
*********************************************************************************************************
*/
void bsp_InitKey(void)
{
	bsp_InitKeyVar();		/* 初始化按鍵變量 */
	bsp_InitKeyHard();		/* 初始化按鍵硬件 */
}

/*
*********************************************************************************************************
*	函 數 名: bsp_InitKeyHard
*	功能說明: 配置按鍵對應的GPIO
*	形    參:  無
*	返 回 值: 無
*********************************************************************************************************
*/
static void bsp_InitKeyHard(void)
{	
	GPIO_InitTypeDef gpio_init;
	uint8_t i;

	/* 第1步:打開GPIO時鐘 */
	ALL_KEY_GPIO_CLK_ENABLE();
	
	/* 第2步:配置所有的按鍵GPIO爲浮動輸入模式(實際上CPU復位後就是輸入狀態) */
	gpio_init.Mode = GPIO_MODE_INPUT;   			/* 設置輸入 */
	gpio_init.Speed = GPIO_SPEED_FREQ_VERY_HIGH;  /* GPIO速度等級 */
	
	for (i = 0; i < HARD_KEY_NUM; i++)
	{
		if(i<3)												/* KEY0 KEY1 KEY2 */
		{
			gpio_init.Pin = s_gpio_list[i].pin;
			gpio_init.Pull = GPIO_PULLUP;                 /* 上拉電阻 */
			HAL_GPIO_Init(s_gpio_list[i].gpio, &gpio_init);	
		}
		else												/* KEY_UP */
		{
			gpio_init.Pin = s_gpio_list[i].pin;
			gpio_init.Pull = GPIO_PULLDOWN;                 /* 下拉電阻 */
			HAL_GPIO_Init(s_gpio_list[i].gpio, &gpio_init);			
		}
	}
}

/*
*********************************************************************************************************
*	函 數 名: bsp_InitKeyVar
*	功能說明: 初始化按鍵變量
*	形    參:  無
*	返 回 值: 無
*********************************************************************************************************
*/
static void bsp_InitKeyVar(void)
{
	uint8_t i;

	/* 對按鍵FIFO讀寫指針清零 */
	s_tKey.Read = 0;
	s_tKey.Write = 0;
	s_tKey.Read2 = 0;

	/* 給每個按鍵結構體成員變量賦一組缺省值 */
	for (i = 0; i < KEY_COUNT; i++)
	{
		s_tBtn[i].LongTime = KEY_LONG_TIME;			/* 長按時間 0 表示不檢測長按鍵事件 */
		s_tBtn[i].Count = KEY_FILTER_TIME / 2;		/* 計數器設置爲濾波時間的一半 */
		s_tBtn[i].State = 0;							/* 按鍵缺省狀態,0爲未按下 */
		s_tBtn[i].RepeatSpeed = 0;						/* 按鍵連發的速度,0表示不支持連發 */
		s_tBtn[i].RepeatCount = 0;						/* 連發計數器 */
	}

	/* 如果需要單獨更改某個按鍵的參數,可以在此單獨重新賦值 */

}

/*
*********************************************************************************************************
*	函 數 名: bsp_PutKey
*	功能說明: 將1個鍵值壓入按鍵FIFO緩衝區。可用於模擬一個按鍵。
*	形    參:  _KeyCode : 按鍵代碼
*	返 回 值: 無
*********************************************************************************************************
*/
void bsp_PutKey(uint8_t _KeyCode)
{
	s_tKey.Buf[s_tKey.Write] = _KeyCode;

	if (++s_tKey.Write  >= KEY_FIFO_SIZE)
	{
		s_tKey.Write = 0;
	}
}

/*
*********************************************************************************************************
*	函 數 名: bsp_GetKey
*	功能說明: 從按鍵FIFO緩衝區讀取一個鍵值。
*	形    參: 無
*	返 回 值: 按鍵代碼
*********************************************************************************************************
*/
uint8_t bsp_GetKey(void)
{
	uint8_t ret;

	if (s_tKey.Read == s_tKey.Write)
	{
		return KEY_NONE;
	}
	else
	{
		ret = s_tKey.Buf[s_tKey.Read];

		if (++s_tKey.Read >= KEY_FIFO_SIZE)
		{
			s_tKey.Read = 0;
		}
		return ret;
	}
}

/*
*********************************************************************************************************
*	函 數 名: bsp_GetKey2
*	功能說明: 從按鍵FIFO緩衝區讀取一個鍵值。獨立的讀指針。
*	形    參:  無
*	返 回 值: 按鍵代碼
*********************************************************************************************************
*/
uint8_t bsp_GetKey2(void)
{
	uint8_t ret;

	if (s_tKey.Read2 == s_tKey.Write)
	{
		return KEY_NONE;
	}
	else
	{
		ret = s_tKey.Buf[s_tKey.Read2];

		if (++s_tKey.Read2 >= KEY_FIFO_SIZE)
		{
			s_tKey.Read2 = 0;
		}
		return ret;
	}
}

/*
*********************************************************************************************************
*	函 數 名: bsp_GetKeyState
*	功能說明: 讀取按鍵的狀態
*	形    參:  _ucKeyID : 按鍵ID,從0開始
*	返 回 值: 1 表示按下, 0 表示未按下
*********************************************************************************************************
*/
uint8_t bsp_GetKeyState(KEY_ID_E _ucKeyID)
{
	return s_tBtn[_ucKeyID].State;
}

/*
*********************************************************************************************************
*	函 數 名: bsp_SetKeyParam
*	功能說明: 設置按鍵參數
*	形    參:_ucKeyID : 按鍵ID,從0開始
*			_LongTime : 長按事件時間
*			 _RepeatSpeed : 連發速度
*	返 回 值: 無
*********************************************************************************************************
*/
void bsp_SetKeyParam(uint8_t _ucKeyID, uint16_t _LongTime, uint8_t  _RepeatSpeed)
{
	s_tBtn[_ucKeyID].LongTime = _LongTime;			/* 長按時間 0 表示不檢測長按鍵事件 */
	s_tBtn[_ucKeyID].RepeatSpeed = _RepeatSpeed;			/* 按鍵連發的速度,0表示不支持連發 */
	s_tBtn[_ucKeyID].RepeatCount = 0;						/* 連發計數器 */
}

/*
*********************************************************************************************************
*	函 數 名: bsp_ClearKey
*	功能說明: 清空按鍵FIFO緩衝區
*	形    參:無
*	返 回 值: 按鍵代碼
*********************************************************************************************************
*/
void bsp_ClearKey(void)
{
	s_tKey.Read = s_tKey.Write;
}

/*
*********************************************************************************************************
*	函 數 名: bsp_DetectKey
*	功能說明: 檢測一個按鍵。非阻塞狀態,必須被週期性的調用。
*	形    參: IO的id, 從0開始編碼
*	返 回 值: 無
*********************************************************************************************************
*/
static void bsp_DetectKey(uint8_t i)
{
	KEY_T *pBtn;

	pBtn = &s_tBtn[i];
	if (IsKeyDownFunc(i))		/* 是否有按鍵i按下 */
	{
		if (pBtn->Count < KEY_FILTER_TIME)
		{
			pBtn->Count = KEY_FILTER_TIME;
		}
		else if(pBtn->Count < 2 * KEY_FILTER_TIME)
		{
			pBtn->Count++;
		}
		else
		{
			if (pBtn->State == 0)
			{
				pBtn->State = 1;

				/* 發送按鈕按下的消息 */
				bsp_PutKey((uint8_t)(3 * i + 1));
			}

			if (pBtn->LongTime > 0)
			{
				if (pBtn->LongCount < pBtn->LongTime)
				{
					/* 發送按鈕持續按下的消息 */
					if (++pBtn->LongCount == pBtn->LongTime)
					{
						/* 鍵值放入按鍵FIFO */
						bsp_PutKey((uint8_t)(3 * i + 3));
					}
				}
				else
				{
					if (pBtn->RepeatSpeed > 0)
					{
						if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
						{
							pBtn->RepeatCount = 0;
							/* 常按鍵後,每隔10ms發送1個按鍵 */
							bsp_PutKey((uint8_t)(3 * i + 1));
						}
					}
				}
			}
		}
	}
	else
	{
		if(pBtn->Count > KEY_FILTER_TIME)
		{
			pBtn->Count = KEY_FILTER_TIME;
		}
		else if(pBtn->Count != 0)
		{
			pBtn->Count--;
		}
		else
		{
			if (pBtn->State == 1)
			{
				pBtn->State = 0;

				/* 發送按鈕彈起的消息 */
				bsp_PutKey((uint8_t)(3 * i + 2));
			}
		}

		pBtn->LongCount = 0;
		pBtn->RepeatCount = 0;
	}
}

/*
*********************************************************************************************************
*	函 數 名: bsp_DetectFastIO
*	功能說明: 檢測高速的輸入IO. 1ms刷新一次
*	形    參: IO的id, 從0開始編碼
*	返 回 值: 無
*********************************************************************************************************
*/
static void bsp_DetectFastIO(uint8_t i)
{
	KEY_T *pBtn;

	pBtn = &s_tBtn[i];
	if (IsKeyDownFunc(i))
	{
		if (pBtn->State == 0)
		{
			pBtn->State = 1;

			/* 發送按鈕按下的消息 */
			bsp_PutKey((uint8_t)(3 * i + 1));
		}

		if (pBtn->LongTime > 0)
		{
			if (pBtn->LongCount < pBtn->LongTime)
			{
				/* 發送按鈕持續按下的消息 */
				if (++pBtn->LongCount == pBtn->LongTime)
				{
					/* 鍵值放入按鍵FIFO */
					bsp_PutKey((uint8_t)(3 * i + 3));
				}
			}
			else
			{
				if (pBtn->RepeatSpeed > 0)
				{
					if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
					{
						pBtn->RepeatCount = 0;
						/* 常按鍵後,每隔10ms發送1個按鍵 */
						bsp_PutKey((uint8_t)(3 * i + 1));
					}
				}
			}
		}
	}
	else
	{
		if (pBtn->State == 1)
		{
			pBtn->State = 0;

			/* 發送按鈕彈起的消息 */
			bsp_PutKey((uint8_t)(3 * i + 2));
		}

		pBtn->LongCount = 0;
		pBtn->RepeatCount = 0;
	}
}

/*
*********************************************************************************************************
*	函 數 名: bsp_KeyScan10ms
*	功能說明: 掃描所有按鍵。非阻塞,被systick中斷週期性的調用,10ms一次
*	形    參: 無
*	返 回 值: 無
*********************************************************************************************************
*/
void bsp_KeyScan10ms(void)
{
	uint8_t i;

	for (i = 0; i < KEY_COUNT; i++)
	{
		bsp_DetectKey(i);
	}
}

/*
*********************************************************************************************************
*	函 數 名: bsp_KeyScan1ms
*	功能說明: 掃描所有按鍵。非阻塞,被systick中斷週期性的調用,1ms一次.
*	形    參: 無
*	返 回 值: 無
*********************************************************************************************************
*/
void bsp_KeyScan1ms(void)
{
	uint8_t i;

	for (i = 0; i < KEY_COUNT; i++)
	{
		bsp_DetectFastIO(i);
	}
}

/***************************** 安富萊電子 www.armfly.com (END OF FILE) *********************************/

/* 例程 */

/* 按鍵濾波和檢測由後臺systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */
//	uint8_t ucKeyCode;		/* 按鍵代碼 */
//	while(1)
//	{
//		ucKeyCode = bsp_GetKey();	/* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */
//		if (ucKeyCode != KEY_NONE)
//		{
//			switch (ucKeyCode)
//			{
//				case KEY_DOWN_K0:			/* K0鍵按下 */
//					bsp_LedToggle(0);
//					break;
//				case KEY_DOWN_K1:			/* K0鍵按下 */
//					bsp_LedToggle(1);
//					break;
//				case SYS_DOWN_K1KU:			/* K1KU鍵按下 */
//					bsp_LedOff(1);
//					bsp_LedOff(0);
//					break;				
//				default:
//					/* 其它的鍵值不處理 */
//					break;
//			}
//		
//		}
//	}
/*
*********************************************************************************************************
*
*	模塊名稱 : 按鍵驅動模塊
*	文件名稱 : bsp_key.h
*	版    本 : V1.0
*	說    明 : 頭文件
*
*	Copyright (C), 2013-2014, 安富萊電子 www.armfly.com
*
*********************************************************************************************************
*/

#ifndef __BSP_KEY_H
#define __BSP_KEY_H

/* 根據應用程序的功能重命名按鍵宏 */

#define KEY_DOWN_K0		KEY_1_DOWN		/*  KEY0  鍵 */
#define KEY_UP_K0		KEY_1_UP
#define KEY_LONG_K0		KEY_1_LONG

#define KEY_DOWN_K1		KEY_2_DOWN		/*  KEY1  鍵 */
#define KEY_UP_K1		KEY_2_UP
#define KEY_LONG_K1		KEY_2_LONG

#define KEY_DOWN_K2		KEY_3_DOWN		/*  KEY2  鍵 */
#define KEY_UP_K2		KEY_3_UP
#define KEY_LONG_K2		KEY_3_LONG

#define KEY_DOWN_KU		KEY_4_DOWN		/* KEY_UP 鍵 */
#define KEY_UP_KU		KEY_4_UP
#define KEY_LONG_KU		KEY_4_LONG

#define SYS_DOWN_K0K1	KEY_5_DOWN		/* K0 K1 組合鍵 */
#define SYS_UP_K0K1	    KEY_5_UP
#define SYS_LONG_K0K1	KEY_5_LONG

#define SYS_DOWN_K1KU	KEY_6_DOWN		/* K1 KU 組合鍵 */
#define SYS_UP_K1KU  	KEY_6_UP
#define SYS_LONG_K1KU	KEY_6_LONG

/* 按鍵ID, 主要用於bsp_KeyState()函數的入口參數 */
typedef enum
{
	KID_K0 = 0,
	KID_K1,
	KID_K2,
	KID_KU,
}KEY_ID_E;

/*
	按鍵濾波時間50ms, 單位10ms。
	只有連續檢測到50ms狀態不變才認爲有效,包括彈起和按下兩種事件
	即使按鍵電路不做硬件濾波,該濾波機制也可以保證可靠地檢測到按鍵事件
*/
#define KEY_FILTER_TIME   5
#define KEY_LONG_TIME     100			/* 單位10ms, 持續1秒,認爲長按事件 */

/*
	每個按鍵對應1個全局的結構體變量。
*/
typedef struct
{
	/* 下面是一個函數指針,指向判斷按鍵是否按下的函數 */
	uint8_t (*IsKeyDownFunc)(void); /* 按鍵按下的判斷函數,1表示按下 */

	uint8_t  Count;			/* 濾波器計數器 */
	uint16_t LongCount;		/* 長按計數器 */
	uint16_t LongTime;		/* 按鍵按下持續時間, 0表示不檢測長按 */
	uint8_t  State;			/* 按鍵當前狀態(按下還是彈起) */
	uint8_t  RepeatSpeed;	/* 連續按鍵週期 */
	uint8_t  RepeatCount;	/* 連續按鍵計數器 */
}KEY_T;

/*
	定義鍵值代碼, 必須按如下次序定時每個鍵的按下、彈起和長按事件

	推薦使用enum, 不用#define,原因:
	(1) 便於新增鍵值,方便調整順序,使代碼看起來舒服點
	(2) 編譯器可幫我們避免鍵值重複。
*/
typedef enum
{
	KEY_NONE = 0,			/* 0 表示按鍵事件 */

	KEY_1_DOWN,				/* 1鍵按下 */
	KEY_1_UP,				/* 1鍵彈起 */
	KEY_1_LONG,				/* 1鍵長按 */

	KEY_2_DOWN,				/* 2鍵按下 */
	KEY_2_UP,				/* 2鍵彈起 */
	KEY_2_LONG,				/* 2鍵長按 */

	KEY_3_DOWN,				/* 3鍵按下 */
	KEY_3_UP,				/* 3鍵彈起 */
	KEY_3_LONG,				/* 3鍵長按 */

	KEY_4_DOWN,				/* 4鍵按下 */
	KEY_4_UP,				/* 4鍵彈起 */
	KEY_4_LONG,				/* 4鍵長按 */
	
	/* 組合鍵 */
	KEY_5_DOWN,				/* 5鍵按下 */
	KEY_5_UP,				/* 5鍵彈起 */
	KEY_5_LONG,				/* 5鍵長按 */

	KEY_6_DOWN,				/* 6鍵按下 */
	KEY_6_UP,				/* 6鍵彈起 */
	KEY_6_LONG,				/* 6鍵長按 */
	
}KEY_ENUM;

/* 按鍵FIFO用到變量 */
#define KEY_FIFO_SIZE	10
typedef struct
{
	uint8_t Buf[KEY_FIFO_SIZE];		/* 鍵值緩衝區 */
	uint8_t Read;					/* 緩衝區讀指針1 */
	uint8_t Write;					/* 緩衝區寫指針 */
	uint8_t Read2;					/* 緩衝區讀指針2 */
}KEY_FIFO_T;

/* 供外部調用的函數聲明 */
void bsp_InitKey(void);
void bsp_KeyScan10ms(void);
void bsp_PutKey(uint8_t _KeyCode);
uint8_t bsp_GetKey(void);
uint8_t bsp_GetKey2(void);
uint8_t bsp_GetKeyState(KEY_ID_E _ucKeyID);
void bsp_SetKeyParam(uint8_t _ucKeyID, uint16_t _LongTime, uint8_t  _RepeatSpeed);
void bsp_ClearKey(void);

#endif

/***************************** 安富萊電子 www.armfly.com (END OF FILE) *********************************/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章