레이블이 플래시롬인 게시물을 표시합니다. 모든 게시물 표시
레이블이 플래시롬인 게시물을 표시합니다. 모든 게시물 표시

2015년 6월 15일 월요일

AVR의 플래시롬에 데이터 기록하기

 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;

하지만 전술한 바와 같이 이 방법은 더 이상 사용하지 않는 것이 바람직하다.