2009年4月10日金曜日

Thin Fax Line (Olimexプリント基板発注記2)

2月19日 19:00。ツベタンくんから新しい発注書をもらったので、これに必要事項を記入します。記入は発注書PDFをドロー系ソフトに取り込んで、上からテキストを貼り付けました。フォントもタイプライターフォントを使って、いかにもという感じです。決済カードにJCBが使えるってのはよほど日本人客が多いのでしょうか。


一番下のシグネチャーは手書きで書いて、さあ、FAXです。FAX…国際FAXか!そんなのやったことないぞ。大慌てで国際FAXの仕方を調べて、部屋のFAXから送信!
…プルプル…
お、かかった。向こうのベルが鳴ってる。
…ブッ、ピーガーピーガー…
お、ネゴシエーション始めた。行ける!
…ギョワー…
行ってる行ってる!これで安心。数秒後…
…(シ~ン)…
…ピー。グガガガ(紙の排出される音)。「通信エラー」
うっそぉ~。途中で通信がぶった切られました。なんでだろう?回線状況がたまたま悪かったのだろう。では、気を取り直してもう一度……全く同じ現象が。困った。ブルガリアまでって通信料金が高い。アメリカに送るのとはわけが違うのです。何度もトライはできない。何しろ回線接続まではできちゃうわけだからその分料金はかかる。コンビニで送るっていう手もあるらしいけど、なんだか癪だし、めんどくさいし、きちんと送れる保証はないし。

で、思いついたのが、SkypeでFAXできないだろうかということ。Skypeの"SkypeOut"サービスはヨーロッパにあるVoIPゲートウェイから電話回線につなぐことができるから、回線品質は日本から直接つなげるよりもきっとずっといいはずだ。ただし、SkypeOutは安いながらも有料で、「Skypeクレジット」という仮想チケットを前払いで購入しておかなければならない。最低料金は750円ですが、今後も何回かOlimexには発注するかもしれないし、長距離電話をSkypeからかけてもいいし、無駄にはならないと思いました。

Skype用のアドオン(Skype用語で"Extra"という)で、"PamFax"というものがすぐに見つかりました。インストールするとプリンタとして登録され、どのソフトウェアからでもPamFaxに向かって印刷をするだけというものです。料金はページごとの課金でSkypeクレジットからの引き落としです。うまく送れた場合のみ課金されるはずで、こちらのリスクは低いと思われます。…が本当にうまくできるかどうか怪しい…。心配しつつもトライすると…

2月19日 21:30。送信成功です。苦闘2時間半。とりあえずPamFax上では送信完了となりました。送信完了通知をメールで受け取ることもでき、そちらもきちんと通知が来ました。
Hazieさん、.
TSVETAN/OLIMEX (+35932621270) 様宛のPamFaxが送信されました。
ポータル画面でFAXの履歴を見ることができます。
PamFaxをご利用いただきありがとうございます。
PamFax Service より
やった、届いた。届いたはず…だよね?Skypeクレジットは119円だけ減っておりました。これ、たぶん安いよね。ブルガリアだもんね。普通に送ったら大変なことになるよね。(後日、立て続けに2回失敗した国際回線での送信で320円かかったことが判明しました。失敗しただけで1回160円だもんね。成功してても500円超えてたかもなあ。今回はこれでよしと。)

2月20日 9:30。ツベタンくんに何か反応してもらいたいので確認のメール。12時間前にFAXしたけど届いてる?
Dear Tsvetan,

I placed the order through fax about 12 hours ago.
Did you receive it? If you didn't do so and inform about it,
I'm ready to re-send it again.
…待てど暮らせど返事が来ない。その12時間後、来ました。
fax received
そ、そんだけかよ?1行だけ?文にもなってないよ?返事が遅くなってすみませんとかもなしかよ。ちょっと憤りを覚えつつも一安心。まあ、忙しいんだろうけどさ。分かってあげようか。FAXが届く=注文完了なので、製造が始まるわけですが、一番安いコースの"3-5 working days"なので1週間くらいは見ておきましょう。

3月3日 2:00。キター。ツベタンくんからメール!FAX入れてから12日だ。
shipped
また1行野郎だ、コノヤロー。でも出荷されたのね。よかったよかった。しかし、時差を考慮しても6 working daysかかっとるな。まあ、仕事じゃないから我慢しよう。その分安いんだから。

さて、無事にツベタンくんの手を離れて、ブツは国際エアメールのパフォーマンスにゆだねられました。いつ届くのか?こいつも怪しい。

3月16日…ポストイン。待ちましたよー、待ちました。首がこんなに長くなっちゃったよー。13日かかった。ほんとにエア?なのか?1週間、長くて10日くらいに想像してたのに甘かった。で、記念撮影。


中身は3枚まとめてシュリンクラップしてありました。そうか、金フラッシュ仕上げだったのか。半田レベラーにしてもっと安くなんないかな…。


ということで、めでたく完成!いきなりジャンパ入ってますが…。


※PamFaxのインストールと使い方が分かりやすく載ってるページがこちらにありましたので紹介しておきます。

2009年4月9日木曜日

よし、ブルガリアにしよう。(Olimexプリント基板発注記1)

前から気になっていました。東欧のある国には激安でプリント基板を作ってくれる業者があるのです。その名はOlimex(たぶんそのまま「オリメックス」と読むのでしょう)。所在地はブルガリア…ブ、ブルガリア?どこ?ヨーグルトの国としか認識していませんでした。それでもトラ技に紹介されていたり、検索すれば多くの日本人がトライしていたり、もうずいぶん有名になりました。「TinyでDRO!」の基板はXYZ軸で3枚いるし、手張りだとLEDの配線が手間だし、密度も高くないし、ここのところ空前の円高ユーロ安だし、今まさにブルガリアを試してみるチャンス。Eagle(回路CAD)のファイルがそのまま使えるのもうれしい。今回はそのOlimexにDROの基板を発注して届くまでの顛末を書こうと思います。

発注前。EagleでDROのパターン設計を終える。今回はOlimex言うところの"DSS"サイズ、すなわち両面板のユーロカードサイズ160mm×100mmにジャスト3枚面付け(同じパターンを1枚の基板に割りつけること。英語で"panelize")することにしました。Olimexは既定のカードサイズ以内なら無料で面付けして、希望によりカット("de-panelize")もしてくれるのです。私なりに要点をまとめたので紹介します。
注文を意識した設計のTIPS

[1] 基板外形サイズを160mm×100mm以内に入れる。外形線は10milの幅で引き、面付けする場合には切り代を10milとる。
[2] OLIMEX用DRCを走らせる。DRCエラーを修正する。
[3] 非標準ドリルを抽出する。0_OLIMEX_DRILL.ulpを実行。現在プロジェクトの直下にできるOLIMEX_DRILL.scrを実行。
[4] ドリル径をOLIMEX仕様に標準化する。
ライブラリに標準ドリル版のパッケージを追加する。0_OLIMEX_LIB.ulpを実行。変更を受けるライブラリを指定。現在プロジェクト直下にできるOLIMEXLIB.scrを実行。ライブラリを保存。基板エディタでライブラリをアップデート。ドリル変更をするすべてのパーツでパッケージ変更(change package)。
[5] シルク幅の変更。OlimexAllSilk.ulpを実行。すべて10mil幅になる。
[6] ドリル穴数のカウント。drillinfo.ulpまたはcount.ulp(面付け後500穴を超えると割増料金)
[7] 半田マスク層(tStop, bStop)を含めてOLIMEXが使うすべての層に任意の文字列を入れる。基板外形の外でよい。(これを自動化するスクリプトがあるらしいが、当時存在を知らなくて使わなかった。)
[8] .brdファイルとREADME.TXTファイルをZIP圧縮する。
[9] ZIPファイルを添付して注文メールを送る。データの出し直しは3ユーロのペナルティを取られる。

