司徒接著說明如何驗證按鍵功能,不過,在進行按鍵驗證之前,我們必須先了解一下按鍵的一些細節,這樣才會更了解按鍵特性,一般掌機使用的按鍵,大部分都是使用十字鍵、4 個功能鍵、SELECT、START、L、R 等按鍵,這也是早期 Nintendo 掌機的標準配備,而用來耦合按鍵和 PCB 的橋樑則是以導電膠、鍋仔片為大宗,這些看似簡單的東西,其實背後隱藏的許多設計的小細節,司徒嘗試來解說一下過程,下圖是 NDSL 掌機的 PCB 圖片

從上圖可以發現一個設計小細節,十字鍵的 PCB 缺口,它的長相跟 4 個功能鍵並不一致,這是因為十字鍵是連通按鍵(上下左右連在一起),而 4 個功能鍵則是各自分開的按鍵,因此,當十字鍵的其中一個方向被按下時,其餘方向也會受影響,因此 PCB 設計必須做些保護措施,避免鬼鍵的問題發生
司徒使用如下圖片說明一下鬼鍵發生的過程,當十字鍵的下方向被按下時,左、右方向的按鍵也會稍微往下傾斜,所以最壞的情況下,系統將會收到下、左、右三個按鍵的訊息

十字鍵正常位置

下方鍵被按下時,左、右方向的按鍵也往下傾斜,說明導電膠會有誤觸到 PCB 的狀況發生

因此,為了避免十字鍵的鬼鍵問題,十字鍵的 PCB 缺口必須依照方向做適當排列,這樣就可以避免誤觸的問題,如下圖所示,十字鍵的左右按鍵會設計成上下導通,而十字鍵的上下按鍵則會設計成左右導通

至於鍋仔片,由於導通點是位於中央,因此,十字鍵的鬼鍵問題得以改善


下圖是小志掌機使用的按鍵,由於按壓力道需要比較用力,因此,這種按鍵比較少用於掌機,不過,如果使用此類的按鍵,則需要注意彈跳的問題

一般按鍵使用彈片做導通,如下所示

因此,按下或者放開時,會有一段不穩定的彈跳時間,如下圖所示,這就是一般按鍵的彈跳問題

針對軟體的解法,可以延遲一段時間後,一般是 10ms,接著再判斷按鍵是否確實被按下

針對硬體的解法,最常見到的作法就是 RC 濾波,在按鍵旁邊加上電容,基於 10ms 計算,最常使用的是:10K 電阻 + 1uF 電容、47K 電阻 + 220nF 電容,透過電容的充、放電效應,來修飾爬升曲線

市面上,有些晶片已經有內建 Debounce 功能,這樣就可以很方便解決按鍵彈跳問題
當然,有些掌機會使用類比搖桿當作十字鍵使用,如:Caanoo 掌機(類比電阻)、Neo Geo Pocket 掌機(4 顆按鍵),如果是使用 4 顆按鍵則可以使用如上的彈跳解法,但是,如果是使用類比電阻,則需要加入 Dead Zone 判斷,避免漂移問題

Dead Zone 一般用於無效區域設定,當類比搖桿的移動是處於這些區域時,則不會發送任何移動訊號,而類比搖桿最常遇到就是靜止不動時,搖桿自動漂移(鬼鍵問題),因此,如果使用類比搖桿當作十字鍵使用時,記得加入 Dead Zone 判斷,避免漂移問題發生


按鍵電路

ASCII 字型

對應按鍵

