2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

pythonでシリアル通信制御してI2C通信する(USBGPIO8デバイスを使用します)

Last updated at Posted at 2020-04-01

本記事について

電子回路経験がありGPIO、I2Cという単語は知っている前提としますのでその辺の用語解説を省きます。

USBGPIO8とは

シリアル通信でGPIOを制御できるデバイスです
Numato Labs社のUSB GPIO(デジタル入出力)ボードで日本だとエレファイン通販から購入できます。
https://www.elefine.jp/SHOP/USBGPIO8.html
ADコンバータも付いているので各種単体実験に便利です。

I2C通信について

http://www.picfun.com/f1/f06.html
こちらの解説がとても分かりやすいです。

なにをするか

python でシリアル通信を制御してUSBGPIO8を制御しEEPROMに1バイト書き込み、その後読み込んで書き込まれている事を確認します。
EEPROM は 24LC64 を使用します。
24LC64はこちらの製品です → http://akizukidenshi.com/catalog/g/gI-00194/

ハードウェアの準備

USBGPIO8 の 0番ポートをSDA とします。
USBGPIO8 の 1番ポートをSCL とします。
各々10kΩでプルアップします。
上記構成でブレッドボード上で接続してます。

GPIO8_I2C_回路図.png

USBGPIO8のコマンドについて

エレファインのサイトや公式からマニュアルがダウンロードできますので詳しくはそちらを参照ください。
最初からプログラム制御ではなくTeraterm等のシリアル端末ソフトから制御実験することをおすすめします。

gpio [command] [arg]\r

という形式です、arg は有ったり無かったりします、末尾の ¥r を忘れないように注意します。
書き込み系のコマンドを送信すると

[送ったコマンド]\n\r>
# 例えば gpio set 0\n\r>

とコマンドにプロンプトを付けて返ってきます。
また読み込み系コマンドを送信すると

[送ったコマンド]\n\r[戻り値]\n\r>
# 例えば gpio read 0\n\r1\n\r>
# \n\r に挟まれている 1 が戻り値

とコマンドに戻り値とプロンプトを付けて返ってきます。

GPIOのLOW-HIGH制御

0番ポートをLOWにするには clear コマンドを使います

SerialInstance = serial.Serial("COM4", 115200, timeout=0.01)
SerialInstance.write("gpio clear 0\r".encode())

0番ポートをHIGHにするには set コマンドを使います

SerialInstance = serial.Serial("COM4", 115200, timeout=0.01)
SerialInstance.write("gpio set 0\r".encode())

コマンド

timeout=0.01 としてタイムアウトを設定します、設定しないと一生応答を返さなくなるので設定したほうが良いです

GPIOの読み取り

0番ポートのLOW-HIGH状態を読み取るには

SerialInstance = serial.Serial("COM4", 115200, timeout=0.01)
SerialInstance.write("gpio read 0\r".encode())

とします

gpio read 0\n\r0\n\r>

と応答が返ってくればGPIOの状態は0(LOW)ということになります。

プログラム構造

0=LOW
1=HIGH
2=読み取りクロック
と定義します。
定義に従い配列を作成し、その配列の通りにSCL、SDAを制御します。


1バイト書き込む場合
EEPROMデバイスアドレス 0x50
書き込みアドレス 0xa0
書き込みデータ 0x33
この条件の時以下の配列を作成する

[
1, 0, 1, 0, 0, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0, 0, 0, 2,
1, 0, 1, 0, 0, 0, 0, 0, 2,
0, 0, 1, 1, 0, 0, 1, 1, 2
]

各行を解説する
[1, 0, 1, 0, 0, 0, 0, 0, 2] 先頭7ビット 0x50 のアドレスを表し、8ビット目は書き込みフラグ、9ビット目はACKの読み出し
[0, 0, 0, 0, 0, 0, 0, 0, 2] 先頭8ビットは書き込みアドレスの上位バイト、9ビット目はACKの読み出し
[1, 0, 1, 0, 0, 0, 0, 0, 2] 先頭8ビットは書き込みアドレスの下位バイト、9ビット目はACKの読み出し
[0, 0, 1, 1, 0, 0, 1, 1, 2] 先頭8ビットは書き込む1バイトのデータ、9ビット目はACKの読み出し

次に1バイト読み込む場合

[
1, 0, 1, 0, 0, 0, 0, 1, 0,
2, 2, 2, 2, 2, 2, 2, 2, 1
]

各行を解説する
[1, 0, 1, 0, 0, 0, 0, 0, 0] 先頭7ビット 0x50 のアドレスを表し、8ビット目は読み込みフラグ、9ビット目はACKの送信
[2, 2, 2, 2, 2, 2, 2, 2, 1] 先頭8ビットは読み出し、9ビット目はNOACKの送信(読み込み完了の通知)

上記のようにSDAのパターンを配列化しそれに従いクロックとデータを送信・受信する構造とする。

ソースコード

# i2c_byte_write_and_read.py
import serial
import sys
import time
import threading

SerialInstance = None

def SerialInit(comString):
    global SerialInstance
    SerialInstance = serial.Serial(comString, 115200, timeout=0.01)
    #SerialInstance = serial.Serial(comString, 19200, timeout=0.1)

def SerialEnd():
    SerialInstance.close()