2月18日。上記[8]のZIPファイルを添付して、最初のメールを送る。
From: hazie@mydomain.jp
To: fastpcb@olimex.com
Subject: PCB ORDER
---
Dear

I would like to order PCB with attached files.

Regards,

Hazie
これだけ。本当にこんなんで大丈夫か?かなり心配する。
すると、発信からたった45分で返信が来た。
From: "Olimex Ltd Support"
To: hazie@mydomain.jp
Subject: Re: PCB ORDER
---
Hi
attached is your po form
Best regards
Tsvetan / Olimex
ををっ!早い!担当者は"Tsvetan"…「ツベタン」ですね。かの有名な「ツベタン」くんとはキミか。発注書が添付されているので中を見ると…

45.5ユーロね。ん?こちらの見積りよりちょっと高いな。あれ?"Non Standard drill sizes 4pce Euro 4.00"って何?非標準のドリルが4種類もあるって。実は、この時点ではドリル径の標準化を知らずに発注してしまったのでした。うーん、このまま発注すれば4ドルのプラス、修正して出し直せば3ドルのプラス。これは、修正したほうがわずかに安く済むな。もしかしたら、初めてってことで無償で対応してくれたりして…と淡い期待をいだきつつ、ネット情報で確認しつつ修正です。

2月19日、(こちらの)朝一。修正しますとツベタンに一報。
Dear Tsvetan,

I have to say I'm sorry that I attached wrong data.
I didn't check enough for your standard drill sizes.
The data standardized will be made and I will place another order.
ごめんね、チェック不足で間違っちゃったから、すぐに直すねって、かわいく聞こえただろうか?んなわけないなぁ…。

その日の昼前、11時ごろ、修正済みファイルを添付して送信。
I would like to order PCB with attached files.
Please cancel previous order PO#xxxxxxx and send another PO form.
今度は4時間半後。ツベタンから返事が。
Hi
please note in this case file re-processing charge EUR 3.00 will be add to your order
Thanks
Tsvetan
ううぅ、許してくれなかった…無慈悲にも追加料金が。まあ、こちらが悪いのであきらめて…
Dear Tsvetan,

Oh, I didn't know it. But I agree re-processing the data with the extra-charge EUR 3.00. Please send another PO.

I will be more careful next time. Thanks.
即、受け入れることにしました。今読むとここまでへりくだる必要はないかも。

その3時間後、例のそっけない文面で新しい発注書が届いた。
Hi
attached is your po form
Best regards
キミは無口だなあ。ま、"Best regards"がついているだけいいか。ちなみに、修正したデータの場合は注文番号に「X」がつくようです。なんか落第点とったみたいでへこむなあ。でもきっちり1ユーロ安くなりました。

さあ、ここからまた一騒動ありました。つづきは次回へ。


2009年4月2日木曜日

タイミングチャートの作図ツール

この手のブログで避けて通れないのがタイミングチャートによる説明です。クロックなどの繰り返し波形あり、波形のエッジの前後関係の厳密さあり、論理反転した波形ありでソフトウェアツールによる作図が最適と思えます。

PowerPointとかVisioを駆使して作図するのも疲れるので、巷の人たちはどのようなツールを使っていらっしゃるのだろうと調べてみましたが、あまり決定番ってないんですね。Excelのマクロなども含め、フリーのツールもいくつか(国産2つ、海外産2つ)試してみましたが、どうもぴったりしたのがない。結局は等幅フォントを使ったプレーンテキストで描くのが一番早くて楽だったり、何しろ何でも描けて自由ですから。で、このブログではいまだにテキストで描いたものを張り付けている次第なんですが…。

今回は試したフリーのツールの中で一番よくできていたものを紹介します。"TimingTool"というそのものずばりの名称で、"MOHC Ltd."という---おそらく英国の会社・詳細不明---の製品が商品化されており、その試用版"TimingTool Lite"がそれです。リンクはここから。とりあえずスクリーンショットを見てみましょう。


なかなか本格的な面構え。製品版とほとんど同じなので当たり前ですが。今回のDROのタイミングチャートを書くのに使おうと思って、途中まで入力してみたものです。どんな特徴があって製品版と何が違うか。
  • Java-Appletでオンラインで動くのでダウンロード、インストール不要。とりあえず動かすのにはユーザ登録も不要。
  • ユーザ登録をすれば専用のワークスペースを先方のサーバー上に作ってくれ、データをワークスペース上に保存しておける。
  • 試用期間に制限はない。(この製品が存続する限り?)
  • 製品版ではタイミング解析とかその他の高級な機能(HDLツールやマクロ)が使えるが、これは使えない。それ以外の機能は"lite"でも変わらない(らしい)。
  • デジタルの波形のみ。アナログ波形は不可。
  • 波形の形状がわりときれい(主観です)。
  • 印刷出力もしくはエクスポート出力には必ず"TimingTool"のロゴが入る(製品版は不明)。下は印刷プレビューのショット。



  • エクスポートのフォーマットは↓こんな感じ。ただし、長ーい周期をズームアウトして1枚にまとめたりするとき波形がつぶれます。そのつぶれ方がちょっと変。なるべく波形をつぶさない大きさで出力するべし。これは印刷も同じ。
  • コメントやテキストの入力では、下付き文字、上付き文字、オーバーバーなど文字の装飾がいろいろ選べるが、日本語は入らない。化けるんじゃなくて、そもそも入力できない。
  • UIがかなり特殊な部類。エッジの左をクリックしたときと右をクリックしたときで挙動を変えるとか。右をちょっと長めにクリックしっぱなしにすると別のメニューが出るとか。
  • Javaベースなので動きはちょっと遅め。サーバーも重め。
上にも書きましたがUIがちょっと特殊なので、波形のコピーとかやり方をマスターするのに苦労しました。ディレイやセットアップ/ホールドなどのパラメータをきっちり入れることもできますが、今回はそこまで試しませんでした。でも、使いこなせばかなり高度なことができそうなので、試す価値はあると思います。(なにしろタダですから!)

なお、日本では一般に「タイミング図」や「タイミングチャート」などと言いますが、ググった感じ、英語では"Timing Diagram"というほうが通りがいいようです。検索の参考までに。

2009年4月1日水曜日

TinyでDRO!(ソフトウェア2)

データの頭出しは、データ送出の時間間隔が約0.3秒開いているという仕様に頼ります。0.3秒と仕様にしてはやや甘いので、実際のプログラムでは「0.25秒以上開いたら」という条件に置き換えます。

このタイミングはTimer1を使って計ります。システムクロックを256分周した周波数でカウントアップします。カウントアップの最大値が0.25秒で訪れるように比較一致レジスタOCR1Aを設定し、最大値に達したら自動ゼロクリアするようにCTC動作にします。

下図のタイミングチャートでは、データビットの送出タイミングに合わせてTimer1のカウント値がどのように変化するかを示しています。「絶対座標のデータ」または「相対座標のデータ」の下の部分を見て分かる通り、データビットを取り込むタイミング(INT1のハンドラ内)でTimer1をクリアしています(図の「エッジ割り込みでクリア」と書いてある部分)。0.25秒未満の短い間隔では決して最大値には達しません。

ところが、データを送出し終わってしばらくデータが来ないとTimer1を強制クリアする機会がなく、そのまま増加し続け、0.25秒経過の後ついには最大値=比較一致レジスタの値に達します(図の「比較一致」)。つまり、この時点でデータの区切りが訪れたと認識するのです。プログラムでは比較一致割り込みを起こさせるようにしてあり、そのハンドラの中でバッファポインタを先頭に持ってくる、つまりデータの頭出しをすることになります。以下は再び"interrupt.c"の該当部分です。(行間が空いているのはCPUタイプによるIF-DEFを消して見やすくしたためです。)

