MicroPython的午睡(141)M5Stack、SRAMモジュール読み書き、ダメダメよ

Joseph Halfmoon

前回、MicroPythonのソフト制御のDMAコントローラ一丁あがりとぬか喜び。早速制御対象のSRAMモジュールを組立。8085風味の8ビット・マルチプレクス・バス接続です。ジャンパ配線多過ぎ。MicroPython制御でメモリに読み書きは出来るみたいなんだけれども信頼性ダメダメ。嫌疑はI2Cバスに。。。

※「MicroPython的午睡」投稿順 Indexはこちら

※M5Stack Grayに書き込んだUIFlow2.0 (Alpha-27)対応のMicroPython処理系とWindowsパソコン上で動作しているThonny 4.0.1で動作確認しています。

今回接続したSRAM

使用したチップは、何年か前に秋月電子通商殿にて購入のEPSON製の256KビットSRAM、SRM2B256SLMXです。まだ電子工作業界では流通しているみたいですが、御本家では既にディスコンになっているチップではないかと。ともあれ、これで32Kワードx8ビットの主記憶を構成することができます。

マルチプレクスのアドレス、データバスの分離は例によってSN74LS373を使用しました。制御信号のデコードは、TC74HC42(BCDデコーダー)です。

冒頭の画像に前回回路に接続した回路全体の回路図を掲げました。現物は配線のたくってます。こんな感じ。M5Stack32KBSRAMmodule

 

SRAMとLS373付近が信号込みすぎ。かなり汚いです。

SRAMの読み書きテストのために使用したMicroPythonスクリプト

以下のスクリプトで読み書きテストを実施してみました。

# softDMA_REWRtest.py
from machine import I2C, Pin
import M5
import time

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

recount = 0
wecount = 0

i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=100000)

def isIOEXavailable():
    global i2c
    lis = i2c.scan()
    if (IOEX0 in lis) and (IOEX1 in lis):
        return True
    return False

def readByte(i2cADR, regADR):
    global i2c, recount
    bufW = bytes([regADR])
    try:
        ackW = i2c.writeto(i2cADR, bufW)
        bufR = bytearray(1)
        ackR = i2c.readfrom_into(i2cADR, bufR)
    except OSError:
        recount += 1
        return -1       
    return int.from_bytes(bufR, 'little')  

def writeByte(i2cADR, regADR, dat):
    global i2c, wecount
    bufW = bytes([regADR, dat])
    try:
        ackW = i2c.writeto(i2cADR, bufW)
    except OSError:
        wecount += 1
        return None
    return ackW  

def letPortsHz():
    writeByte(IOEX0, IODIRA, 0xFF)
    writeByte(IOEX0, IODIRB, 0xFF)
    writeByte(IOEX1, IODIRA, 0xFF)
    writeByte(IOEX1, IODIRB, 0xFF)

def letADRbusOutput():
    writeByte(IOEX1, OLATA, 0xFF) # initial value, A15-A8
    writeByte(IOEX1, OLATB, 0xFF) # initial value, A7-A0
    writeByte(IOEX1, IODIRA, 0x00) # DIR=ouput A15-A8
    writeByte(IOEX1, IODIRB, 0x00) # DIR=ouput AD7-AD0

def letADRbusInput():
    writeByte(IOEX1, IODIRA, 0xFF) # DIR=input A15-A8
    writeByte(IOEX1, IODIRB, 0xFF) # DIR=input AD7-AD0

def outputADRbus(adr):
    adrH = (adr >> 8) & 0xFF
    adrL = adr & 0xFF
    writeByte(IOEX1, OLATA, adrH) # output A15-A8
    writeByte(IOEX1, OLATB, adrL) # output AD7-AD0

def letDATAbusInput():
    writeByte(IOEX1, IODIRB, 0xFF) # AD7-AD0 input

def letDATAbusOutput():
    writeByte(IOEX1, OLATB, 0xFF) # initial value, AD7-AD0
    writeByte(IOEX1, IODIRB, 0x00) # DIR=ouput AD7-AD0

def outputDATAbus(dat):
    dat &= 0xFF
    writeByte(IOEX1, OLATB, dat) # output AD7-AD0

def inputDATAbus():
    d1 = readByte(IOEX1, GPIOB) # input AD7-AD0
    d2 = readByte(IOEX1, GPIOB) # input AD7-AD0
    d3 = readByte(IOEX1, GPIOB) # input AD7-AD0
    while (d1 != d2) or (d2 != d3) or (d3 != d1):
        d1 = d2
        d2 = d3
        d3 = readByte(IOEX1, GPIOB) # input AD7-AD0
    return d1

# B0: ALE, B1: IO/M#, B2:RD#, B3:WR#
# B4: RESET#, B5:HOLD, B6:HLDA, B7:---
def enableCONTSIG():
    writeByte(IOEX0, OLATB, 0x3E) # NO RESET, HOLD
    writeByte(IOEX0, IODIRB, 0xC0) # HLDA, B7: input

