STM32でFlashメモリに読み書きする方法

STM32マイコンソフト

マイコンプログラムをしていると、電源を落としても記憶しておいて欲しいデータが幾つか出てくるものです。

そこで今回は、STM32の内蔵Flashメモリにデータを書き込み/読み出しの方法を説明します。HALが用意されており、比較的簡単に実装が可能です。

こんな方におすすめの記事
  • STM32で電源を落としても記憶されるデータを書き込みたい
  • Flashメモリの特性を知りたい

Flashメモリとは?

Flashメモリは不揮発性のメモリで、電源を落としても情報が記憶されたままです。マイコン内蔵のものは、マイコンのプログラムやデータを保存するのに利用されています。

Flashメモリはその物理的な特性上、書き込みに関してRAMとは異なる下のような特徴があります。

  1. 書き込みはビットをHigh(1)からLow(0)にしか変更出来ません。
  2. 削除(erase)を行うことでビットを(1)に変更します。
  3. 削除はByte単位では行えません。セクタと呼ばれるブロック単位の全データを(1)にします。
  4. 書き込み回数には制限があります。(STM32は1万回まで補償)

この特徴の為に、Flashメモリはデータを頻繁に書き替えるような用途には不向きです。あっという間に書き込み回数上限に達してしまいますし、セクタ全体を削除してから書き込みを行うので書き換えに時間がかかります。保存するデータとしては、たまに行う動作設定データなどになると思います。

また、データの一部を書き換えるだけでもセクタ全体を削除する必要があるので、1バイト書き換えるだけでもセクタ全体のデータを一旦RAMにコピーしておく必要が有ります。

Flashメモリ操作の前処理

Flashメモリを操作するプログラムを記述する前に、操作するセクタの決定とリンカスクリプトの編集が必要です。

使うセクタを決める

STM32F7シリーズのFlashメモリ構造をデータシートから見てみます。

STM32F7シリーズのFlashメモリ構造
STM32F767データシートより引用

下の方のInformationBlockは特別に機能があるので、通常はMainMemortBlockを使って行くことになります。また、プログラムデータはSector0から順番に詰まっていくのでSector11とかをデータ書き込みエリアとして使うのが良さそうです。(大抵の用途には大きすぎるエリアなのですが、、)

また、STM32のFlashメモリには間違って書き込み/削除を起こさないようにライトプロテクトがかけられています。対象のレジスタを操作することでプロテクトを解除できます。

STM32のFlashメモリは2バンクに分割して使う事も出来ます。2バンクに分割すれば、書き込みを行っている最中にもう片方のバンクから読み出すことも可能になるのですが、今回は実施していません。

リンカスクリプトを編集

書き換えに使うFlashメモリのセクタがSector11と決まりました。このセクタはプログラムの動作によって中身が書き換えられるので、プログラムデータが書き込まれないように(ビルドされないように)設定をしておかないといけません。

プログラムデータが書き込まれないようにするためには、リンカスクリプトファイルの編集を行います。

リンカスクリプトファイルは、プロジェクトフォルダに”デバイス型式_FLASH.ld”という名称で存在しています。

”STM32F767ZITX_FLASH.ld”を例にして中身を見てみると、下記のようにメモリの種類とアドレスと容量が記載された箇所があります。

MEMORY
{
  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 512K
  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 2048K
}

これのFLASHエリアを削減して、代わりにDATA領域を追記します。編集後のファイルは下記のようになります。セクタの開始アドレスと容量は、データシートを確認してください。

MEMORY
{
  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 512K
  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 1792K
  DATA     (rx)    : ORIGIN = 0x81C0000,   LENGTH = 256K
}

HALを使ってFlashメモリを操作

STM32ではHALが用意されていて、それを使えば比較的容易にFlashを操作できます。

それでは、HALを使ってSector11にデータを書き込んでみます。

ライトプロテクトの解除

まずは下記のHALでライトプロテクトの解除を行います。

HAL_FLASH_Unlock();

このHALをコールすることで、Flashメモリの削除と書き込みが可能になります。

Flashメモリの削除(全ビット”1”にする)

書き込み前には削除を行う必要があります。繰り返しになりますが、Flashメモリの特性上、書き込みはビットをHigh(1)からLow(0)にしか変更出来ません。なので、削除によって全ビットを(1)にする必要があるのです。

HAL_StatusTypeDef HAL_FLASHEx_Erase(
    FLASH_EraseInitTypeDef *pEraseInit,
    uint32_t *SectorError
);

第一引数の”FLASH_EraseInitTypeDef” は削除の為の設定をまとめた構造体になります。下記が設定例になります。

FLASH_EraseInitTypeDef erase;

//セクタを選択して削除するか、全削除するかを選択します。
erase.TypeErase = FLASH_TYPEERASE_SECTORS;

//セクタ削除の場合、セクタを指定します。
erase.Sector = FLASH_SECTOR_11;

//指定したセクタを先頭として、何セクタ削除するか指定します。
erase.NbSectors = 1;

//マイコンの動作電圧を指定します。
erase.VoltageRange = FLASH_VOLTAGE_RANGE_3;

第二引数の”SectorError” には、削除が失敗した場合にはそのセクタのポインタが入ります。問題無く削除できた場合は0xFFFFFFFFが入ります。この値を使ってエラー制御を実装することができます。

書き込み

削除が終わったら次は書き込みです。下の関数で1~8Byte単位で行えます。

HAL_StatusTypeDef HAL_FLASH_Program(
    uint32_t TypeProgram,
    uint32_t Address,
    uint64_t Data
);

第一引数の”TypeProgram” で何バイト書き込むか下記で設定します。下記の4つから選択できます。

#define FLASH_TYPEPROGRAM_BYTE        ((uint32_t)0x00U)
#define FLASH_TYPEPROGRAM_HALFWORD    ((uint32_t)0x01U)
#define FLASH_TYPEPROGRAM_WORD        ((uint32_t)0x02U)
#define FLASH_TYPEPROGRAM_DOUBLEWORD  ((uint32_t)0x03U)

残りの引数 ”Adress”と”Data” は書き込み先のアドレスと書き込むデータになります。

ライトプロテクトを有効化

書き込みが終わったら、間違って書き込みを行わないよう、プロテクトを有効に戻します。

HAL_FLASH_Lock();

読み出し

Flashデータの読み出しを行うHALは用意されていません。

Flashメモリからデータを読み出すには、アドレスをポインタで指定して読み出します。

まとめ

HALの操作自体はそんなに難しくないですが、Flashメモリの特性を理解していないとつまずくポイントがいくつも有ります。最低限、下の2点は抑えておく必要が有ると思います。

  • 書き込みは1→0にしかできない。
  • 0→1に戻す削除は、セクタ単位でしか行えない。

STM32L1xxシリーズには、データ保存領域としてFlashメモリ以外にEEPROMが内蔵されています。

EEPROMはFlashメモリと違ってイレース作業が不要なので、セクタ全体をイレースせずに特定のバイトのみの書き換えが可能です。

小サイズのデータを書き換える場合はEEPROM搭載のシリーズを検討してみても良いかもしれませんね。

その他のHowto記事

STM32のソフト開発に関するHowtoは他にも記事があります。こちらも参考にしてください。

PCとUSBで接続してシリアル通信する方法

割り込みを使う方法

Nucleoをデバッガとして使う方法

コメント