日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

单片机模块学习之键盘

發布時間:2025/4/16 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 单片机模块学习之键盘 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載一位大神的,很全


一篇理解按鍵掃描的思想的博文。


理論

按鍵涉及到的重要知識點就是掃描和消抖了!

關于掃描,主要三種循環查詢,定時查詢,中斷響應,當然各有優缺點,這里來總結下先。

1、循環查詢

在一個循環函數里不斷地掃描按鍵值,獲取按下的按鍵。

優點:實現簡單。
缺點:消抖需要浪費寶貴的CPU時間,且實時性不足(等待)。

2、定時查詢

在中斷服務函數里掃描按鍵活的按鍵值,根據按鍵按下的值然后存入緩沖區,等主函數有需要再來處理按鍵消息。(關于消息機制其實是一個很有意思的東西,這里這樣稱不知道準不準確。。。)

優點:避免消抖浪費時間,不會丟失捕捉按鍵按下,容易實現按鍵按下,長按,以及彈起等動作的識別。
缺點:需要使用定時器中斷。

3、中斷響應

按鍵按下觸發中斷,獲取相應的按鍵值,需要進行消抖處理。
優點:實時性好。
缺點:需要微控制器支持中斷,并且消抖浪費CPU資源。


通過上面的分析我們也不難猜出,其實應用比較好的還是定時查詢的方式,既可以識別多種按鍵狀態,還不必消抖浪費CPU資源。


實驗

①、獨立按鍵

1-2短接實現矩陣按鍵。
2-3短接實現獨立按鍵。

1個獨立按鍵是每2ms掃描一次(進一次中斷保存一下當前值),獲取連續8個當前值,也就是耗費 2*8 = 16ms。

獨立按鍵,同時使用一個數碼管實現按一下+1的操作。(注意J5插針在右邊)

/* ******************************************************************************* * 文件名: * 描 述: * 作 者:CLAY * 版本號:v1.0.0 * 日 期: * 備 注:S4每次加1,S5每次加2,S6每次加3,S7每次加4 * ******************************************************************************* */#include <stc15.h>sbit KEY_IN_1 = P3^3; sbit KEY_IN_2 = P3^2; sbit KEY_IN_3 = P3^1; sbit KEY_IN_4 = P3^0;typedef unsigned char u8; typedef unsigned int u16; typedef unsigned long u32;u8 code LedChar[] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E }; u8 LedBuff[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; u8 KeySta[4] = {1, 1, 1, 1}; u8 KeyCodeMap[4] = {'1', '2', '3', '4'};u8 T0RH; u8 T0RL; u16 cnt = 0;void CloseFucker(); void ConfigTimer0(u16 ms); void ShowNumber(u16 dat); void KeyDriver();void main() {CloseFucker();ConfigTimer0(2);//2ms一掃。EA = 1;ShowNumber(0);while(1){ KeyDriver();} }void KeyAction(u8 keycode) {if(keycode == '1'){cnt += 1;ShowNumber(cnt);} else if(keycode == '2'){cnt += 2;ShowNumber(cnt);} else if(keycode == '3'){cnt += 3;ShowNumber(cnt);} else if(keycode == '4'){cnt += 4;ShowNumber(cnt);} }void KeyDriver() {u8 i;static u8 backup[4] = {1, 1, 1, 1};for(i=0; i<4; i++){if(KeySta[i] != backup[i]){if(backup[i] != 0){KeyAction(KeyCodeMap[i]);}backup[i] = KeySta[i];}} }void CloseFucker() {P2 = (P2 & 0x1F) | 0x80;P0 = 0xFF;P2 = P2 & 0x1F;P2 = (P2 & 0x1F) | 0xA0;P0 = 0xAF;P2 = P2 & 0x1F; }void ConfigTimer0(u16 ms) {u32 tmp;tmp = 11059200 / 12;tmp = (tmp * ms) / 1000;tmp = 65536 - tmp;T0RH = (u8)(tmp >> 8);T0RL = (u8)tmp;TMOD &= 0xF0;TMOD |= 0x01;TH0 = T0RH;TL0 = T0RL;ET0 = 1;TR0 = 1; }void ShowNumber(u16 dat) {char i;u8 buf[8];for(i=0; i<8; i++){buf[i] = dat % 10;dat /= 10;}for(i=7; i>0; i--){if(buf[i] == 0)LedBuff[i] = 0xFF;elsebreak;}for( ; i>=0; i--){LedBuff[i] = LedChar[buf[i]];} }void LedScan() {static u8 index = 0;P2 = (P2 & 0x1F) | 0xE0;P0 = 0xFF;P2 = P2 & 0x1F;P2 = (P2 & 0x1F) | 0xC0;P0 = 0x80 >> index;P2 = P2 & 0x1F;P2 = (P2 & 0x1F) | 0xE0;P0 = LedBuff[index];P2 = P2 & 0x1F;if(index < 7)index++;elseindex = 0; }void KeyScan() {u8 i;static u8 keybuff[4] = {0xFF, 0xFF, 0xFF, 0xFF};keybuff[0] = (keybuff[0] << 1) | KEY_IN_1;keybuff[1] = (keybuff[1] << 1) | KEY_IN_2;keybuff[2] = (keybuff[2] << 1) | KEY_IN_3;keybuff[3] = (keybuff[3] << 1) | KEY_IN_4;for(i=0; i<4; i++){if(keybuff[i] == 0xFF){KeySta[i] = 1;}else if(keybuff[i] == 0x00){KeySta[i] = 0;}else{}} } void interruptTimer0() interrupt 1 {TH0 = T0RH;TL0 = T0RL; LedScan();KeyScan(); }

