USBシステム: ソフトウェアドライバの作成

jp

目的とするUSBデバイス(ゲームPAD)が動作するシンプルなドライバを作成します。

USBホストCoreの初期設定

ゲームPADを接続してUSBホストCoreからバスリセットを行うと、USBデバイスはデフォルトステートに移行ます。また、USBホストCoreからはCPUに対して接続イベント(CONNECTION_EVENT)インタラプトが発生します。この状態で、USBデバイスのデフォルトアドレス0に対してアクセスが可能になります。

usb_pic4

デフォルトステートまでの処理

USBデバイスとのデータ転送

USBホストCoreとUSBデバイスは、IN/OUT/SETUPのTransactionを用いてデータ転送を行います。
送信(OUT Transaction)
USBホストからUSBデバイスにデータを転送します。USBホストCoreのUSB_TX_FIFO_DATAに送信するデータを設定、HOST_TX_TRANS_REGレジスタのTRANSACTION_TYPEにOUTDATA0_TRANSまたはOUTDATA1_TRANSを設定した後、
HOST_TX_CONTROL_REGのTRANS_REQ_BITを1に設定すると、OUT Transactionを実行します。

usb_pic5

OUT Transactionフロー

受信(IN Transaction)
USBデバイスからデータを受信します。HOST_TX_TRANS_REGレジスタのTRANSACTION_TYPEにIN_TRANSを設定後、HOST_TX_CONTROL_REGのTRANS_REQ_BITを1に設定すると、USBデバイスにIN Tokenが送信されます。USBデバイスからデータを受信するとUSB_INTERRUPT_STATUS_REGのTRANS_DONE_BITが1になります。USB_RX_FIFO_DATA_COUNT_LSBの値を読み出し、その回数だけUSB_RX_FIFO_DATAのデータをリードします。

usb_pic6

IN Transactionフロー

SETUP Transaction
デバイスリクエスト用のデータをUSB_TX_FIFO_DATAに設定、HOST_TX_TRANS_REGレジスタのTRANSACTION_TYPEにSETUP_REANSを設定後、HOST_TX_CONTROL_REGのTRANS_REQ_BITを1に設定すると、SETUP Transactionを実行します。usb_pic7

ディスクリプタの獲得

どのようなデバイスがUSBバスに接続されているかは、そのデバイスの標準ディスクリプタを獲得する事で判断できます。

  • デバイスディスクリプタ
  • コンフィグレーションディスクリプタ
  • インターフェースディスクリプタ
  • HIDディスクリプタ
  • エンドポイントディスクリプタ

各ディスクリプタは、コントロール転送でUSBデバイスから獲得します。HID(Human Interface Device)ディスクリプタは、HIDクラス独自のディスクリプタです。
各ディスクリプタの詳細はこちら
各ディスクリプタから、USBデバイス(ゲームPAD)についての以下の情報が得られます。

  • コンフィグレーション数は1
  • インターフェース数は1
  • エンドポイント数は1(除くエンドポイント0)
  • インターフェースクラスは3(HID)
  • エンドポイントのアドレスは1
  • エンドポイント1の転送はインタラプトIN転送
  • エンドポイント1の最大パケットサイズは4

インタラプト転送
ディスクリプタ情報から、USBデバイス(ゲームPAD)のデータは、エンドポイント1へのインタラプト転送で獲得できることがわかりました。CPUのタイマー割り込みを用いて、一定間隔のインタラプト転送をUSBデバイスに対して発行し、その時のゲームPADの状態を獲得します。

usb_pic10

インタラプト転送フロー

ゲームPADデータの獲得

ゲームPADのデータ(どのボタンが押されたか、どの方向キーが押されたか)は、インタラプト転送で獲得したデータに格納されています。データフォーマットは、HIDクラスのREPORTディスクリプタから得られます。今回使用したゲームPADは、4ByteのデータとしてPADの状態がホストに返されます。PADの状態は次のフォーマットで格納されています。

Byte フィールド名 説明
0 x方向キー 左: 0センター:0x7f, 右: 0xff
1 y方向キー 上: 0センター:0x7f, 下: 0xff
2 ボタン0グループ 各ビットに8個のボタンの状態をアサイン OFF:0, ON:1
3 ボタン1グループ ビット0と1に、2個のボタンの状態をアサイン OFF:0, ON:1

テストプログラム

簡単なテストプログラムで動作の確認を行いました。

  • USBデバイスのアドレスは5に設定
  • インタラプト転送は、CPUのタイマ割り込みを用いて1秒間隔で実行
  • FPGAからのIRQ1インタラプトでUSBホストCoreのFIFOをリードし、ゲームPAD用の構造体にデータを格納

プログラム(抜粋)

メイン

int main(void){
     int i;
     char buf[64];

     puts ("USB Test\n");

     intr_disable();
     int_config();
     timer1_config(); // インタラプト転送用タイマー設定
     intr_enable();

     usb_init(); // USBホストCoreの初期化

     while(1) {
         //インタラプトハンドラでインタラプト転送を実行;
     }
     return 0;
 }

タイマー設定

