반응형

아두이노의 내용을 디스플레이 하는데는 예전에 알아보았던 16x2 LCD 모듈이나 7 세그먼트 LED를 이용하는 방법외에도 화소수가 훨씬 많은 OLED 모듈을 이용하는 방법도 있습니다. 제가 가진 모듈은 128x64 의 해상도를 가지고 있어서 텍스트는 물론 간단한 그래픽이나 애니메이션 등을 표현할 수 있습니다. 이번에 구입한 모듈은 SPI 통신을 지원하는 OLED 디스플레이 모듈로서 0.96 인치의 손목시계 크기 모듈입니다. 아두이노 프로 미니 등의 작은 아두이노와 결합하면 손목시계나 소형 온습도계, 조금 더 발전한다면 간단한 게임도 구현이 가능할 것 같습니다. 테스트겸 Adafruit 의 라이브러리와 예제소스를 이용하여 OLED 모듈을 돌려보았습니다.


OLED 모듈의 스펙

  • 0.96 인치 OLED 

  • 128 x 64 해상도

  • SPI 통신

  • 3V ~ 5V 동작전압


OLED 모듈의 PIN

  • GND (Ground)

  • VDD (3V ~ 5V)

  • SCK (Serial Clock Input, D0 으로 표시된 모듈도 있음)

  • SDA (Serial Data Input, D1 으로 표시된 모듈도 있음)

  • RES (Reset Signal Input, RST 로 표시된 모듈도 있음)

  • DC (Data / Command Control)

  • CS (Chip Select)



OLED 모듈의 모습입니다. 백원짜리 동전과 크기가 비슷합니다. 7개의 연결핀이 있습니다. 핀에는 각각의 용도가 쓰여 있습니다.


뒷면 입니다. 7개의 암핀을 꼽을 수 있도록 되어 있고 보드와 OLED 를 연결하는 갈색의 케이블이 보입니다.



위의 사진과 같이 아두이노와 연결했습니다. 연결은 다음과 같이 하였습니다.


0.96" OLED Display Module 

아두이노 우노 

GND

GND

VDD

3.3V (5V에 연결해도 됩니다.)

SCK

D10

SDA

D9

RES

D13

DC

D11

CS

D12



소스


아래의 소스를 구동하기 위해서는 아래의 사이트에서 두 개의 Adafruit 라이브러리를 다운로드 받아서 설치해야 합니다.


■ SSD1306 oled driver library for 'monochrome' 128x64 and 128x32 OLEDs

https://github.com/adafruit/Adafruit_SSD1306


■ Adafruit GFX graphics core library

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


또는 아래의 파일을 다운로드 받아도 됩니다.

Adafruit-GFX-Library-master.zip

Adafruit_SSD1306-master.zip


라이브러리 설치는 압축을 풀고 아두이노 IDE 설치 폴더 하위의 libraries 폴더에 넣고 아두이노 IDE를 재실행하면 됩니다.


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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
/*********************************************************************
This is an example for our Monochrome OLEDs based on SSD1306 drivers
  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/category/63_98
This example is for a 128x64 size display using SPI to communicate
4 or 5 pins are required to interface
Adafruit invests time and resources providing this open source code, 
please support Adafruit and open-source hardware by purchasing 
products from Adafruit!
Written by Limor Fried/Ladyada  for Adafruit Industries.  
BSD license, check license.txt for more information
All text above, and the splash screen must be included in any redistribution
*********************************************************************/
 
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
 
// If using software SPI (the default case):
#define OLED_MOSI   9
#define OLED_CLK   10
#define OLED_DC    11
#define OLED_CS    12
#define OLED_RESET 13
Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);
 
/* Uncomment this block to use hardware SPI
#define OLED_DC     6
#define OLED_CS     7
#define OLED_RESET  8
Adafruit_SSD1306 display(OLED_DC, OLED_RESET, OLED_CS);
*/
 
#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2
 
#define LOGO16_GLCD_HEIGHT 16 
#define LOGO16_GLCD_WIDTH  16 
static const unsigned char PROGMEM logo16_glcd_bmp[] =
{ B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000 };
 
#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif
 
