這是一個(gè)簡(jiǎn)單的 PCB 鍵盤,您可以輕松地將其用于您的 Arduino 項(xiàng)目。
我目前正在做一個(gè)將有一個(gè)集成鍵盤的項(xiàng)目,做的時(shí)候碰上了一個(gè)問題:我如何在開發(fā)板原型中包含一個(gè)鍵盤?我不能使用 USB 鍵盤或現(xiàn)有的基于 Arduino 的鍵盤,因?yàn)閷?shí)際項(xiàng)目中的鍵盤直接連接到處理所有其他功能的微控制器。所以我設(shè)計(jì)了這個(gè)基本的基于 PCB 的 64 鍵原型鍵盤矩陣。
此 PCB 不包含任何 IC 。鍵盤矩陣的行和列直接連接到排針,以便鍵盤可以連接到 Arduino 或任何其他微控制器。它非常適合對(duì)包含集成鍵盤的項(xiàng)目進(jìn)行原型設(shè)計(jì)。
項(xiàng)目需要包含詳細(xì)的、大量注釋的代碼,以使其與任何具有足夠 I/O 引腳可用的 Arduino 兼容開發(fā)板一起工作——需要 11 個(gè)引腳。鍵盤有 64 個(gè)鍵,包括 shift、caps、ctrl、alt、fn 和“special”的修飾符。還有六個(gè)額外的鍵可用于定制您喜歡的任何內(nèi)容??梢詥为?dú)定義每個(gè)鍵的功能,包括激活修飾符時(shí)每個(gè)鍵的功能。在我看來,這比現(xiàn)有的鍵盤代碼更有用,后者嚴(yán)重限制了您自定義按鍵行為的能力。
提供的代碼會(huì)將文本打印到 Serial。如果您希望文本轉(zhuǎn)到其他地方,則可以輕松更改此設(shè)置。
關(guān)于程序大小的說明:
項(xiàng)目代碼非常大,因?yàn)樗皇褂萌魏维F(xiàn)有的庫。我完全從頭開始編寫這段代碼,以實(shí)現(xiàn)我需要的可定制性。在 Arduino UNO 上,這將使用 9100 字節(jié) (28%) 的程序存儲(chǔ)空間,全局變量使用 394 字節(jié) (19%) 的動(dòng)態(tài)內(nèi)存。
我的代碼可能會(huì)更高效,并且鍵盤的庫和草圖肯定更小,但這是我可以設(shè)計(jì)的唯一方法,可以為每個(gè)修飾符的每個(gè)鍵提供完全的靈活性。它還考慮了現(xiàn)實(shí)世界的鍵盤使用情況。例如,我的代碼在啟用 Caps Lock 的情況下按 Shift 鍵將生成應(yīng)有的小寫字母。默認(rèn)情況下,按住 FN 鍵的同時(shí)按 ESC 不會(huì)執(zhí)行任何操作。但是這種行為是完全可定制的,所以你可以隨意改變它。
所需:
定制PCB
6x6x5mm 觸覺瞬時(shí)按鈕(x64)
1N4148 開關(guān)二極管(x64)
1x8 排針,母頭或公頭(x2)
74HC595移位寄存器
跳線
面包板
Arduino Uno或任何與 Arduino 兼容的微控制器開發(fā)板
第 1 步:鍵盤矩陣的工作原理
為什么需要鍵盤矩陣?
這個(gè)鍵盤有 64 個(gè)鍵。如果您要將這些按鈕中的每一個(gè)都直接連接到您的開發(fā)板,則需要 64 個(gè) I/O 引腳。那是很多引腳,比大多數(shù)開發(fā)板都可用。為了將它降低到一個(gè)更合理的數(shù)字,我們可以使用鍵盤矩陣,它只需要與鍵數(shù)的平方根(向上取整)相等的引腳數(shù)。
設(shè)置了鍵盤矩陣,因此連接了行中的每個(gè)鍵開關(guān),并且連接了列中的每個(gè)鍵開關(guān)。當(dāng)我們想查看按下了哪些鍵時(shí),我們“激活”第一行,然后檢查每一列。如果特定列處于活動(dòng)狀態(tài),我們就知道該列和第 1 行中的鍵已被按下。然后我們停用第 1 行并激活第 2 行,然后再次檢查所有列。在所有行都被激活后,我們只需從第一行重新開始。
如何掃描鍵盤矩陣:
因?yàn)槲覀兪褂玫氖俏?a target="_blank">控制器,所以“激活”意味著將該行設(shè)置為 LOW 或 HIGH。在這種情況下,我們將行設(shè)置為低,因?yàn)槲覀冊(cè)诹休斎胍_上使用了微控制器的內(nèi)置上拉電阻。如果沒有上拉或下拉電阻,輸入引腳將因接口而做出不可預(yù)測(cè)的反應(yīng),這將記錄錯(cuò)誤的按鈕按下。
Arduino UNO 中使用的 ATmega328P 微控制器沒有任何內(nèi)置下拉電阻,只有上拉電阻。所以我們正在使用這些。上拉電阻將每個(gè)輸入引腳連接到 5V,確保在按下按鈕之前它們始終讀取為高電平。
所有的行通常也設(shè)置為 HIGH,這可以防止列引腳連接到行引腳,無論是否按下按鈕。但是當(dāng)我們準(zhǔn)備好檢查一行時(shí),我們可以將該行設(shè)置為 LOW 。如果按下該行中的按鈕,這將為輸入引腳提供一個(gè)被拉到地的路徑——導(dǎo)致該列現(xiàn)在讀取為 LOW 。
因此,總而言之:我們將一行設(shè)置為 LOW,然后檢查哪些列引腳現(xiàn)在讀取 LOW。這些對(duì)應(yīng)于按下的按鈕。這個(gè)過程發(fā)生得非???,因此我們可以每秒多次掃描整個(gè)鍵盤。我的代碼將其限制為每秒 200 次,以平衡性能、彈跳和確保每個(gè)按鍵都被捕獲。
二極管、重影和 n 鍵翻轉(zhuǎn):
當(dāng)按住某些按鈕組合時(shí),電路中的二極管可以防止意外按鍵。二極管只允許電流沿一個(gè)方向流動(dòng),從而防止重影。如果我們不使用二極管,那么按下某些鍵可能會(huì)導(dǎo)致另一個(gè)未按下的鍵被注冊(cè),因?yàn)殡娏髁鬟^相鄰的開關(guān)。這在簡(jiǎn)化的圖形中顯示,其中按下任何三個(gè)相鄰的鍵都會(huì)導(dǎo)致第四個(gè)角的鍵被注冊(cè),即使它沒有被按下。二極管可以防止這種情況并啟用“n鍵翻轉(zhuǎn)”,這意味著我們可以按我們想要的任何組合按我們想要的任意數(shù)量的鍵,而不會(huì)出現(xiàn)任何問題。
用移位寄存器保存引腳:
精明的你們可能注意到我說鍵盤矩陣需要的引腳數(shù)等于鍵數(shù)的平方根,但我也說過我的鍵盤設(shè)計(jì)只需要 11 個(gè)引腳。應(yīng)該是16吧?不,因?yàn)槲覀兪褂玫氖?74HC595 移位寄存器。這個(gè)移位寄存器讓我們只使用 Arduino 的三個(gè) I/O 引腳來控制多達(dá)八個(gè)輸出引腳。這三個(gè)引腳讓我們向移位寄存器發(fā)送一個(gè)字節(jié)(八位),它將其八個(gè)輸出引腳設(shè)置為高電平或低電平。通過對(duì)輸出行引腳使用移位寄存器,我們節(jié)省了 5 個(gè)完整的 I/O 引腳!
“那么為什么不對(duì)輸入引腳也使用移位寄存器呢?” 你問。最簡(jiǎn)單的答案是輸入需要不同類型的移位寄存器,而我手頭沒有那種類型。但是使用移位寄存器進(jìn)行輸入也會(huì)使我們讀取列的方式復(fù)雜化,并可能導(dǎo)致噪聲和“彈跳”問題。我只想說在這種情況下我不需要承擔(dān)這個(gè)頭疼的事。
第 2 步:PCB 設(shè)計(jì)
原理圖設(shè)計(jì)
現(xiàn)在您了解了鍵盤矩陣的工作原理,我的 PCB 設(shè)計(jì)應(yīng)該很簡(jiǎn)單。我在 KiCAD 中設(shè)計(jì)了 PCB,并從原理圖開始。我只是放置了一個(gè)按鈕符號(hào)和一個(gè)二極管符號(hào),然后復(fù)制并粘貼它們,直到我擁有 64 個(gè)鍵的網(wǎng)格。然后我添加了兩個(gè) 1x8 針頭符號(hào),一個(gè)用于行,一個(gè)用于列。按鈕的一側(cè)連接成列,按鈕的另一側(cè)連接成行。
下一步是將 PCB 封裝分配給每個(gè)原理圖符號(hào)。KiCAD 包含的封裝庫內(nèi)置了必要的封裝。當(dāng)您設(shè)計(jì)自己的 PCB 時(shí),您必須非常小心地選擇正確的封裝,因?yàn)檫@些實(shí)際上最終會(huì)出現(xiàn)在您的 PCB 上。有許多組件具有非常相似的足跡,但間距或其他方面略有不同。確保選擇與您的實(shí)際組件相匹配的組件。
封裝和引腳編號(hào)
請(qǐng)?zhí)貏e注意引腳編號(hào)。KiCAD 有一個(gè)奇怪的問題,即原理圖二極管符號(hào)引腳編號(hào)與封裝引腳編號(hào)不匹配。這導(dǎo)致二極管反向,考慮到它們的極性,這是一個(gè)嚴(yán)重的問題,必須創(chuàng)建一個(gè)自定義二極管封裝并交換引腳號(hào)。
PCB布局
完成原理圖并分配足跡后,我開始進(jìn)行實(shí)際的 PCB 布局。電路板輪廓在 Autodesk Fusion 360 中創(chuàng)建,導(dǎo)出為 DXF,然后在 Edge Cuts 圖層上導(dǎo)入 KiCAD。之后的大部分工作只是簡(jiǎn)單地排列按鈕,使它們的布局類似于普通鍵盤。
PCB制造
設(shè)計(jì)好電路板后,我簡(jiǎn)單地繪制了所有層并將它們添加到一個(gè) zip 文件夾中。該文件夾在此處提供,可以直接上傳到 JLCPCB 等 PCB 制造服務(wù)。
第 3 步:PCB 組裝
這是整個(gè)項(xiàng)目中最簡(jiǎn)單但最繁瑣的一步。只需將所有組件焊接到位。它們都是通孔元件,易于焊接。特別注意二極管的方向。二極管上的標(biāo)記應(yīng)與 PCB 上的標(biāo)記相匹配。
根據(jù)我的經(jīng)驗(yàn),最簡(jiǎn)單的方法是用第三只手將 PCB 固定到位,然后將所有二極管先放入。然后翻轉(zhuǎn)電路板并將它們?nèi)亢附?,然后夾住引線。然后放置所有按鈕并焊接它們。然后將排針焊接到位。您可以使用母頭或公頭排針,這完全取決于您。如果你使用公頭然后放在板子下面,間距是正確的,可以將它們直接粘在面包板上。
第 4 步:將鍵盤連接到您的 Arduino
接線看起來很復(fù)雜,但是當(dāng)您注意到細(xì)節(jié)時(shí),它其實(shí)并沒有那么糟糕。
八根跳線將從列標(biāo)題直接進(jìn)入以下 Arduino 引腳:
第 1 列 》 A0
第 2 欄 》 A1
第 3 欄 》 A2
第 4 欄 》 A3
第 5 欄 》 A4
第 6 欄 》 A5
第 7 欄 》 5
第 8 欄 》 6
接下來,將 74HC595 移位寄存器放在面包板上,橫跨中間休息處。注意芯片的方向,圓點(diǎn)表示引腳 1。
查看接線圖,了解 5V 和接地連接的位置。移位寄存器有兩個(gè)連接到 5V 的引腳和兩個(gè)接地的引腳。
只需三根線即可將移位寄存器連接到 Arduino。他們是:
Shift(時(shí)鐘)11 》 4
Shift(閂鎖)12 》 3
Shift(數(shù)據(jù))14 》 2
出于某種原因,移位寄存器的輸出引腳以違反直覺的方式排列。將移位寄存器連接到行引腳時(shí),請(qǐng)?zhí)貏e注意移位寄存器引腳圖。他們是:
第 1 行 》 Shift (Q0) 15
第 2 行 》 Shift (Q1) 1
第 3 行 》 Shift (Q2) 2
第 4 行 》 Shift (Q3) 3
第 5 行 》 Shift (Q4) 4
第 6 行 》 Shift (Q5) 5
Shift 7 》 Shift (Q6) 6
Shift 8 》 Shift (Q7) 7
沒有任何東西連接到 Arduino 0 或 1 引腳,因?yàn)樗鼈円灿糜诖?a target="_blank">端口并導(dǎo)致沖突。
第 5 步:刷新 Arduino 代碼
這一步?jīng)]有什么特別之處,只需像使用任何其他 Arduino 項(xiàng)目一樣上傳代碼即可。
代碼中的所有內(nèi)容都有詳細(xì)的注釋,您可以閱讀,因此我不會(huì)在這里詳細(xì)介紹?;旧希_設(shè)置為輸入和輸出。主循環(huán)只包含一個(gè)計(jì)時(shí)器功能。每5ms,它調(diào)用函數(shù)掃描鍵盤。在檢查每一列之前,該函數(shù)調(diào)用一個(gè)單獨(dú)的函數(shù)來設(shè)置移位寄存器。按下的鍵將其值打印到串行。
如果要更改按鍵時(shí)打印的內(nèi)容,只需更改Serial.print(“_”); 在與條件相對(duì)應(yīng)的 if 語句中。例如,您可以設(shè)置按住 FN 并按 N 時(shí)打印的內(nèi)容。對(duì)于每個(gè)其他帶有每個(gè)修飾符的鍵也是如此。
許多鍵在這段代碼中根本不做任何事情,因?yàn)樗皇谴蛴〉酱小_@意味著退格鍵不起作用,因?yàn)槟鸁o法從串行監(jiān)視器中刪除該數(shù)據(jù)已經(jīng)收到。但是,如果您愿意,可以隨意使用更改。
在項(xiàng)目中使用鍵盤
打印到串口很好,但這并不是這個(gè)鍵盤的真正意義。該鍵盤的目的是為更復(fù)雜的項(xiàng)目制作原型。這就是為什么很容易改變功能的原因。例如,如果您想將鍵入的文本打印到 OLED 屏幕上,您可以簡(jiǎn)單地將每個(gè)Serial.print(替換為display.print(或您的特定顯示器需要的任何內(nèi)容。Arduino IDE 的Replace All工具非常適合替換所有這些一步到位。
ProtoKeyboardV1-Bits.ino:
/* ? ?Sketch for Prototyping Keyboard V1.2
?* ? ?by Cameron Coward 1/30/21
?* ? ?
?* ? ?Tested on Arduino Uno. Requires custom PCB
?* ? ?and a 74HC595 shift register.
?* ? ?
?* ? ?More info: https://www.hackster.io/cameroncoward/64-key-prototyping-keyboard-matrix-for-arduino-4c9531
?*/
const int rowData = 2; // shift register Data pin for rows
const int rowLatch = 3; // shift register Latch pin for rows
const int rowClock = 4; // shift register Clock pin for rows
// these are our column input pins. Pin 0 and Pin 1 are not used,
// because they cause issues (presumably because they're TX and RX)
const int colA = A0;?
const int colB = A1;?
const int colC = A2;?
const int colD = A3;
const int colE = A4;
const int colF = A5;
const int colG = 5;
const int colH = 6;
// shiftRow is the required shift register byte for each row, rowState will contain pressed keys for each row
const byte shiftRow[] = {B01111111, B10111111, B11011111, B11101111, B11110111, B11111011, B11111101, B11111110};
byte rowState[] = {B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000};
byte prevRowState[] = {B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000};
// ASCII codes for keys with no modifiers pressed. Modifiers are NULL (0),
// because we will check those separately and their values should not be printed.
const char key[] = {
? 0, 49, 50, 51, 52, 53, 54, 55,
? 56, 57, 48, 45, 61, 0, 9, 113,
? 119, 101, 114, 116, 121, 117, 105, 111,
? 112, 91, 93, 92, 7, 97, 115, 100,
? 102, 103, 104, 106, 107, 108, 59, 39,
? 0, 0, 122, 120, 99, 118, 98, 110,
? 109, 44, 46, 47, 0, 0, 0, 0,
? 32, 0, 0, 0, 0, 0, 0, 0
};
// ASCII codes for keys with shift pressed AND caps is active
const char capsShiftKey[] = {
? 0, 33, 64, 35, 36, 37, 94, 38,
? 42, 40, 41, 95, 43, 0, 9, 113,
? 119, 101, 114, 116, 121, 117, 105, 111,
? 112, 123, 125, 124, 7, 97, 115, 100,
? 102, 103, 104, 106, 107, 108, 58, 22,
? 0, 0, 122, 120, 99, 118, 98, 110,
? 109, 44, 46, 47, 0, 0, 0, 0,
? 32, 0, 0, 0, 0, 0, 0, 0
};
// ASCII codes for keys with shift pressed.
const char shiftKey[] = {
? 0, 33, 64, 35, 36, 37, 94, 38,
? 42, 40, 41, 95, 43, 0, 9, 81,
? 87, 69, 82, 84, 89, 85, 73, 79,
? 80, 123, 125, 124, 7, 65, 83, 68,
? 70, 71, 72, 74, 75, 76, 58, 22,
? 0, 0, 90, 88, 67, 86, 66, 78,
? 77, 44, 46, 47, 0, 0, 0, 0,
? 32, 0, 0, 0, 0, 0, 0, 0
};
// ASCII codes for keys with ctrl pressed.
const char ctrlKey[] = {
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 9, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 32, 0, 0, 0, 0, 0, 0, 0
};
// ASCII codes for keys with spcl pressed.
const char spclKey[] = {
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0
};
// ASCII codes for keys with alt pressed.
const char altKey[] = {
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0
};
// ASCII codes for keys with fn pressed.
const char fnKey[] = {
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0,
? 0, 0, 0, 0, 0, 0, 0, 0
};
// ASCII codes for keys with caps is active
const char capsKey[] = {
? 0, 49, 50, 51, 52, 53, 54, 55,
? 56, 57, 48, 45, 61, 0, 9, 81,
? 87, 69, 82, 84, 89, 85, 73, 79,
? 80, 91, 93, 92, 7, 65, 83, 68,
? 70, 71, 72, 74, 75, 76, 59, 39,
? 0, 0, 90, 88, 67, 86, 66, 78,
? 77, 44, 46, 47, 0, 0, 0, 0,
? 32, 0, 0, 0, 0, 0, 0, 0
};
long previousKeyboardMicros = 0; ? ? ? ?// will store last time keyboard was checked
?
// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long keyboardInterval = 500; ? ? ? ? ? // interval at which to check keyboard (microseconds)
int rowToCheck = 0; // We check one row per loop of checkKeyboard(), this combined with keyboardInterval?
? ? ? ? ? ? ? ? ? ? // gives the shiftRegister time to fully update between row checks
bool caps = false; ?// is caps lock on?
bool shift = false; // is either left or right shift pressed?
bool capsShift = false; // are shift AND caps active?
bool ctrl = false; // is the ctrl key pressed?
bool spcl = false; ?// is the spcl key pressed?
bool alt = false; // is the alt key pressed?
bool fn = false; ?// is the function key pressed?
void setup() {
? Serial.begin(9600);
? // setup all column pin as inputs with internal pullup resistors
? pinMode(colA, INPUT_PULLUP);?
? pinMode(colB, INPUT_PULLUP);
? pinMode(colC, INPUT_PULLUP);
? pinMode(colD, INPUT_PULLUP);
? pinMode(colE, INPUT_PULLUP);?
? pinMode(colF, INPUT_PULLUP);
? pinMode(colG, INPUT_PULLUP);
? pinMode(colH, INPUT_PULLUP);
? // the outputs needed to control the 74HC595 shift register
? pinMode(rowLatch, OUTPUT);
? pinMode(rowClock, OUTPUT);
? pinMode(rowData, OUTPUT);
? updateShiftRegister(B11111111); // make sure shift register starts at all HIGH
}
void loop() {
?mainTimer();
}
void mainTimer() {
? unsigned long currentMicros = micros(); // how many microseconds has the Arduino been running?
??
? if(currentMicros - previousKeyboardMicros > keyboardInterval) { // if elapsed time since last check exceeds the interval
? ? // save the last time the keyboard was checked
? ? previousKeyboardMicros = currentMicros; ??
?
? ? checkKeyboard(); // check all of the keys and print out the results to serial
? }
}
void updateShiftRegister(byte row) {
? //this function sets the shift register according to the byte that was passed to it
?
? digitalWrite(rowLatch, LOW); // set latch to low so we can write an entire byte at once
? shiftOut(rowData, rowClock, MSBFIRST, row); ?// write that byte
? digitalWrite(rowLatch, HIGH); // set latch back to high so it shift register will remain stable until next change
}
void checkKeyboard() {
? // set the shift register to the current row's byte value, from the shiftRow[] byte array
? updateShiftRegister(shiftRow[rowToCheck]);
? // Check each column
? if (digitalRead(colA) == LOW) {
? ? bitSet(rowState[rowToCheck], 0);
? } else {
? ? bitClear(rowState[rowToCheck], 0);
? }
??
? if (digitalRead(colB) == LOW) {
? ? bitSet(rowState[rowToCheck], 1);
? } else {
? ? bitClear(rowState[rowToCheck], 1);
? }
??
? if (digitalRead(colC) == LOW) {
? ? bitSet(rowState[rowToCheck], 2);
? } else {
? ? bitClear(rowState[rowToCheck], 2);
? }
??
? if (digitalRead(colD) == LOW) {
? ? bitSet(rowState[rowToCheck], 3);
? } else {
? ? bitClear(rowState[rowToCheck], 3);
? }
??
? if (digitalRead(colE) == LOW) {
? ? bitSet(rowState[rowToCheck], 4);
? } else {
? ? bitClear(rowState[rowToCheck], 4);
? }
??
? if (digitalRead(colF) == LOW) {
? ? bitSet(rowState[rowToCheck], 5);
? } else {
? ? bitClear(rowState[rowToCheck], 5);
? }
??
? if (digitalRead(colG) == LOW) {
? ? bitSet(rowState[rowToCheck], 6);
? } else {
? ? bitClear(rowState[rowToCheck], 6);
? }
??
? if (digitalRead(colH) == LOW) {
? ? bitSet(rowState[rowToCheck], 7);
? } else {
? ? bitClear(rowState[rowToCheck], 7);
? }
? // set all shift register pins to HIGH, this keeps values from "bleeding" over to the next loop
? updateShiftRegister(B11111111);
? rowToCheck = rowToCheck + 1; // iterate to next row
? // after checking the 8th row, check the states (button presses) and then start over on the 1st row
? if (rowToCheck > 7 ) {
? ? checkPressedKeys();
? ? rowToCheck = 0;
? }
}
void checkPressedKeys() {
? // check if either shift key is pressed
? if (bitRead(rowState[5], 1) | bitRead(rowState[6], 4)) {
? ? shift = true;
? } else {
? ? shift = false;
? }
? // check if either ctrl key is pressed
? if (bitRead(rowState[6], 5) | bitRead(rowState[7], 3)) {
? ? ctrl = true;
? } else {
? ? ctrl = false;
? }
? // check if either spcl key is pressed
? if (bitRead(rowState[6], 6) | bitRead(rowState[7], 2)) {
? ? spcl = true;
? } else {
? ? spcl = false;
? }
? // check if either alt key is pressed
? if (bitRead(rowState[6], 7) | bitRead(rowState[7], 1)) {
? ? alt = true;
? } else {
? ? alt = false;
? }
? // check if FN key is pressed
? if (bitRead(rowState[7], 4)) {
? ? fn = true;
? } else {
? ? fn = false;
? }
? // check caps is active and shift is pressed
? if (shift == true && caps == true) {
? ? capsShift = true;
? } else {
? ? capsShift = false;
? }
??
? for (int i = 8; i >= 0; i--) { ? ? ? ? ? ? ? ? ? ?// iterate through each row
? ? for (int j = 7; j >= 0; j--) { ? ? ? ? ? ? ? ? ?// iterate through each bit in that row
? ? ??
? ? ? bool newBit = bitRead(rowState[i], j); ? ? ? ? ? ? // check the state of that bit
? ? ? bool prevBit = bitRead(prevRowState[i], j); ? ? ? ? // check the previous state of that bit
? ? ??
? ? ? if ((newBit == 1) && (prevBit == 0)) { ? ? ? ? ? ? ? ? ? ? ? // only allows button press if state has changed to true
? ? ? ? ? int thisChar = (i * 8) + j; ? ? ? ? ? ? ? // calculate which position in char array to select
? ? ? ? ? if (capsShift == true) {
? ? ? ? ? ? processKey(capsShiftKey[thisChar]);
? ? ? ? ? } else if (shift == true) {
? ? ? ? ? ? processKey(shiftKey[thisChar]);
? ? ? ? ? } else if (ctrl == true) {
? ? ? ? ? ? processKey(ctrlKey[thisChar]);
? ? ? ? ? } else if (alt == true) {
? ? ? ? ? ? processKey(altKey[thisChar]);
? ? ? ? ? } else if (spcl == true) {
? ? ? ? ? ? processKey(spclKey[thisChar]);
? ? ? ? ? } else if (fn == true) {
? ? ? ? ? ? processKey(fnKey[thisChar]);
? ? ? ? ? } else if (caps == true) {
? ? ? ? ? ? processKey(capsKey[thisChar]);
? ? ? ? ? } else {
? ? ? ? ? ? processKey(key[thisChar]); ? ??
? ? ? ? ? }
? ? ? }
? ? ??
? ? ? if (newBit == 1) {
? ? ? ? ? bitSet(prevRowState[i], j); ? ? // set previous bit state to true if a key is pressed
? ? ? } else {
? ? ? ? ? bitClear(prevRowState[i], j); ? // set previous bit state to false if key isn't pressed, so it can be pressed again
? ? ? }
? ? }
? }
}
void processKey(char receivedKey) {
? if (receivedKey == 7) { ? ? ? ? ? ? ? ? // check for special functions in the same way as caps (add new "else if" statements)
? ? caps = !caps;
? } else {
? ? Serial.print(receivedKey); ? ? ? ? ? ?// if char does not correspond to a special function, simply print that char
? }
}
評(píng)論