PICのUSBホスト機能を動かしてみる(MSD-Class編)

〔CDC-HOST編〕 〔USBデバイス編〕 〔マイコンのトップに戻る〕


前回はPIC24FJ64GB002を使いUSBのホスト側(CDC)の実験を行いました。
MLAのUSBフレームワークを眺めていたら、HIDクラスよりはホスト側だけで済みそうで簡単な感じが、
したので今回は24FJ64GB002を使いUSBのホスト側(mass-storage device class)の実験を行います。
(実際は簡単ではなかった、動作すれば簡単ではあるがぁ...)
マスストレージクラス(mass-storage device class)は、USBメモリやUSBハードディスクを接続し、
データをやりとりする為のUSB規格です
尚、24FJ64GB002はホスト動作の他に、デバイス動作も出来て、OTG(On The Go)も出来る様です。
又、OTGの話は週刊アスキーさんのこちらを参考にして下さい。

《マスストレージデバイスクラスの話》

マスストレージクラスのインターフェイスは、CBI転送((Control-Bulk-Interrupt(CBI) Transport) )と
バルクオンリー転送(Bulk-Only Transport) ですがMLAでは、バルクオンリー転送のみ実装しています
バルクオンリー転送は、コマンド(SCSIコマンド)やステータス、実際のデータ転送まで全てを
バルク転送で行います。

バルクオンリー転送の流れ
この図がバルクオンリー転送における伝送の流れ概念図です。
詳しくはこちらMONOistさんの”UMSの通信概要”や
組み込みUSBホストにおけるマスストレージクラス”(AN1142)を参考にして下さい。
又、SCSI(Small Computer System Interface)コマンドの事は、こちらこちらを参照下さい。

尚、転送速度に転送モード等の話はこちらで、
バルク転送やディスクリプタにエニュメレーシの話はこちらを参照下さい。


《USBホストのファイルについて》

今回使ったプログラムは、
MLA"v2016-11-07"バージョンのUSBフレームワークで
"C:\microchip\mla\v2016_11_07\apps\usb\host\msd_data_logger\firmware\
exp16_pic24fj64gb004_pim.x
"これを使います。

”exp16_pic24fj64gb004_pim.x”の動作は、
・ファイルオープンでLEDが点灯、ファイル書き込みで点滅、ドライブアンマウントで消灯。
・ファイル名"LOG.CSV"を作成し可変抵抗の値を50ms毎に書込み、ボタンを押したら終了。
 (ファイルからの読み込みは無しです)

でえ、変更した点は、
・先ずは、24FJ64GB004用なので24FJ64GB002用にプログラムを変更しました。
・エニュメレーションの実行状態を確認する為のLED1を追加しました。
 (デバイス接続時の"EVENT_ATTACH"を追加)
・ドライブマウントでLED2点灯し、ドライブアンマウントで消灯(又はエラーで消灯)。
・ファイル名"LOG.TXT"を作成し可変抵抗(AN0)の値を500ms毎に書込み、ボタン(RB4)を押したら終了

・最初ドライブがマウントされずにソースファイルを追っかけまわした、
 でぇどうもSCSIコマンド発行時のリトライ回数が足りていないらしく"usb_host_msd_scsi.c"ソースを
 [5]->[100]回に変更した所上手く動作した。

でえ、追加した点は、
・"app_host_msd_data_read.c"を追加し、"TEST.TXT"ファイルを読み込みPC(UART)に表示する機能。

USB(OTG)のPIC24Fファミリー・日本語リファレンスマニュアルはこちらを参照下さい。


↓からUSBホストで使用したデモプログラムをダウンロード出来ます。
host_MSD_demo.zip

ダウンロードファイルはMPLAB Xのプロジェクトファイルの形で提供しますので
そのまま利用出来ると思います。
但し、MPLAB X V4.10MPLAB(R) XC16 C Compiler Version 1.30コンパイラで行っています。


下記ファイルがUSBホスト側で使用するMSDクラスのプログラム一覧です。




