PICでUSBを!(知識ゼロからのスタートです)
PIC18F14K50のUSB機能を100%自前のソフトで制御する試みです。しかもアセンブラで!
当記事は2009年12月から「TTLでCPUをつくろう!」というタイトルの もとにほとんど毎日連載をしてきたものを再編集したものです。 2011.7.9

前へ
次へ
目次へ戻る
ホームページトップへ戻る
☆USB(HIDクラス)

USBを通じてパソコンとの間でデータを送受信する方法にいくつかの種類があることがわかりました。
その中で特別のドライバをインストールする必要のないHIDクラスを使うことを考えます。

[第29回]

●Hot Newsです! USB通信に成功しました!

氷解という言葉があります。
溶融とか融解という言葉もあります。
一生懸命加熱しても全く変化がなかったのに、ある点を超えると一気に解けてしまう、ということが物質の世界にはあるようです。

トランジスタやダイオードでも確かブレークダウンという現象があったように記憶しています。
そうそう、社会現象としては、「ベルリンの壁」がまさにそういった感じでした。
人間の脳も物質ですから、その産物たる思考もやっぱり物質としての法則に影響されるのでしょうか。

何日も何日も同じところを堂堂巡りばかりしていて、全く解決できなかったことが、ある日突然一気に氷解してしまう、ということを過去に幾度と無く経験をしてきました。
今回のUSBもまさにその通りで、昨年秋から、大きなカベが3つ4つくらいあったのですが、それぞれがある時点で、まさに「突然に溶けて(解けて)」なくなってしまいました。
この数日間の進展もちょうどその通りの展開で、もう何日も、同じところでカベにぶつかってしまって「わからん、だめだ」の繰り返しだったのですが、突然に「わかったぞぉ!」と、一気に解決してしまいました。

いずれゆっくり順をおって説明をしていく予定ですが、まずはホットなトピックスということで。
ホットもホット、USBの通信に成功したのは、ついまだ2時間ほど前のことでした。
こんなことになりました。


あ。かなりコーフンしていましたので、画像の配置が左右逆になっています。
DOS窓(DOSプロンプト)が2つ開いています。
右がホスト(PC)で、送信側です。
プログラムはBorland C++で作成しています。
!〜’までの文字列をUSB経由でPIC18F4550に送信したところです。
確認のため同じプログラム(HIDTEST1_6.exe)を2回実行しています。
いろいろ表示されているデータは、PIC側から送信された情報(デバイスデスクリプタなど)です。
中ほどに
VenderID:4d8
ProductID:a
という文字が見えます。
4d8というのはMicrochip社の登録IDです。
ProductIDは、私が適当に書き込んだIDナンバー(0A)です。

左のDOS窓についてはちょっと説明が必要です。
下図は今回のテストの説明図です。

Windowsパソコン(98SE)とPIC18F4550テスト基板とはUSBケーブルで接続されています。
今回はUSB通信のテストとして、まずPC→PICの方向での送信テストを行いました。
PIC18F4550はUSB経由で送られてきたデータをバッファにプールしたあと、テストのため、16進数データ(ASCIIコード)に変換したうえで、232Cシリアルを通じて送出します。232Cシリアルデータは当社のBASICボードZB25Kで受けます。
両者の間の通信は実際には”RS232C”ではなくて”RS485”に変換して行います(232Cのように専用のケーブル、コネクタが不要で「ミノムシクリップ」で接続してもいけるという簡便さが、その理由です)。
ZB25Kは送信元のパソコンとパラレルケーブルで接続しています。
ここでZB25Kを間にはさんでいる理由は、232Cの受信および結果の表示をするプログラムをZB25KのBASICで組むほうがパソコン本体の上でCなどで組むよりはるかに楽である、という点に尽きます。

さて、そこでさきほどの左のDOS窓ですが、ここの緑色の表示は、じつはパラレルポートを通じて接続されているZB25Kから送られてきた「文字情報」をそのまま表示しているだけなのです。
この表示文字列は同じDOS窓に表示されているBASICプログラムの実行によって、PIC18F4550からRS485経由で送信されてきた文字データを受信して、そのまま表示させたものです。
BASICプログラムはZB25Kで実行されていて、パソコンはその実行には直接関与していません。
つまりZB25K(CPUはKL5C8012)がPIC18F4550から受信したものを、ちょいとWindowsの画面を借りて表示しているもの、と理解してください。

で、その表示文字列(つまりはPIC18F4550がUSB経由で受信した文字データを16進数表示にしたもの)を見てみますと、パソコンから送信した文字列の16進コードに一致していることがわかります。
ただしよく見ると最初の1文字(!、コード21)が欠落しています。
2回テストを行って2回とも同じ結果になっています。
これは実は送信側のAPI(WriteFile)が原因です(バグではありません。Microsoftのサイト”MSDN”にその説明があります)。
この点については、理由がわかっていますから実用段階ではクリアできると思います。

