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;

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


댓글 없음:

댓글 쓰기