?>

Hard Copy World

HCW

Input, display and etc

Home > Info >

Input, display and etc

최고의 디스플레이용 라이브러리 u8glib

페이지 정보

작성자 최고관리자 쪽지보내기 메일보내기 홈페이지 자기소개 아이디로 검색 전체게시물 작성일15-01-12 17:42 조회10,188회 댓글6건

본문

8비트 임베디드 시스템에서 사용할 수 있는 그래픽 라이브러리야 여러 종류겠습니다만 그 중 가장 유명한 라이브러리 중 하나가 아닌가 합니다.

지원하는 디스플레이 드라이버 칩도 다양하고 메모리도 적게 사용하면서 기본적인 그래픽 함수들을 모두 지원해줍니다. 게다가 폰트도 다양하게 내장하고 있어 정말 쓸모가 많은 라이브러리입니다.

진작에 스마트 워치 프로젝트를 u8glib를 이용했어야 하는데.. 저도 이제서야 적용하고 라이브러리 사용법을 공유합니다.

u8glib 공식 홈페이지 : https://code.google.com/p/u8glib/지원 디스플레이 예시지원 디스플레이 종류

 

 

1. u8glib

 

0.96'' OLED 소개하는 글에서 Adafruit GFX, Adafruit SSD_1306 라이브러리를 소개한 적이 있습니다. Adafruit 라이브러리도 사용하기 편리한 라이브러리인데 여러모로 u8glib 와 대조적입니다.

가장 큰 차이점은 Adafruit 라이브러리는 라이브러리 내부에 그래픽 버퍼를 크게 잡고 여기에 드로잉 작업을 한다는 점입니다. 따라서 사용자가 그래픽 작업을 하면 1차로 그래픽 버퍼에 픽셀 정보를 업데이트 합니다. 사용자가 작업을 마치고 commit 하면 수정된 정보를 디스플레이 드라이버로 전송합니다. 

이런 방식은 디스플레이의 refresh가 빠르고 화면의 일부분만 업데이트 할 수 있습니다. 반면에 메모리를 많이 잡아먹습니다. (디스플레이의 크기가 큰 경우 더욱) 128x64 OLED의 경우 내부 버퍼로만 1KB가 필요합니다. 아두이노 RAM이 2KB이므로  사용자가 쓸 수 있는 공간이 매우 적어집니다. (500바이트 정도) 여기에 블루투스 모듈 등을 이용하기 위해 SoftwareSerial 을 사용한다면 사용자는 250byte 정도만을 사용할 수 있습니다. (이 상태에서 Serial 도 사용할 수 없습니다. 디버깅 불가) 

이밖에도 Adafruit 라이브러리의 경우 사용방법은 비교적 직관적이고 쉽지만 라이브러리 자체가 문제점이 많아 사용하기가 까다롭습니다.

 

반면에 u8glib는 매번 업데이트 때마다 화면 전체를 새로 그립니다. 그리고 디스플레이의 화면을 분할해서 작은 사이즈의 버퍼를 가지고 여러번 drawing 작업을 하도록 구성되어 있습니다. 화면을 1/2, 1/4, 1/8 분할하면 2번, 4번, 8번 같은 drawing을 반복해서 한 화면을 완성하는 방식입니다. 디스플레이의 일부분을 업데이트하고 다음 영역을 업데이트하는 작업을 반복하죠. 나중에 소스코드를 보시면 아시겠지만 page 단위로 반복합니다.

u8glib의 방식은 적은 메모리만을 사용합니다. 대신에 같은 drawing을 여러번 반복하기 때문에 화면의 갱식 속도가 느리고, processing power를 많이 소모합니다. 그리고 u8glib의 구조상 화면의 일부만 업데이트 할 수가 없습니다. 그럼에도 u8glib가 좋은 이유는 8bit 임베디드 시스템에서 사용가능한 메모리가 제한적인 경우가 많기 때문입니다. 아두이노 2KB RAM에서는 u8glib의 이런 장점이 빛납니다. 화면 갱신속도는 아두이노 정도라면 초당 20회 정도 가능하다고 합니다. 물론 화면이 복잡해지면 (drawing 루틴이 복잡하면) 더 느려집니다.

드로잉 방식과는 별도로 u8glib는 다양한 driver chipset을 지원합니다. u8glib를 이용해서 사용가능한 디스플레이가 그만큼 많다는 얘기입니다. (코드를 거의 바꾸지 않고 사용가능한 디스플레이가 많다는 얘기) 라이브러리의의 안정성도 좋고 업데이트도 빠릅니다. 게다가 다양한 폰트를 지원합니다.

 

 

2. u8glib 라이브러리 설치

 

라이브러리는 다음 링크에서 다운로드 받으실 수 있습니다. 

https://code.google.com/p/u8glib/

아두이노의 라이브러리 폴더에 넣어주면 됩니다. 라이브러리에 포함된 예제 파일을 확인하시면 사용방법을 아실 수 있습니다. 특히 GraphicsTest 예제가 유용합니다.

 

 

3. u8glib 사용방법

 

