
ラズパイの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ダッシュボードに接続しろよ、自分。

