前回、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デコーダー)です。
冒頭の画像に前回回路に接続した回路全体の回路図を掲げました。現物は配線のたくってます。こんな感じ。
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に読み書き出来ている感じ(うまく行っているところね。)
しかし、問題は読み書きに失敗することがあることです。ちょっとダメダメすぎる。そして原因は新設のSRAMモジュール部分かと思えば(そこもあるかもしれないけど)、M5Stackに接続しているI2Cバスの通信に信頼性がないことに気づきました。I2Cバスにエラーが起きているのです。
上記の読み書きテストのスクリプトではソフトウエアでかなりお茶を濁してますが、I2C通信に失敗するケースが目立ちます。今回の回路ではM5Stack内部のI2Cバス(3.3V)を引き出してそれをNch MOSFETを介して5V化してIOExpanderに接続しており、そのあたりの信号品質に疑念ありです。ここが立派に動いてくれないことには肝心のホスト接続できんよな。
なかなかすんなり動いてくれんのう。お約束か。