今回はPCからPICへの送信テストでしたが、ここまでできたのですから、「受信」についても多分うまくいくと思います。
かれこれ2〜3ヶ月ほども泥沼状態の中で苦戦苦闘してきましたが、これで、やっと泥沼から抜け出せたのではないか、と思います。
大きく完成に向けて進んだことになると思います。

テスト中の写真です。

左下のジャノ目基板がPIC18F4550テストボードです。
USBコネクタの横の8pinのICがMAX485です。
少し上方にもMAX485のジャノ目基板があります。
これは写真右側のZB25Kに接続されています。
2つのMAX485の間は2本のミノムシクリップでつないでいます(完全な手抜きです)。

●最初は仮想COMポート(CDCクラス)でスタートしたのですが…

USBと一口にいっても、その中身は非常に多様で、全体についてはまだよく理解できていません。
ただ、そもそもの目的は、MYCPU80で使用した、FT232RLとPIC16F88を使った、USB→232C変換(仮想COMポート)をPIC18F4550に置き換えてしまいたい、ということにあったわけですから、当然、CDCクラス(Communication Device Classというのだそうですね。なるほど)という分類にある、仮想COMポート方式でのUSB通信を行う、ということからスタートしました。

といってもUSBの基本的なことがらを理解すること自体が大変なことで、なかなか実際の通信を行うというレベルにまでは到達できません。
最初のハードルは「Enumerate」とかいう、あのDevicedescriptorやらConfiguration descriptorやら、さっぱりわけのわからぬものを送出する(どうやって?)という、不得要領なことを理解しなければなりません。
そこをほぼクリアできるところまで来るのにざっと2ヶ月ほどもかかってしまいました。

とりあえずはPIC18F4550のファームウェア構築から取りかかったのですけれど、やっとのことで「不明なデバイス」として検出されるあたりまでこぎつけることができましたので、ぼちぼちPC(ホスト)の側のプログラムにも手をつけようか、と思いましたら…。

うう。CDCクラスはWindows98ではサポートしていない…?
んなことはないでしょうよ。だって、ちゃんとWindows98でもFT232RLを使ってUSB→232C変換をしてるじゃないの?
あれ?そうか。専用のドライバを使ってるんで、CDCクラスとは限らないわけか。

いや、しかし、それでは、困る。
冗談ではない。
そりゃあ、皆様方は多分XPをお使いでしょうけれど、何回も書いていますように、私ぁXPは使いたくない。
ずっとこのまま98を使いたい。
いろいろ調べてはみたのですけれどねえ。
Windows98ではCDCをサポートしていない、というのは本当のところ、そうなのかどうか、ということもいまひとつはっきりしませんでしたけれど、なにしろまだPICの側のプログラムも完全には出来ていないのですから、それを確かめるわけにもいきません。
まさに泥沼です。
こんなところからは、はやくぬけださなくっちゃ…。

●で、HIDクラス、に方針を変更してしまいました

泥沼でもがいていましたら、むむ、ひょっとしてHIDが突破口になるのでは?という考えが浮かびました。
HID(Human Interfase Device)クラスです。
でもねえ…。
キーボードとかマウスでしょ。
ちょっと、そんなんじゃ、232Cの代用にはならないのでは…。

ところが、ところが、ふと気がついてみましたら、かの秋月のPICプログラマ、もHIDだったんですよぉ。
PICプログラマを接続して、デバイスマネージャで見るとちゃんとHIDデバイスとして認識されていることがわかりました。

秋月のPICプログラマは、PIC18F2550を使っています。
PIC18F2550を使って、HIDクラスでUSB通信をして、PICデータの書き込みを行うことができるのですから、その程度のデータの送受信ならできる、ということになります。

(下図)今回通信テストを行ったPIC18F4550テストボードはHIDデバイスとして認識されています。


それにしても、CDCとHIDじゃあ格落ちなんじゃないの?
とお思いかもしれません。
そこで、じゃあ、HIDクラスではどの程度の通信が行えるのか、というあたりを調べてみました。
あ。USBにもいろいろバージョンがあって、通信速度もいろいろ、らしいです。

でもここでは現在ごく一般的なUSB2.0での場合について考えてみます。
HIDクラスとしては、1パケットあたり64バイト、というのが最大送信量のようです。
パケットというのは、USBのハードレベルでのミニマムな送信単位(要素)です。
複数のパケットが集まってフレームという単位になって送信されます(いずれもう少し詳しく説明します)。

たった64バイト?と思うことなかれ、です。
これがなかなかばかにしたものじゃないのですよ。
そのパケットの送信速度に注目、です。

USBではフレームを1ms毎に送信します。
HIDクラスでは1フレームに1データパケットということらしいので、64バイト/パケットということは、64バイト/msということになります。

それはわかるけど、それのどこが、ばかにできない、なのお?

ばかになどできませんです。
232Cと比べてみましょうか。
232Cでは通常1バイトのデータを送るのに、スタートビットとストップビットを付加しますから(USBでは、それはありません)、232Cのレートに換算するなら、64バイトは640ビットの送信量になります。
640ビット/msです。