def disableCONTSIG(opt):
    if opt:
        writeByte(IOEX0, OLATB, 0x3E) # NO RESET, HOLD
    else:
        writeByte(IOEX0, OLATB, 0x1E) # NO RESET, NO HOLD        
    writeByte(IOEX0, IODIRB, 0xCF) # RESET# HOLD : output

def assertALE():
    writeByte(IOEX0, OLATB, 0x3F) # NO RESET, HOLD, ALE

def assertMEMRD():
    writeByte(IOEX0, OLATB, 0x38) # NO RESET, HOLD, IO/M#, RD#

def assertMEMWR():
    writeByte(IOEX0, OLATB, 0x34) # NO RESET, HOLD, IO/M#, WR#

def assertRST():
    writeByte(IOEX0, OLATB, 0x0E)

def negateCNT():
    writeByte(IOEX0, OLATB, 0x3E) # NO RESET, HOLD

def assertM():
    writeByte(IOEX0, OLATB, 0x3C) # NO RESET, HOLD, IO/M#=MEM

def isHLDA():
    temp = readByte(IOEX0, GPIOB)
    return (temp >> 6) & 1

def initDevices():
    writeByte(IOEX0, IOCONA, 0x20)
    writeByte(IOEX0, IODIRA, 0xFF)
    writeByte(IOEX0, IODIRB, 0xCF)
    writeByte(IOEX0, OLATA,  0xFF)
    writeByte(IOEX0, OLATB,  0x1E)
    writeByte(IOEX1, IOCONA, 0x20)
    writeByte(IOEX1, IODIRA, 0xFF)
    writeByte(IOEX1, IODIRB, 0xFF)
    writeByte(IOEX1, OLATA,  0xFF)
    writeByte(IOEX1, OLATB,  0xFF)

def wrMEM(adr, dat, opt):
    enableCONTSIG()
    while isHLDA() == 0:
        pass
    letADRbusOutput()
    outputADRbus(adr)
    assertALE()
    negateCNT()
    outputDATAbus(dat)
    assertMEMWR()
    negateCNT()   
    letADRbusInput()
    disableCONTSIG(opt)

def rdMEM(adr, opt):
    enableCONTSIG()
    while isHLDA() == 0:
        pass
    letADRbusOutput()
    outputADRbus(adr)
    assertALE()
    negateCNT()
    letDATAbusInput()
    assertMEMRD()
    dat = inputDATAbus()
    negateCNT()   
    disableCONTSIG(opt)
    return dat

def WrRdTest(startADDR, endADDR):
    global wecount, recount
    addr = startADDR
    dat = 0
    while addr < endADDR:
        time.sleep_ms(10)
        wrMEM(addr, dat, True)
        time.sleep_ms(10)
        rddat = rdMEM(addr, True)
        if rddat == dat:
            print("VERIFIED ADDRESS: 0x{0:04x}".format(addr))
        else:
            wecount += 1
            print("ERROR ADDRESS: 0x{0:04x}".format(addr))
        addr += 1
        dat += 1
        if dat > 0xFF:
            dat = 0
    rddat = rdMEM(addr, False)
    print("recount:{0} wecount:{1}".format(recount, wecount))       

def main():
    global i2c
    M5.begin()
    print("SRAM RD/WR test")
    print("IO Expander is ", end="")
    if not isIOEXavailable():
        print("not available.")
    else:
        print("available.")
        initDevices()
        WrRdTest(0x0, 0x50)
 
if __name__ == "__main__":
    main()

初期状態ではお約束の配線エラーなどあり、まともに動かなかったですが、現段階では一応SRAMに読み書き出来ている感じ(うまく行っているところね。)SRAMrdwrTest

しかし、問題は読み書きに失敗することがあることです。ちょっとダメダメすぎる。そして原因は新設のSRAMモジュール部分かと思えば(そこもあるかもしれないけど)、M5Stackに接続しているI2Cバスの通信に信頼性がないことに気づきました。I2Cバスにエラーが起きているのです。

上記の読み書きテストのスクリプトではソフトウエアでかなりお茶を濁してますが、I2C通信に失敗するケースが目立ちます。今回の回路ではM5Stack内部のI2Cバス(3.3V)を引き出してそれをNch MOSFETを介して5V化してIOExpanderに接続しており、そのあたりの信号品質に疑念ありです。ここが立派に動いてくれないことには肝心のホスト接続できんよな。

なかなかすんなり動いてくれんのう。お約束か。

MicroPython的午睡(140)M5Stack、ソフト制御でバスサイクル生成 へ戻る

MicroPython的午睡(142)M5Stack、SoftI2Cで無理やりSRAM初期化 へ進む