AVR의 SRAM의 메모리 공간은 매우 제한적이기 때문에 다량의 상수 데이터를 SRAM에 저장하여 메모리를 차지하는 것보다 플래시롬에 저장하는 것이 효율적일 수 있다. 플래시롬에 저장된 데이터는 런타임에는 수정될 수 없으므로 상수(즉, 고정된 값)만을 저장해야만 한다. 예를 들어 문자열이라든가, 아니면 크기가 큰 배열을 저장할 때는 플래시롬를 이용할 수 있다.
플래시롬을 접근하는데 필요한 기능들은 <avr/pgmspace.h>에 정의되어 있으므로 이 헤더파일을 인클루드해줘야 한다. 그리고 사용하는 AVR이 어셈블리명령어인 LPM 혹은 ELPM을 지원하여야 한다. LPM/ELPM은 플래쉬메모리에서 데이터를 레지스터로 읽어오는 동작을 하는 어셈블리 명령어로서 ATmega8A는 LPM명령이 존재한다.
#include <avr/pgmspace.h> |
플래시 메모리에 저장될 데이터는 전역 상수로 정의되어야 하며 변수 선언과 동시에 초기화를 해주어야 한다. 그리고 변수의 타잎지정자 뒤에 PROGMEM (스펠링에 주의할 것) 이라는 키워드를 추가하면 된다. 최신 버전의 AVR툴체인에서는 컴파일할 때 경고를 없애기 위해서 반드시 const 키워드를 추가시켜야 한다.
#include <avr/io.h>#include <avr/pgmspace.h> const signed char cA PROGMEM = -1;const unsigned char cB PROGMEM = -1;const signed int iA PROGMEM = -1;const unsigned int iB PROGMEM = -1;const float fA PROGMEM = -1; const unsigned char cArr[5] PROGMEM ={0x01,0x02,0x03,0x04,0x05}; |
한 가지 주의할 점은 이렇게 저장된 데이터를 읽어내려면 일반 변수와 같이 사용할 수는 없으며 별도로 정의된 다음과 같은 함수들을 사용해야 한다는 점이다.
pgm_read_byte(<pointer>)pgm_read_word(<pointer>)pgm_read_dword(<pointer>)pgm_read_float(<pointer>) |
예를 들어서 위의 예에서 cA값을 다른 변수에 저장하려면 다음과 같이 해야 한다.
signed char cX;cX = pgm_read_byte(&cA);// cX = cA 는 안 됨. |
또는 cArr배열의 세번째 요소를 읽어들이려면 다음과 같이 사용한다.
unsigned char cY;cY = pgm_read_byte(cArr+2); //cY = cArr[2] 는 안 됨 |
또한 <pgmspace.h>에서는 PSTR()이라는 간결한 매크로가 정의되어 있는데 이 매크로는 플래시메모리에 문자열을 저장하고 그 포인터를 반환한다. 이 경우에는 궂이 전역 변수로 정의할 필요는 없으며 포인터이기 때문에 지역 변수로도 사용할 수 있다.
int main(void) { const char *str = PSTR("Hello world!"); LCD_puts(str);} |
또는 다음와 같이 한 줄로도 줄일 수 있으므로 매우 간편하게 사용할 수 있다. 단, 위의 경우는 포인터 변수 str을 당연히 함수 내부의 여러 곳에서 사용할 수 있으나 다음의 경우는 단 한 번만 사용가능하다.
LCD_puts( PSTR("Hello! world") ); |
즉, "Hello! world"라는 문자열은 PSTR()이라는 매크로에 의해서 플래시메모리에 저장되고 그 포인터를 반환하여 LCD_puts()라는 함수에서 사용되는 것이다.
이전 버젼의 WinAVR에서는 [표 1]에 기술한 바와 같이 pgmspace.h에서 typedef명령으로 정의된 별도의 데이터 타잎을 사용할 수 있다. 하지만 Atmel Studio 6.x버젼에 포함된 툴체인에서는 이러한 사용법을 더 이상 권장하지 않고 앞으로 사라질 것이라고 <avr/pgmspace.h>에 기술되어 있다. 단, 이전 프로그램과의 호환성때문에 이러한 데이터형을 사용할 경우에는 <avr/pgmspace.h>를 인클루드하기 전에 상수 __PROG_TYPES_COMPAT__ 를 정의하여야 한다.
[표 1] 플래시롬 상수의 자료형 (권장하지 않음)
자료형 | byte | 표현 범위 | 비고 |
prog_char or prog_int8_t | 1 | | signed char |
prog_uchar or prog_uint8_t | 1 | | unsigned char |
prog_int16_t | 2 | | signed int |
prog_uint16_t | 2 | | unsigned int |
prog_int32_t | 3 | | signed long |
prog_uint32_t | 3 | | unsigned long |
prog_int64_t | 4 | | signed long long |
prog_uint64_t | 5 | | unsigned long long |
위의 도표에 기술된 데이터 타잎을 사용하는 예는 다음과 같다.
#define __PROG_TYPES_COMPAT__ //반드시 있어야 함.#include <avr/pgmspace.h> const prog_char cA = -1;const prog_uchar cB = 1;const prog_int16_t iA = -1;const prog_uint16_t iB = 1; |
하지만 전술한 바와 같이 이 방법은 더 이상 사용하지 않는 것이 바람직하다.