はい、これぞ僕が記事にしたかった内容です!!!さっきの SPI とか適当にやってましたが、この記事はデータシートを参照しながらガチで行きます。
Flash ってなんだ?プログラム書き込みか?と思われるでしょうが、そんな感じです。ここでは、プログラム書き込みで使っていないブロックを EEPROM 的にデータ保存領域として使わせていただく と言う意味で言っています。つまり、パラメータ等の値を不揮発性メモリに記憶したい!って思った時、この STM32 の Flash 構造がとっても役に立つんです。これはもう使うしか無いですよね!!!!(荒い鼻息)
また、僕と同じように OpenOCD を使う場合は Flash 領域でプログラムが占有するブロックのみが消去されるので、Flash 領域後半にデータを書き込んでおけばとってもよろしいのです。
という訳でまずはこの領域について説明をします。
メモリ空間
ではまずメモリ上のどこにこの flash 領域があるか見てみます。データシートにはこんな感じに書いてあります。
この「メインメモリ」に書くんですね。といっても頭から自分のデータを書き込んではいけません。プログラムが頭から書き込まれているからです。
順序が逆になりましたが、この表の前ページにこんなことが書かれています。
型番によってメインメモリ領域が異なるのです。僕が持っている F303K8T6 では 32 ブロックだそうです。ということで、実際のメインメモリ領域は表とは違ってページ31までということがわかりました。
データアクセス方法
読み出し
データの読み出しは特に意識しなくてもそのままアクセスすることが出来ます。つまりポインタによって
uint16_t tmp = *(uint16_t *)0x08000000;
とできる、というわけです。ポインタに即値使うのってすっごく違和感ありますね。
書き込み/イレース時のロックについて
flash 領域は通常何度も書き換えられる領域ではないため(寿命の問題もある)、通常は書き込み保護されています。ロックがかかった状態で書き込み及びイレースを行うとプロセッサは HardFault を発行し動作を停止します。デバッガで実際にやらかしてみると、
Infinite_Loop:
b Infinite_Loop
となり無限ループで停止してしまいます。また、領域外アクセスもダメで、これも HardFault します。
ロックの解除の方法ですが、こんな感じです。
つまりこうすればいいんですね。
FLASH->KEYR = 0x45670123;
FLASH->KEYR = 0xCDEF89AB;
以下、それぞれ書き込み手順・イレース手順をデータシートから抜粋して示します。
書き込み
書き込みは通常のポインタアクセスでできます。しかしその前後に処理が必要です。以下にデータシートに記載の手順を示します。
これだけでOKです。なお、アクセスは16ビットでしてください。8ビットのデータを格納する場合は工夫して詰めてもいいですが、素直に上位8ビットは空けたほうが楽かとは思います。
注意することと言えば、書き込み前にブランクチェック・イレースを忘れないことですかね。ブランクチェックは指定アドレスの値が 0xFFFF
であればイレース済みであるとみなすことが出来るので即座に書き込みしてOKです。一方、そうでない場合は何らかのデータが書き込まれているので、事前にイレースが必要となります。
イレース
イレースには2種類の方法があります。それぞれページ消去・全体消去です。全体消去は、プログラム領域も死んで(多分)危なそうなので紹介しません。
ページ消去ですが、名前の通りまとめてイレースしか出来ません。なので、例えば 0x08001000 に書き込みをして、続いて 0x08001002 に書き込みをしたとしましょう。ここで 0x08001000 の値を変更したくなったとします。しかしここには既に値が書き込まれていますから、このページに含まれる他の値をすべて退避させてページごとイレースするしかありません。
これも手順が載っています。こんな感じに。
こんな感じです。本当にこれに沿ってやれば出来ます。さっすが。
コード
という訳でこれを反映したコードを示します。
コード本体:
ヘッダ:
一応使い方を説明しますと、まず初期化として flash_init()
を呼び出してください。これでフラッシュの書き換えがアンロックされます。書き込みの際は flash_write
を利用します。ここで、万が一指定アドレスの領域がイレースされていなかったら処理を中断するとともに false
(=0) を返します。事前に flash_erase_at
でイレースしておいてください。なお、アドレス指定・データ読み出しは以下のようにしてください。
flash_erase_at(FLAH_BLOCK_PTR(31, 0)); // FLAH_BLOCK_PTRで31ブロック目の0番地目のアドレスを取得・そのブロックを全てイレース
flash_write(114, FLAH_BLOCK_PTR(31, 0)); // データ「114」を書き込み
flash_write(514, FLAH_BLOCK_PTR(31, 1));
lcd_printf("%d", FLASH_BLOCK_ACCESS(31, 0)); // FLASH_BLOCK_ACCESSで31ブロック目の0番地目の値を取ってくる
lcd_printf("%d", FLASH_BLOCK_ACCESS(31, 1)); // lcd_printfはvs_printfを利用したもの
これでマウスの迷路情報を格納できるね!!やった!!!
重要なお知らせ
この「弄ってみた」シリーズですけど、これで一旦終わりにします。どうもシリーズ化すると「書かなきゃ」っていう気持ちが出てきてやる気にならんのですよね。以後はシリーズ的じゃなくていつもみたいになんとなくやっていきますよ。