マトリクスキーパッド4×4 / Cで記述する / 任意の端子に接続できるようにする

テキストからは逸れるが今度は任意の端子を選んで接続できるようにする。セット、クリアを、ポート単位ではなくピン1本ずつ行う。

https://github.com/ti-nspire/AVR/tree/master/MatrixKeypad

  • サンプルファイル:
#define F_CPU 8000000UL

#include <avr/io.h>
#include "MatKeypadClass.h"

const uint8_t KEYS[4][4] = {{ 0, 1, 2, 3},
                            { 4, 5, 6, 7},
                            { 8, 9,10,11},
                            {12,13,14,15}};

int main(){
    DDRB = 0xFF;

    MatKeypadClass keypad("PD4","PD3","PC0","PC1",
                          "PC2","PC3","PC4","PC5"); // 行0~3、列0~3の順番に指定する。
    keypad.init();
    while(1){
        keypad.detectKey();
        PORTB = KEYS[keypad.getRow()][keypad.getCol()];
    }

    return 0;
}
  • ライブラリーのヘッダーファイル:
#ifndef MATKEYPADCLASS_H_
#define MATKEYPADCLASS_H_

#define F_CPU 8000000UL

#include <avr/io.h>
#include <util/delay.h>

class MatKeypadClass{
    private:
    volatile uint8_t *DDR_R0, *DDR_R1, *DDR_R2, *DDR_R3, *DDR_C0, *DDR_C1, *DDR_C2, *DDR_C3;         // DDRnレジスタのアドレスを格納する変数。
    volatile uint8_t *PORT_R0, *PORT_R1, *PORT_R2, *PORT_R3, *PORT_C0, *PORT_C1, *PORT_C2, *PORT_C3; // PORTnのアドレスを格納する変数。
    volatile uint8_t *PIN_C0, *PIN_C1, *PIN_C2, *PIN_C3;                                             // PINnのアドレスを格納する変数。
    uint8_t R0, R1, R2, R3, C0, C1, C2, C3;                                                          // ピン番号を格納する変数。

    uint8_t row, col;
    
    void waitForRelease();
    void waitForPress();
    void searchRow();

    public:
    void init();
    void detectKey();
    uint8_t getRow();
    uint8_t getCol();


    MatKeypadClass(const char*r0, const char*r1, const char*r2, const char*r3,
                   const char*c0, const char*c1, const char*c2, const char*c3);
};

#endif
  • ライブラリーの実装ファイル:
#include "MatKeypadClass.h"

