キャラクターLCDに何かを表示する

3.3 Vで直接動かせるSC1602BBWB-XA-LB-Gを使う。PIC16F1789/1939 & MPUトレーナー カテゴリーの記事一覧 -の評価基板に載っていたのを外した。8ビットモードで動かす。

下のように接続する。
CPUのOUT[7:0] → LCDのDB[7:0]
CPUのOUT[8] → LCDのRS
CPUのOUT[9] → LCDのE

書き込みしかしないのでLCDのR/!WはGNDに固定する。
f:id:ti-nspire:20210218061311j:plain:w700

############################
# アセンブラ(のようなもの) #
############################

import math

def constrain(val, _max, _min):
    return int(min(max(val, _min), _max))

def generate_mif(file_name, width, depth, rom):
    f = open(file_name, "w")
    f.write("WIDTH=%d;\n" % width)
    f.write("DEPTH=%d;\n" % depth)
    f.write("ADDRESS_RADIX=UNS;\n")
    f.write("DATA_RADIX=BIN;\n")
    f.write("CONTENT BEGIN\n")
    format_of_code = "0" + str(width) + "b"
    for i in range(depth):
        machine_code = format(rom[i], format_of_code)
        f.write("%10d   :   %s;\n" % (i, machine_code))
    f.write("END;\n")
    f.close()

def mov(ra, rb):   return  0 << 11 | ra << 8 | rb << 5
def add(ra, rb):   return  1 << 11 | ra << 8 | rb << 5
def sub(ra, rb):   return  2 << 11 | ra << 8 | rb << 5
def _and(ra, rb):  return  3 << 11 | ra << 8 | rb << 5
def _or(ra, rb):   return  4 << 11 | ra << 8 | rb << 5
def sl(ra):        return  5 << 11 | ra << 8                 # 左シフト
def sr(ra):        return  6 << 11 | ra << 8                 # 右シフト
#def sra(ra):       return  7 << 11 | ra << 8                 # 算術右シフト
def jmp_reg(ra):   return  7 << 11 | ra << 8                 # レジスタに記憶してあるアドレスへジャンプ
def ldl(ra, ival): return  8 << 11 | ra << 8 | (ival & 0xFF) # 下位バイトに即値をロード
def ldh(ra, ival): return  9 << 11 | ra << 8 | (ival & 0xFF) # 上位バイトに即値をロード
def cmp(ra, rb):   return 10 << 11 | ra << 8 | rb << 5       # 一致したらフラグが立つ。
def je(addr):      return 11 << 11 | (addr & 0xFF)           # 一致フラグが立っていたらジャンプ
def jmp(addr):     return 12 << 11 | (addr & 0xFF)           # 無条件ジャンプ
def ld(ra, addr):  return 13 << 11 | ra << 8 | (addr & 0xFF) # RAMからレジスタへ値を持ってくる。
def st(ra, addr):  return 14 << 11 | ra << 8 | (addr & 0xFF) # レジスタの値をRAMへ格納する。
#def hlt():         return 15 << 11
def jnc(addr):     return 15 << 11 | (addr & 0xFF)           # jump if no carry

def nop()  : return 0
def _in(ra): return ld(ra, 65) # INポートからレジスタへ読み込む。
def out(ra): return st(ra, 64) # レジスタからOUTポートへ書き出す。


# レジスタへ2バイト即値をロード(★2命令★)
def ld_hl(ra, two_bytes):
    return ldh(ra, two_bytes >> 8), \
           ldl(ra, two_bytes & 0xFF)


FILE_NAME = "rom_init.mif"
WIDTH = 15
DEPTH = 256
_ = [0] * DEPTH




################################################
########### アセンブリ(のようなもの) ###########
################################################

##################################
# directive、macro(のようなもの) #
##################################

F_CPU = 10E3

DELAY_40ms = 40E-3
DELAY_2ms  = 2E-3
DELAY_50us = 50E-6

# (3命令×4クロック + 2命令×4クロック×(LOOPS定数 + 1) + 1命令×4クロック) / F_CPU = delay
# LOOPS定数 = (delay * F_CPU - 16) / 8 - 1
LOOPS_for_40ms = constrain(math.ceil((DELAY_40ms * F_CPU - 16) / 8 - 1), 2**16-1, 0) # F_CPU = 10E6のとき49997
LOOPS_for_2ms  = constrain(math.ceil((DELAY_2ms  * F_CPU - 16) / 8 - 1), 2**16-1, 0)
LOOPS_for_50us = constrain(math.ceil((DELAY_50us * F_CPU - 16) / 8 - 1), 2**16-1, 0)

