SNSへはこちら

AVR32マイコンやってみよう(13) - Flash

ペリフェラルシリーズでは最後の記事です。Flash の User Page に値を書き込むことを目標にします。

User Pageとは

不揮発性メモリである EEPROM の代わりとなる領域です。
8bit AVR、RX マイコンなどの一部のマイコンには内部に電源を切っても消えないメモリ領域が用意されていて、そこにデータを書き込むことでパラメータの保存等を行えるのです。

しかしこのマイコンには EEPROM は載っておらず、それを Flash の User Page で代用するということになります。この領域は通常の Flash 消去コマンドでは消去されないため、このような用途に向いています。一方で書き換えたいというときは専用のコマンドを打つ必要があります。

その容量と User Page の先頭アドレスはデータシートに記載されていませんが、#include <avr32/io.h> をしたソースファイルなら以下で参照することができます。

マクロ名 説明 UC3B064での値
AVR32_FLASHC_USER_PAGE_ADDRESS User Page の先頭アドレス 0x8080 0000
AVR32_FLASHC_PAGE_SIZE User Page の容量(byte) 512

今回使うデバイスにおける値を上の表に記載しておきました。

そもそも

これはそもそもの話になりますが、Flash はビットを 0 にすることはできるけど、再度 1 にすることはできません

もし例えば「0x00 という値を書いたけれど 0x0F に戻したい!」と言うことがあったとしましょう。そのビットのみ値を書き換えることは不可能です。Flash の鉄則として、値を消去する(全て1に戻す)時はページまるごと行わねばならないのです。ある領域だけ消したい時は、一旦 RAM に値を退避させた後に Page 消去、そして再度書き込みをする必要があります。何だめんどくせえ。全然 EEPROM の代用になってないじゃないか。

この User Page はそれ以上に全消去コマンドしかないので、User Page 全てを消去する必要があります。

注意!

いきなりですが、このマイコン、User Page をいじる際に大事な注意点があります。

ブートローダーの使う領域

これを守らねばおそらくマイコンは起動不能になるでしょう。このマイコン、User Page の一部にブートローダーが起動時に用いるデータを格納する必要があります。その場所は User Page 末尾の 1Word です。データシートによると以下のような感じ。

ということで、User Page を消去したら、すかさず 0x8080 01FC0x929E 0D6B を書き込む必要があるということになります。お忘れなく。

書き込む際はワードアクセスで

実際にデータをアドレッシングして書き込む際には必ず 4Bytes アクセス で行います。例えば 0x8080 0011 にデータ 0x55 を書きたい時は以下のようにします(後述するようにこれは実際にデータを書き込んでいるのではなく、Page Buffer に書き込んでいるに過ぎません)。

*(volatile unsigned int *)0x80800010 |= 0x00550000;

4Bytes アクセスではアドレスが必ず4の倍数でなければならないので、素直に 0x8080 0011 というアドレスで書き込みはできません(読み込みは可能だが)。なので以上のように工夫しています。また、このやり方はビッグエンディアンの CPU 固有のやり方であることに注意してください。

もしリトルエンディアン CPU の場合は、

*(volatile unsigned int *)0x80800010 |= 0x00005500;

となるでしょう。まあぶっちゃけ面倒くさいので、4の倍数のアドレスに値を書き込んでいくと考えておけばいいんじゃないですかね。

実際の書き込みオペレーション

ちょっと書き込み方法が特殊なので、簡単にまとめておこうと思います。

  1. CPBコマンドを発行して、Page Buffer をクリアする
  2. 所望するアドレスに値を書き込んでいく
    • 実はこの段階では Flash に書き込みは行われずに、Page Buffer という仮の領域(RAM にある)に書き込まれているに過ぎません。
  3. WUPコマンドを発行して、書き込み開始
  4. FLASHC.FSR.FRDY ビットが 1 になるまでウェイト

これで OK です。

コード

以下に示します。flash_userpage_write 内でのアドレスチェックはやっていません。
ここで強調しておきますが、flash_userpage_erase の一番下 flash_restore_configuration_word はブートローダーの使う領域を復旧させているので、絶対にこの行を消さないでください。また、実行する前には割り込みを禁止してください

#include <avr32/io.h>

void flash_userpage_write(volatile unsigned int *p, unsigned int *data, int size) {
    AVR32_FLASHC.FCR.fws = 1;
    AVR32_FLASHC.FCMD.cmd = AVR32_FLASHC_FCMD_CMD_CPB; // clear page buffer
    AVR32_FLASHC.FCMD.key = AVR32_FLASHC_FCMD_KEY_KEY;
    while( !AVR32_FLASHC.FSR.frdy );

    for(int i = 0; i < size; i++) {
        p[i] = data[i];
    }

    AVR32_FLASHC.FCMD.cmd = AVR32_FLASHC_FCMD_CMD_WUP;
    AVR32_FLASHC.FCMD.key = AVR32_FLASHC_FCMD_KEY_KEY;
    while( !AVR32_FLASHC.FSR.frdy );
}

static void flash_restore_configuration_word(void) {
    unsigned int data[1] = {0x929E0D6B};
    volatile unsigned int *addr = (void *)0x808001FC;
    flash_userpage_write(addr, data, 1);
}
void flash_userpage_erase(void) {
    AVR32_FLASHC.FCMD.cmd = AVR32_FLASHC_FCMD_CMD_EUP;
    AVR32_FLASHC.FCMD.key = AVR32_FLASHC_FCMD_KEY_KEY;
    while( !AVR32_FLASHC.FSR.frdy );

    flash_restore_configuration_word();
}

実際に使うならこんな感じですかね。

void flash_test(void) {
    Disable_global_interrupt(); // Flash 操作中は割り込みを禁止
    flash_userpage_erase(); // User Page消去&ブートローダー領域復旧
    unsigned int arr[] = {0x114514}; // テキトーな値
    flash_userpage_write(AVR32_FLASHC_USER_PAGE + 0x1c, arr, 1); // 先頭から0x1cの場所に書き込んでみます
    Enable_global_interrupt(); // Flash 操作が終わったので割り込みを有効化しておく
}