ワンチップマイコン使用のG3RUH互換モデムRSV96解説書・改訂版 |
G3RUHモデムの簡略化から始まった、このプロジェクトですが、皆様のご協力を得て、なんとか復調率も十分に実用的なレベルまで来ましたし、プログラムも安定してきました。
RSV96ではプログラムが命なのですがCQ誌(1993年3〜5月号)の記事ではプログラムの具体的な解説は不向きと思われ割合されましたが、今回は具体的なプログラムを揚げ解説していきます。ちなみに、紹介したソースは解説を分かりやすくするために若干変更されています。単純に繋ぎ合わせても動作はしませんのであしからず。
今回使用した、80C31は、インテル社の8051シリーズの1チップマイコンです。このシリーズの特徴は、CPU内部に、ROMが4Kバイト(80C31には無い)、RAMが128バイト、パラレル入出力が最大8ビット×4組、非同期用シリアル入出力、タイマ、カウンタ、等が入っています。
8051シリーズは、内部にROMを持つ8751と、ROMが無い8031が有ります。ROMを持つ8751ですと外付けROM回路が無用になるので大変コンパクトに仕上ります。反面、CPUの値段も高く(秋葉原で4000−10000円)、ROMの焼き込みも安価なROMライタでは出来ずに手が出し難くなっています。また、8031は、外付けROMが必要になり少々回路が複雑になりますが、CPUの値段も安く(1000円未満)、ROMには通常の物が使えて開発がしやすいのが長所です。
今回は、回路は少々複雑になりますが、安くて開発のしやすい80C31(8031のC−MOS版)を使いました。現時点では、回路的にもプログラム的にも安定してきたので、今後は87C51を使用して超小型の9600bpsモデムを作れればと、考えています。
別に、この80C31で無くとも、ポピュラーなZ80等の汎用CPUでもモデムは作れそうですが、ROM、RAM、I/Oポートを外付けしなくてはならず回路規模がTNC本体のように少々複雑になってしまいます。また、8051シリーズは1チップCPUの中では高速な部類に入ります。9600bpsの信号を処理するためには、かなり高速なCPUでないと耐えられません。Z80に例えると8〜10MHzのクロックに匹敵するほどです。
RSV96では、G3RUHモデムのアナログ部分を除く全てをCPUで処理させています。動作原理は基本的にG3RUHモデムと同様ですから、G3RUHモデムと同様の性能が得られる事になります。参考迄にRSV96のハードウエアのブロック図(図1)を書いておきました。
●受信部のしくみ
受信部分はアナログ(LPF)部分以外の全てはCPUがやってしまいます。G3RUHモデムでは、PLLや、逆スクランブル回路が有りましたが、これらの仕事は全てCPU内部で行われています。
アナログ(LPF)部分は、G3RUHモデムと全く同等の回路構成・定数設定にしてあります。G3RUHモデムとの比較の為にわざと同じ回路にしてみました。
RSV96では、プログラムの都合でモデムのクロックをCPU内部で生成します。その為に、9600bps専用(CPUのクロックが12MHzの場合)となっています。TNCから与えている9600×16(Hz)のクロックは見ていません。
●送信部のしくみ
送信される波形はアナログです。使用したCPUに(80C31)にはDAC(デジタル→アナログ・コンバータ)が内蔵されていませんので外付けする必要がありました。それ以外は受信同様に極めてシンプルな構成になっています。
送信の場合は受信と違って、TNC(SIOのTXDA)から出力される信号の位相をモデム側で把握しておかないと、うまく送信されませんので、TNCから出力されるクロック(9600×16Hz)もモデム用CPUへ入力する必要があります。そのかわり、このクロックを可変することでモデムのボーレートを変化させる事も可能になります。ただし、CPUの速度的な問題で80C31(12MHz)では9600bpsが限界です。
80C31のI/Oポートは、オープンドレイン+内部プルアップ抵抗で出来ています。この為にI/Oポートから電流を流すと電圧降下が起きるので、R−2R等の抵抗型のDACを直接付けると出力の保証がされません。そこで、74HC541というバッファー用のICが付いています。
送信用のLPFも受信同様の理由で、G3RUHモデムと同じ回路を使いました。
さて、肝心のソフトウエア部分の解説です。解説の都合上ソースリストからの抜粋ですが、G3RUHモデムを完全に理解していて、8051のアセンブラをマスターしていれば、これだけでも何をしているか理解出来ると思いますし、自力で完動するソースリストを作成する事も可能でしょう。解説用にプログラムが何をしているか簡単なフローチャート(図2)にまとめてみました。
●受信部のしくみ
G3RUHモデムの信号を受信するための要は、受信信号に含まれたクロックを取出す事です。G3RUHモデムではハードウエアによりPLLで高分解能(0.4μ秒)を得ていましたが、そのままプログラム化するには速度的に難しい物がありますので、独自に考えて作りました。
※「クロック検出」部分の解説
モデムが持つクロックと、受信した信号が持つクロックとの位相が、遅れているか、進んでいるかを調べる部分です。1ビット(104μ秒)をウインドウ1と、ウインドウ2に時間的に分けて、2つのウインドウの間に、受信した信号のクロックの変化点が来るように、モデムのクロックの位相を変化(1ビット分の時間を変化させる)させます。
タイムチャートを書くと(図3)のような感じになります。ウインドウの間に受信した信号が変化(L→H、H→L)の有無で、次の「位相調節」を調節しています。
ウインドウ1で変化が有った場合は、内部クロックが、受信信号に対して短すぎるわけで、逆にウインドウ2で変化が有った場合は、長すぎるわけです。両方のウインドウに変化が無い場合もありうるので、この時は現在の位相を保持します。
スクランブル処理部分などのクロック検出用ウインドウの無い部分では、信号に変化が有っても検出されません。G3RUHモデムでは終止信号変化を見ていますので、動作としては違う物です。メリットとしては「ノイズに強くなる」のですが、反面「信号を補足しにくくなる場合がある」という欠点ももっています。使用した結果でも、復調率がG3RUHモデムを越える場合と、悪くなる場合があるようで一長一短です。
;*** WINDOW - 1 ウインド1の中でデータに変化が有ったか?
mov R4,P3 受信データ保存 (Bit.2)
call wait 待機 27uS
mov a,P3 受信データ取得 (Bit.2)
xrl a,R4 直前に取得したデータと保存したデータを比較
anl a,#100b 有効なデータは(Bit.2)だけにする
mov R5,a 変化が有ったかどうか保存する
;*** WINDOW - 2 ウインド2の中でデータに変化が有ったか?
mov R4,P3 受信データ保存 (Bit.2)
call wait 待機 27uS
mov a,P3 受信データ取得 (Bit.2)
xrl a,R4 直前に取得したデータと保存したデータを比較
anl a,#100b 有効なデータは(Bit.2)だけにする
mov R6,a 変化が有ったかどうか保存する
※「位相調節」部分の解説
9600bpsでは、1ビットあたり104μ秒の周期でデータが伝送されます。ただ、この時間(位相)はモデムの個体差で微妙に違うので、受信している信号に合わせなくてはなりません。
「クロック検出」で得られた情報を元に、実際にモデムのクロックを一時的に変化させて位相を調整します。時間(位相)調整は、ハードウエア・タイマを使わずソフトウエアでタイマを使い、微妙な時間可変をしてみましたが、80C31(12メガ)の場合は最短命令が1μ秒なので、その分解能はG3RUHモデムに比べて2.5倍粗くなります。
ソフトウエア・タイマを使った為に、ハードウエアでのボーレートの変更が出来ません。また、CPUのクロック(12MHz)に依存してしまうので、これの誤差が±1%を越えると復調率に影響してしまいます。
mov a,R5 ウインド1の変化状況
jz WAIT1 変化無し
mov a,R6 ウインド2の変化状況
jnz WAIT2 変化あり WIN1=1 WIN2=1
sjmp WAIT3 変化無し WIN1=1 WIN2=0
WAIT1 mov a,R6 ウインド2の変化状況
jz WAIT2 変化無し WIN1=0 WIN2=0
sjmp WAIT4 変化有り WIN1=0 WIN2=1
WAIT2 sjmp WAIT5 時間調整用のダミー
WAIT4 nop 位相(時間)の調整はnop(1μ秒)
WAIT5 nop の実行でやっている
WAIT3 nop
※位相調整の詳細
| ウインド1 | ウインド2 | 時間調整 | 備考 |
| 変化無し | 変化無し | 0 | クロック情報が得られないので変化させない |
| 変化無し | 変化有り | +1 | ループの時間を延ばして位相調整 |
| 変化有り | 変化無し | -1 | ループの時間を縮めて位相調整 |
| 変化有り | 変化有り | 0 | 有り得ないので変化無し |
※「逆スクランブル」部分の解説
G3RUHモデムには、送出信号のスペクトラムを拡散させて、エラーレートを減らすために「スクランブル(暗号化)」が掛かっています。
受信した信号のスクランブルを外すのもプログラムで行っています。プログラム的には実行時間を極力減らすために技巧を凝らした物になっていますが、それでも「位相調節」と合わせて40μ秒程度も掛かっています。また、ビット演算が可能な80C31ですが、ビット間でのEXORが出来ない為に8ビットレジスタを使っています。
mov a,RXSR2 #2のBit3のEXORを取る
swap a 処理の都合でBit3→Bit7へ移動
xrl a,RXDAT 入力データとEXOR
xrl a,RXSR3 #3とEXOR
mov c,ACC.7 Bit7だけ抜き取る
cpl c 反転
mov P3.1,c データ出力
mov a,RXDAT 入力データ取得
rlc a 入力データをC(CarryFlag)へ移す
mov a,RXSR1 #1の内容を取得
rlc a シフトする
mov RXSR1,a #1の内容を保存
mov a,RXSR2 #2の内容を取得
mov RXSR3,a #3はBit7だけ有効
rlc a シフトする
mov RXSR2,a #2の内容を保存
mov c,P3.2 受信データ取得
mov RXDAT.7,a 受信データをBit7に保存
●送信部のしくみ
G3RUHモデムの送信部分も極めて簡単な構造になっています。これは、G3RUHモデムの最大の特徴でもある、ROMを使ったF.I.R.フィルタを使っているためです。これをプログラム化する場合も、G3RUH(James
Miller氏)が設計したROMデータを使う事で極めてシンプルなプログラムになっています。
プログラムは、「波形出力関連」と「データ処理関連」のプログラムが同時に別々に動いています。普段は別々に動かして、必要な時のみ双方の処理のタイミングを合わせることで、処理時間の短縮を計っています。
※「送信クロック」部分の解説
TNCから出してくる送信信号と間違い無く同期させるため、TNCから出るクロック(9600×16Hz)を取込んでいます。そのクロックをCPU内部のタイマ機能(ハードウエア)により1/4に分周して、波形出力用割込み信号として使っています。
※「波形出力」部分の解説
G3RUHモデムのROMには、G3RUHモデムが出す波形のデータが入っています。このデータは1ビットにつき4パターン用意されていますので、1ビット(104μ秒)に4つの波形パターンを出力しなくてはなりません。この波形出力に「送信クロック」の部分で得られた割込み信号を使って処理させています。
TNCから得たクロック(9600×16Hz)を1/4に分周すると、わずか26μ秒となり、この間に波形をROMから取出し、CPU外部に出力するのはなかなか大変です。送信部分は全体的に時間との戦いになり、良く考えてプログラムしても9600bpsが限度となってしまいました。また、送信しながら受信出来ない(半二重動作)になってしまった理由も此処にあります。
TXout 波形出力割り込み毎に実行される
movx a,@DPTR 波形データの読みだし
mov P1,a 波形データ出力
inc DPH 次の波形のデータ・アドレスの用意
reti
※「スクランブル」部分の解説
データ処理は、波形出力関連のプログラムとは別に動いています。ただし、データをTNCから取込む時に、「送信クロック」の割込み信号と同期させています。
スクランブルのプログラムは、受信用の「逆スクランブル」と似たような構成になっています。送信部の為の高速プログラミングの為に、受信部の「逆スクランブル」も結果的に早くなったというのが実状です。
mov c,P3.0 送信データ入力
loop mov a,DPH
cjne a,WAVEH,loop TNCのCLOCKと同期を取る
mov TXDAT.3,c 入力データをBit3に保存
mov a,TXSR2 #2のBit3のEXORを取る
xrl a,TXSR3 #3とEXOR
xrl a,TXDAT 入力データとEXOR
mov c,ACC.3 Bit3だけ取り出しC.Flagへ移す
mov a,TXSR1 #1取得
rlc a シフトする
mov TXSR1,a #1保存
mov a,TXSR2 #2取得
rlc a シフトする
mov TXSR2,a #2保存
mov TXSR3.3,c C.Flagを#3へ保存 Bit3のみ使用
※「ROMアドレス計算」部分の解説
「波形出力」の為にF.I.R.のデータ格納アドレスの計算をします。このアドレス計算が極めて高速に行えるように、データを本来の順番と変更してあります。
mov DPL,TXSR1 #1の内容でROMの下位決定
mov DPH,R1 ROMの上位は波形で変更可能
送信波形のパターンは、8KバイトのROMを使用した関係で7種類しか入りません。波形の選択は、CPUに余っている入力ポートが無いために、DAC部分のパラレルポートと共用して、電源入力時(リセット時)だけDIPスイッチを確認するように出来ています。
以上、80C31を使ったRSV96の解説でした。図はCQ誌からの取り込みです(^^;) 1993年のハムフェアーで配布する予定だった資料ですが、動作解説には余り興味を持たれなかったようなので、配布しないままで終わりました(^^;) 今回のNeko2で使ったPICの場合もほぼ同様の処理を行っています。リクエストが有りましたらPIC版を執筆しますのでご連絡ください。(でも、内容変わらないかも?)