void setup()   {                
  Serial.begin(9600);
 
  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC);
  // init done
  
  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
  display.display();
  delay(2000);
 
  // Clear the buffer.
  display.clearDisplay();
 
  // draw a single pixel
  display.drawPixel(1010, WHITE);
  // Show the display buffer on the hardware.
  // NOTE: You _must_ call display after making any drawing commands
  // to make them visible on the display hardware!
  display.display();
  delay(2000);
  display.clearDisplay();
 
  // draw many lines
  testdrawline();
  display.display();
  delay(2000);
  display.clearDisplay();
 
  // draw rectangles
  testdrawrect();
  display.display();
  delay(2000);
  display.clearDisplay();
 
  // draw multiple rectangles
  testfillrect();
  display.display();
  delay(2000);
  display.clearDisplay();
 
  // draw mulitple circles
  testdrawcircle();
  display.display();
  delay(2000);
  display.clearDisplay();
 
  // draw a white circle, 10 pixel radius
  display.fillCircle(display.width()/2, display.height()/210, WHITE);
  display.display();
  delay(2000);
  display.clearDisplay();
 
  testdrawroundrect();
  delay(2000);
  display.clearDisplay();
 
  testfillroundrect();
  delay(2000);
  display.clearDisplay();
 
  testdrawtriangle();
  delay(2000);
  display.clearDisplay();
   
  testfilltriangle();
  delay(2000);
  display.clearDisplay();
 
  // draw the first ~12 characters in the font
  testdrawchar();
  display.display();
  delay(2000);
  display.clearDisplay();
 
  // draw scrolling text
  testscrolltext();
  delay(2000);
  display.clearDisplay();
 
  // text display tests
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.println("Hello, world!");
  display.setTextColor(BLACK, WHITE); // 'inverted' text
  display.println(3.141592);
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.print("0x"); display.println(0xDEADBEEF, HEX);
  display.display();
  delay(2000);
  display.clearDisplay();
 
  // miniature bitmap display
  display.drawBitmap(3016,  logo16_glcd_bmp, 16161);
  display.display();
 
  // invert the display
  display.invertDisplay(true);
  delay(1000); 
  display.invertDisplay(false);
  delay(1000); 
  display.clearDisplay();
 
  // draw a bitmap icon and 'animate' movement
  testdrawbitmap(logo16_glcd_bmp, LOGO16_GLCD_HEIGHT, LOGO16_GLCD_WIDTH);
}
 
 
void loop() {
  
}
 
 
void testdrawbitmap(const uint8_t *bitmap, uint8_t w, uint8_t h) {
  uint8_t icons[NUMFLAKES][3];
 
  // initialize
  for (uint8_t f=0; f< NUMFLAKES; f++) {
    icons[f][XPOS] = random(display.width());
    icons[f][YPOS] = 0;
    icons[f][DELTAY] = random(5+ 1;
    
    Serial.print("x: ");
    Serial.print(icons[f][XPOS], DEC);
    Serial.print(" y: ");
    Serial.print(icons[f][YPOS], DEC);
    Serial.print(" dy: ");
    Serial.println(icons[f][DELTAY], DEC);
  }
 
  while (1) {
    // draw each icon
    for (uint8_t f=0; f< NUMFLAKES; f++) {
      display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE);
    }
    display.display();
    delay(200);
    
    // then erase it + move it
    for (uint8_t f=0; f< NUMFLAKES; f++) {
      display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, BLACK);
      // move it
      icons[f][YPOS] += icons[f][DELTAY];
      // if its gone, reinit
      if (icons[f][YPOS] > display.height()) {
        icons[f][XPOS] = random(display.width());
        icons[f][YPOS] = 0;
        icons[f][DELTAY] = random(5+ 1;
      }
    }
   }
}
 
 
void testdrawchar(void) {
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
 
  for (uint8_t i=0; i < 168; i++) {
    if (i == '\n'continue;
    display.write(i);
    if ((i > 0) && (i % 21 == 0))
      display.println();
  }    
  display.display();
}
 
void testdrawcircle(void) {
  for (int16_t i=0; i<display.height(); i+=2) {
    display.drawCircle(display.width()/2, display.height()/2, i, WHITE);
    display.display();
  }
}
 
void testfillrect(void) {
  uint8_t color = 1;
  for (int16_t i=0; i<display.height()/2; i+=3) {
    // alternate colors
    display.fillRect(i, i, display.width()-i*2, display.height()-i*2, color%2);
    display.display();
    color++;
  }
}
 
void testdrawtriangle(void) {
  for (int16_t i=0; i<min(display.width(),display.height())/2; i+=5) {
    display.drawTriangle(display.width()/2, display.height()/2-i,
                     display.width()/2-i, display.height()/2+i,
                     display.width()/2+i, display.height()/2+i, WHITE);
    display.display();
  }
}
 
void testfilltriangle(void) {
  uint8_t color = WHITE;
  for (int16_t i=min(display.width(),display.height())/2; i>0; i-=5) {
    display.fillTriangle(display.width()/2, display.height()/2-i,
                     display.width()/2-i, display.height()/2+i,
                     display.width()/2+i, display.height()/2+i, WHITE);
    if (color == WHITE) color = BLACK;
    else color = WHITE;
    display.display();
  }
}
 
void testdrawroundrect(void) {
  for (int16_t i=0; i<display.height()/2-2; i+=2) {
    display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i, display.height()/4, WHITE);
    display.display();
  }
}
 
