Unfortunately, now that we’ve gotten entirely familiar with the Wire Library, we’re going to abandon it in favor of the I2C Library developed by Wayne Truchsess at DSSCircuits. The essential problem is, that there are places behind the scenes in the wire library where things can get stuck in eternal loops, freezing your processor until a hard reset is performed, and the I2C Library solves that problem, in addition to providing a larger variety of setup options. Let’s dig in…
Objectives
- Understand how to load the new I2C library.
- Examine the differences in setup options between the I2C Library as opposed to the Wire Library.
- Learn the new commands to read and write from I2C components.
Background
The Wire Library
Schematic
No schematic is necessary for this module.
Setup
No hardware is necessary for this module.
The I2C Library can be downloaded here: I2C Library Rev 5
The I2C Library
While doing some testing on the I2C Display, I discovered that the poor thing would freeze randomly after a few seconds of operation. I scoured the schematic, went back and forth with the chip manufacturer, and finally concluded that the problem was with the Arduino itself, not with the chip.
The problem lies in the Wire Library. There are quite a few entries in wire.cpp that essentially read like this…
1 2 3 |
while (something != value) { something = grabValue } |
Basically, the possibility exists for the code to enter this while loop and never exit if something never equals value.
The I2C library resolves this problem, by adding an extensive timeout feature, such that if something doesn’t equal value within a specific length of time, an error code will be generated, and control will be returned.
In addition to this, the commands are a little more clean to use as you’ll soon discover.
Downloading and installing the I2C Library
Here are the steps necessary to install the I2C Library…
- Download Rev 5 of the I2C Library.
- Open your Arduino IDE and click on Sketch -> Import Library -> Add Library…
- Browse to the zip file you downloaded and select it for import.
- You should receive a message in your IDE saying, “Library added to your libraries.”
- In the IDE, click on Sketch -> Import Library and verify that “I2C” appears at the bottom of the list.
I2C Library Setup Functions
With the Wire Library, our entire scope of setup and configuration involved the command Wire.begin();. With the I2C Library we have a significantly larger configuration capability.
I2c.begin()
This is similar to
Wire.begin , in that it performs the first sets of initializations. This is a required function and should be placed in the Setup area of your code.
I2c.end()
This ends all I2C functionality, returning the A4 and A5 pins to functioning as standard Arduino Analog pins.
I2c.setSpeed(Speed)
Look at that! No need to hunt around inside the guts of the library to change from 100kHz to 400kHz I2C! You can do it right away by specifying it with setSpeed. Speed is either 0 or 1 with 0 = 100kHz and 1 = 400kHz. Note though, that just because you can run at 400kHz, doesn’t mean that your components support it.
I2c.pullup(Activate)
This allows you to enable or disable the internal pullup resistors of your Arduino’s I2C pins. With the standard Wire library, the pullups are enabled by default and you have to modify the library code to change it. In this case, 0 = Pullups OFF and 1 = Pullups ON.
I2c.timeOut(timeOut)
This is the key function here. It allows you to specify how long the library will wait for it’s various conditions to become valid before releasing the SDA and SCL lines to high impedance states, basically resetting the whole thing and allow you to break free and continue processing. The allowed values are 0 = Disable Timeout and 1-65535 milliseconds. The author of the library has this note regarding the timeOut usage…
On a side note, be careful with setting too low a value because some devices support clock stretching which can increase the time before an acknowledgement is sent which could be misconstrued as a lockup.
By enabling timeOut, you will be able to receive error results from your send and receive functions as specified below.
I2c.scan()
Searches the bus for I2C devices by essentially issuing every possible 7-bit address and seeing if an ACK is transmitted. Results are output to the Serial monitor window.
I2C Library Write Functions
All of the I2c.write functions return values that can be used for error checking and troubleshooting. The returns are as follows…
0 – No errors reported
1 – Timeout: waiting for confirmation of Start Condition
2 – Timeout: waiting for ACK/NACK after transmitting address + write flag
3 – Timeout: waiting for ACK/NACK after transmitting data to slave
4 – Timeout: waiting for confirmation of Repeated Start Condition
5 – Timeout: waiting for ACK/NACK after transmitting address + read flag
6 – Timeout: waiting for ACK/NACK after receiving data from slave
7 – Timeout: waiting for confirmation of Stop Condition
8 – 0xFF Error – Buffer size mismatch
I2c.write(I2Caddress, registerAddress)
This function specifies that you are going to write from the master to the slave device, but only moves the pointer register without reading data from that location, or writing data to it.
I2c.write(I2Caddress, registerAddress, data)
This function specifies that you are going to write from the master to the slave device, and allows you to specify a single 8-bit byte of data that you want transmitted.
I2c.write(I2Caddress, registerAddress, *data)
This is exactly the same as the command above, however instead of sending a single byte of data, you can send an array of unsigned bytes (that’s what the “*” means). According to the library documentation, there is no limit on the amount of data that can be transmitted using this function, as opposed to the Wire library which is limited to 32bytes.
I2c.write(I2Caddress, registerAddress, *data, numberBytes)
This is exactly the same as the command above, however it allows you to limit the amount of data you’re sending from your array, instead of blasting the whole thing. Again, there is no limit on the amount of data that can be transmitted using this function.
I2C Library Read Functions
I2c.read(I2Caddress, numberBytes)
This will read the specified number of bytes from the device selected by I2Caddress and store them in an internal buffer for later use. The read will occur from whichever register the pointer was set to previously. This command is limited to 32 bytes of returned data due to the buffer size inside the ATmega328P. In order to use the data returned, you must read it out of the internal buffer using the I2c.receive() command listed below.
I2c.read(I2Caddress, numberBytes, *dataBuffer)
This will read the specified number of bytes from the device selected by I2Caddress and immediately store them in the specified unsigned byte array. The read will occur from whichever register the pointer was set to previously.
I2c.read(I2Caddress, registerAddress, numberBytes)
This will read the specified number of bytes, from the specified register address, inside the device selected by I2Caddress, and store them in an internal buffer for later use. This command is limited to 32 bytes of returned data due to the buffer size inside the ATmega328P. In order to use the data returned, you must read it out of the internal buffer using the I2c.receive() command listed below.
I2c.read(I2Caddress, registerAddress, numberBytes, *dataBuffer)
This will read the specified number of bytes, from the specified register address, inside the device selected by I2Caddress, and immediately store them in the unsigned byte array. This command is not limited to 32 bytes because the data is immediately being cycled into array.
I2C Library Data Functions
I2c.available()
Returns the number of bytes that are stored in the internal 32 byte buffer.
I2c.receive()
Returns the first unread byte in the internal 32 byte buffer. This is equivalent to the
Wire.read() command.