STM32F4には4KiBのバックアップSRAM(BKPSRAM)が搭載されている。VDD or VBATから電力を受けて、「バックアップバッテリが接続されていればEEPROMと同様に扱える」メモリ。
RTCにもバックアップレジスタが実装されているが、こちらは20ワード(80バイト)しか無い。その代わりにタンパ検出(筐体の分解等を検出してデータを破棄する機能)がついている。
大きなデータはバックアップSRAM、小さなデータ(あるいはバックアップSRAMの暗号鍵)はRTCレジスタ、みたいな使い分け?
アドレスは0x40024000
だが、BKPSRAM_BASE
マクロが定義されているので、これを使うと便利。
BKPSRAMにアクセスするにはいくつかの手順が必要になる。読み出しには、電源コントローラ(PWR)の有効化、BKPSRAMインターフェースの有効化、が必要。書き込みにはライトプロテクトの解除も必要。
電源コントローラの有効化は__HAL_RCC_PWR_CLK_ENABLE();
で行えるが、CubeMXでコードを生成するとSystemClock_Config
の中で呼ばれているので、あまり気にする必要はないはず。
インターフェースの有効化には__HAL_RCC_BKPSRAM_CLK_ENABLE();
を、解除には__HAL_RCC_BKPSRAM_CLK_DISABLE();
を呼ぶ。
このインターフェースはマクロ名の通りSRAMインターフェースのクロックを有効化/無効化するもの。有効化しておかないと有効な値を読み取れなくなる。ただし、インターフェースが無効な状態で読み出すと、最後に読みだしたワードに含まれるバイトが読み出される仕様らしい。ゼロ埋めとかが出てくるわけじゃないのでデバッグ中は注意。なぜかうまく動かない、というときはクロック周りを疑うべし(って、これはARM全般に言えるか)。
書き込み時はインターフェースに加え、プロテクト解除が必要。プロテクト解除はHAL_PWR_EnableBkUpAccess();
を、プロテクト有効化にはHAL_PWR_DisableBkUpAccess();
を呼ぶ。アクセス時のEna/Disはインターフェースと逆なので注意。
アクセス時は、uint8_t *const bkpsram = reinterpret_cast<uint8_t *>(BKPSRAM_BASE);
のようにポインタを宣言してやれば、そのままprintf("%02hhX\n", bkpsram[10]);
としてみたり、bkpsram[4095] = 0xAA;
という感じで使える。もちろん、好きな構造体を経由してもいい。
バックアップSRAMは、電源がなくなるとランダムなデータが入るらしい。RAMの中身が有効なデータかは自分で判断する必要がある。例えばSTM32にはCRC計算機が入っているので、BKPSRAMのCRCをRTCバックアップレジスタに入れておいて、BKPSRAM書き込み時にはCRCも計算し、読み出し時はCRCが一致しなければエラーとして扱う、というような挙動が考えられる(もちろんCRC自体をBKPSRAMに入れてもいい)。CRCが過剰であればチェックサムを使うことも考えられる。