main.s
.global _start
.equ GPIO_BASE, 0x02000000
.equ PD_CFG0, 0x0090
.equ PD_CFG1, 0x0094
.equ PD_CFG2, 0x0098
.equ PD_DAT, 0x00a0
.equ PE_CFG0, 0x00c0
.equ PE_CFG1, 0x00c4
.equ PE_DAT, 0x00d0
.equ LCD_RST, (1 << 0)
.equ LCD_WR, (1 << 18)
.equ LCD_RS, (1 << 19)
.equ LCD_RD, (1 << 20)
.equ LCD_CS, (1 << 21)
.equ LCD_BL, (1 << 22)
.equ _250MS, 50000000
.equ _500MS, 100000000
.equ _1S, 200000000
.equ BG, 0x0000
.equ FG, 0xffff
.text
.long 0x4000006f
.byte 'e','G','O','N','.','B','T','0'
.long 0x5F0A6C39
.long 0x8000
.long 0, 0
.long 0, 0, 0, 0, 0, 0, 0, 0
.long 0, 0, 0, 0, 0, 0, 0, 0
.org 0x0400
_start:
li t0, 0x00000000
li a0, GPIO_BASE + PE_CFG0
sw t0, 0(a0)
li a0, GPIO_BASE + PE_CFG1
sw t0, 0(a0)
li t0, 0x11111111
li a0, GPIO_BASE + PD_CFG0
sw t0, 0(a0)
li a0, GPIO_BASE + PD_CFG1
sw t0, 0(a0)
li a0, GPIO_BASE + PD_CFG2
sw t0, 0(a0)
li t0, 0xffffffff
li a0, GPIO_BASE + PD_DAT
sw t0, 0(a0)
jal lcd_rst
li t0, 0xb2
jal lcd_cmd
li t0, 0x5c
jal lcd_dat
li t0, 0x5c
jal lcd_dat
li t0, 0x00
jal lcd_dat
li t0, 0x33
jal lcd_dat
li t0, 0x33
jal lcd_dat
li t0, 0xb7
jal lcd_cmd
li t0, 0x35
jal lcd_dat
li t0, 0x21
jal lcd_cmd
li t0, 0x11
jal lcd_cmd
li t0, _250MS
jal delay
li t0, 0xe0
jal lcd_cmd
li t0, 0xd0
jal lcd_dat
li t0, 0x06
jal lcd_dat
li t0, 0x0b
jal lcd_dat
li t0, 0x07
jal lcd_dat
li t0, 0x07
jal lcd_dat
li t0, 0x24
jal lcd_dat
li t0, 0x2e
jal lcd_dat
li t0, 0x32
jal lcd_dat
li t0, 0x46
jal lcd_dat
li t0, 0x37
jal lcd_dat
li t0, 0x13
jal lcd_dat
li t0, 0x13
jal lcd_dat
li t0, 0x2d
jal lcd_dat
li t0, 0x33
jal lcd_dat
li t0, 0xe1
jal lcd_cmd
li t0, 0xd0
jal lcd_dat
li t0, 0x02
jal lcd_dat
li t0, 0x06
jal lcd_dat
li t0, 0x09
jal lcd_dat
li t0, 0x08
jal lcd_dat
li t0, 0x05
jal lcd_dat
li t0, 0x29
jal lcd_dat
li t0, 0x44
jal lcd_dat
li t0, 0x42
jal lcd_dat
li t0, 0x38
jal lcd_dat
li t0, 0x14
jal lcd_dat
li t0, 0x14
jal lcd_dat
li t0, 0x2a
jal lcd_dat
li t0, 0x30
jal lcd_dat
li t0, 0x36
jal lcd_cmd
li t0, 0xb0
jal lcd_dat
li t0, 0x2a
jal lcd_cmd
li t0, 0x00
jal lcd_dat
li t0, 0x00
jal lcd_dat
li t0, 0x01
jal lcd_dat
li t0, 0x3f
jal lcd_dat
li t0, 0x2b
jal lcd_cmd
li t0, 0x00
jal lcd_dat
li t0, 0x00
jal lcd_dat
li t0, 0x00
jal lcd_dat
li t0, 0xef
jal lcd_dat
li t0, 0x3a
jal lcd_cmd
li t0, 0x55
jal lcd_dat
li t0, 0x29
jal lcd_cmd
li t0, 0x2c
jal lcd_cmd
loop:
li a0, GPIO_BASE + PE_DAT
lw t0, 0(a0)
li t1, (1 << 0)
and t1, t1, t0
bgtz t1, 0f
li t0, 30
j draw
0:
li t1, (1 << 7)
and t1, t1, t0
bgtz t1, 1f
li t0, 31
j draw
1:
li t1, (1 << 4)
and t1, t1, t0
bgtz t1, 2f
li t0, 16
j draw
2:
li t1, (1 << 6)
and t1, t1, t0
bgtz t1, 3f
li t0, 17
j draw
3:
li t1, (1 << 12)
and t1, t1, t0
bgtz t1, 4f
li t0, 'B'
j draw
4:
li t1, (1 << 11)
and t1, t1, t0
bgtz t1, 5f
li t0, 'X'
j draw
5:
li t1, (1 << 1)
and t1, t1, t0
bgtz t1, 6f
li t0, 'L'
j draw
6:
li t1, (1 << 10)
and t1, t1, t0
bgtz t1, 7f
li t0, 'R'
j draw
7:
li t1, (1 << 2)
and t1, t1, t0
bgtz t1, 8f
li t0, 'Y'
j draw
8:
li t1, (1 << 9)
and t1, t1, t0
bgtz t1, 9f
li t0, 'T'
j draw
9:
li t1, (1 << 8)
and t1, t1, t0
bgtz t1, 10f
li t0, 'S'
j draw
10:
li t1, (1 << 3)
and t1, t1, t0
bgtz t1, 11f
li t0, 'M'
j draw
11:
li t1, (1 << 13)
and t1, t1, t0
bgtz t1, 12f
li t0, 'A'
j draw
12:
li x8, 0
li x9, 0
jal set_pos
li t6, 320 * 240
bg:
li t0, BG
jal lcd_dat
addi t6, t6, -1
bgtz t6, bg
li t0, _250MS
jal delay
j loop
draw:
sll t0, t0, 3
la a3, font
add a3, a3, t0
li t6, 8
li x8, 135
li x9, 100
nbyte:
li t5, 8
lb a4, 0(a3)
jal set_pos
byte:
li t1, 0x80
and t0, a4, t1
sll a4, a4, 1
beqz t0, w0
w1:
li t0, FG
jal lcd_dat
li t0, FG
jal lcd_dat
li t0, FG
jal lcd_dat
li t0, FG
jal lcd_dat
j next
w0:
li t0, BG
jal lcd_dat
li t0, BG
jal lcd_dat
li t0, BG
jal lcd_dat
li t0, BG
jal lcd_dat
next:
addi t5, t5, -1
bgtz t5, byte
add a3, a3, 1
add x9, x9, 2
addi t6, t6, -1
bgtz t6, nbyte
j loop
set_pos:
move a2, ra
li t0, 0x2a
jal lcd_cmd
li t0, 0
jal lcd_dat
move t0, x8
jal lcd_dat
li t0, 0x2b
jal lcd_cmd
li t0, 0
jal lcd_dat
move t0, x9
jal lcd_dat
li t0, 0x2c
jal lcd_cmd
jr a2
lcd_wr:
li a0, GPIO_BASE + PD_DAT
li t4, 0x00ff
and t2, t0, t4
li t4, 0xff00
and t3, t0, t4
sll t2, t2, 1
sll t3, t3, 2
move t4, t1
or t4, t4, t2
or t4, t4, t3
sw t4, 0(a0)
li t0, LCD_WR
or t4, t4, t0
sw t4, 0(a0)
jr ra
lcd_dat:
move a1, ra
li t1, LCD_RS | LCD_RD | LCD_BL | LCD_RST
jal lcd_wr
jr a1
lcd_cmd:
move a1, ra
li t1, LCD_RD | LCD_BL | LCD_RST
jal lcd_wr
jr a1
lcd_rst:
move a1, ra
li t0, 0x00000000
li a0, GPIO_BASE + PD_DAT
sw t0, 0(a0)
li t0, _250MS
jal delay
li t0, 0xffffffff
li a0, GPIO_BASE + PD_DAT
sw t0, 0(a0)
li t0, _250MS
jal delay
jr a1
delay:
addi t0, t0, -1
bgtz t0, delay
jr ra
font:
.byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
.byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
.byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
.byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
.byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
.byte 0x3C,0x42,0x99,0xBD,0xBD,0x99,0x42,0x3C,0x3C,0x42,0x81,0x81,0x81,0x81,0x42,0x3C
.byte 0xFE,0x82,0x8A,0xD2,0xA2,0x82,0xFE,0x00,0xFE,0x82,0x82,0x82,0x82,0x82,0xFE,0x00
.byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00
.byte 0x80,0xC0,0xF0,0xFC,0xF0,0xC0,0x80,0x00,0x01,0x03,0x0F,0x3F,0x0F,0x03,0x01,0x00
.byte 0x18,0x3C,0x7E,0x18,0x7E,0x3C,0x18,0x00,0xEE,0xEE,0xEE,0xCC,0x00,0xCC,0xCC,0x00
.byte 0x00,0x00,0x30,0x68,0x78,0x30,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00
.byte 0x3C,0x66,0x7A,0x7A,0x7E,0x7E,0x3C,0x00,0x0E,0x3E,0x3A,0x22,0x26,0x6E,0xE4,0x40
.byte 0x18,0x3C,0x7E,0x3C,0x3C,0x3C,0x3C,0x00,0x3C,0x3C,0x3C,0x3C,0x7E,0x3C,0x18,0x00
.byte 0x08,0x7C,0x7E,0x7E,0x7C,0x08,0x00,0x00,0x10,0x3E,0x7E,0x7E,0x3E,0x10,0x00,0x00
.byte 0x58,0x2A,0xDC,0xC8,0xDC,0x2A,0x58,0x00,0x24,0x66,0xFF,0xFF,0x66,0x24,0x00,0x00
.byte 0x00,0x10,0x10,0x38,0x38,0x7C,0xFE,0x00,0xFE,0x7C,0x38,0x38,0x10,0x10,0x00,0x00
.byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x1C,0x1C,0x18,0x00,0x18,0x18,0x00
.byte 0x6C,0x6C,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x7C,0x28,0x7C,0x28,0x00,0x00
.byte 0x10,0x38,0x60,0x38,0x0C,0x78,0x10,0x00,0x40,0xA4,0x48,0x10,0x24,0x4A,0x04,0x00
.byte 0x18,0x34,0x18,0x3A,0x6C,0x66,0x3A,0x00,0x18,0x18,0x20,0x00,0x00,0x00,0x00,0x00
.byte 0x30,0x60,0x60,0x60,0x60,0x60,0x30,0x00,0x0C,0x06,0x06,0x06,0x06,0x06,0x0C,0x00
.byte 0x10,0x54,0x38,0x7C,0x38,0x54,0x10,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00
.byte 0x00,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x00,0x00
.byte 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x04,0x08,0x10,0x20,0x40,0x00,0x00
.byte 0x38,0x4C,0xC6,0xC6,0xC6,0x64,0x38,0x00,0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00
.byte 0x7C,0xC6,0x0E,0x3C,0x78,0xE0,0xFE,0x00,0x7E,0x0C,0x18,0x3C,0x06,0xC6,0x7C,0x00
.byte 0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x00,0xFC,0xC0,0xFC,0x06,0x06,0xC6,0x7C,0x00
.byte 0x3C,0x60,0xC0,0xFC,0xC6,0xC6,0x7C,0x00,0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00
.byte 0x78,0xC4,0xE4,0x78,0x86,0x86,0x7C,0x00,0x7C,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00
.byte 0x00,0x00,0x18,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x18,0x18,0x30
.byte 0x1C,0x38,0x70,0xE0,0x70,0x38,0x1C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00
.byte 0x70,0x38,0x1C,0x0E,0x1C,0x38,0x70,0x00,0x7C,0xC6,0xC6,0x1C,0x18,0x00,0x18,0x00
.byte 0x3C,0x42,0x99,0xA1,0xA5,0x99,0x42,0x3C,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00
.byte 0xFC,0xC6,0xC6,0xFC,0xC6,0xC6,0xFC,0x00,0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00
.byte 0xF8,0xCC,0xC6,0xC6,0xC6,0xCC,0xF8,0x00,0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xFE,0x00
.byte 0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xC0,0x00,0x3E,0x60,0xC0,0xCE,0xC6,0x66,0x3E,0x00
.byte 0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00
.byte 0x06,0x06,0x06,0x06,0xC6,0xC6,0x7C,0x00,0xC6,0xCC,0xD8,0xF0,0xF8,0xDC,0xCE,0x00
.byte 0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00
.byte 0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00
.byte 0xFC,0xC6,0xC6,0xC6,0xFC,0xC0,0xC0,0x00,0x7C,0xC6,0xC6,0xC6,0xDE,0xCC,0x7A,0x00
.byte 0xFC,0xC6,0xC6,0xCE,0xF8,0xDC,0xCE,0x00,0x78,0xCC,0xC0,0x7C,0x06,0xC6,0x7C,0x00
.byte 0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00
.byte 0xC6,0xC6,0xC6,0xEE,0x7C,0x38,0x10,0x00,0xC6,0xC6,0xD6,0xFE,0xFE,0xEE,0xC6,0x00
.byte 0xC6,0xEE,0x3C,0x38,0x7C,0xEE,0xC6,0x00,0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00
.byte 0xFE,0x0E,0x1C,0x38,0x70,0xE0,0xFE,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00
.byte 0x60,0x60,0x30,0x18,0x0C,0x06,0x06,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00
.byte 0x18,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF
.byte 0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x06,0x3E,0x66,0x66,0x3C,0x00
.byte 0x60,0x7C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x3C,0x66,0x60,0x60,0x66,0x3C,0x00
.byte 0x06,0x3E,0x66,0x66,0x66,0x66,0x3E,0x00,0x00,0x3C,0x66,0x66,0x7E,0x60,0x3C,0x00
.byte 0x1C,0x30,0x78,0x30,0x30,0x30,0x30,0x00,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x3C
.byte 0x60,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x00
.byte 0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x38,0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00
.byte 0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0xEC,0xFE,0xFE,0xFE,0xD6,0xC6,0x00
.byte 0x00,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x3C,0x00
.byte 0x00,0x7C,0x66,0x66,0x66,0x7C,0x60,0x60,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x06
.byte 0x00,0x7E,0x70,0x60,0x60,0x60,0x60,0x00,0x00,0x3C,0x60,0x3C,0x06,0x66,0x3C,0x00
.byte 0x30,0x78,0x30,0x30,0x30,0x30,0x1C,0x00,0x00,0x66,0x66,0x66,0x66,0x6E,0x3E,0x00
.byte 0x00,0x66,0x66,0x66,0x66,0x3C,0x18,0x00,0x00,0xC6,0xD6,0xFE,0xFE,0x7C,0x6C,0x00
.byte 0x00,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x06,0x3C
.byte 0x00,0x7E,0x0C,0x18,0x30,0x60,0x7E,0x00,0x0E,0x18,0x0C,0x38,0x0C,0x18,0x0E,0x00
.byte 0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0x70,0x18,0x30,0x1C,0x30,0x18,0x70,0x00
.byte 0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x10,0x28,0x10,0x54,0xAA,0x44,0x00,0x00
.end
完成

