An indepth reference to the Arduino Wire library.
Greetings, fellow Arduinoid! GreyGnome here. The Arduino Wire library documentation is notoriously stingy in details. Nowhere does it explain its relationship to the twi library of the avr-lib, and worse, to the TWI hardware in the ATmega chips. This reference attempts to fill in the gaps and give the Wire library the documentation it needs and deserves.
Throughout the exploration of the code and the design of this document, this author has used the Arduino Duemilanove, based on the ATmega328 datasheet. See the ATmega328 datasheet for more information. It is an invaluable reference for understanding the TWI hardware and the ATmega's I2C communication methodology.
This discussion is based on the ATmega328 datasheet, revision Rev. 8271C – 08/10. Newer revisions are fine, I'm sure, but my page numbers might be off a little bit.
In order to fully understand the Wire library code, we need to understand what it needs to do to the MCU, to get it set up.
Review of the datasheet (http://www.atmel.com/dyn/resources/prod_documents/8271S.pdf) for the processor shows how the Two Wire system works. To note are:
Java/hardware/tools/avr
. On the Mac the full path is /Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/versions.txt
; the path on your platform will be different but similar.
Here are some of the registers involved with the Two Wire Interface (TWI) hardware module:
Register | Name | Function |
---|---|---|
TWCR | Two Wire Control Register | Controls the actions of the TWI module |
TWSR | Two Wire Status Register | Reports the status of the TWI actions |
TWDR | Two Wire Data/Address Register | Contains the data you want to transmit or have received |
TWBR | Two Wire Bit Rate Register | Controls the frequency of the clock (SCL) |
See the following flow schematic for performing a write on the I2C bus:
Referring to the Registers table above, the Reader's Digest version of an I2C write operation using the TWI hardware goes like this:
TWCR
register to generate a START condition.
TWSR
for status. Assuming all is well,
TWDR
with the slave's address, plus the "write" bit. Together, this value is known as "SLA+W" ("SLave Address plus Write").
TWCR
register to send the SLA+W.
TWDR
with data, TWCR
is set, and data is sent.
TWSR
for status. Assuming all is well,
TWCR
register to send a STOP condition. In the STOP condition, the SCL line is released (to go high), and then the SDA line goes high. Normally the SDA line must remain stable when the SCL line is high.
A read operation is performed similarly.
The Wire library uses a large amount of memory for buffers. It includes 3 different byte buffers, each 32 bytes in size. This isn't a big issue in the ATmega line, but the ATtiny (such as the ATtiny48) line has less available RAM (512/1K on ATmega vs. (256/512 on ATtiny). When I attempted to use the wire library on the ATtiny, I had random failures, hangs, etc. Once I changed the buffer size (in .\libraries\Wire\utility\twi.h) to 6, everything worked just fine.
Don't make the mistake of thinking that you can just do a #define TWI_BUFFER_LENGTH 6
in your sketch because that wont work. The way that the Arduino environment compiles your code is not obvious. The library is compiled separately from your sketch and then linked together later. So you you'll have to change the buffer size in the library header which is also included by the library source (twi.c).
A detailed Wire library reference.
Note that the Wire library is written in C++. Therefore, all the library calls listed here are in truth methods of a C++ class. If you're unfamiliar with C++ programming, you can think of a "method" as simply a function or a subroutine and you'll have the right idea. You can safely leave the gory details to the language geeks, and go ahead and work on getting your Arduino to communicate over the I2C bus.
The Wire library's I/O blocks. This means that when sending or receiving over the I2C bus, your application is prevented from running until the communication is complete. In truth the operation of the TWI hardware in your processor is interrupt-driven, so your application should be free to run at full speed while the TWI communication takes place, but this capability is not utilized by the Wire library.
In the following reference section, "Under the Hood" describes the action that each method has on the ATmega's TWI hardware, and consequently on the I2C bus.
Function:Wire.begin()
or Wire.begin(address)
must first be called to start any I2C communication using the Wire library. The Wire.begin()
method sets the Wire library's internal read and write buffer indices and lengths to 0, then readies the ATMEL TWI hardware with the twi.init()
function call.
The twi.init()
call is internal to the Wire library. It performs the following tasks:
Wire.setClock()
. The value passed is the bitrate from 400000L to 31000L. Wire.setClock(400000L);
should set it to 400kHz. Wire.setClock()
must be called after Wire.begin()
. Wire.begin()
initializes the bitrate to 100kHz.
Arguments:
None.
Under the Hood:
twi.init()
, which
begin(address)
Function:
TWAR
(TWI Address Register) with the lower 7 bits of its address argument. The 8 bits of the argument are shifted left and a 0 is appended. That 0 is the TWGCE
bit.
twi_attachSlaveTxEvent(onRequestService);
and twi_attachSlaveRxEvent(onReceiveService);
functions. These functions assign user-designated functions that are called during an I2C transmit and/or receive, when the processor is acting as an I2C slave. Thus, onRequest()
and/or onReceive()
should have been called prior to this method call. See onReceive(handler)
or onRequest(handler)
, below.
begin()
method as above.
Arguments:
address
: 8 bit integer, or an int value (ints are 16 bits long by default). This is the I2C address of the ATmega processor, in slave address mode. It is cast to an 8-bit unsigned integer. If given an integer (16 bit) value, the number will have its most significant 8 bits truncated.
Notes:
This method sets the TWAR
register in the TWI hardware, which is used when you want the Arduino to act as an I2C slave. The TWI's Address Match unit checks incoming I2C communications against the TWAR
. When there's a match, the TWI Control Unit is informed and the TWI hardware will perform an action depending on the settings of the TWCR
. Note that in the Wire library, the TWGCE
bit is not set. This means that the Arduino will not respond to the I2C General Call address (address 0x00, which is basically an I2C broadcast).
The twi_attachSlaveTxEvent()
assigns a callback function to be used in an I2C transmit, if the ATmega is acting as an I2C slave transmitter. This function must have been previously set using the onRequest(handler)
method. It is called after a SLA+R has been received, and it must call twi_transmit()
in order to fill the transmit buffer with data. It also must call a twi_reply(1)
to actually begin the transfer over the I2C bus (the argument to twi_reply()
specifies that the ATmega will send an ACK after its reply). Once the twi_reply()
is called, the Wire library's interrupt handler and the TWI hardware will handle the data transfer.
The twi_attachSlaveRxEvent()
assigns a callback function to be used in an I2C receive, if the ATmega328 is acting as an I2C slave. This function must have been previously set using the onReceive(handler)
method; it will be called with 2 arguments: a receive buffer, and the index to the last of the data in the buffer. The function is called after a STOP condition has been received, signaling the end of the I2C conversation. The ATmega328 will stretch the clock, thereby causing the I2C master to wait until the handler has completed.
Under the Hood:
TWAR
register in the TWI hardware.
twi_transmit()
and twi_reply(1)
.
Function:
Used when the ATmega is acting as an I2C master. Requests count bytes from the I2C slave given by address. Each call to this method resets the read buffer index; that is, the internal buffer is read into beginning with array cell 0. Note that address must be a 7-bit address; if an 8-bit address is given, the top bit is simply truncated.
The requestFrom()
method calls twi_readFrom()
, which actually performs the I2C read by sending the START condition and waiting for the buffer to be filled as requested. The I2C slave will NACK the read request if you request more bytes than are available, and the requestFrom()
method will exit cleanly.
The method returns the number of bytes read from the slave.
Arguments:
BUFFER_LENGTH
which is defined in the Wire.h include file; at the time of this writing that number is 32. If count is greater than 32 it will be set to 32 without notice.
Under the Hood:
Function:
Used when the ATmega is acting as an I2C master. Sets internal variables in the Wire library in preparation for transmitting to the given address.
The next Wire library call should be the send()
method.
Arguments:
The argument is the address of an I2C slave. This is a 7-bit address; if 8 bits are given, the top bit is simply truncated.
Under the Hood:
No TWI activity. This method simply modifies some Wire library internal variables.
Function:
Calls the twi routine that does the actual TWI transmission when in master transmitter mode: It attempts to become the twi bus master and write a series of bytes to a device on the bus. The bytes should have been previously loaded into the Wire library send buffer by the send()
call.
The results are returned as a byte. The results are as follows:
Under the Hood:
Function:
Populate the send data buffer with the data found in the argument list. See Arguments, below.
The term "send" for this method is something of a misnomer. Nothing is actually sent via this method. Only a data buffer (an array) is populated with the data argument.
For Master Transmitter mode: In the case of send(value)
, a single byte is appended to the data buffer. In the case of send(string)
or send(data, quantity)
, the data presented is appended to the data buffer up to the limit of BUFFER_LENGTH
. Any data requested to send that would overflow the buffer past BUFFER_LENGTH
(which is 32 at the time of this writing), is simply ignored.
When acting as an I2C slave transmitter, the data is inserted into the slave I2C transmit buffer for delivery by the TWI hardware. See Wire.begin(address)
and onRequest(handler)
.
Arguments:
Can take one of three parameters:
send(value)
: Send a byte, where value is of type uint8_t (...or, simply: a byte).
send(string)
: Send a string of chars. Calls send(data, quantity)
where the quantity is computed as the length of the string.
send(data, quantity)
: An array of data bytes to send. This array is limited to BUFFER_LENGTH
which is given as 32 bytes in the header files. If quantity is greater than BUFFER_LENGTH
, the additional data is quietly ignored.
Under the Hood:
No TWI activity. This method simply modifies some Wire library internal variables.
Returns the number of bytes available in the Wire library receive buffer. This buffer is filled:
requestFrom()
method
onReceiveService()
method. That is called after a master has sent a STOP or repeated START condition during an I2C slave receive session, effectively finalizing the data transfer.
Arguments:
None.
Under the Hood:
No TWI activity. This method simply modifies some Wire library internal variables.
Returns the next byte in the Wire library receive buffer. See available()
, above, for more information about the Wire library receive buffer.
Arguments:
None.
Under the Hood:
No TWI activity. This method simply modifies some Wire library internal variables.
Sets a function ("handler") to be called after the ATmega has received data as an I2C slave. The handler is called with a single argument, an integer which is the number of bytes that have been received. It should return no values. Therefore, it should look like this:
void handler(int byteCount)
The I2C bus is not released until the handler returns, so it is probably wise to ensure that the handler is relatively quick to complete. Note, however, that "relatively quick" means there's a lot of room for the ATmega to work, as on most Arduinos it runs at 16MHz and the I2C communication is only at 100kHz.
The handler can use the receive()
method to read the next byte from the Wire library's receive buffer.
Sets a function ("handler") to be called when the ATmega is in slave transmitter mode. The handler takes no arguments and returns no values. Therefore, it should look like this:
void handler(void)
The handler is called after the ATmega has received SLA+R ("SLave Address plus the Read bit"). It must call twi_transmit()
in order to fill the transmit buffer with data. It also must call the Wire library's send()
method to fill the transmit buffer; see
send()
.
Then it must call twi_reply(1)
to actually begin the transfer over the I2C bus (the argument "1" to twi_reply()
specifies that the ATmega will send an ACK after its reply). Once the twi_reply()
is called, the Wire library's interrupt handler and the TWI hardware will handle the data transfer.