Introduction
A good way of adding complexity of features to your projects without adding complexity of wiring, is to make use of the Inter-integrated circuit (I2C) protocol. The I2C protocol is supported on all Arduino boards. It allows you to connect several peripheral devices, such as sensors, displays, motor drivers, and so on, with only a few wires. Giving you lots of flexibility and speeding up your prototyping, without an abundancy of wires. Keep reading to learn about how it works, how it is implemented into different standards, as well as how to use the Wire Library to build your own I2C devices.
Wire Library
The Wire library is what Arduino uses to communicate with I2C devices. It is included in all board packages, so you don’t need to install it manually in order to use it.
To see the full API for the Wire library, visit its documentation page.
-
– Initialise the I2C bus
begin()
-
– Close the I2C bus
end()
-
– Request bytes from a peripheral device
requestFrom()
-
– Begins queueing up a transmission
beginTransmission()
-
– Transmit the bytes that have been queued and end the transmission
endTransmission()
-
– Writes data from peripheral to controller or vice versa
write()
-
– returns the number of bytes available for retrieval
available()
-
– Reads a byte that was transmitted from a peripheral to a controller.
read()
-
– Modify the clock frequency
setClock()
-
– Register a function to be called when a peripheral receives a transmission
onReceive()
-
– Register a function to be called when a controller requests data
onRequest()
-
– Sets the timeout for transmissions in controller mode
setWireTimeout()
-
– Clears the timeout flag
clearWireTimeoutFlag()
-
– Checks whether a timeout has occurred since the last time the flag was cleared.
getWireTimeoutFlag()
Derived libraries
When you buy basically any breakout module that makes use of the I2C protocol, they will come with some library that helps you use the sensor. This library is more often than not built on top of the Wire library, and uses it under the hood. Adding functionality in order to make, for example, reading temperature easier.
An example of this is if you want to use Adafruits MCP9808 sensor module, you download the Adafruit_MCP9808 Library from the IDEs library manager, which enables you to use functions such as
in order to read the sensors temperature data by requesting from the right address, and read the information returned with just a single line instead of writing the Wire code yourself.
tempsensor.readTempC()
To learn how to install libraries, check out our guide to installing libraries.
Examples
The remainder of this article is a collection of examples that can get you off the ground with I2C.
Controller Reader
In some situations, it can be helpful to set up two (or more!) Arduino boards to share information with each other. In this example, two boards are programmed to communicate with one another in a Controller Reader/Peripheral Sender configuration via the I2C synchronous serial protocol. Several functions of Arduino’s Wire Library are used to accomplish this. Arduino 1, the Controller, is programmed to request, and then read, 6 bytes of data sent from the uniquely addressed Peripheral Arduino. Once that message is received, it can then be viewed in the Arduino Software (IDE) serial monitor window.
Controller Reader Sketch
1// Wire Controller Reader2// by Nicholas Zambetti [http://www.zambetti.com](http://www.zambetti.com)34// Demonstrates use of the Wire library5// Reads data from an I2C/TWI peripheral device6// Refer to the “Wire Peripheral Sender” example for use with this78// Created 29 March 2006910// This example code is in the public domain.111213#include
1415void setup() {16 Wire.begin(); // join i2c bus (address optional for master)17 Serial.begin(9600); // start serial for output18}1920void loop() {21 Wire.requestFrom(8, 6); // request 6 bytes from peripheral device #82223 while (Wire.available()) { // peripheral may send less than requested24 char c = Wire.read(); // receive a byte as character25 Serial.print(c); // print the character26 }2728 delay(500);29}
Peripheral Sender Sketch
1// Wire Peripheral Sender2// by Nicholas Zambetti [http://www.zambetti.com](http://www.zambetti.com)34// Demonstrates use of the Wire library5// Sends data as an I2C/TWI peripheral device6// Refer to the “Wire Master Reader” example for use with this78// Created 29 March 2006910// This example code is in the public domain.111213#include
1415void setup() {16 Wire.begin(8); // join i2c bus with address #817 Wire.onRequest(requestEvent); // register event18}1920void loop() {21 delay(100);22}2324// function that executes whenever data is requested by master25// this function is registered as an event, see setup()26void requestEvent() {27 Wire.write(“hello “); // respond with message of 6 bytes28 // as expected by master29}
Controller Writer
In some situations, it can be helpful to set up two (or more!) Arduino boards to share information with each other. In this example, two boards are programmed to communicate with one another in a Controller Writer/Peripheral Receiver configuration via the I2C synchronous serial protocol. Several functions of Arduino’s Wire Library are used to accomplish this. Arduino 1, the Controller, is programmed to send 6 bytes of data every half second to a uniquely addressed Peripheral. Once that message is received, it can then be viewed in the Peripheral board’s serial monitor window opened on the USB connected computer running the Arduino Software (IDE).
Controller Writer Sketch
1// Wire Master Writer2// by Nicholas Zambetti [http://www.zambetti.com](http://www.zambetti.com)34// Demonstrates use of the Wire library5// Writes data to an I2C/TWI Peripheral device6// Refer to the “Wire Peripheral Receiver” example for use with this78// Created 29 March 2006910// This example code is in the public domain.111213#include
1415void setup()16{17 Wire.begin(); // join i2c bus (address optional for master)18}1920byte x = 0;2122void loop()23{24 Wire.beginTransmission(4); // transmit to device #425 Wire.write(“x is “); // sends five bytes26 Wire.write(x); // sends one byte27 Wire.endTransmission(); // stop transmitting2829 x++;30 delay(500);31}
Peripheral Receiver Sketch
1// Wire Peripheral Receiver2// by Nicholas Zambetti [http://www.zambetti.com](http://www.zambetti.com)34// Demonstrates use of the Wire library5// Receives data as an I2C/TWI Peripheral device6// Refer to the “Wire Master Writer” example for use with this78// Created 29 March 2006910// This example code is in the public domain.111213#include
1415void setup()16{17 Wire.begin(4); // join i2c bus with address #418 Wire.onReceive(receiveEvent); // register event19 Serial.begin(9600); // start serial for output20}2122void loop()23{24 delay(100);25}2627// function that executes whenever data is received from master28// this function is registered as an event, see setup()29void receiveEvent(int howMany)30{31 while(1 < Wire.available()) // loop through all but the last32 {33 char c = Wire.read(); // receive byte as a character34 Serial.print(c); // print the character35 }36 int x = Wire.read(); // receive byte as an integer37 Serial.println(x); // print the integer38}
Accelerometer
This code lets you read accelerometer data from a Grove 6-Axis Accelerometer module using the seeed arduino LSM6DS3 library.
1#include “LSM6DS3.h”2#include “Wire.h”34//Create instance of Accelerometer class5LSM6DS3 accelerometer(I2C_MODE, 0x6A);67void setup() {8 // put your setup code here, to run once:9 Serial.begin(9600);10 while (!Serial);1112 if (accelerometer.begin() != 0) {13 Serial.println(“LSM6DS3 not found, check your wiring.”);14 } else {15 Serial.println(“LSM6DS3 found!”);16 }17}1819void loop() {20 //Gyroscope21 Serial.print(“\nGyroscope:\n”);22 Serial.print(” X1 = “);23 Serial.println(accelerometer.readFloatGyroX(), 4);24 Serial.print(” Y1 = “);25 Serial.println(accelerometer.readFloatGyroY(), 4);26 Serial.print(” Z1 = “);27 Serial.println(accelerometer.readFloatGyroZ(), 4);2829 //Accelerometer30 Serial.print(“\nAccelerometer:\n”);31 Serial.print(” X1 = “);32 Serial.println(accelerometer.readFloatAccelX(), 4);33 Serial.print(” Y1 = “);34 Serial.println(accelerometer.readFloatAccelY(), 4);35 Serial.print(” Z1 = “);36 Serial.println(accelerometer.readFloatAccelZ(), 4);3738 delay(1000);39}
I2C BMP280
This code example lets you read the temperature over I2C from a BMP280 breakout module from Adafruit:
1#include
2#include
34//Create an instance of the BMP280 sensor5Adafruit_BMP280 bmp;67void setup() {8 Serial.begin(9600);910 // Start the sensor, and verify that it was found11 if (!bmp.begin()) {12 Serial.println(“Sensor not found”);13 while (1){}14 }1516}1718void loop() {19 // Read the values20 float temperature = bmp.readTemperature();2122 // Print to the Serial Monitor23 Serial.print(“Temperature: “);24 Serial.print(temperature);25 Serial.println(” C”);2627 Serial.println();28 delay(2000);29}
I2C OLED
1#include
2#include
3#include
45Adafruit_SSD1306 display(128, 64, &Wire, -1);67// The Arduino Logo in a bitmap format8const uint8_t arduinoLogo[] = {9 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,10 0x00, 0x00, 0x00, 0x00, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x30, 0x00, 0x00,11 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x70, 0x00, 0x00,12 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x07, 0xff, 0xff, 0x80, 0x50, 0x00, 0x00,13 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xe0, 0x40, 0x00, 0x00,14 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xf0, 0x20, 0x00, 0x00,15 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00,16 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00,17 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,18 0x00, 0x00, 0x01, 0xff, 0xfc, 0x0f, 0xff, 0xe0, 0x07, 0xff, 0xf0, 0x3f, 0xff, 0x80, 0x00, 0x00,19 0x00, 0x00, 0x01, 0xff, 0xc0, 0x00, 0xff, 0xf0, 0x0f, 0xff, 0x00, 0x07, 0xff, 0x80, 0x00, 0x00,20 0x00, 0x00, 0x03, 0xff, 0x80, 0x00, 0x7f, 0xf8, 0x1f, 0xfc, 0x00, 0x01, 0xff, 0xc0, 0x00, 0x00,21 0x00, 0x00, 0x07, 0xfe, 0x00, 0x00, 0x1f, 0xfc, 0x3f, 0xf8, 0x00, 0x00, 0x7f, 0xe0, 0x00, 0x00,22 0x00, 0x00, 0x07, 0xfc, 0x00, 0x00, 0x0f, 0xfe, 0x7f, 0xf0, 0x00, 0x00, 0x3f, 0xe0, 0x00, 0x00,23 0x00, 0x00, 0x0f, 0xf8, 0x00, 0x00, 0x07, 0xff, 0x7f, 0xe0, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00,24 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x03, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00,25 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x01, 0xff, 0xff, 0x80, 0x0f, 0x00, 0x0f, 0xf8, 0x00, 0x00,26 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x0f, 0x80, 0x07, 0xf8, 0x00, 0x00,27 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x0f, 0x80, 0x07, 0xf8, 0x00, 0x00,28 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x0f, 0x80, 0x07, 0xf8, 0x00, 0x00,29 0x00, 0x00, 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x0f, 0x80, 0x03, 0xf8, 0x00, 0x00,30 0x00, 0x00, 0x1f, 0xc0, 0x1f, 0xff, 0x00, 0x1f, 0xf8, 0x00, 0xff, 0xf8, 0x03, 0xfc, 0x00, 0x00,31 0x00, 0x00, 0x1f, 0xc0, 0x3f, 0xff, 0x00, 0x1f, 0xf8, 0x00, 0xff, 0xf8, 0x03, 0xfc, 0x00, 0x00,32 0x00, 0x00, 0x1f, 0xc0, 0x3f, 0xff, 0x00, 0x1f, 0xf0, 0x00, 0xff, 0xf8, 0x03, 0xfc, 0x00, 0x00,33 0x00, 0x00, 0x1f, 0xc0, 0x1f, 0xff, 0x00, 0x1f, 0xf8, 0x00, 0xff, 0xf8, 0x03, 0xfc, 0x00, 0x00,34 0x00, 0x00, 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x0f, 0x80, 0x03, 0xf8, 0x00, 0x00,35 0x00, 0x00, 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x0f, 0x80, 0x07, 0xf8, 0x00, 0x00,36 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x0f, 0x80, 0x07, 0xf8, 0x00, 0x00,37 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x0f, 0x80, 0x07, 0xf8, 0x00, 0x00,38 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x01, 0xff, 0xff, 0x80, 0x0f, 0x00, 0x0f, 0xf8, 0x00, 0x00,39 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x03, 0xff, 0xff, 0x80, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00,40 0x00, 0x00, 0x0f, 0xf8, 0x00, 0x00, 0x07, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00,41 0x00, 0x00, 0x07, 0xfc, 0x00, 0x00, 0x0f, 0xfe, 0x7f, 0xf0, 0x00, 0x00, 0x3f, 0xe0, 0x00, 0x00,42 0x00, 0x00, 0x07, 0xfe, 0x00, 0x00, 0x1f, 0xfc, 0x3f, 0xf8, 0x00, 0x00, 0x7f, 0xe0, 0x00, 0x00,43 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x7f, 0xf8, 0x1f, 0xfc, 0x00, 0x00, 0xff, 0xc0, 0x00, 0x00,44 0x00, 0x00, 0x01, 0xff, 0xc0, 0x01, 0xff, 0xf0, 0x0f, 0xff, 0x00, 0x03, 0xff, 0x80, 0x00, 0x00,45 0x00, 0x00, 0x01, 0xff, 0xf8, 0x07, 0xff, 0xf0, 0x07, 0xff, 0xe0, 0x1f, 0xff, 0x80, 0x00, 0x00,46 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,47 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00,48 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00,49 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00,50 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00,51 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x07, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00,52 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x80, 0x00, 0x00, 0x01, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00,53 0x00, 0x00, 0x00, 0x00, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x00,54 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,55 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,56 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,57 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,58 0x00, 0x00, 0x00, 0x0c, 0x07, 0x80, 0xf0, 0x04, 0x18, 0xff, 0x88, 0x18, 0x1c, 0x00, 0x00, 0x00,59 0x00, 0x00, 0x00, 0x1e, 0x0f, 0xf8, 0xff, 0x1e, 0x1c, 0xff, 0x9e, 0x1c, 0x7f, 0x00, 0x00, 0x00,60 0x00, 0x00, 0x00, 0x1f, 0x0f, 0xfc, 0xff, 0x9e, 0x1c, 0xff, 0x9f, 0x1c, 0xff, 0x80, 0x00, 0x00,61 0x00, 0x00, 0x00, 0x3f, 0x0e, 0x3c, 0xe3, 0xce, 0x1c, 0x1c, 0x1f, 0x1d, 0xe3, 0x80, 0x00, 0x00,62 0x00, 0x00, 0x00, 0x3f, 0x0e, 0x1c, 0xe1, 0xce, 0x1c, 0x1c, 0x1f, 0x99, 0xc3, 0x80, 0x00, 0x00,63 0x00, 0x00, 0x00, 0x33, 0x8e, 0x1c, 0xe1, 0xce, 0x1c, 0x1c, 0x1f, 0x99, 0xc3, 0xc0, 0x00, 0x00,64 0x00, 0x00, 0x00, 0x73, 0x8f, 0x3c, 0xe1, 0xce, 0x1c, 0x1c, 0x1d, 0xd9, 0xc3, 0xc0, 0x00, 0x00,65 0x00, 0x00, 0x00, 0x73, 0x8f, 0xf8, 0xe1, 0xce, 0x1c, 0x1c, 0x1d, 0xf9, 0xc3, 0xc0, 0x00, 0x00,66 0x00, 0x00, 0x00, 0x7f, 0x8f, 0xf0, 0xe1, 0xce, 0x1c, 0x1c, 0x1c, 0xf9, 0xc3, 0x80, 0x00, 0x00,67 0x00, 0x00, 0x00, 0xff, 0xce, 0x70, 0xe3, 0xce, 0x1c, 0x1c, 0x1c, 0xf9, 0xc3, 0x80, 0x00, 0x00,68 0x00, 0x00, 0x00, 0xff, 0xce, 0x78, 0xe7, 0x8e, 0x3c, 0x3c, 0x1c, 0x7d, 0xe7, 0x80, 0x00, 0x00,69 0x00, 0x00, 0x00, 0xe1, 0xce, 0x3c, 0xff, 0x8f, 0xf8, 0xff, 0x9c, 0x7c, 0xff, 0x00, 0x00, 0x00,70 0x00, 0x00, 0x01, 0xe1, 0xee, 0x3c, 0xff, 0x07, 0xf0, 0xff, 0x9c, 0x3c, 0x7e, 0x00, 0x00, 0x00,71 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,72 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,73};7475void setup() {76 Serial.begin(9600);7778 if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {79 Serial.println(F(“SSD1306 not found”));80 while(1){}81 }8283 display.clearDisplay();8485 display.drawBitmap(0, 0, arduinoLogo, 128, 64, SSD1306_WHITE);86 display.display();8788}8990void loop() {91}
Suggested changes
The content on docs.arduino.cc is facilitated through a public GitHub repository. You can read more on how to contribute in the contribution policy.
License
The Arduino documentation is licensed under the Creative Commons Attribution-Share Alike 4.0 license.
I2C Wiring
Below you’ll find a couple ways to wire I2C breakout modules. Which way is best depends on each module, and your needs.
Breakout Boards
Some brekout board modules let you wire them directly, with bare wires on a breadboard. To connect a module like this to your Arduino board, connect it as follows:
- VCC* – 5V/3V3 pin (depending on breakout module)
- GND* – GND
- SDA – SDA
- SCL – SCL
*Pin name might vary depending on what module, VCC might be named “VIN”, “+”, etc. GND might be named “-“.
Here’s an example of how you might connect a sensor to an UNO R4 WiFi:
Qwiic & STEMMA QT
When delving into the market of breakout modules and sensors, you’ll find that there are entire ecosystems, where standards are built around the I2C protocol. Examples of such standards are Qwiic, developed by Sparkfun, and STEMMA QT, developed by Adafruit. Both Qwiic and STEMMA QT use a 4-pin JST SH connector for I2C devices, making it easier for third parties to design hardware with vast compatibility. By having a standardized connector, you’ll know that if you see the word Qwiic or STEMMA QT in association with an item, that it will work together with an Arduino board with a Qwiic or STEMMA QT connector, such as the UNO R4 WiFi.
Both Qwiic and STEMMA QT bundle together wires for power, ground, as well as the SDA and SCL wires for I2C, making it a complete kit, one cable that bundles everything together.
But what’s the difference between the two?
Both Qwiic and STEMMA QT use I2C, and even when inspecting modules using the two standards up close, it may be difficult to tell what makes them unique from each other. But there is a difference! And it has some implications on how and for what you may use them.
Qwiic has level shifting and voltage regulation on the controller (but not on the peripherals). What this means is that Qwiic is 3.3 V logic only. This makes it easier to use, as for the end user, there is one less thing that can go wrong when designing and assembling your circuit.
STEMMA QT, on the other hand, doesn’t have this. This lets you use both 3.3 V and 5 V logic for modules. This also means that there is one more thing you may need to consider when creating your circuit, but it also grants some more flexibility in power and logic requirements.
The pin order for STEMMA QT is designed to match the pin order for Qwiic, enabling cross-compatibility between the two standards.
Grove
Grove is another connector standard, this one developed by seeed studio. You can find a plethora of modules with a Grove connector, however only some of them use I2C. There are no Arduino boards that have a built in Grove connector, however you can use products such as the MKR connector carrier, Nano Grove Shield, or the Base Shield from the Arduino Sensor Kit to connect Grove sensors to your Arduino board.
Arduino – Giao tiếp I2C
Ngoài chuẩn truyền thông thông nối tiếp Serial, chúng ta còn có một chuẩn giao tiếp khác giữa các vi điều khiển, IC đó là chuẩn I2C.
Ở chương này, chúng ta sẽ tìm hiểu các nội dung sau:
-
Các khái niệm liên quan tới giao tiếp I2C như: mô hình master/slave, hoạt động cơ bản – truyền, nhận bit của giao thức I2C.
-
Cách xác định địa chỉ của thiết bị trong giao tiếp I2C bằng chương trình, xác định địa chỉ với nhiều thiết bị khác nhau, những vấn đề xảy ra với nhiều thiết bị giống nhau trong truyền, nhận dữ liệu I2C.
-
Sử dụng giao tiếp I2C đơn giản với LCD và OLED.
-
Giao tiếp giữa 2 board Arduino Uno với nhau thông qua chuẩn I2C.
Mô hình Master/slave
Master/slave là một mô hình giao tiếp mà một thiết bị có thể điều khiển ít nhất một thiết bị khác. Mô hình master/slave được dùng để chỉ các mô hình giao tiếp giữa các thiết bị điện tử, cơ khí và máy tính.
Trong mô hình master/slave, một thiết bị được chọn làm master (thiết bị chủ điều khiển các thiết bị khác), các thiết bị còn lại làm vai trò slave (là các thiết bị chịu sự điều khiển của master).
Ví dụ, trong một mô hình ta có một vi điều khiển muốn điều khiển, hoặc đọc dữ liệu từ các cảm biến, relay,… thì vi điều khiển đóng vai trò là một master và các cảm biến, relay sẽ đóng vai trò là slave.
Giao tiếp I2C
Giới thiệu
I2C là một chuẩn giao tiếp theo mô hình master/slave được phát triển vào năm 1982 bởi hãng Philips cho việc giao tiếp ngoại vi giữa các vi điều khiển, IC trong khoảng cách ngắn. Chuẩn giao tiếp I2C được sử dụng rất phổ biến trong các thiết bị điện tử bởi đặc tính dễ thực hiện với giao tiếp giữa một hoặc nhiều thiết bị làm master với một hoặc hoặc nhiều thiết bị làm slave. Các hãng sản xuất IC ngày nay đều hỗ trợ giao tiếp I2C trên các IC có ứng dụng cần giao tiếp với các IC hay các vi điều khiển khác.
Giao tiếp I2C chỉ sử dụng 2 dây cho việc giao tiếp giữa 128 thiết bị với việc định địa chỉ 7 bit và 1024 thiết bị với việc định địa chỉ 10 bit. Khi đó, mỗi thiết bị trong mô hình master/slave sẽ có một địa chỉ cố định và duy nhất và master sẽ lựa chọn thiết bị nào cần giao tiếp.
Hoạt động
I2C sử dụng 2 dây giao tiếp là SCL và SDA. Dây SCL (viết tắt của Serial Clock Data) là dây truyền xung clock phát từ thiết bị master đồng bộ với việc truyền dữ liệu. Dây SDA (Serial Data) là dây truyền dữ liệu.
Mạch vật lý I2C là mạch cực thu hở, do đó để mạng I2C có thể hoạt động được, cần tối thiểu 2 cặp điện trở pull-up như trên hình, thông thường các giá trị điện trở 4k7 hoặc 1k2 được sử dụng, tùy thuộc vào tốc độ truyền và khoảng cách truyền.
Truyền nhận bit trong I2C
Các dữ liệu được truyền trong I2C dạng các chuỗi 8 bit. Sau khi bit Start đầu tiên được truyền, 8 bit tiếp theo chứa thông tin về địa chỉ của thiết bị sẽ được truyền tiếp. Sau đó, một bit ACK sẽ được truyền để xác nhận việc truyền bit thành công. Sau khi truyền bit ACK, 8 bit tiếp theo chứa thông tin địa chỉ thanh ghi nội của thiết bị slave sẽ được truyền. Sau khi hoàn tất việc định địa chỉ với 8 bit này, một bit ACK nữa sẽ được truyền để xác nhận. Sau đó, 8 bit Data sẽ được truyền tiếp theo. Cuối cùng, quá trình truyền kết thúc với 1 bit ACK và 1 bit Stop xác nhận việc truyền dữ liệu kết thúc.
Để hiểu rõ hơn về quá trình truyền nhận dữ liệu và bit trong I2C, tham khảo: Understanding the I2C Bus, How I2C Communication Works and How To Use It with Arduino.
Sử dụng giao thức I2C
Với các vi điều khiển, IC, cảm biến,… có hỗ trợ chuẩn giao tiếp I2C thì sẽ có 2 chân có tên SCL và SDA, tương ứng với 2 dây SCL, SDA cho giao tiếp I2C. Với board Arduino Uno, chân A4 là chân SDA và chân A5 là chân SCL, 2 chân A4, A5 này cũng được nối tới 2 chân SDA, SCL tương ứng ở header sử dụng với OLED của board Arduino Uno. Với các IC và vi điều khiển được sản xuất ngày nay đều có hỗ trợ điện trở nội kéo lên do đó trong việc kết nối dây không cần thêm điện trở nội kéo lên. Với các module hỗ trợ I2C chỉ có 1 tính năng như OLED SSD1306, LCD 1602 để hiển thị, Cảm biến AM2315 để đọc nhiệt độ, độ ẩm của môi trường,… thường hoạt động ở chế độ slave và có 4 chân: SDA, SCL, GND và VCC.
Viết chương trình cho I2C
Chương trình cho giao tiếp I2C giữa cảm biến và vi điều khiển thường cần thư viện thích hợp cho từng loại vi điều khiển. Các cảm biến, thiết bị,… sẽ đọc dữ liệu từ môi trường và gởi về cho vi điều khiển cũng như vi điều khiển sẽ điều khiển các thiết bị thông qua giao tiếp I2C. Nhiều loại cảm biến, module cần dùng thêm thư viện riêng cho việc đọc, hiển thị dữ liệu,…
Một số chương trình có yêu cầu phải khai báo địa chỉ của thiết bị slave trong giao tiếp I2C thì việc đọc, hiển thị dữ liệu mới thực hiện được. Khi đó, nảy sinh vấn đề phải xác định được địa chỉ của thiết bị trong giao tiếp I2C. Địa chỉ này thường được cung cấp trong datasheet của thiết bị, hoặc có thể xác định thông qua các chương trình đọc địa chỉ.
Để sử dụng thư viện I2C trong Arduino, người dùng cần gọi thư viện
tích hợp sẵn trên trình biên dịch Arduino IDE.
Xác định địa chỉ của thiết bị trong giao tiếp I2C
Như đã trình bày ở phần giới thiệu, mỗi thiết bị trong giao tiếp I2C sẽ có một địa chỉ riêng. Các địa chỉ này sẽ được nhà sản xuất cung cấp trong datasheet của thiết bị. Ở phần này, chúng ta sẽ tìm hiểu về chương trình xác định địa chỉ của một thiết bị trong giao tiếp I2C. Một số linh kiện có hỗ trợ I2C nhưng người dùng khó tìm được datasheet chính thức từ nhà sản xuất thì việc dùng một chương trình ngắn để định địa chỉ I2C là một việc làm đơn giản và cần thiết.
Yêu cầu
Xác định địa chỉ các thiết bị trong giao tiếp I2C và phát hiện số thiết bị có giao tiếp I2C trong kết nối hiện tại.
Linh kiện cần dùng
-
Board Arduino Uno
-
Module LCD 20×4
Source code
#include
void setup() { Wire.begin(); // Khởi tạo thư viện Wire Serial.begin(9600); while (!Serial); // Đợi Serial Monitor hiển thị Serial.println("I2C Scanner"); } void loop() { byte error, address; int nDevices; Serial.println("Scanning..."); nDevices = 0; // Định địa chỉ I2C có 7 bit ứng với 128 thiết bị, việc quét sẽ tiến hành // từ 1 tới 127 (1 thiết bị làm master nên chỉ còn 127) for (address = 1; address < 127; address++ ) { Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address < 16) Serial.print("0"); Serial.print(address, HEX); Serial.println(" !"); nDevices++; Serial.println("Device no. " + String(nDevices)); } else if (error == 4) { Serial.print("Unknown error at address 0x"); if (address < 16) Serial.print("0"); Serial.println(address, HEX); } } if (nDevices == 0) Serial.println("No I2C devices found\n"); else Serial.println("number of devices " + String(nDevices)); delay(5000); // Delay 5s cho quá trình scan tiếp theo }
Với chương trình này, đầu tiên ta kiểm tra khi chỉ có 1 master – 1 slave với master là board Arduino Uno và slave là Module LCD 20×4 (các kiến thức cơ bản về hoạt động của LCD bạn đọc tham khảo ở nội dung phần tiếp theo).
Module LCD20x4 | Arduino Uno |
VCC |
5V |
GND |
GND |
SDA |
A4 |
SCL |
A5 |
Sau khi kết nối thành công, ta nạp chương trình trên vào board Arduino Uno và kiểm tra kết quả.
Kết quả:
-
Số thiết bị kết nối vào là 1 thiết bị.
-
Slave có địa chỉ I2C là 0x3F, ta sẽ dùng địa chỉ này để khai báo trong những chương trình giao tiếp I2C với module này.
Tiếp theo, ta xét việc định địa chỉ với chương trình trên khi trong giao tiếp I2C có ít nhất 2 thiết bị làm slave. Kết nối board Arduino Uno với LCD 20×04 và OLED SSD1306.
OLED SSD1306 | Arduino Uno |
VCC |
5V |
GND |
GND |
SDA |
A4 |
SCL |
A5 |
Kết nối song song các chân SCL, SDA, VCC, GND của các thiết bị slave và thiết bị master với nhau. Sau khi kết nối thành công, nạp chương trình trên vào cho board Arduino Uno.
Kết quả
Bạn đọc có thể sẽ đặt câu hỏi rằng: “Vậy việc định địa chỉ sẽ như thế nào khi trong giao tiếp I2C có 2 thiết bị giống nhau?”
Để kiểm chứng việc này, ta sẽ kết nối 2 thiết bị giống hệt nhau trong giao tiếp I2C và nạp chương trình trên. Kết quả nhận được sẽ là chương trình chỉ phát hiện được có 1 thiết bị kết nối vào, và trả về địa chỉ I2C duy nhất của thiết bị đó.
Một số module có các option để lựa chọn địa chỉ I2C thông qua việc setup phần cứng, một số module cho phép người dùng có thể định lại địa chỉ I2C bằng phần mềm, nếu không chúng ta có thể sử dụng các mạch I2C Multiplexer khi muốn kết nối nhiều module có cùng địa chỉ I2C. |
Giới thiệu về LCD và OLED
Giới thiệu về LCD
LCD là chữ viết tăt của Liquid Crystal Display, tiếng Việt có nghĩa là màn hình tinh thể lỏng, đây là loại thiết bị để hiển thị nội dung, cấu tạo bởi các tế bào (cũng là các điểm ảnh) chứa các tinh thể lỏng (liquid crystal) có khả năng thay đổi tính phân cực của ánh sáng và do đó thay đổi cường độ ánh sáng truyền qua khi kết hợp với các kính lọc phân cực. LCD có ưu điểm là phẳng, cho hình ảnh sáng, chân thật và tiết kiệm năng lượng.
Các hãng sản xuất linh kiện ngày nay sản xuất nhiều module LCD hỗ trợ cho việc tương tác với các vi điều khiển mà phổ biến nhất là 2 module LCD text 16×02 và LCD text 20×04. Các thông số 16×02 và 20×04 là số hàng và số cột tương ứng của các module, ví dụ với 16×02 cho biết module có 16 hàng và 2 cột, 20×04 là 20 hàng và 4 cột.
Như ở hình vẽ, các module có 16 chân để kết nối với chức năng mỗi chân:
-
VSS
: Tương đương với GND – cực âm. -
VDD
: Tương đương với VCC – cực dương (5V). -
Constrast Voltage (Vo)
: Điều khiển độ sáng màn hình. -
Register Select (RS)
: Lựa chọn thanh ghi (RS=0 chọn thanh ghi lệnh, RS=1 chọn thanh ghi dữ liệu). -
Read/Write (R/W)
: R/W=0 ghi dữ liệu , R/W=1 đọc dữ liệu. -
Enable pin
: Cho phép ghi vào LCD. -
D0 - D7
: 8 chân nhận dữ liệu. -
Backlight (Backlight Anode (+) và Backlight Cathode (-))
: Tắt bật đèn màn hình LCD.
Để khắc phục nhược điểm đấu nối nhiều dây với module LCD thông thường, các nhà sản xuất đã tích hợp thêm IC LCM1602 hỗ trợ giao tiếp I2C vào module LCD, việc đấu nối cũng như nạp chương trình từ đây sẽ trở nên đơn giản hơn.
LCM1602 | Arduino Uno |
VCC |
5V |
GND |
GND |
SDA |
A4 |
SCL |
A5 |
Chúng ta sẽ viết chương trình hiển thị ký tự lên màn hình LCD. Với module LCD có module I2C kèm theo, chúng ta cần thêm thư viện LiquidCrystal_I2C và Wire.h.
Link download thư viện : Liquid Crystal I2C. Add thư viện sau khi download vào chương trình.
Yêu cầu
Chương trình hiển thị dòng chữ “UniCloud Vietnam” và “Hello World” lên LCD trên 2 hàng
Source code
#include
#includeLiquidCrystal_I2C lcd(0x3F, 20, 4); // Khởi tạo lcd 20x04 với 0x3F là địa chỉ của LCD void setup() { lcd.begin(); // Khởi tạo LCD lcd.backlight(); // Mở đèn nền của LCD lcd.setCursor(1,0); // Lệnh setCursor() tương tự như với thư viện LiquidCrystal lcd.print("UniCloud Vietnam"); lcd.setCursor(3,1); lcd.print("Hello, world!"); } void loop() { // Không làm gì cả }
Giới thiệu về OLED
OLED là chữ viết tắt của Organic Light Emitting Diode), là loại màn hình hiển thị bao gồm một lớp vật liệu hữu cơ với thành phần chính là carbon nằm giữa hai điện cực anode và cathode, nó sẽ tự động phát sáng mỗi khi có dòng điện chạy qua. OLED sử dụng diode phát quang hữu cơ, chính vì thế OLED không cần tới đèn nền chiếu sáng nên có kích thước nhỏ gọn cũng như tiết kiệm điện hơn so với các loại LCD, độ sáng của OLED cũng tương đối tốt ở môi trường sáng tự nhiên.
OLED SSD1306
OLED SSD1306 là loại OLED có màn hình loại nhỏ, kích thước tầm 0.96 inch cho tới 1.25 inch. OLED SSD1306 hỗ trợ chuẩn giao tiếp I2C được sử dụng khá rộng rãi trong các sản phẩm điện tử. Tấm nền của OLED được điều khiển bằng chip driver SSD1306. Về cơ bản, để OLED có thể hiển thị được các thông tin mong muốn thì cần có thư viện hỗ trợ, cũng giống như khi làm việc với LCD. Tùy vào mỗi loại vi điều khiển với kiến trúc phần cứng khác nhau mà sẽ có những thư viện OLED SSD1306 khác nhau hỗ trợ, ví dụ như với các board dùng chip ESP8266 có thể sử dụng thư viện: ESP8266 OLED SSD1306, với board Arduino Uno (dùng chip ATmega328), thư viện OLED hỗ trợ được nhiều người sử dụng là Adafruit SSD1306.
Để sử dụng được hoàn chỉnh tất cả các tính năng thư viện này bao gồm cả những tính năng đồ họa, người dùng cần cài đặt thêm thư viện: Adafruit GFX Library (thư viện hỗ trợ thêm các tính năng đồ họa). |
Viết chương trình hiển thị thời gian lên màn hình OLED
Yêu cầu
Chương trình hiển thị thời gian từ lúc nạp chương trình vào board Arduino Uno.
Đấu nối
OLED | Arduino Uno |
VCC |
5V |
GND |
GND |
SDA |
A4 |
SCL |
A5 |
Source code
#include
#include
#include
#include
#define OLED_RESET 4 #define LOGO16_GLCD_HEIGHT 16 #define LOGO16_GLCD_WIDTH 16 #if (SSD1306_LCDHEIGHT != 32) #error("Height incorrect, please fix Adafruit_SSD1306.h!"); #endif Adafruit_SSD1306 display(OLED_RESET); int time_run = 0; void setup() { Serial.begin(9600); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.display(); delay(2000); display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0, 0); display.println("Hello, world!"); display.display(); delay(2000); display.clearDisplay(); } void loop() { int hour_run, min_run, sec_run; delay(1000); time_run ++; hour_run = time_run / 3600; min_run = (time_run % 3600) / 60; sec_run = time_run % 60; display.clearDisplay(); display.setCursor(0, 0); display.println(String(hour_run) + ":" + String(min_run) + ":" + String(sec_run)); display.display(); }
Ngoài ra, chúng ta cũng có thể tự tìm hiểu thêm các tính năng đồ họa của OLED với thư viện Adafruit SSD1306 với các chương trình có sẵn trong phần Example của thư viện.
Giao tiếp giữa 2 board Arduino Uno
Có nhiều cách để giao tiếp giữa các vi điều khiển với nhau như truyển nhận qua Serial như đã đề cập ở bài hướng dẫn trước. Phần này, chúng ta sẽ tìm hiểu cách giao tiếp giữa 2 board Arduino Uno thông qua chuẩn giao tiếp I2C.
Yêu cầu*
Board master điều khiển bật tắt LED trên thiết bị slave.
Kết nối
Đầu tiên ta thực hiện việc kết nối giữa 2 board Arduino Uno.
Master | Slave |
5V |
5V |
GND |
GND |
A4 |
A4 |
A5 |
A5 |
Ở đây, để thuận tiện ta có thể lấy ngõ ra 5V trên master để cấp nguồn cho slave, người dùng cũng có thể cấp nguồn riêng cho thiết bị slave trong các ứng dụng thực tế tùy theo yêu cầu sử dụng.
Nạp các chương trình cho master và slave.
Source code cho master
// Master #include
void setup() { Serial.begin(9600); // Khởi tạo giao tiếp I2C Wire.begin(); // Khởi tạo truyền nhận dữ liệu I2C } void loop() { // Bắt đầu quá trình truyền dữ liệu trên Serial Monitor while (Serial.available()) { char c = Serial.read(); // Đọc dữ liệu từ serial nếu có và lưu vào biến c if (c == 'H') { Wire.beginTransmission(3); // Bắt đầu truyền đến slave với address la 3 Wire.write('H'); // Truyền chữ H đến slave Wire.endTransmission(); // Kết thúc quá trình truyền } else if (c == 'L') { Wire.beginTransmission(3); Wire.write('L'); Wire.endTransmission(); } } }
Source code cho slave
// Slave #include
#define pinLed 13 // Định nghĩa chân LED void setup() { Wire.begin(3); // Khởi tạo giao tiếp I2C với địa chỉ của slave là 3 Wire.onReceive(receiveEvent); // Đăng kí hàm receiveEvent sẽ được gọi khi nhận được dữ liệu pinMode(pinLed,OUTPUT); digitalWrite(pinLed,LOW); } void loop() { // Không làm gì cả } void receiveEvent() { while(Wire.available()) { char c = Wire.read(); // Lưu dữ liệu nhận được từ master vào biến c nếu có if(c == 'H') // So sánh dữ liệu nhận được và điều khiển LED digitalWrite(pinLed,HIGH); else if(c == 'L') digitalWrite(pinLed,LOW); } }
Giải thích source code
I2C sử dụng việc định địa chỉ 7 bit tương ứng với 128 thiết bị kết nối. Trong Arduino, ta có thể định địa chỉ cho slave bởi hàm
Wire.begin(địa chỉ slave)
, khi đó, slave được khởi tạo một địa chỉ với địa chỉ nằm trong khoảng từ 1 tới 127.Master sẽ truyền dữ liệu tới slave qua câu lệnh
Wire.beginTransmission(địa chỉ slave)
. Master thì không cần truyền địa chỉ.
Kết quả
Sau khi nạp chương trình cho master và slave thành công, ta mở cửa số Serial Monitor ở board Arduino Uno master lên và gõ vào dòng Send kí tự hoặc tương ứng với các lệnh để bật và tắt LED trên board Arduino Uno slave.
Tổng kết
Qua phần này, chúng ta đã tìm hiểu được những khái niệm cơ bản về giao tiếp I2C, các kết nối giữa vi điều và các IC sử dụng giao thức này, hiểu được cách xác định địa chỉ và tìm hiểu một số chương trình cơ bản dùng giao thức này.Bạn đọc có thể tham khảo các ứng dụng mở rộng của giao thức này với các module cảm biến gia tốc, thời gian thực, các module phối hợp chuẩn I2C và SPI,… Tùy vào những ứng dụng cụ thể mà người dùng sẽ phải lựa chọn những IC thích hợp dùng giao tiếp I2C.
What Is I2C?
The I2C protocol involves using two lines to send and receive data: a serial clock pin (SCL) that the Arduino Controller board pulses at a regular interval, and a serial data pin (SDA) over which data is sent between the two devices.
In I2C, there is one controller device, with one or more peripheral devices connected to the controllers SCL and SDA lines.
As the clock line changes from low to high (known as the rising edge of the clock pulse), a single bit of information is transferred from the board to the I2C device over the SDA line. As the clock line keeps pulsing, more and more bits are sent until a sequence of a 7 or 8 bit address, and a command or data is formed. When this information is sent – bit after bit -, the called upon device executes the request and transmits it’s data back – if required – to the board over the same line using the clock signal still generated by the Controller on SCL as timing.
Each device in the I2C bus is functionally independent from the controller, but will respond with information when prompted by the controller.
Because the I2C protocol allows for each enabled device to have it’s own unique address, and as both controller and peripheral devices to take turns communicating over a single line, it is possible for your Arduino board to communicate (in turn) with many devices, or other boards, while using just two pins of your microcontroller.
An I2C message on a lower bit-level looks something like this:
- The controller sends out instructions through the I2C bus on the data pin (SDA), and the instructions are prefaced with the address, so that only the correct device listens.
- Then there is a bit signifying whether the controller wants to read or write.
- Every message needs to be acknowledged, to combat unexpected results, once the receiver has acknowledged the previous information it lets the controller know, so it can move on to the next set of bits.
- 8 bits of data
- Another acknowledgement bit
- 8 bits of data
- Another acknowledgement bit
But how does the controller and peripherals know where the address, messages, and so on starts and ends? That’s what the SCL wire is for. It synchronises the clock of the controller with the devices, ensuring that they all move to the next instruction at the same time.
However, you are nearly never going to actually need to consider any of this, in the Arduino ecosystem we have the Wire library that handles everything for you.
Arduino I2C Pins
Below is a table that lists the different board form factors and what pins are for I2C.
Form factor | SDA | SCL | SDA1 | SCL1 | SDA2 | SCL2 |
UNO | SDA/A4 | SCL/A5 | ||||
Nano | A4 | A5 | ||||
MKR | D11 | D12 | ||||
GIGA | D20 | D21 | D102 | D101 | D9 | D8 |
Mega & Due | D20 | D21 |
CHI TIẾT BÀI VIẾT
HƯỚNG DẪN CƠ BẢN ARDUINO UNO R3 KẾT NỐI I2C HIỂN THỊ THÔNG BÁO TRÊN LCD
1. Giới thiệu
Bài viết hướng dẫn các bạn cách kết nối board mạch Arduino Uno R3 kết nối với I2C và LCD và thực hiện lập trình trên Arduino để hiển thị thông báo trên màn hình LCD
2. Thiết bị phần cứng
- Arduino Uno R3
- Mạch điểu khiển LCD
Điện áp MAX : 7V
Điện áp MIN : – 0,3V
Hoạt động ổn định : 2.7-5.5V
Điện áp ra mức cao : > 2.4
Điện áp ra mức thấp : <0.4V
Dòng điện cấp nguồn : 350uA – 600uA
Nhiệt độ hoạt động : – 30 – 75 độ C
- Module giao tiếp I2C
LCD có quá nhiều nhiều chân gây khó khăn trong quá trình đấu nối và chiếm dụng nhiều chân trên vi điều khiển.
Module I2C LCD ra đời và giải quyết vấn để này cho bạn.
Thay vì phải sử dụng 6 chân vi điều khiển để kết nối với LCD 16×2 (RS, EN, D7, D6, D5 và D4) thì module IC2 bạn chỉ cần tốn 2 chân (SCL, SDA) để kết nối.
Module I2C hỗ trợ các loại LCD sử dụng driver HD44780(LCD 16×2, LCD 20×4, …) và tương thích với hầu hết các vi điều khiển hiện nay.
3. Kết nối các thiết bị
4. Kết nối Arduino Uno R3 với máy tính
Kết nối cổng USB từ Arduino đến máy tính
Xác định Port Kết nối : Chuột phải vào My computer trên máy tính 🡪 manager 🡪 Device manager 🡪 check Port ( COM & LPT )
Cấu hình trên phần mềm Arduino 🡪 mở phần mềm Arduino 🡪 chọn tool 🡪 chọn Board
Ở đây mình sử Dụng Board Arduino Uno R3 🡪 chọn Arduino /Genuino Uno
Nhập code
#include LiquidCrystal_I2C lcd(0x27,16,2); void setup() lcd.init(); lcd.backlight(); lcd.setCursor(4,0); lcd.print(“ITSTARVN”); lcd.setCursor(0,1); lcd.print(“Xin chao cac ban”); void loop() |
Thực hiện upload code trên thiết bị Arduino
Kết quả hiển thị thông báo trên màn hình LCD
Thực hiện: Nguyễn Mạnh Cương
Tư vấn: 0979.466.469 / 0938.128.290 Email: [email protected]
Nhiều lúc chúng ta muốn chia sẻ khối lượng công việc của một Arduino với một Arduino khác. Hoặc có thể chúng ta muốn nhiều chân digital hoặc analog thì I2C là giải pháp tốt nhất.
I2C là giao thức thường được sử dụng để giao tiếp giữa các linh kiện trên bo mạch chủ trong máy ảnh và trong bất kỳ hệ thống điện tử nhúng nào.
Trong bài này chúng ta sẽ tạo một bus I2C bằng hai arduino. Chúng ta sẽ lập trình một arduino chủ để ra lệnh cho arduino slave khác nhấp nháy đèn LED của nó một hoặc hai lần tùy thuộc vào giá trị nhận được.
Để làm điều này, chúng ta cần các thành phần sau:
2 arduino
Cáp jumper
Cách kết nối
Thực hiện theo các bước sau để kết nối hai Arduino Uno bằng I2C:
Nối các chân A4 và A5 trên một Arduino với các chân tương tự trên Arduino kia.
Dòng GND phải chung cho cả 2 Arduino. Nối nó với một jumper.
Chú ý không được nối Arduino 5 V và 3,3 V với nhau. Tuy không ảnh hưởng Arduino 5V, nhưng chắc chắn sẽ ảnh hưởng đến Arduino 3,3V.
Code
Đoạn code sau được chia thành hai phần: code chủ và code slave, chạy trên hai Arduino khác nhau. Đầu tiên là code chủ:
Và đây là code slave diễn giải các ký tự được gửi từ chủ:
Đầu tiên, chúng ta hãy nhìn vào chủ. Chúng ta cần thêm thư viện Wire.h :
Sau đó, trong phần thiết lập, chúng ta bắt đầu bus I2C bằng cách sử dụng hàm Wire.begin (). Nếu không có đối số được cung cấp trong hàm, Arduino này sẽ là bo chủ.
Cuối cùng, chúng ta gửi một ký tự x, nằm trong khoảng từ 0 đến 5. Chúng ta sử dụng các hàm sau để bắt đầu truyền đến thiết bị có địa chỉ 9, viết ký tự và sau đó dừng truyền:
Tiếp theo là Arduino slave. Chúng ta cũng thêm thư viện Wire.h, nhưng bắt đầu bus I2C bằng cách sử dụng Wire.begin (9). Số trong đối số là địa chỉ chúng ta muốn sử dụng cho Arduino. Tất cả các thiết bị có địa chỉ 9 sẽ nhận được tín hiệu truyền.
Bây giờ chúng ta cần phản ứng khi nhận được tín hiệu truyền I2C. Hàm sau sẽ thêm chức năng kích hoạt bất cứ khi nào nhận được ký tự. Có nghĩa là bất cứ khi nào Arduino nhận được một ký tự trên I2C, nó sẽ chạy chức năng mà chúng ta bảo nó chạy:
Và đây là hàm. Ở đây, chúng ta chỉ cần lưu trữ giá trị của ký tự nhận được:
Trong loop(), chúng ta chỉ cần diễn giải ký tự đó để nháy đèn LED tích hợp ở các tốc độ khác nhau tùy thuộc vào ký tự nhận được.
Tìm hiểu thêm về I2C
Chúng ta đi sơ qua phần lý thuyết, I2C cần hai dòng digital: Dòng dữ liệu nối tiếp (SDA) để truyền dữ liệu và Dòng clock nối tiếp (SCL) để giữ clock. Mỗi kết nối I2C có một chủ và nhiều slave. Một chủ có thể viết lên slave và yêu cầu slave cung cấp dữ liệu, nhưng không một slave nào có thể trực tiếp viết cho chủ hoặc cho một slave khác. Mỗi slave có một địa chỉ duy nhất trên bus và chủ cần biết địa chỉ của từng slave mà nó muốn truy cập.
Mỗi bus I2C có thể hỗ trợ tới 112 thiết bị. Tất cả các thiết bị cần chia sẻ GND. Tốc độ khoảng 100 kb / s không quá nhanh nhưng vẫn đáng kể và hữu dụng. Có thể có nhiều hơn một chủ trên bus, nhưng nó rất phức tạp và thường ít được sử dụng.
Rất nhiều cảm biến sử dụng I2C để liên lạc, điển hình là dụng cụ đo quán tính, áp kế,
cảm biến nhiệt độ, và một số sonar. Cần nhớ rằng I2C không sử dụng cho cáp dài. Chỉ cần cáp dài 2 mét đã gặp vấn đề.
I2C là một giao thức truyền dẫn phức tạp, nhưng nó rất hữu ích. Tất cả Arduino đều có thể dùng được, với một vài khác biệt ở chân:
Các chân I2C của bo
Uno, Pro Mini A4 (SDA), A5 (SCL)
Mega, Due 20 (SDA), 21 (SCL)
Leonardo, Yun 2 (SDA), 3 (SCL)
Kết nối nhiều thiết bị
Nếu chúng ta cần kết nối nhiều hơn hai thiết bị trên một bus I2C, chúng ta chỉ cần kết nối tất cả các dòng SDA và SCL lại với nhau. Chúng ta sẽ cần địa chỉ của mọi slave được giải quyết từ Arduino chính.
Code: 7203-014 Còn hàng
Code: 7204-245 Còn hàng
Code: 7006-031 Còn hàng
Code: 7204-237 Còn hàng
Hotline: 0979 466 469
I2C có nghĩa là Giao thức truyền thông mạch tích hợp liên tục. Nó là một giao thức truyền thông nối tiếp 2 dây cho các ứng dụng truyền dữ liệu tầm ngắn. Đây là một giao thức truyền thông rất phổ biến được sử dụng trong các dự án nhúng để giao diện các cảm biến dựa trên, màn hình chữ số và mô-đun giao tiếp.
Các thiết bị muốn giao tiếp với từng kết nối thông qua bus I2C. Bus I2C hỗ trợ nhiều thiết bị nô lệ và nhiều thiết bị chính.
Nhiều Cảm biến sử dụng giao thức truyền thông nối tiếp này để truyền dữ liệu của chúng đến các bộ vi điều khiển hoặc thông qua giao thức này, các mạch phụ khác nhau có thể giao tiếp với các mạch chủ. Nó chỉ áp dụng cho việc truyền dữ liệu khoảng cách ngắn.
Đặc điểm nổi bật của I2C so với SPI là nó chỉ sử dụng hai dây để thực hiện giao tiếp. Một dây là SCL (dòng đồng hồ nối tiếp) đồng bộ hóa việc truyền dữ liệu giữa các thiết bị và dây còn lại là SDA (dữ liệu nối tiếp) mang dữ liệu thực tế cần truyền. Các đường này là các đường thoát nước mở có nghĩa là chúng cần được kết nối để kéo điện trở lên nếu các thiết bị hoạt động ở mức thấp. Mỗi thiết bị phụ được kết nối với xe buýt sẽ có một địa chỉ 8-bit duy nhất.
Việc giao tiếp giữa các thiết bị cụ thể sử dụng hai dây được thực hiện bởi thực tế là mỗi thiết bị có ID hoặc địa chỉ thiết bị duy nhất của riêng mình và sử dụng địa chỉ này; Master có thể chọn bất kỳ thiết bị cụ thể nào để giao tiếp.
Mỗi thiết bị phụ có địa chỉ duy nhất được sử dụng để nhận dạng thiết bị trên bus. Nói cách khác, địa chỉ phụ giúp thiết bị chính gửi thông tin đến mộtthiết bị nô lệ cụ thể trên bus.
Các thiết bị chính có thể gửi và nhận thông tin. Nô lệ phản ứng với bất cứ điều gì một chủ nhân gửi. Khi gửi thông tin trên bus, chỉ cần một thiết bị duy nhất có thể gửi thông tin tại một thời điểm.
Tóm lại, chúng ta chỉ cần hai dây để truyền dữ liệu hoặc giao tiếp với các số lượng thiết bị khác nhau. Như chúng ta đều biết rằng Arduino có các chân hạn chế, I2C cho phép kết nối với nhiều thiết bị cùng một lúc. Chỉ thiếu sót là bạn không thể sử dụng giao thức này để truyền dữ liệu đường dài.
Truyền dữ liệu từng chút một nối tiếp dọc theo một dây (đường SDA). Giống như SPI, I2C là đồng thời, đầu ra của các bit được đồng bộ hóa với việc kiểm tra các bit bằng tín hiệu đồng hồ được chia sẻgiữa chủ và nô lệ.
Đối với giao tiếp I2C, các bo mạch khác nhau của Arduino có các chân khác nhau được dành riêng dưới dạng chân SDA và SCL. Danh sách dưới đây hiển thị các số pin này trong các bảng khác nhau.
Trong Arduino UNO, Chân A4 = SDA và Chân A5 = SCL
Đối với Arduino = SDA và Chân 21 = SCL
Trong Arduino Leonardo, Chân 2 = SDA và Chân 3 = SCL
Đối với Arduino due, Chân 20 = SDA và Chân 21 = SCL, SDA1, SCL1
Hình dưới đây cho thấy các chân SDA và SCL trong Arduino UNO sẽ được sử dụng trong bài viết này.
Thư viện “dây” được sử dụng để giao tiếp I2C trong Arduino. Sau đây là các chức năng quan trọng của thư viện này.
Thư viện dây được bắt đầu bằng lệnh này và tham gia bus I2C với tư cách là chủ hoặc nô lệ. Địa chỉ là tùy chọn. Địa chỉ này bao gồm bảy bit cho các thiết bị nô lệ. Trường hợp không quy định; thiết bị tham gia xe buýt với tư cách là Master.
Lệnh này được sử dụng bởi thiết bị Master để yêu cầu byte từ thiết bị phụ. Các hàm “Available ()” và “read ()” có thể được sử dụng để thu thập các byte này sau đó. “Địa chỉ” là địa chỉ của một thiết bị phụ cụ thể mà từ đó yêu cầu sẽ được thực hiện và “số lượng” chỉ định số byte được yêu cầu.
Lệnh này bắt đầu truyền byte với thiết bị phụ của địa chỉ nhất định. Sau đó, các byte được truyền đi được xếp hàng đợi bằng hàm write () và các byte này được truyền bằng hàm endTransmission().
Việc truyền byte được khởi tạo bằng hàm wire.beginTransmission(), được kết thúc bằng lệnh này.
Hàm này chấp nhận một đối số. Nếu đối số là TRUE, lệnh dừng được gửi sau khi truyền byte và bus I2C được tha.
Nếu đối số là FALSE, nó sẽ gửi lệnh khởi động lại sau khi truyền byte và bus không được tha và nó ngăn một thiết bị chính khác truyền giữa các tin nhắn.
Lệnh này thực hiện một trong 2 chức năng cùng một lúc.
Khi một yêu cầu được thực hiện bởi master, hàm này sẽ ghi dữ liệu từ thiết bị nô lệ.
Giữa các lệnh gọi của hàm beginTransmission() và endTransmission(), lệnh này được sử dụng để xếp hàng byte để truyền.
Sau lệnh requestFrom (), các byte được truyền từ thiết bị nô lệ đến thiết bị chính hoặc thiết bị chính đến thiết bị nô lệ; được đọc bằng lệnh này.
Hàm này trả về số byte sẵn có để truy xuất sau khi gọi hàm read(). Trong thiết bị chính, nó được gọi sau hàm requestFrom() và trong thiết bị nô lệ, nó được gọi bên trong hàm onReceive ().
Điều này đăng ký một chức năng được gọi là khi truyền từ thiết bị chính được nhận bởi thiết bị phụ. Hàm được gọi là được đại diện bởi trình xử lý.
Trong phần này, chúng ta sẽ thấy một code ví dụ để truyền dữ liệu giữa hai bo mạch Arduino sử dụng bus I2C. Chúng tôi sẽ định cấu hình một bảng Arduino làm thiết bị chính I2C và một bảng Arduino khác làm thiết bị nô lệ I2C. Master sẽ chuyển các số từ 0-6 theo trình tự sang thiết bị phụ. Nếu số mà thiết bị phụ nhận được nhỏ hơn 3, đèn LED được kết nối với chân D13 của Arduino nô lệ sẽ vẫn bật nếu không đèn LED vẫn tắt.
Để xây dựng chương trình này, chúng ta cần các thành phần sau:
Hai Arduinos (Chủ và nô lệ)
LED-Thạc sĩ
Nô lệ LED
Dây kết nối
Hình dưới đây cho thấy các kết nối I2C giữa hai bảng Arduino. Ngoài ra, nó cho thấy kết nối của đèn LED với D13 của nô lệ Arduino. Kết nối chân cực dương của đèn LED với chân kỹ thuật số Arduino 13 thông qua điện trở giới hạn dòng điện 220 ohm. Chốt catốt sẽ được nối đất.
Bên cạnh để bật giao tiếp I2C, hãy kết nối các chân I2C A4 và A5 của Arduino chính với các chân I2C của Arduino nô lệ.
Sau đó, bằng cách sử dụng dây nhảy chung mặt đất của cả hai bảng Arduino.
Sau khi thực hiện kết nối, hãy tải mã của cả nô lệ và chủ lên bảng Arduino.
Bây giờ trước tiên hãy thảo luận về mã thiết bị chính Arduino.
Ở đây thư viện dây đã được bao gồm và biến x được bắt đầu. Giá trị của biến này sẽ được truyền đến thiết bị phụ.
#include
int x = 0;
Ở đây hàm wire.begin() đã được gọi. Chi tiết có thể được tìm thấy trong phần trên. Ngoài ra bộ đệm nội bộ đã được bắt đầu.
void setup()
Wire.begin();
Serial.begin(9600);
pinMode(LED,OUTPUT);
Ở đây việc truyền tải đã được bắt đầu với địa chỉ nô lệ là 9. Sau đó, giá trị của x đã được xếp hàng để truyền qua bus I2C. Hàm wire.write() truyền giá trị hiện tại của ‘x’ trên bus I2C. Cuối cùng, giá trị của x đã được truyền đi và bus đã được tha bằng lệnh endTranmission().
Wire.beginTransmission(9);
Wire.write(x);
Wire.endTransmission();
Giá trị của x được tăng theo bước 1 trong vòng lặp với độ trễ 200milli giây, nếu giá trị của x lớn hơn 6, giá trị này được đặt lại về 0 và một lần nữa tăng từ 0.
x++;
if (x > 6)
x = 0;
delay(200);
#include
int LED=13;
int x = 0;
void setup()
Wire.begin();
Serial.begin(9600);
pinMode(LED,OUTPUT);
void loop()
Wire.beginTransmission(9);
Wire.write(x);
Wire.endTransmission();
x++;
if (x > 6)
x = 0;
delay(200);
Đầu tiên, chúng tôi xác định tên tượng trưng cho chân D13 của Arduino. Chân D13 sẽ bật và tắt tùy thuộc vào giá trị nhận được từ thiết bị chính. Ngoài ra, chúng ta khai báo và khởi tạo một biến x bằng không. Biến x này sẽ được sử dụng để lưu trữ giá trị nhận được từ một Arduino chính.
#include
// add i2c library
int LED = 13;
int x = 0;
Bên trong chức năng thiết lập, đặt chân LED làm chân ouput bằng chức năng pinMode (). Cũng đặt địa chỉ nô lệ của Arduino thành 9.
pinMode(LED, OUTPUT);
Wire.begin(9);
Wire.onReceive(receiveEvent);
Serial.begin(9600);
Khai báo và gọi hàm đã được thực hiện như được định nghĩa trong phần trên. Tại đây hàm receiveEvent () đã được đăng ký bằng cách gọi hàm onReceive(handler).
void receiveEvent(int bytes)
x = Wire.read();
Hàm reciveEvent () này đã được định nghĩa ở đây. Nó đọc giá trị từ 1 đến 6 sẽ được gửi bởi thiết bị chính; và sau đó lưu trữ giá trị này trong biến x đã được khai báo trong chức năng thiết lập của chương trình.
void loop()
if (x <= 3)
digitalWrite(LED, HIGH);
else
digitalWrite(LED, LOW);
Nếu giá trị của x nhận được từ thiết bị chính nhỏ hơn hoặc bằng 3, đèn LED ở chân 13 được đặt để BẬT và nếu giá trị của x lớn hơn 3, đèn LED ở chân 13 được đặt để TẮT.
#include
int LED = 13;
int x = 0;
void setup()
pinMode (LED, OUTPUT);
Wire.begin(9);
Wire.onReceive(receiveEvent);
Serial.begin(9600);
void receiveEvent(int bytes)
x = Wire.read();
void loop()
if (x <= 3)
digitalWrite(LED, HIGH);
else
digitalWrite(LED, LOW);
Để xem phần trình diễn, hãy tải mã chủ và mã nô lệ lên bảng Arduino. Tuy nhiên, trước khi tải mã lên, hãy đảm bảo chọn bảng Arduino từ Bảng > Công cụ và cũng chọn cổng COM chính xác mà bảng Arduino được kết nối từ Công cụ > Cổng.
Master Arduino sẽ liên tục truyền dữ liệu qua bus I2C và dữ liệu sẽ nằm trong khoảng từ 0-6. Tùy thuộc vào dữ liệu mà thiết bị phụ nhận được và các điều kiện được đề cập ở trên Led ở chân 13 được bật và tắt.
Giao tiếp này được sử dụng để cân bằng màu sắc, thay đổi độ tương phản và màu sắc trong màn hình.
Trong loa thông minh, âm lượng được thay đổi bằng phương thức giao tiếp này.
Nó được sử dụng trong việc đọc các cảm biến chẩn đoán như tốc độ quạt trong máy tính.
Bật và TẮT nguồn điện trong các thành phần hệ thống.
Truy cập đồng hồ thời gian thực và chip NVRAM giữ cài đặt của người dùng.
Nó có thể thích ứng được vì nó duy trì nhiều giao tiếp chủ và nhiều nô lệ. Chỉ sử dụng hai dây Nó rất linh hoạt có thể dễ dàng thích ứng với các yêu cầu của các thiết bịphụ khác nhau ACK / NACK khẳng định rằng mỗi cạnh đã được gửi một cách hiệu quả. Các thiết bị có thể đượcgiới thiệu hoặc loại bỏ trên xe buýt.
Nó không áp dụng cho giao tiếp đường dài Chỉ sử dụng hai dâyNó có tốc độ chậm Yêu cầu nhiều không gian hơn vì việc sử dụng điện trởPhức tạp khi số lượng thiết bị tăng lên.
>>> 100+ Mã Sản Phẩm Dây Rút: https://mecsu.vn/san-pham/day-rut-nhua.5op
>>> 1000+ Mã Sản Phẩm Đầu Cosse: https://mecsu.vn/san-pham/dau-cosse.Q1j
-
- Tổng tiền thanh toán:
|
#include void loop() |
|
#include void loop() |
|
#include void loop() |
|
#include void loop() |
Description
This library allows you to communicate with I2C devices, a feature that is present on all Arduino boards. I2C is a very common protocol, primarly used for reading/sending data to/from external I2C components. To learn more, visit this article for Arduino & I2C.
Due to the hardware design and various architectural differences, the I2C pins are located in different places. The pin map just below highlights the default pins, as well as additional ports available on certain boards.
Board |
I2C Default |
I2C1 |
I2C2 |
Notes |
UNO R3, UNO R3 SMD, UNO Mini Ltd |
A4(SDA), A5(SCL) |
I2C also available on the SDA / SCL pins (digital header). |
||
UNO R4 Minima, UNO R4 WiFi |
A4(SDA), A5(SCL) |
Qwiic: D27(SDA), D26(SCL) |
I2C also available on the SDA / SCL pins (digital header). |
|
UNO WiFi Rev2, Zero |
20(SDA), 21(SCL) |
|||
Leonardo, Micro, Yùn Rev2 |
D2(SDA), D3(SCL) |
|||
Nano boards |
A4(SDA), A5(SCL) |
|||
MKR boards |
D11(SDA), D12(SCL) |
|||
GIGA R1 WiFi |
20(SDA), 21(SCL) |
D102(SDA1), D101 (SCL1) |
D9(SDA2), D8 (SCL2) |
Use |
Due |
20(SDA), 21(SCL) |
D70(SDA1), D71(SCL1) |
Use |
|
Mega 2560 Rev3 |
D20(SDA), D21(SCL) |
This library inherits from the Stream functions, making it consistent with other read/write libraries. Because of this,
send()
and
receive()
have been replaced with
read()
and
write()
.
Recent versions of the Wire library can use timeouts to prevent a lockup in the face of certain problems on the bus, but this is not enabled by default (yet) in current versions. It is recommended to always enable these timeouts when using the Wire library. See the Wire.setWireTimeout function for more details.
Note: There are both 7 and 8-bit versions of I2C addresses. 7 bits identify the device, and the eighth bit determines if it’s being written to or read from. The Wire library uses 7 bit addresses throughout. If you have a datasheet or sample code that uses 8-bit address, you’ll want to drop the low bit (i.e. shift the value one bit to the right), yielding an address between 0 and 127. However the addresses from 0 to 7 are not used because are reserved so the first address that can be used is 8. Please note that a pull-up resistor is needed when connecting SDA/SCL pins. Please refer to the examples for more information. MEGA 2560 board has pull-up resistors on pins 20 and 21 onboard.
The Wire library implementation uses a 32 byte buffer, therefore any communication should be within this limit. Exceeding bytes in a single transmission will just be dropped.
To use this library:
#include
Keywords searched by users: i2c on arduino uno
Categories: Khám phá 34 I2C On Arduino Uno
See more here: kientrucannam.vn
See more: https://kientrucannam.vn/vn/