// スケールのデータ送信間隔を測る



ISR(TIMER1_COMPA_vect)

{
Rbufp = Rbuf; /* バッファポインタを先頭へ */
}

2009年3月27日金曜日

TinyでDRO!(ソフトウェア1)

ソフトウェアの説明です。例によってSourceForgeで公開します。


ヒューズの設定はZIPファイルの中のFuseMapping.jpgにAVRStudioの(中のAVRProgの)設定画面のスクリーンショットを入れてありますので参考にしてください。また、ソースコード中にもコメントで書き込んであります。要は内蔵RC発振器をONにして、1/8分周をOFFにするということです。

今回のプログラムの要点は、スケールから送られてくるデータをクロックに同期して取り込むこと、そして直列で送られてくるデータを切り出し---つまり頭出し---することです。

まずマクロな視点から。データブロックは約0.3秒の間隔でスケールから出力されています。


クロック(CLK)は出っぱなしではなく、データ(DATA)が送られるときのみ出ているのが分かります。クロックが出ていないときは論理"0"(-1.5V電位)にスタックしています。データブロックの部分をクローズアップしてみます。


クロックはデータ出力開始直前に"1"(GND電位)となり、しばらくしてからクロッキングを始めます。クロック周期は約12usですので、周波数にすると約83kHzです。

データは2種類送られてきます。先に絶対座標のデータ、後に相対座標のデータです。絶対座標はそのスケール固有の位置情報で、原点が絶対にずれることはありません。相対座標はスケールのゼロクリアボタンを押すごとに原点を設定するもので、通常はこちらが使われることになります。

これらのデータは24ビット長で、クロックパルスに同期して順次最下位ビットから送り出されます。図でA0~A23が絶対座標、R0~R23が相対座標です。R24(ダミー)と書いてあるビットがありますが、これは本来は取り込まないビットで、後ほど説明します。

図で分かる通りクロックの立下りエッジでデータを取り込むとよさそうな感じです。実際のプログラムでは、AVRに入ってくるときにレベルシフタにより論理反転していますので、クロックの立ち上がりエッジで捉えることにしています。ここでファイル"interrupt.c"の該当部分。(コメント部分で改行されてしまい読みにくいのでコメントをちょっといじってあります。)

// スケールからのデータ取り込み。検出エッジは立上がりエッジ。
ISR(INT1_vect)
{
*Rbufp++ = SCALE_PIN; /* 読んでバッファにためる */
if (Rbufp >= Rbuf + sizeof(Rbuf)) {/*バッファ端*/
Rbufp = Rbuf + sizeof(Rbuf) - 1;
Updated = 1; /* main()に更新を通知 */
}
TCNT1 = 0; /* 送信間隔タイマーをクリア */
}
INT1は立ち上がりエッジを検出するように設定されています。重要なことは割り込みセンスから割り込みプログラムが起動されるまでの時間がかなりかかることです。あまり時間がかかるとデータを読みそこなってしまいます。割り込み処理のシーケンスは、
  1. INT1エッジ認識
  2. 割り込みハンドラに分岐
  3. 使用するレジスタ群を退避
  4. ユーザの定義した割り込み処理プログラムを実行
上記の1.から2.までで約4クロックかそれ以上かかり、これはAVRハードウェアによるオーバーヘッドですので回避不能です。3.はコンパイラの出力するコードによるオーバーヘッドが含まれ、アセンブラで書かない限りは制御はやっかいです。コンパイラのアセンブラ出力を見るとここに16クロックかかっていることが分かります。そこでこの割り込み処理では、スケールのDATAがつながっている入力ポート(上記リストの"SCALE_PIN"=PIND)を何はともあれ読み込んで、バッファにためることだけを行っています(52行目)。その際にそのビットを抽出するなど余計なことはやっていません。ちょっとでも複雑なことをしようとすると、コンパイラはためらうことなくたくさんのレジスタを使おうとして、それらを退避するコードを割り込み処理の冒頭に突っ込んでくるからです。

今回データ読み込みはすべてクロックの立ち上がりエッジ(タイミング図上では立下り)で検出しているので、前出のタイミング図では、データ送出終わった後にクロックが"0"スタックする時点でもうひとつ検出エッジが来てしまいます。これが「R24(ダミー)」と書いてあるゆえんです。プログラムではこの部分も愚直に読み込んでしまうことで、割り込みプログラム中の無用な条件判定を回避しています。読み込んだとしても値を集計するときに使わなければいいのです。

2009年3月25日水曜日

TinyでDRO!(ハードウェア2)

このプロジェクトで何が一番難しかったかって、デジタルスケールにどうやってケーブルを接続するか考え出すことでした。プラグインできる汎用のコネクタなどありませんし、かといってケーブルをじかに出すのは気が引けるし。逡巡したあげく結局どこかのサイトで見た、コネクタヘッダをはんだ付けする方法を取りました。出来上がりはこんな風になります。



スケールを完全に分解する改造です。メーカー保証はあきらめましょう。分解して基板をむき出しにしたら基板用コネクタヘッダをはんだ付けします。基板エッジにでている接点パターンは、きちんと計測すると1.65mmピッチくらいだということが分かるのですが、ここでは2.0mmピッチのこれを使いました。4ピンなのでぎりぎりこれでも大丈夫です。ただ、このコネクターは非常に熱に弱くて少しでも余分に長くこてを当てようものならコネクタハウジングが融けてピンが泳ぎだすのが難点です。少々高くても他のメーカーのものを使うのが賢明でしょう。なお、分解しないで無理やりはんだ付けする方法もありますが、スケールのケースが溶けるのでお勧めはできません。後述のコネクタカバーが閉まらなくなってしまいます。



組み立てる前にスケールのプラスチックカバーの加工をします。下の写真の緑の四角で囲んである部分を削り落します。こうしないとコネクタヘッダの下半分が干渉してしまうからです。



組み立てた後はこうなります。


このままだとコネクタカバーが閉まりませんのでこいつも加工します。左下から11.0mm×2.5mmだけ削り込んでやると…

下側の「ツメ」がぎりぎり0.5mmほど残ります。このおかげでパチンと小気味よくカバーが閉まります。このポストの最初の写真がカバーを閉めたところです。

2009年3月19日木曜日

TinyでDRO!(ハードウェア1)

このシステム全体は+5V電源で動作するようになっています。5V出力のACアダプターで直接給電です。それを前提に回路の説明です。

図の左端にはスケールからのクロックおよびデータの信号入力があります。スケールの電源は電位の高いほうがフレーム=GND電位で、低いほうが-1.5Vと通常とは逆の電位になっているので、C3およびC4による容量結合で4049(hex inverter, IC1)でレベルシフト、増幅しています。4049から出力された両信号は元の信号の反転になっています。これらはTiny2313のポート入力につながります。

左上のNチャンネルMOSFET uPA2753GR(Q7)はゼロクリアをスケールに対して送るためにあります。ポート出力でスケールのGNDとクロック線を(抵抗を介して)ショートさせます(スケール本体のZEROボタンを押したのとまったく等価と思われる)。

図の上半分はLEDドライブ回路です。Tiny2313の少ないポートでこれだけのLEDセグメントを駆動するためダイナミック点灯をさせることにします。数字が6桁すなわちデューティ1/6なので、見掛け上の明るさを稼ぐために1セグメントあたり20mAずつ流します。1コモン端子あたりの最大電流は、7セグメント+1DPで8素子ですから×8で、160mAです。ドライブ用トランジスタをポピュラーな2SA1015ではなく2SA950にしたのはこのためです。実は160mAというのはTiny2313の「全ポートのIOLの合計が60mAを超えるべきではない」という注意書きから外れています(前モデルの90S2313にすればこの条件が100mAと若干軽減されます)。本来ならばドライバトランジスタを挿入するべきでしょう。…が、とりあえず動いているのでまあいいかと。

