Bài viết này sẽ hướng dẫn cách sử dụng màn hình OLED I2C SSD1306 0.96 inch với Arduino. Chúng tôi sẽ chỉ cho bạn một số tính năng của màn hình OLED, cách kết nối nó với bo mạch Arduino và cách viết văn bản, vẽ hình và hiển thị hình ảnh bitmap trên màn Oled

Bạn có thể tham khảo & mua linh kiện tại đây:
  1. Mạch Arduino UNO R3
  2. Màn hình Oled I2C SSD1360 0.96 inch
  3. Test board (Breadboard)
  4. Dây cắm test board
  5. Điện trở 4.7k ôm


Giới thiệu màn hình OLED 0.96 inch


Màn OLED (organic light-emitting diode )  mà chúng tôi sẽ sử dụng trong hướng dẫn này là model SSD1306: màn hình đơn sắc, 0,96 inch với 128 × 64 pixel như trong hình dưới đây.




Màn hình OLED không yêu cầu đèn nền, tuy nhiên nó vẫn hiển thị tốt trong tối.


Ngoài ra, các pixel của nó chỉ tiêu thụ năng lượng khi hoạt đồng, vì vậy màn hình OLED tiêu thụ ít năng lượng hơn khi so sánh với các màn hình khác.


Sơ đồ mà chúng tôi sử dụng ở đây chỉ có bốn chân và giao tiếp với Arduino bằng giao thức truyền thông I2C.


Có những sơ đồ đi kèm với một chân RESET thêm.



PinNối với chân trong Arduino Uno
Vin5V
GNDGND
SCLA5
SDAA4


Nếu bạn sử dụng một bo mạch Arduino khác, hãy đảm bảo bạn kiểm tra các chân I2C chính xác:

  • Nano: SDA (A4); SCL (A5);
  • MEGA: SDA (20); SCL (21);
  • Leonardo: SDA (20); SCL (21);



Thư viện

Để điều khiển màn hình OLED, bạn cần cài thư viện adafruit_SSD1306.h và thư viện adafruit_GFX.h nếu đã có sẵn thì bỏ qua bước này. Thực hiện theo các hướng dẫn bên dưới để cài đặt các thư viện. 

1. Mở phần mềm Arduino IDE  và vào Sketch Include Library > Manage Libraries. Trình quản lý thư viện (Library Manager) sẽ mở.

2. Nhập vào SSD1306, trong hộp tìm kiếm và cài đặt thư viện SSD1306 từ Adafruit.


3. Sau khi cài đặt thư viện SSD1306 từ Adafruit, hãy nhập GFX trực tiếp vào hộp tìm kiếm và cài đặt thư viện.


4. Sau khi cài đặt các thư viện, hãy thoát và mở lại phần mềm Arduino IDE.

Mẹo viết văn bản bằng các thư viện này

Ở đây, một số chức năng sẽ giúp bạn xử lý thư viện màn hình OLED để viết văn bản hoặc vẽ đồ họa đơn giản.

  • display.clearDisplay() – tắt tất cả các điểm ảnh (pixels)
  • display.drawPixel(x,y, color) – vẽ một điểm ảnh theo tọa độ x, y
  • display.setTextSize(n) – Đặt kích thước phông chữ, hỗ trợ kích thước từ 1 đến 8
  • display.setCursor(x,y) – đặt tọa độ để bắt đầu viết văn bản
  • display.print(“message”) – in các ký tự tại vị trí x, y
  • display.display() – gọi phương thức này để thay đổi có hiệu lực
Kiểm tra màn hình OLED

Sau khi kết nối màn hình OLED với Arduino và cài đặt tất cả các thư viện cần thiết, bạn có thể sử dụng một ví dụ từ thư viện để xem màn hình có hoạt động tốt không.


Tiếp theo nên nạp đoạn code sau: 

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define NUMFLAKES     10 // Number of snowflakes in the animation example

#define LOGO_HEIGHT   16
#define LOGO_WIDTH    16
static const unsigned char PROGMEM logo_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 };