②、矩陣鍵盤

1個獨立按鍵的時候,一端是接地的。

同理,矩陣按鍵無非就是軟件設置分別接地而已。


關于掃描時間,這里如果還是2ms, 8個掃描值的話。那么4行按鍵,每個按鍵掃8次,也就是2*4*8 = 64ms……有點長了。我們改成,1ms一掃,每個按鍵掃4次。1*4*4 = 16ms 和 1個獨立按鍵的時間一樣!

矩陣按鍵映射關系

/* ******************************************************************************* * 文件名: * 描 述: * 作 者:CLAY * 版本號:v1.0.0 * 日 期: * 備 注:顯示對應的0-9 * ******************************************************************************* */#include <stc15.h>typedef unsigned char u8; typedef unsigned int u16; typedef unsigned long u32;sbit KEY_OUT_1 = P3^0; sbit KEY_OUT_2 = P3^1; sbit KEY_OUT_3 = P3^2; sbit KEY_OUT_4 = P3^3; sbit KEY_IN_4 = P3^4; sbit KEY_IN_3 = P3^5; sbit KEY_IN_2 = P4^2; sbit KEY_IN_1 = P4^4;u8 code LedChar[] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E }; u8 LedBuff[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };u8 KeySta[4][4] = {{1, 1, 1, 1}, {1, 1, 1, 1,}, {1, 1, 1, 1}, {1, 1, 1, 1} };u8 KeyCodeMap[4][4] = { {'1', '2', '3', 0x26},{'4', '5', '6', 0x25},{'7', '8', '9', 0x28},{'0', 0x1B, 0x0D, 0x27} };u8 T0RH; u8 T0RL;void CloseFucker(); void ConfigTimer0(u16 ms); void KeyDriver();void main() {CloseFucker();ConfigTimer0(1);EA = 1;while(1){KeyDriver();} }void ShowNumber(u8 dat) {char i;u8 buf[8];for(i=0; i<8; i++){buf[i] = dat % 10;dat /= 10;}for(i=7; i>0; i--){if(buf[i] == 0)LedBuff[i] = 0xFF;elsebreak;}for( ; i>=0; i--){LedBuff[i] = LedChar[buf[i]];} }void KeyAction(u8 keycode) {if((keycode >= '0') && (keycode <= '9')){ShowNumber(keycode - '0');} }void KeyDriver() {u8 i, j;static u8 backup[4][4] = {{1, 1, 1, 1}, {1, 1, 1, 1,}, {1, 1, 1, 1}, {1, 1, 1, 1}};for(i=0; i<4; i++){for(j=0; j<4; j++){if(KeySta[i][j] != backup[i][j]){if(backup[i][j] != 0){KeyAction(KeyCodeMap[i][j]);}backup[i][j] = KeySta[i][j];}}} }void CloseFucker() {P2 = (P2 & 0x1F) | 0x80;P0 = 0xFF;P2 = P2 & 0x1F;P2 = (P2 & 0x1F) | 0xA0;P0 = 0xAF;P2 = P2 & 0x1F; }void ConfigTimer0(u16 ms) {u32 tmp;tmp = 11059200 / 12;tmp = (tmp * ms) / 1000;tmp = 65536 - tmp;T0RH = (u8)(tmp >> 8);T0RL = (u8)tmp;TMOD &= 0xF0;TMOD |= 0x01;TH0 = T0RH;TL0 = T0RL;ET0 = 1;TR0 = 1; }void LedScan() {static u8 index = 0;P2 = (P2 & 0x1F) | 0xE0;P0 = 0xFF;P2 = P2 & 0x1F;P2 = (P2 & 0x1F) | 0xC0;P0 = 0x80 >> index;P2 = P2 & 0x1F;P2 = (P2 & 0x1F) | 0xE0;P0 = LedBuff[index];P2 = P2 & 0x1F;if(index < 7)index++;elseindex = 0; }void KeyScan() {u8 i;static u8 keyout = 0;static u8 keybuff[4][4] = {{0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF},{0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}};switch(keyout){case 0: KEY_OUT_1 = 0; KEY_OUT_4 = 1; break;case 1: KEY_OUT_2 = 0; KEY_OUT_1 = 1; break;case 2: KEY_OUT_3 = 0; KEY_OUT_2 = 1; break;case 3: KEY_OUT_4 = 0; KEY_OUT_3 = 1; break;default : break;}keybuff[keyout][0] = (keybuff[keyout][0] << 1) | KEY_IN_1;keybuff[keyout][1] = (keybuff[keyout][1] << 1) | KEY_IN_2;keybuff[keyout][2] = (keybuff[keyout][2] << 1) | KEY_IN_3;keybuff[keyout][3] = (keybuff[keyout][3] << 1) | KEY_IN_4;for(i=0; i<4; i++){if((keybuff[keyout][i] & 0x0F) == 0x0F)KeySta[keyout][i] = 1;else if((keybuff[keyout][i] & 0x0F) == 0x00)KeySta[keyout][i] = 0;else{}}keyout++;keyout &= 0x03; }void interruptTimer0() interrupt 1 {TH0 = T0RH;TL0 = T0RL;LedScan();KeyScan();}

