<USBローダ & フレームワーク>

PIC32ボードに実装されるPIC32MX795F512Lには512Kbyteのフラッシュメモリと128KbyteのRAMが搭載されています。フラッシュメモリも十分大容量ですが、RAMの128Kbyteは他の同クラスのマイコンと比較して倍の容量があります。更にRAM上でプログラムを80MHzノーウェイトで実行することが可能ですので、大容量のRAMを有効に使わない手はありません。

フラッシュメモリ上でもプリフェッチキャッシュを使ってノーウェイトでプログラムを実行することは可能ですが、フラッシュメモリに書き込むには時間がかかります。また、一枚のPIC32基板を複数の目的(例えばJTAGライターや各種ロガーなど)で使用する場合には、先にフラッシュ内容を消去してから接続を変更して、書き込む必要があります。

RAMでその都度必要なプログラムを転送して実行するようにすれば、上記のようなわずらわしさからは解放されます。(プログラムを転送するためにPCと接続するという前提は必要になりますが、、。)

また、PCと連携して動作するツールやアプリケーションには必ず必要な機能があります。例えばPCにメッセージを表示したり、PCの指示によって動作を変えたり、PC上のデータ(ファイル)を読み書きしたりといった機能です。

通常これらの機能を実装するためには、マイコン側のアプリケーション(ファームウエア)とは別にPC側のアプリケーション(ホストアプリ)を開発する必要があります。連携するファームとホストの両方のアプリケーションを同時に開発するのは骨の折れる作業です。

今回提供しているUSBローダとフレームワークは上記の問題を解決することが可能です。マイコン側のファームウエア開発だけで済むような仕組みになっています。


USBローダの容量は32Kbyteでプログラムフラッシュメモリの最上位(0x1d078000-0x1d07ffff)に書き込まれています。リセット時はリセットベクタの位置に置かれた小さなプログラムが半田ジャンパ(SJ2)の状態を判断して、ユーザプログラムあるいはUSBローダを起動する仕組みです。USBローダが起動されると、USBを有効にしてRAMにダウンロードするプログラムがPCから送られてくるのを待ちます。

PC側ではPIC32で実行するプログラム(ヘキサファイル)と、PIC32で実行されるプログラムに渡す引数を指定して、pic32consと呼ばれるホストアプリを起動します。指定されたヘキサファイルはPIC32にUSB経由で転送され、PIC32のRAM上で実行されます。

RAM上で実行されるプログラム(ユーザアプリ)とUSBローダは別プログラムですので、普通はUSB接続も一度切断する必要があります。ですが今回のフレームワークでは、リンカスクリプトや初期化プログラム(crt0.S)を工夫することでユーザアプリがUSBローダと共存出来るようにしてあります。

そして、ユーザアプリで発行されるprintf()やPCファイルオープンの要求は、USBローダのプログラムがホストアプリと通信を行って処理します。このためユーザアプリでは全くUSBの処理を気にすることなく、PCホストと連係動作することが可能です。また、マイクロチップの提供する便利なペリフェラルライブラリも変更なしでユーザアプリにリンクして呼び出せます。


<pic32cons.exe>

USBローダの起動しているPIC32ボードにユーザアプリ(ヘキサファイル)を転送し、指定された引数で実行させます。その後、ホストプログラムとして動作し、ユーザアプリからのメッセージ表示、ファイル入出力処理などを行います。コンソールプログラム(コマンドプロンプトから実行)で動作にはデバイスドライバ(mchpusb.sys)の組み込みと、DLL(mpusbapi.dll)が必要です。

起動方法は「pic32cons [-dev] ヘキサファイル [arg1] [arg2] [arg3] [arg4]」になります。

「-dev」オプションを付けて起動すると、指定したヘキサファイルがMPLABで再ビルドされタイムスタンプが変更された場合、PIC32側に通知します。PIC32で動作中のユーザアプリはこの通知を受け取り、ソフトリセットを行い、再度USBローダを起動します。そして更新済みのヘキサファイルがPIC32に転送され実行されます。

ヘキサファイルの拡張子(.hex)は省略することが出来ます。

