AVR Atmel ATmega328P
微處理器 - AVR Atmel ATmega328P提供了32KB的Flash Memory(Program Memory),以及2KB的RAM(Data Memory)。而由於AVR是採用Harvard architecture,因此會利用Flash Memory來儲存program;並利用RAM儲存data,且有各自的address space。
Harvard architecture V.S. Von Neumann architecture
因為Data Memory容量比較有限,因此我們希望能將一些constant data存放在空間較大的Flash Memory。加上,C語言是for Von Neumann architecture,也就是code跟data皆存在同一個address space,所以,針對AVR(Harvard architecture)所設計的compiler,必須符合code跟data存放在不同address space這個設計標準。
AVR GCC __attribute__
要達到Harvard architecture的特性有許多方法,
而AVR則利用類似GCC的__attribute__這個tag來實作,簡單講,__attribute__可以附加在函式或變數的宣告,就是在編譯時會告訴compiler要特別對該函式或變數做一些處理。AVR提供一些特殊用途的attribute,例如它提供__attribute__((__progmem__))這個attribute讓programmer可以將constant data儲存在Flash Mem.(Program Mem.):
In AVR GCC, there is a special attribute called progmem. This attribute is use on data declarations, and tells the compiler to place the data in the Program Memory (Flash). ~ avr-libc user manual 1.6.6
PROGMEM是AVR-Libc自行定義的一個macro,它被定義成類似GCC的attribute syntax,以下是<avr/pgmspace.h>這個標頭檔對於特殊attribute定義的節錄
#ifndef __ATTR_PROGMEM__
#define __ATTR_PROGMEM__ __attribute__((__progmem__))
#endif
/**
\ingroup avr_pgmspace
\def PROGMEM
Attribute to use in order to declare an object being located in
flash ROM.
*/
#define PROGMEM __ATTR_PROGMEM__
Storing/Retrieving Data in the Program Space
接下來,我們來看如何利用AVR-Libc所提供的macro來存取program space的data:
首先,假設我們有以下的data
unsigned char mydata[2][10] =
{
{0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09},
{0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13}
};
我們可能會利用以下的方式來存取data
byte = mydata[i][j];
但是我們想要把它存放在program space,因此需要用到PROGMEM這個macro來幫助我們達成目的,因為PROGMEM是在<avr/pgmspace.h>定義,所以我們需將它include進來
#include <avr/pgmspace.h>
然後在變數宣告後面加上PROGMEM
unsigned char mydata[2][10] PROGMEM =
{
{0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09},
{0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13}
};
經過以上幾個步驟,就可以將data存在program space,接下來介紹如何從program space取出data。需要注意的是,我們已經將data搬到了program space了,因此我們沒辦法使用一般的方法來取出data,因為data space的data已被搬到program space。為此,AVR-Libc提供一個pgm_read_* macro讓我們可以取出program space的data,我們只需將data的位址當成pgm_read_* macro的參數即可,用法如下
byte = pgm_read_byte(&(mydata[i][j]));
結論
The macros and functions used to retrieve data from the Program Space have to generate some extra code in order to actually load the data from the Program Space. This incurs some extra overhead in terms of code space (extra opcodes) and execution time. ~ avr-libc user manual 1.6.6
另外,雖然user manual中也提到,從program space中取出data所花費的時間小於把data存入program space的時間,但是,如果程式在呼叫pgm_read_* macro上可以最佳化,當然是至上之選了 : )
[Reference]
AVR-Libc user manual (v1.6.6)