設(shè)計按鍵 FIFO 主要有三個方面的好處:
1.可以有效的記錄按鍵事件的發(fā)生,特別是需要實現(xiàn)按鍵的按下,長按,彈起等事件,使用 FIFO的方式來實現(xiàn)是一種非常好的思路。
2.系統(tǒng)是非阻塞的,這樣系統(tǒng)在檢測到按鍵按下的情況下,由于機械按鍵抖動的原因不需要在這里等待一段時間,然后再確定按鍵是否按下。
3.按鍵 FIFO 程序在嘀嗒定時器中定期的執(zhí)行檢測,不需要在主程序中一直做檢測,這樣可以有效的降低系統(tǒng)資源消耗。
關(guān)于按鍵是否該使用中斷方式去實現(xiàn)的問題,很多初學者都比較模糊,我這里從兩方面簡單說一下,純屬個人見解,如果那位有更好的意見,歡迎提出來。
從裸機的角度分析
中斷方式:中斷方式可以有效的檢測到按鍵按下的消息,并執(zhí)行相應的程序,但是用中斷方式實現(xiàn)按鍵 FIFO 相對就有點麻煩,如果每個按鍵都是獨立的接一個 IO 引腳,需要我們給每個 IO都設(shè)置一個中斷,程序中過多的中斷會影響系統(tǒng)的穩(wěn)定性。
查詢方式:查詢方式有一個最大的缺點就是需要程序定期的去執(zhí)行查詢,耗費一定的系統(tǒng)資源,實際上耗費不了多大的系統(tǒng)資源,因為這種查詢方式也只是查詢按鍵是否按下,按鍵事件的執(zhí)行還是在主程序里面實現(xiàn)。
從 OS 的角度分析
中斷方式:在 OS 中要盡可能少用中斷方式,因為在 RTOS 中過多的使用中斷會影響系統(tǒng)的穩(wěn)定性和可預見性(搶占式調(diào)度的 OS 基本沒有可預見性,基于時間觸發(fā)調(diào)度的可預見性要好很多)。比較重要的事件處理需要用中斷的方式。
查詢方式:對于用戶按鍵推薦使用這種查詢方式來實現(xiàn),現(xiàn)在的 OS 基本都帶有 CPU 利用率的功能,這個按鍵 FIFO 占用的還是很小的,基本都在%1 以下。
這個按鍵 FIFO 程序主要用于掃描掃描獨立按鍵,具有軟件濾波機制,具有按鍵 FIFO??梢詸z測如下事件:
1. 按鍵按下
2. 按鍵彈起
3. 長按鍵
4. 長按時自動連發(fā)
聲明代碼如下:
/**
******************************************************************************
* @file : bsp_key.h
* @author : xiaofeng
* @version : V1.0
* @date : 2015.05.21
* @brief : STM32F4 KEY FIFO
******************************************************************************
* @attention:
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __BSP_KEY_H__
#define __BSP_KEY_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx.h"
/* Exported types ------------------------------------------------------------*/
// 按鍵ID
typedef enum
{
KID_K1 = 0,
KID_K2,
KID_K3,
KID_K4
}KEY_ID_E;
/*
定義鍵值代碼, 必須按如下次序定時每個鍵的按下、彈起和長按事件
推薦使用enum, 不用#define,原因:
(1) 便于新增鍵值,方便調(diào)整順序,使代碼看起來舒服點
(2) 編譯器可幫我們避免鍵值重復。
*/
typedef enum
{
KEY_NONE = 0, /* 0 表示按鍵事件 */
KEY1_DOWN, /* 1鍵按下 */
KEY1_UP, /* 1鍵彈起 */
KEY1_LONG, /* 1鍵長按 */
KEY2_DOWN, /* 2鍵按下 */
KEY2_UP, /* 2鍵彈起 */
KEY2_LONG, /* 2鍵長按 */
}KEY_ENUM;
/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
#define KEY_COUNT 2 // 按鍵個數(shù)
#define KEY_FIFO_SIZE 10 // 按鍵FIFO大小
#define KEY_FILTER_TIME 5 // 按鍵濾波時間50ms, 只有連續(xù)檢測到50ms狀態(tài)不變才認為有效,包括彈起和按下兩種事件
#define KEY_LONG_TIME 0 // 長按時間. 0,表示不檢測長按鍵; 其他,檢測長按鍵的時間
#define KEY_REPEAT_SPEED 0 // 長按鍵連發(fā)速度. 0,表示不支持連發(fā),上報長按事件;其他,連發(fā)按下
// 按鍵口對應的RCC時鐘及引腳
#define RCC_ALL_KEY (RCC_AHB1Periph_GPIOA )
#define GPIO_PORT_K1 GPIOA
#define GPIO_PIN_K1 GPIO_Pin_0
#define GPIO_PORT_K2 GPIOD
#define GPIO_PIN_K2 GPIO_Pin_1
/* Exported functions --------------------------------------------------------*/
void KEY_Init(void);
void KEY_Scan(void);
void KEY_FIFO_Clear(void);
uint8_t KEY_FIFO_Get(void);
uint8_t KEY_GetState(KEY_ID_E ucKeyID);
void KEY_SetParam(uint8_t ucKeyID, uint16_t LongTime, uint8_t RepeatSpeed);
#ifdef __cplusplus
}
#endif
#endif
/*****END OF FILE****/
實現(xiàn)代碼如下:
/**
******************************************************************************
* @file : bsp_key.h
* @author : xiaofeng
* @version : V1.0
* @date : 2015.05.21
* @brief : STM32F4 KEY FIFO
******************************************************************************
* @attention:
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "bsp_key.h"
/* Private typedef -----------------------------------------------------------*/
// 每個按鍵對應1個全局的結(jié)構(gòu)體變量。
typedef struct
{
/* 下面是一個函數(shù)指針,指向判斷按鍵手否按下的函數(shù) */
uint8_t (*IsKeyDownFunc)(void); /* 按鍵按下的判斷函數(shù),1表示按下 */
uint8_t State; /* 按鍵當前狀態(tài)(按下還是彈起) */
uint16_t LongCount; /* 長按計數(shù)器 */
uint16_t LongTime; /* 按鍵按下持續(xù)時間, 0表示不檢測長按 */
uint8_t RepeatSpeed; /* 連續(xù)按鍵周期 */
uint8_t RepeatCount; /* 連續(xù)按鍵計數(shù)器 */
}KEY_T;
// 按鍵FIFO用到變量
typedef struct
{
uint8_t Buf[KEY_FIFO_SIZE]; /* 鍵值緩沖區(qū) */
uint8_t Read; /* 緩沖區(qū)讀指針 */
uint8_t Write; /* 緩沖區(qū)寫指針 */
}KEY_FIFO_T;
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static KEY_T s_tBtn[KEY_COUNT];
static KEY_FIFO_T s_tKey; /* 按鍵FIFO變量,結(jié)構(gòu)體 */
/* Private function prototypes -----------------------------------------------*/
static void KEY_FIFO_Init(void);
static void KEY_GPIO_Config(void);
static void KEY_FIFO_Put(uint8_t _KeyCode);
static void KEY_Detect(uint8_t i);
/* Private functions ---------------------------------------------------------*/
/**
* @brief : KEY初始化
* @note :
* @param :
* @retval :
*/
void KEY_Init(void)
{
KEY_GPIO_Config();
KEY_FIFO_Init();
}
/**
* @brief : 清空按鍵FIFO緩沖區(qū)
* @note : 無
* @param : 無
* @retval : 無
*/
void KEY_FIFO_Clear(void)
{
s_tKey.Read = s_tKey.Write;
}
/**
* @brief : 從按鍵FIFO緩沖區(qū)讀取一個鍵值
* @note : 無
* @param :
* @retval : 按鍵代碼
*/
uint8_t KEY_FIFO_Get(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;
}
}
/**
* @brief : 讀取按鍵的狀態(tài)
* @note : 無
* @param : ucKeyID : 按鍵ID,從0開始
* @retval : 1 表示按下, 0 表示未按下
*/
uint8_t KEY_GetState(KEY_ID_E ucKeyID)
{
return s_tBtn[ucKeyID].State;
}
/**
* @brief : 設(shè)置按鍵參數(shù)
* @note : 無
* @param : ucKeyID : 按鍵ID,從0開始
* LongTime : 長按事件時間
* RepeatSpeed : 連發(fā)速度
* @retval : 無
*/
void KEY_SetParam(uint8_t ucKeyID, uint16_t LongTime, uint8_t RepeatSpeed)
{
s_tBtn[ucKeyID].LongTime = LongTime; /* 長按時間 0 表示不檢測長按鍵事件 */
s_tBtn[ucKeyID].RepeatSpeed = RepeatSpeed; /* 長按鍵連發(fā)的速度,0表示不支持連發(fā) */
s_tBtn[ucKeyID].RepeatCount = 0; /* 連發(fā)計數(shù)器 */
}
/**
* @brief : 掃描所有按鍵。非阻塞,被周期性的調(diào)用(如systick中斷)
* @note : 無
* @param : 無
* @retval : 無
*/
void KEY_Scan(void)
{
uint8_t i;
for (i = 0; i < KEY_COUNT; i++)
{
KEY_Detect(i);
}
}
/***************************************************************************/
/**
* @brief : 配置按鍵對應的GPIO
* @note : 無
* @param : 無
* @retval : 無
*/
static void KEY_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 第1步:打開GPIO時鐘 */
RCC_AHB1PeriphClockCmd(RCC_ALL_KEY, ENABLE);
/* 第2步:配置所有的按鍵GPIO為浮動輸入模式(實際上CPU復位后就是輸入狀態(tài)) */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; /* 設(shè)為輸入口 */
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; /* 設(shè)為推挽模式 */
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; /* 無需上下拉電阻 */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /* IO口最大速度 */
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K1;
GPIO_Init(GPIO_PORT_K1, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K2;
GPIO_Init(GPIO_PORT_K2, &GPIO_InitStructure);
}
/**
* @brief : 判斷按鍵是否按下
* @note : 無
* @param : 無
* @retval : 1 表示按下,0表示未按下
*/
static uint8_t IsKey1Down(void) {if (GPIO_ReadInputDataBit(GPIO_PORT_K1, GPIO_PIN_K1) == 0) return 1;else return 0;}
static uint8_t IsKey2Down(void) {if (GPIO_ReadInputDataBit(GPIO_PORT_K2, GPIO_PIN_K2) == 0) return 1;else return 0;}
/**
* @brief : 初始化按鍵變量
* @note : 無
* @param : 無
* @retval : 無
*/
static void KEY_FIFO_Init(void)
{
uint8_t i;
/* 對按鍵FIFO讀寫指針清零 */
s_tKey.Read = 0;
s_tKey.Write = 0;
/* 給每個按鍵結(jié)構(gòu)體成員變量賦一組缺省值 */
for (i = 0; i < KEY_COUNT; i++)
{
s_tBtn[i].LongTime = KEY_LONG_TIME; /* 長按時間 0 表示不檢測長按鍵事件 */
s_tBtn[i].Count = KEY_FILTER_TIME / 2; /* 計數(shù)器設(shè)置為濾波時間的一半 */
s_tBtn[i].State = 0; /* 按鍵缺省狀態(tài),0為未按下 */
s_tBtn[i].RepeatSpeed = KEY_REPEAT_SPEED; /* 按鍵連發(fā)的速度,0表示不支持連發(fā) */
s_tBtn[i].RepeatCount = 0; /* 連發(fā)計數(shù)器 */
}
/* 判斷按鍵按下的函數(shù) */
s_tBtn[0].IsKeyDownFunc = IsKey1Down;
s_tBtn[1].IsKeyDownFunc = IsKey2Down;
}
/**
* @brief : 將1個鍵值壓入按鍵FIFO緩沖區(qū)
* @note : 無
* @param : KeyCode : 按鍵代碼
* @retval : 無
*/
static void KEY_FIFO_Put(uint8_t _KeyCode)
{
s_tKey.Buf[s_tKey.Write] = _KeyCode;
if (++s_tKey.Write >= KEY_FIFO_SIZE)
{
s_tKey.Write = 0;
}
}
/**
* @brief : 檢測一個按鍵。非阻塞狀態(tài),必須被周期性的調(diào)用
* @note : 無
* @param : 按鍵數(shù)
* @retval : 無
*/
static void KEY_Detect(uint8_t i)
{
KEY_T *pBtn;
pBtn = &s_tBtn[i];
if (pBtn->IsKeyDownFunc())
{// 按鍵按下
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;
/* 發(fā)送按鈕按下的消息 */
KEY_FIFO_Put((uint8_t)(3 * i + 1));
}
if (pBtn->LongTime > 0)
{
if (pBtn->LongCount < pBtn->LongTime)
{
/* 發(fā)送按鈕持續(xù)按下的消息 */
if (++pBtn->LongCount == pBtn->LongTime)
{
if (pBtn->RepeatSpeed > 0)
{
pBtn->LongCount = 0;
if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
{
pBtn->RepeatCount = 0;
/* 常按鍵后,每隔10ms發(fā)送1個按鍵 */
KEY_FIFO_Put((uint8_t)(3 * i + 1));
}
}
else
{
/* 鍵值放入按鍵FIFO */
KEY_FIFO_Put((uint8_t)(3 * i + 3));
}
}
}
}
}
}
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;
/* 發(fā)送按鈕彈起的消息 */
KEY_FIFO_Put((uint8_t)(3 * i + 2));
}
}
pBtn->LongCount = 0;
pBtn->RepeatCount = 0;
}
}
/*****END OF FILE****/
上一篇:STM32內(nèi)存使用及分配
下一篇:KeilMDK配置項中Use MicroLIB是干什么的
推薦閱讀
史海拾趣
設(shè)計資源 培訓 開發(fā)板 精華推薦
- 阿里黑科技落地!夸克AI眼鏡全球首發(fā),高德、淘寶、支付寶都能用
- 化繁為簡, 適配復雜磁場環(huán)境,MT73xx 3D雙路輸出霍爾鎖存器賦能車規(guī)電機精準控制
- 9.5億美元收購恩智浦MEMS傳感器業(yè)務,意法半導體 在傳感器領(lǐng)域的地位再升級
- 高性能電動滑板車 BLDC 電機驅(qū)動器:技術(shù)解析與應用展望
- 5G工業(yè)網(wǎng)關(guān)的“邊緣計算+AI推理”一體化設(shè)計,PLC協(xié)議解析與缺陷檢測的實時聯(lián)動
- AR眼鏡的“工業(yè)指令投射”系統(tǒng),SLAM的空間定位、PLC數(shù)據(jù)實時疊加顯示
- 多光譜氣體傳感器的抗交叉干擾設(shè)計
- 多模態(tài)融合感知的“語義-幾何”聯(lián)合建模
- 工業(yè)觸摸屏的“壓感-手勢”多模態(tài)交互設(shè)計
- 工業(yè)機器人高精度力控的“雙模融合”傳感器設(shè)計