③、長按鍵

如果上面所介紹的都沒有問題了的話,就可以在其上的基礎上再來了解一下長按鍵的實現了! 拿獨立按鍵來說,短按下只加一回,長按一直加,思路也很簡單,用到了閾值的思路,這個要特別注意長按的加入不能影響到短按!

設置一個像KeySta的全局變量KeyDownTime,用來保存每個按鍵按下的時間累加,只要彈起就清零,這個是在KeyScan()里面進行操作的,也是和KeySta狀態再一起進行判斷的!
然后還需要個TimeThr這個在KeyDriver()里面,初始值為1000。如果檢測到按下,執行按鍵動作函數,繼續往下執行,如果檢測到某個按鍵的KeyDownTime不為0,再判斷是否大于閾值,大于閾值也要執行按鍵動作函數,然后讓閾值增大,調節閾值增量可以控制增長速度。一旦KeyDownTime等于0,就是按鍵彈起來了,讓閾值回歸1000。

看下怎么實現吧!

以獨立按鍵的實驗為例,矩陣按鍵同理

/* ******************************************************************************* * 文件名: * 描 述: * 作 者:CLAY * 版本號:v1.0.0 * 日 期: * 備 注:S4每次加1,S5每次加2,S6每次加3,S7每次加4 * ******************************************************************************* */#include <stc15.h>sbit KEY_IN_1 = P3^3; sbit KEY_IN_2 = P3^2; sbit KEY_IN_3 = P3^1; sbit KEY_IN_4 = P3^0;typedef unsigned char u8; typedef unsigned int u16; typedef unsigned long u32;u8 code LedChar[] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E }; u8 LedBuff[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; u8 KeySta[4] = {1, 1, 1, 1}; u16 KeyDownTime[4] = {0, 0, 0, 0}; u8 KeyCodeMap[4] = {'1', '2', '3', '4'};u8 T0RH; u8 T0RL; u16 cnt = 0;void CloseFucker(); void ConfigTimer0(u16 ms); void ShowNumber(u16 dat); void KeyDriver();void main() {CloseFucker();ConfigTimer0(2);//2ms一掃。EA = 1;ShowNumber(0);while(1){ KeyDriver();} }void KeyAction(u8 keycode) {if(keycode == '1'){cnt += 1;ShowNumber(cnt);} else if(keycode == '2'){cnt += 2;ShowNumber(cnt);} else if(keycode == '3'){cnt += 3;ShowNumber(cnt);} else if(keycode == '4'){cnt += 4;ShowNumber(cnt);} }void KeyDriver() {u8 i;static u8 backup[4] = {1, 1, 1, 1};static u16 TimeThr[4] = {1000, 1000, 1000, 1000};for(i=0; i<4; i++){if(KeySta[i] != backup[i]){if(backup[i] != 0){KeyAction(KeyCodeMap[i]);}backup[i] = KeySta[i];}if(KeyDownTime[i] > 0){if(KeyDownTime[i] > TimeThr[i]){KeyAction(KeyCodeMap[i]);TimeThr[i] += 200;} }else{TimeThr[i] = 1000;}} }void CloseFucker() {P2 = (P2 & 0x1F) | 0x80;P0 = 0xFF;P2 = P2 & 0x1F;P2 = (P2 & 0x1F) | 0xA0;P0 = 0xAF;P2 = P2 & 0x1F; }void ConfigTimer0(u16 ms) {u32 tmp;tmp = 11059200 / 12;tmp = (tmp * ms) / 1000;tmp = 65536 - tmp;T0RH = (u8)(tmp >> 8);T0RL = (u8)tmp;TMOD &= 0xF0;TMOD |= 0x01;TH0 = T0RH;TL0 = T0RL;ET0 = 1;TR0 = 1; }void ShowNumber(u16 dat) {char i;u8 buf[8];for(i=0; i<8; i++){buf[i] = dat % 10;dat /= 10;}for(i=7; i>0; i--){if(buf[i] == 0)LedBuff[i] = 0xFF;elsebreak;}for( ; i>=0; i--){LedBuff[i] = LedChar[buf[i]];} }void LedScan() {static u8 index = 0;P2 = (P2 & 0x1F) | 0xE0;P0 = 0xFF;P2 = P2 & 0x1F;P2 = (P2 & 0x1F) | 0xC0;P0 = 0x80 >> index;P2 = P2 & 0x1F;P2 = (P2 & 0x1F) | 0xE0;P0 = LedBuff[index];P2 = P2 & 0x1F;if(index < 7)index++;elseindex = 0; }void KeyScan() {u8 i;static u8 keybuff[4] = {0xFF, 0xFF, 0xFF, 0xFF};keybuff[0] = (keybuff[0] << 1) | KEY_IN_1;keybuff[1] = (keybuff[1] << 1) | KEY_IN_2;keybuff[2] = (keybuff[2] << 1) | KEY_IN_3;keybuff[3] = (keybuff[3] << 1) | KEY_IN_4;for(i=0; i<4; i++){if(keybuff[i] == 0xFF){KeySta[i] = 1;KeyDownTime[i] = 0;}else if(keybuff[i] == 0x00){KeySta[i] = 0;KeyDownTime[i] += 4;}else{}} } void interruptTimer0() interrupt 1 {TH0 = T0RH;TL0 = T0RL; LedScan();KeyScan(); }

小結

1、充分利用獨立按鍵和矩陣按鍵再次感受模塊化編程的便利,應用層和底層分離,維護修改記憶都方便,一石好幾鳥。

2、注意程序中KEY_IN和KEY_OUT引腳定義以及KeyCodeMap的定義。

總結

以上是生活随笔為你收集整理的单片机模块学习之键盘的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。