PICをUSBメモリにするためのMSDクラスを使ってみる

投稿日 2016/03/31

Microchip®社のPIC用USBフレームワークのデバイスクラスのひとつであるMSDクラスを使ってみました。

 

FATファイルシステムと一緒に使って、PICにつないだSDカード・メモリをUSBメモリのようにプラグアンドプレイで、windows側から大容量記憶装置(外部ストレージ・ドライブ)として認識させるものです。


今回は認識されるかどうかの実験のみですので、関係のない回路やソフトは組み込んでいません。

 

参考にしたのは文献: 技術評論社 後閑氏著「改訂新版 PICで楽しむUSB機器自作のすすめ」です。これにGPSロガー(GPS_Logger3)としてMSDクラスの使い方が説明されています。後述しますがFSconfig.h, HardwareProfile.h, usb_config.h, usb_descriptors.cはGPS_Logger3のものを流用しました。またUSBフレームワークやファイルシステムが含まれるMicrochip Application Libraryは同書で提供されているものを使いました。

 

MSDやFATファイルシステムの処理はすべてMicrochip社が用意してくれてますので、main.cを一定のルールに従って作るだけで、簡単に実現します。

 

最低必要なハードウェア

 

USBをサポートするPICマイコン:

      今回は24FJ64GB002を使用

      FATファイルシステムやUSBフレームワークを使うのである程度大きいメモリ

  が必要です。24FJ64GB002は64KBです。(秋月電子で入手)

 

SDカードスロットをSPIでPICに繋ぐ:

     マイクロSDのスロットを使用しました。(秋月電子で入手)

 

     今回はLCD表示などのMSDに直接関係ないものは組み込んでいません。

 

ICSP回路

 

     プログラムを書き込み機(pickit3)で書き込むために必要です。

 

リセット回路

 

     PICをリセットしプログラムの実行をやり直します。

 

 

回路図

 

簡単にするため、不要なものは極力省いています。

 

PIC 24FJ64GB002にSPIインターフェースのためのGPIOでSDカードスロットをつなぎます。

USBコネクタを配線します。

ICSPを配線します。(書き込み機はpickit3を使用)

電源には3.3Vを供給します。

USB_MSD_01.jpg

USB MSDのための必要最小限の回路

PIC 24FJ64GB002を中心に、SDカード、USBコネクタ、

ICSP、リセットボタンを配置

電源は3.3V供給

(水魚堂の回路図エディタで描画)

 

開発環境

 

文献のソースをなるべくそのまま使いたいので、レガシーMPLABのV8.92を使用しました。コンパイラはC30です。

 

PICへのプログラミング(書き込み)はpickit3を使用しました。

 

microchip社のApplication Libraryが必要ですが、文献付属のCDに含まれるUSB_Bookのmicrochipフォルダから下をまるごとコピーしておきます。

 

USBディスクリプタは文献のサンプルプログラムGPS_Logger3のものをそのまま使いました。(GPS_Logger3はMSDクラスとCDCクラスを同時に使うサンプルなのでCDCクラスの定義も含まれていますが、じゃまにはならないので今回はそのまま使用しました。)

 

USB_Book(文献付属のCDに含まれる)のフォルダ構成概略

 

USB_Book: 

      ・・・

      GPS_Logger3 <- MSD使用のサンプル・プログラム一式

      Microchip: <- Application Libraryの一部

           Include

           MDD File System <- MSDクラスとファイルシステム

           USB    <- USBフレームワーク


ソフトの構成

 

MPLABのプロジェクトファイル構成

 

今回は24FJ64GB002_USB_MSDという名前のプロジェクトにしました。

USB_MSD_02.jpg

プロジェクト 24FJ64GB002_USB_MSDのファイル構成

 

 GPS_Logger3からコピーするものは関係ないものも含まれますが、今回はそのまま使用しています。(usb_descriptor.c内のCDCクラスのための定義など) 

 

以下のようにmain.cのみ作りますが、そのほかはGPS_Logger3からのコピーと、microchipのApplication Libraryへのリンクです。

 

FSIO.c:

          USB_Book/microchip/MDD File SystemのFSIO.cにリンク

main.c:

          ユーザ作成。一定のルールに従った形だけのものです。

SD-SPI.c:

          USB_Book/microchip/MDD File SystemのSD-SPI.cにリンク

usb_descriptors.c:

          USB_Book/GPS_Logger3からコピー

usb_device.c:

          USB_Book/microchip/USB/usb_device.cにリンク

usb_function_msd.c:

          USB_Book/microchip/USB/MSD Device Drive/usb_function_msd.cにリンク

FSconfig.h:

          USB_Book/GPS_Logger3からコピー

HardwareProfile.h:

          USB_Book/GPS_Logger3からコピー

