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

Joseph Halfmoon

前回は古いハードを掘り起こし、M5StackのI2Cバス(3.3V)の先に5Vの外部電源動作のIOExpander MCP23017を2個接続OKなことまで確認しました。今回はMCP23017を制御して古代の8ビット8085風味のバスサイクルを作り出してみます。MicroPython制御のDMAコントローラ一丁あがり?

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

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

今回動作確認の実機回路図

前回、回路図を掲げましたが、今回は「DMAC単体」での動作確認ということもありプルアップ抵抗など追加したので回路図を再掲載いたします。こんな感じ。M5Stack8085SoftDMA_schematic

8085風のバスサイクルを作りだすMicroPythonコード

とりあえず8085の「メモリリード」と「メモリライト」サイクルのみかけられるようにMicroPythonをプログラムしてみましたぞ。遥か以前にZ80に接続しているROMをダンプするのに使ったコードをかなり流用してます。

# softDMA8085.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

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
    bufW = bytes([regADR])
    ackW = i2c.writeto(i2cADR, bufW)
    bufR = bytearray(1)
    ackR = i2c.readfrom_into(i2cADR, bufR)
    return int.from_bytes(bufR, 'little')  

def writeByte(i2cADR, regADR, dat):
    global i2c
    bufW = bytes([regADR, dat])
    ackW = i2c.writeto(i2cADR, bufW)
    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():
    return readByte(IOEX1, GPIOB) # input AD7-AD0

# 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():
    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 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):
    enableCONTSIG()
    while isHLDA() == 0:
        pass
    letADRbusOutput()
    outputADRbus(adr)
    assertALE()
    negateCNT()
    outputDATAbus(dat)
    assertMEMWR()
    negateCNT()   
    letADRbusInput()
    disableCONTSIG()

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

def main():
    global i2c
    M5.begin()
    print("8085 SoftDMA")
    print("IO Expander is ", end="")
    if not isIOEXavailable():
        print("not available.")
    else:
        print("available.")
        initDevices()
        addrW = 0x12
        datW  = 0xA5
        addrR = 0x13
        while True:
            wrMEM(addrW, datW)
            rdMEM(addrR)
            time.sleep(1)
 
if __name__ == "__main__":
    main()
テストプログラムで生成したバスサイクルを観察

上記はダラダラとライトとリードを繰り返すので、その様子をAnalog Discovery2のロジアナ機能で観察してみました。メンドイのでアドレス・データバスの下4ビットしか見てません。M5Stack8085SoftDMA_busCycleEC

左の赤い方がライト・サイクルで、右の青い方がリード・サイクルです。

次は8085のバスに接続可能なSRAMモジュールをこしらえて、上記のバスサイクルで読み書きだな。さっさとやれよ。

MicroPython的午睡(139)M5Stack、MCP23017 2個接続、異電源 へ戻る

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