-
Notifications
You must be signed in to change notification settings - Fork 356
TWI
This discussion assumes familiarity with Digital I/O.
Two Wire Interface (TWI) is the generic name for the interfaces commonly known as I²C and SMBus. It is an interface which enables half-duplex serial communication between multiple devices, sharing the same bus, with as little as 2 wires (hence the name). Each of the devices can be either master or slave, where masters are the only ones that can initiate data transactions. Each slave device has a unique address on the bus, which is used to select it as the target of a transaction. TWI supports multiple masters on a bus, but this is a rather rare use-case, and for the rest of this discussion we'll assume there is only one.
The two wires used are called SDA (data) and SCL (clock). Both operate in open-drain mode, with an external pull-up resistor to 3.3V or 5V needed on each. The SCL line is normally controlled by the master, but in certain situations (clock streching) may be pulled low by a slave device. The SDA line is used for data transfer in both directions. The protocol dictates who has access to the SDA line at any given time. Certain combinations of SDA/SCL states are used as special signals between the master and slave, such as beginning and ending a transaction.
A TWI transaction is comprised of one or more write steps or read steps. In a write step, the master sends data to a slave. In a read step, the slave sends data back to the master. Most commonly, TWI transactions will have either a write followed by a read, or only a single write / read.
A few parameters need to be matched between master and slave. First, TWI supports two addressing formats: 7-bit and 10-bit, the former being more common. IOIO supports both. Consult the slave's documentation to find out its address format and value. Note that in 7-bit addressing, some device documentations specify the address as a number between 0-127, while others give a number between 0-255, where the least significant bit designates read/write. You need to figure out which convention is used. IOIO uses the former, so you might need to divide by 2 the address provided on the device's datasheet. Second, the difference between I²C and SMBus is the voltage levels they expect on the wires, so this needs to be configured correctly. Next is the data rate: 100KHz, 400KHz or 1MHz are supported. Last is the actual protocol, which specifies what message the device expects and what messages it sends in response.
IOIO supports acting as a TWI master, on up to 3 concurrent TWI buses. It can perform write-read, write-only or read-only transactions in which the length of the response is known in advance. It needs to be the only master on each of those buses. The pins used for TWI are fixed. They are marked at the bottom of the board as DAx/CLx. where x corresponds to the TWI module number. A complete list of pin functions can be found in the table at the bottom of this page. External pull-up resitors (typically 10k-Ohm) to 3.3V are required on both lines.
Using the IOIO TWI modules is done via the TwiMaster
interface. An instance of this interface corresponds to a single TWI module on the board, as well as to the pins it uses for SDA and SCL. TwiMaster
instances are obtained by calling IOIO.openTwiMaster()
. For example:
TwiMaster twi = ioio.openTwiMaster(twiNum, TwiMaster.Rate.RATE_100KHz, false);
This opens a TWI module number twiNum
in master mode, using its dedicated SDA and SCL pins. The TWI module will run at 100KHz (400KHz and 1MHz also supported) and will use I²C voltage levels (pass true
as third argument for SMBus levels). It is required that at the time of call, the dedicated TWI pins are not being used for anything else, and that the requested TWI module is not in use.
Once an instance of TwiMaster
is obtained, requests can be sent by using:
byte[] request = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 };
byte[] response = new byte[4];
twi.writeRead(address, false, request, request.length, response, response.length);
This will write 0x01, 0x02, 0x03, 0x04, 0x05 to the device with the requested address, then read 4 bytes from it. The false
passed as second argument means we're using 7-bit addressing mode.
The writeRead()
method will block until a response is received, and then the response can be read from the response buffer. If blocking is not desired, there is also an asynchronous version:
TwiMaster.Result result = twi.writeReadAsync(address, false, request, request.length, response, response.length);
// ...do some stuff while the transaction is taking place...
result.waitReady(); // blocks until response is available
doSomethingWithResponse(response);
When you are done using the TWI module, call:
twi.close();
in order to return the pins to a "floating" state and possibly be able to re-open them in the same or in a different mode, as well as free the TWI module. The TwiMaster
instance becomes useless after this call - it is illegal to do anything with it.