usb_config.h:

          USB_Book/GPS_Logger3からコピー

 

MPLAB build optionsのDirectries and Search PathのInclude Search Pathに

     Program Files/Microchip/MPLAB C30/support/PIC24F/h

     USB_Book/Microchip/Include

     .mcpファイルのある開発フォルダ自身

を設定しておきます。

 

MPLAB build optionsはプロジェクト名を右クリックすると出ます。


 

プログラミングの手順

 

main.cでやることを書きます。FATファイルシステムや、MSDの処理に関して作り込は必要ありません。

 

PICのUSBクロックを48MHzにする

 

クロック・コンフィグレーションはいろいろな構成が可能ですが、今回はFRC(PIC内蔵の8MHz発振器)を使い、1/2してPLLに4MHzを注入し、PLLで24倍の96MHzにし、それの1/2でUSB用の48MHzを得ています。CPUクロックはFRCPLLとし32MHzです。CPUクロックは32MHzでなければならないということはありません。

 

PICのコンフィギュレーション設定の例 赤い部分がクロック設定関連です。


_CONFIG1(WINDIS_OFF & WDTPS_PS32768 & FWPSA_PR128 & FWDTEN_OFF & ICS_PGx1 & GWRP_OFF & GCP_OFF & JTAGEN_OFF)
_CONFIG2(POSCMOD_NONE & I2C1SEL_PRI & OSCIOFNC_ON & FCKSM_CSECMD & IOL1WAY_OFF & FNOSC_FRCPLL & PLL96MHZ_ON & PLLDIV_DIV2 & IESO_OFF & SOSCSEL_SOSC)
_CONFIG4(RTCOSC_LPRC)

 

POSCMOD_NONE: 外付けの(HS, HX, HEなど)発振回路は使いません

FNOSC_FRCPLL: CPUクロックはPLL 96MHzを1/3した32Mを使用します

PLL96MHZ_ON:  PLLを動作させます。

PLLDIV_DIV2:  PLLに注入するクロックを4MHzにするために8MHzを1/2します。

 

USBにはPLLの96MHzを1/2したものが供給されます。

 

 

MSD処理ルーチンへのエントリの定義を行う

 

文献のサンプルのままです。ファイルシステムが使う処理ルーチンへのエントリです。

 

/* USB MSDクラス用関数呼び出しポインタ */
LUN_FUNCTIONS LUN[MAX_LUN + 1] = 
{
      {
      &MDD_SDSPI_MediaInitialize,  // メディア初期化
      &MDD_SDSPI_ReadCapacity,   // 容量読み出し
      &MDD_SDSPI_ReadSectorSize,   // セクタサイズ読み出し
      &MDD_SDSPI_MediaDetect,    // メディア検出
      &MDD_SDSPI_SectorRead,    // 1セクタ読み出し
      &MDD_SDSPI_WriteProtectState,  // 保護状態読み出し
      &MDD_SDSPI_SectorWrite    // セクタ書き込み
      }
};

 

SCSI処理のためのコマンドテーブルを用意する。

 

文献のサンプルのままです。

 

/* USB MSDクラスのSCSI INQUIRYコマンド応答用データ */
const ROM InquiryResponse inq_resp = {
      0x00,  // peripheral device is connected, direct access block device
      0x80,  // RMB removable
      0x04,  // ISO version=0, ECMA version=0, ANSI version=4=SPC-2
      0x02,  // response is in format specified by SPC-2
      0x20,  // Additional Length(n-4) = 36-4=32= 0x20
      0x00,  // sccs etc.
      0x00,  // etc
      0x00,  //bque=1 and cmdque=0,indicates simple queueing 00 is obsolete,
    // but as in case of other device, we are just using 00
    // 00 obsolete, 0x80 for basic task queueing
      {'T','.','G','o','k','a','n',' '}, // T10 Vendor ID 8cha 
      {'G','P','S',' ','U','S','B',' ','L','o','g','g','e','r',' ',' '},//product ID 16cha
      {'0','0','0','1'} // product revison 4cha
};

 

SDカードのSPIインターフェースのためのI/Oポートを設定する。

 

SDIの信号をピンに割り付けています。

 

 /* SD CARD SPIピン割付 */
 RPINR20bits.SDI1R = 15;    // SDI1 -> RP15
 RPOR6bits.RP13R = 7;    // SDO1 -> RP13
 RPOR7bits.RP14R = 8;    // SCK1OUT -> RP14 

 

GPS_Logger3のやり方をそのまま使っていますが、SDカードを繋ぐピンによって自由に割り付けできます。これを変更した場合、HardwareProfile.hもチャックする必要があります。

 

ファイルシステムを初期化する。

 

FSInit();

 

これを一回実行しておくだけです。

 

