現在地

ソフトウェアUART


カテゴリー:

前々回前回書いたような厄介な点はありましたが、CDP1802ボードUniversal Monitorを動かすのは思っていたより簡単でD(ump), G(o), S(et), L(oad)の各コマンドは動作するようになりました。

そうなると今度はSBC1802でも動かしてみたくなります。

ところがこれにはUART相当のハードウェアは無く、CPU内蔵のQ, EF3をシリアルポートとして使うようになっています。UARTの機能をソフトウェアで実現しなくてはなりません。

これはシリアルクロックの話の送受信処理をソフトウェアで行なうということです。処理自体は難しくありませんが、1ビット・1.5ビットの時間は正確でなくてはなりません。

ボーレートはMCBASIC3が9600bpsで動作していることと最近作ったボードはどれも9600bpsを標準にしていることから9600bpsで検討することにします。

まず1ビットの時間は 1 / 9600 [s] ≒ 104.16 [μs] となります。

SBC1802にはタイマは無い(仮にあっても時間が短すぎて使えないでしょう)ので命令の実行時間を利用するしかありません。

COSMACの命令は一部の3バイト命令が3サイクルかかる以外は2サイクルです。1サイクルは8クロックです。

1サイクルの時間CPUクロックからは 1 / (1.79 [MHz]) × 8 ≒ 4.47 [μs] となります。

これらから1ビットは23サイクル、1.5ビットは35サイクルとなります。誤差は1.3%ほどなのでギリギリですね。

また約10命令で1ビットの処理をしなくてはならないのでこちらもギリギリです。パラメータを渡してボーレートを切り替える余地はなさそうです。

送信の方が簡単なのでまず送信から見ていきましょう。

(1)   62/     736 :                     CONOUT:				; Cyc
(1)   63/     736 : 7A                  	REQ			;    : TxD H=>L (Start bit)
(1)   64/     737 :                     
(1)   65/     737 : 88                  	GLO	8		;  2
(1)   66/     738 : B7                  	PHI	7		;  2
(1)   67/     739 : F8 08               	LDI	8		;  2 : 8bit
(1)   68/     73B : A7                  	PLO	7		;  2
(1)   69/     73C :                     
(1)   70/     73C : C8 07 3C            	NLBR	$		;  3
(1)   71/     73F : 38 3F               	NBR	$		;  2
(1)   72/     741 :                     CO0:
(1)   73/     741 : 97                  	GHI	7		;  2
(1)   74/     742 : F6                  	SHR			;  2
(1)   75/     743 : B7                  	PHI	7		;  2
(1)   76/     744 : 33 49               	BDF	CO1		;  2
(1)   77/     746 : 7A                  	REQ			;  2
(1)   78/     747 : 30 4C               	BR	CO2		;  2
(1)   79/     749 :                     CO1:
(1)   80/     749 : 7B                  	SEQ
(1)   81/     74A : 30 4C               	BR	CO2
(1)   82/     74C :                     CO2:
(1)   83/     74C : 38 4C               	NBR	$		;  2
(1)   84/     74E : C8 07 4E            	NLBR	$		;  3
(1)   85/     751 :                     
(1)   86/     751 : 27                  	DEC	7		;  2
(1)   87/     752 : 87                  	GLO	7		;  2
(1)   88/     753 : 3A 41               	BNZ	CO0		;  2
(1)   89/     755 :                     
(1)   90/     755 : C8 07 55            	NLBR	$		;  3
(1)   91/     758 : C8 07 58            	NLBR	$		;  3
(1)   92/     75B : 7B                  	SEQ			;  2 : TxD =>H (Stop bit)
(1)   93/     75C :                     
(1)   94/     75C : C8 07 5C            	NLBR	$		;  3
(1)   95/     75F : C8 07 5F            	NLBR	$		;  3
(1)   96/     762 :                     
(1)   97/     762 : D5                  	SEP	RETN

まず63行でQを下げます。これがスタートビットの開始です。本来は前のストップビットの開始から1ビット以上の時間が経過しているか確認しなくてはなりませんが、本ルーチンを呼び出すためのCALL, RETN処理だけで23サイクル以上消費するので連続して呼んでもストップビット長が不足する心配はありません。

73~75行で1ビット取り出して、77行か80行でQに出力します。リストの右に2とか3と書いてあるのが命令のサイクル数で、66行からここまで23サイクルになるように70,71行で調整しています。

