아두이노에 OLED가 달려 있으면 정보 표현의 폭이 상당히 넓어진다.영문의 경우는 다양한 크기의 폰트를 사용할 수 있지만 문제는 한글이다.한글 폰트 한 벌 크기는 70kb 가량이 되므로 32kb의 프로그램 사이즈 밖에 없는 아두이노 우노급에서는 사용이 어렵다.
그러나 이미지로 한글을 표시하기로 하면 의외로 간단해진다.물론 모든 한글을 표현할 수는 없지만 선택한 소수의 한글에 대해서는 다양한 크기의 여러가지 폰트를 사용할 수 있다.
OLED에 한글 이미지를 표시하기 위한 워크플로우는 다음과 같다.
- 이미지 편집 툴인 GIMP에서 한글 텍스트 이미지를 그린다.
- 한글 이미지를 .xbm 형식으로 내 보낸다.
- 아두이노 프로그램에 .xbm 텍스트를 붙여 넣는다.
- u8g나 u8g2 라이브러리의 drawXBM 메쏘드를 이용해 이미지를 표시한다.
GIMP에서 한글 이미지 만들어 xbm 파일로 내보내기
GIMP 설치하기
GIMP는 무료료 사용할 수 있는 오픈소스 이미지 편집 프로그램이다.웹사이트를 방문하면 첫 화면에 프로그램을 다운로드할 수 있는 배너가 있다.특별히 어렵지 않게 설치가 가능하다.
GIMP 웹사이트 바로 가기
GIMP 웹사이트 바로 가기
텍스트 이미지 만들기
- 파일>새 이미지
너비와 높이는 각각 사용할 기기의 화면 크기(예:128과 64)로 지정하면 편하다. - 보기>확대
400%로 확대하여 보는게 편하다. - 도구>텍스트
도구 박스에서 폰트 종류와 크기를 정할 수 있다.이 때 ‘부드럽게 하기’는 체크 해제해야 선명한 이미지를 얻을 수 있다.기본 폰트는 ‘Gulim’, ‘GulimChe’, ‘Dotum’, ‘DotumChe’, ‘Gungsuh’, ‘GungsuChe’ 등이 있다. 원하는 문자를 입력한다. - 이미지>이미지 자동 잘라내기
이미지▶이미지 자동 잘라내기’ 또는 ‘Crop to Content’를 선택한다. 글자만 표시되어 내보낼 준비가 된다.
xbm 형식으로 내보내기
- 파일>다른 이름으로 내보내기
‘파일▶다른 이름으로 내보내기’ 또는 ‘Export As…’를 선택한다. 좌측 하단의 ‘파일 유형 선택(확장자로)’을 눌러 ‘ X 비트맵 이미지’를 선택하고 파일명을 지정하고 내보내면 된다.
아두이노 프로그램에서 xbm 파일 사용하기
아두이노 프로그램에서 그대로 붙여서 사용하면 된다.
여기서 문제는 hangeul_bits라는 변수가 SRAM을 차지한다는 것이다.우노의 SRAM이 2k바이트인데 이 변수는 70(=35*16/8) 바이트를 잡아 먹는다.이미지가 크고 갯수가 많아지면 2k바이트는 금방 소진되고 만다.
따라서 한글 이미지는 통상적인 방식이 아니라 프로그램 영역인 플래시에 보관하도록 해야 한다.
그 표기 방법은 다음과 같다.원래 문장은 코멘트 처리하였다.
const와 PROGMEM에 유의하자.순서가 바뀌면 컴파일 오류가 날 수 있다.
PROGMEM에 의하여 프로그램 플래시 영역에 데이타를 보관하면 되지만,이 데이터는 바로 접근할 수는 없고 pgm_read_byte_near라는 함수를 이용하여 SRAM에 있는 버퍼(변수)로 옮겨온 후 사용해야 한다.
다음은 버퍼를 자동으로 할당해서 PROGMEM에서 지정한 내용을 옮겨와서 u8g.drawXBM을 호출하는 함수이다.
마지막으로 PROGMEM을 이용해서 한글 이미지 데이타를 보관하고 활용하는 전형적인 프로그램은 다음과 같다.단,OLED용 constructor와 I2C 번지는 사용 제품에 맞게 지정해야 한다.
XBM 형식으로 저장된 파일은 OLED에 이미지를 표시할 때 사용할 수 있지만, TFT나 e-paper등 다른 디스플레이 장치에서 이미지를 표현할 때 사용할 수도 있다.GIMP 프로그램에서 ‘한글’이라는 텍스트를 이미지로 만들고 ‘hangeul.xbm’으로 내 보냈다면 파일 내용은 다음과 같다.
1 2 3 4 5 6 7 8 9 10 |
#define hangeul_width 35 #define hangeul_height 16 static unsigned char hangeul_bits[] = { 0xfc, 0x20, 0xf0, 0xff, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0xff, 0x23, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x01, 0x78, 0x20, 0x00, 0x00, 0x01, 0x84, 0x20, 0x00, 0x80, 0x00, 0x02, 0xe1, 0xfd, 0xff, 0x07, 0x02, 0x21, 0x00, 0x00, 0x00, 0x84, 0x20, 0x00, 0x00, 0x00, 0x78, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0xf0, 0xff, 0x00, 0x04, 0x20, 0x00, 0x00, 0x01, 0x04, 0x00, 0xe0, 0xff, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0xf8, 0x3f, 0xe0, 0xff, 0x01 }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#define hangeul_width 35 #define hangeul_height 16 static unsigned char hangeul_bits[] = { 0xfc, 0x20, 0xf0, 0xff, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0xff, 0x23, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x01, 0x78, 0x20, 0x00, 0x00, 0x01, 0x84, 0x20, 0x00, 0x80, 0x00, 0x02, 0xe1, 0xfd, 0xff, 0x07, 0x02, 0x21, 0x00, 0x00, 0x00, 0x84, 0x20, 0x00, 0x00, 0x00, 0x78, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0xf0, 0xff, 0x00, 0x04, 0x20, 0x00, 0x00, 0x01, 0x04, 0x00, 0xe0, 0xff, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0xf8, 0x3f, 0xe0, 0xff, 0x01 }; #include "U8glib.h" U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_DEV_0); #define OLED_address 0x3c void setup() { u8g.begin(); } void loop() { u8g.firstPage(); do { u8g.drawXBM(5,10,hangeul_width,hangeul_height,hangeul_bits); } while( u8g.nextPage() ); } |
따라서 한글 이미지는 통상적인 방식이 아니라 프로그램 영역인 플래시에 보관하도록 해야 한다.
그 표기 방법은 다음과 같다.원래 문장은 코멘트 처리하였다.
1 2 |
//static unsigned char hangeul_bits[] = { ... }; const unsigned char hangeul_bits[] PROGMEM = { ... }; |
PROGMEM에 의하여 프로그램 플래시 영역에 데이타를 보관하면 되지만,이 데이터는 바로 접근할 수는 없고 pgm_read_byte_near라는 함수를 이용하여 SRAM에 있는 버퍼(변수)로 옮겨온 후 사용해야 한다.
다음은 버퍼를 자동으로 할당해서 PROGMEM에서 지정한 내용을 옮겨와서 u8g.drawXBM을 호출하는 함수이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <avr/pgmspace.h> #ifdef _U8G2LIB_HH #define U8G_CLASS_NAME U8G2 #else #define U8G_CLASS_NAME U8GLIB #endif void drawXBM(U8G_CLASS_NAME *u8g,uint8_t x,uint8_t y,uint8_t width,uint8_t height,char *bits) { int rowChars; int len; rowChars = (width + 7) / 8; len = rowChars * height; char * buffer; buffer = (char*) malloc(len+1); for(int i = 0; i < len; i++) { *(buffer + i ) = pgm_read_byte_near(bits + i); } u8g->drawXBM(x,y,width,height,buffer); free(buffer); return; } |
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#define hangeul_height 16 //static unsigned char hangeul_bits[] = { const unsigned char hangeul_bits[] PROGMEM = { 0xfc, 0x20, 0xf0, 0xff, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0xff, 0x23, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x01, 0x78, 0x20, 0x00, 0x00, 0x01, 0x84, 0x20, 0x00, 0x80, 0x00, 0x02, 0xe1, 0xfd, 0xff, 0x07, 0x02, 0x21, 0x00, 0x00, 0x00, 0x84, 0x20, 0x00, 0x00, 0x00, 0x78, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0xf0, 0xff, 0x00, 0x04, 0x20, 0x00, 0x00, 0x01, 0x04, 0x00, 0xe0, 0xff, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0xf8, 0x3f, 0xe0, 0xff, 0x01 }; #include "U8glib.h" U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_DEV_0); #define OLED_address 0x3c void setup() { u8g.begin(); } void loop() { u8g.firstPage(); do { //u8g.drawXBM(5,10,hangeul_width,hangeul_height,hangeul_bits); //progmemcpy(progmemBuffer,hangeul_width,hangeul_height,hangeul_bits); //u8g.drawXBM(5,10,hangeul_width,hangeul_height,progmemBuffer); drawXBM(&u8g,5,10,hangeul_width,hangeul_height,hangeul_bits); } while( u8g.nextPage() ); } #include <avr/pgmspace.h> #ifdef _U8G2LIB_HH #define U8G_CLASS_NAME U8G2 #else #define U8G_CLASS_NAME U8GLIB #endif void drawXBM(U8G_CLASS_NAME *u8g,uint8_t x,uint8_t y,uint8_t width,uint8_t height,char *bits) { int rowChars; int len; rowChars = (width + 7) / 8; len = rowChars * height; char * buffer; buffer = (char*) malloc(len+1); for(int i = 0; i < len; i++) { *(buffer + i ) = pgm_read_byte_near(bits + i); } u8g->drawXBM(x,y,width,height,buffer); free(buffer); return; } |
댓글 남기기