MSD.c
MSDクラスのメインデモプログラムです。
app_host_msd_data_logger.c
app_host_msd_data_logger.h
USB(MSD)でUSBメモリに書き込む為のアプリ側サンプルプログラムです。
app_host_msd_data_read.c
app_host_msd_data_read.h
USB(MSD)でUSBメモリから読み込む為のアプリ側サンプルプログラムです。
"app_host_msd_data_logger"と入れ替えて使います。
skUART.c
skUART.c
UART通信を行うドライバです。
PCとUSBシリアル通信を行います。
このファイルの使い方等は、こちらを参照下さい。
skDEBUG.c
skDEBUG.c
"usb_host_msd_scsi.c"と"usb_host.c"プログラムに組み込まれているデバッグ表示機能をONにしたい場合に使用します。
"usb_host_msd_scsi.h"の #define DEBUG_MODE を生かします。
"usb_config.h"の #define DEBUG_ENABLE を生かします。
usb_config.c
usb_config.h
USB組み込みホスト用の周辺機器設定リスト(TPL)の定義と
USBの構成を設定する機能を提供します。
timer_1ms.c
timer_1ms.h
Timer3による間欠タイマー割り込み処理機能を提供します。

ァイル層
fileio.c
fileio.h
ファイルにアクセスする為のライブラリ機能を提供します。
fileio_config.h
fileio_media.h
fileio_private.h
その他のファイルサポートヘッダファイル
”fileio_config.h”で各種ファイル機能をコンパイルするかしないか選択出来ます、これによりファイルサイズを調整可能です。
fileio_lfn.c
fileio_lfn.h
fileio_private_lfn.h
このファイルと入れ替えれば、ロングファイル名に対応できると思いますが今回は未実験です。
但し、PICのリソースをかなり消費するでしょう。
M
S
D
クライアント層
usb_host_msd_scsi.c
usb_host_cdc_scsi.h
アプリケーション層がMSDクライアントドライバにアクセスする為のSCSIインタフェース機能を提供します。

usb_host_msd.c
usb_host_msd.h
USB組み込みホストにMSDクラスのサポートを提供します。





U
S
B
組込み
ホスト

usb_host.c
usb_host.h
全てのデバイスに対してUSB組み込みホストドライバ
サポートを提供します。
クラスサポートは提供しません。
usb_hal_pic24.c
usb_hal_pic24.h
PIC24Fコアをスリープ状態にして、USBアクティビティで
デバイスを起動する様にUSBモジュールを設定します。
usb.h
usb_ch9.h
usb_common.h
usb_hal.h
usb_hal_local.h
usb_host_local.h
usb_struct_queue.h
その他のUSBサポートヘッダファイル

赤色ファイルが、プログラムの変更及び改造&ファイル追加を行った物です。
※ 又、プログラム内のコメントを一部日本語化しています。
※ "usb_config.c/usb_config.h"ファイルは"MPLAB X IDE"で開けなかったので、
  "TerPad"で[SHIFT-JIS]で上書き保存を行ったところ開ける様になりました。

ヒープ領域

USB組み込みホストスタックは、メモリ空間を動的(ダイナミック)にメモリの割り付けと解放を行う様に
プログラミングされています、なのでヒープ領域を割り当ててやらないとエラーが出ます。

ヒープ領域の割り当て画面
IDEのプロジェクトプロパティを開き、"xc16-ld"をクリックし、オプション画面を表示させます。
オプションカテゴリは"General"で"Heap size"の項で"2000"程入力しましょう。
但し、この操作はMPLAB X v4.10でXC16 v1.30時での操作となります。
でもぉ、今回はプロジェクトファイルで提供するので設定は出来ているはずです。
"ヒープ領域"と"メモリの割り付けと解放"の記事はこちらを参照して下さい。

コンパイラの最適化

制限事項:
「このデモはサイズが大きい為、このデモを特定のハードウェアプラットフォームで動作させるには、
 コンパイラで最適化を有効にする必要があります。
 最適化は、全てのバージョンのコンパイラで利用できるわけではありません。」
っとぉ、"USB Library Help"に書いて有る。
もぉ、コンパイルは出来るがぁ、リンクが出来なくてぇハマリまくったぞぉ.....