void MatKeypadClass::init(){
    *DDR_R0 |= (1 << R0); // 行側のIOを全部OUTにする。
    *DDR_R1 |= (1 << R1);
    *DDR_R2 |= (1 << R2);
    *DDR_R3 |= (1 << R3);
    
    *DDR_C0 &= ~(1 << C0); // 列側のIOを全部INにする。
    *DDR_C1 &= ~(1 << C1);
    *DDR_C2 &= ~(1 << C2);
    *DDR_C3 &= ~(1 << C3);

    *PORT_C0 |= (1 << C0); // 列側を全部内部プルアップする。
    *PORT_C1 |= (1 << C1);
    *PORT_C2 |= (1 << C2);
    *PORT_C3 |= (1 << C3);
}
void MatKeypadClass::waitForRelease(){
    *PORT_R0 &= ~(1 << R0); // すべての行をLにして、
    *PORT_R1 &= ~(1 << R1);
    *PORT_R2 &= ~(1 << R2);
    *PORT_R3 &= ~(1 << R3);

    asm("NOP");

    while( !((*PIN_C0 >> C0 & 1)
    &(*PIN_C1 >> C1 & 1)
    &(*PIN_C2 >> C2 & 1)
    &(*PIN_C3 >> C3 & 1)) ); // すべてのキーから指が離れるまで(すべての列がHになるまで)とどまる。
}
void MatKeypadClass::waitForPress(){
    while( (*PIN_C0 >> C0 & 1)
    &(*PIN_C1 >> C1 & 1)
    &(*PIN_C2 >> C2 & 1)
    &(*PIN_C3 >> C3 & 1) );           // どれかキーが押されるまで(どれか列がLになるまで)とどまって、

    _delay_ms(20);                          // どれかキーが押されたらバウンスの収まるのを待って、

    if     (~(*PIN_C0 >> C0) & 1){col = 0;} // 列番号を取得する。
    else if(~(*PIN_C1 >> C1) & 1){col = 1;}
    else if(~(*PIN_C2 >> C2) & 1){col = 2;}
    else if(~(*PIN_C3 >> C3) & 1){col = 3;}
}
void MatKeypadClass::searchRow(){
    volatile uint8_t *PORTs[4] = {PORT_R0, PORT_R1, PORT_R2, PORT_R3};
    const uint8_t posRow[4] = {R0, R1, R2, R3};

    for(uint8_t i=0; i<4; i++){
        *PORT_R0 |= (1 << R0);
        *PORT_R1 |= (1 << R1);
        *PORT_R2 |= (1 << R2);
        *PORT_R3 |= (1 << R3);
        *PORTs[i] &= ~(1 << posRow[i]); // 0行目から順番にLにして、

        asm("NOP");
        
        if( (~(*PIN_C0 >> C0) & 1)
        |(~(*PIN_C1 >> C1) & 1)
        |(~(*PIN_C2 >> C2) & 1)
        |(~(*PIN_C3 >> C3) & 1) ){   // どれかキーの押下が見つかったら、
            row = i;                    // 行番号を取得して、
            break;                      // 抜ける。
        }
    }
}
void MatKeypadClass::detectKey(){
    waitForRelease();
    waitForPress();
    searchRow();
}
uint8_t MatKeypadClass::getRow(){
    return row;
}
uint8_t MatKeypadClass::getCol(){
    return col;
}


MatKeypadClass::MatKeypadClass(const char*r0, const char*r1, const char*r2, const char*r3,
const char*c0, const char*c1, const char*c2, const char*c3){

    // DDRnレジスタのアドレスを格納する変数のアドレスを排列化しておいて、
    volatile uint8_t**DDRs[8] = {&DDR_R0, &DDR_R1, &DDR_R2, &DDR_R3,
                                 &DDR_C0, &DDR_C1, &DDR_C2, &DDR_C3};
    
    // PORTnレジスタのアドレスを格納する変数のアドレスを排列化しておいて、
    volatile uint8_t**PORTs[8] = {&PORT_R0, &PORT_R1, &PORT_R2, &PORT_R3,
                                  &PORT_C0, &PORT_C1, &PORT_C2, &PORT_C3};

    // 指定した端子のDDRn、PORTnを取得しておく。
    const char*sute0[] = {r0, r1, r2, r3, c0, c1, c2, c3};
    for(int i=0; i<8; i++){
        switch(sute0[i][1]){
            case 'B': *DDRs[i] = &DDRB; *PORTs[i] = &PORTB; break;
            case 'C': *DDRs[i] = &DDRC; *PORTs[i] = &PORTC; break;
            case 'D': *DDRs[i] = &DDRD; *PORTs[i] = &PORTD; break;
            default : break;
        }
    }

    // PINnレジスタのアドレスを格納する変数のアドレスを排列化しておいて、
    volatile uint8_t **PINs[4] = {&PIN_C0, &PIN_C1, &PIN_C2, &PIN_C3};

    // 指定した端子のPINnを取得しておく。
    const char*sute1[] = {c0, c1, c2, c3};
    for(int i=0; i<4; i++){
        switch(sute1[i][1]){
            case 'B': *PINs[i] = &PINB; break;
            case 'C': *PINs[i] = &PINC; break;
            case 'D': *PINs[i] = &PIND; break;
            default : break;
        }
    }

    // 指定した端子のピン番号を取得しておく。
    R0 = (uint8_t)(r0[2]-'0');
    R1 = (uint8_t)(r1[2]-'0');
    R2 = (uint8_t)(r2[2]-'0');
    R3 = (uint8_t)(r3[2]-'0');
    C0 = (uint8_t)(c0[2]-'0');
    C1 = (uint8_t)(c1[2]-'0');
    C2 = (uint8_t)(c2[2]-'0');
    C3 = (uint8_t)(c3[2]-'0');
}