本專欄內容經修訂後,已在豆瓣閱讀集結成書出版:

進階教程5 兩根線實現無限連線 初識 I2C

Arduino UNO擴充套件介面並不多,但它有一項必殺技:I2C(全稱“Inter-Integrated Circuit”,也叫“IIC”)。這個通訊協議已經存在了將近40年,最初由飛利浦半導體(現在的NXP)所定義。它將連線數百個裝置或感測器的複雜電路,簡化為2根資料線(SDA——資料和SCL——時序)。

現在已有成千上萬使用 I2C 協議的裝置或感測器,透過Arduino開發板都能控制它們。常見的如。外部精密時鐘(DS1307)、數字電位器、溫度計、指南針、FM收音模組、外部儲存、I/O擴充套件,液晶顯示屏、數字放大器等等。只需一條匯流排(即兩條資料線)就可以連線所有的裝置,上限是112個。

Arduino UNO 的I2C埠是 A4(SDA)、A5(SCL)(圖23。1):

進階教程5 兩根線實現無限連線 初識 I2C

圖23。1

如果是 Arduino Mega2560,那麼SDA 和 SCL 分別是 20、21,其他開發板請參考官方DataSheet。如果使用 DIY系統 則 SDA、SCL 分別為27腳和28腳。I2C 匯流排接線方式很簡單(圖23。2):

進階教程5 兩根線實現無限連線 初識 I2C

圖23。2

如果只連線一個 I2C 裝置,那麼上拉電阻就不是必須的。如果連線多個裝置,那麼要接兩個 10KΩ 上拉電阻,但也有些裝置會要求 4。7KΩ 上拉電阻。沒關係,按要求連線即可。I2C 匯流排可以達到 1 米左右,但要注意導線寄生電容對資料傳輸的影響。另外,使用 NXP P82B715 等IC,可以將匯流排長度延長至20米以上,這個以後我們會講到。

I2C 裝置分 master 和 slave 兩類定義,Arduino 開發板一般就是 master,匯流排連線的裝置和感測器是 slaves 。問題出現了,總線上裝置那麼多,讀寫資料時怎樣才能區分它們?我們想對A裝置寫入資料,但如何保證不會寫進了B裝置?實際上,每個 I2C 裝置都有一個唯一的地址。利用地址資訊就可以區分它們。

首先,I2C 需要使用庫 wire。h,同時要用到方法 Wire。begin(),要把它放到 void setup() 中。然後,向I2C裝置傳送資料必須具備兩個元素,裝置的唯一地址(16進位制)和至少一個位元組的資料。

Wire。beginTransmission(0x2F); // 裝置地址為0x2F

地址會向 SDA 線傳送,提醒對應的裝置,資料來了:

Wire。write(0); //向匯流排寫入0,這個資料會傳送到上面的地址中

根據地址找到裝置後,向其傳送資料。這期間,這個裝置會張開手臂迎接資料的到來,而其他裝置則會無視,因此一次只能操作一個I2C裝置。

Wire。endTransmission(); //結束通訊

此語句表示本次通訊結束。否則,無法進行下一個裝置的資料傳輸。我們需要從裝置接受資料,同時也知道了有多少資料將會反饋回來。比如,要求某個裝置一次性反饋3個暫存器的資訊,每個資訊需要一個變數來儲存:

Wire。requestFrom(device_address, 3);

這告訴裝置,向 Arduino 反饋3個暫存器的資訊,後面立刻跟上3個指標變數:

*a = Wire。read();

*b = Wire。read();

*c = Wire。read();

獲得裝置的資料反饋後,將指標變數 *a、*b、*c 視作普通變數使用即可。實際上,用普通變數接受資料也沒有問題,但一些操作中,使用指標變數會帶來意想不到的好處,比如利用 sizeof(指標變數型別) 的方法,跳轉到前後一個位元組。

更多 I2C 的細節可以參考 NXP 的官方文件:

http://www。

nxp。com/documents/appli

cation_note/AN10216。pdf

現在利用上面的知識,透過I2C控制 MCP4725(12bits DAC)模組(圖23。3)的電壓輸出。其實,MCP4725是一個能將參考電壓分成 4096 份(12bits)的精密電壓 IC。在 Arduino 上,可以輸出0~5V之間的電壓,每級電壓為 5/4096V :

進階教程5 兩根線實現無限連線 初識 I2C

圖23。3

在 Arduino UNO 上,模組 SDA 接 A4,SCL 接 A5,VCC 是參考電壓,接5V或3。3V都可以,輸出範圍分別為 0~5V 和 0~3。3V,GND 接 GND。

學習操作一個模組時,首先要做的事情是查閱其 DataSheet:

http://

pdf1。alldatasheet。com/d

atasheet-pdf/view/233449/MICROCHIP/MCP4725。html

第19頁有一個表格(FIGURE 6-2: Write Commands for DAC Input Register and EEPROM,圖23。4)很重要:

進階教程5 兩根線實現無限連線 初識 I2C

圖23。4

表格告訴我們,操作這個模組要給出4個位元組:

1st byte 是地址;

2nd byte 為操作位元組。前三位C2=0、C1=1、C0=0 時,只對 DAC 操作,如果C0=1,還會對EEPROM寫入。

3rd byte 是 12bits DAC 的前8bits;

4th byte 前4位為 DAC 總資料的後4位,最後4位沒有定義。

現在寫一個程式,輸出 0~ 3。3V 任意一個電壓值,精度位 3。3/4096 V(程式23。1):

/*

程式23。1

作者:Ardui。Co

效果:MCP4725 輸出 0~3。3V 任意電壓

*/

#include

// I2C 協議必不可少的庫

#define MCP4725 0x62

// MCP4725 的地址,如果不清楚可以掃描一下

unsigned

int

val

byte

buffer

3

];

//設定位元組變數

void

setup

()

{

Wire

begin

();

// I2C 要開始傳輸了

}

void

loop

()

{

buffer

0

=

0b01000000

// 對應表格 2nd byte “操作位元組”

val

=

3000

// 寫入 0 ~ 4095 的範圍

buffer

1

=

val

>>

4

// 將一個整數轉化為二進位制,並向右移位4位,對應 3rd byte

buffer

2

=

val

<<

4

// 將一個整數轉化為二進位制,並向左移位4位,最右邊空出4位了,對應 4th byte

Wire

beginTransmission

MCP4725

);

// 傳入地址,對應 1st byte

Wire

write

buffer

0

]);

// 找到地址後,將操作引數傳遞給 2nd byte

Wire

write

buffer

1

]);

// 將前面8位傳遞給 3rd byte

Wire

write

buffer

2

]);

// 將後面4位傳遞給 4th byte

Wire

endTransmission

();

// 結束傳輸

}

此時測量MCP4725 上的 OUT 和 GND 之間的電壓:

Vout =

VCC * val/4095 = 3.3V * 3000/4095 ≈ 2.418V。

不過,由於儀器的測量誤差和 LDO 的輸出精度,VCC不一定等於3。3V,此時,先測量一下VCC,再做運算。實際測得 VCC = 3。274V,因此 Vout = 3。274V * 3000/4095 = 2。398V

Arduino 是沒有內建 DAC 的,但 DAC 的用途很廣,比如結合第13課的旋轉編碼器,就能製作一個連續可以調的數字電源;也可以利用各種數學函式,輸出任意波形的訊號。