ラズパイの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です。
こんな感じ。
入出力とも動いとります。当然か。さっさとNode-REDダッシュボードに接続しろよ、自分。