やっつけな日常(22) ラズパイ、IO Expander制御、入力モード追加

Joseph Halfmoon

ラズパイのI2Cバスを5V化した先に接続してあるIO Expander MCP23017、いままで全ビット出力設定でした。今回から入力ビットを「混ぜられる」ようにしたいと思います。本当は1ビット毎に方向設定可能なのですが、テスト用のコードを書くのがメンドイのでPortA/Bの8ビット単位で設定できるようにしてみました。

※「やっつけな日常」投稿順 indexはこちら

MCP23017は、合計16ビットの入出力端子があり、それらを1ビットづつ方向制御することができます。入出力のレジスタ自体は8ビット構成で、PortA、PortBの2つに分かれています。Portの各ビットを分割して使用できるのですが、今回はPortA、PortB全体で入力または出力設定としたのでお手軽。

入力設定するにあたっては、何か「入力デバイス」用意しないとテストできませんな。とりあえずありがちな8ビットのDIPスイッチとプルアップ抵抗をPortBに接続してみました。回路図は冒頭のアイキャッチ画像に。

実験用のPythonコード

前回、PortAからの出力は、Pythonスクリプトで制御していましたが、UI的にはNode-REDダッシュボードからON/OFFできるようにしてあったので分かり易かったです。しかし今回は、前回のPythonスクリプトに入力機能を追加する作業だけで疲れてしまったので、Pythonだけです。Node-REDダッシュボードはまた別記事でかな。暑いし。。。

以下は前回のPythonスクリプトのチョイ直しで、Port毎の入出力に対応させたものです。

#! /usr/bin/python3
# coding: utf-8
import argparse
import smbus
import sys
import time

versionSTR = "ioexpander.py v0.1"

i2c = smbus.SMBus(1)

IOEX0 = 0x20
IOEX1 = 0x21
IOCONA = 0x0A
IOCONB = 0x0B
IODIRA = 0x00
IODIRB = 0x01
GPIOA = 0x12
GPIOB = 0x13
OLATA = 0x14
OLATB = 0x15
device = IOEX0

def errPrint(mes, opt=True):
    sys.stderr.write(mes)
    if opt:
        sys.stderr.write('\n')

def initMCP23017(PortA=1, PortB=1):
    """initialize MCP23017 IO expander
    
    IOCON config
    BANK=0, MIRROR=0, SEQOP=1(NO ADDR POINTER INC), DISSLW=0, ...
    """
    i2c.write_byte_data(device, IOCONA, 0x20) #NO SEQOP
    if PortA:
        i2c.write_byte_data(device, IODIRA, 0x00) #OUTPUT
        i2c.write_byte_data(device, OLATA,  0xFF) #LATCH=0xFF
    else:
        i2c.write_byte_data(device, IODIRA, 0xFF) #INPUT
    if PortB:
        i2c.write_byte_data(device, IODIRB, 0x00) #OUTPUT
        i2c.write_byte_data(device, OLATB,  0xFF) #LATCH=0xFF
    else:
        i2c.write_byte_data(device, IODIRB, 0xFF) #INPUT

def testMode():
    """test Mode
    
    Output test pattern to PortA/B. 
    """
    while (1):
        err = i2c.write_byte_data(device, OLATB,  0xAA)
        if err:
            print("ERROR")
        i2c.write_byte_data(device, OLATA,  0xAA)
        time.sleep(0.1)
        i2c.write_byte_data(device, OLATB,  0x55)
        i2c.write_byte_data(device, OLATA,  0x55)
        time.sleep(0.1)    

def outputPORT(prt, dat):
    """output Port
    
    prt=0(A)/1(B), dat=8bit int
    """
    OLAT = OLATA
    if prt:
        OLAT = OLATB
    ov = int(dat) & 0xFF
    i2c.write_byte_data(device, OLAT, ov)

def inputPORT(prt):
    """input Port
    prt=0(A)/1(B), dat=8bit int
    """
    GPIO = GPIOA
    if prt:
        GPIO = GPIOB
    dat = i2c.read_byte_data(device, GPIO)
    return dat

def main():
    parser = argparse.ArgumentParser(description=versionSTR)
    parser.add_argument('--OUTA', nargs=1, help='Port A output data.')
    parser.add_argument('--OUTB', nargs=1, help='Port B output data.')
    parser.add_argument('-INA', dest='INA', help='input from Port A.', action='store_true', default=False)
    parser.add_argument('-INB', dest='INB', help='input from Port B.', action='store_true', default=False)
    parser.add_argument('-T', dest='TEST', help='Start Test Mode.', action='store_true', default=False)
    parser.add_argument('-V', dest='VERSION', help='Show Version, then exit', action='store_true', default=False)
    args = parser.parse_args()
    if args.VERSION:
        errPrint(versionSTR, opt=False)
        sys.exit(1)
    if args.TEST:
        initMCP23017()
        testMode()
        sys.exit(2) #Never!
    PA = 1
    PB = 1
    if args.INA:
        PA=0
    if args.INB:
        PB=0
    initMCP23017(PortA=PA, PortB=PB)
    if PA==0:
        print("A=",inputPORT(0))
    if PB==0:
        print("B=",inputPORT(1))
    if (PA==1) and (args.OUTA is not None):
        outputPORT(0, args.OUTA[0])
    if (PB==1) and (args.OUTB is not None):
        outputPORT(1, args.OUTB[0])
# Normal End
    sys.exit(0)

if __name__ == "__main__":
    main()

8ビットのポート毎の入出力なので、出力にせよ、入力にせよ、0から255までの整数をとります。なお、出力に取り付けてあるLEDはロウで点灯、ハイで消灯です。また今回追加した入力用のDIPスイッチはOFFでハイ、ONでロウです。いずれもロウ・アクティブです。

動作確認

スクリプトの引数 –OUTA に0を渡すとポートA出力がオール・ロウになるのでLEDが全点灯するのは前回と同じです。フラグ -INB を付加するとポートBの8ビット値を読んで10進で返してきます。DIPスイッチオールオフなら255です。また、DIPスイッチオールオンなら0です。

こんな感じ。

Results

入出力とも動いとります。当然か。さっさとNode-REDダッシュボードに接続しろよ、自分。

やっつけな日常(21) NodeRedダッシュボードからIO Expander制御 へ戻る

やっつけな日常(23) ラズパイ4に Rust言語処理系をインストール。野望?は広がる へ進む