はじめに
ユーザコード(アプリケーション)からSTM32の内蔵ブートローダを起動する必要があり、その調査をしたので忘備のためにまとめました。
内蔵ブートローダは、生産時にSTマイクロエレクトロニクス社によってプログラムされているそうです。
使用機器
メーカ | 型格 |
---|---|
STマイクロエレクトロニクス社 | NUCLEO-L476RG |
STマイクロエレクトロニクス社 | STM32CubeIDE |
ブート設定
ブートモードの選択は、オプションバイトでBOOT0ピンおよびnBOOT1ビットによって3種類から選択することができます。
BOOT1 | BOOT0 | ブートモード |
---|---|---|
x | 0 | メインフラッシュメモリ |
0 | 1 | システムメモリ |
1 | 1 | 内蔵SRAM1 |
NUCLEO-L476RGではBOOT0がLOWになっているのでブートモードはメインフラッシュメモリが選択されています。
アプリケーションノート(AN2606)を読むとBOOT0がLOWであっても有効なコードが含まれていなければ内蔵ブートローダにジャンプできるとありましたが、メインフラッシュメモリの内容を全て消去したくなかったのでユーザコードから内蔵ブートローダにジャンプする方法を選択しました。
内蔵ブートローダの配置
内蔵ブートローダは下表の通り配置されており、下表のRAMを使用するようになっているようです。
アドレス | メモ | |
---|---|---|
システムメモリ | 0x1FFF 0000 | 左記のアドレスから28KBにブートローダが格納されています |
RAM | 0x2000 0000 | 左記のアドレスから12KBがブートローダに使用される |
内蔵ブートローダへジャンプするための条件
ユーザコードから内蔵ブートローダにジャンプするには下記の準備をする必要があります。
- 全てのペリフェラルクロックの無効化
- 使用したPLLの無効化
- 割り込みの無効化
- ペンディング割り込みのクリア
内蔵ブートローダへのジャンプ
内蔵ブートローダの先頭にベクタテーブルがあります。ベクタテーブルの先頭にはMSP、その次にはリセットハンドラのアドレスが格納されているので、これを利用して内蔵ブートローダにジャンプします。
内蔵ブートローダにジャンプするための条件を満たすのは大変なため、STM32CubeIDEが自動生成したスタートアップを変更して対応しました。
ldr ap, =_estack
/* ここで内蔵ブートローダにジャンプするかmain関数にジャンプするか判断する */
bl Loader
void Loader(void)
{
uint32_t msp = *((uint32_t *)0x1FFF0000u);
uint32_t Reset_Handler = *((uint32_t *)0x1FFF0000u + 4u);
// 内蔵ブートローダのメインスタックポインタを設定する
__set_MSP(msp);
// 内蔵ブートローダのリセットハンドラにジャンプする
((void (*)(void))Reset_Handler)();
while (1) {
// 内蔵ブートローダにジャンプするのでここに来ることはない
__asm __volatile("nop\n");
}
}
内蔵ブートローダの起動後の対応
内蔵ブートローダが起動したら、CAN, USART, USB DFU, I2C, SPIで通信を行います。
プロトコルの詳細はアプリケーションノートに記載されています。
参考サイト
ブートローダ
アプリケーションノート(AN2606)
アプリケーションノート(CANプロトコル)
アプリケーションノート(USARTプロトコル)
アプリケーションノート(USB DFUプロトコル)
アプリケーションノート(I2Cプロトコル)
アプリケーションノート(SPIプロトコル)
v