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

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

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

[第49回]

●XPでもUSBマウスが検出できました

[第47回]で、WindowsXPではCrearFile関数でUSBマウスの検出に失敗してしまいます、ということを書きました。
そうしましたら、メールをいただきました。
こちらのページ(http://noritan-micon.blog.so-net.ne.jp/2008-04-07)に書いてありますよ、と教えていただきました。
さっそく訪問させていただきました。

そのページのずっと下の、終わりに近いところに、
c)システムデバイス
と書いてあって、そこにUSBマウスとUSBキーボードについてのコメントがあります。
そのところを一部引用させていただきます。

[以下引用](原文のまま)
Windows はマウスとキーボードをシステムデバイスとして、直接のアクセスを制限します。
(一部省略)
キーボードとマウスに対するCreateFileは、次のパラメータで開けます。
CreateFile()
- dwDesiredAccess = 0
- dwDesiredAccess = (FILE_SHARE_READ | FILE_SHARE_WRITE)
[引用ここまで]

ただし下側の行に誤記があります。
−dwDesiredAccess=(FILE_SHARE_READ | FILE_SHARE_WRITE)ではなくて、−dwShareMode= (FILE_SHARE_READ | FILE_SHARE_WRITE)です。

そういうことなのですか。
MSDNのCreateFileの説明(http://msdn.microsoft.com/ja-jp/library/cc429198.aspx)をもう一度参照してみました。

dwDesiredAccessに0を設定すると、「デバイスにアクセスすることなく、デバイス属性の問い合わせを行えます」と書いてあります。

HIDテストプログラムのCreateFileのところを直して試してみました。
dwShareMode(第3パラメータ)は、もとからFILE_SHARE_READ|FILE_SHARE_WRITEですから、dwDesiredAccess(第2パラメータ)のみ0に変更しました。

       HANDLE handle = CreateFile( dev_det->DevicePath, 0,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );

そのように変更したHIDテストプログラムをWindowsXPマシンで実行した結果です。

おお、今度はUSBマウスに対しても、CreateFile関数でhandleが取得できました。
その結果、USBマウスのVenderID(15ca)とProductID(c3)も取得できました。

もっとも今回の私の目的はUSBマウスを検出することではなくて、どちらかというと、それを除外できればよいので、その目的からすれば、[第48回]でとった方法が正解なのですが、将来何かの目的でUSBマウスをコントロールしたい、というときには、この情報は役に立つと思います。
なお、dwDesiredAccess=0にして得たhandleでは、多分ReadFile、WriteFileは使えないと思いますから、今回の私の目的には合いません。
USBマウスに対してデータアクセスをする方法については、さきほど紹介したページに記載があります。

メールで上記の情報をお知らせいただいた、高知県のMさま。ありがとうございました。
これからもよろしくお願いいたします。

●デバイスインターフェイスの詳細情報の取得

前回は、SetupDiGetDeviceInterfaceDetail関数を2回Callする部分について、「よくわからないので、ここはそういうことで、パスします」と書きました。
まだそれほどよくわかったわけではありませんが、リストとMSDNの解説を見ていましたら、少しは見えてきたように思えましたので、無知をかえりみずに、説明を少しだけ加えたいと思います。

これがその部分のリストですが、長ったらしい名前が繰り返されていて余計にわかりにくく思えますので、少し書き直してみました。

        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;

直したのは3行目と4行目です。

        unsigned long size;
        SetupDiGetDeviceInterfaceDetail( devinf, &spid,NULL, 0, &size, 0 );
        PSP_INTERFACE_DEVICE_DETAIL_DATA dev_det =new SP_INTERFACE_DEVICE_DETAIL_DATA[size];
        dev_det->cbSize=sizeof(*dev_det);
// 
        SetupDiGetDeviceInterfaceDetail( devinf, &spid, dev_det, size, &size,0 );
        cout << dev_det->DevicePath << endl;

少しは見やすくなったと思います。

MSDNのSetupDiGetDeviceInterfaceDetailの説明(http://msdn.microsoft.com/ja-jp/library/cc429201.aspx)によりますと、この関数は、第1パラメータにHIDデバイス情報セットのハンドルdevinfoを、第2パラメータには個別デバイス情報の構造体が書き込まれたバッファのポインタ&spidを置きます。
そして第3パラメータには、詳細情報が返されたときに入るバッファのポインタdev_detを指定します。
それからがちょっとややこしいのですが、第4パラメータには、そのバッファのサイズを、そして第5パラメータには、そのサイズをもつ変数のポインタをそれぞれ指定します。
第6パラメータは0にします。

問題はバッファのサイズにあります。
この関数をCallすることで得られる詳細情報(構造体)は、デバイスによってサイズが異なっているようなのです。
したがって、その詳細情報が入るバッファのサイズは事前には決められません。
この関数をCallすることではじめてそのサイズが求まる、ということのようです。
これは鶏が先か卵が先か、という命題です。

それをクリアするために用意された機能なのかどうかはわかりませんが、第3パラメータにNULLを、そして第4パラメータのバッファサイズに0を指定したうえで、この関数をCallすると、第5パラメータで指定した&sizeに、詳細情報を入れるべきバッファサイズが返される、のだそうです。
この関数を2回Callするのはその理由からです。

最初のCallによって得られたsizeの値を使って、バッファのサイズを決定してから、あらためて2回目の関数Callを行います。
CPUをつくろう!第476回(2010.4.12upload)を再編集

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

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