本帖更新內容詳見:http://www.zg4o1577.cn/bbs/dpj-176186-1.html
#define BaseTime 10 //時間基準10 ms ,如果設計的時基是5ms 則前面的10就用該變成5
#define number_init 0xfffffffd // 初始化相關的utime(unsigned long)變量(如果是unsigned int 就應該是0xfffd),采用這個值的好處是,即使發生時鐘計數器溢出,也不影響其他的程序
typedef unsigned long utime; //這個類型是為了方便移植專門給時間相關變量使用
typedef unsigned long ulong;
typedef unsigned int uint;
typedef unsigned char uchar;
enum ButtonModel{noneClick=0,singalClick,doubleClick,repeatClick,longPress}; //doubleClick目前未定義完整動作 pressDownHold,按下保持
enum ButtonStaus{nonePress=1,pressDown,pressUp,pressDownHold}; /
struct button
{
uchar outPutEn:1; //發送數據使能
uchar lastButton:1; //按鍵上次變動后的狀態 默認 1
uchar init_leavel:1; //設置默認按鍵電平
uchar longPressFlag:1; //長按釋放標志 默認0,一旦開始長按則置1
uchar Gpio_level :3;
uchar applyUseOpenMaxPwm:1; //在關機狀態下,若本按鍵按下啟動了系統,則需要申請全局變量標明,禁止其他按鍵關閉被本按鍵打開的系統(自己打開自己負責關閉)
uchar ticks; //按鍵按下次數
enum ButtonStaus lastButtonStaus; //按鍵上個循環的狀態
enum ButtonStaus thisButtonStaus; //按鍵本循環的狀態
enum ButtonModel lastButtonModel; //按鍵上個循環所處的模式
enum ButtonModel thisButtonModel; //按鍵本循環應該所處的模式
uint changeModelTime; //10ms基準 //長按時間定義
uint pressLongTime; //10ms基準 //多擊時間定義
utime lastPressDownMoment; //上次按鍵按下所處的時刻
utime thisPressDownMoment; //本次按鍵按下所處的時刻
uint tempTime; //緩存按鍵兩次按下之間的時長
utime buttonConfir; //按鍵防抖時長
utime getTimer; //獲取時鐘精準時刻,用于設定按鍵掃描周期
utime acquisitionMoment; //獲取時鐘精準時刻,用于記錄相同按鍵狀態持續時長
uchar (*read_gpio)(void); //獲取按鍵狀態方法
};
/*================================
outPutEn 是為后面接收按鍵是單擊、多擊長按函數準備的參數。比如按鍵掃描程序10ms運行一次,輸出的是單擊,如果沒有outPutEn這個參數,后面的接收程序會在10ms內一直都接收的是單擊指令,這樣就會一直執行單擊需要進行的操作,本來單擊一次檔位變化1,結果現在檔位變化了n
applyUseOpenMaxPwm 是為多按鍵且按鍵操作有優先權做準備,,有優先權的操作不會被其他按鍵操作打斷
===============================*/
struct button button1,button2,button3;
void Scan_key(struct button *Key,utime timer ,uint enOutTime ,uint noiseProofTime)
{
enOutTime/=BaseTime;
noiseProofTime/=BaseTime;
if(number_init==Key->getTimer) //如果是第一次運行,則更新時間
{
Key->getTimer=timer;
}
else
{
if(timer-Key->getTimer>=enOutTime) //如果時間足夠“定義的循環時間” ,則更新時間并允許運行
{
Key->getTimer=timer;
Key->Gpio_level=Key->read_gpio();
if(Key->Gpio_level>1);
else
{
if(Key->lastButton^Key->Gpio_level)//按鍵有電平變化模塊處理開始----------------------------------------------------------------------------//
{
if(number_init==Key->buttonConfir)
Key->buttonConfir=timer;
if(timer-Key->buttonConfir>=noiseProofTime)//按鍵防抖,必須再確認狀態
{
Key->lastButton=Key->Gpio_level;
Key->acquisitionMoment=number_init;
Key->buttonConfir=number_init; //二次確認標志重置
if(Key->init_leavel^Key->Gpio_level)//本次按鍵狀態改變后與定義的電平不一致模塊處理開始
{
switch(Key->lastButtonStaus)
{
case nonePress: //上個循環是定義的初始電平
case pressUp:
{
if(number_init==Key->lastPressDownMoment)//是第一次記錄按鍵按下時刻
{
Key->thisPressDownMoment=Key->lastPressDownMoment=timer;
}
else //不第一次記錄
{
Key->thisPressDownMoment=timer;
Key->tempTime+=Key->thisPressDownMoment-Key->lastPressDownMoment; //獲取兩次按鍵按下之間的時間間隔
Key->lastPressDownMoment=Key->thisPressDownMoment; //更新
}
Key->ticks++;
//不在此處增加pressLongTime判斷的原因是,buftime0記錄的是兩次按鍵按下之間的時長,在兩次按下之間必有彈起,一旦彈起,pressLongTime 就置零
switch(Key->ticks)
{
case 1:
{
if(Key->tempTime>=Key->changeModelTime)//ticks未初始化。
{
Key->ticks=0;
Key->tempTime=0;
}
Key->thisButtonModel=singalClick;
break;
}
case 2:
{
if(Key->tempTime<Key->changeModelTime)//ticks未初始化
Key->thisButtonModel=doubleClick;
else
{
Key->thisButtonModel=singalClick;
Key->ticks=0;
Key->tempTime=0;
}
break;
}
case 3: //三擊,想要增加N擊自己在后面添加 CASE 4,CASE 5,CASE N
{
if(Key->tempTime<Key->changeModelTime)//第三擊的時間也小于定義的改變模式時間
{
Key->thisButtonModel=repeatClick;
}
else //第三擊的時間大于定義的改變模式時間
{
Key->thisButtonModel=singalClick;
}
Key->ticks=0;
Key->tempTime=0;
Key->thisPressDownMoment=Key->lastPressDownMoment=number_init;
break;
}
default :
{
Key->ticks=0;
Key->thisButtonModel=noneClick;
}
}
Key->thisButtonStaus=pressDown; //按鍵按下
break;
}
}
} //按鍵狀態改變后是與定義的電平模塊不同處理結束//
///////////////////////////////////////////////////////////////////////////
else //按鍵狀態改變與定義的電平模塊相同開始處理//
{
Key->longPressFlag=0;
switch(Key->lastButtonStaus)
{
case pressDown://上個循環是低電平,機器人手速
case pressDownHold:
{
if(Key->tempTime>=Key->changeModelTime)
{
Key->tempTime=0;
Key->ticks=0;
}
Key->thisButtonStaus=pressUp; //按鍵彈起
Key->thisButtonModel=noneClick;
break;
}
}
}//本次按鍵狀態改變后是定義的電平處理結束//
}
}//按鍵有電平變化模塊處理結束-------------------------------------------------------------------------------------------------------//
else if(Key->init_leavel==Key->Gpio_level)//按鍵無改變且是初始電平處理開始//
{
if(number_init==Key->acquisitionMoment)
{
Key->acquisitionMoment=timer;
}
if(timer-Key->acquisitionMoment>Key->pressLongTime||timer-Key->acquisitionMoment>Key->changeModelTime||Key->tempTime>=Key->pressLongTime||Key->tempTime>=Key->changeModelTime)
{
Key->ticks=0;
Key->tempTime=0; //tempTime 一旦置零,記錄按鍵按下時刻的值都應當重置
Key->thisPressDownMoment=Key->lastPressDownMoment=number_init;
}
if(timer-Key->acquisitionMoment>259200000)//一個月都沒有使用了,所以清零
{
Open_timer0; //重置timer0 防止一年后溢出
}
switch(Key->lastButtonStaus)
{
case nonePress:
case pressUp:
{
Key->thisButtonStaus=nonePress;
Key->thisButtonModel=noneClick;
break;
}
/*
case pressDown:
case pressDownHold: //容錯處理,理論上若上個循環與本循環電平不一致,應該在電平有變化模塊處理不會在本模塊
{
Key->thisButtonStaus=pressUp;
if(Key->tempTime+1>=Key->pressLongTime)
{
Key->thisButtonModel=longPress;
}
else
{
Key->thisButtonModel=noneClick;
}
Key->tempTime=0;
Key->thisPressDownMoment=Key->lastPressDownMoment=number_init;
break;
}
*/
}
}//按鍵狀態無改變且是初始電平處理結束//
else//按鍵狀態無改變且是非初始電平處理開始//
{
if(number_init==Key->acquisitionMoment)
{
Key->acquisitionMoment=timer;
}
switch(Key->lastButtonStaus)
{
/*
case nonePress: //容錯處理,理論上若上個循環與本循環電平不一致,應該在電平有變化模塊處理不會在本模塊
case pressUp:
{
if(longPress==Key->lastButtonModel)
{
Key->thisButtonStaus=nonePress;
Key->thisButtonModel=noneClick;
}
else
{
if(timer-Key->acquisitionMoment>Key->pressLongTime||Key->tempTime>=Key->pressLongTime)
{
Key->thisButtonModel=longPress;
Key->lastPressDownMoment=Key->thisPressDownMoment=number_init; //一但超時,所謂的記錄按鍵按下時間間隔就無意義,必須重置
Key->tempTime=0; //按鍵鍵緩沖時間不清零,對操作體驗影響很不好
Key->ticks=0;
}
else
{
Key->thisButtonModel=singalClick;
}
Key->thisButtonStaus=pressDown;
}
}
*/
case pressDown:
case pressDownHold:
{
if(longPress==Key->lastButtonModel||Key->longPressFlag)
{
Key->thisButtonModel=noneClick; // 改變了模式,下個循環程序進入其它分支,所以必須在上面加入模式判斷
Key->thisButtonStaus=pressDownHold;
Key->acquisitionMoment=timer;
Key->lastPressDownMoment=Key->thisPressDownMoment=timer; //PressDownMoment必須在按鍵按下保持狀態下更新時間,否則tempTime會計算總的按鍵時長,以至于無法進入repeat模式
}
else
{
if(timer-Key->acquisitionMoment>Key->pressLongTime)
{
Key->longPressFlag=1;
Key->thisButtonModel=longPress;
Key->tempTime=0;
Key->ticks=0;
Key->thisPressDownMoment=Key->lastPressDownMoment=Key->acquisitionMoment=number_init;
}
else
{
Key->thisButtonModel=noneClick;
}
Key->thisButtonStaus=pressDownHold;
}
break;
}
}
}
Key->lastButtonStaus=Key->thisButtonStaus; //最后更新
Key->lastButtonModel=Key->thisButtonModel;
Key->outPutEn=1;
}
}
}
}
/*============================
void Scan_key(struct button *Key,utime timer ,uint enOutTime ,uint noiseProofTime) 中,timer 是授時系統,比如每10ms加一位,enOutTime 是循環檢測時間,比如允許每50ms建成運行一次,noiseProofTime是按鍵防抖動時間,程序運行后檢測按鍵的thisButtonModel狀態就能知道是單擊雙擊多擊還是長按。目前在函數中定義了單擊到三擊,三擊以上可自己添加
================================*/
|