avr/eeprom.h 内蔵EEPROM制御

AVR内蔵のEEPROMを操作するための関数群が <avr/eeprom.h> に用意されています。

AVR Libc 1.6.7 で提供されている関数に基づきます。(1.7.1まで確認済み)

EEPROMの利点と注意点

利点

  • 電源をOFFにしてもデータが消えない

注意点

  • 書き込み消去がSRAMより遅い (SRAM1クロック・EEPROM数ms)
  • 書き込み回数に制限がある (10,000回保証だが壊れたという話はあまり聞かない)
  • 低圧動作時に誤動作がありえる (CPUと最低動作電圧が異なる)
  • 書き込みが非同期動作である

詳細なEEPROMの扱いは、データシートを読んでください。AVR Memory の中にEEPROMの扱いが載っています。

EEPROMアクセス

アクセスの流れ

EEPROMアクセスチャート図

EEPROMのアクセスは、必ず使用できる状態かを確認してから読み書きをする。

  1. EEPROMのアクセスが出来る状態を確認
  2. 読み書き

アクセス可能か確認

確認方法は2種類ある。

  • eeprom_is_ready()
  • eeprom_busy_wait()

その他の方法として、割込 (EE_READY_vect / EE_RDY_vect / EEPROM_READY_vect ) を使用する方法もある。

eeprom_is_ready()

EEPROMがアクセス可能かを判定する。(並行処理する場合のポーリングに用いる。)

返値

  • アクセス可能: 成功 (非ゼロ)
  • アクセス不可: 失敗 (ゼロ)
eeprom_busy_wait()

EEPROMがアクセス可能になるまでループして待つ。

EEPROMアドレス

EEPROMのアクセスは、0x0000~E2END (avr/io.hで定義) のアドレスとなります。 各AVR毎に搭載されているEEPROMのサイズが違うため、使う場合はデータシートを確認してください。

EEPROMのアドレス管理方法は、次の2つがあります。

  • 各自で管理する方法 (0x0000 , 0x0001 と指定する方法)
  • コンパイラで管理・生成する方法 (EEMEMを使用して宣言する方法)

アドレスを各自で管理する方法

各自でアドレスとサイズを把握しておかなければ、予想外に上書きなどをする場合があるため、しっかりと管理しなければならず煩雑となる。

#include <stdint.h>
#include <avr/io.h>
#include <avr/eeprom.h>

#define EEPROM_V1	0x0000	/* EEPROM uint16_t 0x0000-0x0001 2Byte */
#define EEPROM_V2	0x0002	/* EEPROM uint8_t 0x0002 1Byte */
#define EEPROM_V3	0x0003	/* EEPROM int16_t 0x0003-0x0004 2Byte */

int main( void )
{
	uint16_t value;

	/* EEPROM 読出し */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	value = eeprom_read_word((uint16_t *)EEPROM_V1);	/* EEPROM_V1の読出し */

	/* EEPROM 書込み */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_write_word((uint16_t *)EEPROM_V1, 0xCCAA);	/* EEPROM_V1へ2byte書き込み */

	/* EEPROM 書込み */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_write_byte((uint8_t *)EEPROM_V2, 0xAA);		/* EEPROM_V2へ1byte書き込み */
}

アドレスをコンパイラで管理・生成する方法

ソースコード上で、EEPROM用の変数として宣言する方法。管理が楽であるが、コードを書き換える(宣言の順番を入れ替える)と、EEPROMのアドレスが変り、昔の情報を読み出せなくなったりするので注意。

宣言方法は次の2種類がありどちらも同じです。初期値の設定も出来きます。

  • EEMEM を付ける方法
  • __attribute__((section(“.eeprom”))) を付ける方法
#include <stdint.h>
#include <avr/io.h>
#include <avr/eeprom.h>

static uint16_t EEMEM EEPROM_V1;	/* EEPROM_V1を宣言 */
static uint8_t EEPROM_V2 __attribute__((section(".eeprom")));	/* EEPROM_V2を宣言 */
static uint16_t EEMEM EEPROM_V3 = 5;	/* EEPROM_V3を宣言と初期値 5  */

