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

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

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

[第52回]

●HID送信テスト

いままで説明してきましたHIDテストプログラムに送信部分を付け加えて実行してみました。
HID送信はWriteFile関数を使います。送信はWindowsマシンからUSBケーブルで接続したPIC18F4550基板に対して行い、PIC18F4550からはRS485で当社のBASICボードZB25Kに対して行います。
その簡単な接続図と写真は[第29回]でお見せしたものと同じです。
[第29回]では、64バイト固定長のデータを送信するテストでしたが、今回は任意の長さのデータを送ってみました。

まずは今回のテストプログラムを実行したときの画面のコピーをお見せします。

右側が送信側です。
左側の画面はBASICボードZB25KがRS485経由でPIC18F4550から受信したデータをパラレルポート経由でWindowsマシンのDOS窓に表示させているところです。
RS485(RS232C)のボーレートは9600ボーです。

HID通信の概略については、[第29回]から以後の何回かにわたって説明をしていますから、HIDについて忘れてしまったよ、というお方はもう一度お読みくださいませ。

そこでも説明しましたようにHID送信(もちろん受信も同じ)では、一度に64バイトしか送ることができません。
右側の送信画面に見えておりますように、データが無い部分は全て00にしてしまえば、送信データ部分だけが認識できるようにも思われますが、そういうわけにはいきません。
今回は見やすさも考えて、ASCII文字コードで送りましたが、メモリデータやプログラムなどをバイナリ(2進数)データのまま送信することも考慮すると、その場合には00〜FFの全てがデータになる可能性がありますから、00とかFFをデータがないことを示す記号にすることはできません。
同様の理由で改行コード0D・0Aも、データエンドマークとしては使うことができません。

ではどうするか。
これはもう、64バイトのうちの1バイトを使って、送信文字数を示すことしかないのではありませんか?
画面右のDOS窓では、送信する文字列データをそのまま表示させたあと、そのデータコードを16進数で表示させています。
最初のバイトが[00]ですが、これはWriteFile関数、ReadFile関数の約束事です。
送信、受信するデータは64バイトですが、送受信バッファは65バイト必要です。
そして最初のバイトには00を入れます。

実際に送受信されるデータは2バイト目からになります。
今回の送信プログラムでは、その2バイト目に、送信バイト数を入れています。
これを受信したPIC18F4550は、この先頭の文字数情報によって、送られてきた64バイトのうち、真の送信データが何バイトかを知ることができますから、必要なデータのみを取り出すことができます。

●HID送信プログラムリストです

今回の送信テスト(上の画面)で使ったHID送信プログラムです。

//HID TEST from hidtest8
//
//10/4/13 4/15 4/16
//
#include <windows.h>
#include <setupapi.h>
extern "C" {
#include "hidsdi.h"
}
#include <iostream.h>
#pragma comment(lib, "hid.lib")

void main( void )
{
        cout << hex;
        GUID hidGuid;
        HidD_GetHidGuid(&hidGuid);
        HDEVINFO devinf;             
        devinf = SetupDiGetClassDevs(&hidGuid, NULL, 0, DIGCF_PRESENT |
 DIGCF_DEVICEINTERFACE);
        SP_DEVICE_INTERFACE_DATA spid;
        spid.cbSize = sizeof(spid);
//
        int getmk=0;
        HANDLE handle;
        for( int index = 0; ; index++ )
  {
        if(!SetupDiEnumDeviceInterfaces(devinf, NULL, &hidGuid, index, &spid))break;
        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 );
//
        handle = CreateFile( dev_det->DevicePath, GENERIC_READ|GENERIC_WRITE,
 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
        if(handle==INVALID_HANDLE_VALUE){cout<<"INVALID_HADLE"<<endl;continue;}
        HIDD_ATTRIBUTES attr;
        HidD_GetAttributes( handle, &attr ) ;
        if (attr.VendorID==0x4d8 && attr.ProductID==0xa){cout << "GET!" <<endl;getmk=1;break;}
        CloseHandle(handle);
   }
        if (getmk==0){cout <<"not found" <<endl;return;}
//
//write file test

        DWORD nwrite=65;//not 64!
        DWORD nwritn=0;
        unsigned char wbf[65]="\0";
        char *wdata1="samidareo/atsumetehayashi/mogamigawa";
        int n=strlen(wdata1);
        wbf[1]=n+2;//with CR+LF
        int i;
        int j=0;
        for (i=2;i<=n+1;i++){wbf[i]=wdata1[j];j++;}
        wbf[i]=0x0d;
        wbf[i+1]=0x0a;
        cout << wdata1 << endl;
        int d;
        for (i=0;i<=0x40;i++)
   {
        d=wbf[i];
        printf("[%x]",d);
   }
        cout << endl;
        if(!WriteFile(handle,&wbf,nwrite,&nwritn,NULL))cout<<"write error"<<endl;
        else cout<< "write ok"<<endl;
        CloseHandle(handle);
}

