1. I2C 0.96” OLED Display Module

가로세로 21.7mm x 11.1mm 의 작은 사이즈에 128×64 해상도를 지원하는 단색 초소형 OLED 모듈입니다. 저는 스마트워치 프로젝트용으로 사용하기 위해 디스플레이 모듈을 뒤지다 찾았는데 정말 쓸만한 물건이네요. 128×64 외에 128×32 해상도도 있으며 I2C, SPI, 8bit parallel 버전이 있습니다. Adafruit 에서 제공하는 OLED 전용 Graphic library를 이용하면 2가지의 영문 폰트(대략 6×6, 12×12 정도의 사이즈), 점, 직선, 원, 사각형, 이미지, XOR 연산을 이용한 출력 등을 사용할 수 있습니다. 그래픽을 위한 기본 함수를 제공하므로 아두이노용 모니터처럼 활용할 수 있습니다.

단점은 Adafruit에서 제공하는 그래픽 라이브러리가 너무 무겁다는 겁니다. ATmega128 의 RAM 사이즈가 1Kbyte에 불과한데 이 정도로는 그래픽 라이브러리 활용이 어려울 수 있습니다. ATmega328 의 2Kbyte RAM 에서 적당히 돌아가는 수준입니다만 이때에도 Software serial 등 통신용 라이브러리를 함께 사용할 경우(특히 블루투스 사용을 위해) 통신용 라이브러리에서 사용하는 버퍼 때문에 가용 RAM이 급격히 줄어듭니다. 가용 RAM을 초과해서 사용할 경우 디버깅 하기가 무척 까다롭습니다. 주로 화면 출력이 깨지거나 loop() 함수가 계속 진행되지 않고 setup() 함수가 반복 호출되는 문제가 나타나는 것 같습니다.

그럼에도 불구하고 이미지나 그래프 출력이 필요한 프로젝트에 사용되기에는 더할나위없는 솔루션인듯 합니다. 가격도 비교적 저렴하네요. 이베이 시세 15$ 정도?

모듈을 구입, 사용하기 전에 Manual 을 한번 읽어보시길 권합니다.

http://learn.adafruit.com/monochrome-oled-breakouts

2. 연결방법

제가 구입한 것은 I2C 인터페이스 모듈로 I2C 연결을 기준으로 설명하겠습니다.

아래 배선에서는 디스플레이 모듈에 6개 핀이 있지만 구입한 모델에서는 4개 핀이 있습니다. 일반적인 I2C 연결 방법대로 연결하면 됩니다. 0.96” OLED 모듈은 5V 까지 지원하기도 하므로 제품 스펙에서 확인해보시기 바랍니다.

LCD Interface Converter Arduino Uno Board
GND GND
VCC 3.3V (5V – 지원하는 경우)
SDA A4 (아날로그 4번핀)
SCL A5 (아날로그 5번핀)

oledboard_back

SPI 방식인 경우 연결은 아래를 참고하세요.

http://learn.adafruit.com/monochrome-oled-breakouts/wiring-128×32-spi-oled-display

20개의 핀 홀이 있는 예전 방식인 경우 아래를 참고

http://learn.adafruit.com/monochrome-oled-breakouts/wiring-096-128×64-oled-display

3. 코드 (스케치)

3-1. Adafruit graphic library 없이 간단하게 사용하는 방법

먼저 아래 링크에서 예제 코드를 받습니다.

http://www.wide.hk/download/i2c_OLED.rar

예제 파일 중 data.c 파일을 먼저 열어보시면 바이너리 데이터가 두 개의 변수로 선언되어 있는게 보이실 겁니다. 하나는 폰트 이미지이고, 하나는 제작사 로고이미지입니다. 영문 글자와 이미지 출력을 위해 비트맵을 코드에 직접 삽입해 둔 부분입니다.

이제 i2c_arduino.pde 파일을 열어서 아래 data.c 파일을 include 하는 부분에서 경로를 맞춰줍니다.

#include <M:\i2c OLED\data.c>

그리고 I2C 통신을 위해 디스플레이 모듈이 사용하는 주소를 지정해줘야 합니다.

#define OLED_address  0x3c

모듈마다 조금씩 틀린데.. 보통 0x3c 혹은 ox3d 가 사용되는 듯 합니다.

이제 폰트와 이미지 출력 코드를 확인하시면 됩니다. 그래픽 모듈 초기화와 실제 화면에 출력할 컨텐츠를 구성하는 코드는 setup(), loop() 함수 안에 모두 있습니다. 그 외에는 폰트, 이미지 출력을 위해 I2C로 그래픽 모듈과 데이터를 주고받는 방법을 정의한 함수들입니다.

3-2. Adafruit graphic library 사용하는 방법