int main( void )
{
	uint16_t value;

	/* EEPROM 読出し */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	value = eeprom_read_word(&EEPROM_V1);	/* EEPROM_V1の読出し */

	/* EEPROM 書込み */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_write_word(&EEPROM_V1, 0xCCAA);	/* EEPROM_V1へ2byte書き込み */

	/* EEPROM 書込み */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_write_byte(&EEPROM_V2, 0xAA);	/* EEPROM_V2へ1byte書き込み */
}

EEPROMの読み書き更新

AVR libCでは、EEPROMへのアクセスする関数として読出し・書き込み・更新の3種類が提供されている。

読出し(eeprom_read_xxx())・書き込み関数(eeprom_write_xxx())は、指定されたアドレスに対して読み書きを行う関数です。

更新(eeprom_update_xxx())は、一度読出しを行い、書き込み指定値と違うときのみ書き込みを行う関数です。これを用いることで、EEPROMを頻繁に書き換えをすることを防げます。ただし、一度読出しを行うため、動作は単純な書き込みより少し(CPU 6サイクル分)遅くなります。

EEPROMの読出し・書き込み・更新の目安時間 (ATmega88の場合)

関数 時間
eeprom_read_byte(0x0000) 4 CPUサイクル (20MHz時 200ns) データシート p23 EEREの記述より
eeprom_write_byte(0x0000, 0x00) 3.4ms データシート p22 Table6-1より
eeprom_update_byte(0x0000, 0x00) 4 CPUサイクル + 3.4ms(書き換え)

読出し関数

関数プロトタイプ 説明
uint8_t eeprom_read_byte (const uint8_t *__p) __ATTR_PURE__ EEPROM 1Byte(8bit)読出し
uint16_t eeprom_read_word (const uint16_t *__p) __ATTR_PURE__ EEPROM 2Byte(16bit)読出し
uint32_t eeprom_read_dword (const uint32_t *__p) __ATTR_PURE__ EEPROM 4Byte(32bit)読出し
float eeprom_read_float (const float *__p) __ATTR_PURE__ EEPROM float読出し
void eeprom_read_block (void *__dst, const void *__src, size_t __n) EEPROM ブロック読出し

書き込み関数

関数プロトタイプ 説明
void eeprom_write_byte (uint8_t *__p, uint8_t __value) EEPROM 1Byte(8bit)書き込み
void eeprom_write_word (uint16_t *__p, uint16_t __value) EEPROM 2Byte(16bit)書き込み
void eeprom_write_dword (uint32_t *__p, uint32_t __value) EEPROM 4Byte(32bit)書き込み
void eeprom_write_float (float *__p, float __value) EEPROM float書き込み
void eeprom_write_block (const void *__src, void *__dst, size_t __n) EEPROM ブロック書き込み

更新関数

関数プロトタイプ 説明
void eeprom_update_byte (uint8_t *__p, uint8_t __value) EEPROM 1Byte(8bit)更新
void eeprom_update_word (uint16_t *__p, uint16_t __value) EEPROM 2Byte(16bit)更新
void eeprom_update_dword (uint32_t *__p, uint32_t __value) EEPROM 4Byte(32bit)更新
void eeprom_update_float (float *__p, float __value) EEPROM float更新
void eeprom_update_block (const void *__src, void *__dst, size_t __n) EEPROM ブロック更新

サンプルコード

uint8_t のサンプル

1ByteのEEPROMアクセスのサンプルコードです。

  • eeprom_read_byte( address )
  • eeprom_write_byte( address , value )
  • eeprom_update_byte( address , value )

符号ビットを含めるかの違いのため、int8_t型を入れることも可能

#include <stdint.h>
#include <avr/io.h>
#include <avr/eeprom.h>

static uint8_t EEMEM EEPROM_V1 = 5;	/* EEPROM_V1 を宣言 */
static uint8_t EEMEM EEPROM_V2;		/* EEPROM_V2 を宣言 */

int main( void )
{
	uint8_t value;

	/* EEPROM 読出し */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	value = eeprom_read_byte(&EEPROM_V1);	/* EEPROM_V1の読出し */

	/* EEPROM 書込み */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_write_byte(&EEPROM_V1, 0xAA);	/* EEPROM_V1へ書き込み */

	/* EEPROM 更新 */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_update_byte(&EEPROM_V2, value);	/* EEPROM_V2を更新 */
}

