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

前へ
次へ
目次へ戻る
ホームページトップへ戻る
☆C++でUSB(HID)アクセスプログラムを作成

PIC側のプログラムはC18コンパイラを使わずPICアセンブラで作成しますが、パソコン側のUSB(HID)アクセスプログラムはBorland C++コンパイラで作成します。
しかしこれがまた難物で悪戦苦闘の連続でありました。

[第39回]

●前回のHID送受信結果についての疑問

前回のテストの結果、HIDクラスでは送受信の間隔(インターバル)を1msに設定していても、ダミーのINトランザクションが実行されるらしく、計算上よりも遅い送受信速度になってしまいます、という説明をしました。
でも、よーく考えてみると、なんだかおかしいなあ、という気がします。

もしもWriteFile後のReadFileが1ms以内に実行されていれば、そしてそのReadFileの実行後1ms以内に次のWriteFileが実行されているとするならば、前回考えたような、ダミーのINトランザクションは実行される余地がないはず、ではありませんか?
にもかかわらず、前回のような結果(送信と受信各1回の平均時間が2.5ms)になったということは、つまりWriteFile、ReadFileの実行には1ms以上かかっているということになります。

PIC18F4550はすぐに応答しています。そのことに間違いはありません。
PIC18F4550のプログラムはマシン語(アセンブラ)で書いてありますし、PIC18F4550の場合、ホストの要求に対する応答は、PIC内蔵のUSBコントローラがオートマチックに行うため、1msもの遅延が生じることは考えられません。

とすると、問題はPC側にあるということになります。
PC側のプログラムがマシン語で書かれていれば、こんなことはおきないはず。
ですけれど、USBの送受信は残念ながらC++で組まざるを得ません。
しかしながら、もし問題があるとすれば、それはC++のせいではありません。

つまるところ、WriteFile()、ReadFile()のせいである、としか言いようがありません。
うーん。
そうなってくると、もうそこから先は追及してもムダ、ということになってしまいますねえ。

前回の結果には、ちょっと納得のいかないところもあるのですが、ここはまあ、とりあえずは、そういうものだ、ということにしておきましょう。
とにかく、これで、送信も受信もとりあえずはできたわけですから、余りこんなところでこだわってたりしないで、これまでに得られた結果をもとにして、実際の送受信プログラムを完成させなければなりません。
なにしろ、早く実際に動く送受信プログラムをPICに実装して、4ビット、8ビットのCPUボードや、TK80ボードの製作にかからねばなりませんから、そこそこ利用できそうなパフォーマンスが得られたならば、もうそれでGOするに限ります。

実際問題として、送信、あるいは受信のみの場合でも、1回64バイトの通信に2msかかったとしても、それでも32KB/秒の速度は得られるわけですから、それなら今回のそもそもの目的としては十分な速度だと思います。
皆様方への説明は、これから先も、当分はこのテーマ(USB、HID)でいくことになりそうですけれど、私の作業としては、そろそろといいますか、やっとといいますか、CPUボードやTK80ボードの設計作業にかかれそうな段階にまでこぎつけたように思います(はやくボードの設計にかからないと、そのうち寿命がつきてしまうかも…)。

●HIDサンプルプログラム

さてそういうことでありまして、前回までの説明で、とりあえずHIDデバイス(PIC18F4550)に対してデータの送信と受信のテストができたところまでお話をしました。
でもすんなりとそこまでできたわけではありませんでした。
そもそも送信どころか、最初はC++で書いたソースプログラムがどうやってもコンパイルエラーになってしまい、そのエラーの原因がわからず四苦八苦しました。
やっとのことでWriteFileはうまくいったのですが、そうしたら今度はReadFileがエラーになってしまって、なかなかクリアすることができませんでした。
そういったあたりのところも順をおって説明していきたいと思います。

本当は、PICの側のプログラム作業についての説明からとりかかるのがそもそもの順序なのですけれど。
あ。まだ、USBの信号だとか、パケットやトランザクションなどについても、中途半端な説明で終わってしまっていますから、それについても整理しなければなりません。

ですけれど、せっかく、ようやくにして、C++プログラムで送受信に成功したところでもありますから、順序は逆になりますけれど、まずは送受信のテストに使ったパソコン側のサンプルプログラムについての説明からとりかかることにしたいと思います(いろいろ説明をすすめていきますうちに、やがてまた、USB信号の詳細、などについてもさらに詳しく説明をする機会がでてくることと思います)。