LEDコモンのドライバトランジスタの選択に74HC138(3-to-8 decoder, IC3)を使用しています。Tiny2313のピン数が足りないための苦肉の策。リセット直後のポート出力が確定していない時期に、特にフラッシュ書き込みを行っている最中に変な表示が出ないように、リセットが利いている間は出力を禁止するようHC138のG1にリセット線を接続しています。

図の右下にダイオードを介してスイッチがつながっているのが見えます。LEDの(ダイナミック点灯のための)スキャンをキーのスキャンにも利用しているのです。これもやはりポートが足りないのが理由です。LEDはダイナミック点灯により約360Hz周期で切り替えますので約60Hzで1周します。キーのスキャンにはちょうど良いくらいの周期です。この方法ならばあと4つもスイッチを増やすことも可能です。なおダイオードと反対側のスイッチの端子は1つの入力ポートに集められます。この入力ポートはプルアップされてなければいけません。ダイオードの役割は2つのスイッチが同時にONになったときHC138の出力同士がショートしてしまうのを防ぐためにあります。逆流防止ダイオードとかいいますね。

Tiny2313にはセラロックが接続されているように書いてありますが、内蔵のRC発振回路で十分だということが分かりましたので、実際は接続しません。ただ、90S2313を使用する場合は必要です。RC発振回路が内蔵されていないからです。

回路の簡単な説明でした。次は基板を設計してOLIMEXに発注するのですが、先人が苦労されてきたとおりやはり最初は一筋縄ではいかずちょっと苦労しました。その話はまたいずれ。

基板が届くまでにずいぶんと時間がありますから、その間にソフトのコーディングとブレッドボードによるプロトタイピングです。これはそのときの様子です。ここまでちゃんと動くのに結構悩みました…。

2009年3月18日水曜日

TinyでDRO!(概要)

以前このブログでデジタルスケールをフライスに装着した話を載せました。その中で、「そのうちLEDの表示器作ろっと」と書いたとおり作ってしまいました。

まずは先例の調査から。ググるためにはまずキーワードが必要です。「デジタルスケール」ちゅう言葉は実は「デジタル式の秤」--つまりキッチンスケール--の意味のほうが(英語でも)一般的らしい。いろいろ探し回ったところ、こういうのは「DRO (Digital Read-Out) 」と言うのだということが分かりました。NCフライスなどを使ったことのある人にとっては当たり前のことかもしれませんが、知らなかったなぁ…。あと、まったく同じメカニズムを持つ「デジタルノギス」「Digital Caliper」なども検索キーワードとしては役に立ちました。

果たして信号の解説をしているサイト(ここここ)や製作例を掲げるいくつかのサイト(ここここ)が見つかりました。信号の解説をしているサイトによれば、また、自身で測定してみたところこのデジタルスケールの信号は以下の特徴があることが分かります。
  • クロック線と1本のデータ線を持つ同期式シリアルである。
  • 電源はGNDと-1.5V。GNDはフレームに落ちている。
  • データは24ビットの2の補数で、最下位ビット(LSB)から送られてくる。
  • 1回のデータ送信で2つのデータ--先に絶対座標、後に相対座標--がつながって送られる。
  • データの送信間隔は約0.3秒。
  • クロックの周波数は約80kHz。データ送信していない間は-1.5Vに固定されている。
  • データの取り込みはクロックの立下りエッジで行うのが妥当。
  • 外からクロック線をGNDレベルに落とすことでゼロクリア信号入力となる。つまりZEROボタンを押したのと同じ。
  • 外からデータ線をGNDレベルに落とすことでモード切換え入力となる。これ相当のボタンは本体にはない。
  • スケール本体の電源がOFFでも信号だけは出力され続ける(液晶画面は消えている)。
負電圧になっているのが嫌だなあと思いつつ、ここは挑戦です。以下の目標を掲げて突進です。
  • とにかく安い部品を使って仕上げる。そのためATTiny2313を使う。できれば部品箱に眠っているAT90S2313でも動くようにする。
  • 軸ごとの独立とする。つまり、X・Y・Z軸それぞれに1基板を使う。
  • ボタンはゼロクリアとミリ/インチ切り替えのみ。
  • 枚数の少ない試作基板では一番安価と思われるブルガリアのOLIMEXに基板を発注する。
  • 最近になって秋月で販売し始めたプラスチックケース(195×110×76mm)に収める。これは500円で非常に安価だ。
  • スケール本体から出すケーブルにはUSBケーブルのデバイス側をちょん切って使う。切ったところに2mmピッチ×4ピンのコネクタをつける。
  • スケール本体から出るケーブルとDROの接続はUSB(A-Type=ホスト側)を使う。安く上げるため。A-Typeメスのパネル取り付け用ケーブルが2本入りで280円(@千石)。
  • 基板以外は1軸あたり1,000円程度に抑える。
写真は完成して動作させているところです。上からX軸、Y軸、Z軸なのですが、Z軸にはスケールそのものがついていない(まだ買ってない…)ので消えたままになっています。次回はハードウェアの説明をします。

2009年2月20日金曜日

TinyでPONG!(ソフトウェア4)

水平1ライン分を描画するプログラムの仕上げとしてスコア表示を説明しましょう。

スコアは2桁の数字です。普通に考えれば1桁分の「数字フォント」を0から9まで10種類用意することになるのですが、ここではフォントの幅=ドット数も勘案して2桁で1つの数字として扱っています。2桁だと100種類のバリエーションができてフォントの格納領域が膨大になってしまいそうですが、そこはPONG。0点から15点まで16種類しか必要としません。その定義が以下の"fconst.asm"です。

fconst.asm ("1"を強調表示しています)

.setと.dbがあって一瞬なんだこりゃ?ですが、フォントをビジュアルで定義するための小技です。1のビットが白――文字部分――になります。1つの数字は横×縦=4×5ドットの枠の中に3×5ドットとして表現されています。これを横に2桁分並べて8×5ドットの「2桁数字」を1フォントにしています。

一方、画面デザイン上の問題として、フォントの1ドット=画面上の1ドットにしてしまうと数字があまりにも小さくなりすぎてバランスが良くありません。数字をある程度の大きさで見せるためにはサイズを拡大してあげないといけない。そこでフォントの1ドット=画面上の4ドットと縦横4倍になるようにしています。

また、スコアの数値自体はSRAM上の変数ScoAとScoBに入っているのですが、そこから上記のフォントデータに変換して、4倍にふくらませて…とやっていると水平ブランキング期間内に処理が終わりません。そこで、スコア→フォントデータへの変換部分だけはあらかじめ――垂直ブランキング期間内に――終わらせておくという措置をとります。その処理をするのが"font_cache.asm"です。ScoA/Bの値を表すフォントデータがSRAM上のバッファ変数Ftbufに格納されます。

さて、Ftbufに格納されたフォントデータを描画する部分が"draw.asm"の最後の部分です。まず、スコアが描かれる縦位置に来たかどうか判定します。

;; --- draw scores ---
drawscore:
mov zl, nxline ; +1=42 prepare for font load
subi nxline, DGT_TOP ; +1=43
cpi nxline, DGT_BOT+1-DGT_TOP; +1=44
brsh eol ; +1=45