def SerialTalk(cmd, response=False):
    readLen = len(cmd) + 1 # gpio read 0\n\r # 最初から \r が付いているので +2 ではなく +1 する
    if response == True:
        readLen += 3 # N\n\r
    readLen += 1 # >
    cnt = SerialInstance.write(cmd.encode())
    res = SerialInstance.read(readLen)
    res = res.decode("utf-8").strip()
    return res
    # 返される文字数を正確に読み取り切ること、1文字でも過不足があると応答待ち状態が続く事になる

def gpioHigh(n):
    SerialTalk("gpio set {}\r".format(n))

def gpioLow(n):
    SerialTalk("gpio clear {}\r".format(n))

def gpioRead(n):
    res = SerialTalk("gpio read {}\r".format(n), response=True)
    return res

def ByteToLH(b):
    lh = []
    for i in range(8):
        if (b << i & 128) == 0:
            lh.append(0)
        else:
            lh.append(1)
    return lh

def SDA_LOW():
    gpioLow(0)

def SDA_HIGH():
    gpioHigh(0)

def SCL_LOW():
    gpioLow(1)

def SCL_HIGH():
    gpioHigh(1)
def READ_DATA():
    return gpioRead(0)

def parseData(all):
    res = []
    for l in all:
        a = l.split("\n\r")
        res.append(a[1])
    return res

# 0 = LOW
# 1 = HIGH
# 2 = READ
# スタートコンディション: SCLがHIGHの間にSDAをLOWにする
# ストップコンディション: SCLをHIGHにしてから、SDAをHIGHにする
def WriteProc():
    ctrlByte = ByteToLH(constDeviceAddress << 1)
    addrHigh = ByteToLH((constDataAdress >> 8) & 0xff)
    addrLow = ByteToLH(constDataAdress & 0xff)
    write1Byte = ByteToLH(constData)
    cmdWrite = ctrlByte + [2] + addrHigh + [2] + addrLow + [2] + write1Byte + [2]
    
    # START CONDITION
    SCL_HIGH()
    SDA_LOW()
    
    size = len(cmdWrite)
    data = []
    for i in range(size):
        SCL_LOW()
        d = cmdWrite[i]
        if d == 0:
            SDA_LOW()
        elif d == 1:
            SDA_HIGH()
        SCL_HIGH()
        if d == 2:
            response = READ_DATA()
            data.append(response)
    SCL_LOW() # LOWに戻す
    SDA_LOW() # LOWに戻す
    
    # STOP CONDITION
    SDA_LOW()
    SCL_HIGH()
    SDA_HIGH()
    
    # 書き込み待ち、仕様では5msだが適当に長く待つ
    time.sleep(0.1)
    print(parseData(data))
    print("write end")

def ReadProc():
    #
    #
    # set address
    ctrlByte = ByteToLH(constDeviceAddress << 1)
    addrHigh = ByteToLH((constDataAdress >> 8) & 0xff)
    addrLow = ByteToLH(constDataAdress & 0xff)
    cmdSetAddress = ctrlByte + [2] + addrHigh + [2] + addrLow + [2]
    
    # START CONDITION
    SCL_HIGH()
    SDA_LOW()
    
    size = len(cmdSetAddress)
    data = []
    for i in range(size):
        SCL_LOW()
        #print(i)
        d = cmdSetAddress[i]
        if d == 0:
            SDA_LOW()
        elif d == 1:
            SDA_HIGH()
        SCL_HIGH()
        if d == 2:
            response = READ_DATA()
            data.append(response)
    SCL_LOW() # LOWに戻す
    SDA_LOW() # LOWに戻す
    
    # STOP CONDITION
    SCL_HIGH()
    SDA_HIGH()
    
    print(parseData(data))
    
    #
    #
    # read data
    ctrlByte = ByteToLH((constDeviceAddress << 1) | 0x01)
    readByte = [2] * 8
    cmdReadByte = ctrlByte + [0] + readByte + [1]
    
    # START CONDITION
    SCL_HIGH()
    SDA_LOW()
    
    size = len(cmdReadByte)
    data = []
    for i in range(size):
        SCL_LOW()
        #print(i)
        d = cmdReadByte[i]
        if d == 0:
            SDA_LOW()
        elif d == 1:
            SDA_HIGH()
        SCL_HIGH()
        if d == 2:
            response = READ_DATA()
            data.append(response)
    SCL_LOW() # LOWに戻す
    SDA_LOW() # LOWに戻す
    
    # STOP CONDITION
    SCL_HIGH()
    SDA_HIGH()
    
    print(parseData(data))
    print("read end")

# defines
constDeviceAddress = 0x50 # ICの設定に従う
constDataAdress = 0x0a88 # ICのアドレス範囲内で適当に決める、今回は最大2バイト(0-65535)と想定したコード、3バイトアドレスの場合はコード修正が必要
constData = 0x44 # 0-255 の範囲で適当に決める

def run(comString):
    SerialInit(comString)
    
    WriteProc()
    ReadProc()
    
    SerialEnd()

if __name__ == "__main__":
    run(sys.argv[1])
# python i2c_byte_write_and_read.py COM4

ソースコード解説

使い方

Linuxの場合

python i2c_byte_write_and_read.py /dev/ttyUSB0

Windowsの場合

python i2c_byte_write_and_read.py COM4

戻り値の見方

['0', '0', '0', '0'] # コントロールバイトのACK、アドレス上位バイトのACK、アドレス下位バイトのACK、書き込みデータのACK
write end
['0', '0', '0'] # コントロールバイトのACK、アドレス上位バイトのACK、アドレス下位バイトのACK
['0', '0', '1', '1', '0', '0', '1', '1'] # 読み込んだ8ビットの状態
read end

以上です

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?