msp430でi2c keypad

以前ジャンク袋で手に入れたkeypadをi2cで使えるようにしてみました。

電話機などで使われている12キーのもので3x4のマトリックスになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/29e9dfe0-dd59-0470-f727-8ac76b34bc3c.png)

i2c keypadをネットで調べるとマイコンを使ったものと、i2c gpio変換を使ったものがあります。後者はホスト側でスキャンするようです。

ホスト側の処理を簡単にするためにマイコンを使い、8ピンでは処理できないので、ピンの多いMSP430を使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/3b9516e0-4619-f264-a5c8-371b2223afc4.png)

P1はI2CやISPで使うので、P2で処理する事にしてP2.0,P2.1,P2.2が出力でP2.3,P2.4,P2.5,P2.6を入力にします。

ネットの記事を参考に出力には適当なダイオードを入れました。

keypadの端子をP2.1,P2.3,P2.0,P2.6,P2.2,P2.5,P2.4の並びで接続します。

mainのループで繰り返しキースキャンを行い、i2cのリクエストで返すようにします。

[以前試したコード](https://qiita.com/yamori813/items/abb705100140be5028e0)をベースにPlatformIOで開発します。

```c:main.c
/*
 * This file is part of the MSP430 USCI I2C slave example.
 *
 * Copyright (C) 2012 Stefan Wendler <sw@kaltpost.de>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * This firmware acts as an I2C slave on address 0x90(W)/0x91(R).
 * It receives a command / parameter pair from the I2C master and
 * sends out a response on a masters read request.
 *
 * The firmware knows two commands:
 *
 * CMD_SETLED: sets the build in LED (P1.0) of the launchpad depending
 * on the given parameter to HIGH (0x01) or LOW (0x00).
 *
 * CMD_GETBTN: copies the state of the build in push-button (P1.3) to
 * the response buffer and transmits it to the master on the next read
 * request.
 *
 * As a I2C master, the bus pirate could be used. To set the BP into
 * I2C mode use:
 *
 *    m4 3
 *
 * Bus-Piret I2C commands to
 *
 * - set LED HIGH (P1.0)
 *    [0x90 0x00 0x01
 *
 * - set LED LOW  (P1.0)
 *    [0x90 0x00 0x00
 *
 * - get BUTTON state (P1.3)
 *    [0x90 0x01 [0x91 r
 *
 * For a quick start to setup the I2C slave, the TI USCI I2C slave
 * code from slaa383 was used and slightly modified to work on
 * the MSP430G2553 and the MSP-GCC. For detail on leagal issues
 * regarding the TI_USCI_I2C_slave code see "ti-usci-i2c-slave-legal.txt".
 *
 * NOTE: 100k extrnal pull-ups are needed on SDA/SDC.
 */

#include <msp430.h>
#include <legacymsp430.h>

#include "TI_USCI_I2C_slave.h"

/* callback for start condition */
void start_cb();

/* callback for bytes received */
void receive_cb(unsigned char receive);

/* callback to transmit bytes */
void transmit_cb(unsigned char volatile *receive);

/* Commands */
#define CMD_SETLED  0x00
#define CMD_GETBTN  0x01
#define CMD_GETKEY  0x02
#define CMD_UNKNOWN 0xFF

/* Parameters */
#define PAR_LEDOFF  0x00
#define PAR_LEDON   0x01
#define PAR_UNKNOWN 0xFF

/* Responses */
#define RES_ERROR   0xFF


/* last command */
unsigned char cmd = CMD_UNKNOWN;

/* last parameter */
unsigned char par = PAR_UNKNOWN;

/* response to send out on read req. */
unsigned char res = RES_ERROR;

unsigned char key = 0;

void process_cmd(unsigned char cmd, unsigned char par)
{
    res = RES_ERROR;

    switch(cmd) {
    case CMD_SETLED:
        if(par == PAR_LEDON) {
            P1OUT |= BIT0;
        } else if(par == PAR_LEDOFF) {
            P1OUT &= ~BIT0;
        }
        break;
    case CMD_GETBTN:
        if((P1IN & BIT3)) {
            res = 0x01;
        } else {
            res = 0x00;
        }
        break;
    case CMD_GETKEY:
        res = key;
        break;
    }
}

void start_cb()
{
    cmd = CMD_UNKNOWN;
    par = PAR_UNKNOWN;
}

void receive_cb(unsigned char receive)
{
    if(cmd == CMD_UNKNOWN) {

        cmd = receive;

        if(cmd == CMD_GETBTN || cmd == CMD_GETKEY) {
            process_cmd(cmd, PAR_UNKNOWN);
        }
    } else {
        par = receive;
        process_cmd(cmd, par);
    }
}

void transmit_cb(unsigned char volatile *byte)
{
    *byte = res;
}

char keymap_char[12] = {'1', '4', '7', '*', 
                        '2', '5', '8', '0',
                        '3', '6', '9', '#'};

scan()
{
    unsigned int o_pins[3] = {BIT0, BIT1, BIT2};
    unsigned int i_pins[4] = {BIT3, BIT4, BIT5, BIT6};
    int row, col;
    unsigned char tmp = 0;
    int i;

    for(row = 0; row < 3; ++row)
    {
        P2OUT = o_pins[row];
        for (i = 0; i < 100; ++i)
            P2IN;
        for(col = 0; col < 4; ++col)
        {
            if(P2IN & i_pins[col])
                tmp = keymap_char[row*4+col];
        }
        P2OUT = 0;
    }
    key = tmp;
}

int main(void)
{
    WDTCTL = WDTPW + WDTHOLD;                      // Stop WDT
    P1DIR |= BIT0;                                 // Set P1.0 to output direction
    P1DIR &= ~BIT3;                                // Set P1.3 to input  direction
    P1REN |= BIT3; // Enable internal pullup on P1.3
    P1OUT &= ~BIT0;
 
    TI_USCI_I2C_slaveinit(start_cb, transmit_cb, receive_cb, 0x48);
    BCSCTL1 = CALBC1_16MHZ;
    DCOCTL  = CALDCO_16MHZ;

    __bis_SR_register(GIE);

    P2SEL = 0;
    P2REN = (BIT3 | BIT4 | BIT5 | BIT6);
    P2DIR = (BIT0 | BIT1 | BIT2);
    while(1) scan();

    return 0;
}
```

何故かポートの最初の読み込みが安定しないので、ダミーリードを入れてあります。こうするととりあえず正常に拾えます。keypadの問題かもしれません。

ArduinoにはKeypadのライブラリがありますが、C++ではないので使えません。

mruby-bsdiicで拾ってみます。

```ruby
#!/usr/local/bin/mruby

MSPADDR = 0x48

t = BsdIic.new(0)

last = 0
loop do
  cur = t.read(MSPADDR, 1, 2)
  if cur != 0 && cur != last
    p cur.chr + ","
  end
  last = cur
  usleep 100_000
end
```