電源供給がなくても設定値を保存したい場合に使用します。Body MCU は個々のロボットに固有なサーボモータのオフセット値などを記憶します。
◀ この記事の前に:
▶ この記事の次に:
フラッシュメモリを使う目的
MCU内のメモリはおもにプログラムを保存する ROMと 変数を保存する RAMがあります。ROMは電源の供給がなくても記憶が保存されます。RAMは電源の供給がなくなると記憶は消えます。
ROMに記憶させるためにはプログラムの書き込みが必要です。プログラムの書き込みは専用の回路やツールが必要です。個々のロボットに固有な設定値や保存しておきたい値を記憶するためには向きません。
このような値を保存しておくために、電源の供給がなくても値を保存できるメモリが必要です。この目的で使用するのがフラッシュメモリです。

EEPROMエミュレーション
しかし、Body MCUは STM32L4で EEPROMを持っていません。そのためフラッシュメモリの一部を EEPROMのように使用します。具体的には EEPROMエミュレーションライブラリを使用します。
Technical Considerations Flash と EEPROM の違い
FlashとEEPROMはどちらも電源を切っても内容が保持される不揮発メモリです。Flashは ROM のように見えますが、ソフトウェアで書き込みできたり、専用命令で消去できたり、電源断でもデータが残ったり、ROMと異なる特徴があります。しかし、STM32 の Flash 制御回路はソフトウェアから書き込み操作が細かくできる設計になっています。そのため、EEPROMのように使用できる可能性があります。
STM32 Flash が持つ機能は、ページ単位で erase / Word 単位(32bit)で書き込み可能 / 書き込み回数保証(1〜10万回)/ 書き込みエラー検出 / 電源断時のエラーから回復しやすい動作があります。
サーボモータのオフセット値
サーボモータは頭部や四肢など各部位によって最初の位置(原点)と動く範囲(可動範囲)が異なります。
サーボモータにはロボットを組み立てる前から、各部位を正しい方向で接続して保持できるようにハブが付いています。このハブによりサーボモータと各部位を正しい位置(角度)関係で接続することができます。
実際は部品のバラツキや取付誤差により、その接続位置(角度)が微妙にずれる場合があります。また時間が経つとズレが大きくなる場合もあります。二足歩行はそのズレに影響を受けます。本来であれば片足を上げるところでこのズレにより足が上がらないということが起きます。
それを調整して本来の位置にすることが必要です。このズレを「オフセット」と呼びます。オフセットは個々のサーボモータにより異なります。また個々のロボットによっても異なります。そのため、ロボットごとに調整して調整した値(オフセット値)を記憶しておくことが必要になります。
オフセット値はロボットごとに異なります。また、調整した後に記憶します。そこに設定値を変えることができて記憶することができるフラッシュメモリを使用します。
初期化の有無を判別する
このとき EEPROMに保存してあるデータが正しくないと動作が不安定になる可能性があります。そのため、保存してあるデータが過去に正しく書き込んだか否かを判別する必要があります。
判別する方法として、メモリの 0番地と 1番地に 0xAAを書き込みます。リセット後のセットアップ時にメモリの 0番地と 1番地を確認して 0xAAが書き込んであれば、過去に正しくデータを書き込んだことを確認できます。
0番地と 1番地の内容が 0xAAでない場合は、過去に正しくデータを書き込んでいないことになります。この場合は、データを初期化して 0番地と 1番地に 0xAAを書き込んで初期化が完了している(データが正しい)ことが次回以降に確認した場合に、過去に正しくデータを書き込んだことを確認できるようにします。
バージョンを判別する
EEPROM上のデータは、現在はサーボモータのオフセット値だけを記憶しています。将来、オフセット値以外の設定値を記憶する可能性があります。この場合、データを保存する場所が変わることがあります。
電源が OFFになってもそのまま記憶しています。データの保存場所が変わった場合、前に保存したデータをそのまま読み出すと保存場所が異なるために誤った設定となってしまいます。この問題を防ぐために「バージョン」を設けます。データの保存場所(構成)が変わった場合にバージョンを変更します。読み出し時に保存してあるバージョンとプログラムに記録しているバージョンを比較して、データの保存場所(構成)の変更の有無を確認します。
バージョンの変更がない場合は、前に保存してあるデータをそのまま設定値として使用します。変更があった場合はデータの初期化を行います。
EEPROM 上の割り当て
EEPROM上の割り当ては下図のとおりです。
0番地と 1番地は初期値(前項)の記憶場所です。データは 2番地から配置しました。
Technical Considerations 「アドレス」は本当のアドレスではない
EEPROMエミュレーションライブラリを使用するときの「アドレス」は、正確にはメモリ上のアドレスとは異なります。フラッシュメモリの特徴として書き換え寿命(耐用回数)があります。そのため同じアドレスを繰り返して使用することは避ける必要があります。EEPROMエミュレーションライブラリではアドレスの代わりに「検索キー」でデータの記憶場所を特定します。検索キーを使用した場合でもアドレスと同じ様に開始位置やサイズは自らが管理する必要があります。
関数の概要
関数は以下のとおりです。
外部関数
他のジュールやファイルの外部からアクセス可能な関数です。
setupEEPROM()
引 数:
・なし
分 類:
API( セットアップ、他 )
機 能:
EEPROMの初期化を行います。過去に記憶した場合はその値をオフセット値として使用します。過去に記憶していない場合はすべて 0として扱います。
関係する関数:
上位関数: セットアップ
下位関数: initEEPROM() / EEPROMを使用するための初期化
この関数のコードはこちらです。
writeSVparamToEEPROM( int nIndex, int* pram, int nSize )
引 数:
int nIndex: EEPROMのアドレス( 正確にはアドレスではありません )
int* pram: 書き込むオフセット値( 8要素の int型の配列 )
int nSize: 書き込むオフセット値の数( 8要素 )
戻り値:
なし
分 類:
API( EEPROMアクセス、 アプリケーション、他 )
機 能:
サーボモータのオフセット値を EEPROMに書き込みます。
関係する関数:
上位関数: ー
下位関数: ー
この関数のコードはこちらです。
readSVparamFromEEPROM( int nIndex, int* pram, int nSize )
引 数:
int nIndex: EEPROMのアドレス( 正確にはアドレスではありません )
int* pram: 読み込むオフセット値( 8要素の int型の配列 )
int nSize: 読み込むオフセット値の数( 8要素 )
戻り値:
なし
分 類:
API( EEPROMアクセス、 アプリケーション、他 )
機 能:
サーボモータのオフセット値を EEPROMから読み込みます。
関係する関数:
上位関数: ー
下位関数: ー
この関数のコードはこちらです。
内部関数
外部から直接アクセスできずモジュール( 特定のスコープ内 )で使用する関数です。
initEEPROM()
引 数:
・なし
分 類:
API( セットアップ、他 )
機 能:
EEPROMの初期化を行います。過去に記憶した(書き込んだ)場合はその値をオフセット値として使用します。過去に記憶していない(書き込んでいない)場合はすべて 0として扱います。
関係する関数:
上位関数: setupEEPROM() / セットアップ
下位関数: -
この関数のコードはこちらです。●
データの構造
データの構造は以下のとおりです。
グローバル定義
グローバル定義は以下のとおりです。
定 義
EEPROM内の構造
//---- Version of EEPROM usage ----
constexpr uint16_t EEPROM_VERSION = 1;
//---- Confirmation of EPPROM usage ----
constexpr uint8_t EEPROM_MAGIC = 0xAA;
//-------- Address --------
constexpr size_t EEPROM_HEADER_OFFSET = 0;
constexpr size_t EEPROM_DATA_OFFSET =
EEPROM_HEADER_OFFSET +
sizeof(EEPROMHeader);
//.... Size ....
constexpr size_t EEPROM_SIZE =
EEPROM_DATA_OFFSET +
sizeof(SV_PARAMETERS);| 名 称 | 型 | 値 | 説 明 |
|---|---|---|---|
| EEPROM_VERSION | uint16_t | – | データ部の構成のバージョン 変更があるたびにインクリメント |
| EEPROM_MAGIC | uint8_t | 0xAA | 書き込み済みか否かを確認するデータパターン |
| EEPROM_HEADER_OFFSET | size_t | – | ヘッダー部の位置 |
| EEPROM_DATA_OFFSET | size_t | – | データ部の位置 |
| EEPROM_SIZE | size_t | – | 使用領域のサイズ |
構造体
構造体は以下のとおりです。
EEPROM ヘッダ部
//-------- Data structure stored in EEPROM --------
//.... Header ....
struct EEPROMHeader {
uint8_t configured[2];
uint16_t version;
};| 名 称 | 型 | 値 | 説 明 |
|---|---|---|---|
| configured[2] | uint8_t | – | 書き込み済みか否かを確認するデータ |
| version | uint16_t | – | データ部の構成のバージョン |
サーボモータのオフセット値
//.... Servomotor parameters ....
// Motor settings saved in non-volatile memory
// Scale as needed
typedef struct {
int svOffset;
} SV_PARAMETER;
// The argument specifies the servomotor index, defined separately.
typedef struct {
SV_PARAMETER svParameters[SVN_MAX];
} SV_PARAMETERS;個々のサーボモータのオフセット値
| 名 称 | 型 | 値 | 説 明 |
|---|---|---|---|
| svOffset | int | – | サーボモータのオフセット値 |
すべてのサーボモータのオフセット値
| 名 称 | 型 | 値 | 説 明 |
|---|---|---|---|
| svParameters[] | SV_PARAMETER | – | すべてのサーボモータのオフセット値を保持する配列構造 要素の並びはこちらです。 |
グローバル変数
グローバル変数は以下のとおりです。
サーボモータのオフセット値
//-----------------------------------------------
// EEPROM
//-----------------------------------------------
SV_PARAMETERS svParameters;| 名 称 | 型 | 値 | 説 明 |
|---|---|---|---|
| svParameters | SV_PARAMETERS | – | すべてのサーボモータのオフセット値を保持する配列 要素の並びはこちらです。 |
コード
コードは以下のとおりです。
外部関数
setupEEPROM()
EEPROMの初期化を行います。
/*----------------------------------------------------------------------------*/
/* Request to control power
/* param : -
/* return : -
/*----------------------------------------------------------------------------*/
void setSavingPower( uint8_t nSource, bool bPower )
{
//.... save status ....
if( bPower == true ){
gSAstatus = gSAstatus | bPower; // request power on
} else {
gSAstatus = gSAstatus &( ~bPower ); // request power off
}
//.... consolidate power control requests ....
if( gSAstatus != true ){
digitalWrite( PIN_PS_SA, PS_ON ); // power on
} else {
digitalWrite( PIN_PS_SA, PS_OFF ); // power off
}
}getSavingPowerAmp()
省電力の状態を返す。
/*----------------------------------------------------------------------------*/
/* Get saving status
/* param : -
/* return : status each requests
/*----------------------------------------------------------------------------*/
uint8_t gSPstatus()
{
return gSPstatus;
}参考情報
参考になる情報は以下のとおりです。
このホームページ内
- 発: M
他のWebサイト
- M: I2


