標準TTLだけ(!)でCPUをつくろう!(組立てキットです!)
(ホントは74HC、CMOSなんだけど…)
[第582回]

●ロジアナで確認、でもその前に

その前に、もう少し補足して説明をさせていただきます。

前回からの続きです。
Z80BASICで簡単なRS232Cの受信プログラムを書いて試してみたところ、受信データがときどき欠落してしまいます。
その欠落したデータをよく見てみますと、見事に規則性があることがわかりました。
Z80BASICのRS232C受信命令はREAD #1,A$,A%という形をしています。
受信したデータは文字変数A$に格納されます。
Z80BASICの文字変数は39バイトの固定長です。
受信データがA$に39バイト格納されるか、または0D0Aコードを受信すると、READ #1命令が完成します。
どうやら、そのときに、データの欠落がおきるようです。

前回の最後にお見せした、テストプログラムの実行結果の一部ですが、確かに文字変数に39バイトのデータが格納されたあとに、データの欠落がおきています。

12345678901234567890ABCDE0,1,2,3,4,5,6,
12345678901234567890ABCDE7,8,9,10,11,12
12345678901234567890ABCDE13,14,15,16,17
12345678901234567890ABCDE18,19,20,21,22
12345678901234567890ABCDE,23,24,25,26,2

12345〜ABCDEはダミーです。
データの欠落の原因を探るために、こんなややこしいことをさせています。
ここでは、12と13の間、それと17と18との間の , が欠落しています。

最初に考えられることは、文字変数に39バイトのデータが格納されてREAD #1命令が完了してリターンしてきたあと、次にふたたびREAD #1命令が実行されるまでの間の時間がかかりすぎて、それでデータが欠落してしまうのではないか、ということです。
しかし、それはちょっと変なのですよねえ。

確かに前回の最初のプログラム(下記)では、READ #1命令が完了するごとに、PRINT文が実行されています。

    10 A%=0
    20 READ #1,A$,A%
    30 IF A%<1 GOTO 20
    40 PRINT A$
    50 GOTO 10

READ #1命令は受信データが無い場合、そこでデータが来るまで待っているのではなくて、すぐにリターンしてきます。
そのときは、受信の状態を示す整数型変数A%に−1が入れられます。
受信データがある場合でも、データの終わりを示す0D0Aコードが来るまでは、−1が示されます。
A$には、そのときまでに格納されたデータが入れられています(このデータはつねに参照可能です)。

0D0Aコードが受信されたときは、A%には受信してA$に入れられた文字数が入ります。
このときは、A%の値=A$の文字バイト数になります。
A$には受信途中のデータが格納されている状態でも参照できますから、そのままではデータの終わりが確認できません。

RS232C通信では一般にデータの終わりには0D0Aコードを送ります。
もし、READ #1,A$,A%の実行後に、PRINT A#を実行したところ、
ABC
と表示された場合、
それだけでは、
[41][42][43][0D][0A]
と送信されてきたのか、それとも
[41][42][43][44]…
と続くデータの途中までを受信しただけなのかがわかりません。ですから後者の場合にはA%=−1にしてリターンするのです。
前者ではA%=3になります。

[注記]41、42、43はそれぞれ文字A、B、CのASCIIコードです。一般にRS232Cでは、データをバイナリコードのままで送らずに、ASCIIコードで送ります。

もし0D0Aコードがまだ受信されていないならば、その続きのデータがあるはずですから、続きのデータを受信しなければなりません。

39バイトよりも大きいデータを受信した場合には、0D0Aコードが来る前に、文字変数A$が一杯になってしまいます。
この場合にはA$のデータを一旦引き取って画面に表示するとか、別の文字変数に移し換えるとかしなければなりません。
ですからこの場合にはA%=40でリターンします。
そのようにしたあとで、A%=0にしてから再びREAD #1の実行に戻ります。

A%=0で READ #1,A$,A% にエントリしたときはA$をクリアしてから受信データを格納します。
A%=−1で READ #1,A$,A% にエントリしたときは現在A$に格納されているデータの後ろに受信データを追記します。
A%がそれ以外の値で READ #1,A$,A% にエントリしたときは、何もしないですぐに戻ってきます。

なんだかめちゃめちゃ面倒なようですが、こうすることで、プログラムが受信待ちでハングアップすることが避けられます。
BASICのプログラムは受信データが無い場合には、受信データがくるのを待っていないで、READ #1文をすぐに抜け出しますから、受信データが無いときには、そのほかの処理をすることができます。

RS232Cの受信は、PIC18F14K50が割り込み処理によって行いますから、せっかくそのような処理をしているのに、もしもBASICプログラムがひたすら受信データを待っていて、その間何もできない、というのではそれこそ、「宝の持ち腐れ」になってしまいます。
ですから受信データが無い場合にはすぐにREAD #1を終了するようにしてあるのです。

ちょっと余談になってしまいますが、せっかくREAD #1の動作の説明をいたしましたので、その実際の動きを見ていただくことにいたしましょう。

    10 PRINT "start"
    20 A%=0
    30 READ #1,A$,A%
    40 PRINT A$,A%
    50 IF A%<1 GOTO 30 ELSE GOTO 20
>r.
start
             -1
             -1
             -1

今回のはじめのところでお見せしたプログラムとは少し、PRINT文の位置が異なっています。
そのプログラムですと、A$に受信データが39バイト格納されるか、または0D0Aコードを受信したときしかPRINT文は実行されません。
その実行結果は前回、[第581回]でお見せしました。
それに対して上のプログラムでは、受信データの有無に関係無くPRINT文が実行されますから、受信の状態をモニタすることができます。

