
投稿日 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のための必要最小限の回路
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という名前のプロジェクトにしました。

プロジェクト 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)

実行中のPIC18F Starter Kit(上)とPIC24F Starter Kit
PIC8F Starter Kitの下のUSBソケットはDebugger、
上のUSBソケットがターゲットPIC18のUSBポート

windows側dynamic_cdc_demo.exeの画面
PICがCOM7として認識されている
ABCDE1234と入力し「Send Data」をクリックすると
BCDEF2345とエコーする。
ボード上のプッシュボタンを押すとButton Pressedと表示する。
MLAはとても複雑ですし、デモプログラムはMicroship社が提供している各種デモボードそれぞれで使えるように作られているため、少々難解です。しかしMLAを使いこなすにはデモプログラムから入るしかないかと思います。fileioやUSBのMSDクラス、CDCクラスなどのデバイス/ホスト・アプリケーションを作るにあたって避けて通れない機能が含まれているため、使いこなしたいものですね。
(JF1VRR)