マトリクスキーパッド4×4

pp.413-418
16個あるスイッチのどれが押されたのかを8ピンだけで検出する。接続は下のように固定する。検出されたキーに割り当てた数値をポートBに出力して動作を確認する。
f:id:ti-nspire:20200622160600p:plain:h315

; 下のように接続する。
; ROW0 →PD7
; ROW1 →PD6
; ROW2 →PD5
; ROW3 →PD4

; COL3 →PD3
; COL2 →PD2
; COL1 →PD1
; COL0 →PD0

; スタックポインタの初期化マクロ
.MACRO INITSTACK
    LDI R16, HIGH(RAMEND)
    OUT SPH, R16
    LDI R16, LOW(RAMEND)
    OUT SPL, R16
.ENDMACRO

INITSTACK

; 検出されたキーに割り当ててある数値をポートBに出力して確認したいのでポートBのIOを全部OUTにする。
LDI R21, 0xFF
OUT DDRB, R21

; ポートDのIOを、上位ニブルはOUT (ROW)にし、下位ニブルはIN (COL)にする。
LDI R21, 0xF0
OUT DDRD, R21

; すべての行を一旦Lにする。入力(COL)は内部プルアップする。
GROUND_ALL_ROWS:
    LDI R20, 0x0F
    OUT PORTD, R20

; どれかキーが押されていたら(COLのいずれかが0であったら)、そのキーが放される(COLが全部1になる)のを待つ。
WAIT_FOR_RELEASE:
    NOP
    IN R21, PIND
    ANDI R21, 0x0F
    CPI R21, 0x0F         ; Compare with Immediate
    BRNE WAIT_FOR_RELEASE ; Branch if Not Equal

; どれかキーが押されるのを待つ。
WAIT_FOR_KEY:
    NOP
    IN R21, PIND
    ANDI R21, 0x0F
    CPI R21, 0x0F
    BREQ WAIT_FOR_KEY ; Branch if Equal

    ; どれかキーが押されたら、バウンスが落ち着くのを待って、
    CALL DELAY_20ms

    ; 結局どれもキーが押されていなかったら、戻ってまたキーが押されるのを待つが、
    ; 結局どれかキーが押されていたら、どの行が押されていたのかを調べる処理に移る。
    IN R21, PIND
    ANDI R21, 0x0F
    CPI R21, 0x0F
    BREQ WAIT_FOR_KEY

    ; 0行目を調べる。
    LDI R21, ~(1<<PD7) ; 0行目を0にして、
    OUT PORTD, R21
    NOP
    IN R21, PIND       ; COLのいずれかが
    ANDI R21, 0x0F
    CPI R21, 0x0F
    BRNE ROW0          ; 0であったら、0行目のデータを読み取る処理に移る。

    ; 1行目を調べる。
    LDI R21, ~(1<<PD6) ; 1行目を0にして、
    OUT PORTD, R21
    NOP
    IN R21, PIND       ; COLのいずれかが
    ANDI R21, 0x0F
    CPI R21, 0x0F
    BRNE ROW1          ; 0であったら、1行目のデータを読み取る処理に移る。

    ; 2行目を調べる。
    LDI R21,  ~(1<<PD5) ; 2行目を0にして、
    OUT PORTD, R21
    NOP
    IN R21, PIND       ; COLのいずれかが
    ANDI R21, 0x0F
    CPI R21, 0x0F
    BRNE ROW2          ; 0であったら、2行目のデータを読み取る処理に移る。

    ; 3行目を調べる。
    LDI R21, ~(1<<PD4) ; 3行目を0にして、
    OUT PORTD, R21
    NOP
    IN R21, PIND       ; COLのいずれかが
    ANDI R21, 0x0F
    CPI R21, 0x0F
    BRNE ROW3          ; 0であったら、3行目のデータを読み取る処理に移る。

ROW0:
    LDI R31, HIGH(KCODE0 << 1) ; 0行目のキーに割り当てたデータをZレジスタにロードして、
    LDI R30, LOW(KCODE0 << 1)
    RJMP FIND                  ; 何列目が0であるかを調べる処理に移る。
ROW1:
    LDI R31, HIGH(KCODE1 << 1) ; 1行目のキーに割り当てたデータをZレジスタにロードして、
    LDI R30, LOW(KCODE1 << 1)
    RJMP FIND                  ; 何列目が0であるかを調べる処理に移る。
ROW2:
    LDI R31, HIGH(KCODE2 << 1) ; 2行目のキーに割り当てたデータをZレジスタにロードして、
    LDI R30, LOW(KCODE2 << 1)
    RJMP FIND                  ; 何列目が0であるかを調べる処理に移る。
ROW3:
    LDI R31, HIGH(KCODE3 << 1) ; 3行目のキーに割り当てたデータをZレジスタにロードして、
    LDI R30, LOW(KCODE3 << 1)
    RJMP FIND                  ; 何列目が0であるかを調べる処理に移る。

FIND:
    LSR R21     ; Logical Shift Right
    BRCC MATCH  ; Branch if Carry Cleared
    LPM R20, Z+ ; Load Program Memory
    RJMP FIND

MATCH:
    LPM R20, Z
    OUT PORTB, R20
    RJMP GROUND_ALL_ROWS

;;;;;;;;;;
; delays ;
;;;;;;;;;;
SHORT_DELAY:
    RET ; 4

DELAY_100us:
    PUSH R17          ; 2
    LDI R17 , 200     ; 1
DR0:
    RCALL SHORT_DELAY ; 3
    DEC R17           ; 1W
    BRNE DR0          ; falseで1, trueで2
    POP R17           ; 2
    RET               ; 4

DELAY_20ms:
    PUSH R17          ; 2 
    LDI R17, 200      ; 1
DR1:
    RCALL DELAY_100us ; 3
    DEC R17           ; 1
    BRNE DR1          ; falseで1, trueで2
    POP R17           ; 2
    RET               ; 4

.ORG 0x300
KCODE0: .DB 0x0, 0x1, 0x2, 0x3
KCODE1: .DB 0x4, 0x5, 0x6, 0x7
KCODE2: .DB 0x8, 0x9, 0xA, 0xB
KCODE3: .DB 0xC, 0xD, 0xE, 0xF