トホホな疑問(45) I2CでAQM1602通信失敗、Pull-up抵抗強すぎた。

Joseph Halfmoon

今回はM5ATOM Liteに使い慣れたI2C接続のLCDディスプレイAQM1602XAを接続しようとして足をすくわれました。まさか。結論から言えばI2Cバスに接続してあった他のデバイスのプルアップ抵抗が強力すぎ。無駄な努力でMicroPythonでACK無視するI2Cもどきを作成、表示OK。端子変えれば済む話だ。

秋月電子通商殿の以下のI2CキャラクタLCDモジュールには、相当前からお世話になっております。

AE-AQM1602A(KIT)

ぶっちゃけ、ディスプレイなど無いマイコンに(多くは問題発生したときに)ちょっと表示が欲しいなどというときに取り付けて参りました。「歴戦の(使い回しとも言う)」デバイスであります。今回は、M5ATOM Lite に取り付けたい要求あり、取り付けて「ハマって」しまいました。使用しているソフトウエアは、MicroPython (ESP32用のgeneric port版)です。

動いて当然、な筈、だった

M5ATOM LiteにAQM1602を取り付けたことは無かったのですが、同じくMicroPythonをのせたRaspberry Pi Picoには取り付けたことがあったのです。

MicroPython的午睡(22) ラズパイPico、AQM1602 LCDパネル接続

勿論、表示OK、動いておりました。このときMicroPythonスクリプトで制御できていたので、これをコピペして、使用するI2Cインタフェースの番号(と端子番号)を変更すれば動作するであろうと考えました(通常ならこれで問題なく動いた筈。)

M5ATOMLiteからはI2Cバスを既に引き出しており、Uno様用に「5V化されてしまって」いるBMP280モジュールに接続していました。M5ATOMLiteの3.3VのI2Cバスを5V化するために以下のモジュール(やはり秋月製)を噛ませてありました

AE-PCA9306

AQM1602は5Vでも動作します。また、先にとりつけてあるBMP280とI2Cアドレスも衝突しないです。よって、バスにつなげば(当然5V電源で)動くでしょう、と。しかし動きませぬ。

見れば分かる、波形を

動く筈のスクリプトを動作させるとエラーが返ってきます。こんな感じ。

AQMerror

ここでの OSError: [Errno 19] ENODEV の意味が良くわらず大分無駄な時間を費やしました。トホホ。結論からいうと

I2Cバスでの通信時に相手デバイスからACKが返ってこない場合にも発生

するようです。波形見ればしみじみ分かります。まずは、正常に動作している5V化されたBMP280モジュールの波形。黄色C1がSCL、青色C2がSDAです。注目は黄色のパルスの9発目、ちょっと隙間があいた直後のところ。SCLの立ち上がり辺でロウに落ちているのが分かるかと思います。

BMPwaveform

一方、エラーとなるAQM相手の通信の波形が以下です。赤で囲ったあたりに注目。

AQMwaveform
ここは、AQM1602がACKを返すべく、ロウにバスを引いているところ、しかしながら、バスに接続しているプルアップが強すぎて中間電位になっているようでした。AE-PCA9306のモジュール上のプルアップ抵抗 1kΩ ありました。電位からするとAQM1602のオープンドレインとほぼほぼ同程度の力で引っ張り合いになっているように想像されます。この電位ではACKが返ったようには見えないと。

ACKが返らないとMicroPythonのI2C関係の関数は以降の転送をキャンセルしてOSErrorを返すみたいです。これはハードウエアI2Cだけでなく、ソフトウエアI2Cというインタフェースでも同様な動作みたいです。

まあI2Cバスの速度によるんだと思います。100kHzでの動作なら、4.7kΩとか10kΩとかでプルアップしても十分じゃないかと思うので、1kΩは強すぎる気もしないでもないです。

とはいえこのACKを無視したらAQM1602動くのか。もしかして壊れてたりしない?

無理矢理の接続スクリプト

ACKを無視して無理やりAQM1602を動作させるスクリプトを書いてみました。本来、AQM1602がACKを返してくるタイミングではSDAピンを入力に切り替えて信号を読まねばなりません。しかし、AQM1602はオープンドレインで電流引いている筈なので、SDAを出力のままこちらからもロウだしてやればいいじゃん、という乱暴な割り切りです。作成した「フルソフトのOUTPUTオンリ」なMicroPythonスクリプトは以下です。

import time
from machine import Pin

scl=Pin(21, Pin.OUT)
sda=Pin(25, Pin.OUT)

def startCond():
    scl.value(1)
    sda.value(1)
    time.sleep_us(10)
    sda.value(0)
    time.sleep_us(10)
    scl.value(0)   

def stopCond():
    scl.value(0)
    sda.value(0)
    time.sleep_us(10)
    scl.value(1)
    sda.value(0)
    time.sleep_us(10)
    sda.value(1)   

def sendBit(bdat):
    scl.value(0)
    sda.value(bdat)
    time.sleep_us(10)
    scl.value(1)
    time.sleep_us(10)
    scl.value(0)

def sendByte(Bdat):
    Bdat &= 0xFF
    for i in range(8):
        bitData = (Bdat >> (7-i)) & 0x1
        sendBit(bitData)
    sendBit(0) #dummy ACK
        
def softAQM1602(sel, dat):
    startCond()
    sendByte(0x7C)
    if sel == 0:
        sendByte(0x00)
    else:
        sendByte(0x40)
    sendByte(dat)
    stopCond()
        
def writeDatAQM1602(dat):
    softAQM1602(1, dat)
    time.sleep_ms(1)    

def writeComAQM1602(com):
    softAQM1602(0, com)
    time.sleep_ms(1)    

def initAQM1602():
    time.sleep_ms(100)
    writeComAQM1602(0x38)
    time.sleep_ms(20)
    writeComAQM1602(0x39)
    time.sleep_ms(20)
    writeComAQM1602(0x14)
    time.sleep_ms(20)
    writeComAQM1602(0x73)
    time.sleep_ms(20)
    writeComAQM1602(0x56)
    time.sleep_ms(20)
    writeComAQM1602(0x6C)
    time.sleep_ms(20)
    writeComAQM1602(0x38)
    time.sleep_ms(20)
    writeComAQM1602(0x01)
    time.sleep_ms(20)
    writeComAQM1602(0x0C)
    time.sleep_ms(20)

def writeLineAQM1602(nL, lin):
    buf = bytearray(lin)
    if len(buf) <= 0:
        return False
    if len(buf) > 16:
        buf = buf[0, 16]
    if nL == 0:
        writeComAQM1602(0x80)
    else:
        writeComAQM1602(0xC0)
    for idx in range(0, len(buf)):
        writeDatAQM1602(buf[idx])

initAQM1602()
while(1):
    writeLineAQM1602(0, "Hello, World!")
    writeLineAQM1602(1, "1234567890ABCDEF")

これを走らせたところが以下です。

AQMDUT

液晶に文字が表示されているのが見えますか?部品が壊れていないことは分かったけれど、無駄な努力です。

別端子にAQM1602を接続した方が良いでしょうね。そしてちゃんとMicroPythonのI2Cインタフェースを呼び出す、と。

トホホな疑問(44) WindowsPCからWindowsPCにSSH接続 に戻る

トホホな疑問(45) WindowsPCからAndroidスマホにSSH接続 へ進む