pic32consの特殊な使い方として、「pic32cons.exe」を「pic32wr.exe」のようにコピー&リネームします。そして「pic32wr [arg1] [arg2] [arg3] [arg4]」のように起動すると、「pic32cons pic32wr.hex [arg1] [arg2] [arg3] [arg4]」と起動したのと同じ結果が得られます。この機能はpic32consをリネームすることで、ユーザ専用のホストアプリを作るのと同様のことが出来ます。
(実際にこの後に紹介するpic32wr.exeとmax2wr.exeはpic32cons.exeをりネームしただけのものです。)

pic32consのホスト機能の詳細は別の項目で解説します。

pic32cons実行時にUSBケーブルを抜いたりすると、「Error: 5」のようなメッセージが表示される場合があります。このメッセージはDLL(mpusbapi.dll)が出力しているもので、消すことが出来ません。無視してください。


<フラッシュライター pic32wr.exe>

MPLABでビルドしたヘキサファイルをPIC32ボードに書き込むためのツールです。書き込む対象は自分自身のフラッシュメモリ(セルフモード)、あるいは別のPIC32ボード(ISPモード)になります。いずれの場合でも、PIC32ボードはUSBローダが起動した状態でPCに接続されている必要があります。PC側でpic32wr.exeが起動されると、PIC32のRAMにpic32wr.hexファームウエアがダウンロードされ実行されます。(pic32wr.exeの動作にはpic32wr.hex、mpusbapi.dllが必要です。)

起動方法はコンソール画面(コマンドプロンプト)から「pic32wr [pgm.hex] [/ISP] [/E] [/J]」と実行します。

オプション 動作
/ISP 別のPIC32ボードのフラッシュメモリに書き込む(ISPモード)
(無指定時は自分自身に書き込むセルフモード)
/E 消去のみを行う、hexファイルは無効(不要)
/J 半田ジャンパ(SJ2)ショート時にUSBローダを起動します
(無指定時はオープン時にUSBローダ起動)

ISPモード時はターゲットとライター間の配線を行います。

信号名 ライター側 ターゲット側
GND(VSS) CN2-6 CN2-6
+3V3 CN2-4 CN2-4
MCLR CN2-19(G14) CN2-2
PGD CN2-23(A6) CN2-8
PGC CN2-22(A7) CN2-10

PIC32ボードはPICKit3などに直接接続して書き込めるようにCN2コネクタにプログラム用の信号を集めて配置してあります。


ISPモード時は指定されたヘキサファイルに忠実にフラッシュメモリーのプログラムを行います。プログラムコードの読み出し禁止などの設定もコンフィグレーションビットを設定したヘキサファイルを書き込むことで可能です。誤ってUSBローダを消去してしまった場合も、2台のPIC32ボードがあればUSBローダを書き込んで復活させることが出来ます。


セルフモード書き込みは、不適切なヘキサファイル(例えばクリスタルの動作モードなど)を書き込むと、USBローダが起動出来なくなる可能性があります。これでは困るので、ISPモードとは逆に、可能な限りコンフィグレーションビットの内容を訂正して書き込むようにしてあります。コンフィグレーションビットの内容を変更した場合には、書き込み時のメッセージに表示されますので確認してください。


<フラッシュユーティリティ flashutil.hex>

フラッシュユーティリティはPIC32ボードのフラッシュメモリを直接書き換えたり、ファイルに読み書きするためのツールです。便利な機能として、半田ジャンパ(SJ2)の状態に係らず、フラッシュメモリーに書き込んだプログラムを直接起動することが出来ます。pic32cons.exeを使ってPIC32のRAMにダウンロード&実行する、対話形式のツールです。

起動はコンソール画面(コマンドプロンプト)から「pic32cons flashutil [コマンドファイル]」と実行します。

コマンドファイルはflashutilに対するコマンドを記述したテキストファイルで、コマンドファイルを指定するとバッチモード(対話モードでない)で動作します。

フラッシュメモリにデータを蓄積していくユーザの作成したロギングプログラムのデータを、バッチコマンドでバイナリファイルに保存するといった使い方が出来ます。また、フラッシュメモリの特定の内容を書き換えることにより、ユーザプログラムの動作パラメータを変更するといった用途などにも使用できます。

