개발자/Arduino

How to prevent SSD1306 allocation failed error

지구빵집 2021. 4. 30. 10:33
반응형

 

결론은 SSD1306 사용할 때, 특히 복잡한 프로그램 개발 할 경우 아래와 같은 코드에서 말 그대로 "SSD1306 allocation failed" 메시지가 나올 경우 무조건 메모리(SRAM) 문제니까 당황하지 말고 코드를 줄여야 한다. 일단 변수 선언을 줄이고 테스트를 위해 준비한 코드를 지운다. 스트링은 가능한 짧게 줄이거나 없앤다. 그러면 에러는 사라진다. 

 

// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

 

RAM을 확보하는 방법

 

1. 사용하지 않는 변수는 제거한다.

 

2. F() 매크로 사용

보통 문자열(string) 사용은 플래쉬 메모리와 RAM에 많은 영향을 준다. 문자열은 플래시 메모리에 프로그램 이미지에 포함되어 구워지고 나서, 다시 static 변수로 SRAM을 잡아먹는다. 이에 Paul Stoffregen of PJRC and Teensyduino 이 만든 간단하고 훌륭한 솔루션이 F() 매크로라고 알려져 있다. 이 매크로는 문자열이 SRAM에 복사되지 않도록 해주며, 아두이노가 실행되면서 문자열을 사용할 때 플래시 메모리에서 직접 읽어 사용하도록 해준다. 실제로는 PROGMEM 지시어를 사용해서 선언된 변수를 사용하는 것과 같은 원리다. 아래 예제처럼 문자열을 F() 로 감싸서 사용하면 된다.

 

Serial.println("Sram sram sram sram. Lovely sram! ");
Serial.println(F("Sram sram sram sram. Lovely sram! "));

 

3. Reserve() 사용

 

아두이노의 String 라이브러리를 사용하는 경우 문자열이 동적으로 커지면서 힙(heap)을 파편화시키게 됩니다. 이때 reserve(num) 함수를 사용하면 동적으로 증가하는 문자열을 위한 버퍼를 할당해서 힙 파편화를 줄여줍니다. 문자열을 처리하면서 길이가 증가하고, 잠깐 사용하는 문자열이 아닌 경우는 단지 reserve() 함수만 호출해주면 됩니다. 이런 작업은 C string 을 이용해서 더 효율적으로 할 수 있지만, 간단한 가이드라인만 지킴으로써 효율성에서 큰 차이없이 편리한 String object를 사용할 수 있습니다. 

 

읽기 전용 데이터에 PROGMEM 사용 앞선 F() 설명하면서 언급된 PROGMEM 지시어는 플래시 메모리 영역만 사용하고 RAM을 소모하지 않도록 해줍니다. 대신 읽기 전용(read only) 데이터만 사용이 가능합니다. 읽기 전용 데이터의 경우 가급적 PROGMEM 지시어를 사용하는 것이 램을 아끼는 지름길입니다. 상세 내용은 아래 링크를 참고하세요.

 

4. 버퍼 사이즈 축소 버퍼 할당

 

  • 버퍼, 배열 등을 사용한다면 필요한 만큼만 할당
  • 라이브러리의 버퍼 : 버퍼 사이즈를 조절할 수 있도록 함수를 제공하는 라이브러리들이 있습니다.
  • 시스템 버퍼 : 아두이노의 가장 기본인 Serial 통신에 사용되는 버퍼들도 있습니다. 아두이노에서 기본으로 사용되는 Serial 라이브러리는 Serial receive buffer 용도로 64 byte 를 할당합니다. 빠른 속도로 많은 양의 데이터가 오가지 않는다면 이걸 반 이하로 줄일 수도 있습니다. 아두이노 설치폴더의 HardwareSerial.cpp 파일을 열어서( 위치는 다음과 같습니다)
  • ….\Arduino-1.x.x\hardware\arduino\cores\arduino\HardwareSerial.cpp 아래 부분을 적당히 수정해주면 됩니다. 

 

#define SERIAL_BUFFER_SIZE 64

 

불필요하게 크게 잡은 변수 수정 불필요하게 크게 잡은 변수도 수정하세요. (double->float, long->int 등등) 

 

5. Global & Static Variables

 

Global, Static 변수로 선언된 경우 아두이노가 동작할 동안은 일정량의 메모리를 계속 차지하고 있습니다. Global, Static 변수를 남발하지 마세요.

 

6. 동적 할당 (Dynamic Allocations)

 

Object, 데이터가 동적으로 할당되는 경우는 힙 사이즈가 스택 방향으로 증가합니다. 이렇게 할당된 메모리는 다시 해제가 가능하지만 그렇다고 반드시 힙이 줄어드는 것은 아닙니다!! 만양에 다른 데이터가 힙에 동적으로 할당되어 올라간다면 힙의 꼭지점은 줄어들지 않기 때문입니다. 이 경우는 구멍 송송 스위스 치즈처럼 힙이 파편화(fragmented) 되게 됩니다. 동적 할당은 꼭 필요할 때만 사용하세요. (특히 문자열)

 

7. 지역 변수(Local Variables)

 

함수가 실행될 때 stack frame 을 생성하면서 스택이 증가합니다. 각각의 stack frame은 함수로 전달된 파라미터, 함수 내부에서 선언된 변수를 포함합니다. 하지만 이 메모리는 함수 사용이 끝나면 100% 반환됩니다. 지역 변수는 Global, Static 변수처럼 공간을 영구적으로 차지하지 않기 때문에 코드 일부에서만 사용되는 변수는 함수안에서 사용되도록 배려가 필요합니다. 

 

참고 사이트: 메모리 관련 글 복사

 

아두이노 메모리 상세분석

You know you have a memory problem when... 

 

 

https://learn.adafruit.com/memories-of-an-arduino/measuring-free-memory

 

 

 

반응형