화면을 그리는 가장 기본적인 코드는 아래와 같습니다.

#include "U8glib.h"

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0);	// I2C / TWI 

void draw(void) {
  // graphic commands to redraw the complete screen should be placed here  
  u8g.setFont(u8g_font_unifont);
  //u8g.setFont(u8g_font_osb21);
  u8g.drawStr( 0, 22, "Hello World!");
}

void setup(void) {
  // flip screen, if required
  // u8g.setRot180();
  
  // set SPI backup if required
  //u8g.setHardwareBackup(u8g_backup_avr_spi);

  // assign default color value
  if ( u8g.getMode() == U8G_MODE_R3G3B2 ) {
    u8g.setColorIndex(255);     // white
  }
  else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) {
    u8g.setColorIndex(3);         // max intensity
  }
  else if ( u8g.getMode() == U8G_MODE_BW ) {
    u8g.setColorIndex(1);         // pixel on
  }
  else if ( u8g.getMode() == U8G_MODE_HICOLOR ) {
    u8g.setHiColorByRGB(255,255,255);
  }
}

void loop(void) {
  // picture loop
  u8g.firstPage();  
  do {
    draw();
  } while( u8g.nextPage() );
  
  // rebuild the picture after some delay
  delay(50);
}

 

 두 번째 라인에서 u8g 인스턴스를 초기화 합니다. 이 부분을 디스플레이 종류(드라이버 칩셋 종류), 통신방식(I2C/SPI/기타)에 따라 바꿔줘야 합니다. 제가 가진 SSD1306  - 128x64 OLED는 아래와 같은 초기화 코드를 사용합니다.

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0);	// I2C / TWI 

 

u8glib 의 가장 중요한 동작 부분은 아래 loop() 함수 안에 있는 코드입니다.

void loop(void) {
  // picture loop
  u8g.firstPage();  
  do {
    draw();
  } while( u8g.nextPage() );
  
  // rebuild the picture after some delay
  delay(50);
}

 u8g 첫번째 페이지부터 마지막 페이지까지 반복해서 똑같은 draw() 함수를 호출합니다. 즉, 화면이 분할된 만큼 그리는 작업을 반복해서 전체화면을 완성하는 것입니다.

이 부분이 반드시 loop 안에 있을 필요는 없고 화면을 갱신해야 하는 부분에서 위 코드를 실행해주면 됩니다. draw() 함수안에는 현재 상태에 맞는 drawing 코드를 넣어주면 되구요.

그럼 화면 전체를 그리는데 몇 개의 페이지가 필요할까요? 페이지 분할은 전적으로 u8glib 가 현재 설정된 시스템과 디스플레이의 종류를 가지고 판단합니다. 제가 아두이노 Nano 보드에서 128x64 단색 OLED를 가지고 테스트 했을 경우 8개의 페이지로 분할되었습니다. 그래서 조금 복잡한 화면은 느리게 화면이 갱신됩니다.

 

 

4. Font 사용 방법

 

위 예제 소스에도 잠시 나온것처럼 폰트를 지정할 수 있습니다. (여러개의 폰트를 사용하면 아두이노에 올릴 바이너리 사이즈가 커지고 컴파일-업로드가 느려집니다.)

일반적으로 아래처럼 쓰시면 됩니다.

    u8g.setFont(u8g_font_fixed_v0);
    u8g.setFontRefHeightExtendedText();
    u8g.setDefaultForegroundColor();
    u8g.setFontPosTop();
    u8g.drawStr(45, 45, "Hello World!");

위 4 라인이 폰트 설정 코드이고 마지막 라인이 실제 그리는 코드입니다. 폰트 종류는 아래 페이지에서 쉽게 확인하실 수 있습니다.

https://code.google.com/p/u8glib/wiki/fontsize 

폰트에 대한 자세한 사용방법은 아래 링크를 참고하세요.

https://code.google.com/p/u8glib/wiki/tstring

 

 

5. 그래픽 함수, 이미지

 

그래픽 함수들은 u8glib에 포함된 GraphicsTest 예제를 참고하시면 됩니다. 좀 더 상세한 사용법은 라이브러리의 U8glib.h, utility/u8g.h 파일을 참고하시면 좋습니다.

이미지 파일을 출력하기 위해서는 아래 코드를 사용하시면 됩니다. u8glib에 포함된 Bitmap 예제를 참고하세요.

u8g.drawBitmapP( 0, 0, 1, 8, rook_bitmap)

 

Tree 형태의 메뉴를 쉽게 구성할 수 있도록 지원합니다. (별도 라이브러리 설치)

https://code.google.com/p/u8glib/wiki/tmenu

추천 0
  • 페이스북으로 보내기
  • 트위터로 보내기
  • 구글플러스로 보내기

댓글목록

최고관리자님의 댓글

최고관리자 쪽지보내기 메일보내기 홈페이지 자기소개 아이디로 검색 전체게시물 댓글의 댓글 작성일