void testfillroundrect(void) {
  uint8_t color = WHITE;
  for (int16_t i=0; i<display.height()/2-2; i+=2) {
    display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i, display.height()/4, color);
    if (color == WHITE) color = BLACK;
    else color = WHITE;
    display.display();
  }
}
   
void testdrawrect(void) {
  for (int16_t i=0; i<display.height()/2; i+=2) {
    display.drawRect(i, i, display.width()-2*i, display.height()-2*i, WHITE);
    display.display();
  }
}
 
void testdrawline() {  
  for (int16_t i=0; i<display.width(); i+=4) {
    display.drawLine(00, i, display.height()-1, WHITE);
    display.display();
  }
  for (int16_t i=0; i<display.height(); i+=4) {
    display.drawLine(00, display.width()-1, i, WHITE);
    display.display();
  }
  delay(250);
  
  display.clearDisplay();
  for (int16_t i=0; i<display.width(); i+=4) {
    display.drawLine(0, display.height()-1, i, 0, WHITE);
    display.display();
  }
  for (int16_t i=display.height()-1; i>=0; i-=4) {
    display.drawLine(0, display.height()-1, display.width()-1, i, WHITE);
    display.display();
  }
  delay(250);
  
  display.clearDisplay();
  for (int16_t i=display.width()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-1, i, 0, WHITE);
    display.display();
  }
  for (int16_t i=display.height()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-10, i, WHITE);
    display.display();
  }
  delay(250);
 
  display.clearDisplay();
  for (int16_t i=0; i<display.height(); i+=4) {
    display.drawLine(display.width()-100, i, WHITE);
    display.display();
  }
  for (int16_t i=0; i<display.width(); i+=4) {
    display.drawLine(display.width()-10, i, display.height()-1, WHITE); 
    display.display();
  }
  delay(250);
}
 
void testscrolltext(void) {
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(10,0);
  display.clearDisplay();
  display.println("scroll");
  display.display();
 
  display.startscrollright(0x000x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);
  display.startscrollleft(0x000x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);    
  display.startscrolldiagright(0x000x07);
  delay(2000);
  display.startscrolldiagleft(0x000x07);
  delay(2000);
  display.stopscroll();
}
cs


위의 소스는 라이브러리 설치 후 파일 -> 예제 -> Adafruit SSD1306 -> SSD1306_128x64_spi 를 불러 와도 됩니다. 만약 128x32 의 해상도의 모듈을 가지고 있다면 SSD1306_128x32_spi 예제를 불러오면 됩니다. 

그런데 위의 소스를 컴파일하니 에러가 납니다. 소스의 62행을 보면 

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

이 부분에서 컴파일 에러가 나고 업로드가 되지 않습니다. 라이브러리가 128x32 해상도로 설정되어 있어서 그렇습니다. 해결방법은...

1. 위에서 설치한 SSD1306 oled driver library 폴더에 들어갑니다.
2. Adafruit_SSD1306.h 파일을 편집기로 엽니다.
3. #define SSD1306_128_32 부분을 주석처리하고 #define SSD1306_128_64 의 주석을 해제 하고 저장합니다. (물론 128x32 해상도의 모듈을 사용한다면 수정하지 않아도 됩니다.)
4. 아두이노 IDE를 닫았다가 다시 실행하고 소스를 다시 업로드 하면 컴파일이 될 것 입니다.

소스를 보면 영어이긴 하지만 자세하게 설명이 달려 있습니다. 텍스트를 표시하는건 display.println("Hello, world!"); 이런 식으로 아주 간단하게 표시가 가능하고 부가적으로 텍스트 크기, 컬러, 표시 위치 등의 설정 등이 가능합니다. 스크롤도 간단하게 가능하네요. 소스와 주석을 참고하면 거의 모든 것을 그릴 수 있을 것 같습니다.



128x64 해상도의 모듈에 128x32 소스를 적용한 모습... 실행은 되지만 해상도가 맞지 않아서 일부모습만 보인다거나 화면이 깨지는 모습을 보여줍니다.


128x64 해상도의 제대로된 소스를 적용해 보았습니다. 모든 화면이 잘 표시되고 삼각형이나 원도 제대로 표시 됩니다. 텍스트도 깨지지 않고 표시 됩니다.



다양한 표시가 가능해서 각종 센서류와 연결하여 휴대용 장치를 만들기에 아주 적당한 디스플레이 모듈 같습니다. 앞으로 많이 활용을 해 봐야겠습니다.



▶추가사항(2016.08.03) : http://deneb21.tistory.com/429 글에서 위의 모듈에 텍스트를 출력하는 방법을 정리해 보았다.

반응형

+ Recent posts