鳥なき里のマイコン屋(108) GD32VF103にKEYPAD4X3を接続

Joseph Halfmoon

GD32VF103に割り込みを掛けるのに単体スイッチなど使っておりましたが、ちょいと数字が入力できる程度の「スイッチ配列」が欲しいもんだな、と思い立ちました。しかし、自作して醜い配線が多数というのは嫌。ちょうど良い塩梅の「キット」が売られていました。AE-KIT45-KEYPAD4X3、これなら汚い配線にはならない筈。

※「鳥なき里のマイコン屋」投稿順Indexはこちら

これまたArduino用がメインで売られているキットのようです。以下の商品ページへ行けば、Arduino用のサンプルプログラムをダウンロードすることができます。

秋月通商: 4×3キーパッド作成キット用基板 AE-KIT45-KEYPAD4X3

当方は、Arduinoではなく、GigaDevice社のRISC-Vコア搭載MCU、GD32VF103を搭載したSeeed社の開発ボード(環境はGD32VF103-SDK)にこのキーパッドのキット接続しようとしているので、そのままという分けには行きませぬ。

まずは、「キット」の組み立てから。以下がキットの内容であります。タクトスイッチ12個を4ビットx3ワード構成にするボードです。「ビット線」A,B,C,Dには電源との間に1kΩのプルアップをそれぞれ接続。駆動側の「ワード線」X,Y,Zと各スイッチの間にはダイオードが挿入されます。表面実装部品などはまったくないので、簡単に半田付けできます。

KEYPAD4x3_PACKAGEなおキットには、ピンヘッダが付属しているのですが、当方の事情により、手持ちのピンソケットに換えて実装いたしました。

半田付けしたら、評価ボードのGPIOに接続するだけです。GD32VF103VBT6は、16ビットのGPIOポートが、A,B,C,D,Eと5個も使用可能なので、今回は、

    • Port Dの8、9、10番を「ワード線」X,Y,Z
    • PortEの12、13、14、15番を「ビット線」A,B,C,D

とし、電源は3.3Vといたしました。開発に使っているPlatformIO上では、同じGD32VF103でも48ピンバージョンを搭載している小型ボード Longan nanoと「たばかって」動作させていますが、Longan nanoではこの辺のポートは使えない筈。

接続したところは、こんな感じ。

KEYPAD4x3_TEST単なるGPIO接続なので、初期化、コードについては特に難しいことはありません。以下とおり(などと言いながら、最初、まったく動作せず、慌てふためいて調べたところが、結局、最初にPORT DとEに rcu_periph_clock_enableするのを忘れていた、というオチでした。トホホ。)

void initializePeriph(void) {
    // Enable Peripheral clock
~途中略~
    rcu_periph_clock_enable(RCU_GPIOD);     // Keypad
    rcu_periph_clock_enable(RCU_GPIOE);     // Keypad
~途中略~
// A .. PE12
// B .. PE13
// C .. PE14
// D .. PE15
// X .. PD8
// Y .. PD9
// Z .. PD10
    gpio_init(GPIOD, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10); // KEYPAD drive
    gpio_init(GPIOE, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); // KEYPAD inputs
    gpio_bit_set(GPIOD, GPIO_PIN_8);
    gpio_bit_set(GPIOD, GPIO_PIN_9);
    gpio_bit_set(GPIOD, GPIO_PIN_10);
~以下略~

キーの読み取りルーチンは以下のとおり、全12キーについてそれぞれキーがおされていたら0、キーが押されていなかったら1の1ビットの値を並べたステータス値を返してきます。同時に2つ以上のキーが押されていても当然分かります。

unsigned int keyStatus(void) {
    uint16_t xstat, ystat, zstat;

    gpio_bit_reset(GPIOD, GPIO_PIN_8); //X
    gpio_bit_set(GPIOD, GPIO_PIN_9);
    gpio_bit_set(GPIOD, GPIO_PIN_10);
    delay_1ms(1);
    //delayS(100);
    xstat = gpio_input_port_get(GPIOE) & 0xF000;

    gpio_bit_set(GPIOD, GPIO_PIN_8);
    gpio_bit_reset(GPIOD, GPIO_PIN_9); //Y
    gpio_bit_set(GPIOD, GPIO_PIN_10);
    delay_1ms(1);
    //delayS(100);
    ystat = gpio_input_port_get(GPIOE) & 0xF000;

    gpio_bit_set(GPIOD, GPIO_PIN_8);
    gpio_bit_set(GPIOD, GPIO_PIN_9);
    gpio_bit_reset(GPIOD, GPIO_PIN_10); //Z
    delay_1ms(1);
    //delayS(100);
    zstat = gpio_input_port_get(GPIOE) & 0xF000;
    gpio_bit_set(GPIOD, GPIO_PIN_10);

    return (unsigned int)((zstat >> 4) | (ystat >> 8) | (xstat >> 12));
}

単独キーのキースキャンは問題なかったのですが、ひとつ「トラブッた」のは、2つ以上のキー押しの場合です。同一のワード線上の複数キーについては問題ないのですが、複数のワード線にまたがる同じビット線のキーを2個押すと、押していない3本目のワード線のビットも押されているかに見えました。具体的に言うと、XのAとYのAを同時に押すと押していないZのAも押されているように見えます。

まあ予想はしていました。上のコードの//で潰してあるところが修正箇所。最初使った待ちの長さが短すぎると判断したので、タイマで確実に「長時間」待つ関数で「待ち」をとても長くしたところ正常に動作するようになりました。

ビット線の「1」はプルアップ抵抗で回復するようになっているので、ワード線のスキャンの切り替えがその「回復時間」より早ければ、1と認識されるレベルになる前に読み込んでしまう筈。回復時間は、抵抗1kΩと、浮遊容量(マイコンのポートからKEYPAD間の長いジャンパ線とスイッチ通ってダイオードにいたる経路の)で決まる筈。本当はちゃんと測った方がいいと思うのだけれど、ゆるゆるスキャンすれば、まあ動くので、許す。いい加減な。

鳥なき里のマイコン屋(107) Tontek、タッチに生きる? 4bitMCU へ戻る

鳥なき里のマイコン屋(109) NUC120で28BYJ-48、2相励磁 へ進む