uint16_t のサンプル

2Byte(WORD)のEEPROMアクセスのサンプルコードです。

  • eeprom_read_word( address )
  • eeprom_write_word( address , value )
  • eeprom_update_word( address , value )

符号ビットを含めるかの違いのため、int16_t型(int型)を入れることも可能

#include <stdint.h>
#include <avr/io.h>
#include <avr/eeprom.h>

static uint16_t EEMEM EEPROM_V1 = 5;	/* EEPROM_V1 を宣言 */
static uint16_t EEMEM EEPROM_V2;	/* EEPROM_V2 を宣言 */

int main( void )
{
	uint16_t value;

	/* EEPROM 読出し */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	value = eeprom_read_word(&EEPROM_V1);	/* EEPROM_V1の読出し */

	/* EEPROM 書込み */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_write_word(&EEPROM_V1, 0xCCAA);	/* EEPROM_V1へ書き込み */

	/* EEPROM 更新 */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_update_word(&EEPROM_V2, value);	/* EEPROM_V2を更新 */
}

uint32_t のサンプル

4Byte(DWORD)のEEPROMアクセスのサンプルコードです。

  • eeprom_read_dword( address )
  • eeprom_write_dword( address , value )
  • eeprom_update_dword( address , value )

符号ビットを含めるかの違いのため、int32_t型(long int型)を入れることも可能

#include <stdint.h>
#include <avr/io.h>
#include <avr/eeprom.h>

static uint32_t EEMEM EEPROM_V1 = 5;	/* EEPROM_V1 を宣言 */
static uint32_t EEMEM EEPROM_V2;	/* EEPROM_V2 を宣言 */

int main( void )
{
	uint32_t value;

	/* EEPROM 読出し */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	value = eeprom_read_dword(&EEPROM_V1);	/* EEPROM_V1の読出し */

	/* EEPROM 書込み */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_write_dword(&EEPROM_V1, 0x55DDCCAA);	/* EEPROM_V1へ書き込み */

	/* EEPROM 更新 */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_update_dword(&EEPROM_V2, value);	/* EEPROM_V2を更新 */
}

float のサンプル

float型のEEPROMアクセスのサンプルコードです。浮動小数点の変数を取り扱えます。

  1. eeprom_read_float( address )
  2. eeprom_write_float( address , value )
  3. eeprom_update_float( address , value )
#include <stdint.h>
#include <avr/io.h>
#include <avr/eeprom.h>

static float EEMEM EEPROM_V1 = 10.1;	/* EEPROM_V1 を宣言 */
static float EEMEM EEPROM_V2;	/* EEPROM_V2 を宣言 */

int main( void )
{
	float value;

	/* EEPROM 読出し */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	value = eeprom_read_float(&EEPROM_V1);	/* EEPROM_V1の読出し */

	/* EEPROM 書込み */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_write_float(&EEPROM_V1, 20.2);	/* EEPROM_V1へ書き込み */

	/* EEPROM 更新 */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_update_float(&EEPROM_V2, value);	/* EEPROM_V2を更新 */
}

配列のサンプル

EEPROM上に配列を宣言して取り扱うことも可能です。

1バイト単位の制御

  • eeprom_read_byte( address )
  • eeprom_write_byte( address , value )
  • eeprom_update_byte( address , value )

ブロック単位(配列全体)の制御

  • eeprom_read_block( dist_address , src_address(EEPROM) , size )
  • eeprom_write_block( src_address , dist_address(EEPROM) , size )
  • eeprom_update_block( src_address , dist_address(EEPROM) , size )
#include <stdint.h>
#include <avr/io.h>
#include <avr/eeprom.h>

static uint8_t EEMEM EEPROM_V1[10];	/* EEPROM_V1[10] を宣言 */
static uint8_t EEMEM EEPROM_V2[] = { 10, 3, 20, 4};	/* EEPROM_V2[] を宣言・初期値 */

