作者: Jack OmniXRI, 2026/01/15

相信大家看到標題就會問「不就把 Adafruit_SSD1306 函式庫裝上就搞定了嗎?」,如果你是使用 UNO R3 的朋友,那麼確實只要三兩下就搞定。但要寫一個讓 Arduino UNO R3 / UNO R4 Minima / UNO R4 WIFI 同時能工作的程式,可就學問多多了,包括硬體接線、函式庫應用、顯示字串組合等。為了記取花費了好幾天才搞定的經驗,也為了避免下次再犯同樣的錯誤,所以趁著還有印象,趕緊寫下筆記。
1. OLED 顯示器及驅動晶片簡介
為了方便 Arduino 顯示漂亮的文字(含中文)及繪製圖形,通常會搭配一組小尺寸的 OLED 顯示器模組。一般這類顯示器都會搭配一個驅動晶片(Driver IC),方便設定連接不同點數、顏色、連接介面的 OLED 或 TFT LCD。
在 Arduino 上常見的串列通訊介面有二線式雙向雙工一對一的 UART、二線式雙向單工一對多的 I2C 及四線式雙向雙工一對多的 SPI,這些都可以用來連接顯示器模組,其通訊速度 SPI > I2C > UART。有時考慮到顯示內容多寡,通訊速度會影響到顯示內容更新頻率,因此會選用不同的介面連接。
而像 0.96吋 單色(或假雙色) 128x64 像素的 OLED 就很適合使用 I2C 來控制及傳輸顯示內容。一個畫面資料量為 128x64 (8192) bit,若一秒要更新30次(30 FPS),則串列傳輸速度只要大於 8192x30 = 245,760 bit/sec (約246KHz),就能完成常見的動畫效果,當然如果考慮 Arduino 工作效能,要減少資料儲存、處理及傳輸量,則 10 FPS 也可產生不錯動畫效果。而 Arduino UNO R3/R4 I2C 標準模式時脈為 100KHz, 快速模式則可高達 400KHz,可輕鬆搞定。
由於顯示器的種類非常多,如果要每一種都設計一個專用的控制晶片就有點不現實,於是有很多廠商設計了通用型顯示控制晶片以利兼容多種需求,包括顯示內容長寬像素、色彩數(1, 8, 16, 24 bit)、旋轉方向(0, 90, 180, 270度)、通訊介面(I2C, SPI, 8 bit)及局部滾動等等。常見用於小尺寸 OLED 的控制晶片有 SSD1306, SSD1315, SH1106 等等,以下簡單秀出幾款 0.96 吋 OLED。

Fig. 1 不同介面及控制晶片的 0.96 吋 OLED 顯示器。(OmniXRI 整理製作, 2026/01/15)
以下就把此次實驗的 OLED (如Fig. 1 右二)的規格進行簡單說明:
- 顯示尺寸: 0.96 吋
- 解 析 度: 128x64 像素
- 色 彩 數: 單色 (1 bit)
- 通訊介面: 四線式 I2C (GND, VCC, SCL, SDA)
(注意:不同廠家模組 GND, VCC 順序可能會顛倒,要注意接線以免燒毀) - 工作電壓: 3.3V ~ 5.0V (本實驗採VCC=3.3V, SCL & SDA = 3.3V 或 5.0V 皆可)
- 驅動晶片: SSD1306
2. Arduino 連接 I2C OLED 方式
本次實驗共使用了三塊 Arduino 開發板,包括 UNO R3, UNO R4 Minia, UNO R4 WIFI。依原廠設計開發板右下的 A4 和左上角的 SDA 是相通的(短路),A5 和 SCL 相通,且預設就是用於 I2C,因此使用哪一組都可以,如 Fig.2 所示。

