stm32f405rg flashに書き込み

こんにちは

今回はstm32f405rgtのフラッシュにデータを書き込み、読み込み方を書いていきます。

ここに書いてあるのはあくまで一例なのでそこのところはご了承ください。いつも通りこれで動きましたという紹介のような形で書いていきます。

 

それでは本文に入っていきたいと思います。

今回はSTMicroelectronicsの公式サイトにあるja.DM00031020.pdfから絶対に必要だと思われる場所を一部抜粋します。f:id:sora_siro:20180525111624p:plain

f:id:sora_siro:20180525111735p:plain

今回はこの中のセクタ11を使いたいと思います。

 

これでセクタとフラッシュメモリにどのように書き込みができるか削除ができるか、メモリの番地がわかりました。

 

それではプログラムに入っていきたいと思います。

まずはSTM32F405RGTx_FLASH.ldファイルを開いてください。そこのだいたい40行目を以下のように追記、変更してください。

/* Specify the memory areas */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 896K
DATA (rx) : ORIGIN = 0x80E0000, LENGTH = 128k
}

このこうすることでDATAっていうプログラムを書き込まない部分を作ることができます。*1

 

それではプログラムにはいっていきたいと思います。

私が実装したソフトは以下のようになりました。

///////////////////////////////////////////////////////////////////////
// ereaseFlash
// [argument] nothing
// [Substitutiong] nothing
// [return] nothing
///////////////////////////////////////////////////////////////////////
void eraseFlash( void )
{
  FLASH_EraseInitTypeDef erase;
  erase.TypeErase = FLASH_TYPEERASE_SECTORS;  // セクタを選ぶ
  erase.Sector = FLASH_SECTOR_11;       // セクタ11を指定
  erase.NbSectors = 1;    // 消すセクタの数(今回はひとつ)
  erase.VoltageRange = FLASH_VOLTAGE_RANGE_3; // 3.3Vで消去

  uint32_t pageError = 0;

  HAL_FLASHEx_Erase(&erase, &pageError);  // HAL_FLASHExの関数で消去
}

///////////////////////////////////////////////////////////////////////
// writeFlash
// [argument] address, *data, size
// [Substitutiong] nothing
// [return] nothing
///////////////////////////////////////////////////////////////////////
void writeFlash(uint32_t address, uint8_t *data, uint32_t size )
{
  HAL_FLASH_Unlock();   // フラッシュをアンロック
  eraseFlash();     // セクタ11を消去
  do {
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, address, *data); // 1byteずつフラッシュに書き込む
++address;
++data;
--size;
  } while ( size > 0);

  HAL_FLASH_Lock();   // フラッシュをロック
}

///////////////////////////////////////////////////////////////////////
// loadFlash
// [argument] address, *data, size
// [Substitutiong] struct
// [return] nothing
///////////////////////////////////////////////////////////////////////
void loadFlash(uint32_t address, uint8_t *data, uint32_t size )
{
  memcpy(data, (uint8_t*)address, size); // addressの内容をコピー
 
}

 

私は構造体自体で書き込み・読み込みをするようにしています。

 理由としてはフラシュに書き込みするためには1byteずつ書き込みになるので構造体だと都合がよかったため使いました。

また、書き込む構造体を増やしたいときは次のように改造すればできます。

 

///////////////////////////////////////////////////////////////////////
// writeFlash
// [argument] address, *data, size
// [Substitutiong] nothing
// [return] nothing
///////////////////////////////////////////////////////////////////////
void writeFlash(uint32_t address, uint8_t *data, uint32_t size, uint8_t *data2, uint32_t size2 )
{
  HAL_FLASH_Unlock();   // フラッシュをアンロック
  eraseFlash();     // セクタ11を消去
  do {
    // 1byteずつフラッシュに書き込む
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, address, *data);
++address;
++data;
--size;
  } while (--size > 0);
 
do {
    // 1byteずつフラッシュに書き込む
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, address, *data2);
++address;
++data;
--size;
  } while (size2 > 0 );

  HAL_FLASH_Lock();   // フラッシュをロック
}

///////////////////////////////////////////////////////////////////////
// loadFlash
// [argument] address, *data, size
// [Substitutiong] struct
// [return] nothing
///////////////////////////////////////////////////////////////////////
void loadFlash(uint32_t address, uint8_t *data, uint32_t size, uint8_t *data2, uint32_t size2 )
{
  memcpy(data, (uint8_t*)address, size);
address = address + size;
memcpy(data2, (uint8_t*)address, size2);
address = address + size2;
}

ただ単に引数を増やしてアドレスの更新をしてあげるだけです。

実際に動作確認をした際のソフトは以下のようになりました。*2

 

#include <string.h>
// flash use address ( sector11 )
const uint32_t start_address = 0x80E0000; //sentor11 start address
const uint32_t end_adress = 0x80FFFFF;
 
typedef struct {
int x;
int y;
int dir;
float angle;
float distance;
}POSITION;

POSITION position;

int main()
{
loadFlash( start_address,(uint8_t*)&position, sizeof(POSITION) );
printf("%d,%d,%f,%f\r\n",
position.x, position.y,position.angle, position.distance);

position.x = 100;
position.y = 150;
position.angle = 1.52;
position.distance = 0.18;

writeFlash( start_address,(uint8_t*)&position, sizeof(POSITION) );

}
 

こうしてprintfで構造体positionのメンバの値の変化を一度目の電源を入れたときと二度目で見比べてあげれば書き込まれているかどうかがわかると思います。

 

まとめ

私の勉強不足によりこまかい動作に対しての説明ができず申し訳ないです。

HALの関数の動作に関してはSTMicroelectronicsの公式サイトにある関数をまとめたドキュメントから参照していただければと思います。

 

最後に

知見のあるかたで、ここをこうしたほうがいいということがあればご指摘していただきたいです。よろしくお願いします。

 

更新履歴

2018.5/16

writeFlash関数の中のdo while文の条件式が分かりづらいとの指摘をいただいたので訂正しました。

2018.6/9 

誤字を指摘していただいたため訂正しました。

 

 

 

*1:リンカファイルに関して勉強不足なので間違っているかもしれないです。知見のある方で何か問題があれば指摘をしていただきたいです。

*2:一部抜粋で書きます。