单片机基础学-按键篇
其實我現在也是一個菜鳥,但是這是我學習的過程中的總結,我想寫出來,可能對初學者有用,在此獻丑了。
按鍵總結篇
???????????????????????????????????????????????? 作者:HouZuping
在所有智能產品中,按鍵是最為常用的,所以按鍵程序的好壞很重要。以前我們在學校里學的按鍵檢測方法都是不適用,很浪費時間,減少了CPU的效率。在大家的不斷努力下,基本上編程都是用狀態轉移思想。用狀態機思想編的按鍵檢測程序也很多,像一些高手就寫過很好的按鍵檢測程序,下面我總結了用一個定時器的獨立按鍵檢測,能檢測單擊,連擊,長擊等。這個按鍵檢測程序中包含了狀態轉移思想,時間片段思想,對象編程。
關于按鍵流程圖我就不說了,這個大家都清楚。首先對按鍵進行一個數據結構定義。
struct KeyData
{
?????? unsigned char KeyValue;???????????????????? //按鍵返回值
?????? unsigned char LongClickFlag;?????? ?? //長擊標志
?????? unsigned char RunClickFlag;??????? //連擊標志
?????? unsigned char JudgeRunFlag;?????? ??? //軟定時器的連擊標志
?????? unsigned int? RunCount;???????????? ??? //連擊間隔時間計數
};
?
//按鍵常量的定義 ,根據具體的時鐘來跟改
#define? KeyLongTime_2s?????? 100? //??? 1/Fcy*timerValue*1000
#define? KeyRunClick_500Ms??? 40?? //連擊間隔時間大約是500ms左右
?
這個數據結構是把按鍵當成一個對象,這樣每次按鍵按下后都會有一個返回值,其他標志都是按鍵需要其他功能功能。不管有幾個獨立按鍵,每個按鍵返回值都不同。需要幾個按鍵時,就定義幾個按鍵關鍵字。
//------------接口區
#define KeyOne?? P1_0
#define KeyTwo?? P1_1
?
//----------初始狀態
#define IdleState?? 1
?
這里我只用了兩個按鍵,其實本按鍵最為不好的地方就是按鍵檢測和讀取按鍵的值,每次移植時都需要修改,這里還需要修改,在讀取按鍵值時,我可以采用關鍵字固定,是來確定按鍵返回值固定這種方式,但是我覺得沒有必要,移植時就改吧,有的單片機直接支持位操作,有的不支持。
//****************************************************************
//鍵盤掃描接口部分,移植時需要修改
//****************************************************************
unsigned char KeySwap()
{
?????? if((KeyOne!=IdleState)||(KeyTwo!=IdleState))//按鍵數目判斷狀態,這里是位變量
?????? return 1;
?????? else
?????? return 0;
}
//***************************************************************
//讀取接口值函數,移植時需要修改
//********************************************************************
unsigned char RdKeyValue()
{
?????? if(KeyOne!=IdleState)
?????? return 0x02;
?????? if(KeyTwo!=IdleState)
?????? return 0x03;
?????? else
?????? return 0;
}
?
下面來看看我主要的按鍵檢測部分,也是最重要的部分,我在定時器內定義每隔12ms就去掃描一次按鍵,這樣每個狀態間隔的時間就是12ms,我就是利用這個時間來去抖動,我也是利用這個時間來進行連擊和長擊的判斷。不說了,看程序吧
/********************************************************************
基于軟定時器的通用模塊的建立之一:獨立按鍵通用模塊
創建人? :侯祖平
創建日期:2010.7.01
博客網址:
歡迎大家到我的博客交流..........
*********************************************************************
?
?????? #include "EvenKey.h"
?????? #include "KeyBasic.h"
?
//********************************************************************
//狀態變量
//********************************************************************
enum?
{
?????? KeyIdle = 0,
?????? KeyDownDelay,??????????? //判斷按鍵按下
?????? KeyWait,??????? ??? //按下等待每隔一個軟定時器,變量就加一
?????? KeyFunction,???????? //長擊和短擊判斷
?????? KeyLongClick,????????????? //若按下時間超出,則為長按
?????? KeyShortClick,???????????? //
?????? KeyRunClick,??????? //連擊,可以用一個變量來記錄連擊的次數
?????? KeyUp,??????????????????????? //按鍵抬起
?????? KeyUpDelay,? ??? ?????? //長擊按下延時
?????? KeyOver
};
?
//********************************************************************
//鍵值掃描循環
//********************************************************************
void KeyLoop(struct KeyData *KeyNumber)??? ??????????????????? ???
{
?????? static unsigned char KeyCurValue = 0 , KeyLastValue = 0;
?????? static unsigned char KeyState = 0;
?????? static unsigned int? LongCount = 0;
?????? switch(KeyState)
?????? {
????????????? case KeyIdle :
?????????????????????????????????? if(KeySwap())
?????????????????????????????????? {
????????????????????????????????????????? KeyState = KeyDownDelay;
?????????????????????????????????? }
?????????????????????????????????? else
?????????????????????????????????? {
????????????????????????????????????????? KeyState = KeyIdle;
?????????????????????????????????? }
?????????????????????????????????? break;
?
????????????? case KeyDownDelay :
?????????????????????????????????? if(KeySwap())
?????????????????????????????????? {
????????????????????????????????????????? KeyState = KeyWait;
?????????????????????????????????? }
?????????????????????????????????? else
?????????????????????????????????? {
????????????????????????????????????????? KeyState = KeyIdle;
?????????????????????????????????? }
?????????????????????????????????? break;
?
????????????? case KeyWait : ????????????????????????????????????????????????????? ?//有鍵按下,看是否是連擊
?????????????????????????????????? KeyCurValue = RdKeyValue();
?????????????????????????????????? if((KeyCurValue==KeyLastValue)&&(KeyNumber->JudgeRunFlag))
?????????????????????????????????? {???????????????????????????????????????????????????????????? //
?????????????????????????????????? ?? if(KeyNumber->RunCount < KeyRunClick_500Ms)
????????????????????????????????????????? {????????????????????????????????????????????????????? //則為連擊
???????????????????????????????????????????????? KeyNumber->RunCount = 0;
???????????????????????????????????????????????? KeyNumber->JudgeRunFlag = 0;
???????????????????????????????????????????????? KeyState = KeyRunClick;??????????? //跳轉到連擊狀態
????????????????????????????????????????? }
????????????????????????????????????????? else
????????????????????????????????????????? {
???????????????????????????????????????????????? KeyNumber->RunClickFlag = 0;
???????????????????????????????????????????????? KeyNumber->RunCount = 0;
???????????????????????????????????????????????? KeyNumber->JudgeRunFlag = 0;
???????????????????????????????????????????????? KeyState = KeyFunction;???????????? //若不是繼續判斷
????????????????????????????????????????? }
?????????????????????????????????? }
?????????????????????????????????? else
?????????????????????????????????? {
????????????????????????????????????????? KeyState = KeyFunction;?????
?????????????????????????????????? }
?
?????????????????????????????????? break;
?
????????????? case KeyFunction :
?????????????????????????????????? if(KeySwap())
?????????????????????????????????? {???? ?
????????????????????????????????????????? ?KeyCurValue = RdKeyValue();
????????????????????????????????????????? ?LongCount ++;
????????????????????????????????????????? ?if(LongCount >= KeyLongTime_2s)
????????????????????????????????????????? ?{
????????????????????????????????????????? ????? LongCount = 0;
????????????????????????????????????????? ????? KeyState = KeyLongClick;???
????????????????????????????????????????? ?}
?????????????????????????????????? }
?????????????????????????????????? else
?????????????????????????????????? {
????????????????????????????????????????? if(LongCount < KeyLongTime_2s)
????????????????????????????????????????? ?{
????????????????????????????????????????? ????? LongCount = 0;
????????????????????????????????????????? ????? KeyState = KeyShortClick;??
????????????????????????????????????????? ?}
?????????????????????????????????? }
????????????????????
?????????????????????????????????? break;
?
????????????? case KeyLongClick :????????????????????????????????? ???? //長擊
?????????????????????????????????? #ifdef? HaveLongKey
?????????????????????????????????? KeyNumber->LongClickFlag = 1;
?????????????????????????????????? KeyNumber->KeyValue = KeyCurValue|0x80;
?????????????????????????????????? #endif
?????????????????????????????????? if(!KeySwap())?????????????????????????? //若按鍵抬起 ,則狀態跳轉
?????????????????????????????????? KeyState = KeyUpDelay;
?????????????
?????????????????????????????????? break;
?
?
????????????? case KeyRunClick :????????????????????????????????????????? //連擊
?????????????????????????????????? #ifdef? HaveRunKey
?????????????????????????????????? KeyNumber->RunClickFlag = 1;
?????????????????????????????????? KeyNumber->KeyValue = KeyCurValue|0x40;
?????????????????????????????????? #endif
?????????????????????????????????? if(!KeySwap())?????????????????????????? //若按鍵抬起 ,則狀態跳轉
?????????????????????????????????? KeyState = KeyUpDelay;
?????????????
?????????????????????????????????? break;
?
????????????? case KeyShortClick :????????????????????????? ???? //短擊和連擊的判斷
???????????????????? ????????????? KeyNumber->JudgeRunFlag = 1; //連擊判斷啟動標志位
?????????????????????????????????? KeyNumber->KeyValue = KeyCurValue;
?????????????????????????????????? KeyState = KeyUp;
???????????????????????????????????????????????????????
?????????????????????????????????? break;
?
????????????? case KeyUp :
?????????????????????????????????? KeyNumber->RunClickFlag? = 0;
?????????????????????????????????? KeyNumber->LongClickFlag = 0;
?????????????????????????????????? KeyState = KeyOver;
?????????????????????????????????? break;
?
?????????????
????????????? case KeyUpDelay:
?????????????????????????????????? if(!KeySwap())?????????????????????????? //若按鍵抬起 ,則狀態跳轉
?????????????????????????????????? KeyState = KeyOver;
?
?????????????????????????????????? break;
?
????????????? case KeyOver :????????????????????????????????????????? ???? //結束
?????????????????????????????????? KeyLastValue = KeyCurValue;?????
?????????????????????????????????? KeyState = KeyIdle;
?????????????????????????????????? break;
????????????????????
?????? }????
}
?
?
?
//********************************************************************
//end of file
//********************************************************************
其中連擊的判斷也是我寫這個按鍵檢測的真正目的,以前我都是用兩個定時器來判斷連擊的,我在以前的程序添加了一個程序。
//****************************************************************
//參數傳遞函數,用于傳遞連擊的時間間隔
//****************************************************************
void WrRunTime(struct KeyData *KeyNumber)
{????
?????? ?if(KeyNumber->JudgeRunFlag)
?????? ?{
?????? ????? KeyNumber->RunCount ++;??????? ? //間隔計數啟動
????????????? if(KeyNumber->RunCount >= KeyRunClick_500Ms)
????????????? KeyNumber->JudgeRunFlag = 0;? //若大于等待間隔,自動關閉標志
?????? ?}
?????? ?else
?????? ?{
?????? ????? KeyNumber->RunCount = 0;
?????? ?}??????????
}
?
void WrRunTime(struct KeyData *KeyNumber)這個函數放在定時器中斷函數中,在一次短擊過后,程序會啟動連擊檢測標志KeyNumber->JudgeRunFlag = 1; //連擊判斷啟動標志位
在中斷函數中,若標志KeyNumber->JudgeRunFlag = 1則啟動計時。
//********************************************************************
//中斷函數非常重要,一切以中斷函數為參考
//********************************************************************
void Timer0Interrupt(void) interrupt 1
{
?????? ?TH0 = (65536-22000)/256;? //12MS
?????? ?TL0 = (65536-22000)%256;
?????? ?RunFunction = 1;?????????????? ??? //時間片段標志位
?????? ?WrRunTime(&KeyNumber);????????????? //連擊時間判斷
}
?
一旦檢測到if(KeyNumber->RunCount >= KeyRunClick_500Ms)
則連擊檢測自動關閉。
?
我的長擊和連擊判斷后并沒有在返回值中加什么東西,只有返回了一個標志位。
所以在測試程序中,你需要寫看標志是否至1。
你的測試程序中需要寫一下函數:
struct KeyData? KeyNumber;
//時間片段標志位
unsigned char RunFunction = 0;
?
void TimerInit()
{
?????? ?TMOD = 0x01;
?????? ?TH0 = (65536-22000)/256;? //12MS
?????? ?TL0 = (65536-22000)%256;
?????? ?ET0 = 1;
?????? ?EA = 1;
?????? ?TR0 = 1;
}
//測試
void Loop_f(unsigned char KeyValue)
{
?????? switch(KeyValue)
?????? {
?????? }
}
//test
?
//test
?
void main()
{????
?????? TimerInit();
?????? while(1)
?????? {
????????????? if(RunFunction)
????????????? {
???????????????????? RunFunction = 0;
???????????????????? KeyLoop(&KeyNumber);
???????????????????? Loop_f(KeyNumber.KeyValue);
????????????? }????
?????? }
}
?
總結
以上是生活随笔為你收集整理的单片机基础学-按键篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【如何开发小程序?】如何快速开发一个小程
- 下一篇: 云服务器的主要性能参数,云服务器参数到配