コマンド 動作 使用例
(16進数の場合は頭に0xを付けて指定する)
VF addr file [bytes] 指定したアドレスの内容とファイル内容を比較する
bytes省略時はファイルの長さを比較対照にする
VF 0xbd010000 test.bin 0x1000
RF addr file bytes 指定したアドレスの内容をファイルにbytes分書き出す RF 0xbd070000 test.bin 1024
WF addr file [bytes] ファイルの内容を指定したアドレスに書き込む
bytes省略時はファイルの長さ分書き込む
WF 0xbd070000 test.bin
WB addr m1 ... [m8] 指定したアドレスにバイトデータを書き込む
(MAX 8byte)
WB 0xbd070000 0x12 0xAA
WH addr m1 ... [m8] 指定したアドレスにハーフワードデータを書き込む
(MAX 8word)
WH 0xbd070000 0x1234 0xABCD 0xFFFF
WW addr m1 ... [m4] 指定したアドレスにワードデータを書き込む
(MAX 4word)
WW 0xbd070000 0x12345678
WS addr "string" 指定したアドレスに文字列を書き込む
(MAX 32文字)
WS 0xbd070000 "192.168.1.32"
RB addr num 指定したアドレスのバイトデータををnum個表示する RB 0xbd070000 32
RH addr num 指定したアドレスのハーフワードデータををnum個表示する RH 0xbd070000 8
RW addr num 指定したアドレスのワードデータををnum個表示する RW 0xbd070000 4
RE フラッシュメモリに書き込み済みのプログラムを実行する
(flashutilも終了します)
RE
Q flashutilを終了する Q


<USBローダ対応アプリの開発>

上記で紹介しているpic32wrとflushutilもUSBローダ対応アプリで、実用性のある本格的なアプリケーションです。もっとも単純な「Hello World.」アプリの開発はMPLABインストールページの最後の部分で紹介していますので、そちらもご参照ください。

USBローダ対応アプリの特徴は「RAM上で動作すること」、「USBローダが確立したホスト(PC)とのUSBコネクションを継続使用する」の2点にあります。このことを実現するためにUSBローダでは専用のリンカースクリプト(elf32pic32mx.ld loader_procdefs.ld)と、スタートアップコード(crt0.S)を使用します。

USBローダはROM領域0x9d078000 - 0x9d07ffffまでの32Kbyte、RAM領域0xa0000000 - 0xa0001fffまでの8Kbyteを使用しています。従ってUSBローダ対応アプリはコード領域が0xa0010000 - 0xa001ffffの64Kbyte、データ領域が0xa0002000 - 0xa000efffまでの52Kbyteが使用できます。(0xa000f000 - 0xa000ffff の4Kbyteは割り込みベクタなどに使用)

MPLABでUSBローダ対応アプリ用のプロジェクトを作成する場合は、上記の「Hello World.」アプリを改変して使用するか、リンカスクリプト2ファイル(elf32pic32mx.ld loader_procdefs.ld)と、スタートアップコード(crt0.S)を新規作成したプロジェクトにコピーして追加します。

新規にプロジェクトを作成した場合は、ライブラリに含まれるスタートアップファイルがリンクされないように下記のようにプロジェクトの設定を変更します。



USBローダ対応アプリは通常のフラッシュメモリで動作するプログラムと同様に、マイクロチップ社の提供するペリフェラルライブラリをリンクして使用することが可能です。
また、割り込み処理なども通常のアプリケーション同様に「 void __ISR(_TIMER_2_VECTOR, ipl2) Timer2Handler(void){ ..... } 」のように記述することが出来ます。

一つだけ注意すべきは、USBローダ対応アプリがUSBコネクションをUSBローダから引き継がなければならない点です。USBローダ側ではUSB関連の処理は全て割り込み処理として記述されています。このためUSBローダ対応アプリ側ではUSB関連の独自処理は出来ず、USB関連は必ずUSBローダに処理を委ねる必要があります。
また、USBの割り込みが処理されないと、殆どのホストI/O関数が処理できないため、必ず最初に INTEnableSystemMultiVectoredInt() 関数を呼び出す必要があります。

ホストI/O関数はヘッダファイルhio.hにて定義されています。ホストI/O関数はUSBローダのフラッシュROM領域に置かれている関数に直接ロングジャンプを行って処理されます。ホストI/O関数の概略は下記の表にまとめました。詳しい使い方については、公開したサンプルプログラムを参照してください。

