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

前へ
次へ
目次へ戻る
ホームページトップへ戻る
☆USB(HID)送信プログラム

パソコンからPIC18F2550へデータを送信するプログラムの試験的なものはすでにできています。
そのプログラムをもとに実用的なプログラムに作り上げました。
実用になったHID送信プログラムの説明です。

[第66回]

●HID送信プログラムを変更いたしました

前回([第65回])お見せしましたHID送信プログラムは、ASCIIコードで書かれた16進数データファイル(HTX)を読み込んで、それをバイナリデータに変換してから、PIC18F2550に送信するものでした。
MYCPU80がUSB・RS232C変換IC(FTDI社FT232RL)を使って、Windowsパソコン側もMYCPU80側もRS232Cとして通信を行っていることをそのまま踏襲しているためです。
RS232C通信は、通常は生のバイナリデータを送受信するのではなくて、ASCIIコード文字列として送受信を行います。

もちろんRS232C通信でもバイナリデータをそのまま送受信することができないわけではありません。
ですが、一般的には、ASCII文字列で送受信することが多い、ということを踏まえて、そのようにしました。
MYCPU80のために作った8080アセンブラも、ASCIIコードで記述した16進数ファイル(HTX)を出力するようにしました(CPUをつくろう![第182回])。

んでも、ちょっと、むむ、この記述は?と思いまして、そのふたつ前のCPUをつくろう![第180回]を見てみましたら、
おや、バイナリファイルも出力してるじゃないの。
というより、もとはバイナリファイルのみ出力していたところを、HTXファイルも出力するように直した、と書いておりますですねえ。

それなら、なにもわざわざHTXファイルを読み込んで、それをまたバイナリファイルに変換してからHIDで送信しなくても、はじめからバイナリファイルを読み込んで、それをそのまま送信すればいいじゃないの。
むむむ。
自分で作った8080アセンブラなのに、そこのところを完全に忘れてしまっておりまする。
こいつぁいよいよ年貢の納めどきか…とほ。

しかし、しかし、さらにここしばらくの思考の過程をさかのぼってみましたら、むむ、やっぱりHTXファイルでないとだめではないか、というところに行きつきました。
そういうことも、すぐ忘れてしまって、同じところを行きつ戻りつしてしまいます。
こりゃあ、やっぱり、いよいよ…。

そうなのでした。
8080アセンブラで出力されるバイナリファイル(拡張子bin)は、そのままROM焼きできるROMイメージファイルだったのでした。
そのままROMに焼くことができますから、オリジナルのZ80アセンブラでも、ずっとそのようにしてきました。
8080アセンブラで出力されるバイナリファイルには、そういうちゃんとした目的がありました。

ところで。
TK80のSTORE、LOADで送受信するデータは、先頭に開始アドレスと終了アドレス4バイトが付加されています。
オリジナルのモニタプログラムの機能をできるだけ尊重する、という考えからすると、ここはそのままにしておきたいところです。
実際に送受信するところは、バイナリでもヘキサ(ASCIIコード)でもいいのですけれど。

そうすると、8080アセンブラで出力されるバイナリファイルには、その先頭のアドレス部分がありませんから、それをそのままHIDプログラムで送信するわけにはいかなかったのです。
一方、8080アセンブラでバイナリファイルと同時に出力されるHTXファイルは、MYCPU80のUSB送信で利用することを目的にしたものですから、先頭にアドレスがついています。
今回のHID送信プログラムで、入力するファイルをHTXファイルとしたことには、そういう理由があったのでした。
その思考の過程をすっかり忘れておりました。

むむむ。
これは悩ましい問題です。
実は、HID送信プログラムだけではなくて、HID受信プログラムも出来あがっているのです。
ところが、受信プログラムの方は、その思考の過程をすっかり忘れてしまっていたものですから、先頭のアドレス付きで、バイナリで受信したものを、そのまま先頭にアドレスをつけたバイナリファイルの形で保存するようにしてしまいました。
ええ。
それも拡張子をbinにして。

これはまずい、です。
同じbinファイルなのに先頭にアドレスが入っているものと、入っていないものの2種類が存在する、というのは先々混乱のもとになります。
かといって、それならHID受信でも、バイナリから16進数(ASCIIコード)に変換して、HTXファイルとして保存するか、ということなのですけれど…。
確かに、MYCPU80でのUSB受信では、もともとASCIIコードで受信していることですから、当然そのままHTXファイルとして出力しておりました。が…。

ASCIIコードで送受信するのではなくて、バイナリのまま送受信しているのに、Windows側のファイルの読み込みと、書き出しのところだけ、ASCIIに変換する、というのもなんだかなあ、という感じなのですよねえ。
そうなってくるとASCIIに変換する意味はほとんど無い、のですから。
先頭にアドレス情報があるか、無いか、つうだけのために、ASCIIコードファイルを作成するというのもねえ。

むむ。
少し見えてきましたよ。
ちょいと整理してみます。

1)8080アセンブラでは、先頭にアドレス情報をつけて、16進数表記をASCIIコードにしたHTXファイルとバイナリデータのみのBINファイルが作成されます。
2)TK80モニタプログラムのSTORE(SAVE)、LOADでは先頭にアドレス情報が付加されたデータを送受信します。
3)今回のHID送信、HID受信では、データはASCIIコードではなくて、バイナリデータのまま送受信します。
このいずれも、変更はしたくありません。

すると、2)と3)から、先頭にアドレス情報を付加したバイナリファイルが一番フィットすることになります。
しかし、すでに私はもうずっと昔から先頭にアドレス情報を付加しない、生のバイナリファイルをbinという拡張子で使っています。
それを変更したくはありません。

