【Maker 電子學】小型 OLED 顯示裝置的原理與應用—PART 2

作者:Bird

上篇文章【Maker 電子學】小型 OLED 顯示裝置的原理與應用 — PART 1,我們從小型顯示裝置的老祖宗 LCD 開始,聊了用 SSD1306 OLED 控制 IC 驅動的小型 OLED 面板。這一回我們將繼續這個主題,要告訴讀者們如何將它連上 Arduino、Node MCU 等快速開發平台,並用軟體驅動它來顯示你想要的內容。

準備材料

這次的實驗會用到兩個模組:Node MCU 以及 I2C(以下寫作 I2C) 版本的 128×32 SSD1306 OLED 模組,這兩個模組現在都非常容易取得,價格也不貴,是做實驗的好幫手。

(圖片來源:Bird 提供)

我們之所以選用以 ESP8266 爲核心的 NodeMCU,而不用標準的 Arduino 板子,是因爲 SD1366 只支援 3.3V 的邏輯準位,而標準的 Arduino 板子如 UNO 等,則是 5V 邏輯準位的系統。

ESP8266 是功能非常強大的 Wi-Fi SoC,不過我們這次只會把它當作 Arduino 的平台來使用,不會使用到任何其它的功能,因此讀者如果要用其它的 Arduino 平台如 Pro Micro 也可以,但要注意必須使用 3.3V 邏輯準位的版本,否則可能會導致 OLED 模組的損壞。

連接

拜 SSD1306 簡潔的設計和完整性所賜,它可以很簡單地用 I2C 來控制,因此我們只要在這兩張板子之間連接 4 條線,分別是 VCC、GND、SCL、SDA。

ESP8266 並沒有硬體 I2C 界面,當它用 Arduino 開發時,I2C 是用軟體在 GPIO 上模擬出來的,因此理論上 NodeMCU 的任何兩支 GPIO 都可以拿來當 I2C 使用,只要在軟體啟動 I2C 界面時指定就好。如果沒有特別指定,預設的 I2C 接腳在 GPIO4 和 GPIO5,其中 GPIO4 是 SDA、GPIO5 是 SCL,我們就遵照這個慣例來連接。

(圖片來源:Bird 提供)

我建議有焊接能力的讀者可以拿起烙鐵來,直接用鍍銀 OK 線或彩虹排線,焊 4 根線將它們連接起來,不必再用麵包板或杜邦跳線(通常確實而可靠的訊號連接是避免實驗失敗的重要因素,而品質不良的麪包板或是杜邦跳線,往往是不可靠連接的罪魁禍首)。

這裡要特別注意的是,NodeMCU 板子上標示的接腳編號如 D0、D1、D2 等,跟我們使用 Arduino 來驅動 NodeMCU 時的 GPIO 編號並不一致。事實上 NodeMCU 這個名字指的是另一個不同於 Arduino 的軟體開發平台,它使用一種叫「Lua」的程式語言來開發,但因爲 Arduino 實在太受歡迎、可攜性又高,因此大部分的人在 NodeMCU 板子上仍然使用 Arduino 做爲開發工具。 NodeMCU 板子上的接腳編號是在 NodeMCU 開發平台中的編號,它與 Arduino 的接腳編號對應關係列在上面那張圖中。

(圖片來源:Bird 提供)

將兩張板子連接好之後,我們就可以寫程式來驅動它了。

站在巨人的肩膀上

接下來我們會示範使用 Arduino 上的 library 來驅動這個 OLED 顯示器。

我們先到 Adruino IDE 的 Library Manager 中搜尋並新增這兩個 library:「Adadruit_SSD1306」以及 「Adafruit_GFX_Library」。熟悉 Arduino 的讀者對 Adafruit 這家公司應該不會太陌生,這是一間位於紐約的開源硬體公司,供應各種 maker 需要的模組、電子零件,也提供許多開發工具和開源的 library,我們這次使用的這兩個 library 就是由 Adafruit 提供的。

(圖片來源:Bird 提供)