コンパイラの最適化画面
IDEのプロジェクトプロパティを開き、"xc16-gcc"をクリックし、オプション画面を表示させます。
オプションカテゴリは"Optimizations"で"Optimization level"の項で"1"を入力しましょう。
但し、この操作はMPLAB X v4.10でXC16 v1.30時での操作となります。
でもぉ、今回はプロジェクトファイルで提供するので設定は出来ているはずです。

《USBホストの回路について》

USBホストの回路
回路は前回のCDCホスト回路にスイッチと半固定抵抗を追加しただけです。
回路の電源は3.3Vで、USBのVBUS用電源が5.0Vなので両電源を用意する必要が有ります。
PICの内蔵レギュレータ用に、"10uF/16V low-ESR capacitor"ですが
無いので電解コンデンサを使いました。
又、VBUSを安定化させる電解コンデンサ150uFも持っていないので100uFで行っています。

超小型USBシリアル変換モジュール
PICのUART通信をUSBでPCに接続してくれるモジュールです、
モジュールのピンは図左側から[5V][GND][TXD][RXD]の順で、[5V]ピンは使用しません。
このモジュールの少し詳しい話は、こちらを参照下さい。

USBコネクタDIP化キット(Aメス)
USB-TypeA(メス)コネクタをブレッドボードで使用出来る様にするキットです。
注意は、固定用の半田付けピンが2.54/1.27mmの両方あるのでよぉ〜くぅ見て見て。
無論キットなので半田付け必須です、
最近半田付けがぁチョットぉ辛い、メガネタイプの拡大鏡が欲しいかもぉ (^0_0^)

尚、OTG機能を利用する場合は、”マイクロBメスUSBコネクタDIP化キット”を使いましょう。

(USBホスト回路の動作1)

”動作1”はUSBメモリにファイルを作成し半固定抵抗の値を書き込みます。
(ファイルが作成されていれば上書き保存となります。)
使うファイルは、"app_host_msd_data_logger.c/app_host_msd_data_logger.h"です。

@ コンパイルしPICに書き込みます。
  (コンパイルした結果"69%"とかなりデカい)

A 電源を入れたら、[LED1]が3秒間点灯しPICが起動した事を知らせます。

B さぁ、USBメモリを挿して下さい。

C [LED1]が点灯しデバイスの接続を知らせます、デバイスが外れるまでは点灯しています。
  もし、点灯後消灯した場合(もしくは点灯しない)はエニュメレーションが失敗しています。
  エニュメレーションの話は、[picfun(電子工作の実験室)]さんのこちら「USBのプラグ&プレイ」
  参考にしましょう、又、「CDCクラスでパソコンとデータの送受信を行う」も併せて読みましょう。

D その後、すぐにUSBメモリがマウントされ[LED2]が点灯します。
  (ファイルが作成されずオープンエラーになれば[LED2]は消灯します)

E ファイルが作成("LOG.TXT")されファイルに可変抵抗値が500ms毎に書き込みが始まります。
  (この間に可変抵抗値を回して見て下さい)

F スイッチを押しましょう。
  ファイルがクローズされUSBメモリがアンマウントされます、[LED2]が消灯します。

G USBメモリを外しましょう、[LED1]が消灯します。
  (たぶん、USBメモリにはアクセスしていないのでぇ安全に取り外せると思います)

(USBホスト回路の動作2)

”動作2”はUSBメモリからファイルを読み出してPCに送ります。
使うファイルは、"app_host_msd_data_read.c/app_host_msd_data_read.h"です。
(”動作1”のファイルと入れ替えてコンパイルしますよ、念の為)

@ コンパイルしPICに書き込みます。

A USBメモリにPCのメモ帳辺りでファイル名"TEST.TXT"でファイルを作成して置きます。
  ルートディレクトリ以外にも"KIMU/TEST.TXT"とサブディレクトリも可能だが、
  サブディレクトリは8文字の大文字のみ可能です。

B "超小型USBシリアル変換モジュール"をPCに接続します。

