轉(zhuǎn)自LarryBank-技術(shù)博文-使用幾個(gè) GPIO 引腳控制大量 OLED 顯示器
https://bitbanksoftware.blogspot.com/2019/01/controlling-lots-of-oled-displays-with.html
原文翻譯:
在我盡可能多地了解“物聯(lián)網(wǎng)”和市場(chǎng)上流行的所有配件/傳感器/顯示器的使命中,我遇到了一些使用多個(gè) OLED 顯示器來(lái)形成儀表板和控制面板的項(xiàng)目。 我沒(méi)有考慮過(guò)這種用法,但考慮到它們的便宜(和小)是有道理的。 這個(gè)想法帶來(lái)的挑戰(zhàn)是,廉價(jià)的業(yè)余愛(ài)好部件要么具有固定的 I2C 地址(通常為 0x3C),要么具有用于選擇備用地址(通常為 0x3D)的單個(gè)跳線。

上圖顯示了一組現(xiàn)成且價(jià)格低廉的 I2C OLED 顯示器。 前 3 個(gè)有一個(gè)地址選擇跳線。 底部 2 沒(méi)有顯示任何用于更改地址的特定跳線,盡管一個(gè)或多個(gè) SMD 電阻器可能會(huì)控制它。 無(wú)論哪種方式,大多數(shù)愛(ài)好者都不準(zhǔn)備使用微小的表面貼裝電阻器。
如果它們都設(shè)置為相同的 I2C 地址,那么為了讓超過(guò) 1 個(gè)顯示器從同一個(gè) MCU 工作,您將需要多個(gè) I2C 總線。 我已經(jīng)在 GPIO 線路上編寫了位爆炸 I2C 的代碼,因此編寫可以使用多條 GPIO 線路管理多條 I2C 總線的代碼似乎是一項(xiàng)相對(duì)容易的任務(wù)。 了解 I2C 協(xié)議的工作原理后,我的腦海中形成了另一個(gè)想法:“來(lái)自同一源的多條 I2C 總線可以共享一條時(shí)鐘線嗎?” 我認(rèn)為這可能的原因是因?yàn)?I2C 啟動(dòng)信號(hào)需要時(shí)鐘和數(shù)據(jù)線都變低。 如果公共時(shí)鐘線變低,是否會(huì)激活您不打算激活的設(shè)備?
我的第一個(gè)實(shí)驗(yàn)是嘗試僅使用 5 條 GPIO 線(4 SDA + 1 SCL)來(lái)控制 4 個(gè) OLED 顯示器(都具有相同的 I2C 地址)。 我選擇 Arduino Pro Micro(又名 Leonardo)作為本次測(cè)試的理想電路板。

正如您在上圖中所看到的,它有效。 4 個(gè)顯示器都共享同一條時(shí)鐘線,并且都可以單獨(dú)尋址,沒(méi)有干擾問(wèn)題。 然后我意識(shí)到我已經(jīng)通過(guò)將一個(gè)顯示器連接到每條 I2C 總線來(lái)設(shè)計(jì)我的 Multi_OLED 庫(kù)。 我對(duì)代碼進(jìn)行了第二次檢查并添加了一個(gè)新參數(shù),以便為每個(gè)顯示器指定一個(gè)地址和總線編號(hào),以便可以控制每條總線的多個(gè)顯示器。

在這張照片中,頂部的 2 個(gè)顯示器設(shè)置為不同的地址并共享同一條總線,而下面的 3 個(gè)顯示器都在自己的 I2C 總線上。 像素操作需要一個(gè)獨(dú)特的后備緩沖區(qū)(1K RAM)。 這在大多數(shù) AVR MCU 上是不可行的,因?yàn)樗鼈兊陌遢d RAM 很少,但其他 MCU(例如 ESP32、Cortex-M)有足夠的 RAM 來(lái)分配多個(gè) 1K 緩沖區(qū)。 對(duì)于這些 MCU,線和像素功能可用。 這是在 Adafruit nRF52840 Feather 上運(yùn)行的 2 個(gè)顯示器的視頻:

我在這里將代碼發(fā)布在 Github 上:
Github 上的 Multi_OLED 庫(kù)
我認(rèn)為將多個(gè) I2C 總線用于 OLED 顯示器以外的東西可能會(huì)有用,所以我單獨(dú)分解了 Multi_BitBang 庫(kù):
Github 上的 Multi_BitBang 庫(kù)
我創(chuàng)建的用于顯示的函數(shù)非常簡(jiǎn)單。 首先初始化您的顯示器:
void Multi_OLEDInit(uint8_t *iBus, uint8_t *iAddr, uint8_t *iType, uint8_t *bFlip, uint8_t *bInvert, uint8_t iCount);
每個(gè)參數(shù)都是一個(gè)列表(一個(gè)用于您要控制的每個(gè)顯示器)
iBus - I2C 總線編號(hào)(0 到您使用 Multi_BitBang 庫(kù)定義的最大總線)
iAddr - I2C 地址(例如 0x3C)
iType - OLED顯示類型(枚舉值如oled_128x32)
bFlip - 指示顯示是否應(yīng)翻轉(zhuǎn) 180 度的布爾值
bInvert - 一個(gè)布爾值,指示顯示器是否應(yīng)啟用反色模式
iCount - 顯示器總數(shù)
我在此處提供了一個(gè)演示如何驅(qū)動(dòng)多個(gè) OLED 顯示器的示例草圖:
Multi_OLED 示例草圖

關(guān)于我添加到 Multi BitBang 庫(kù)以加快速度的代碼的最后一個(gè)注釋。 Arduino 核心庫(kù)包括一種操作 GPIO 引腳的標(biāo)準(zhǔn)方法。 這些函數(shù)(pinMode、digitalRead、digitalWrite)隱藏了許多實(shí)現(xiàn)細(xì)節(jié),使它們能夠在 Arduino 工具集支持的所有 MCU 上以相同的方式工作。 在 AVR MCU(例如 Arduino Uno / aka ATmega328P)上,這些功能非常慢。 它們很慢,因?yàn)樗鼈儠?huì)進(jìn)行表查找、邊界檢查并嘗試將中斷放在合適的位置。 對(duì)于整個(gè)社區(qū)來(lái)說(shuō),這通常無(wú)關(guān)緊要。 閃爍 LED 或讀取按鈕狀態(tài)不需要最高速度,但生成用于通信的數(shù)字信號(hào)卻需要。 使用 Arduino GPIO 函數(shù),Multi_OLED 庫(kù)能夠非常緩慢地更新顯示器。 有一種更快的方法,但它需要直接操作端口寄存器。 我想出了一個(gè)方案,既能保持引腳功能的簡(jiǎn)單性,又能提高直接端口訪問(wèn)的速度。 每個(gè) AVR 芯片都有一個(gè)獨(dú)特的端口-> 引腳映射,可將引腳號(hào)轉(zhuǎn)換為端口和位。 下圖顯示了 ATmega32u4 (Pro Micro) 的引出線。 查看數(shù)字引腳 2,您可以看到它也標(biāo)有 PD1。 這意味著引腳 2 實(shí)際上是端口 D 的位 1。使用我的編號(hào)方案,您可以通過(guò)將其引用為引腳 0xD1 來(lái)訪問(wèn)該引腳。
(圖片來(lái)源 - Sparkfun Electronics)
我的代碼不需要使用表查找,不需要進(jìn)行太多邊界檢查,也不需要禁用中斷。 它確實(shí)允許您使用現(xiàn)有代碼并僅更改傳遞的密碼。 通過(guò)簡(jiǎn)化的引腳編號(hào)方案,Multi_OLED 庫(kù)能夠像 AVR MCU 上的普通 I2C 一樣快速地驅(qū)動(dòng)顯示器。 更快的 MCU(例如 ESP32)不會(huì)因?yàn)槭褂迷家_功能的額外開(kāi)銷而減慢速度。 為了區(qū)分這兩種情況,小于 0xA0 的引腳編號(hào)將調(diào)用原始(慢速)引腳功能,否則快速功能將與端口/位編號(hào)方案一起使用。 我還將這段代碼分離到一個(gè)名為“FastIO”的獨(dú)立庫(kù)中。 這里是:
Github 上的 FastIO 庫(kù)
https://github.com/bitbank2/FastIO