int main( void )
{
	uint8_t value;
	uint8_t vars[10];

	/* EEPROM 読出し */
	/* 1Byte単位のアクセス */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	value = eeprom_read_byte(&EEPROM_V1[0]);		/* EEPROM_V1[0]の読出し */
 
	/* EEPROM 書込み */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_write_byte(&EEPROM_V1[1], 0xAA);		/* EEPROM_V1[1]へ書き込み */
 
	/* EEPROM 更新 */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_update_byte(&EEPROM_V1[2], value);		/* EEPROM_V1[2]を更新 */


	/* 配列単位(ブロック)のアクセス */
	/* EEPROM 読出し */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_read_block(vars, EEPROM_V1, sizeof(vars));		/* EEPROM_V1[]をvars[10]のサイズまとめて読出し */

	/* EEPROM 書込み */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_write_block(vars, EEPROM_V1, sizeof(vars));		/* EEPROM_V1[]へvars[10]をまとめて書き込み */
 
	/* EEPROM 更新 */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_update_block(vars, EEPROM_V1, sizeof(vars));		/* EEPROM_V1[]をvars[10]でまとめて更新 */



	/* 配列単位(ブロック)のアクセス その2 10Byteを指定する場合で上と同じ動作 */
	/* EEPROM 読出し */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_read_block(&vars[0], &EEPROM_V1[0], 10);		/* EEPROM_V1[0]からvars[0]へ10Byteまとめて読出し */

	/* EEPROM 書込み */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_write_block(vars, &EEPROM_V1[0], 10);		/* EEPROM_V1[]へvars[10]をまとめて書き込み */

	/* EEPROM 更新 */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_update_block(&vars[0], EEPROM_V1, 10);		/* EEPROM_V1[]をvars[10]でまとめて更新 */
}

文字列のサンプル

EEPROM上に配列を宣言して取り扱うことも可能です。

ブロック単位(配列・文字列全体)の制御

  • eeprom_read_block( dist_address , src_address(EEPROM) , size )
  • eeprom_write_block( src_address , dist_address(EEPROM) , size )
  • eeprom_update_block( src_address , dist_address(EEPROM) , size )
#include <stdint.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/eeprom.h>

static uint8_t EEMEM EEPROM_V1[12] = "Hello World";	/* EEPROM_V1[12] を宣言 */

int main( void )
{
	char str[12];

	/* 配列単位(ブロック)のアクセス */
	/* EEPROM 読出し */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_read_block(str, EEPROM_V1, sizeof(str));		/* EEPROM_V1[]をstr[]へまとめて読出し */

	/* EEPROM 書込み */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_write_block(str, EEPROM_V1, sizeof(str));		/* EEPROM_V1[]へstr[]をまとめて書き込み */
 
	printf("%s", str);
	sprintf(str,"hello2");

	/* EEPROM 更新 */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_update_block(str, EEPROM_V1, sizeof(str));		/* EEPROM_V1[]をstr[]でまとめて更新 */

}

構造体のサンプル

EEPROM上に構造体を宣言して取り扱うことも可能です。

ブロック単位(構造体)の制御

  1. eeprom_read_block( dist_address , src_address(EEPROM) , size )
  2. eeprom_write_block( src_address , dist_address(EEPROM) , size )
  3. eeprom_update_block( src_address , dist_address(EEPROM) , size )
#include <stdint.h>
#include <avr/io.h>
#include <avr/eeprom.h>

typedef struct {
   uint8_t x,y;
   uint16_t z;
} st_t;

static st_t EEMEM EEPROM_st = { 1, 3, 579 };	/* 構造体 EEPROM_st を宣言 */

int main( void )
{
	st_t st;
	uint8_t val;

	/* 構造体(ブロック)のアクセス */
	/* EEPROM 読出し */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_read_block(&st, &EEPROM_st, sizeof(st_t));		/* EEPROM_stをstへ読出し */

	/* EEPROM 書込み */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_write_block(&st, &EEPROM_st, sizeof(st_t));		/* EEPROM_stへstを書き込み */

	/* EEPROM 更新 */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	eeprom_update_block(&st, &EEPROM_st, sizeof(st_t));		/* EEPROM_stをstで更新 */


	/* 構造体のメンバへアクセス */
	/* EEPROM 読出し */
	eeprom_busy_wait();		/* 読み書き可能までwait */
	val = eeprom_read_byte(&EEPROM_st.x);		/* EEPROM_st.xをvalへ1byte読出し */

}

参考文献

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です