아두이노 강좌 #27 동적 메모리 용량 부족 문제 PROGMEM F() 메크로로 해결
Lucy Archive
Lucy / Facilitate4U
2020. 8. 25. 02:36

아두이노 SRAM FLASH MEMORY아두이노 SRAM FLASH MEMORY

Arduino : PROGMEM F() Macro

아두이노 우노의 SRAM 은 2KB 로 메모리 문제에서 자유로울 수 없습니다. 특히, LED Matrix, OLED 와 같은 디스플레이 장치를 사용하거나, SD Card 장치 라이브러리를 사용하는 경우 메모리 문제에 직면하게 됩니다. 본 포스트에서는

SRAM 부족 방법을 해결하기 위해 전역 변수를 FLASH 에 저장하는 매크로인 PROGMEM 과 F() 사용법

을 소개합니다.


아두이노 메모리

아두이노 메모리는 FLASH MEMORY, SRAM, EEPROM 3가지 종류가 있습니다. 각 메모리의 저장 공간과 용도는 아래와 같습니다.

[각주:1]메모리 종류와 용도

  • FLASH MEMORY : 32Kbyte - 프로그램이 저장되는 비휘발성 메모리
  • SRAM : 2Kbyte - 동적으로 변화하는 휘발성 메모리
  • EEPROM : 1Kbyte - 마음대로 읽기, 쓰기가 가능한 비휘발성 메모리
참고로, FLASH MEMORY 는 프로그램을 업로드 할 때만 기록 할 수 있습니다. 프로그램이 실행되는 동안에는 메모리 내의 값을 변경 할 수 없습니다. 

메모리 할당

아래와 같은 프로그램 동작 시나리오가 있는 경우, 아래 모든 내용은 FLASH 메모리에 저장이 되고, 프로그램이 실행되면 파란색변수는 SRAM 에 올려놓고 프로그램을 실행합니다. 프로그램에 의해서 정해진 적당한 시간에 빨간색의 변수는 EEPROM 에서 기록 된 것을 읽거나, 쓰기를 합니다.

아침에 일어나면 아침을 먹고, 출근을 한다.

출근하면 오늘 할일이 기록된 노트를 체크하고, 점심시간까지 열심히 일한다.

점심 시간이 되면 장과장이 선택한 점심메뉴를 먹고 오후 근무를 한다.

오후 4시가 되면 사다리 타기를 하고 꽝이 걸린 사람은 아이스크림을 산다.

오후 5시 30분이 되면 노트에 내일 할일을 써놓고, 6시가 되면 숨도 안쉬고 바로 퇴근한다.

파란색으로 강조된 아침, 점심메뉴, 꽝에 걸린 사람은 상황에 따라 언제든지 변경 될 수 있는 데이터이고, 계속 저장할 가치는 없는 변수이기 때문에 자고 일어나면 사라지는 휘발성 메모리인 SRAM 에 저장됩니다. 빨간색으로 표시된 오늘 할일내일 할일은 자고 일어나도 어딘가 기록이 되어 있어야 하기 때문에 비휘발성 메모리인 EEPROM 에 저장됩니다. 

추가적으로 오후 4시가 되면 사다리 타기를 한다고 했는데, 사실 사다리 타기를 할 수도 있고, 가위바위보를 할 수도 있어 게임을 한다라고 프로그래밍을 하고 게임을 변수로 만들 수 있습니다. 이때 변수는 프로그램이 실행되면 SRAM에 로드해서 프로그램이 돌아가게 됩니다. 하지만, 게임은 변수이지만 게임을 사다리 타기 오직 하나로 못 박아 놓게 되면 용량이 적은 SRAM에 저장하지 않고, 용량이 넉넉한 FLASH MEMORY 에 저장 할 수 있게 됩니다. 이 때 조건이 있습니다, 게임은 변수이지만 수정 하거나 변경 될 수 없고, 오직 게임은 사다리 게임입니다. 이런 형태를 상수라고 하며, 상수 선언자인 const 가 사용됩니다.

결론적으로, 데이터를 SRAM 에 저장하기 말고 FLASH MEMORY 에 저장해서 아두이노의 메모리 문제를 해결 할 수 있습니다.


F() Macro

Serial.println("Hello world!") 와 같은 코드가 있는 경우 "Hello World" 문자열 데이터는 SRAM 에 저장됩니다. SRAM 이 넉넉하지 않은 상태에서 문자열을 많이 출력하게 되면 SRAM 에 저장되는 데이터가 많아지고, 메모리 문제가 발생할 수 있습니다. 이때 아래와 같이 F() 메크로를 사용하여 출력할 문자열을 FLASH MEMORY 에 저장할 수 있습니다. 

Serial.println("Hello world!"); Hello world 문자열이 SRAM 로드 후 사용

Serial.println(F("Hello world!")); Hello world 문자열이 Flash Memory 에 저장

PROGMEM Macro

[각주:2]PROGMEM 키워드는 변수 수식어로 사용되고 pgmspace.h 라이브러리에 저장되어 있습니다. 최신 Arduino IDE 를 사용하는 경우 선언문에 라이브러리 참조가 필요 없지만, 1.0 미만의 버전을 사용하고 계시는 분은 코드 선언문에 #include <avr/pgmspace.h> 코드를 작성하셔야 합니다. 

