(2004.03.23更新) | |
(2004.03.24更新) | |
(2005.08.25更新) |
マイコン野郎としてはとにかく楽をしたい、何でもマイコンに直結したい、そう思っています。Treva も Bluetooth モジュールもそうでした。USB もそうです。Full Speed デバイスの 12Mbps となると低価格のマイコンでは苦しいのですが、1.5Mbps の Low Speed デバイスなら何とか直結できるんじゃないか、USB ホストコントローラ等を使わなくてもソフトウェア UART と同じようにソフトウェアで USB のパケットを作ったり受け取ったり出来るんじゃないか、そんな思いが頭から離れませんでした。離れないならやってみましょう。チャレンジです。
USB の通信は、基本的には 3.3V 程度の差動信号を半二重で送受信すれば実現出来るものです。USB マウスやキーボードといった Low Speed デバイスなら 1.5Mbps なので、データ変化の周期は 666.6ns となります。これならマイコンでも何とかなりそうです。更に、スレーブとして何時でもホスト側からの要求に応えないといけないデバイスコントローラと違い、ホスト側は USB の規定を守る範囲ならば自分のペースで信号を送れます。これならマイコンでも何とかなりそうです。
そもそも IgorPlug-USB では AVR のソフトウェアでデバイスコントローラを実現しています。ホストコントローラくらい簡単に作れないで如何するよ?という自分への問いかけに対して、現物で応えたいと思います。
まずは力弱く C8051F310 で実装
とは言ったものの、USB ホストの何たるかをよく分かっていない状況では、開発環境の整ったマイコンを使いたくなります。という訳でまずは力弱く C8051F310 を使って開発をしてみることにします。これならCコンパイラも使えるしソースコードデバッグも出来るしトラブった時でも安心です。
更に C8051F310 を使うメリットとして、内蔵クロック周波数の微調整が出来ることがあります。ソフトウェア UART 等でシビアなタイミングが必要な時には、アセンブラで記述しステート数をカウントして nop 等でタイミングを調節する方法が取られます。そのような手間をかけずに、C言語で適当に信号出力のコードを書き、ロジアナで出力波形の周期を測定しながら周波数調整レジスタへの設定値を調整することで、あっさり 1.5Mbps の信号を出力できるようになりました。これはお手軽です。
しかしそれには副作用がありました。実は送信処理はデフォルトの 24.5MHz 設定では間に合わず、若干クロックアップの方向で調整をする必要がありました。その影響なのか分かりませんが、デバッグ中に内蔵 RAM の読み出しに時々失敗するようになってしまいました。正直言ってデバッグ効率が落ちました。もし同じことにチャレンジする方がおりましたら、貴重なチップを捨てる覚悟が必要ですのでお気をつけ下さい。
ちなみに回路はチップと抵抗のみで OK です。USB 接続部分は 15Kohm のプルダウンのみでも良いのかもしれませんが、念のため USB 仕様書に従って 33ohm の抵抗を入れています。また動作確認のために LED を2個つなげています。
USB プロトコルを理解する
次はソフトウェアの実装です。www.usb.org にある仕様書や USB コンプリートも読みましたが、一番分かりやすかったのはトラ技 2000年6月号の特集「作るオリジナル USB アダプタ」でした。これらの記述を参考にして USB のプロトコルを仕込みます。
まず 1ms 周期が必要です。Low Speed では 1ms 周期で D+/D- 共に 0 とする End of Packet を出力します。これが無いとデバイスが Sleep してしまいます。次に全体の流れですが、デバイスの接続が確認されたら Reset を出力し、エニュメレーションを行った後、定周期でインタラプト転送を行うという流れになります。
エニュメレーションはどのようなデバイスが接続されたのかを知るために情報を取得し、その情報を元にデバイスの設定を行うものです。しかし接続するデバイスの種類を固定とするならば、必要な処理は Set Configuration だけで OK だということが分かりました。今回はテストも兼ねて、Get Descriptor と Set Address と Set Configuration を実装してみました。
ソースを見て頂けると分かりますが、基本的には指定したパケットが出力できるように RAM 上に差動出力データを生成し、それをまとめて 1.5Mbps で出力します。出力したらすぐに I/O ポートを入力に設定し、入力データが変化したらデータ受信を開始します。その後 ACK を出力する必要があるのですが、ここで RAM に展開をしようとすると応答時間が遅れて動作が正常に行われなかったので、ROM テーブルの差動データを直接 I/O ポートに出力しています。高速アクセスが可能な IDATA 領域の RAM 容量がもっと沢山あれば良かったのですが。
USB マウスをつなげてみる
実際の Low Speed デバイスを接続してデータを受け取ってみます。まずは手持ちのロジクール USB マウスをつなげてみます。動作が確認できるように、マウスの左右ボタンの状態を LED に表示させてみました。
とりあえず当初の目的である Low Speed デバイスをマイコンに直結して通信することは実現できました。しかし C8051F310 の I/O 設定をよく理解していない為か活線挿抜が出来なかったり、クロックアップの暗黒面にぶつかったりと自分的には不満足な結果に終わりました。Low Speed 対応 USB ホストで最低限やるべきことは分かったので、次は AVR マイコンを使って満足できるものを実装したいと思います。
何となく AVR の命令を並べて Cycle 数を数えてしまいました。RAM 上に置かれたデータを Port に順次出力するのに必要なのは 6Cycle、ROM 上のデータでも 8Cycle あれば出力できることが分かりました。AVR でも 12MHz なら割と簡単に 1.5Mbps の信号を送受信できる、ということで IgorPlug-USB と同じように AVR を使ってマイコン直結にチャレンジしてみます。
早速 USB 直結回路を作ってみました。当初は ATtiny26L でも使ってみようかと思っていたのですが、外付け発振子が必要なことを考えて結局ポピュラーな AT90S2313 を使うことにしました。結局今回もクロックアップの力を借りることにしました。
AVR は Port レジスタの設定が分かりやすい為か USB デバイスを抜き差しを PIND レジスタの変化で確認することが出来ました。これで C8051F310 では実現できなかった活線挿抜に対応することが出来ます。
まずは USB マウスで動作を確認する
C8051F310 では、送信シリアルデータをプログラムで生成して出力させました。今回は開きなおって送信シリアルデータを ROM テーブルで持ち、何も考えずに出力させることにしてみました。いわばシリアルデータプレーヤとして CRC の演算も何も全て固定値で出力させます。自由度は無くなりましたが、プログラム自体は非常に単純なものとなりました。
また C8051F310 ではマイコン自体の Reset 後に USB デバイスの接続を待ち、接続が確認されたら USB バスの Reset とエニュメレーションを行っておりましたが、今回は USB デバイス接続/非接続の2つの状態を持たせ、非接続状態から接続状態に移行する際に USB バス Reset とエニュメレーションを行うようにしました。これで活線挿抜が行えるようになりました。
まずは USB マウスを使って動作確認をしました。C8051F310 と同じく、左右ボタンを押すと LED が光るようにしてみます。これで DIP パッケージで安価・入手性の高いイカサマ USB ホスト環境が実現できました。
USB ゲームパッド変換アダプタを実現する
マウス以外の Low Speed USB デバイスとしては、USB キーボードや USB ゲームパッドが思い浮かびます。イカサマ USB ホストの応用例として USB ゲームパッドのボタン状態を AVR の Port 出力に変換するものを作ってみます。
USB ゲームパッドのエニュメレーションに関しては USB マウスと同じです。Set Address でアドレスを設定し Config 番号1番固定で Set Configuration を行うと Interrupt 転送によるデータ受信が可能となります。ASCII PAD USB mini で受信データのフォーマットを確認をした所、SYNC / DATA / 0x01 / 左右 / 上下 / ボタン / といったデータ列が返されているのが分かりました。左右と上下は 0x00 / 0x80 / 0xFF の3種類の値でボタンの状態が返されます。またデータはボタンが ON になった時と OFF になった時に送信されます。
ATARI 仕様のジョイスティックと違って 5V 電源が必要なのが面倒ですが、とりあえずその辺で売られている USB ゲームパッドのボタン情報を AVR の Port 出力に変換することが出来ました。USB ホストというよりは変換アダプタとして使えるかもしれません。
無線 USB マウスでお手軽無線リモコンを実現する
最近は無線 USB マウスも安価に入手できるようになりました。これをイカサマ USB ホストで制御すると、お手軽な無線リモコンが作れるのではないかと考えました。当初は有線 USB マウスと同じで挿せば動くと思っていたのですが、それまで作っていた C8051 / AVR 用ファームウェアでは手持ちの無線 USB マウスを使うことは出来ませんでした。
従来のファームウェアの問題点が2つありました。一つは Set Address や Set Configuration に対して NAK が返された時の再送処理が無かった点です。ロジクールの USB マウスや USB ゲームパッドでは NAK が返っていなかったので問題無かったのですが、無線 USB マウスでは2回 NAK ガ返った後に STATUS を返すことが分かりました。そこで NAK か DATA0/1 かを受信データの特定位置の bit が 0 か 1 かで判定することで、高速に NAK かどうか判定し、NAK の場合には IN を再送する処理を追加しました。
もう一つは Interrupt 転送で ACK を返すまでの時間です。USB 仕様書を良く見ると、少なくとも token 間隔として 18bit 待つようにと書かれています。ということは逆に言えば 18bit 以内に ACK を返さないといけないということになります。これも USB マウスや UBS ゲームパッドではもっと間が空いていても動いていたのですが、無線 USB マウスでは不具合が発生してしまいました。
ファームウェアを修正し、何とか無線 USB マウスを使ってお手軽無線リモコンを実現することが出来ました。現在はボタン情報の 2bit だけのリモコンですが、折角のマウスなのでXY変位の指示を有効に活かした応用例を考えてみたいと思います。
USB マウス、USB ゲームパッドと来たら次は順当に USB キーボードとなるでしょう。キー入力に応じた処理を行う何かが出来たら良いのですが、とりあえず安直に USB キーボードの入力を UART 送信する変換アダプタを作ってみました。
直結モノなので、正直な所回路は殆ど変わらないです。AT90S2313 は UART を内蔵しているので、回路的には LED 点灯を UART からのデータ出力に変更しただけです。また今回は手持ちの 4P DIPSW を付けてみました。UART のボーレート切替えやモード変更に利用しています。
力弱く SHIFT キーのみ対応する
キーボードを取り扱うので、本来であれば CTRL キーの対応や CAPS LOCK の対応、状態表示の LED の ON/OFF などを行う必要がありますが、力弱く SHIFT キーが押された時の処理だけ対応しました。ホントは VT100 互換のシリアルコンソール代わりに使えるレベルまで実現できるとヨカッタのですが、実力が伴いませんでした。
実力が伴わないので USB マウスの出力データを UART で送信するモードも仕込みました。応用例があまり思いつきませんが、XY変位をシリアル出力できるので何かの役に立つかもしれません。
USB ハブ機能を持つキーボードは接続出来ないなど制約の多い変換アダプタですが、何かの役に立つかもしれないと思い公開します。いつまでも Low Speed 専用ではサビシイので、そろそろ次のチャレンジに進みたいと思っています。