スコアが描かれる位置に来ました。次はフォントを縦4倍に広げるのですが、どうしたらよいのでしょう。答えは「4回足踏みして待つ」です。次にどこの行を走査するのかを示す変数nextlineは1ずつ増加するのに対し、4回同じデータを描画すれば縦が4倍に伸びることになります。nxline(のコピーのZL)を4で割っている(LSR命令が2回)のはそのためです。

lsr zl ; +1=46
lsr zl ; +1=47
addi zl, low(Ftbuf)-DGT_TOP/4; +1=48
clr zh ; +1=49

そして横の4倍化。「11110000」と「00001111」の2つのビットパターンを用意しておいて、フォントデータの1になっているビットの位置によってこれら2つを使い分けて行のデータと論理和を取ります。分かりにくいと思うので実例で。

今、描画しようとしている先の行には下記のようにボールの一部が描画されているとします。R7-R10はスコアを表示する位置にあたります。


ここにたとえばフォントデータ"14"を重ねて描画しようとしたとして、
    11001010
    01001010
    01001110 ←この行を描画
    01000010
    11100010

今はそのフォントの3行目だとすると、その描画データは01001110です。これを上位ビットから判定していきます。ルールは0なら何もせず、1ならそれが偶数ビットであればパターン11110000を、奇数ビットであればパターン00001111を重ねるというものです。するとこうなります。
    ビット7は0 … 何もしない
    ビット6は1 … パターン00001111をR7に重ねる
    ビット5は0 … 何もしない
    ビット4は0 … 何もしない
    ビット3は1 … パターン11110000をR9に重ねる
    ビット2は1 … パターン00001111をR9に重ねる
    ビット1は1 … パターン11110000をR10に重ねる
    ビット0は0 … 何もしない

図示するとこうです。

この部分のソースはこれです。ScoA側だけ示します。SBRC命令を使って0のビットをスキップしています。ScoBについても同じ処理になります。

.def fontd =r24
.def pat10 =r27
.def pat01 =r26
ldi pat10, 0b11110000; +1=50
ldi pat01, 0b00001111; +1=51
;; for Score A
ld fontd, z ; +2=53 load a font row of A
sbrc fontd, 7 ; +1=54
or rast7, pat10 ; +1=55
sbrc fontd, 6 ; +1=56
or rast7, pat01 ; +1=57
sbrc fontd, 5 ; +1=58
or rast8, pat10 ; +1=59
sbrc fontd, 3 ; +1=60
or rast9, pat10 ; +1=61
sbrc fontd, 2 ; +1=62
or rast9, pat01 ; +1=63
sbrc fontd, 1 ; +1=64
or rast10, pat10 ; +1=65

以上で映像出力と描画関連のプログラムの説明は終わりです。ゲームの進行については垂直ブランキング期間――1/60秒に1回かつ1.28msの間だけ――に行われますが、それはgame.asmを読んでいただくことにしましょう。

長々と説明をしてきましたが、とりあえずソフトウェアの説明はこれで終了です。お疲れ様でした!

2009年2月7日土曜日

TinyでPONG!(ソフトウェア3)

TinyでPONG!(ソフトウェア2)では映像出力の最前線の部分の説明をしました。1水平ライン分の映像データは24個の汎用レジスタのR0~R23に用意されているということが前提でした。逆にいえばデータをレジスタに入れさえすればいいわけです。

全速力で1ラインの映像を出力した後は、次のライン出力の出番が来るまでのつかの間にデータを仕込みます。ソースは"draw.asm"にあります。最初に判定すべきは何のデータを仕込まなければならないのかです。つまり次に何行目を走るのかで書きだすものを変える。大くくりでは、
  • 1~10行目 → サイドライン(横一本ずっと白=「1」)
  • 11~226行目 → コート内部(センターライン、スコア、ボール、パドル)
  • 227~236行目 → サイドライン
となります。この判定をしているのが以下の部分。

;; --- prepare next raster ---
cpi nxline, COURT_MIN; +1=2
brlo sideline ; +1=3
cpi nxline, COURT_MAX; +1=4
brlo field ; +2=6

;; --- draw sidelines ---
sideline: ; draw sideline
filrast ; (+24)
rjmp eol

次に何行目を走るのかはnxline(ここではR27と同義)に入っています。それがコート内部にあるかどうかの比較があって、外部であればサイドラインを書くべくfilrastを実行して終わりです。filrastはマクロで、汎用レジスタR0~R23の全ビットを1に立てるだけです。これは簡単。

コート内部だったら前出の通りサイドラインとボールとスコアとパドルのデータを用意しなくてはなりません。これらは排他的ではなくて、すべて書かなければならない状態もあり得ますから1つ書いてサヨナラというわけにはいきません。全部説明するのも冗長なのでここではまずボールを書く部分を。

;; --- draw ball ---
.def pos =r24
drawball:
lds pos, Ballt ; +2=10
cp nxline, pos ; +1=11 skip if (line > Ball_top)
brlo drawpad ; +1=12
addi pos, BAL_HEI ; +1=13
cp pos, nxline ; +1=14 skip if (line < Ball_bot)
brlo drawpad ; +1=15 / +2=16

.def pattern =r26
clr zh ; +1=16
lds zl, Ballad ; +2=18
lds pattern, Ballpt ; +2=20
st z, pattern ; +2=22
com pattern ; +1=23
std z+1, pattern ; +2=25 (r24 can be written)

次の行nxlineがボールの上辺の位置Balltと下辺の位置Ballt+BAL_HEIの間にあるかどうかの判定が冒頭にあります。なければそこにはボールがないので書く必要がないということになり、次のパドルを書きに行きます。

ボールを書く場合、ボールのバイトアドレスBalladでレジスタ番号を指定し、ボールのビットパターンBallptをそのレジスタに書き込みます。ここがAVRのアーキテクチャの特徴をうまく使った部分で、レジスタがメモリアドレスのゼロ側にマップされていることを利用しています。つまり、メモリアドレス0x0000はR0を、0x001fはR31を指し示し、この例のようにZレジスタ間接などでもあたかもSRAMをアクセスしているかのごとしです。このようにできることはデータシートには書いてなくて(探しが足りなかったのかも)、実験してみたらできてしまってラッキーでした。こいつは便利だ。AVRすごい!

ボールのビットパターンを書くと言いましたが、ボールの幅が8ドット=8ビット、レジスタが8ビット単位なので(ほとんどの場合)2つのレジスタにまたがって書かなければなりません。この例ではまず指定されたレジスタに左側を書き、次のレジスタに右側を書いています。右側のパターンはちょうど左側のパターンのビット反転になっているのが分かりますでしょうか。(だからcom命令で反転しています)

ボールの形を書く


今回はこのくらいで。次はスコアの表示です。

そうそう、ソースアーカイブにレジスタマップ(regmap.txt)を入れておいたのを忘れていました。今Excelで開いてもう少し見やすくしたイメージを下に貼っておきます。

レジスタマップ

2009年2月6日金曜日

TinyでPONG!(液晶テレビで映らない?)

このPONGの記事を参考に実際に作ってみてくださった方がいらっしゃるらしくてとても光栄な気持ちです。この場を借りて御礼します。

そちらの記事の中で液晶テレビには映らないというご報告がありました。本当に申し訳ないのですがそのことを説明するのを忘れていました。そうなんです。確かに私も確認していました。仕事で使っているNTSC入力付きのデルの液晶モニターには映りませんでした。これは液晶テレビ全般がだめかと思いきや、自宅で使っている三菱製の32インチの液晶テレビ(2005年製の地デジ・BSデジ対応機)では映ります。

実は、今回の作例ではNTSCの信号規格を完全には満たしていません。一番効いているのが、簡略化のためなのですが、インターレースにしていないところでしょう。本来は秒間60フレームではなくて2フィールド×30フレームにするために同期タイミングを小細工しなくてはならないのです。これが、シビアな同期判定の仕組みを持つ最近の液晶テレビだと認識してくれない(だまされてくれない)ケースがあるんじゃないかと。説明が不足していてすみません。