おお、すると、結局これは拡張子の問題、ということになりそうです。
それなら、HID送信、HID受信プログラムでは、先頭にアドレス情報をつけたバイナリデータファイルを扱うこともできるようにして、その拡張子はbin以外の名前にする、ということにすればよいのではありませんでしょうか。

ま、しかしそのあたりにつきましては、それぞれ好みとか、考え方の相違もいろいろあるか、と思います。
応用が利く形としまして、とりあえずはHTXファイルを扱うプログラムと先頭にアドレス情報のついたバイナリファイルを扱うプログラムの2本を作ってしまうのが、手っ取り早いと思います。

むむ。
1)の8080アセンブラも、先頭にアドレス情報を付加した、バイナリファイルを出力できるようにするとよいかもしれません。
これも、現行の8080アセンブラと、アドレス情報を付加したバイナリファイルを出力するアセンブラの2本立てにするか、いっそのことその両方を出力できるように変更してしまうとよいかもしれません。

すでにある、HTXファイルとアドレス情報付きのバイナリファイルとを相互に変換するプログラムも作ってしまうとよいかもしれませんね。

[2011.7.12 以下の部分がわかりにくい表現でしたので書き直しました]
ということで、HTXファイル(文字型ファイル)を読み込んで、それをバイナリに直してHIDで送信する現行のHID送信プログラムのファイル読み込み部分を直して、文字型ファイルもバイナリファイルとして読み込んで、それをASCII2バイト→バイナリに変換してHIDで送信するように変更しました。
こうしておけば、HTXファイルではなくて生のバイナリファイルを送信するプログラムはASCII→バイナリ変換の部分を外すだけでできてしまいますから好都合です。

前回のプログラムとどこが違うかといいますとfgets()で文字ストリームを読むところをfgetc()でバイナリデータとして読み込むようにしたという点が違うだけです。

fgets()関数はバイナリファイルを読み込む関数ではなくて、TXTファイルなどから文字ストリームを読み込む関数でした。
fgets()関数では生のバイナリファイルは読めません。
fgetc()なら、TXTファイルでもバイナリデータとして読み込むことができますし、もちろん生のバイナリファイルもそのまま読み込めます。[2011.7.12ここまで書き直し]

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

実際のプログラムリストを見ていただいたほうが話が早い、と思います。

//hidwr2
//for ND80Z write
//
//10/4/29 4/30 5/4
//
#include <windows.h>
#include <setupapi.h>
extern "C" {
#include "hidsdi.h"
}
#include <iostream.h>
#pragma comment(lib, "hid.lib")
//
        HANDLE handle;
        int hidget();
        FILE *rfp;
        char *fname;
//
void main(int argn,char *args[])
{
        cout << hex;
        fname=args[1];
//file open
        if(!(rfp=fopen(fname,"rb")))
        {
                printf("ファイル名が指定されていないかまたはファイルがみつかりません %s\n",fname);
                return;
        }
// get hid handle       
        if (!hidget()){cout <<"hid device is not found" <<endl;return;}
//
        DWORD nwrite=65;//not 64!
        DWORD nwritn=0;
        unsigned char wbf[65]="\0";
//read file
        int n;
        int d;
        int dx;
        int inbfn;
        int dtbyte=0;
        fseek(rfp,0,SEEK_END);
        inbfn=ftell(rfp);
        fseek(rfp,0,SEEK_SET);
        while(inbfn!=0)
        {
// data set to wbf,max 63 bytes
                for (n=0;n<63;inbfn--)
                {
                if (inbfn==0)break;
// ascii(hex) to bynary
// high 4bits
                d=fgetc(rfp);
                if (d<30)continue;//pass 0d0a
                if (d<=0x39)dx=(d-0x30)*16;
                else dx=(d-0x37)*16;
//low 4bits
                d=fgetc(rfp);
                inbfn--;
                if (d<30)continue;
                if (d<=0x39)dx=dx+(d-0x30);
                else dx=dx+(d-0x37);
//set binary data to wbf[]
                wbf[n+2]=dx;
                n++;
                }
//set number of send data to wbf[1]
                printf("n=%d\n",n);
                if (n==0)break;
                wbf[1]=n;
                if (dtbyte==0)printf("s=%x,e=%x\n",wbf[2]*256+wbf[3],wbf[4]*256+wbf[5]);
                dtbyte=dtbyte+n;
// send data (64bytes)
                if(!WriteFile(handle,&wbf,nwrite,&nwritn,NULL))cout<<"write error"<<endl;
        }
        printf("send data %d(=%x) bytes\n",dtbyte,dtbyte);
	fclose(rfp);
        CloseHandle(handle);
}
//
// get hid handle
int hidget()
   {
        int getmk=0;
        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);
//      
        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_HANDLE"<<endl;continue;}
        HIDD_ATTRIBUTES attr;
        HidD_GetAttributes( handle, &attr ) ;
        if (attr.VendorID==0x4d8 && attr.ProductID==0xa){getmk=1;break;}
     } 
        return getmk;
   }
//end

main()の前で宣言していた
char inbf[4096]="\0";
を削除しました。
今回はfgets()ではなくてfgetc()を使いますから、入力バッファは不要です。
main()の中で変更しているのは、
//read file
から
// send data (64bytes)
までのところです。
最初にfseek()関数とftell()関数を使って、入力ファイルの総バイト数inbfnを得ます。
あとはfgetc()関数で入力ファイルから1バイトずつデータを読み込んで処理するごとにinbfnを−1していきます。
for文でHID出力バッファに63バイト書き込むまで繰り返してから、WriteFile()で送信します。
inbfn=0になったら処理終了です。

今回変更したHIDプログラムを実行した画面です。


CPUをつくろう!第493回(2010.5.4upload)を再編集

PICでUSBを![第66回]
2011.7.12upload

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