C PCで"Tera Term"等のシリアル端末を起動させます。(操作方法は省略します)
  ボーレートは"19200bps"に設定して下さい。

D PICの電源を入れたら、[LED1]が3秒間点灯しPICが起動した事を知らせます。

E さぁ、USBメモリを挿して下さい。

F [LED1]が点灯しデバイスの接続を知らせます、デバイスが外れるまでは点灯しています。
  もし、点灯後消灯した場合(もしくは点灯しない)はエニュメレーションが失敗しています。
  エニュメレーションの話は、[picfun(電子工作の実験室)]さんのこちら「USBのプラグ&プレイ」
  参考にしましょう、又、「CDCクラスでパソコンとデータの送受信を行う」も併せて読みましょう。

G その後、すぐにUSBメモリがマウントされ[LED2]が点灯します。
  (ファイルオープンエラーになればPCに"FILE OPEN ERROR!"が表示され、[LED2]は消灯します)

USBホスト回路の動作2結果表示 H ファイルの内容が読み出されPCに表示されます。
  (左の様なかんじぃ)

I 表示が終わればファイルがクローズされ、
  USBメモリがアンマウントされ、[LED2]が消灯します。



J USBメモリを外しましょう、[LED1]が消灯します。
  (たぶん、USBメモリにはアクセスしていないのでぇ安全に取り外せると思います)

(関数等の説明)

ファイル作成時の日付は、"GetTimestamp()"関数で行われるが今回は時刻を得る手段が無いので
"2013/1/5 8:10:40"で固定となっております。

"fileio"については、”MLAのFrameworkでfileioを試してみる”を参照しましょう。

"skUARTlib"については、こちらを参照しましょう。

タイマー

タイマー関数は、"bcd/timer_1ms.c"と"bcd/timer_1ms.h"のファイルとなります。

ans = TIMER_SetConfiguration( TIMER_CONFIGURATIONS configuration )
この機能は、Timer3を初期化します。
システムクロックは32MHzで、1ms毎の割り込みを生成します。

 引数  : TIMER_CONFIGURATION_1MS/TIMER_CONFIGURATION_OFF
 bool ans : true  - 成功
       false - 失敗

ans = TIMER_RequestTick ( TICK_HANDLER handle , uint32_t rate )
この機能は、定期的なイベントの発生を要求します。
イベント発生時に指定した関数を実行するが、割り込み処理の中で実行されるので、
指定する関数の処理は短い物を作る事をお勧めする。

 TICK_HANDLER handle : タイムイベントが発生した時に呼び出される関数を指定します
 uint32_t    rate  : 指定した時間毎にベントが発生します。(1ms x rate)
 bool      ans   : true  - 成功
              false - 失敗

TIMER_CancelTick(TICK_HANDLER handle)
この機能は、イベントの発生をキャンセルします。

 TICK_HANDLER handle : タイムイベントが発生した時に呼び出されていた関数を指定します

転送の実行

errorCode = USBHostMSDRead( *address, 0, commandBlock, 10, dataBuffer,
                 mediaInformation.sectorSize )

この関数は、関数USBHostMSDTransfer()を利用して大容量記憶装置の読み取りを開始します。

 uint8_t deviceAddress    : デバイスのアドレス
 uint8_t deviceLUN      : アクセスするデバイスのLUN(Logical Unit Number)
 uint8_t *commandBlock   : CBW用のコマンドブロックへのポインタ
                 実行する動作(SCSI命令)をデバイスに伝える為の最大16バイトの
                 ブロックです。
 uint8_t commandBlockLength : コマンドブロックの長さ
 uint8_t *data         : 読み取ったデータを格納するバッファへのポインタ
 uint32_t dataLength     : データバッファのバイトサイズ
 errorCode
  USB_SUCCESS         : リクエストは正常に開始されました
  USB_MSD_DEVICE_NOT_FOUND: 指定されたアドレスを持つデバイスがありません
  USB_MSD_DEVICE_BUSY    : 転送を実行する為のデバイスが適切な状態ではありません
  USB_MSD_INVALID_LUN    : 指定されたLUNが存在しません