아두이노용이 아니라 다른 버전의 라이브러리를 받으셔야 할 것 같은데... 아래 링크 라이브러리가 맞는지 모르겠네요.
https://code.google.com/p/u8glib/wiki/avr
https://bintray.com/olikraus/u8glib/AVR

김찬님의 댓글

김찬 이름으로 검색 작성일

Oled에 센서 값을 출력 할려면 어떻게 해야 하나요?

최고관리자님의 댓글

최고관리자 쪽지보내기 메일보내기 홈페이지 자기소개 아이디로 검색 전체게시물 댓글의 댓글 작성일

[4. 폰트 사용 방법] 을 보시면 됩니다.
    u8g.setFont(u8g_font_fixed_v0);
    u8g.setFontRefHeightExtendedText();
    u8g.setDefaultForegroundColor();
    u8g.setFontPosTop();
    u8g.drawStr(45, 45, "Hello World!");

aopsofw님의 댓글

aopsofw 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 작성일

오실로 스코프를 만들려하는데 SSD1306.h라이브러리를 사용하니
메모리 부족으로 작동이 안되는데 u8glib라이브러리를 사용할려면
어떻게 해야 되나요
밑에가 소스 코드 입니다
#include "SPI.h"
#include "Wire.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"

//OLED 연결핀
#define CLK 13
#define MOSI 11
#define CS 10
#define DC 4
#define RST 12
Adafruit_SSD1306 display(MOSI, CLK, DC, RST, CS);

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

/********************************************/

#define CHARWIDTH          5
#define CHARHEIGHT          8
#define AXISWIDTH          (2 + 1)                 
#define VISIBLEVALUEPIXELS  (128 - AXISWIDTH)       
#define NUMVALUES          (2 * VISIBLEVALUEPIXELS)

#define TRIGGER_ENABLE_PIN      2 
#define SCREEN_UPDATE_ENABLE_PIN 3 

byte values[NUMVALUES];         
int pos = 0;                   
int count = 0;                 
unsigned long readStartTime = 0;
int sampleRate = 1;           

/********************************************/

//OLED에 글씨 출력하는 함수
void displayln(const char* format, ...)
{
  char buffer[32];

  va_list args;
  va_start(args, format);
  vsprintf(buffer, format, args);
  va_end(args);

  display.setTextSize(1);
  display.setTextColor(WHITE);
 
  int len = strlen(buffer);
  Serial.println(len);
  for (uint8_t i = 0; i < len; i++) {
    display.setCursor(9 * CHARWIDTH+(i*7), 7 * CHARHEIGHT - 2);
    display.println(buffer[i]);
    Serial.print(buffer[i]);
  }
}

//그래프의 Y축에 기준점 표시
void drawAxis()

  for (int x = 0; x < 2; x++) {
    display.drawPixel(x,  0, WHITE);
    display.drawPixel(x, 13, WHITE);
    display.drawPixel(x, 26, WHITE);
    display.drawPixel(x, 38, WHITE);
    display.drawPixel(x, 50, WHITE);
    display.drawPixel(x, 63, WHITE); 
  }
}

//들어온 데이터를 바탕으로 파형을 OLED에 그린다
void drawValues()
{
  int start = 0;

  if ( digitalRead(TRIGGER_ENABLE_PIN) ) {
    for (int i = 0; i < NUMVALUES; i++) {
      if ( values[i] == 0 ) {
        for (; i < NUMVALUES; i++) {
          if ( values[i] != 0 ) {
            start = i;
            break;
          }
        }
        break;
      }
    }   

    if ( start >= VISIBLEVALUEPIXELS )
      return;
  }

  for (int i = 0; i < VISIBLEVALUEPIXELS; i++) {
    display.drawPixel(i + AXISWIDTH, 63 - (values[i + start]), WHITE);
  }
}

//화면 프레임시간 출력
void drawFrameTime(unsigned long us)
{
  displayln("%ld us", us);
}

/********************************************/

void setup() {
  display.begin(SSD1306_SWITCHCAPVCC);

  //버튼 모드 설정
  pinMode(TRIGGER_ENABLE_PIN, INPUT_PULLUP);
  pinMode(SCREEN_UPDATE_ENABLE_PIN, INPUT_PULLUP);

  display.display();
  delay(2000);
  display.clearDisplay();
}

/********************************************/

void loop() {
  //1프레임 출력의 시간을 체크하기 위한 시간 측정 시작
  if ( pos == 0 )
    readStartTime = micros();

  //BIT SHIFT 연산
  if ( (++count) % sampleRate == 0 ) {
    values[pos++] = analogRead(0) >> 4;
  }

  if ( pos >= NUMVALUES ) {
    unsigned long totalSampleTime = (micros() - readStartTime) / 2; 
   
    //화면 업데이트 핀이 눌려졌다면 OLED에 파형을 출력한다.
    if ( !digitalRead(SCREEN_UPDATE_ENABLE_PIN) ) {
      display.clearDisplay();
      drawAxis();
      drawValues();
      drawFrameTime(totalSampleTime);
      display.display();
    }
   
    pos = 0;
    count = 0;
  }
}