void setup() {
  Serial.begin(115200);

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

  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
  display.display();
  delay(2000); // Pause for 2 seconds

  // Clear the buffer
  display.clearDisplay();

  // Draw a single pixel in white
  display.drawPixel(10, 10, WHITE);

  // Show the display buffer on the screen. You MUST call display() after
  // drawing commands to make them visible on screen!
  display.display();
  delay(2000);
  // display.display() is NOT necessary after every single drawing command,
  // unless that's what you want...rather, you can batch up a bunch of
  // drawing operations and then update the screen all at once by calling
  // display.display(). These examples demonstrate both approaches...

  testdrawline();      // Draw many lines

  testdrawrect();      // Draw rectangles (outlines)

  testfillrect();      // Draw rectangles (filled)

  testdrawcircle();    // Draw circles (outlines)

  testfillcircle();    // Draw circles (filled)

  testdrawroundrect(); // Draw rounded rectangles (outlines)

  testfillroundrect(); // Draw rounded rectangles (filled)

  testdrawtriangle();  // Draw triangles (outlines)

  testfilltriangle();  // Draw triangles (filled)

  testdrawchar();      // Draw characters of the default font

  testdrawstyles();    // Draw 'stylized' characters

  testscrolltext();    // Draw scrolling text

  testdrawbitmap();    // Draw a small bitmap image

  // Invert and restore display, pausing in-between
  display.invertDisplay(true);
  delay(1000);
  display.invertDisplay(false);
  delay(1000);

  testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps
}

void loop() {
}

void testdrawline() {
  int16_t i;

  display.clearDisplay(); // Clear display buffer

  for(i=0; i<display.width(); i+=4) {
    display.drawLine(0, 0, i, display.height()-1, WHITE);
    display.display(); // Update screen with each newly-drawn line
    delay(1);
  }
  for(i=0; i<display.height(); i+=4) {
    display.drawLine(0, 0, display.width()-1, i, WHITE);
    display.display();
    delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=0; i<display.width(); i+=4) {
    display.drawLine(0, display.height()-1, i, 0, WHITE);
    display.display();
    delay(1);
  }
  for(i=display.height()-1; i>=0; i-=4) {
    display.drawLine(0, display.height()-1, display.width()-1, i, WHITE);
    display.display();
    delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=display.width()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-1, i, 0, WHITE);
    display.display();
    delay(1);
  }
  for(i=display.height()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-1, 0, i, WHITE);
    display.display();
    delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=0; i<display.height(); i+=4) {
    display.drawLine(display.width()-1, 0, 0, i, WHITE);
    display.display();
    delay(1);
  }
  for(i=0; i<display.width(); i+=4) {
    display.drawLine(display.width()-1, 0, i, display.height()-1, WHITE);
    display.display();
    delay(1);
  }

  delay(2000); // Pause for 2 seconds
}

void testdrawrect(void) {
  display.clearDisplay();

  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(); // Update screen with each newly-drawn rectangle
    delay(1);
  }

  delay(2000);
}

void testfillrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2; i+=3) {
    // The INVERSE color is used so rectangles alternate white/black
    display.fillRect(i, i, display.width()-i*2, display.height()-i*2, INVERSE);
    display.display(); // Update screen with each newly-drawn rectangle
    delay(1);
  }

  delay(2000);
}