むか~し同じような簡略化をしていたやつ(ゲーム機だったか、コンピュータだったか)があったような。そういうのは液晶テレビには映らない(ことがある)んでしょうね。

(次回はソフトウェアの解説の続きをします。)

2009年2月2日月曜日

フライスにデジタルスケールがついた

プロクソンの小型フライスを使っています。プラケースにスライドスイッチ用の角穴を開ける時などに重宝しているのですが、X-Yテーブルの送りハンドルのバックラッシュ(遊び)に手を焼いていました。バックラッシュがあるのは当然のことだとは思うのですが、それを考慮に入れてもハンドルに刻んである目盛がなんか信用できない。ハンドルの目盛を頼りに四角くぐるっと一周しても元の位置に戻らないんです。
いらいらは募り、ついにデジタルスケールを装着することにしました。かなり高額なものなんだろうと思っていたら、探したら結構安いのがあったんです。

昨今はこの手のフライスを改造してコンピュータNC化するキットなども比較的安価(といっても20万円くらいはする)に買え、それを使えばNCデータを作成するだけで加工は機械任せにできます。しかし…そこまで一足飛びにやるべきか?とりあえず練習の意味を込めて手動でもきちんと図面通りに加工できるレベルに到達することが大切と考えました。

右の写真はX軸側、左下のはY軸側です。3×30mmのアルミ材に固定し、そのアルミ材をテーブルに固定しています。スライダー部の裏側には同じアルミ材やLアングル(今回は手元にあったアルミ製のブックエンドを切って利用)をねじ止めしています。困ったのはテーブルの壁面が垂直じゃないこと!微妙に下方向にすぼまってる。あと、ねじの頭が引っかからないように皿ネジにしたり、ハンドルのコラムが邪魔でタップ(ネジ切りの道具)のハンドルが回せないところにある穴はタップタイトねじを使ってねじ切りしたり…。まあいろいろと、現物合わせで苦労しましたが、なんとか形になりました。

このX-Yテーブルは左右に約140mm(=±70mm)、前後に110mm移動しますので、デジタルスケールはどちらも150mmまで計測可能なタイプにしました。ゼロ点合わせ、インチ/ミリ切り替えの機能がついて1本税込3,780円と安価でした。ちゃんとしたモノでしたよ。バックル付きの木箱に入って、合格検査証も入ってたし。でも…たぶん某国製なんでしょうね、取説の説明と現物が合わない…ボタンの数が違うし…。

あと、通常デジタルスケールにはリードアウト端子が出ていて外部表示装置をつなぐことができるようになっているのですが、今回購入したものは端子がありませんでした(涙)。取説上はあることになっているのに。諦めきれずに分解して中をのぞくと…ありました!それらしきものが。オシロで観測してみるときちんと信号が出ています。ラッキー!ケースの外には出てないけど、基板上にはあったのです。むふふ。そのうちLEDの表示器作ろっと。ケースに穴あけて。おっと、右の写真、ピンボケですな…。

ちなみにデジタルスケールの購入先はスリースカンパニーでした。きちんと使えるし、質感も悪くないし、値段考えると十分満足。他の特価商品としてデジタルノギス1,400円(!)などがありました。うぉっ、安すぎる…。

2009年1月20日火曜日

TinyでPONG!(ソフトウェア2)

映像出力の仕方を具体的に説明します。

映像データを出力ポートに滞りなく書きこまねばならないのですが、どのくらいの速度でなければならないのでしょう。水平方向192ドットを、水平映像期間48μs=384クロックで出力するので、1ドット当たり2クロックにすればよいのです。

この速度はプログラムをいくら効率よく書いてもループ(分岐)を使った時点で達成はできません。分岐命令は、後方分岐(ループ)の場合それだけで2クロックを消費してしまうからです。そこで、ループを「展開する」ことにしました。ループを展開するというのは同じ命令をループの回数だけずらっと並べて書くということで、当然プログラムの容量は増大します。

ループを展開するとはということです。簡単ですね。

ループの展開をすれば1ドット出力に2クロックを使うことができます。しかし2クロックしかありませんから、これとてもいくつかの工夫をしなくてはなりません。
  1. 汎用レジスタにあらかじめ映像データを用意しておく。
  2. 1つめのクロックでポート出力を完了させ、
  3. 次の1クロックで汎用レジスタ内の映像データを1ビット(左)シフトする。
直前の周期であらかじめ1行分の映像データを汎用レジスタ(群)に仕込んでおくことで上記のアイディアが実現できます。これは汎用レジスタが32本と多いAVRだからこそできる技で、そのうちの24本をこの目的のためにつかいます。24本×8ビット=192ドットですから。

また、出力命令のOUTは1クロックで完了するのですが、8ビットいっぺんに出力されます。8ビットマイコンなんで当たり前のことですね。その中の1ビットだけ出力したい(変化させたい)という場合、通常はポートを8ビットで読んで→狙ったビットをクリアし→狙ったビット以外の部分を0でマスクしたデータと論理和し→ポートに書き戻すとか、ビットクリア/ビットセット命令を分岐で使い分けたりと結構面倒なもので、とても1クロックで済むような話ではありません。

そこで、出力ポートの他のビットを無視すなわち未使用にすることにしました。そうすると面倒くさい1ビット書き換え処理が全く不要になり、単に8ビットいっぺんに出力するだけで済んでしまうのです。以前「映像出力がポートの最上位ビットであることと、同じポートの他のビットに出力が割り当てられていないことがとても重要」と書いたのはこのためだったのです。

これらを図で示すと下記のようになります。

プログラムの中でこの処理をやっているのはファイル"draw.asm"の以下の部分です。ループの展開はまだるっこしく読みにくいのでマクロを使っています。

8ビット分のループ展開(マクロ定義)

.macro vout8px
out PORTB, @0 ; +1=1 output bit7
lsl @0 ; +1=2
out PORTB, @0 ; +1=3 output bit6
lsl @0 ; +1=4
out PORTB, @0 ; +1=5 output bit5
lsl @0 ; +1=6
out PORTB, @0 ; +1=7 output bit4
lsl @0 ; +1=8
out PORTB, @0 ; +1=9 output bit3
lsl @0 ; +1=10
out PORTB, @0 ; +1=11 output bit2
lsl @0 ; +1=12
out PORTB, @0 ; +1=13 output bit1
lsl @0 ; +1=14
out PORTB, @0 ; +1=15 output bit0
clr @0 ; +1=16 clear this segment for next timing
.endmacro

24レジスタ分のループ展開

vout8px r0 ; 16*24=384cycles(visible period)
vout8px r1
vout8px r2
vout8px r3
vout8px r4
vout8px r5
vout8px r6
vout8px r7
vout8px r8
vout8px r9
vout8px r10
vout8px r11
vout8px r12
vout8px r13
vout8px r14
vout8px r15
vout8px r16
vout8px r17
vout8px r18
vout8px r19
vout8px r20
vout8px r21
vout8px r22
vout8px r23
out PORTB, r23 ; 0+1=1 set to blank level

最後のOUT命令は描画が終わったら背景の黒レベルにもどす処理です。これをやらないと右端のドットが1だった場合、白い線が画面の右端までびよ~んと尾を引いてしまいます。

2009年1月19日月曜日

TinyでPONG!(ソフトウェア1)

さあ、ここからが本番、ソフトウェアの説明に入ります。早速ソースコードをアップロードしておきました。


