PIC32MXのUSBブートローダーを使ったファームウェア更新

Firmware updating from PIC32MX USB boot loader.
 
ハードウェアを開発している本人であればなんら問題はありませんが、一般の人に「配布したハードウェアにバグがあったのでプログラムライターを購入してデータを書き換えてください」というわけにはいきません。将来的な不具合に対処するためには、そのハードウェア単体でファームウェアの更新を完結させる必要があります。

PIC32MXのフレームワーク「MPLAB Harmony」にはUSBブートローダープログラムが標準で用意されており、あらかじめこのブートローダーをマイコンに書き込んでおくことで、HID USB機器をソフトウェアレベルで制御できるパソコンを介してプログラムの更新に対応することができます。今回はHarmonyフレームワークを使ったブートローダーの活用例を紹介します。

まずは、ブートローダープログラムを作成してみましょう。統合開発環境のMPLAB X IDEを起動してHarmonyフレームワークの新規プロジェクト(hid_bootloader)を作成したら、MPLAB Harmony Configuratorにて「use Bootloader Library?」にチェックを入れます。「Bootloader Type」に「USB_DEVICE」を指定すると、USB対応のPIC32MX(ここではPIC32MX230F064Bを使用)だけでファームウェアの更新が実装できます。
つづいてHID USBデバイスの定義を指定します。USBの送信・受信と2つの領域が必要なので「Number of Endpoints Used」には「2」を入力し、「Function 1→Device Class」には「HID」を選択します。基本的にVendor ID/Product IDは任意の値でかまいませんが、画像ではマイクロチップのブートローダーユーティリティの初期値(0x04D8/0x003C)を書き込んできます。また、開発ボードは使用していないので「Use BSP?」のチェックは外しておきます。
ブートローダーにおけるクロックダイアグラムの設定は、後で追加・更新するプログラムにも適用されるので、ファームウェアの挙動を前提にした値を指定しましょう。
続いてブートローダー時におけるピンの設定を行います。この例ではRB4にタクトスイッチを、RB1にLEDを接続します。
ブートローダーコードを生成したら、プログラムに若干手を加えます。この状態でコンパイルするとタイマー関連のエラーが発生しますが、該当するコードを削除しても問題ありません。
app.c
APP_DATA appData;

int APP_Bootloader_ForceEvent(void)
{
    /* Check the switch press to trigger bootloader */
    if (BOOTStateGet()) return (1);

    /* Check the trigger memory location and return true/false. */
    if (*(uint32_t *)APP_RESET_ADDRESS == 0xFFFFFFFF) return (1);
    
    return (0);
}

void APP_Initialize ( void )
{
    appData.state = APP_STATE_INIT;

    // Register the bootloader callbacks
    BOOTLOADER_ForceBootloadRegister(APP_Bootloader_ForceEvent);
}

void APP_Tasks ( void )
{
    switch ( appData.state ){
        case APP_STATE_INIT:
        {
            bool appInitialized = true;             
            if (appInitialized) appData.state = APP_STATE_SERVICE_TASKS;
            break;
        }

        case APP_STATE_SERVICE_TASKS:
        {
            static uint32_t cntr = 0;
            // Blink the LED
            if (cntr++ == 100000) {
                LEDToggle();
                cntr = 0;
            }       
            break;
        }
    }
}
プログラム実行時に「APP_Bootloader_ForceEvent」で登録した関数が呼び出され、この時点でタクトスイッチが押されている(BOOTStateGet() == true)場合は1を返すことでブートローダープログラムを実行し続けます。スイッチが押されておらず、かつファームウェアが見つかった場合は0を返して、ファームウェアプログラムに移行します。

ブートローダープログラムが実行中であれば、ここの「APP_Tasks」が呼び出されるため、RB0に接続したLEDが点滅します。

プロジェクトをビルドして「hid_bootloader.X.production.hex」が生成されたら、これをマイコンに書き込みます。開発環境に付属している「MPLAB X IPE」を起動したら、「Hex File」に先ほどのコンパイル済みバイナリファイルを指定し、プログラムライター(PicKit3など)経由で書き込みます。
正常に書き込まれれば、ブートローダープログラムが起動し、「D+/D-」ピンに接続したUSBケーブルとそれに繋がっているパソコン間でHID USBデバイスが認識されます。

ブートローダーを書き込んだマイコンは一度わきに置いておき、次はファームウェアを開発します。MPLAB X IDEで新しいプロジェクト(BootAppTest)を作成して、任意のプログラム(ここではRB1に繋げたLEDを単純に点灯させている)を作成します。

注意点として、ブートローダープログラムのシステム設定と競合するのを避ける点が挙げられます。そのため「#pragma config」は記述しない、もしくは、Harmonyプロジェクトであれば「system_init.c」にある「#pragma config」の宣言をコメントアウトしておくように心がけましょう。
では、これをビルドした「BootAppTest.X.production.hex」ファイルをマイクロチップ社のサイトで配布されているブートローダーユーティリティを経由して書き込んでみます。
このブートローダーに付属しているファイルはWindows専用ですが、自前のUIを用意したかったり、Linuxやmacでも動くツールを提供したいのであれば、「オープンソースのブートローダーツール」を参考にすると良いでしょう。

マイコンつないでいるタクトスイッチを押しながら電源を投入するとブートローダーモードに移行し、RB0のLEDが点滅します。この状態でユーティリティソフトの「Connect」ボタンを押すとマイコンとのUSB通信が開始されます。「Load Hex File」で「BootAppTest.X.production.hex」を指定し、「Erase-Program-Verify」ボタンを押すと、ファームウェアが更新されます。書き込みと整合性の検証が完了したら、「Run Application」ボタンを押してマイコンをリセットしてみましょう。


この映像ではファームウェアの更新により、2つのLEDが点滅するプログラムが1つのLEDのみが点滅するプログラムに入れ替わっています。
2019/04/18