USBフレームワークを初期化する

 //USBフレームワーク初期化
 USBDeviceInit();     // USBデバイス初期化
 USBDeviceAttach();     // USBデバイスアタッチ許可

 

これを一回実行しておくだけです。

 

メインループ(while(1))内の処理

 

USBからの処理要求があるかどうかチェックし、あればMSDTasks()をコールする。なければユーザ処理を行う。(今回はなにもしない)


 while(1){
       /********** USB接続中の場合 *************/
       if((USBDeviceState >= CONFIGURED_STATE) &&

                                       (USBSuspendControl != 1)){ //USB
              MSDTasks();         // ファイルシステムステート更新
   
       /********** USB接続なしの場合 ***********/
       } else {
              //nothing to do!

              //ユーザ処理を書く
       } //USB
} //while

 

本来は、//ユーザ処理を書くの部分に温度計測や、データのSDカードへの記録などのアプリケーション部分を組み込みます。 

 

USB Callback Functionsを組み込む

 

GPS_Logger3のmian.cからコピーしました。

 

関数の中身は省略しますが、microchip社から提供されるもので以下のような関数です。 USBCBInitEP(void)にあるMSDの初期化USBMSDInit();のみを残しCDCの初期化は削除します。

 

//************************************************
//************** USB Callback Functions ***************
// ************************************************

void USBCBSuspend(void)
void USBCBWakeFromSuspend(void)
void USBCB_SOF_Handler(void)
void USBCBErrorHandler(void)
void USBCBCheckOtherReq(void)
void USBCBStdSetDscHandler(void)
void USBCBInitEP(void)
void USBCBSendResume(void)
BOOL USER_USB_CALLBACK_EVENT_HANDLER(USB_EVENT event, void *pdata, WORD size)

 

ただし、USBCBInitEP()だけは、USBMSDInit();が追加されています。

 

/*********************************************************
 * Function:        void USBCBInitEP(void)
 *********************************************************/
void USBCBInitEP(void)
{
    #if (MSD_DATA_IN_EP == MSD_DATA_OUT_EP)
        USBEnableEndpoint(MSD_DATA_IN_EP,USB_IN_ENABLED|USB_OUT_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP);
    #else
        USBEnableEndpoint(MSD_DATA_IN_EP,USB_IN_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP);
        USBEnableEndpoint(MSD_DATA_OUT_EP,USB_OUT_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP);
    #endif

    USBMSDInit();   // ファイルシステム初期化
}

windowsにつないでみる

コンパイルしてエラーが無ければpickit3で書き込みます。

 

書き込み後、実行が開始されても表面的には何も起こりませんが、USBコネクタにUSBケーブルを接続してwindows PCとつなぐと、デバイス・マネージャのユニバーサル・シリアル・バスコントローラーにUSB Composite DeviceとUSB 大容量記憶装置が自動的に追加されます。

 

また、コンピューターの下に、F:やE:のようなディスクドライブが追加されます。

 

後は通常のディスクドライブのようにアクセス可能です。これは市販のUSBメモリを差したときと同じ動きです。

 

USBケーブルを抜くと、ユーザアプリケーション部分に戻ります。

 

今回はFATファイルシステムをFSInit()で初期化しただけですが、本来は上記の//ユーザ処理を書く部分にユーザ独自のアプリケーションを組み込みます。大事なのはMSDTasks();をwhile(1)ループの内側で毎回実行されるようにすることです。

 

ファイルシステムはFSInit()の他にFSopen(), FSRead(),FSwrite()などの関数がありSDカードへのファイル作成、読み書き等が行えます。後述の参考アプリケーションノートが参考になります。

 

 

 

main.cの例

 

コンフィギュレーション設定は関係ないものも含まれています。

 

レガシーMPLAB用

 

/***********************************************************
* Microchip USB MSD test prog.
************************************************************/
#include <p24fj64gb002.h>
#include "USB/usb.h"
#include "HardwareProfile.h"
#include "MDD File System/SD-SPI.h"
#include "MDD File System/FSIO.h"
#include "USB/usb_function_msd.h"

/******* コンフィギュレーション設定 *****/
_CONFIG1(WINDIS_OFF & WDTPS_PS32768 & FWPSA_PR128 & FWDTEN_OFF & ICS_PGx1 & GWRP_OFF & GCP_OFF & JTAGEN_OFF)
_CONFIG2(POSCMOD_NONE & I2C1SEL_PRI & OSCIOFNC_ON & FCKSM_CSECMD & IOL1WAY_OFF & FNOSC_FRCPLL & PLL96MHZ_ON & PLLDIV_DIV2 & IESO_OFF & SOSCSEL_SOSC)
_CONFIG4(RTCOSC_LPRC)

 

