設定データを内部FLASHに記憶させたい
・システムを作る中、設定データ(例えばIPアドレスなど)を内部に記憶させたいですよね。
・picoには1Mもフラッシュ領域があり、その一部を設定データ記憶に使えれば便利です。
・フラッシュ操作はSDKのAPIを使って簡単に実装できました。
picoの内蔵フラッシュ
・picoの内蔵SPIフラッシュは「W25Q16JVUXIQ」という型式。
・容量は2MByte、アドレス:は0x000000 ~ 0x1FFFFFです。
・ただし2MBすべて使えるわけでなく、使えるのは1MByteのみです。
・ROMエリア(0x000000~0x0FFFFF)とXIP(0x100000~0x1FFFFF)エリアで分かれており、
ROMエリアには工場出荷時に書き込まれるブートローダ(消せない)が実装されています。
・ユーザープログラムなど、自由に書き込める領域はXIPエリアになります。
・Pico起動時、CPUはROMに格納されたブートローダを実行します。ブートローダは、
フラッシュメモリの初期化やXIPモードの設定などを行い、その後にXIP領域に格納された
ユーザープログラムを実行します。
・ROMはPicoの起動処理を行い、XIPはユーザープログラムの実行領域として機能します。
フラッシュ操作APIの説明
・[hardware/flash.h]をインクルードして使います。
★削除
■flash_range_erase
・機能:指定されたアドレス範囲のフラッシュメモリを消去します。
・注意:フラッシュメモリは、書き込み前に消去する必要があります。
・引数:offset: 消去を開始するフラッシュメモリのアドレス count: 消去するバイト数
・備考:フラッシュメモリは、セクタ単位で消去されます。セクタサイズは通常4KBです。
countは、セクタサイズの倍数である必要があります。
★書き込み
■flash_range_program
・機能:指定されたアドレス範囲のフラッシュメモリにデータを書き込みます。
・引数:offset: 書き込みを開始するフラッシュメモリのアドレス
src: 書き込むデータのポインタ count: 書き込むバイト数
・備考:フラッシュメモリは、ページ単位で書き込まれます。
ページサイズは通常256バイトです。
★読み込み
・特別なAPIは必要なく、アドレスを直接読むことで値をreadできます。
消去、書き込み時の注意点
・フラッシュメモリへの削除、書き込み中に、別のコアがプログラムコードを含む
フラッシュ領域にアクセスするとエラーが発生し、例外ハンドラへ飛びます。
・この期間は別のコアも命令をフェッチしたり、データを読み出したりできません。
・フラッシュ内の同じページではなく、フラッシュ自体へのアクセスが禁止されます。
・Picoのマルチコア環境では、core0とcore1が独立して動作するため、片方のコア
がフラッシュ書き込みを行っている間も、もう片方のコアは動作していますので、
普通にフラッシュAPIを使うとエラーとなります。
消去、書き込み時の対策(割り込みの禁止)
・対策としてはそのままですが、別のコアを停止させるAPIが用意されています。
・その前に割り込みを禁止しておきます。
・save_and_disable_interrupts()は、割り込みの状態を保存し、すべての
プロセッサコアの割り込みを無効化するために使用されます。
・これは割り込みによって中断されてはならないコード領域を保護するために
使用します。(クリティカルセクション)
・使用した場合、必ず対応する restore_interrupts() を呼び出して、保存し
ておいた割り込みの復元が必要です。そうしないと、システム全体の割り込みが
無効化されたままになり異常動作となります。(デッドロック)
// 割り込み無効にする
uint32_t ints = save_and_disable_interrupts();
// コア0を停止
multicore_lockout_start_blocking();
// Flash消去
// 消去単位 FLASH_SECTOR_SIZE(4096Byte) の倍数
flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE);
// Flash書き込み
// 書込単位 FLASH_PAGE_SIZE(256Byte) の倍数
flash_range_program(FLASH_TARGET_OFFSET, wd, FLASH_PAGE_SIZE);
// コア0を再開
multicore_lockout_end_blocking();
// 割り込みフラグを戻す
restore_interrupts(ints);
消去、書き込み時の対策(victimコアの宣言とロック)
・multicore_lockout_start_blockingは、「victim core」に定義したコアを一時的に停止して
割り込み無効にするAPIです。 (実際はRAM 内のコードでタイトなループを実行)
・タイムアウト付きのmulticore_lockout_start_timeout_us()を使うこともできます。
・使用方法として事前に「victim」コア(他のコアにロックされる可能性のあるコア)
として定義する必要があります。multicore_lockout_victim_init()APIを呼び出して、
宣言しておきます。
void main_c0(void)
{
/*c0は停止可能なサブコアとして定義*/
multicore_lockout_victim_init();
gpio_init(LED_PIN); // GPIO初期化
gpio_set_dir(LED_PIN, GPIO_OUT); // GPIOを出力に設定
i2cLcdPerfInit();
while (1) {
disp_adc_val();
sleep_ms(100);
}
}
・ロックアウトが不要になると、multicore_lockout_end_blocking()APIを呼び出します。
・タイムアウト付きのmulticore_lockout_end_timeout_us()を使うこともできます。
今日はここまで。