FLASH MEMORY 로 저장 방법

PROGMEM 을 사용하여 변수를 FLASH 메모리에 저장하는 방법은 아래와 같습니다.

Syntax

  • const dataType variableName[] PROGMEM = {};
  • const PROGMEM dataTYpe variableName[] = {};

Prameters

  • dataType : byte, char, int 와 같은 데이터 타입
  • variableName : 배열의 이름

Example

  • const char charLists[] PROGMEM = { 'a', 'b', 'c'};
  • const int intLists[] PROGMEM = { 0, 1, 2 };

FLASH MEMORY 에서 불러오는 방법

PROGMEM 으로 FLASH MEMORY 에 저장된 변수를 불러오는 방법은, 변수의 종류에 따라 아래의 매크로 함수를 사용할 수 있습니다. 각 함수의 ()안에는 변수명이 아닌 변수의 주소가 들어가야 합니다. 여러가지 방법이 있지만 변수명 앞에 & 기호로 변수의 주소를 표시할 수 있습니다. 더 많은 종류의 호출 메트로가 있으며, pgmspace.h 라이브러리 파일에 저장되어 있습니다. 참고해주세요.

  • pgm_read_byte_near(address_short) : 8bit 변수 - char, byte, uint8_t
    • 예시 : char buffer = pgm_read_byte_near(&(charLists[i])); charLists 배열의 i 인덱스 데이터 buffer 에 저장
  • pgm_read_word_near(address_short) : 16bit 변수 - int, uint16_t
    • 예시 : uint16_t buffer = pgm_read_word_near(&(intLists[i])); intLists 배열의 i 인덱스 데이터 buffer 에 저장

예제1 : FLASH MEMORY 저장 후 불러와서 출력

예제1은 byte, char, uint16_t 형 배열을 FLASH MEMORY 에 저장 후 시리얼 포트로 출력하는 예제입니다.

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* 전역 변수 FLASH MEMORY 에 저장*/
const byte byteLists[] PROGMEM = { 0b000000010b000000100b000001000b000010000b000100000b001000000b01000000};
const char charLists[] PROGMEM = { 'a''b''c''d''e''f''g'};
const uint16_t intLists[] PROGMEM = { 01234 ,56};
 
byte bTemp;
char cTemp;
int iTemp;
 
void setup(){
    Serial.begin(115200);
    /* Serial.print 문의 문자열 FLASH MEMORY 에 저장 */
    Serial.println(F("Program Start!"));
}
 
void loop(){
    for(int i = 0 ; i < 7 ; i++){
        bTemp = pgm_read_byte_near(&(byteLists[i]));
        cTemp = pgm_read_byte_near(&(charLists[i]));
        iTemp = pgm_read_byte_near(&(intLists[i]));
        Serial.print("0b");
        Serial.println(bTemp, BIN);
        Serial.println(cTemp);
        Serial.println(iTemp);
        delay(500);
    }
}
cs

코드 설명

  • 1~3 Line : byte, char, uint16_t 형 배열을 FLASH MEMORY 에 저장하는 코드입니다.
  • 13 Line : Serial.println() 출력 문의 문자열을 FLASH MEMORY 에 저장하는 코드입니다.
  • 18~20 Line : Flash Memory 에 저장된 데이터를 불러오는 코드입니다.


예제2 : LED Matrix Code 적용

LED Matrix, OLED 와 같은 디스플레이 장치는 픽셀당 출력할 데이터를 모두 저장하고 있어야 하기 때문에 SRAM 사용량이 매우 커서 주의가 필요한 장치입니다. 아래 코드는 이전 포스트에서 진행한 LED Matrix 코드에서 PROGMEM 을 사용한 코드와 프로그램 메모리 크기를 비교한 그림입니다. 이전 포스트는 하단에 링크하였습니다.

아두이노 FLASH MEMORY 로 저장(좌) 기존 코드 (우) PROGMEM 사용

위 사진에서 왼쪽의 기존 코드와 우측의 PROGMEM 사용 코드로 빌드시 FLASH 와 SRAM 메모리가 각 8Byte 차이가 납니다. 이 크기는 상단에 LED Matrix 데이터를 저장한 heart8x8 배열의 크기입니다. 이 프로그램은 하트 형상만 출력하는 코드입니다. 만약 문자열을 처리하기 위해 ASCII 코드의 데이터를 모두 전역 변수로 설정한 경우 상당한 SRAM 의 여유 공간을 확보 할 수 있습니다.


마무리

본 포스트에서는

PROGMEMF() 메크로를 사용하여 SRAM 의 용량을 확보

하는 방법에 대해 소개하였습니다. 다음 포스트에서는 PROGMEM 메크로를 사용하여 2차원 배열을 FLASH MEMORY 로 저장하는 방법을 안내하고, 적용 전 후 LED Matrix 코드를 비교 할 예정입니다.

끝까지 읽어주셔서 감사합니다.😄

관련포스트

👉 아두이노 SRAM 용량 부족 문제 관련글 목록 보기

👉 아두이노 LED Matrix 관련글 목록 보기

👉 아두이노 관련글 전체 목록 보기

  1. https://playground.arduino.cc/Learning/Memory/ [본문으로]
  2. https://www.arduino.cc/reference/en/language/variables/utilities/progmem/ [본문으로]