void testdrawcircle(void) {
  display.clearDisplay();

  for(int16_t i=0; i<max(display.width(),display.height())/2; i+=2) {
    display.drawCircle(display.width()/2, display.height()/2, i, WHITE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testfillcircle(void) {
  display.clearDisplay();

  for(int16_t i=max(display.width(),display.height())/2; i>0; i-=3) {
    // The INVERSE color is used so circles alternate white/black
    display.fillCircle(display.width() / 2, display.height() / 2, i, INVERSE);
    display.display(); // Update screen with each newly-drawn circle
    delay(1);
  }

  delay(2000);
}

void testdrawroundrect(void) {
  display.clearDisplay();

  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();
    delay(1);
  }

  delay(2000);
}

void testfillroundrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2-2; i+=2) {
    // The INVERSE color is used so round-rects alternate white/black
    display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i,
      display.height()/4, INVERSE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testdrawtriangle(void) {
  display.clearDisplay();

  for(int16_t i=0; i<max(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();
    delay(1);
  }

  delay(2000);
}

void testfilltriangle(void) {
  display.clearDisplay();

  for(int16_t i=max(display.width(),display.height())/2; i>0; i-=5) {
    // The INVERSE color is used so triangles alternate white/black
    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, INVERSE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testdrawchar(void) {
  display.clearDisplay();

  display.setTextSize(1);      // Normal 1:1 pixel scale
  display.setTextColor(WHITE); // Draw white text
  display.setCursor(0, 0);     // Start at top-left corner
  display.cp437(true);         // Use full 256 char 'Code Page 437' font

  // Not all the characters will fit on the display. This is normal.
  // Library will draw what it can and the rest will be clipped.
  for(int16_t i=0; i<256; i++) {
    if(i == '\n') display.write(' ');
    else          display.write(i);
  }

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

void testdrawstyles(void) {
  display.clearDisplay();

  display.setTextSize(1);             // Normal 1:1 pixel scale
  display.setTextColor(WHITE);        // Draw white text
  display.setCursor(0,0);             // Start at top-left corner
  display.println(F("Hello, world!"));

  display.setTextColor(BLACK, WHITE); // Draw 'inverse' text
  display.println(3.141592);

  display.setTextSize(2);             // Draw 2X-scale text
  display.setTextColor(WHITE);
  display.print(F("0x")); display.println(0xDEADBEEF, HEX);

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

void testscrolltext(void) {
  display.clearDisplay();

  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(WHITE);
  display.setCursor(10, 0);
  display.println(F("scroll"));
  display.display();      // Show initial text
  delay(100);

  // Scroll in various directions, pausing in-between:
  display.startscrollright(0x00, 0x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);
  display.startscrollleft(0x00, 0x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);
  display.startscrolldiagright(0x00, 0x07);
  delay(2000);
  display.startscrolldiagleft(0x00, 0x07);
  delay(2000);
  display.stopscroll();
  delay(1000);
}

void testdrawbitmap(void) {
  display.clearDisplay();

  display.drawBitmap(
    (display.width()  - LOGO_WIDTH ) / 2,
    (display.height() - LOGO_HEIGHT) / 2,
    logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
  display.display();
  delay(1000);
}

#define XPOS   0 // Indexes into the 'icons' array in function below
#define YPOS   1
#define DELTAY 2

void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) {
  int8_t f, icons[NUMFLAKES][3];

  // Initialize 'snowflake' positions
  for(f=0; f< NUMFLAKES; f++) {
    icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
    icons[f][YPOS]   = -LOGO_HEIGHT;
    icons[f][DELTAY] = random(1, 6);
    Serial.print(F("x: "));
    Serial.print(icons[f][XPOS], DEC);
    Serial.print(F(" y: "));
    Serial.print(icons[f][YPOS], DEC);
    Serial.print(F(" dy: "));
    Serial.println(icons[f][DELTAY], DEC);
  }

  for(;;) { // Loop forever...
    display.clearDisplay(); // Clear the display buffer

    // Draw each snowflake:
    for(f=0; f< NUMFLAKES; f++) {
      display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE);
    }

    display.display(); // Show the display buffer on the screen
    delay(200);        // Pause for 1/10 second

    // Then update coordinates of each flake...
    for(f=0; f< NUMFLAKES; f++) {
      icons[f][YPOS] += icons[f][DELTAY];
      // If snowflake is off the bottom of the screen...
      if (icons[f][YPOS] >= display.height()) {
        // Reinitialize to a random position, just off the top
        icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
        icons[f][YPOS]   = -LOGO_HEIGHT;
        icons[f][DELTAY] = random(1, 6);
      }
    }
  }
}

Nếu OLED của bạn không có chân RESET, bạn nên đặt biến OLED_RESET thành -1 như dưới đây:

#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)

Nạp code vào board Arduino của bạn. Đừng quên chọn đúng board và cổng COM bên phải trong menu Tools


Nếu màn hình OLED của bạn không hiển thị bất cứ điều gì:



  • Kiểm tra xem màn hình OLED có được kết nối đúng với Arduino không
  • Kiểm tra kỹ địa chỉ I2C của màn hình OLED: với OLED được kết nối với Arduino, nạp lại mã này và kiểm tra địa chỉ I2C trong Serial Monitor
Bạn nên thay đổi địa chỉ OLED trong dòng sau, nếu cần. Trong trường hợp của chúng tôi, địa chỉ là 0x3C.

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
Viết chữ trên màn hình OLED

Dòng code dưới đây để hiển thị chữ Hello, world! trên màn hình OLED


/*********
  Rui Santos
  Complete project details at https://randomnerdtutorials.com  
*********/

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

void setup() {
  Serial.begin(115200);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  delay(2000);
  display.clearDisplay();

  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 10);
  // Display static text
  display.println("Hello, world!");
  display.display(); 
}

void loop() {
  
}



Sau khi nạp code, màn hình Oled sẽ hiển thị như hình dưới đây:


Cài đặt thư viện

Đầu tiên, bạn cần cài thư viện cần thiết. Thư viện Wire để sử dụng thư viện I2C và thư viện Adafruit để ghi vào màn hình: Adafruit_GFX và Adafruit_SSD1306.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Khởi tạo màn hình OLED

Sau đó, bạn xác định kích thước OLED của bạn. Trong ví dụ này, chúng ta sử dụng màn hình OLED 128 × 64. Nếu bạn sử dụng các kích thước khác, bạn có thể thay đổi nó trong các biến SCREEN_WIDTH và SCREEN_HEIGHT.

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
Sau đó, khởi tạo một đối tượng hiển thị với kích thước được xác định trước đó với giao thức truyền thông I2C (&Wire).

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
Tham số (-1) có nghĩa là màn hình OLED của bạn không có chân RESET. Nếu màn hình OLED của bạn có chân RESET, thì nó sẽ được kết nối với GPIO. Trong trường hợp đó, bạn nên truyền số GPIO làm tham số.

Trong setup(), khởi chạy Serial Monitor với baud raute 115200 cho mục đích gỡ lỗi.

Serial.begin(115200);
Khởi tạo màn hình OLED bằng phương thứbegin() như sau:

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
  Serial.println("SSD1306 allocation failed");
  for(;;); // Don't proceed, loop forever
}
Đoạn mã này cũng in một thông báo trên Serial Monitor, trong trường hợp chúng ta không thể kết nối với màn hình.

Serial.println("SSD1306 allocation failed");
Trong trường hợp bạn sử dụng màn hình OLED khác, bạn có thể cần thay đổi địa chỉ OLED. Trong trường hợp của chúng tôi, địa chỉ là 0x3C.

Nếu địa chỉ này không hoạt động, bạn có thể chạy code quét I2C để tìm địa chỉ OLED của mình. Bạn có thể tải code quét địa chỉ I2C tại đây.

Sau khi khởi tạo màn hình, hãy thêm độ trễ màn 2 giây để OLED có đủ thời gian để khởi tạo trước khi viết văn bản:

delay(2000);
Xóa màn hình, đặt kích thước phông chữ, màu sắc và viết văn bản

Sau khi khởi tạo màn hình, xóa bộ đệm hiển thị bằng hàm clearDisplay():

display.clearDisplay();
Trước khi viết văn bản, bạn cần đặt kích thước văn bản, màu sắc và nơi văn bản sẽ được hiển thị trong OLED.

Đặt kích thước phông chữ bằng cách sử dụng hàm setTextSize():

display.setTextSize(1); 
Đặt màu phông chữ bằng hàm setTextColor()

display.setTextColor(WHITE);     
WHITE đặt phông chữ màu trắng và nền đen.

Xác định vị trí nơi văn bản bắt đầu sử dụng hàm setCursor(x,y). Trong trường hợp này, chúng tôi cài đặt văn bản để bắt đầu ở tọa độ (0,10).

display.setCursor(0,10);  
Cuối cùng, bạn có thể hiển thị chữ lên màn hình bằng hàm println(), như sau:

display.println("Hello, world!");
Sau đó, bạn cần gọi hàm display() để thực sự hiển thị văn bản trên màn hình.

display.display();

Xem tiếp phần 2: Cách tạo một số hiệu ứng chữ dịch chuyển trên Oled, thay đổi font chữ và vẽ hình trên màn hình OLED.

Theo dõi chúng tôi qua kênh Facebook hoặc Twitter.
TÙY CHỌN TIỆN ÍCH M

Post a Comment

Previous Post Next Post