標準TTLだけ(!)でCPUをつくろう!(組立てキットです!)
(ホントは74HC、CMOSなんだけど…)
[第480回]

●WriteFile()関数

いつものことなのですが、前回は時間がなくなってしまいましたので、HID送信プログラムについて詳しく説明することができませんでした。
今回はWriteFile()関数について、すこし詳しく説明をいたします。

WriteFile()関数はWindowsAPIです。
MSDNライブラリ(http://msdn.microsoft.com/ja-jp/library/cc429856.aspx)にその説明があります。
そのページでは下記のように説明されています。
同じページの下の方には、各パラメータについての詳しい説明もあるのですが、例によって業界用語の羅列で概ね意味不明です。
つまり、ドシロウトは立入禁止、ということのようです。

BOOL WriteFile(
  HANDLE hFile,                    // ファイルのハンドル
  LPCVOID lpBuffer,                // データバッファ
  DWORD nNumberOfBytesToWrite,     // 書き込み対象のバイト数
  LPDWORD lpNumberOfBytesWritten,  // 書き込んだバイト数
  LPOVERLAPPED lpOverlapped        // オーバーラップ構造体のバッファ
);
前回のHID送信プログラムでは、
if(!WriteFile(handle,&wbf,nwrite,&nwritn,NULL))cout<<"write error"<<endl;

というように使いました。

ファイルのハンドル、データバッファ、書き込み対象のバイト数、書き込んだバイト数については説明がなくてもわかります。
最後のパラメータについてはよくわかりませんが、ここはNULLにしておけばよいようです。
第2パラメータ(データバッファ)と第4パラメータ(書き込んだバイト数)はポインタを指定します。

HIDの場合、第3パラメータ(書き込み対象のバイト数)は要注意です。
前回のHID送信プログラムでは、

DWORD nwrite=65;//not 64!

としました。

HIDでは送信および受信はフルスピードモードでは64バイトということになっています([第423回])。
[第423回]で引用しました「USB2.0 Specification」のChapter 5には、下記のように書かれています。

64bytes or lessとあります。
それならWriteFile()関数の第3パラメータには64バイト以内なら何を指定してもいい、とつい誤解してしまいますが、そういう意味ではないようです。
たとえ1バイトしか送信するデータが無い場合でもダミーデータと一緒にして、常に64バイトを送信しなければいけません。

念の為に、HID送信プログラムの送信バイト数を60に直して実行してみました。

ご覧の通り、write errorになってしまいました。
やっぱりHIDではいつも64バイトを送信しなければいけないようです。

ところで。
では、64バイトではなくて、なぜ65バイトなのか?
ということなのですが…。

確かにどこかのサイトにそのようなことが書いてあったと思ったのですが、でも今調べてみますと、どこだったのわかりません。
WriteFile()はRS232Cでも使っていますから、そのような制約がWriteFile()にある、ということではないはずです。
HIDの場合については、WriteFile()、ReadFile()では、そうしなければならない、ということのようです。

念の為、DWORD nwriteを64、65、66と変えて試してみましたが、nwrite=65以外はwrite errorになってしまいました。

なお実際に送信されるのはwbf[1]〜wbf[64]の64バイトで、wbf[0]は送信されません。
wbf[0]には0(NULL)を指定する、ということのようだったと思うのですが、それも念の為、wbf[0]=0xffとして試してみたところ、それについては特にエラーにもならず正しく送信できました。
しかしあえてそのようにする必要もありませんから、ここはおとなしく

unsigned char wbf[65]="\0";

としておくのがよいでしょう。

と、ここまで書いてきてから、はてね?
と思い当たるところがありました。
nwrite=65にしなければエラーになってしまう、ということは、ひょっとすると、USB接続時にPICからホストに送られるHIDレポートディスクリプタの設定値がもとになっているのでは?

うむむ。そうなのかもしれません。
でも、まあ、学校の研究レポートではありませんから、その設定値を変えてまで試してみる必要は、私の場合には、ありません。
とにかく、送信も受信もHIDの場合の最大値である64バイトに設定して使いますから、その場合にはnwrite=65にする、ということになります。
ここは、それでよい、ということにいたしましょお。
ではみなさま。おやすみなさい。

のつもりだったのですが、またまた、ひょっとしたら、と思いついて、[第477回]でご紹介させていただきました、ema様のサイト(http://emaame.com/20061227.html)を再び訪問させていただきました。
おお。やっぱり。ここだったのですねえ。

HID送信テストプログラムの
//write file test
以下のところを、次のように書き変えてみました。

//
PHIDP_PREPARSED_DATA pp;
if ( !HidD_GetPreparsedData( handle, &pp ) )return;
//
HIDP_CAPS caps;
if ( HidP_GetCaps( pp, &caps ) != HIDP_STATUS_SUCCESS )return;
cout << "InputByteLength  : " << caps.InputReportByteLength   << endl;
cout  <<  "OutputByteLength : "  <<  caps.OutputReportByteLength    <<  endl;
        CloseHandle(handle);
}

ema様のサイト(http://emaame.com/20061227.html)から、そのままいただいてしまいました。

そして、これが実行結果です。

HIDレポートディスクリプタでは40(=64)と指定しているのですが、HidP_GetCaps関数で得られるOutputReportByteLengthおよびInputReportByteLengthは、それよりも1バイト大きい41(=65)バイトになっています。
むむむ。
そういうことだったのですねえ。
やっと、これで、送信バイト数を65バイトにする根拠が得られました。

うむむ。
そういうことならば…。
さきほどは、学校の研究レポートではありませんから、などと書きましたけれど。
それほど手間のかかることでもありませんから。

PIC18F4550のレポートディスクリプタの、Output、Inputのバイト数を書き直して、その結果がどうなるかを試してみることにいたしました。

そうしましたら。
なんとも、よくわからない結果となりました。

画面に見えているPICアセンブラのリストの最下行の
movlw 20;max packet size(Low)
は、もとは40(=64)にしていたところです。
それを半分の20(=32)にしてみました。

ところが、右のDOS窓の上の方に見えるように、さきほど書き直したHIDテストプログラムを実行してみると、PICの値を直したはずなのに、表示された値はやっぱり41(=65)でした。
しかし、この状態で実際にWriteFile()つきのテストプログラムを実行してみますと、画面左のDOS窓に表示されているように、正しく送信されたのは32バイトでした。
…/mogamまでで31バイトしかありませんが、実は送信データの先頭には1バイトの送信バイト数データがついていますから、それを合わせるとちょうど32バイトになります。

そうすると、caps.InputReportByteLengthとcaps.OutputReportByteLengthの値が変わらないのがおかしいのですけれど…。
念の為にWindows98マシンをリセットして再起動させてから、もう一度テストしてみたのですけれど、結果は全く同じでした。
どこかがおかしいようです。

しかし、それはそれとして、仮に送信バイト数を64バイトよりも小さい値に設定してみたとしても、それでスループットが向上するわけではありませんから、ここはやっぱり64バイトの固定長として扱うべきなのでしょう。
2010.4.18upload

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