void timer1_config() {
     // TMU1を使用して1秒間隔でインタラプトを発生
     // pck = 33MHz = 30ns, pck/1024 = 32.5KHz = 30us
     TMU.TSTR0.BYTE = 0x00; // stop timer
     // set interrupt priority for TMU0(bit28:24),TMU1(bit20:16)
     INTC.INT2PRI0.LONG = 0x0c0c0000; // bit 28-24 (0,1 = mask)
     INTC.INT2MSKCR.LONG = 1; // TMU0-2
     TMU1.TCOR = get_timer_count(1000000000); // timer constant (1s)
     TMU1.TCNT = get_timer_count(1000000000);
     TMU1.TCR.WORD = 0x0024; // timer control UNF=0,UNIE=1,CKEG=00 TPSC=100
     TMU.TSTR0.BYTE = 0x02; // start tmu1 
 }

インタラプトハンドラ(タイマー割り込み)

void INT_TMU1_TUNI1() {
     unsigned int x;
     puts ("TMU1 INT\r\n");
     // clear TCR0.UNF
     TMU1.TCR.WORD = TMU1.TCR.WORD & ~0x0100;
     x = 1;
     while (x) x = INTC.INT2A0.LONG & 1; // bit0 : TMU0-2
     // start usb interrupt transfer
     if (usb_state == USB_NORMAL) interrupt_in_transfer_nb(5, 1); // アドレス5, エンドポイント1へのインタラプト転送
 }

 void interrupt_in_transfer_nb(int adrs, int endp) {
     USB_TX_ADDR_REG.LONG = adrs;
     USB_TX_ENDP_REG.LONG = endp;
     USB_TX_TRANS_TYPE_REG.LONG = 1; // IN TRANS
     int_trans_done = 0;
     USB_CONTROL_REG.LONG = 1;
     puts("Start INT_TRANS\r\n");
 }

インタラプトハンドラ(FPGA IRQ1割り込み)

void INT_IRL_LEVEL11() {
     // INTR IRQ1
     unsigned int d;
     char buf[64];
     d = USB_INTERRUPT_STATUS_REG.LONG;
     puts("INT detected\r\n");
     if (d & 0x1) {
         puts("TRANS_DONE_BIT\r\n");
         USB_INTERRUPT_STATUS_REG.LONG = 1; // clear status
         int_trans_done = 1;
         if (usb_state == USB_NORMAL) interrupt_in_transfer_get(5, 1); // データ獲得
     } else if (d & 0x2) {
         puts("RESUME_INT_BIT\r\n");
         USB_INTERRUPT_STATUS_REG.LONG = 2;
     } else if (d & 0x4) {
         puts("CONNECTION_EVENT_BIT\r\n");
         USB_INTERRUPT_STATUS_REG.LONG = 4;
         int_connect_done = 1;
     } else if (d & 0x8){
         puts("SOF_SENT_BIT\r\n");
         USB_INTERRUPT_STATUS_REG.LONG = 8;
     } else {
         sprintf(buf, "UNKOWN INT %x \r\n", d);
         puts(buf);
     }
 }

 void interrupt_in_transfer_get(int adrs, int endp) {
     int i, fnum;
     char buf[64];
     unsigned int status;

     // buffer clear
     for (i=0; i < 4; i++) joypad_status.PACKED[i] = 0;
     while (!int_trans_done) ;
     puts("interrupt in trans done\r\n");

     status = USB_RX_STATUS_REG.LONG;
     sprintf(buf, "INT RX STATUS %x \r\n", status);
     puts(buf);
     // get IN data
     fnum = USB_RX_FIFO_DATA_COUNT_LSB.LONG;
     sprintf(buf,"interrupt in length = %d\r\n",fnum);
     puts(buf);
     for (i = 0; i < fnum; i++) {
         if (i <4) joypad_status.PACKED[i] = USB_RX_FIFO_DATA.LONG; // 受信データを構造体に格納
     }
     // status
     sprintf(buf, "X = %x\r\n",joypad_status.BYTE.x);
     puts(buf);
     sprintf(buf, "Y = %x\r\n",joypad_status.BYTE.y);
     puts(buf);
     sprintf(buf, "B1 = %x, %d %d\r\n",joypad_status.BYTE.b1.BYTE,
     joypad_status.BYTE.b1.BIT.button0,
     joypad_status.BYTE.b1.BIT.button1);
     puts(buf);
     sprintf(buf, "B1 = %x, %d %d %d %d %d %d %d %d\r\n",joypad_status.BYTE.b0.BYTE,
         joypad_status.BYTE.b0.BIT.button0,
         joypad_status.BYTE.b0.BIT.button1,
         joypad_status.BYTE.b0.BIT.button2,
         joypad_status.BYTE.b0.BIT.button3,
         joypad_status.BYTE.b0.BIT.button4,
         joypad_status.BYTE.b0.BIT.button5,
         joypad_status.BYTE.b0.BIT.button6,
         joypad_status.BYTE.b0.BIT.button7);
     puts(buf);

 }

ゲームPAD用データ構造体

typedef union {
     unsigned char PACKED[4];
     struct {
         unsigned char x; // left: 0, center:0x7f. right: 0xff
         unsigned char y; // up: 0, center:0x7f. down: 0xff
         union {
             unsigned char BYTE;
             struct {
                 unsigned char button0 : 1;
                 unsigned char button1 : 1;
                 unsigned char button2 : 1;
                 unsigned char button3 : 1;
                 unsigned char button4 : 1;
                 unsigned char button5 : 1;
                 unsigned char button6 : 1;
                 unsigned char button7 : 1;
             } BIT;
         } b0;
         union {
             unsigned char BYTE;
             struct {
                 unsigned char button0 : 1;
                 unsigned char button1 : 1;
                 unsigned char : 6;
             } BIT;
         } b1;
     } BYTE;
 } st_joypad;

タイトルとURLをコピーしました