4004でシリアル通信するためのプログラムを書いてみました。いわゆるソフトウェアUARTというやつです。GitHubに下記の前例があったので参考にしましたが、基本的にプログラム自体はゼロから書きました。
参考にしたのは、開発環境(The Macroassembler AS)、PRINTルーチンと文字列データは256byte単位の同一ページ内に置く、putcharではstart bitとstop bit をキャリーに入れておくとシンプルになるというあたりです。前例では4800bpsがmaxのようでしたが、今回9600bpsまで実現することができ、NoritakeのVFD(CU22042)でそのまま表示させことができました。
まとまったらGitHubに載せる予定ですが、とりあえず今回書いたコードをここに載せます。
ハードウェアは入力が4004のTEST端子、出力は4002の出力ポート(作業用に4bit全部使っていて、LSBが実際の出力)です。前回の記事に書いたように、フォトカプラでレベル変換しています。
まずはGETCHARです。やってることはコメントに拙い英語で書いた通りですが、ポイントをまとめると下記の通り。
・4004の実行速度は10.8003857us/cycle(5.185MHzの水晶使用)
・9600bpsは9.645cycle/bit
・start bitをポーリングで検出すると0~2cycleの遅れになるんだけど、さらにちょっと(1~4サイクル)遅らせたあたりでbitの0/1判定すれば良さそう。
・bit0~bit3, bit4~bit7を9cycle間隔、bit3~bit4を12cycle、bit7~stop bitを10cycle ぐらいにしておけばbitの有効領域で判定できそう。
次にPUTCHARです。基本的にGETCHARと同じ考え方です。
正確には9.645cycle/bitという半端なタイミングで出力する必要があるのですが、前半を9cycle/bit、後半を10cycle/bitで出力すれば許容範囲だよねという考え方です。
試したところちゃんと動いてくれました。ソフトウェアUARTでこれ以上速くするのは無理そうかな。
参考にしたのは、開発環境(The Macroassembler AS)、PRINTルーチンと文字列データは256byte単位の同一ページ内に置く、putcharではstart bitとstop bit をキャリーに入れておくとシンプルになるというあたりです。前例では4800bpsがmaxのようでしたが、今回9600bpsまで実現することができ、NoritakeのVFD(CU22042)でそのまま表示させことができました。
まとまったらGitHubに載せる予定ですが、とりあえず今回書いたコードをここに載せます。
ハードウェアは入力が4004のTEST端子、出力は4002の出力ポート(作業用に4bit全部使っていて、LSBが実際の出力)です。前回の記事に書いたように、フォトカプラでレベル変換しています。
まずはGETCHARです。やってることはコメントに拙い英語で書いた通りですが、ポイントをまとめると下記の通り。
・4004の実行速度は10.8003857us/cycle(5.185MHzの水晶使用)
・9600bpsは9.645cycle/bit
・start bitをポーリングで検出すると0~2cycleの遅れになるんだけど、さらにちょっと(1~4サイクル)遅らせたあたりでbitの0/1判定すれば良さそう。
・bit0~bit3, bit4~bit7を9cycle間隔、bit3~bit4を12cycle、bit7~stop bitを10cycle ぐらいにしておけばbitの有効領域で判定できそう。
;;; functuon for setting counter for ISZ loop
loop function x, (16-(x))
;;;---------------------------------------------------------------------------
;;; GETCHAR
;;; receive a character from serial port (TEST) and put into P1(R2, R3)
;;; baud rate: 9600bps (104.17us/bit, 9.645cycle/bit)
;;;
;;; Input: none
;;; Output: P1(R2,R3), ACC=0(OK), ACC=1(error)
;;; Working: P6, P7
;;; This subroutine destroys P6, P7.
;;;---------------------------------------------------------------------------
;;;
;;; |--12--|-9--|-9-|-9--|-12--|-9--|-9-|-9--|-10--|
;;; ~~~~~~~~|____|~~~~|____|~~~~|____|~~~~|____|~~~~|____|~~~~~ 9.645cycle/bit
;;; ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
;;; start 0 1 2 3 4 5 6 7 stop
;;; |->phase delay
;;; - In order to check data bits in the middle of the signal,
;;; a "phase delay" should be added between the start bit and data bits.
;;; (1 to 4 cycles may be moderate for 9.645cycle/bit)
;;; - Detection of the start bit may cause delay of 2 cycles due to polling.
;;;---------------------------------------------------------------------------
GETCHAR:
FIM R12R13, loop(4) ; loop for first(lower) 4 bit
;
JCN TN, $ ;(2) wait for start bit (TEST="0")
FIM P7, loop(4) ;(2)
ISZ R15,$ ;(8) 12 cycles between startbit and bit0
; phase(bit0)= 12 -9.645 = 2.355cycle
GETCHAR_L1:
JCN TN, GETCHAR_L2 ;(2) check a bit
CLC ;<1> TEST="0" then CY=0
JUN GETCHAR_L3 ;<2>
GETCHAR_L2:
STC ;[1] TEST="1" then CY=1
NOP ;[1]
NOP ;[1]
GETCHAR_L3:
RAR ;(1) load CY->ACC
NOP ;(1) 9cycle/bit (error=-0.645 cycle/bit)
ISZ R13, GETCHAR_L1 ;(2) repeat until 4 bit received
; phase(here)= 2.355 -0.645*3 = 0.42cycle
XCH R3 ;(1)
FIM R12R13, loop(4) ;(2) loop for second(upper) 4 bit
; 12 cycles between bit3 and bit4
; phase(bit4)= 2.42 +12 -9.645 = 2.775cycle
GETCHAR_L4:
JCN TN, GETCHAR_L5 ;(2) check a bit
CLC ;<1> TEST="0" then CY=0
JUN GETCHAR_L6 ;<2>
GETCHAR_L5:
STC ;[1] TEST="1" then CY=1
NOP ;[1]
NOP ;[1]
GETCHAR_L6:
RAR ;(1) load CY->ACC
NOP ;(1) 9cycle/bit (error=-0.645 cycle/bit)
ISZ R13, GETCHAR_L4 ;(2) repeat until 4 bit received
; phase(here)= 4.755 -0.645*3 = 0.84 cycle
XCH R2 ;(1)
; 10 cycles/between bit7 and stopbit
; phase(stop)= 2.84 +10 -9.645 = 1.195cycle
;; check stop bit
JCN TN, GETCHAR_OK ; stop bit == "1"
BBL 1 ; stop bit != "1"
GETCHAR_OK:
BBL 0
次にPUTCHARです。基本的にGETCHARと同じ考え方です。
正確には9.645cycle/bitという半端なタイミングで出力する必要があるのですが、前半を9cycle/bit、後半を10cycle/bitで出力すれば許容範囲だよねという考え方です。
;;;---------------------------------------------------------------------------
;;; PUTCHAR
;;; send the character in P1(R2, R3) to OUTPORT
;;; baud rate: 9600bps (104.17us/bit, 9.645cycle/bit)
;;;
;;; Input: P1(R2,R3)
;;; Output: ACC=0
;;; Working: P6(R12R13), P7
;;; This subroutine destroys P6, P7.
;;;---------------------------------------------------------------------------
;;;
;;; |--9-|-9--|-9-|-9--|-10--|-10-|-10-|-10-|-10--|(ave.9.56cycle/bit)
;;; ~~~~~~~~|____|~~~~|____|~~~~|____|~~~~|____|~~~~|____|~~~~~ 9.645cycle/bit
;;; ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
;;; start 0 1 2 3 4 5 6 7 stop
;;;---------------------------------------------------------------------------
PUTCHAR:
LDM BANK_SERIAL ; bank of output port
FIM P7, CHIP_SERIAL ; chip# of output port
DCL ; set port bank
SRC P7 ; set port address
FIM R12R13, loop(5) ; start bit and lower 4bit(R3)
LD R3
CLC ; start bit is 0
RAL
PUTCHAR_L1:
NOP ;(1) 9cycle/bit
NOP ;(1)
NOP ;(1)
NOP ;(1)
NOP ;(1)
WMP ;(1)
RAR ;(1)
ISZ R13, PUTCHAR_L1 ;(2)
FIM R12R13, loop(5) ;(2) upper 4bit(R2) and stop bit
LD R2 ;(1)
STC ;(1) stop bit is 1
NOP ;(1) timing adjustment
NOP ;(1) 10cycle between bit3 and bit4
PUTCHAR_L2:
WMP ;(1) 10cycle/bit
FIM R14R15, loop(2) ;(2)
ISZ R15, $ ;(4)
RAR ;(1)
ISZ R13, PUTCHAR_L2 ;(2)
BBL 0
試したところちゃんと動いてくれました。ソフトウェアUARTでこれ以上速くするのは無理そうかな。
※コメント投稿者のブログIDはブログ作成者のみに通知されます