オペランドの「$」は命令のアドレスです。どうせ絶対にブランチしない命令なので何を書いても良いのですが、最初「0」にしたところNBR命令がページ外でエラーになったのでこうしています。

81行も無駄に見えますが、0のときと1のときでサイクルが同じになるように調整しています。

72~88行を8回ループすることで8ビット分出力しますが、これも一周が23サイクルになるように83,84行で調整しています。

ループを抜けたあと92行でQを上げてストップビットとします。ここも77,88行からここまで23サイクルになるように90,91行を挿入して調整しています。

上で1ビットは23サイクルと書きましたが、より正確には約23.3サイクルです。今回は23サイクルで1ビット読むのを8回ループにしていますが、23サイクルで1ビットと24サイクルで1ビットを読むのを4回ループするようにすれば平均23.5サイクルになり23.3サイクルに近づきます。各ビットのタイミングのずれは問題ではなく、ずれが蓄積することが問題なのです。これはソフトウェアならではの方法ですね。

続いて受信の処理です。

(1)   23/     70C :                     CONIN:				; Cyc
(1)   24/     70C : 3E 0C               	BN3	CONIN		;    : Wait falling edge of /EF3
(1)   25/     70E :                     
(1)   26/     70E : F8 03               	LDI	3		;  2
(1)   27/     710 : A7                  	PLO	7		;  2
(1)   28/     711 :                     CI0:
(1)   29/     711 : 27                  	DEC	7		;  2
(1)   30/     712 : 87                  	GLO	7		;  2
(1)   31/     713 : 3A 11               	BNZ	CI0		;  2
(1)   32/     715 : C8 07 15            	NLBR	$		;  3
(1)   33/     718 :                     
(1)   34/     718 : F8 08               	LDI	8		;  2 : 8 bit
(1)   35/     71A : A7                  	PLO	7		;  2
(1)   36/     71B :                     CI1:
(1)   37/     71B : 88                  	GLO	8		;  2
(1)   38/     71C : F6                  	SHR			;  2
(1)   39/     71D : 36 23               	B3	CI2		;  2
(1)   40/     71F : F9 80               	ORI	80H		;  2
(1)   41/     721 : 30 27               	BR	CI3		;  2
(1)   42/     723 :                     CI2:
(1)   43/     723 : 38 23               	NBR	$		;  2
(1)   44/     725 : 30 27               	BR	CI3		;  2
(1)   45/     727 :                     CI3:
(1)   46/     727 : A8                  	PLO	8		;  2
(1)   47/     728 :                     
(1)   48/     728 : 38 28               	NBR	$		;  2
(1)   49/     72A : C8 07 2A            	NLBR	$		;  3
(1)   50/     72D :                     	
(1)   51/     72D : 27                  	DEC	7		;  2
(1)   52/     72E : 87                  	GLO	7		;  2
(1)   53/     72F : 3A 1B               	BNZ	CI1		;  2
(1)   54/     731 :                     
(1)   55/     731 : D5                  	SEP	RETN

まず24行でEF3が下がるのを待ちます。これがスタートビットの始まりです。

35サイクル後に39行で最初のビットを判定してR(8)の上位から入れていきます。これは右にシフトされていくので8ビット読み終わった時には最下位のビット0になります。

これを8回繰り返して1キャラクタ8ビットの受信が完了します。このループも周期が23サイクルになるよう調整しています。

ストップビットのチェックはしていません。どうせフレーミングエラーになっても無視するだけですから。むしろストップビットの確認をすると次のスタートビットまでの余裕が不足してきます。

実はソフトウェアUARTを書くのは久しぶりなのですが、最初送信処理のサイクル数を数え間違えていて文字化けしオシロスコープのお世話になった以外はすんなり動作しました。

最後に制約というか注意点を一つ。

バックグラウンドでの受信はできないのでCONSTルーチンは使えず、メモリダンプを途中で中断することはできません。またHEXファイルの送信などを行なう場合はキャラクタごとに少し間隔をあけるなどしないと取りこぼす可能性があるので要注意です。

参考文献・関連図書: 
CDP1802A,CDP1802AC,CDP1802BCデータシート, Intersil.

コメントを追加

Plain text

  • HTMLタグは利用できません。
  • ウェブページアドレスとメールアドレスは、自動的にハイパーリンクに変換されます。
  • 行と段落は自動的に折り返されます。
※ コメントは原則公開です。個別のご相談などは「ご意見・ご要望」からお願いします。