/* USB MSDクラス用関数呼び出しポインタ */
LUN_FUNCTIONS LUN[MAX_LUN + 1] = 
{
 {
 &MDD_SDSPI_MediaInitialize,  // メディア初期化
 &MDD_SDSPI_ReadCapacity,   // 容量読み出し
 &MDD_SDSPI_ReadSectorSize,   // セクタサイズ読み出し
 &MDD_SDSPI_MediaDetect,    // メディア検出
 &MDD_SDSPI_SectorRead,    // 1セクタ読み出し
 &MDD_SDSPI_WriteProtectState,  // 保護状態読み出し
 &MDD_SDSPI_SectorWrite    // セクタ書き込み
 }
};

 

/* USB MSDクラスのSCSI INQUIRYコマンド応答用データ */
const ROM InquiryResponse inq_resp = {
 0x00,  // peripheral device is connected, direct access block device
 0x80,  // RMB removable
 0x04,  // ISO version=0, ECMA version=0, ANSI version=4=SPC-2
 0x02,  // response is in format specified by SPC-2
 0x20,  // Additional Length(n-4) = 36-4=32= 0x20
 0x00,  // sccs etc.
 0x00,  // etc
 0x00,  //bque=1 and cmdque=0,indicates simple queueing 00 is obsolete,
    // but as in case of other device, we are just using 00
    // 00 obsolete, 0x80 for basic task queueing
 {'T','.','G','o','k','a','n',' '}, // T10 Vendor ID 8cha 
 {'G','P','S',' ','U','S','B',' ','L','o','g','g','e','r',' ',' '},//product ID 16cha
 {'0','0','0','1'} // product revison 4cha
};

 

/********* 関数プロトタイピング *********/
void USBDeviceTasks(void);
void USBCBSendResume(void);

 

/*********** メイン関数 ********************/
int main(void)
{
 CLKDIVbits.RCDIV = 0;    //FRC 8MHz
 CLKDIVbits.PLLEN = 1;    //96MHz PLL On, CPU32MHz
 AD1PCFG = 0xFFFF;     //ALL DEGITAL

 

 /* SD CARD SPIピン割付 */
 RPINR20bits.SDI1R = 15;    // SDI1 -> RP15
 RPOR6bits.RP13R = 7;    // SDO1 -> RP13
 RPOR7bits.RP14R = 8;    // SCK1OUT -> RP14

 

 /* ファイルシステム初期化 */
 FSInit();

 

//USBフレームワーク初期化
 USBDeviceInit();     // USBデバイス初期化
 USBDeviceAttach();     // USBデバイスアタッチ許可

 

 while(1){
  /********** USB接続中の場合 *************/
  if((USBDeviceState >= CONFIGURED_STATE)&&(USBSuspendControl != 1)){ //USB
   MSDTasks();         // ファイルシステムステート更新
   
  /********** USB接続なしの場合 ***********/
  } else {
   //nothing to do!
   //SDカードへのアクセスなどユーザ処理を書く
  } //USB
 } //while
}

// ***********************************************
// *********** USB Callback Functions *****************
// ************************************************
以下省略

 

/*********************************************************
 * Function:        void USBCBInitEP(void)
 *********************************************************/
void USBCBInitEP(void)
{
    #if (MSD_DATA_IN_EP == MSD_DATA_OUT_EP)
        USBEnableEndpoint(MSD_DATA_IN_EP,USB_IN_ENABLED|USB_OUT_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP);
    #else
        USBEnableEndpoint(MSD_DATA_IN_EP,USB_IN_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP);
        USBEnableEndpoint(MSD_DATA_OUT_EP,USB_OUT_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP);
    #endif

    USBMSDInit();   // ファイルシステム初期化
}

以下省略

 

 

参考

Microchip アプリケーション・ノート AN1189

Microchip アプリケーション・ノート AN1045

 


 

(JF1VRR)

MLA_demo_cdc_basic02.jpg

実行中のPIC18F Starter Kit(上)とPIC24F Starter Kit

PIC8F Starter Kitの下のUSBソケットはDebugger、

上のUSBソケットがターゲットPIC18のUSBポート

MLA_demo_cdc_basic03.jpg

windows側dynamic_cdc_demo.exeの画面

PICがCOM7として認識されている

ABCDE1234と入力し「Send Data」をクリックすると

BCDEF2345とエコーする。

ボード上のプッシュボタンを押すとButton Pressedと表示する。

 

 

MLAはとても複雑ですし、デモプログラムはMicroship社が提供している各種デモボードそれぞれで使えるように作られているため、少々難解です。しかしMLAを使いこなすにはデモプログラムから入るしかないかと思います。fileioやUSBのMSDクラス、CDCクラスなどのデバイス/ホスト・アプリケーションを作るにあたって避けて通れない機能が含まれているため、使いこなしたいものですね。 

 

 

 

 

 

 

(JF1VRR)