前言
I²C是一種相當常見且有名的傳輸協議,它被廣泛的應用在各種感測器的通訊界面上。本文將說明如何將ESP32操作在Master以及Slave Mode下,並以SHT3x溫溼度感測器,以及1.3吋OLED顯示器為例,說明ESP32如何分別使用這兩個I²C裝置;最後結合兩者,將測量到的溫溼度數值顯示在OLED上。
什麼是I2C
I²C(唸作I-Square-C),是 Inter-Integrated Circuit 的縮寫,有時也會寫作IIC、I2C 或 I²C bus,於1980年代由Philips公司(現為NXP)所設計出的一種通訊協議,最初的目的是為了讓同一片板子上的元件可以互相通訊。
I²C是一種具有同步訊號的串列通訊協議,支援多個主從設備進行雙向通訊,它允許由一個控制器(Controller)控制多個周邊設備(Peripheral),也可以由多個控制器控制一個周邊設備,擁有靈活的架構是它的優點之一。
I²C的另一個優點是硬體接線簡單,只需要兩條訊號線(Bus)就能與其它裝置通訊,這點與UART相同。不同的是,I²C可以連接多個設備,且不論是主設備或是周邊設備,所有設備都共享兩條相同的訊號線(稱作Bus),分別用來傳輸資料(data)與時脈訊號(clock)。控制器可以依照每個周邊設備的地址選擇要進行通訊的對象,每個周邊設備都有不重複的7位元地址。在嵌入式應用中,許多短距離傳輸的設備上都使用I²C傳輸協議,像是OLED顯示器、數位溫(溼)度感測器、DAC、MPU6050、LCD顯示器等都是常見的周邊裝置。
I²C的基本通訊協定
如何在ESP32使用I2C
I²C通訊時只需使用兩條Bus:SDA(serial data)與SCL(serial clock)。SDA用來傳輸資料,SCL則是用來同步設備通訊的時脈訊號,這兩條Bus都使用open-drain(或是 open-collector)的電路結構驅動。這種電路結構使得I²C可以只用一條資料線就達到雙向傳輸的功能,但同時這也表示設備沒有自主輸出高電壓準位的驅動能力。為了解決這個問題,我們會在open-drain的外部電路連接上拉電阻,讓資料線的狀態維持在高電位,這麼一來設備就可以按照實際情況選擇下拉(低電位)或釋放資料線(高電位)來改變電壓準位。
上拉電阻的數值可以經由計算得出,通常5V的設備會使用4.7kΩ,3V的設備會使用2.4kΩ。不過,大部分的模組在設計時都有加入上拉電阻,使用時不需要再另外連接。本篇所使用的模組都不需要加上拉電阻,只要將對應的接腳連接在一起就可以了。
當我們使用Arduino IDE開發時,預設的SDA接腳是GPIO 21,SDL接腳是GPIO 22。
I2C設備 | ESP32 |
---|---|
VCC | 3.3V/5V |
GND | GND |
SDA | SDA (GPIO 21) |
SCL | SCL (GPIO 22) |
ESP32的I²C介面可以做為主控端(Master Mode)或是做為周邊設備(Slave Mode)使用。多數情況下ESP32都會做為主控端,搭配周邊感測器/模組使用。若想瞭解如何當作周邊設備使用,可以自行參考Arduino IDE中的範例程式 (File>Examples>Wire>WireMaster,WireSlave)。在使用I²C模組時,通常也都要另外安裝函式庫,與I²C協議通訊過程的關聯度較低,所以下面接紹細節部分的內容僅提供給有興趣的讀者參考。若想瞭解如何使用SHT3x溫溼度感測器和OLED,可以直接跳到範例部分。
Master Mode
我們通常會將ESP32當作主控端連接各項感測器或控制其它的周邊設備,這時候ESP32就會操作在Master mode下,可以對周邊設備發起、結束傳輸,或是讀取周邊設備回傳的內容。
使用範例
關於Master Mode具體的使用範例可以在Arduino IDE的File>Examples>Wire>WireMaster中找到
下面是關於Master Mode 傳輸相關API的說明:
- begin
使用預設I²C接腳的話,在setup()中呼叫Wire.begin()即可
void setup() {
Wire.begin();
}
如果要使用其他接腳,可以在begin()前加上setPins(),並將指定的接腳當作參數傳入:
#define I2C_SDA 4
#define I2C_SCL 5
void setup() {
Wire.setPins(I2C_SDA, I2C_SCL);
Wire.begin();
}
- beginTransmission
用來對周邊設備發起傳輸beginTransmission
函式原型:
void beginTransmission(uint16_t address)
使用範例:
Wire.beginTransmission(0x55);
參數說明:
address: 周邊設備的I2C地址
- write
可以將資料寫入緩衝區(buffer),它會回傳內添加到緩衝區的資料大小。write
函式原型:
size_t write(uint8_t);
使用範例:
Wire.write(x);
- endTransmission
使用
將資料從緩衝區發送到周邊設備,並結束傳輸。這個函式會回傳錯誤代碼(error code)endTransmission
函式原型:
uint8_t endTransmission(bool sendStop);
使用範例:
Wire.endTransmission(true);
參數說明:
sendStop: 啟用 (true) 或停用 (false) 停止的訊號。預設參數為true。
error code:
// error code
typedef enum {
I2C_ERROR_OK=0,
I2C_ERROR_DEV,
I2C_ERROR_ACK,
I2C_ERROR_TIMEOUT,
I2C_ERROR_BUS,
I2C_ERROR_BUSY,
I2C_ERROR_MEMORY,
I2C_ERROR_CONTINUE,
I2C_ERROR_NO_BEGIN
} i2c_err_t;
- requestFrom
如果要讀取裝置回傳的內容,可以呼叫
它會回傳讀取到的資料大小(number of bytes)。requestFrom
函式原型:
uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop)
使用範例:
Wire.requestFrom(0x55, 16);
參數說明:
address: 周邊設備的I2C地址
size: 所需的記憶體大小
sendStop: 啟用 (true) 或停用 (false) 停止的訊號。預設參數為true
- readBytes
用來查看資料readBytes
函式原型:
virtual size_t readBytes(char *buffer, size_t length);
使用範例:
uint8_t bytesReceived = Wire.requestFrom(0x55, 16);
uint8_t temp[bytesReceived];
Wire.readBytes(temp, error);
如何掃描I2C設備地址
I²C bus上的周邊設備都會有一組用16進位表示的設備地址,通常地址會標註在模組上。在使用設備前,我們可以先掃描一次,以確認設備實際的地址。
範例1: 使用SHT3x測量溫濕度
這個範例會使用Circus SHT3x在Arduino IDE的序列埠監控視窗顯示溫溼度數值。
本範例使用的是Circus SHT3x 溫溼度感測器模組。相較於另一款常見的溫溼度感測器DHT22,這款感測器(SHT30晶片)的溫度量測範圍更廣,也有更好的測量精度,體積小、價錢也更便宜,不妨可以將其作為取代DHT22的另一項選擇。
以下是SHT3x模組與DHT22的簡易比較表:
參數 | SHT3x模組 | DHT22模組 |
---|---|---|
輸入電壓 (DC) | 3.3V ~ 5V | 3.3V ~ 5.5V |
工作範圍(溫度) | -40°C~125°C | -40°C ~ 80°C |
工作範圍(濕度) | 0%~100% RH | 0% ~ 99.9% RH |
測量精度 (溫度) | ±0.3℃ | ±0.5℃ |
測量精度 (濕度) | ±0.3% | ±2% |
接腳數量 | 4 | 3 |
價格 (參考自iCShop販售價格) | 140元 | 260元 |
材料清單
- ESP32開發板 x1
- Circus SHT3x 溫溼度感測器模組 x1
- micro USB 傳輸線 x1
- 麵包板 x1
- 杜邦線(公-公) x3
連接示意圖
SHT3x | ESP32 |
---|---|
SCL | GPIO 22 |
SDA | GPIO 21 |
VCC | 3.3V |
GND | GND |
安裝函式庫
首先,到Arduino IDE的Library Manager搜尋並安裝Adafruit SHT31函式庫
掃描設備地址
我們可以透過前面的範例程式確認設備地址:
這片模組預設的I2C地址是0x44。透過更改模組上的焊盤連接,也可以將地址改成0x45。
範例程式碼
複製下方的程式碼進行測試
程式碼說明
上面的範例修改自函式庫的範例測試程式。
一開始我們先建立一個 sht3x 物件,由於使用的是預設接腳,因此Adafruit_SHT31()
不需要傳入參數。
Adafruit_SHT31 sht3x = Adafruit_SHT31();
初始化物件時,使用
,並將設備的地址當作參數傳入函式。begin
sht3x.begin(0x44);
完成初始化以後就可以用
和readTemperature
直接讀取目前的溫度和濕度readHumidity
sht3x.readTemperature(); // 讀取目前溫度
sht3x.readHumidity(); // 讀取目前濕度
程式每間隔1秒就會讀取溫溼度數值,並顯示在序列埠監控視窗。
SHT3x系列的感測器內部都有加熱器功能,用意是提高濕度量測準確度,我們可以透過軟體控制加熱器的開關
sht3x.heater(true); // 開啟加熱器
sht3x.heater(false); // 關閉加熱器
也可以用
重置感測器。reset
sht3x.heater(true); // 開啟加熱器
sht3x.heater(false); // 關閉加熱器
執行結果
範例2: 驅動1.3吋OLED
本範例使用Circus 1.3吋OLED螢幕顯示模組,它是一款由SSD1306晶片驅動,解析度128 x 64,顯示顏色為白色,使用I2C通訊協議的OLED螢幕。
這個範例會示範如何在畫面中心顯示circus pi的圖片,並在螢幕最下方顯示1.3” OLED的文字。
材料清單
- ESP32開發板 x1
- Circus 1.3吋OLED螢幕顯示模組 x1
- micro USB 傳輸線 x1
- 麵包板 x1
- 杜邦線(公-公) x3
連接示意圖
連接方式與範例1相同。
安裝函式庫
首先,到Arduino IDE的Library Manager搜尋並安裝U8g2函式庫
圖片轉換
與之前使用的電子紙相同,要在OLED上顯示圖案時我們可以用Image2Lcd這個軟體把圖片轉換成C語言的陣列資料
轉換完成後,就可以將產生的陣列貼到程式中。
範例程式碼
程式碼說明
首先匯入Wire和U8g2的函式庫。我們使用U8g2函式庫來驅動OLED。
#include <U8g2lib.h>
#include <Wire.h>
定義圖片大小為56×56 pixel
#define ImgWIDTH 56
#define ImgHEIGHt 56
接著選擇適合的建構函式。U8g2這套函式庫能支援非常多種類的顯示器與型號,以這個模組為例,要選擇的建構函式為:
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
在setup()函式中,先初始化螢幕
u8g2.begin();
然後設定要顯示的字型
u8g2.setFont(u8g2_font_luRS08_tr); // 設定字型
可以到這裡選擇支援的字型以及字體大小。在這個範例中,我們使用的是8 pixel大小的字型。
使用
設定圖片:drawBMP
u8g2.drawXBMP(32,0, ImgWIDTH, ImgHEIGHt, circus_logo); // 設定圖片
參數分別為圖片左上角的xy位置、圖片的寬度和高度,以及要顯示的圖片陣列。
使用
設定文字:drawStr
u8g2.drawStr(32, 64, "1.3\" OLED"); // 設定文字
參數分別為文字的xy位置,以及要顯示的文字。注意xy位置是從文字的左下方開始算。
最後使
顯示文字和圖片:sendBuffer
u8g2.sendBuffer();
進階範例: 連接多個i2c設備
經過前面的2個例子,我們已經知道要如何驅動溫溼度感測器與OLED顯示模組。接下來我們可以將它們連接在一起,在OLED上顯示目前的溫濕度資訊。
連接示意圖
SHT3x | 1.3” OLED | ESP32 |
---|---|---|
SCL | SCL | GPIO 22 |
SDA | SDA | GPIO 21 |
VCC | VCC | 3.3V |
GND | GND | GND |
圖片轉換
範例中使用的圖片是從Flaticon這個網站上下載的,圖片來源: 濕度 溫度
下載後將圖片匯入Image2Lcd,將圖片大小設為28×28,其他選項都與範例2相同,不用更改。
儲存後,我們一樣將轉換好的陣列放到程式中。
範例程式碼
程式說明
基本上程式都與前兩個範例一樣,只是這個範例的寫法會比sendBuffer
還要節省記憶體空間。print函式用來顯示數字,參數分別是變數名稱,以及小數點位數。
例如:
u8g2.print(t, 2); // 顯示溫度t 以及小數點後兩位的數值。
執行結果
小結
在這篇文章中,我們介紹了如何使用ESP32的I2C,以及掃描I2C設備的方式。也分別用兩個範例說明溫溼度感測器以及OLED模組的使用方式,最後說明如何在同一條I2C Bus上使用多個I2C設備。
下一篇我們會使用1.9吋的段碼電子紙螢幕搭配SHT3x顯示溫溼度數值,比較不同的顯示方式。