トップレベルのエントリーは"pong.asm"です。拡張子を見ただけで分かると思いますが、8MHzという低速のCPUでビデオ信号のタイミングにシビアに合わせるためアセンブラを使っています。AVRのような簡易なマイコンはパイプライニングなどの高級な高速化技術を一切使っていないため、命令表の消費クロック数の値がそのままタイミングカウントとして簡単に計算できますので、アセンブラで記述すればタイミングを正確に合わせることができます。

時代はデジタルハイビジョンに向かっているのに今更ながらアナログTV信号(NTSC方式)を扱いますが、アナログ放送の停波まで公式にはまだ2年ちょっとありますし、全世帯からアナログTVがなくなるのはまだまだ時間がかかると思います。逆にデジタルTVへの切り替えで不要になるアナログTV(壊れても泣かなくて済む!)を実験などに存分に使えるようになる絶好の機会が到来すると前向きに考えましょう。

NTSC信号を使ってTVに絵を出すためにどんな信号をどのようなタイミングで出さなければならないかは、こちらなどのサイトに素晴らしい説明がされていますので参照していただくことにして、本プロジェクトではそのタイミングをどのようにして作り出しているのかを説明します。なお、本プロジェクトで扱う映像信号は白黒で、カラーではありません。また、グレースケールはなく2値の白黒ーーすなわち1画素=1ビットです。

信号出力のタイミングは唯一、16ビットタイマーのTimer1からの割り込みによります。Timer1は原クロック8MHzでカウントアップし、水平同期信号の周期(63.555μs≒64μs)に達したところでゼロクリアして自動的に循環するモードに設定。このカウンタの2つある比較レジスタ一致の1つOCR1Bでの一致により発生する割り込みで映像信号出力を開始します。同時に比較レジスタOCR1Aの値によるPWM動作を設定し、OC1Aピン(=PB1ピン)から水平同期信号を自動的に出力しています。


これをこのように作ります。無理やりテキストで書いて、見にくくてすみません。のこぎり状の波形はTimer1カウンタの内容です。


水平方向にいくつカウントするかは64μs×8MHz=512カウントですのでTimer1は0から511まで変化します。カウンタがOCR1Bレジスタと一致したところで事前に準備してあった水平1行分の映像データをタイミングをきちんと計算されたプログラムで1画素=1ビットずつ出力します(図中の「映像送」)。1行出力した直後には次の行のデータを準備しておきます。図中「備」と書かれた部分がそれに相当します。準備してから次の割り込みが来るまでの間は、たった数クロック程度の余裕しかありませんが、スリープ(図中の「Z」)して待ちます。

割り込みハンドラ(ソースtim1oc1b.asm)は垂直方向のカウント、垂直同期の生成、パドル=簡易A/Dコンバータの状態遷移の管理、音声出力など軽い処理をするのみです。

割り込みハンドラから戻ってくるとメインプログラム(main.asm)のスリープ命令が解除されて、映像送出(draw.asm)と次の行の準備(同)を行います。

OC1A出力はOCR1Aレジスタでの一致で"L"を出力、カウントトップすなわちICR1レジスタとの一致で"H"を自動的に出力するような高速PWMモードの設定にしてあります。これが同期信号出力になります。

水平方向の表示期間が終わり、垂直ブランキング期間に入ったかどうかはメインプログラムの冒頭で判断しています。本プログラムでは水平20行分がその期間となります。その中の3行分の期間は水平同期信号の極性反転を行います。これが垂直同期信号となります。極性反転は前述のPWMの条件を反転するだけで実現しています(つまり、OCR1A一致で"H"、トップで"L"になるように)。

垂直ブランキング期間では、映像出力が必要なくなるのでこの時とばかりにゲーム本来の動作を実行します(game.asm)。表示期間とブランキング期間合わせて全部で256行あるうちたった20行がゲーム進行に使われますので、本来の7%のパフォーマンスしか得られません。8MHzで公称8MIPSのAVRでも約0.6MIPSとなります。それでもこの程度のゲームであれば全く問題なくスムーズに動作しています。

2009年1月7日水曜日

TinyでPONG!(ハードウェア)

長らくのお待たせでした。PONG復活です。今回は回路図の公開とハードウェアの解説をしましょう。

例によって秋葉原の某所(ほとんど秋月ですが…)で入手しやすくできるだけ安価な部品ばかり集めています。基板を除けば千円でお釣りがくるくらいです。

Tiny2313は約8MHzのRC内蔵発振器を持っていますが、わざわざセラロックを用いているのは周波数の安定性が重要になるからです。もちろん水晶発振子でもかまいません。ためしに内蔵発振器を使って画面出力をしてみたところ、絵がゆらゆら、ばらばらでとても見られたものではありませんでした。温度係数もやたら大きくて、温めたり冷やしたりすると揺れ具合も大きく変化します。

映像出力はPB7、同期信号出力OC1A(=PB3)で、これらはダイオードとRを使って混ぜてコンポジット出力としています。実は映像出力がポートの最上位ビットであることと、同じポートの他のビットに出力が割り当てられていないことがとても重要なのです。(PB3が出力として使っているように見えますが、これはあくまでもOC1A出力として使っているので影響ありません。)

音声出力はPD6から出しています。出力コンデンサを省略していますが、心配ならば100uF程度のアルミ電解を直列につなぐと良いでしょう。

パドル入力用の簡易ADCにはコンデンサーへの充放電を制御する出力PD4、PD5と放電時間を計測するための入力PD0、PD1を使います。PD4またはPD5をHにしてダイオードを経由して急速に充電したのちLに切り替えます。放電はボリュームの抵抗を通って徐々に行われます。コンデンサの端子電圧がVILまで下がったことをPD0またはPD1でセンスし、それまでの時間をボリュームの抵抗値=パドルの位置として求めるのです。

ありがたいことにTiny2313は旧製品の90S2313と違って3Vで動作しますので、電源は単三乾電池2本でOKです。この回路では電源スイッチはありませんが、ソフトウェアスイッチ(サーブスイッチSW2を長押し)でパワーダウンモードに入ります。この時の消費電流は極めて低くて1μA未満です。

(2009.2.12 回路図のTYNY2313のシンボルマークを修正。)

2009年1月6日火曜日

90S2323でスリープタイマー付きベッドライト(2)

あけましておめでとうございます。
うだうだしている間に時間がたってしまい…年が明けてしまいました。
この先どうなる事やら…ま、気を取り直して更新してまいりましょう。

本件のベッドライトは自宅で毎日便利に使っております。自分としては満足です。

操作はいたって簡単で、白いボタンで点灯、もう一度白いボタンで30分スリープタイマー起動です。消したくなったら黒いボタンを押すだけ。たったそれだけのものなのですが、これで本を読みながら眠ってしまっても大丈夫。照度は十分すぎるくらいにとれていて、逆にまぶしすぎるくらいです。白色LEDの性能上少し青白いので赤とか黄色のLEDキャップをいくつかはめ込むとちょうどいいくらいの感じになります。

写真ギャラリー:
秋月の100円ユニバーサル基板(72x47mm)に組みました。左側から出ている4本の赤いリード線はポリウレタン線で、LED出力となっています。LEDヘッドを支えるアームをケースに固定している部分が見えます。アームは嫁が手芸用に持っていたダイソーの直径3mmのアルミニウム線を使っています。ちょうどポリウレタン線と同じ色の塗装がかかっていてうまい具合です。

ケースはタカチのプラケースSW-95Bだったと思います。白ボタンの上に開いている穴はパイロットランプ用。タイマー動作中は1Hzで点滅します。アームは、軸のアルミ線とLED出力のポリウレタン線にシリコンチューブをかぶせて両端を熱収縮チューブでギュッとしめてあります。


LEDヘッド部を正面から見たところです。グリーンレジストのかかったユニバーサル基板の切れ端にLEDをマウントしています。黒い台座は…