그래픽 라이브러리를 사용하기 위해서는 Adafruit_SSD1306, Adafruit-GFX-Library 두 개의 라이브러리를 설치해야 합니다. 아래 GitHub 에서 우측 아래에 있는 download 버튼으로 다운로드 받아서 라이브러리 폴더를 [아두이노 설치폴더/libraries/] 아래에 복사해 줍니다. 그리고 ssd1306_128x64_i2c.ino 예제 파일을 불러옵니다. 그래픽 라이브러리 사용을 위한 기본적인 방법은 예제 파일의 setup() 함수 안에 모두 있습니다. 예제를 그대로 실행시켜보면 다양하고 화려한 쑈를 보여줍니다……..

https://github.com/adafruit/Adafruit_SSD1306

https://github.com/adafruit/Adafruit-GFX-Library

제 경우에는 2개의 그래픽 라이브러리 설치 후 컴파일 할 때 에러가 발생 했습니다. 기존에 설치된 라이브러리와 충돌이 있어서 발생한 문제였던것 같은데 사용하지 않는 라이브러리라서 충돌난 라이브러리를 백업해 두고 라이브러리 폴더에서 삭제했습니다.

폰트, drawing 함수는 코드를 보고 따라면 문제 없기에 여기서는 이미지 출력을 위한 방법만 따로 설명하도록 하겠습니다.

3-3. 이미지 출력하는 방법

OLED 모듈은 단색 1비트 bitmap 만을 지원합니다. 뭔 소린고 하니… OLED 모듈 자체가 하나의 점을 on/off 시키는 기능만 있기 때문에 비트맵도 1픽셀 당 on/off 정보를 표시하는 1비트만 있으면 됩니다. 따라서 1Byte로 8개의 점을 표시할 수 있습니다. 8×8 사이즈의 이미지면 1Byte * 8 라인 = 8 Byte 가 필요하고, 16×16 사이즈의 이미지는 2Byte * 16 라인 = 32 Byte 가 필요합니다. 즉 원하는 이미지를 해당하는 1비트 비트맵 정보를 담은 char[] 배열로 바꿔줘야 합니다.

행여나 이미지를 핸드메이드로 만드느라 고생할까봐 Adafruit 에서는 변환 툴을 제공합니다. 3-1 에서 링크한 압축파일 안에 해당 프로그램이 포함되어 있습니다만 사용 방법이 까다롭고 변환이 잘 안됩니다. ezCircuits 에서 제작한 ezBMP 프로그램이 이 작업에 제격입니다. 강추 완소 필수 변환 프로그램입니다.

http://ezcircuits.net/zbxe/24758

여기에 비트맵 이미지를 넣고 변환하면 xxx.c 파일로 결과를 만들어줍니다. 아래와 같은 형식으로 나오는데

const unsigned char xxxxx[] = {
  0x00,0x03,0x1c,0xe0,0x1f,0xff
};

이걸 그대로 사용하면 에러가 발생하기 때문에 아래와 같이 변경해주어야 합니다.

const unsigned char xxx[] ==> const unsigned char PROGMEM xxx[]

PROGMEM 지시자는 코드에 선언된 변수를 메모리의 프로그램 영역으로 올려주는 역할을 합니다. 뭔 소리냐면… 

아두이노 보드는 프로그램(Flash) 영역 32KB, 실행시 코드에 포함된 변수 할당을 위해 사용하는 RAM 영역 2KB, 디스크 드라이브 처럼 읽고 쓰기가 가능한 EEPROM 1KB 를 가지고 있습니다. 컴파일하고 업로드하는 과정을 아두이노 IDE에서 수행하면 코드 영역 – 32KB에 바이너리가 올라가고, 실제 프로그램이 실행될 때 변수가 사용하는 영역은 2KB에 불과합니다. 2KB를 프로그램에 포함된 라이브러리와 같이 써야하기 때문에 이미지 처럼 큰 데이터를 직접 선언하고 사용하면 램이 심각하게 부족해지므로 이미지를 프로그램 영역에 포함시키고 Read-only 로 사용하는 겁니다. Adafruit 그래픽 라이브러리에서는 이 형식으로 선언된 이미지만 출력할 수 있습니다. 

C 파일을 만들어서 이미지들을 포함시키고 아두이노 스케치에서 include 해서 이미지 출력 함수를 이용해서 사용하시면 됩니다. 팁 하나 더… 이미지를 배열로 만들어서 인덱스 값으로 접근하고 싶으면 아래와 같이 사용하세요.

선언 : PROGMEM const unsigned char* bitmap_array[] = { ICON_BITMAP_0, ICON_BITMAP_1, ………. }

호출 : display.drawBitmap(x, y, (const unsigned char*)pgm_read_word(&(bitmap_array[icon_num])), width, height, WHITE);

부족한 부분은 알려주시면 본문 추가하도록 하겠습니다.