今まで説明してきましたプログラムリストでは、確認のためにプログラムの実行途中で得た変数やハンドルの値などを表示させていましたが、今回のプログラムでは、それらの表示部分やコメントなどを外して、必要最小限のプログラムリストにしてあります。

そのように今までのプログラムリストから余分なところは省きましたが、必要なところについては、今までの説明でお見せしていましたプログラムリストと同じものです。
今回のテストプログラムでは、その今までのテストプログラムに、
// Write file test
より後ろの部分を追加いたしました。

●文字列を連続で送信してみました
今度は上で行ったテストプログラムを、複数の文字列を連続して送信するように書き換えてテストしてみました。



連続して送信しても、ちゃんと受信できることが確認できました。

●変更したHID送信プログラムリストです

//HID WRITE TEST from hidtest8n
//
//10/4/13 4/15 4/16
//
#include <windows.h>
#include <setupapi.h>
extern "C" {
#include "hidsdi.h"
}
#include <iostream.h>
#pragma comment(lib, "hid.lib")
//
        void hidwrite(char *wdata);
        HANDLE handle;
//
void main( void )
{
        cout << hex;
        GUID hidGuid;
        HidD_GetHidGuid(&hidGuid);
        HDEVINFO devinf;             
        devinf = SetupDiGetClassDevs(&hidGuid, NULL, 0, DIGCF_PRESENT |
 DIGCF_DEVICEINTERFACE);
        SP_DEVICE_INTERFACE_DATA spid;
        spid.cbSize = sizeof(spid);
//
        int getmk=0;
        for( int index = 0; ; index++ )
  {
        if(!SetupDiEnumDeviceInterfaces(devinf, NULL, &hidGuid, index, &spid))break;
        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 );
//
        handle = CreateFile( dev_det->DevicePath, GENERIC_READ|GENERIC_WRITE,
 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
        if(handle==INVALID_HANDLE_VALUE){cout<<"INVALID_HADLE"<<endl;continue;}
        HIDD_ATTRIBUTES attr;
        HidD_GetAttributes( handle, &attr ) ;
        if (attr.VendorID==0x4d8 && attr.ProductID==0xa){cout << "GET!" <<endl;getmk=1;break;}
        CloseHandle(handle);
   }
        if (getmk==0){cout <<"not found" <<endl;return;}
//
//write file test
        char *wdata1="samidareo/atsumetehayashi/mogamigawa";
        char *wdata2="natsukusaya/tsuwamonodomoga/yumenoato";
        char *wdata3="shizukasaya/iwanishimiiru/seminokoe";
        char *wdata4="yukuharuya/torinakiuono/mewanamida";
        char *wdata5="araumiya/sadoniyokotou/amanogawa";
        hidwrite(wdata1);
        hidwrite(wdata2);
        hidwrite(wdata3);
        hidwrite(wdata4);
        hidwrite(wdata5);
        CloseHandle(handle);
}
//
void hidwrite(char *wdata)
{
        DWORD nwrite=65;//not 64!
        DWORD nwritn=0;
        unsigned char wbf[65]="\0";
        int n=strlen(wdata);
        wbf[1]=n+2;//with CR+LF
        int i;
        int j=0;
        for (i=2;i<=n+1;i++){wbf[i]=wdata[j];j++;}
        wbf[i]=0x0d;
        wbf[i+1]=0x0a;
        cout << wdata << endl;
        int d;
        if(!WriteFile(handle,&wbf,nwrite,&nwritn,NULL))cout<<"write error"<<endl;
        else cout<< "write ok"<<endl;
        return;
}
//

今度は複数の文字列データを連続して送信できるようにプログラムを直しました。
WriteFile関数をCallしているところは、hidwrite()関数としてまとめました。
CPUをつくろう!第479回(2010.4.17upload)を再編集

PICでUSBを![第52回]
2011.7.11upload

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