內容:I2C通訊(wire函數) 實作:Arduino <->Arduino by I2C 簡報:廖崇義 I²C(Inter-Integrated Circuit)是內部整合電路的稱呼 ,是一種串列通訊匯流排,使用多主從架構,由飛利浦公 司在1980年代為了讓主機板、嵌入式系統或手機用以連 接低速週邊裝置而發展。I²C的正確讀法為"I-squared-C“ 使用I²C協定不需要為其專利付費,但製造商仍然需要付 費以獲得I²C從屬裝置位址(IP Adress)。 I²C只使用兩條雙向開放集極(Open Drain)(串列資料 (SDA)及串列時脈(SCL))並利用電阻將電位上拉。 I²C允許相當大的工作電壓範圍,但典型的電壓準位為 +3.3V或+5v。 I²C的參考設計使用一個7位元長度的位址空間但保留了16 個位址,所以在一組匯流排最多可和112個節點通訊。 常見的I²C匯流排依傳輸速率的不同而有不同的模式:標 準模式(100 Kbit/s)、低速模式(10 Kbit/s),但時脈 頻率可被允許下降至零,這代表可以暫停通訊。 而新一代的I²C匯流排可以和更多的節點(支援10位元長 度的位址空間)以更快的速率通訊:快速模式(400 Kbit/s)、高速模式(3.4 Mbit/s)。 雖然最大的節點數目是被位址空間所限制住,但實際上也 會被匯流排上的總電容所限制住,一般而言為400 pF。 數位周邊IC、感測IC(溫濕度計、加速度計、陀螺儀等) 存取低速的數位類比轉換器(DAC)。 存取低速的類比數位轉換器(ADC)。 取得硬體監視及診斷資料,例如CPU的溫度及風扇轉速。 讀取實時時鐘(Real-time clock)。 在系統設備中用來開啟或關閉電源供應。 最常被使用在於微控制器的應用,利用兩根通用的輸入輸出 接腳及軟體的規劃,可以讓微控制器控制一個小型網路。 週邊可以在系統仍然在運作的同時加入或移出匯流排,這代 表對於有熱插拔需求的裝置而言是個理想的匯流排。 像I²C這樣的匯流排之所以流行起來,是因為電腦工程師發現 到對於整合電路設計而言,許多的製造成本源自於封裝尺寸 及接腳數量。 This library allows you to communicate with I2C / TWI devices. On the Arduino boards with the R3 layout (1.0 pinout), the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. The Arduino Due has two I2C / TWI interfaces SDA1 and SCL1 are near to the AREF pin and the additional one is on pins 20 and 21. As a reference the table below shows where TWI pins are located on various Arduino boards. Board I2C / TWI pins Uno, Ethernet A4 (SDA), A5 (SCL) Mega2560 20 (SDA), 21 (SCL) Leonardo 2 (SDA), 3 (SCL) Due 20 (SDA), 21 (SCL), SDA1, SCL1 Functions ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ begin() requestFrom() beginTransmission() endTransmission() write() available() read() onReceive() onRequest() Wire.begin() Wire.begin(address) 說明 參數 Initiate the Wire library and join the I2C bus as a master or slave. This should normally be called only once. address: the 7-bit slave address (optional); if not specified, join the bus as a master. Wire.beginTransmission(address) 說明 Begin a transmission to the I2C slave device with the given address. Subsequently, queue bytes for transmission with the write() function and transmit them by calling endTransmission(). 參數 address: the 7-bit address of the device to transmit to Wire.write(value) 說明 slave 裝置回覆 master 裝置的請求。或是從 master 裝置 向slave 裝 置請求的資料傳送(in-between calls to beginTransmission() and endTransmission()). 參數 value: a value to send as a single byte string: a string to send as a series of bytes data: an array of data to send as bytes length: the number of bytes to transmit Example #include <Wire.h> byte val = 0; void setup() { Wire.begin(); // join i2c bus } void loop() { Wire.beginTransmission(44); // transmit to device #44 (0x2c) // device address is specified in datasheet Wire.write(val); // sends value byte Wire.endTransmission(); // stop transmitting val++; // increment value if(val == 64) // if reached 64th position (max) { val = 0; // start over from lowest value } delay(500); } Wire.requestFrom(address, quantity, stop) 說明 用來讓 master裝置向 sleave 裝置請求 byte 資料 請求的 byte 資料可以用 available() 和 read() 這兩個函數取得 參數 address: 要請求資料之設備端的 7位元位址 quantity: 請求資料的數量 stop: boolean型態. true 將在請求之後送出停止訊息, 釋放匯流排.false 將在 請求之後送出重新開始, 持續保持連結 這樣防止其他的 master 裝置在兩個訊息之間發送請求 這種做法讓 1個 master 裝置, 當控制時送出多個請求。 Wire.available() 說明 Returns可讓 read () 接收的資料數量 這個函數應該在 master 裝置呼叫完 requestFrom() 之後呼叫,或是在 slave 的 onReceive() handler 函數內呼叫 Returns 可以讀取的資料數量 Wire.read() 說明 讀取一個從 salve 裝置傳送到 master 的位元組(在master 呼叫 requestFrom() 之後), 或是master 傳送到 slave 的位元組資料. Example #include <Wire.h> void setup(){ Wire.begin(); //加入到 I2C 匯流排 (未加位址代表是 master ) Serial.begin(9600); //啟動串列埠, 輸出訊息到電腦上 } void loop(){ Wire.requestFrom(2, 6); //從裝置 2 , 請求6個位元組 while(Wire.available()) //slave 送出的也許少於 master 請求的 { char c = Wire.read(); // 用 character 型態接收 位元組資料 Serial.print(c); // 輸出接收到的資料 } delay(500); } Wire.onReceive(handler) 說明 註冊一個自定義函數, 當 slave 裝置收到來自 master 的訊息時呼叫這 個自定義函數 參數 handler: 當接收到資料時會被呼叫的自定義函數; 這個函數必須帶一個 int 的參數 (被傳入來自master 資料的位元組數 )而且沒有返回值 例如: void myHandler(int numBytes) Wire.onRequest(handler) 說明 註冊處理當接收到來自Master Arduino requestFrom(address,count)要求回應資料時會觸發的處理程序 (handler)。 參數 handler: 當接收到資料時會被呼叫的自定義函數; 沒有參數, 也沒有返回值 例如:void myHandler() 1.電路圖 // master_sender.ino // Refer to the "slave_receiver" example for use with this #include <Wire.h> const int SLAVE_ADDRESS = 1; char incomingByte = 0; void setup() { Wire.begin(); // join I2C bus as a Master Serial.begin(9600); Serial.println("Type something to send:"); } void loop() { } void serialEvent() { // read one byte from serial port incomingByte = Serial.read(); // send the received data to slave Wire.beginTransmission(SLAVE_ADDRESS); Wire.write(incomingByte); Wire.endTransmission(); } // slave_receiver.ino // Refer to the "master_sender" example for use with this #include <Wire.h> const int SLAVE_ADDRESS = 1; char incomingByte = 0; void setup() { Wire.begin(SLAVE_ADDRESS); // join I2C bus as a slave with address 1 Wire.onReceive(receiveEvent); // register event Serial.begin(9600); Serial.println("Received data:"); } void loop() { } void receiveEvent(int howMany) { while (Wire.available()) { // receive one byte from Master incomingByte = Wire.read(); Serial.print(incomingByte); } } 1.電路圖 兩塊Arduino PIN13皆接一LED並串一電阻其餘同上題 2.動作說明 兩塊Arduino上的LED會交互閃爍 Master端傳送布林值HIGH、LOW給Slave端,告知Slave端LED轉 態, Slave端使用Wire.onReceive()函數得知Master發送指令,並 以Wire.read()取得資料藉以改變LED狀態 #include <Wire.h> const int SLAVE_ADDRESS = 1; const int ledPin = 13; void setup() { Wire.begin(); // join I2C bus as a Master pinMode(ledPin, OUTPUT); } void loop() { delay(delay_ms); digitalWrite(ledPin, HIGH); // turn on Master's LED delay(delay_ms); digitalWrite(ledPin, LOW); // turn off Master's LED delay(400); slaveLED(HIGH); // turn on Slave's LED delay(delay_ms); slaveLED(LOW); // turn off Slave's LED } void slaveLED(byte value){ Wire.beginTransmission(SLAVE_ADDRESS); Wire.write(value); Wire.endTransmission(); } // Refer to the "blink_master" example for use with this #include <Wire.h> const int SLAVE_ADDRESS = 1; const int ledPin = 13; char incomingByte = 0; void setup() { Wire.begin(SLAVE_ADDRESS); // join I2C bus as a slave with address 1 Wire.onReceive(receiveEvent); // register event pinMode(ledPin, OUTPUT); } void loop() { } void receiveEvent(int howMany) { // receive one byte from Master incomingByte = Wire.read(); // turn on or off LED according to the received data digitalWrite(ledPin, incomingByte); } 1.電路圖 同習題一 2.動作說明 模擬Master向slave設備請求回傳資料 Master 必須用 Wire.requestFrom() 發出 request 給 slave Slave 在收到 Master端requestFrom()的請求後,Slave端的 onRequest() 會由其參數呼叫對應函數,然後由函數把資料傳回 Master #include <Wire.h> const int SLAVE_ADDRESS = 1; char incomingByte = 0; void setup() { Wire.begin(); // join I2C bus as a Master Serial.begin(9600); } void loop() { Wire.requestFrom(SLAVE_ADDRESS, 6); // request 6 bytes from slave while (Wire.available()) // slave may send less than requested { incomingByte = Wire.read(); Serial.print(incomingByte); } delay(1000); } // receive a byte // print the character #include <Wire.h> const int SLAVE_ADDRESS = 1; void setup() { Wire.begin(SLAVE_ADDRESS); // join I2C bus as a slave with address 1 Wire.onRequest(requestEvent); // register event } void loop() { } // function that executes whenever data is requested by maste void requestEvent() { Wire.write("hello\n"); // respond with message of 6 bytes // as expected by master } DS1307 時鐘用的正是 I2C 介面, 我們既可以用 Arduino 讀取 DS1307 的時間資訊: Wire.beginTransmission(DS1307_I2C_ADDRESS); // Open I2C line in write mode Wire.send(0x00); // Set the register pointer to (0x00) Wire.endTransmission(); // End Write Transmission Wire.requestFrom(DS1307_I2C_ADDRESS, 7); // Open the I2C line in send mode second = bcdToDec(Wire.receive() & 0x7f); // Read seven bytes of data minute = bcdToDec(Wire.receive()); hour = bcdToDec(Wire.receive() & 0x3f); dayOfWeek = bcdToDec(Wire.receive()); dayOfMonth = bcdToDec(Wire.receive()); month year = bcdToDec(Wire.receive()); = bcdToDec(Wire.receive()); 而且也可以設定它的時間: Wire.beginTransmission(DS1307_I2C_ADDRESS); // Open I2C line in write mode Wire.send(0x00); // Set the register pointer to (0x00) Wire.send(decToBcd(second)); // Write seven bytes Wire.send(decToBcd(minute)); Wire.send(decToBcd(hour)); Wire.send(decToBcd(dayOfWeek)); Wire.send(decToBcd(dayOfMonth)); Wire.send(decToBcd(month)); Wire.send(decToBcd(year)); Wire.endTransmission(); // End write mode