首先說明:這是一個失敗的按鍵掃描程序。它的前身是機智云的STM32按鍵掃描代碼。 不管是用什么芯片,按鍵掃描的延時去抖總是令人心痛,費CPU費電費時間,于是各種各樣好玩有趣的按鍵掃描代碼誕生了。就比如我曾經下載的這個機智云的代碼,使用狀態機+事件驅動,看起來非常漂亮。 使用狀態機和一個定時器,就可以去除消抖延時,提高按鍵掃描效率,還可以在里面添加各種各樣的功能判斷,好處說不完吶。 而事件驅動,把按鍵和按鍵對應的功能隔離開,使用函數指針連接,這樣分隔開了按鍵掃描和事件處理。回想起多級菜單,每一級菜單按鍵功能都不一樣,處理起來簡直就是噩夢。如果用事件機制來做的話,就可以通過函數指針動態改變按鍵功能,終于把重復混亂的變量和代碼送進了回收站。 最近放假實在是心動,我想,如果能把它用在51上該多好呀,于是乎就動手了。但是當我興沖沖地把它改了好半天,終于用在51上的時候,程序直接掛掉了——51速度太慢,消抖檢測周期內不能跑完回調函數,于是就會死機。 實驗環境是STC12C5A60S2 11.0592MHz 代碼8級優化 對于51單片機來說,這個程序致命的缺點: 1. 51速度不夠,如上所述。我試著把KeyHandle函數里后三個功能的檢測注釋掉,就不會死機了。但是這不是寫這段程序的初衷,所以不能這么做。 2. 51內存不夠。這段程序在定義4個按鍵3個事件的時候RAM占用量就過百了,看著51那可憐巴巴的128字節RAM,我都有點不忍了。至于STC自帶的擴展的RAM,速度實在是不夠,如果定義到xdata,回調函數里很簡短的操作都會死機。
結果是失敗了,芯片配置不行,但是代碼還是很有趣的。雖然下面說的這些因為51的速度被限制了,但是我還是想說一說,都是實際問題。 以往的按鍵掃描,按鍵IO口要用sbit或者#define,離掃描函數隔得老遠,16進制鍵碼還得自己算,算錯了就不響應,功能函數就更不忍直視了,和消抖攪和在一起…… 再看這個按鍵掃描: 1. 按鍵IO直接在初始化函數里用字符串輸入,支持任意IO口連接的矩陣鍵盤和單線開關按鍵。 - void KeyInit(void) //按鍵掃描初始化
- {
- SingleKey[EnumKey_Left].IOPort1 = "P34"; SingleKey[EnumKey_Left].IOPort2 = "P30"; //注冊按鍵 Port1必須是IO口 Port2是IO口或"GND"
- SingleKey[EnumKey_Right].IOPort1 = "P35"; SingleKey[EnumKey_Right].IOPort2 = "P30";
- SingleKey[EnumKey_Up].IOPort1 = "P36"; SingleKey[EnumKey_Up].IOPort2 = "P30";
- SingleKey[EnumKey_Down].IOPort1 = "P37"; SingleKey[EnumKey_Down].IOPort2 = "P30";
- ...
復制代碼 矩陣鍵盤不一定要接在一個8 位的整組IO 上。對于40 腳直插的單片機來說,這反而復雜了些。但是呢,看看那些一不丟丟的小可憐單片機,比如STC15W408AS 的16腳封裝,一組完整引出的IO 都沒有,要是在這樣的單片機上用傳統的方式應用4x4 的矩陣鍵盤,那處理起來可難受死了……
10.png (71.78 KB, 下載次數: 53)
下載附件
2021-9-14 20:07 上傳
2. 按鍵編碼就是從0開始到最大按鍵支持數量-1 ,放在一個enum枚舉里面,鍵碼就是移位,不用算,直接復制粘貼就可以。還需要把下面#define的按鍵成員總數也一起改了,注意不要超過最大值,最大值在KeyScan.h里定義,如果有需要可以修改。 - enum EnumUserKey //按鍵編號和鍵值枚舉 編號從0開始 不得超過(KEY_MAX_NUMBER-1)
- {
- EnumKey_Up = 0, EnumKey_Up_TriggerValue = 1<<EnumKey_Up,
- EnumKey_Down = 1, EnumKey_Down_TriggerValue = 1<<EnumKey_Down,
- EnumKey_Left = 2, EnumKey_Left_TriggerValue = 1<<EnumKey_Left,
- EnumKey_Right = 3, EnumKey_Right_TriggerValue = 1<<EnumKey_Right
- };
復制代碼
3. 功能函數作為事件單獨定義,我改進了一下原來的代碼,讓功能和按鍵互相獨立,用戶自定義觸發方式或按鍵組合,再用函數指針連接想要觸發的事件,邏輯簡潔清晰。 還需要把上面#define的用戶自定義的功能總數一起改了,就是KeyFuncs成員的數量,不一定和事件函數或者按鍵數量一致,只要內存夠用,想要多少就要多少。
- void Key7ShortPressEvent(void)
- {
- static u8 i=0;
- i = (i+1)%CountOfArray(table);
- display(i);
- }
- void Key12ShortPressEvent(void)
- {
- static u8 i=0;
- i = (i-1)%CountOfArray(table);
- display(i);
- }
- void Key17_22ShortPressEvent(void)
- {
- Uart_SendString("Func2! \r\n");
- //這個太慢了 會死機! 放主函數里也不行 串口被打斷了就會卡死
- }
復制代碼- KeyFuncs[0].TriggerValue = EnumKey_Up_TriggerValue; //需要響應的鍵值 注意是鍵值! 不是鍵編號! 組合按鍵用或
- KeyFuncs[0].SingleClick = Key7ShortPressEvent; //注冊回調函數
- KeyFuncs[1].TriggerValue = EnumKey_Down_TriggerValue; //需要響應的鍵值 注意是鍵值! 不是鍵編號! 組合按鍵用或
- KeyFuncs[1].SingleClick = Key12ShortPressEvent; //注冊回調函數
- KeyFuncs[2].TriggerValue = EnumKey_Left_TriggerValue | EnumKey_Right_TriggerValue; //需要響應的鍵值 注意是鍵值! 不是鍵編號! 組合按鍵用或
- KeyFuncs[2].MultiPress = Key17_22ShortPressEvent; //注冊回調函數
復制代碼
到這里就算是完成了按鍵驅動的應用,這個思路是不是比傳統的方式簡單多了,嘿嘿。 除非要修改按鍵消抖的時間常數,或者刪減按鍵功能(長按短按判定等),或者改變軟件支持的按鍵數量,不然不需要修改KeyScan.c和KeyScan.h,這樣就完全分離了驅動和應用,不管是移植維護還是調試,都非常方便。
說了這么多,結果不還是不能用嗎?我用STC12C5A60S2 11.0592MHz速度確實不夠,不過現在STC15W內部IRC時鐘可以飆到30MHz,我沒有試過,不知道夠不夠。就算51用不了,移植到別的單片機上也是不錯的。狀態機和事件驅動的思路可以放到很多應用里去,雖然這是一個失敗的程序,但是到目前為止還沒發現邏輯問題,只是受硬件配置限制,就當是一次學習的過程吧。 失敗是成功之母。
GizwitsMCUSTM32F103C8x20170428114156281c5df12c.zip
(725.67 KB, 下載次數: 19)
2018-10-9 10:50 上傳
點擊文件名下載附件
機智云的 下載積分: 黑幣 -5
狀態機按鍵20181008bak.zip
(63.99 KB, 下載次數: 20)
2018-10-9 10:50 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
|