実はヒートシンクを2個連結したものだったのです。分厚い熱伝導シートを基板との間に挟みねじでしっかりと密着固定しています。LEDが低消費電力だと言ってもかなり発熱しますし、その熱がLEDの寿命を縮めますので放熱してあげることは良いことです。このヒートシンクも秋月の1個50円のものです。


ソースと回路図を以下に載せておきました。

2008年11月14日金曜日

90S2323でスリープタイマー付きベッドライト(1)

いやあ、すっかりご無沙汰してしまいました。とはいってもPONGの続きではございません。

私、寝床についてから眠くなるまで本を読むのが日課なんですが、眠くなってきて、いよいよ眠りに就くぞというときに電気スタンドを消す動作をしたとたん、眠さ50%オフ!で、結局入眠できず…(涙)ということを難儀に思っていました。

眠くなったらそのまま何もしないで自然に眠りにつきたい…そういう需要ってちまたにもあるんですね。タイマー付きのベッドライトって市販されています。でも、いまさら買いたくはないし…。

いろいろ逡巡したあげく、以前仕事の関連で購入しておいた超高輝度の白色LEDがたくさんあったことを思い出し、自作することにしました。これのために新たに購入することはせず、手持ちの部品だけでなんとかなりました。

このベッドライトの仕様は
  • 白色LED×9灯=25cd×9=225cd(6灯でやってみてちょっと暗かったもので…)
  • 30分間のスリープタイマー(タイマー起動後30分で消灯)
  • スリープタイマーで消灯するときは徐々に減光するギミック
  • 電源はDC12VのACアダプター
です。

タイマーと減光ギミックの実現のためやはりマイコンの力を借ります。(だからこのブログに載せられる~!)マイコンは部品箱にあったAT90S2323-10PC。8ピンDIPでI/Oが3本しかとれないやつ。それでもフラッシュは2KB積んでいて、GCCでコンパイルできるのはありがたい。その他回路の詳細は回路図を見ていただくことにしましょう。

2008年10月31日金曜日

混沌から秩序へ(ジャンパワイヤ)

秋月で購入したブレッドボードのジャンパワイヤは透明な整理箱に入って付属してきます。いろいろな種類が「そこそこ」分類されているわけなんですが、今まではあまり気にしないで必要な長さのワイヤを「当て勘で」ピックアップし、現物合わせをしながら使っていました。

しかし、最近はこの現物合わせが面倒くさくなってきて、何とかならないかと軽く悩んでおりました。

ふとワイヤの被覆の色と長さに対応関係があるような気がして全部皿の上にひっくり返して調べてみると…なんだ、長さが抵抗と同じカラーコードに対応しているじゃありませんか。つまりこういうことでした。

{被覆なし,赤,橙,黄,緑,青,紫,灰,白}={1,2,3,4,5,6,7,8,9}

イコールの右側の単位はグリッド長で、1グリッド=0.1インチ=2.54mmです。ここから上が10グリッド単位のもので、(さっきのものと重複しますが、長さがまるで違うので見た目で判別がつきます。)

{茶,赤,橙,黄, 緑}={10,20,30,40,50}

なるほど。こりゃ覚えやすい。それじゃあ、分類して入れておけば探すのがもっと楽になるはず…しかし、14種類もありますな。付属のプラケースは仕切りがいくつも入っていない。

さんざん考えてしまいました。100円ショップでピルケースのような仕切りのたくさんあるケースを買おうか、いやもったいないから何か仕切りを立てて使おうか…。

悩みぬいた揚句たどり着いた答えがこれです。通常ICを挿して保存するためのプラスチックフォーム(硬いスポンジみたいなもの)をついたての隙間よりも少し大きめに切って差し込むだけ。このケースでは1グリッドから10グリッドまできれいに仕分けして入りました。めでたしめでたし。

2008年10月15日水曜日

TinyでPONG!(概要)

これから製作例を紹介する「TinyでPONG!」は、非常に安価な8ビットマイコンATTiny2313ひとつでこの黎明期のテレビゲームを「安価に」模倣してみようじゃないかというプロジェクトです。

Tiny2313はプログラムメモリー(ROM)が2Kバイト、RAMが128バイトしかありません。RAMといっても当然VRAMではないので、そのまま映像出力ができるような仕組みは一切ありません。

しかも画面表示解像度は水平192ドット×垂直236ラインですので、VRAMで実現しようとすると1ドットあたり1ビットとして5,664バイトが必要になってしまいます。やはりVRAM方式では無理ということになります。

PONGの映像を出力するのと同時に水平・垂直の同期信号を作ります。テレビ側はこれらの信号に合わせて画面の上端と左端のタイミングをつかみます。この同期信号のタイミングをTiny2313内蔵のタイマ/カウンタで作り出します。

プログラムでは、前述のボール左端カウンタ、右端カウンタ、上端カウンタ、下端カウンタ、左パドル上端カウンタ、下端カウンタ…が変数として使われて、ハードウェアカウンタをシミュレートしているような格好になっています。

1「行」分の映像を出力するところは一番工夫したところで、非常に高速に、かつ、安定した速度で動かなければならないので、かなりトリッキーなことをやっています。AVRの汎用レジスタの数の多さを最大限利用しています。PONGの仕組みを模倣すると言っておきながら、この部分ではちょっとずるをしているのですが…VRAMを使わないことが一番の目的なのでまあ許してもらいましょうか。

パドルは10KΩのボリュームで制御します。ボリュームの位置を検出するのに電圧を直読できるA/Dコンバータがほしいところでしたが、あいにくこのマイコンには内蔵されていません。そこで、簡易A/Dコンバータをソフトウェアで実装してあります。コンデンサを決まった電圧に充電しておいて、ボリュームの抵抗で放電させ、電圧が一定レベルまで下がったときまでの時間を測ります。抵抗が小さければ素早く放電され、抵抗が大きければゆっくり放電されます。詳しく計算すると分かるのですが、抵抗値とこの放電時間は比例します。つまり、ボリュームはBカーブ(=直線)であればそのままBカーブとして使えるのです。

サーブをするためのボタンは電源ボタンを兼ねています。電池を入れるとすぐには映像は出てきません。サーブのボタンを押すと初期画面が現れます。映像が出ているときはいつでもサーブのボタンを5秒以上押し続けることにより電源を切ることができます。

そのほかにも音が出たり、ソフトで電源ランプ(LED)の輝度調節をしていたり、さまざまなことをやっています。

プログラムのソースコードを公開する予定ですので、詳しくはそちらをのぞいてみてください。

2008年10月14日火曜日

なぜPONGなのか(4)

ここまで来てやっと「なぜPONGなのか」がお話しできます。

PONGは今や当たり前となっているコンピュータ+VRAM+ソフトウェアの方式とは構造をまったく異にしています。

テレビがスキャンビームを左から右に、上から下に振って絵を描き出すその仕組みを直に利用しています。テレビ画面はもとから2次元ではなく、織物をほどいて時間軸方向に引き伸ばしたものを、逆の手順をたどって織り上げたものです。今まさにスキャンビームが通るその瞬間を捕まえて輝点を打つ、そのタイミングが画面上の座標に対応します。その瞬間を捕まえるのに多数のカウンタとコンパレータが活躍するのです。

ゲームの進行もコンパレータでの一致検出とカウンタの計数方向の反転だけで実現されています。動物に例えると条件反射あるいは脊髄反射的といえましょう(図らずも同じ「反射」ですね…)。そこにはソフトウェアのソの字もありません。

現代人にとっては、ゲームとしては単純すぎて、遊ぶにはすぐに飽きてしまいますが、コンピュータではなく「デジタル回路」と「テレビジョン技術」の学習としては実によくできた教材になるのではないでしょうか。