Fig. 2 Aruino UNO R3/R4 Minima/R4 WIFI 與 I2C OLED 連接方式。(OmniXRI 整理製作, 2026/01/15)
一般 I2C 為了可以併接多組裝置,所以時脈信號(SCL)和資料信號(SDA)通常採用開漏極(Open-Drain)設計,若沒有配上拉(Pull-High)電阻 4.7KΩ 或 10KΩ 則信號無法傳遞。在 UNO R3 I2C 已有內建了一組上拉電阻,所以當 OLED 接上後就可直接使用。但 UNO R4 Minima/WIFI 並沒有,所以在板上留了空點,如 Fig. 2 左上角所示,讓使用者自行焊好上拉電阻後,才能使用 I2C。預設會上拉到 5V,這並不影響 OLED VCC 是接 3.3V 或 5.0V,有時反而可以協助通訊頻率過高時,修整 SDA/SCL 波形(電壓高低變化)不確實問題。由於 SMD 電阻一般使用者不易取得,可以改用常見的 1/4W 或 1/8W 穿孔式電阻替代,如 Fig.2 左下角所示。由於各家 OLED 模組板設計不同,如果你買的 OLED 上已有在 SDA/SCL 接上拉電阻,則可不用手動加入。
3. OLED 顯示函式庫 - Adafruit_SSD1306
本實驗採用的 OLED 顯示器並非 Arduino 標準元件,所以內建函式庫並沒有,需手動安裝。好在 SSD1306 是很常用的驅動晶片,所以已有很多人幫忙寫好函式庫,其中最多人使用莫過於「Adafruit SSD1306」。安裝方式很簡單,開啟 Arduino IDE 後,只要點擊主選單 Sketch - Include Library - Manage Libraries… 即可開啟函式庫管理器,或者直接點擊左側工具快捷鍵亦可開啟,如 Fig. 3 所示。
接著在搜尋欄輸入「SSD1306」就會出現很多相關的函式庫,選擇 「Adafruit SSD1306 by Adafruit」 按下【Install】即可開始安裝。安裝過程中會提示要安裝「Adafruit GFX」,請一起安裝,後續繪圖相關函式會引用到。
完成安裝後點擊 IDE 主選單 File - Examples - Adafruit_SSD1306 - ssd1306_128x64_i2c 即可開啟範例程式。由於這個範例支援多種開發板、多種解析度及不同的 I2C 位址,所以開啟後,要將第35列位址修改為 0x3C 「#define SCREEN_ADDRESS 0x3C」,接著按下快捷功能鍵【上傳】(含編譯)就可看到 OLED 上顯示各種測試圖案及文字。