FUNC_SET = 0b111000 # 1, 8/4 bits, 2/1 lines, 10/8 dots, don't care, don't care
DISP_CNT = 0b1111   # 1, 文字表示, カーソル表示, カーソル点滅
CLS      = 0b1      # 表示を消去
ENT_MOD  = 0b110    # 1, 右/左シフト, 表示範囲全体シフト有効化

r0, r1, r2, r3, r4, r5, r6, r7 = range(8) # 汎用レジスタ
COUNTER = r0
OUT     = r1
RET_TO  = r2
DEC     = r3

# call命令 ★2命令★
# サブルーチンから戻ってくるアドレスを保存しておいてからサブルーチンへ飛ぶ、
def call(go_to, return_to):
    return ldl(RET_TO, return_to), \
           jmp(go_to)

# ret命令
def ret():
    return jmp_reg(RET_TO)



###############
# subroutines #
###############

# DELAY_LONG:
DELAY_LONG = 230
_[(pc := DELAY_LONG)] = ldl(DEC, 1)
_[(pc := pc+1)], \
_[(pc := pc+1)]       = ld_hl(COUNTER, LOOPS_for_40ms)
_[(pc := pc+1)]       = sub(COUNTER, DEC)
_[(pc := pc+1)]       = jnc(pc)
_[(pc := pc+1)]       = ret()

# COMMAND_W:
COMMAND_W = 215
_[(pc := COMMAND_W)] = out(OUT)
_[(pc := pc+1)]      = ldh(OUT, 0b00)
_[(pc := pc+1)]      = out(OUT)
_[(pc := pc+1)]      = ldh(OUT, 0b10)
_[(pc := pc+1)]      = out(OUT)
_[(pc := pc+1)]      = nop()
_[(pc := pc+1)]      = ldh(OUT, 0b00)
_[(pc := pc+1)]      = out(OUT)
_[(pc := pc+1)]      = ldl(DEC, 1) # ビジーループ
_[(pc := pc+1)], \
_[(pc := pc+1)]      = ld_hl(COUNTER, LOOPS_for_50us)
_[(pc := pc+1)]      = sub(COUNTER, DEC) 
_[(pc := pc+1)]      = jnc(pc)
_[(pc := pc+1)]      = ret()

# DELAY_SHORT:
DELAY_SHORT = 205
_[(pc := DELAY_SHORT)] = ldl(DEC, 1)
_[(pc := pc+1)], \
_[(pc := pc+1)]        = ld_hl(COUNTER, LOOPS_for_2ms)
_[(pc := pc+1)]        = sub(COUNTER, DEC)
_[(pc := pc+1)]        = jnc(pc)
_[(pc := pc+1)]        = ret()

# DATA_W:
DATA_W = 190
_[(pc := DATA_W)] = out(OUT)
_[(pc := pc+1)]   = ldh(OUT, 0b11)
_[(pc := pc+1)]   = out(OUT)
_[(pc := pc+1)]   = nop()
_[(pc := pc+1)]   = ldh(OUT, 0b01)
_[(pc := pc+1)]   = out(OUT)
_[(pc := pc+1)]   = ldl(DEC, 1) # ビジーループ
_[(pc := pc+1)], \
_[(pc := pc+1)]   = ld_hl(COUNTER, LOOPS_for_50us)
_[(pc := pc+1)]   = sub(COUNTER, DEC)
_[(pc := pc+1)]   = jnc(pc)
_[(pc := pc+1)]   = ret()


################
# main routine #
################

ad = -1

# 最初にLCDをdisableにする。
_[(ad := ad+1)] = ldh(OUT, 0b00)
_[(ad := ad+1)] = out(OUT)

# CALL DELAY_40ms
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DELAY_LONG, ad+3)

# Function Set
_[(ad := ad+1)] = ldl(OUT, FUNC_SET)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

# Display ON/OFF Control
_[(ad := ad+1)] = ldl(OUT, DISP_CNT)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

# Clear Display
_[(ad := ad+1)] = ldl(OUT, CLS)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

# CALL DELAY_2ms
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DELAY_SHORT, ad+3)

# Entry Mode Set
_[(ad := ad+1)] = ldl(OUT, ENT_MOD)
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(COMMAND_W, ad+3)

# 1文字ずつ表示する。
_[(ad := ad+1)] = ldl(OUT, ord('h'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('e'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('l'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('l'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('o'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord(','))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord(' '))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('w'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('o'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('r'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('l'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, ad+3)

_[(ad := ad+1)] = ldl(OUT, ord('d'))
_[(ad := ad+1)], \
_[(ad := ad+1)] = call(DATA_W, 255)

_[255] = jmp(255)

generate_mif(FILE_NAME, WIDTH, DEPTH, _)