PIC18F4550のプログラムはPICアセンブラで書きましたが、パソコン側のプログラムはBorland C++で書きました。

なんですか最近はVC++とかVBとかがハバを利かせているようですけれど、私はどうも好きになれないんですよねえ。
で、HIDの参考にしようとして、あちこちのサイトをさまよったのですけれど、なかなか「純正の」C++ソースには出会えなくて、結構苦労いたしました。

これが苦労の結果出来上がったHIDサンプルプログラムです。

//HID sample
//
//10/1/18 10/1/19 1/20 1/29
//2/6
//from hidtest1_8_b
//
#include <windows.h>
#include <setupapi.h>
#include "hidsdi2.h"
#include <iostream.h>
#pragma comment(lib, "hid.lib")

void main( void )
{
        cout << hex;
        GUID hidGuid;
        HDEVINFO devinf;
        SP_DEVICE_INTERFACE_DATA spid;
        SP_DEVICE_INTERFACE_DETAIL_DATA* fc_data=NULL;
//
        HidD_GetHidGuid(&hidGuid);
        devinf = SetupDiGetClassDevs(&hidGuid, NULL, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);     
        spid.cbSize = sizeof(spid);
        cout <<"devinf="<<devinf<<endl;
//
        for( int index = 0; ; index++ )
  {
        if(!SetupDiEnumDeviceInterfaces(devinf, NULL, &hidGuid, index, &spid))
                {
                cout<<"end"<<endl;
                break;
                }
        cout<<"index="<<index<<endl;
//
        unsigned long size;
        SetupDiGetDeviceInterfaceDetail( devinf, &spid, NULL, 0, &size, 0 );
        PSP_INTERFACE_DEVICE_DETAIL_DATA dev_det = PSP_INTERFACE_DEVICE_DETAIL_DATA( new char[size] );
        dev_det->cbSize=sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
        SetupDiGetDeviceInterfaceDetail( devinf, &spid, dev_det, size, &size, 0 );
        cout << dev_det->DevicePath << endl;
//

        HANDLE handle = CreateFile( dev_det->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
//
        HIDD_ATTRIBUTES attr;
        HidD_GetAttributes( handle, &attr ) ;
        cout<<"VenderID: " <<attr.VendorID <<endl;
        cout<<"ProductID: " <<attr.ProductID<<endl;
        cout<<endl;
//
        CloseHandle(handle);
   }
}

このプログラムは、ema log(http://emaame.com/20061227.html)様のサイトにありましたソースリストをもとに作成させていただきました。ema様には感謝申し上げます。

●まずはHIDデバイスを検出します

上のリストには、まだ送信、受信部分はありません。
そこまで含めると、プログラムを理解するのが難しくなりますから、とりあえずは省いてあります。
では、このプログラムは何をするのか、といいますと、これでHIDデバイスをまず検出するのです。

あ。その説明にとりかかる前に。
実は、このソースプログラムはこのままではコンパイルできません。
最初のヘッダーファイルのインクルードのところの、
#include "hidsdi2.h"
でエラーになってしまいます。
なぜならhidsdi.hはありますけれど、hidsdi2.hは私が細工をして作ったヘッダーファイルなので、もともと用意されているヘッダーファイルではないからです。

●コンパイルをクリアするまでがひと苦労です

WEB上で紹介されているHIDプログラムなるものなのですが、まずはここのところでつまづいてしまいました。
その、#include "hidsdi2.h"についてですけれど、ここを普通に、
#include <hidsdi.h>
としておいて、コンパイラにかけると、わけのわからないエラーメッセージに遭遇してしまうことになります。
こんなことになってしまいます。



コンパイルはできるのですけれど、そのあとのリンクでエラーになってしまいます。
’外部シンボル’stdcall HidD_GetHidGuid(_GUID *)’が未解決
って、それって一体全体なんだっていうの?
いきなり「未解決」といわれたって、それだけでは全くわけがわからないじゃないのさあ。

うう。
HidD_GetHidGuidとHidD_GetAttributesが組み込めない???
Borland C++コンパイラでは、この関数は使えない、ということなのか???

これにはほんと困りました(だから、Cは嫌いなの!)。

あ。時間がなくなってしまいました。
この続きは、また次回にいたします。
CPUをつくろう!第431回(2010.2.7upload)を再編集

PICでUSBを![第39回]
2011.7.10upload

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