AVRはハーバード

ふつうマイコンって、コードと定数はROMに、初期値なし変数はRAMに置き、初期値付き変数は、初期値をROMに置いてスタートアップでRAMにコピーしますよね。ところが、AVRって定数もRAMにコピーしてから使うんですね。ただでさえRAM容量乏しいのに、なんでそんな無駄なことするんだろうと思いましたが、それもそのはずで、AVRってPICと同じくハーバード・アーキテクチャなんですね。コードのROM(プログラムメモリ)とデータのRAM(データメモリ)が別々のバスにぶら下がってるわけで、定数といえどデータはRAMの方にないといけないわけです。


あれ? じゃあどうやって最初に値をセットしてるん?


もちろん、定数や初期値は不揮発のROMに置いとかないといけないわけで、やはりスタートアップでRAMにコピーしているようです。コンパイラの吐いたリストを解読してみるとLPMないしELPMという命令が使われています。これはプログラムメモリからレジスタへのロード命令です。許されるアドレッシングはZレジスタ間接のみ。また当然のことながら、通常のLD命令より多くクロックを要するようです。いわばノイマンアーキテクチャ風に振舞うわけですからね。


やはりテーブルのような大量のデータをRAMに移して使うのは避けたいところ。
C言語で記述するにはどうすればいいのか? WinAVRの場合…


(1) プログラムメモリ(ROM)を使う
詳細はavr-libcののマニュアル参照


// 定数をプログラムメモリに置くならこれをインクルード
#include

// プログラムメモリ定数の宣言
const PROGMEM unsigned char piyo[10] = {1,2,3,4,5,6,7,8,9,10};

// 読み出し
a = pgm_read_byte(&piyo[i]);

// マクロにしとく
#define piyo_get(i) pgm_read_byte(&piyo[i])

(2) EEPROMを使う
詳細はavr-libcののマニュアル参照

// 定数をEEPROMに置くならこれをインクルード
#include

// EEPROM定数の宣言
const EEMEM unsigned char piyo[10] = {1,2,3,4,5,6,7,8,9,10};

// 読み出し
a = eeprom_read_byte(&piyo[i]);

// マクロにしとく
#define piyo_get(i) eeprom_read_byte(&piyo[i])