【Raspberry Pi Pico】Flashメモリの空き領域に設定値保存 (C/C++)

PicoではオンボードのFlashメモリにプログラム[.uf2]を書き込んで動作させますが、大抵の場合はメモリの一番後ろの方は空いているはずです。(メモリをフルに使うようなギリギリのプログラムを作成されているなら別ですが)

その空いているFlashメモリの領域は使用できますので、電源が切れても保持しておきたい設定値等を保存することができます。

※本当はUSB接続によりPicoのマスストレージにテキストファイルをコピーしてPicoで読み書きしたかったのですが、C/C++ではどうやらまだ難しそうでした。(2022/7月現在。MicroPythonならできるかも)


1. Pico基板上のFlashメモリについて

  • 型式: W25Q16JVUXIQ
  • 容量: 16Mbit (16777208bit = 2097151Byte)
  • アドレス: 0x000000 ~ 0x1FFFFF
  • 通信: SPI (Pico C/C++SDK有り)

ブロック図

容量は16Mbitで、32ブロックに分かれています。.uf2のプログラムは先頭から書き込まれるため、大抵の場合32ブロック目(Block31)は空き容量となると思います。よってここ(Block31)を電源遮断時でも維持したいデータの保存場所とします。開始アドレスは 0x1F0000 です。

各ブロック自体も16セクタに分かれており、1セクタにつき4kByteとなっています。Flashの消去はこの1セクタ(4kByte)単位で行われ、書き込みは256Byte単位で行われるとのことです。


2. Flashメモリに設定値を書き込む例

用意されているC/C++SDKの関数で簡単に実現できます。アドレスはFlashメモリのアドレスを指定します。

#include <hardware/flash.h>

static void save_setting_to_flash(void)
{
    // W25Q16JVの最終ブロック(Block31)のセクタ0の先頭アドレス = 0x1F0000
    const uint32_t FLASH_TARGET_OFFSET = 0x1F0000;
    // W25Q16JVの書き込み最小単位 = FLASH_PAGE_SIZE(256Byte)
    // FLASH_PAGE_SIZE(256Byte)はflash.hで定義済
    uint8_t write_data[FLASH_PAGE_SIZE];

    // 保存データのセット(例)
    write_data[0] = 12;
    write_data[1] = 34;
    write_data[2] = 56;
    …

    // 割り込み無効にする
    uint32_t ints = save_and_disable_interrupts();
    // Flash消去。
    //  消去単位はflash.hで定義されている FLASH_SECTOR_SIZE(4096Byte) の倍数とする
    flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE);
    // Flash書き込み。
    //  書込単位はflash.hで定義されている FLASH_PAGE_SIZE(256Byte) の倍数とする
    flash_range_program(FLASH_TARGET_OFFSET, write_data, FLASH_PAGE_SIZE);
    // 割り込みフラグを戻す
    restore_interrupts(ints);
}

CMakeLists.txt にてtarget_link_libraries に『hardware_flash』と『hardware_sync』の追記が必要です。


target_link_libraries(
        your_lib_name
        pico_stdlib
        hardware_flash
        hardware_sync
        )


3. Flashメモリから設定値を読み込む例

読み込みはXIP(Execute-In-Place)の仕組みを利用して簡単に実現できます。XIPとは、CPUのとあるアドレスにアクセスすると内部で別の処理を行って値を返してくれる仕組みの事です。アドレスXIP_BASE(0x10000000)はプログラムの先頭となりますので、Block31の先頭は、XIP_BASE(0x10000000)+0x1F0000 ということになります。

#include <hardware/flash.h>

uint8_t g_read_data[3];

void load_setting_from_flash(void)
{
    // W25Q16JVの最終ブロック(Block31)のセクタ0の先頭アドレス = 0x1F0000
    const uint32_t FLASH_TARGET_OFFSET = 0x1F0000;
    // XIP_BASE(0x10000000)はflash.hで定義済み
    const uint8_t *flash_target_contents = (const uint8_t *) (XIP_BASE + FLASH_TARGET_OFFSET);
    
    g_read_data[0] = flash_target_contents[0];
    g_read_data[1] = flash_target_contents[1];
    g_read_data[2] = flash_target_contents[2];
}

コメント

このブログの人気の投稿

v4l2-ctlで行うUSBカメラ設定方法まとめ

Raspberry Piでシリアル通信する方法(通信設定・通信確認)

Raspberry Pi Picoのステップ実行できる開発環境づくり