Fig. 3 安裝 Adafruit SSD1306 函式庫及啟動測試程式。(OmniXRI 整理製作, 2026/01/15)
測試完 UNO R3 本來想說把 UNO R4 Minima/WIFI 的上拉電阻加上,就能愉快地收工,沒想到事情不像憨人想得這麼簡單,程式上傳完,只見上面幾列(ROW)勉強看得到內容,剩下的一片雪花(白點),心中百思不得其解,不禁反覆碎碎唸道「怎麼會這樣??」。
經詢問各家 AI 後,得知最大可能是 I2C 資料送太快太多造成塞車,後面的資料蓋到前面造成,或者是信號0/1不夠清楚,數值被誤判。於是把各家 AI 提供的建議,都試了一輪,並交叉、混合試了多種排列組合都無解,如下所示。
- 換一家 OLED 模組。(手邊有二家模組試過都不行,但不想再買新的)
- 上拉電阻從 10KΩ 改到 4.7KΩ。
- 上拉電壓從 3.3V 改到 5.0V。
- I2C SCL 時脈頻率從 400K 降到 100K 甚至 50K。
- 宣告 Adafruit_SSD1306 oled(…)後,在 setup() 函式中宣告 oled.begin() 後加上 delay(500) 甚至到 delay(2000) 讓 oled 初始化能更穩定。
- 在每個 oled.display() 後加上 delay(2000) 讓 I2C 緩衝可以消化完。
4. OLED 顯示函式庫 ─ U8G2
正想著要放棄時,一條 AI 建議給出了希望,就是不要使用「Adafruit SSD1306」函式庫,改用「U8G2」函式庫,它對記憶體的管理比較理想,於是繼續實驗。
仿照 Fig. 3 作法,安裝「u8g2 by oliver」。完成安裝後點擊 IDE 主選單 File - Examples - U8g2 - u8x8 ─ HelloWorld 即可開啟範例程式。由於這個範例支援多種開發板、多種解析度、不同介面,甚至有實體和軟體 I2C 的支援,所以開啟後,預設會註解掉所有驅動晶片的宣告,須手動去除指定項目的註解符號(//)才能正常編譯和執行範例。注意:一次只能去除其中一列的註解符號。
第56列為硬體 I2C,會直接使用原本板上指定的 SCL(A4), SDA(A5),無使用 RESET,工作時脈預設 400KHz。
第58列為軟體 I2C,即可指定任意腳位作為 SCL(clock)和SDA(data),板上左上角 SCL 和右下角 A4 同為 D19, SDA 和 A5 同為 D18,為統一硬體接線可直接設為 19, 18。不過使用軟體 I2C會變得很慢,且在傳送及接收資料時會佔用 CPU,無法處理其它工作。
原則上使用硬體 I2C 即可。接著按下快捷功能鍵【上傳】(含編譯)就可看到 OLED 上顯示二列測試文字,第0列為反白的數字,第1列為「Hello World!」。但是要注意,如 Fig. 4 所示,這裡繪製字串 u8x8.drawString() 的y軸座標表示方式和 Adafruit_SSD1306 的 setCursor() 加上 println() 文字座標表示方式不同,這裡是採文字高度的倍數而不是像素的數量。若 u8x8.drawString() 座標設為(0,1),字高8個像素,則第1列字串顯示的真正座標相當於 Adafruit_SSD1306 setCursor(0,8)。

Fig. 4 不同繪圖函式庫顯示字串的座標表示方式。(OmniXRI 整理製作, 2026/01/15)
5. 字串組合顯示限制
本以為終於可以讓 UNO R3/R4 都能正常使用 U8g2 函式庫加上硬體 I2C 推動 SSD1306 的 OLED 了,於是寫了一個讀取 ADC0 的數值,再轉換成二位小數點的電壓值顯示在 OLED 上的範例。顯示內容包括一個抬頭(Title),再繪製一條分隔線,最後顯示電壓值。
但奇怪的事又發生了,在 UNO R4 Minima/WIFI 上正常顯示,在 UNO R3 卻只顯示了前兩項而第三項卻消失了,於是加上一行 Serial.println(待顯示字串) 來檢查,結果竟是空白字串。於是又開始向各家 AI 求解,終於找到問題,是因 UNO R3 對字串的組合方式有限制,導致結合不出來就直接給空白字串,但又不會造成編譯失敗。
整理後得到四種方式,前三種只適用 UNO R4 Minima/WIFI,第四種則 UNO R3/R4 都可支援,簡單說明如下:
- 宣告成 String 型別,將多個字串直接相加後,再以setCursor()和print()顯示。
- 宣告成 String 型別,將多個字串直接相加後,再以drawStr()顯示。但 String 需透過 c_str() 轉換成 C 語言格式字串。
- 宣告字元陣列空間,配合 sprintf() 產生 C 語言格式字串,再交由 drawStr() 顯示字串。
- 由於 UNO R3 使用 sprintf() 時不能使用 %.2f 來獲取電壓值,因此須宣告二組字元陣列空間來解決,一組負責儲存 dtostrf() 浮點數轉字串結果,另一組負責儲存 sprintf() 合併所有要輸出的字串,最後就能交給 drawStr() 來顯示了。
6. 完整範例程式
完整範例程式如下所示。
小結
「使用 Arduino 點亮 SSD1306 OLED 有這麼難嗎?」,以往只使用 UNO R3 時一點都不難,但加上 UNO R4 後就變得更複雜了,絕不是一句「不就把 Adafruit_SSD1306 函式庫裝上就搞定了嗎?」。此次費盡千辛萬苦,終於能讓一種程式在三種硬體上都順利執行,不知道是否我用錯方法?還是真的要注意的事項就是這麼多?還請大家多多指教!最後分享試錯經驗給大家參考,希望大家不要再踩坑。
參考文獻
- Arduino UNO R3 Document
https://docs.arduino.cc/hardware/uno-rev3/ - Arduino UNO R4 Minima Document
https://docs.arduino.cc/hardware/uno-r4-minima/ - Arduino UNO R4 WIFI Document
https://docs.arduino.cc/hardware/uno-r4-wifi/ - I2C OLED Module with SSD1306
https://shop.playrobot.com/products/096-inch-oled-12864-iic-1 - SSD1306 Datasheet
https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
沒有留言:
張貼留言