Adadruit_SSD1306 是用來驅動 SSD1306 的底層 library,它支援 I2C 和 SPI 界面與 SSD1306 通訊,並提供了控制 SSD1306 的基本功能如 register 和 frame buffer 讀寫等。而 Adafruit_GFX_Library 則是在顯示裝置上繪圖的核心 library,它提供了如顯示文字、劃線、幾何圖形等功能。

第一件事,我們要在程式碼中 include 所有需要的 library:

1
2
3
#include <Wire.h> // I2C library
#include <Adafruit_GFX.h> // Adafruit graphics library
#include <Adafruit_SSD1306.h> // Adafruit SSD1306 library

除了上面介紹的兩個 library 外,我們還需要一個叫 Wire 的 library,這是 Adruino 標準的 I2C 通訊 library,不過我們不會直接使用它,當我們告訴 Adafruit_SSD1306 我們要用 I2C 當作與 SSD1306 通訊的界面之後,Adafruit_SSd1306 會自動去呼叫 Wire 相關的功能。

整個 Adafruit_SSd1306 中只有一個 class,就叫做 ,Adafruit_SSd1306。這個 class 有 6 個 constructor (其中 2 個已經不再使用),16 個 member function。其中 I2C 界面用的 constructor 長這個樣子:

1
2
3
4
5
6
7
8
Adafruit_SSD1306::Adafruit_SSD1306 (
uint8_t w,
uint8_t h,
TwoWire * twi = &Wire,
int8_t rst_pin = -1,
uint32_t clkDuring = 400000UL,
uint32_t clkAfter = 100000UL
)

當我們建立 OLED 顯示器物件時,需要傳 5 個參數給它,分別是:顯示器的水平方向點數、垂直方向點數、指向 I2C 通訊界面的指標、reset 接腳的 GPIO 編號與 SSD1306 通訊時的 I2C bus 速度以及與 SSD1306 通訊完了之後的 I2C bus 速度。

於是在 setup() 和 main() 之前,我們要先用上面這個 constructor 建立一個叫做 my_display 的物件,用來代表 OLED 顯示器:

1
Adafruit_SSD1306 my_display(128, 32, &Wire, -1, 400, 100);

由於我們手上的 OLED 模組並沒有外接的 reset 接腳,因此 reset 接腳那一個參數填 -1,代表我們不去控制 reset 訊號。

接下來,在 setup() 中要執行的第一個指令,就是 Adafruit_SSD1306::begin() 這個 member function,它負責設定跟 SSD1306 有關的幾個重要參數,並初始化 SSD1306。這個 member function 需要 4 個參數:

1
2
3
4
5
6
bool Adafruit_SSD1306::begin(
uint8_t vcs = SSD1306_SWITCHCAPVCC,
uint8_t addr = 0,
bool reset = true,
bool periphBegin = true
)

第一個參數是設定 SSD1306 供應 OLED 面板電源的方式。SSD1306 需要一個 7~15V 的 VCC 電壓,用來驅動 OLED 面板,而 SSD1306 內建了 charge pump 升壓電路,可以由 3.3V 的邏輯電源產生 7.5V 的 VCC。在大部分的狀況下,我們都會建議使用這個內部的升壓電路來產生面板的電源,只有在很少的設計中我們會需要使用外部供應的 VCC。第一個參數設成 SSD1306_SWITCHCAPVCC 代表使用內建的升壓電路,設成 SSD1306_EXTERNALVCC 代表使用外部供應的 VCC 電源。

第二個參數是設定 SSD1306 的 I2C slave address(最近聽說 slave 這個字爭議很大啊!),SSD1306 可以用一根 pin 選擇 0x3c 或 0x3d 這兩個 I2C address,不過大部分的模組都沒有把這個接腳拉出來讓使用者選擇,因此 99% 的狀況下就只能用 0x3c 這個位址。填 0 就代表使用預設的 0x3c 這個位址。

第三個參數代表要不要驅動 reset pin,第四個參數代表要不要去呼叫 I2C library(也就是 Wire)的初始化 function。

