USIを使ったバッファー付きUARTライブラリーの公開
受信もUSIで動かそうとしてはまる。簡単には動かない(4/2/2014)
AVRマイコンTinyシリーズ特有のUSI(汎用シリアルインターフェース)を使ったUART関数の開発に難儀している。このほど送信部が完成した。これで同時処理の中でCPUに負担をかけないUARTの送信が可能になった。次は受信部である。
これまでに開発したソフトUARTの受信部はピンチェンジ割り込みなので非同期にデータを読めるがGPIOでビットを拾っているので、その間は割り込みも受けない。受信は、送信と違って、データを読み終わって始めて次の処理に移るものなので、送信ほど同時処理にこだわる必要はない。しかし、送信部だけUSIというのもバランスが悪い。乗りかかった舟でもある。受信もUSIを使って作り直すことにした。
この前に懲りて、受信は最初からAtmelのソースコード(AV307)を見せてもらった。いやあ相変わらずえらい複雑なことをやっているなあ。そもそも変数名が長すぎて、英語を母国語にしない人間にとっては判じ物を見るようだ。ふーむ、ピンチェンジ割り込みは汎用のI/Oピンのときだけ有効で、USIに入った途端、止まるようだ。
これはかえって都合が良い。ピンチェンジ割り込みの受信中に更なる割り込みを止めておく必要がない。USIによる受信は、送信と違って8ビットのシフトレジスターを一巡させるだけでデータが読めるはずだ。
ただ、ボーレートタイマーの割り込みが送信と共用になるのでその仕分けをする必要がある。Atmelのソースと同様、受信フラグを新設する。たいした手間も取らずにUSIを使った受信部のコード開発が終わった。早速テストに入る。全く動かない。まあ、こういうのには慣れている。ロジアナを接続して動きを調べ始めた。
うーむ、どうも挙動がおかしい。USIの入力ピンにすればピンチェンジ割り込みは止まるようなことがAtmelの資料には書いてあるが、どうもUSIになったあとも起きている。ロジアナには、はっきり入力ピンの立下りの度に、割り込みルーチンに飛び込んでいる波形が記録されている。
ふー、受信部がやっと出来た(4/4/2014 23:45)
Atmelの資料では、割り込まれないはずなのだが、現実のロジアナの結果はそういうことになっている。このアプリケーションノートは、10年以上も前の2002年の作成である。Tiny26をターゲットとしている。
どうも、この石は、Tiny26から、2,4,861シリーズに上がった時、ピンチェンジ割り込み近辺が大幅な仕様変更を受けている(各ピン毎の割り込みが可能になった)。アプリケーションノートの記述はあてにならない。ロジアナの波形の実際のピンの動きの方が頼りになる。
それなら、いちいちピンチェンジ割り込みを止めるのではなく、最初の立ち下がり(スタートビット)を検知したら、先ほどの受信フラグを上げ、このフラグが上がっている間はピンチェンジ割り込みを無視するようにすれば所定の動きになる。早速ロジックを加える。
ありゃあ、今度は、8ビット読んだ後のストッパーが効かない。受信ルーチンで暴走する。割り込みプログラムで外部変数の動きを待っているのだが、いつまでたっても終了条件が真にならない。暫くして気がついた。
あ、そうだ。割り込みプログラムがさらに割り込まれることは通常有り得ない。ここは割り込み禁止になっている。なんとお馬鹿なことだ。試しにここで割り込み解除(sei()など)してみると無事それらしく動いた。
しかし、これは多重割り込みで、この程度のプログラムでこれを使うのは少し気が重い。そこで、 Atmelのアプリケーションノートにならって、4ビットカウンターの割り込みを使って多重割り込みを避けることにした。
送信部もタイマーにいれていたステートマシンの部分を、このカウンターオーバーフロー割り込みを使って作り変える。心配していたが、プログラムはかえってこちらの方が読みやすくなった。変更も少ない。
勇躍テストに入る。まだ動かない。しかし大分まともな波形がロジアナにでてきた。ロジアナの波形を見ながら、ちまちまと直していく。そのうち、正しい文字が出てくるようになった。1秒ごとのレスポンスUART出力が重なって見にくいので、これを押さえる。よーし、上手く読んでいるようだ。
雨の京都の枝垂桜が美しい(3/30/2014)
たまには電子工作以外の話題をご紹介しておこう。何年か前にも書いた京都の小学校の同窓会の話である。この間は大津の石山寺だったが、今度は京都御苑の中の宮家別邸が会場だ。これまで音信不通だった同級生の一人が何十年ぶりかで来るよという誘いにのり、また京都まで足を延ばした。
会場となった別邸は、旧九条家が茶会に使っていた拾翠(しょうすい)亭で、桂離宮などと同じように申し込めば誰でも利用できるそうだ。御苑の西南部に位置する。小ぶりだが舟遊びが出来るくらいの池があり、藤棚の船溜まりがある。東山を借景とした庭が見事である。
ここを貸し切って優雅に懐石弁当を賞味した。生憎の雨だったが、お庭の緑は雨の方が映える。ただ酒類の持ち込みは禁止されているのが少し物足らない(男共は辛抱しきれず夕刻、二次会に走った)。
ほぼ50年ぶりに参加した同級生とも再会でき、今回も有意義な同級会だった。今年は男が少し増えて6名。在学時50名程度のクラスで、70を越した男女が20人近く集まれるということは幸せなことだ。
ただし、みんなの話題が介護の苦労の話ばかりで少々閉口した。景気づけに自分のスピーチの時は「私は2039年のケネディ暗殺の調査公開を見るまで死ねない」と宣言した。生きていれば90才なかばである。
桜の時期で、一次会のあとは雨の上がった御苑内の桜見物も楽しんだ。なかでも「出水(でみず)の枝垂れ」で有名な枝垂桜が、ちょうど盛りで(3/30)、実に優雅で見ごたえのある花の姿を見せてくれた。
今回も日帰りである。おみやげに錦(にしき)市場の三木鶏卵の出し巻きを買って帰る。ここの卵焼きはちょっと他では味わえない微妙な味付けと風味で是非お勧めである。
バッファー付き送信関数の開発でまたつまずく(4/6/2014)
電子工作に戻ろう。UARTは送受信ともUSIになったのだが、今ひとつ課題が残っている。送信関数が1バイトの送信が終わるまで元に戻ってこないことだ。このあいだは割り込み可能なので、同時処理も出来るのだが、デバッグやモニターなどでなるべく外乱を少なくしたい時には大きな障害になる。
これは、送信関数にデータバッファーを取り入れれば解決する。ユーザーの1バイト送信関数は、このバッファーに書き込むだけで、実際の送信は、割り込みルーチンでUSIを使って行う。今後のためにも、バッファー付き送信関数は用意しておきたい。それにそんなに難しくない。気楽に着手した。
しかし、これが予想以上に難航したのである。送ったデータがどうもオーバーラン(用意したバッファー以上にデータを書きこんでいる)しているのは、まあ良いとしても、途中から暴走する。
全く同じルーチンで、最初は良くても、暫く時間を空けて送信すると、つまり、一度バッファーをすべて読み出して空にすると、次は、全くデータを拾わなくなる。ボーレートタイマーが止まらず暴走している。
他の処理との干渉を疑うが、関係するようなところは何もない。何度もメモに、最初と2番目からの条件の差を列挙して、一つづつつぶしていくが、違いを見出すことができない。
遂に解決。単に設定の順序を換えただけ。やはりUSIは難しい(4/7/2014)
ところが、やることがなくなって、USI周りのコマンドの順序を何気なく換えていたところ、突然、正常に戻った。何だあ、これは。やったことは、4ビットカウンターのカウント値を設定するUSISR(USIステイタスレジスター)と、USIそのものの全体の構成を決めるレジスターUSICR(USI制御レジスター)の設定順を逆さまにしただけである。
普通なら、まず構成を決めるUSICRを実行して、3線動作、クロック元、割り込み許可などを設定し、そのあと、各種のフラグをクリアし、4ビットカウンターの初期値を決めるUSISRを実行するのが筋だと思う。
ところが最初は問題ないのだが、次からはこの順番だと暴走する。つまりUSICRの設定を一番最後にやる必要がある。恐らく、ペンディングになっている割り込みのフラグをクリアしていないで、動作指定をいきなりやると前の条件で動き出すということなのかもしれない。
何のことはない順番を換えただけで直った。釈然としない解決だが、少なくとも暴走は止まった。このUSISRは、頭4ビットが、各種の割り込みのリクエストをクリアするフラグで、下4ビットがカウンターになっているという、どうも変則的なレジスターなのだが、こんなところに落とし穴があるとは気がつかなかった。
まだ、バッファーのハンドリングがうまく行っていないので完全とはいえないが、とりあえずこれでバッファー付きの送受信が出来るUARTライブラリが出来たようである。ここ1週間これにかかりきりだった。
バッファーオーバーフローも何ともみっともない解決(4/8/2014)
残っていたトラブル、バッファーオーバーフローだが、簡単に原因がわかるだろうと甘く見ていたが、これも一筋縄ではいかなかった。
リングバッファーの終端と、バッファー満杯で待つ場所を一緒にしたせいなのだろうか、リングバッファーが始点に戻るところに、送信の割り込みがぶつかり、データポインターが乱されているような感じだ。
ポインターをいじるコードを、割り込みを止めるcliとseiで、はさむが好転しない。かえって時間遅れが出て、時々字が乱れる。何べんもロジックを確認するが間違っていない。万策が尽きて、やりたくはなかったのだが、試しにバッファーの満杯を待つ地点を最終端から前にずらしてみた。
何と、これだけで、ぴったり直ってしまった。どれだけ長いメッセージを持ってきても問題なく出力する。何だ、何だ。不愉快な解決だなあ。やっぱり割り込みでポインターがずれてしまうのか。あれこれ可能性を考える。
とりあえずは直ったが、バッファー最終端と、満杯になるポイントは一定ではない。出力を繰り返していれば、いつかはまたぶつかる。なんだ解決になっていないじゃないか。当面は動いているが、爆弾を抱えているようなものだ。気分は晴れない。
しかし、これも暫くして(風呂に入っているとき)、原因が判明した。わかってしまえば、至極当たり前のことに、この2日間悩んでいた自分があほらしくなる。何のことはない、書き込みは一度に2バイトなのに、1バイト減ったところで書き込み許可を与えていたのである。やれやれ何とお馬鹿な。
さあ、これでもう大丈夫とメッセージを大量に出し、全く問題なく表示されるのを、にんまりと眺めていて、また落とし穴に落ちてしまった。今度は、これまでうまくいっていた受信が出来なくなったのである。いやいや、次から次に大変である。
最後の字化け。受信もフレーム2つに分ける必要があった(4/10/2014)
正しく文字は入っているのだが、エコーバックの送信が乱れる。ロジアナの波形に首を傾げる。ロジアナの送信波形には送信直前にグリッチが入っているのが見える。受信の直後、必ず送信ラインが数μs分0になってしまう。これではエコーバックのとき予定より早めにスタートビットが始まって誤動作する。
良く見るとグリッチと同期しているのは受信の時のUSIDRの読み出しで送信関数ではない。エコーバックの時だけ乱れるので、タイマーで遅れを作ればいいのだろうが、こういう対症療法は一番やりたくない。
そのうち閃いた。もしかすると受信のシフトレジスターが8ビット動いたとき、バッファーが空になるので送信ラインが0になってしまうのだろうか。ということは8ビットレジスターでデータを受け取ると、必ず0になってしまうのか。送信ラインと同じように2回にわたって読み取る必要があるのか。
物は試しと、受信の時、適当なデータをあらかじめUSIDRに入れ、受信してみた。ビンゴ!であった。ロジアナには、入れたデータと全く同じデータが送信ラインに出た。USIのシフトレジスターは8ビットを読み切れば、必ず出力が0になってしまうのだ。
原因がわかれば、解決は早い。送信と同じように、受信も2回にわけてデータを読み取り、USIDRには1のデータを残すようなコードを加えた。いやあ、USIは難しい。
やった、やった。ロジアナには綺麗な波形が出て、コンソール上も全く字化けがなくなった。いやはや今度もドラマの連続だったなあ。
ソースコードと回路図の公開(4/11/2014)
余り利用する人はいないだろうけれど、せっかくここまで苦労して開発したコードだ。ライブラリーとして公開することにした。フラッシュも少し喰うし(200バイトほど多い)、新たに8ビットタイマーのリソースを必要とするので使う機会は少ないかもしれないが、非同期処理などでCPUを持っていかれたくないときのUARTとして使ってもらえば嬉しい。
ボーレートの設定が少し厄介かもしれない。クロックによって、ボーレートの下限や上限が出てくる。場合によっては、プリスケールを変更する必要がある。Atmelのソースコードには、#defineを使ったタイマーの設定が出来ているが、こういうのってバグが出ることが多いので、ここではやめた(単にさぼっているだけです)。
回路図とメインプログラムのusi861.cは、単にこのUSI-UARTの動作確認だけのテスト用プログラムである。まあ、16ビットのPWMをやりたい人には参考になるかもしれない。
なお、このUSI-UARTは半二重である。全二重にするには完全なハードウエアがない限りもともと無理である。この原型となったソフトUARTもGPIOというCPUリソースを共用しているので勿論半二重である。
ここにいつものようにAVRStudioのフォルダーにしたソースコード一式をzipファイルにしておきます。
| 固定リンク
「AVR」カテゴリの記事
- ソフトI2Cはクロックストレッチまで手を出してあえなく沈没(2017.09.02)
- オシロのテストどころかソフト開発で大はまり(2017.07.26)
- 超音波方式の人感センサーI2C化と新しいオシロ(2017.06.29)
- motionの動体検知はRaspi3の電源が安定しない(2017.04.16)
- 赤外線学習リモコンはデータ再現で挫折したまま進まず(2016.07.21)
コメント