関数 動作
int printf(const char *format, ...); 書式付メッセージの表示
(標準入出力)
printf("X = %d\n", x);
char *gets(char *s); 文字列の入力
(標準入出力)
gets(buf);
void PcInit(); ホストI/O関数初期化 PcInit();
s32 PcOpen(s8 *fname,s32 mode); PCファイルのオープン fh = PcOpen("test.bin", PC_BINARY | PC_RDONLY);
s32 PcClose(s32 fh); PCファイルのクローズ PcClose(fh);
s32 PcRead(s32 fh, void *buf, s32 len); PCファイルのリード PcRead(fh, buf, 1024);
s32 PcWrite(s32 fh,void *buf, s32 len); PCファイルの書き込み PcWrite(fh, buf, len);
s32 PcLseek(s32 fh,s32 ofs, s32 where); PCファイルのシーク PcLseek(fh, 0, PC_SEEK_SET);
void PcPuts(s8 *str); 文字列の表示 PcPuts("Hello world.\n");
void PcPutc(s8 c); 文字の表示 PcPutc('A');
void PcFlush(); 表示用バッファのフラッシュ PcFlush();
s32 PcGets(s8 *str); 文字列の入力 PcGets(buf);
s32 PcGetc(); 文字の入力 c = PcGetc();
void PcReset(); PIC32ボードのソフトリセット PcReset();
s32 PcSync(s8 *name, struct PcSync *sync); ホストプログラムとの同期処理
(引数の取得など)
PcSync(APP_NAME, &sync);
void PcExit(); ホストプログラムを終了させる PcExit();
void PcHexLoad(s8 *fname, u32 flash_addr); ヘキサファイルのロード
(サンプルコード参照)
PcHexLoad(hex_file, 0x1d000000);
s32 PcSvfLoad(s8 *fname); SVFファイルのロード
(MAX2専用のため汎用性無)
PcSvfLoad(svf_file);
void PcKeySence(struct PcKeySence *key); リアルタイムキー入力 PcKeySence(&key);
s32 PcEp2Read(u8 *buf, s32 len); USBエンドポイント2の読み込み PcEp2Read(buf, 1024);
s32 PcEp2Write(u8 *buf, s32 len); USBエンドポイント2の書き込み PcEp2Write(buf, 1024);

<各種リソースのダウンロード>


<USBローダ対応アプリのデバッグ ICD3未使用>

pic32cons.exeプログラムは起動時に「-dev」オプションを付加することで、ターゲットのヘキサファイルが更新(MPLABでリメイク)された際に、PIC32で動作中のターゲットプログラムに通知(PcSync())します。
通知されたターゲットプログラムは自分自身をソフトリセットすることで、再度USBローダを起動して、更新済みのプログラムを読み込んで実行することが可能になります。

while(1){
    printf("t1=%d t2=%d t3=%d\n", t1, t2, t3);
    if(PcSync("APPNAME", NULL)){
       PcReset();
    }
}

処理としては上記のようになります。

上記のようにPcSync()で更新をチェックしなくても、ターゲットプログラムの処理が終了したところでPcReset()を入れておけば、自動的にUSBローダが再起動するので新しいヘキサファイルのアップロードが可能になります。

<USBローダ対応アプリのデバッグ ICD3使用時>

ICD3(確認していませんがPICKit3も同様と思います)を使用するとICE機能を使用したデバッグを行うことが出来ます。本来デバッグの対象となるプログラムはフラッシュメモリに書き込まれている状態なのですが、USBローダ対応アプリはRAMで実行されています。また、USBローダ対応アプリは前提としてUSBローダが動作した直後に動作することになっています。このため、本来であればICD3でのデバッグは困難なのですが、若干トリッキーなことを行うことで一応ICD3でデバッグ出来るようにしています。

USBローダの書き込まれているPIC32ボードを用意します。このままICD3でデバッグを行うと、デバッグの度にフラッシュ全体が消去されますので、書き込まれているUSBローダも消去されてしまいます。そこでMPLABで「Debugger -> Settings」で下記の画面を開き、Preserve Program Memory Rangeの設定を行い、USBローダの領域(0x1d078000 - 0x1d07ffff)が消去されないようにします。



これでUSBローダは消えないのですが、デバッグ対象プログラムが起動する前にUSBローダを起動しておく必要があります。そこでcrt0.Sを修正して、ユーザのmain()処理の前に強制的にUSBローダを実行するようにしました。これでICD3を使ってデバッグが可能になりました。但し理由は不明ですが、何故か初回ビルド時はうまくデバッグできません。プロジェクトを開いたら、初回は一度ダミーでビルドを行い、2回目のビルドからデバッグを行うようにしてください。