於是這個指令寫起來就是這樣:

1
my_display.begin(SSD1306_SWITCHCAPVCC, 0, false, true);

初試啼聲

執行完這一行,SSD1306 就會被初始化,並開始驅動 OLED 面板,如果沒有意外,OLED 面板就會亮起來了,像這樣:

(圖片來源:Bird 提供)

亮是亮了,但怎麼顯示了一些看起來亂七八糟的雜點呢?

不然你期望它顯示什麼?我們又沒告訴它要畫什麼,它當然就自由發揮啦!SSD1306 內部有一塊叫做 frame buffer 的記憶體,是用來儲存要顯示的內容的。而我們看到的這些雜點,就是供電後,這塊記憶體裡隨機的內容。

因此我們要把它清掉:

1
my_display.clearDisplay();

但如果只加上這一行,畫面還是不會變。我們還需要這個指令:

1
my_display.display();

GFX 這個繪圖 library 的運作方式是這樣:所有的繪圖動作都是先在 Arduino 系統內一塊記憶體 buffer 中操作。當你下達 .display() 時,它才會把記憶體中畫完的 buffer 透過通訊界面一次送到 SSD1306 的 frame buffer 中,讓 OLED 上的顯示更新。因此當我們執行 .clearDisplay() 時,它是先清掉中 buffer 的內容,等到我們執行 .display() 時,這個被清掉的內容才更新到 SSD1306 上。

Hello World!

把顯示器上的雜點清乾淨之後,讓我們來寫幾個字吧!

GFX library 繼承了 Arduino 系統的 print() 和 println() 兩個 class,因此雖然它沒有這個 member function,我們也可以用 print() 和 println() 在顯示器上面寫字。不過在此之前,我們得先設定要顯示的字的顏色、大小、和位置:

1
2
3
my_display.setTextSize(1);
my_display.setCursor(0,0);
my_display.setTextColor(1);

最後就可以用 print() 來顯示字串:

1
my_display.print("Hello World!");

最後別忘了要把內容更新到顯示器上:

1
my_display.display();

於是就完成了我們在 OLED 顯示器上的 Hello World!

(圖片來源:Bird 提供)

GFX library 還有非常非常多的功能,它可以畫線、畫圓、畫矩形、填色,也可以使用不同的字型顯示文字。Adafruit 提供了非常完整的文件和範例程式,有興趣的讀者可以搜尋相關的資料,所有 library 的原始碼在 GitHub 上也看得到。

小結

這一次我們將 SSD1306 的 OLED 顯示器模組和 NodeMCU 連接起來,並用了 Adafruit 在 Arduino 上提供的 library,示範了一個非常簡單的例子:在 128×32 的 OLED 顯示器上寫出 “Hello World!”。下一回,我們要更深入地探討 SSD1306 內部運作的方式,讀者們便可以更深入地了解到 Adafruit 所提供的 library 幫我們做了什麼事。

這一次完整的程式碼附於文末:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <Wire.h> // I2C library
#include <Adafruit_GFX.h> // Adafruit graphics library
#include <Adafruit_SSD1306.h> // Adafruit SSD1306 library for OLED panel

Adafruit_SSD1306 my_display(128, 32, &Wire, -1, 400, 100);

void setup() {
my_display.begin(SSD1306_SWITCHCAPVCC, 0, false, true);
my_display.clearDisplay();

my_display.setTextSize(2);
my_display.setCursor(0,0);
my_display.setTextColor(1);
my_display.print("Hello World!");
my_display.display();
}

void loop() {

}

(責任編輯:賴佩萱)

Bird

Author: Bird

在外商圈電子業中闖蕩多年,經歷過 NXP、Sony、Crossmatch 等企業,從事無線通訊、影像系統、手機、液晶面板、半導體、生物辨識等不同領域產品開發。熱愛學習新事物,協助新創團隊解決技術問題。台大農機系、台科大電子所畢業,熱愛賞鳥、演奏管風琴、大提琴、法國號,亦是不折不扣的熱血 maker。

Share This Post On
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x