232Cではボーレートという単位を使いました。ボーレートは実質的にはビット/秒です。
すると、640ビット/msは、640Kビット/秒です。つまり、640Kボーです。
DOSの時代の232Cのボーレートは、最高でも19200ボーくらいでしたから、HIDの威力がわかろうというものです。
どうです。
これなら、文句無し、じゃありませんか。

あ。最初にお見せした、送信テスト、あれがそのテストです。
速度の測定はしていませんが、1パケット(1ms)で64バイトのデータが送信されたことは間違いありません。

[10.1.20追記]
ここでは話を簡単にするため、1パケット=1フレーム=1ms、として説明をしています。
実際には1パケットではなくて複数パケットが集まったフレームが1msごとに送られる単位です(次回説明の予定)。
転送の方式によっては、1つのフレームに複数のデータパケットを詰め込んでしまうものもあるようです。
映像や音声データなどのように多少データが不正確になっても大量に送りたいとき(アイソクロナス転送方式)は、そうなっているようです。

ここで扱うUSBの送信周波数は、なんと12MHzです。
しかもこの周波数はそのまま送信データのビットレートになっています(もちろんチェックビットなどを含めた総体として送るときのビットレートですが)。
DOSの時代のRS232Cは、1バイト8ビットにスタートビットとストップビットを加えたうえに、さらに、送信クロックx16とかx64とかということで、1ビットあたり16クロックまたは64クロックを消費して送信していたのですから、隔世の感あり、とはまさにこういうことをいうのでしょう(いやあ技術の進歩と言いますが、ほんとにすごいものです)。
12MHzでもおったまげてしまいますのに、なんですか480MHzなんていうモードまであるのだそうですから、そりゃあもう、そこまではとてもついていけません。
12MHzでもTTLの限界に近い速度ですから、そんなものには、手が出せませんし、理解の外です。

で、話をもとに戻しまして、そのフレームなのですが、ちょっと計算をしてみました。
1フレームが1msで、ということは、転送クロックは12MHzですから、フレームあたり、最大では12000ビット送信できることになります。もちろんこれを全て送信データに割り当てることはできません。当然のことながらフレームごとに区切りが必要ですし、いろいろ仕掛けが必要です。
しかし、上で説明しましたHIDでの転送データが1パケット(=1フレーム)あたり最大で64バイト(=512ビット)である、ということから考えますと、そのいろいろな仕掛けなるものを考慮したとしても、フレームの中は「がら空き」だということになります。

ま、ちょいともったいない、とも思いますが、その間にホストもPICも、そのまたさらにその先にあるCPUも、それぞれ送受信のためのプログラムを実行しなければなりませんから、「がら空き」程度が、それでちょうど、程よい速度、ということになるのでは、と思います。
程よいどころか、64バイト/msということは、PICや8ビットCPUは、1バイトのデータをわずか15μsで処理しなければならない、ということですから、これは多分とても「間に合わない」ほど高速な転送速度である、と言えるでしょう。

たとえばMYCPU80やその他のマイコンボードで、PICを介してUSBで送受信するとした場合、マイコン(CPUは8080とします)側は、すくなくとも

LOOP:MOV A,M
OUT 80 ;I/Oアドレス80にデータを出力
MOV A,FE ;ストローブパルスON
OUT 81 ;ストローブパルスがI/Oアドレス81のビット0であるとしたときの例
INR A   ;ストローブパルスOFF
OUT 81
INX H
DCR B
JNZ LOOP

というようなプログラムを実行して、やっとPICに1バイトのデータを送ることができるのですから、これはとても15μsではこなせません。
概算として、OUT、JNZを10クロック、その他を5クロックで計算すると、上のプログラム例の1サイクルは、65クロックかかります。
CPUクロックが2MHzの場合、1クロックは0.5μsになります。
すると65×0.5=32.5μsですから、とてもとても間に合いません。
CPUクロックを4MHzにしてもまだ間に合いません。
6MHzならなんとか間に合います(約10μs)。
でも6MHzでも余裕は4μsぐらいしかありませんから、PICへデータを送るだけでほぼ精一杯です。ほかのことはおそらく何もできません。

CPUはそれでよいとしても、この同じ1msの間に、PICはCPUからデータを受け取ってそれをホスト(パソコン)に送信しなければなりません。
ホスト(パソコン)も、同じ1msの間で、PICから受け取ったデータをファイルに格納するなどの作業をしなければなりません。

そのように考えてくると、HIDで十分(過ぎる)ということになろうかと思います。
[追記ここまで]

●参考までに、プログラムの送信部分のリストです

ところで、送信部分のプログラム(C++)なのですが、まだテスト段階なので、一部だけ、お見せいたします。
これがまた、232Cよりも簡単、あっけないくらいです。
ご覧の通りWriteFile()一発で終わりです。


CPUをつくろう!第421回(2010.1.19upload)を再編集

PICでUSBを![第29回]
2011.7.9upload

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