プログラムの実行を開始した直後は、まだ受信データが来ていませんから、A$の値としては何も表示されません。
A%は受信が継続中であることを示す−1になっています。

受信データが入ってきました。

             -1
             -1
 0           -1
 0,          -1
 0,1         -1
 0,1,2       -1
 0,1,2,      -1
 0,1,2,3     -1
 0,1,2,3,    -1
 0,1,2,3,4   -1
 0,1,2,3,4,  -1
 0,1,2,3,4,5,-1
 0,1,2,3,4,5,6            -1
 0,1,2,3,4,5,6,           -1
 0,1,2,3,4,5,6,7,         -1
 0,1,2,3,4,5,6,7,8        -1
 0,1,2,3,4,5,6,7,8,9      -1
 0,1,2,3,4,5,6,7,8,9,     -1
 0,1,2,3,4,5,6,7,8,9,10   -1
 0,1,2,3,4,5,6,7,8,9,10,  -1
 0,1,2,3,4,5,6,7,8,9,10,11-1
 0,1,2,3,4,5,6,7,8,9,10,11,            -1
 0,1,2,3,4,5,6,7,8,9,10,11,12          -1
 0,1,2,3,4,5,6,7,8,9,10,11,12,1        -1
 0,1,2,3,4,5,6,7,8,9,10,11,12,13       -1
 0,1,2,3,4,5,6,7,8,9,10,11,12,13,1     -1
 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,   -1
 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 -1
 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,40
16,          -1
16,1         -1
16,17        -1
16,17,       -1
16,17,1      -1
16,17,18,    -1
16,17,18,1   -1
16,17,18,19  -1
16,17,18,19, -1
16,17,18,19,2-1
16,17,18,19,20            -1
16,17,18,19,20,2          -1
16,17,18,19,20,21         -1

あ。説明するのを忘れていました。
このプログラムのように232C受信と同時にPRINT文を実行するときの問題点として、画面表示が異常に遅いために、受信バッファがオーバーフローしてしまう、ということにつきましては、[第571回]でcprintf()をputc()に変更することでクリアできましたが、それでもさすがに9600ボーでこのプログラムを実行することはできません。
39バイト受信ごとに表示するようにした、はじめのところでお見せしたプログラムでも、やっぱり受信バッファがオーバーフローしてしまいます。

ですから、そのどちらのプログラムでも、実はボーレートを2400ボーにして実行いたしました。

さてそこで、本題に戻ります。
さきほど、文字変数A$に受信データが39バイト入れられてリターンしてくるときに時間がかかりすぎる、と考えるのはちょっとおかしい、と書きました

確かに前回、[第581回]でお見せしました最初のプログラムでは、39文字受信するごとにPRINT文が実行されています。
しかし同じく前回、[第581回]の終わりのところでお見せしたプログラムは、そのあたりのところを考慮して、受信中は別の配列変数にデータを移すだけで、PRINT文は実行しないようにしているのです。

受信を終了してから、プログラムを強制的に終了して、そのあとで、goto110をダイレクトに実行して、データの表示を行っています。
もちろん、A$から配列変数にデータを移すのにも多少の時間はかかりますが、それが問題になるほどかかるとは思えません。

実は結論から先に書きますと、確かにA$に39バイトの受信データが入れられて戻るときのみ、問題が発生する条件が成立していたのです。
しかしそれは、処理に時間がかかりすぎていたためではありませんでした。

●ロジアナで観測してみました

そこで、やっと、ロジアナの登場です。
[第374回]でご紹介いたしました、カメレオンUSB+ロジアナです。


DATA0〜DATA3をPROBEの若い番号から割り当てるつもりがうっかりして逆にしてしまいました。
でもまあどちらでも結果に変わりはありません。
この波形は、今回の説明の始めのところでお見せした受信データの出力表示が行われるのと同時に、つまりその受信データが作成されるのと同時に、ロジアナで記録されたものです。

もういちどその受信データをお見せします。

12345678901234567890ABCDE0,1,2,3,4,5,6,
12345678901234567890ABCDE7,8,9,10,11,12
12345678901234567890ABCDE13,14,15,16,17
12345678901234567890ABCDE18,19,20,21,22
12345678901234567890ABCDE,23,24,25,26,2

12345〜ABCDEはダミーです。
その後ろに受信データがあります。
データの欠落は12と13の間と、17と18との間で発生しています。
どちらも , が落ちています。
上のロジアナの波形は、その前後の波形の解析から、16,17と続くデータの、 ,1の部分をPIC18F14K50がZ80CPUに渡しているところだということがわかっています。

[2C]は , の、[31]は 1 のASCIIコードです。
最初に下位4ビットを、次に上位4ビットを渡しています。
実は、ここで、
あれえ?
というおかしなところがあることに気がつきました。
そこが、やっぱりおかしかったのですが、それについてはまたあとで説明いたします。

そして、これが問題のデータ欠落部分です。



[37]は 7 のASCIIコードです。
16,17,18
と続く部分の、7, のところです。
上でも説明しましたが、[2C]は、 , のASCIIコードです。

コード[2C]はちゃんとPIC18F14K50からZ80CPU側に渡されています。
Z80BUSY信号も2回出ていますから、Z80側も確かに、[2C]を受け取っているはずなのですが…。
実はここで、とんでもないことがおきていたのです。

上から続く、[2C]、[31]、[37]の受け渡しの波形と、最後の[2C]の波形とをよーく見ていただきますと、そのとんでもないことが、見えてくるのでありますが…。
おわかりいただけますでしょうか?

というところで、本日も時間がなくなってしまいました。
この続きは、次回にさせていただくことにいたします。
2010.8.13upload

前へ
次へ
ホームページトップへ戻る