※ 使い方の例は、"usb_host_bsd_scsi.c"の"USBHostMSDSCSISectorRead()"関数を見ましょう。

errorCode = USBHostMSDWrite( *address, 0, commandBlock, 10, dataBuffer,
                 mediaInformation.sectorSize )

この関数は関数USBHostMSDTransfer()を利用して大容量記憶装置への書き込みを開始します。

 uint8_t deviceAddress    : デバイスのアドレス
 uint8_t deviceLUN      : アクセスするデバイスのLUN(Logical Unit Number)
 uint8_t *commandBlock   : CBW用のコマンドブロックへのポインタ
                 実行する動作(SCSI命令)をデバイスに伝える為の最大16バイトの
                 ブロックです。
 uint8_t commandBlockLength : コマンドブロックの長さ
 uint8_t *data         : 書き込むデータを格納するバッファへのポインタ
 uint32_t dataLength     : データバッファのバイトサイズ
 errorCode
  USB_SUCCESS         : リクエストは正常に開始されました
  USB_MSD_DEVICE_NOT_FOUND: 指定されたアドレスを持つデバイスがありません
  USB_MSD_DEVICE_BUSY    : 転送を実行する為のデバイスが適切な状態ではありません
  USB_MSD_INVALID_LUN    : 指定されたLUNが存在しません

※ 使い方の例は、"usb_host_bsd_scsi.c"の"USBHostMSDSCSISectorWrite()"関数を見ましょう。

ans = USBHostMSDTransferIsComplete( *address, &errorCode, &byteCount )
この機能は、送受信の転送状態を調べる為に呼び出されます。
転送が成功したか如何かを確認するには、返されたエラーコードを確認する必要があります。
最後の転送が"USBHostMSDRead"だった場合、byteCountは受信したバイト数を返します。
最後の転送が"USBHostMSDWrite"だった場合、byteCountは送信されたバイト数を返します。

 uint8_t deviceAddress : デバイスアドレス
 uint8_t *errorCode   : 最後の転送からのエラーコード
 uint32_t *byteCount  : 転送バイト数
 bool ans : true - 転送が完了しました、errorCodeは有効です
      false - 転送は完了していません、errorCodeは無効です

SCSIコマンドの"TEST UNIT READY"を送る例
"USBHostMSDRead"を発行する事で、コマンド転送・データIN・ステータス転送が実行されます。
  // コマンドブロックにTEST UNIT READYパラメータを入力します。
  commandBlock[0] = 0x00;     // 操作コード
  commandBlock[1] = 0;        // Reserved
  commandBlock[2] = 0;        // Reserved
  commandBlock[3] = 0;        // Reserved
  commandBlock[4] = 0;        // Reserved
  commandBlock[5] = 0x00;     // Control

  error = USBHostMSDRead( *address, 0, commandBlock, 6, inquiryData, 0 );
  if (!error)
  {   // 転送の完了を待ちます。
      while (!USBHostMSDTransferIsComplete( *address, &error, &byteCount ))
      {
          USBHostTasks();
          USBHostMSDTasks();
      }
  }

《その他》

実験時の写真
実験時の回路写真です。
CDC時の回路にスイッチと半固定抵抗を追加しただけですね。
この写真ではブレッドボードを反対で使っているので、上の実態配線図とは電源が逆です。

試したUSBメモリは、
 「GreenHouse PicoDUAL USB Device 16GB」(上の写真)
 「BUFFALO USB Flash Disk USB Device 4GB」
壊れたPCから取り出したハードディスクをUSBケースに収めた物はなぜかぁ動作せずでした。
(PCでは動作する、コンフィグレーションディスクリプタはUSBメモリと同じ様に思えるのだがぁ...)
手持ちの「Canon MEDIA STORAGE M30」はハードディスク(30GB)を内蔵している、動作した。
最初から作られている外付けUSBハードディスクは動作するかもね。




【きむ茶工房ガレージハウス】
Copyright (C) 2006-2019 Shigehiro Kimura All Rights Reserved.