In the last article I talked about How I2C and TWI protocol works and we saw that they are mostly the same so this library works for both I2C and TWI serial interfaces. You don't have to know every detail about how the I2C protocol works but I strongly recommend reading the article to have a general idea about it, and that way it will be easier to use this library.
Contents
- Structure objects
- TWI initialization
- Start transmission
- Send TWI address
- Transmit a byte
- Transmit a string of bytes
- Read byte
- Byte ready
- Stop transmission
- Disable TWI
- Read error flag
- Read status code
- Reset TWI interface
- Error flag and status codes
- Code example - Transmit data to an I2C or TWI device
- Code example - Read data from an I2C or TWI device
- Download
API
Structure objects
Since some micro-controllers have two TWI (I2C) modules, every function takes a pointer to a structure object as an argument. This way, both TWI modules can be used at the same time.
Example:
TWI_Init(&twi0, TWI_400KHZ)
Notice the ampersand used to pass the memory address as an argument.
There are two structure objects defined that can be used: twi0 and
twi1. By default only twi0 module is included. If you need the twi1
module, the TWI_ENABLE_TWI1 define must be set to 1 in the header
file.
TWI initialization
void TWI_Init(TWI_t* twi, uint32_t frequency)
Used to initialize the TWI module. This will set the TWI bit rate and enable global interrupts. 400kHz is the maximum TWI speed that regular AVR microcontrollers supports although I have managed to talk to a DAC at 577kHz.
frequency:
Can be one of the following constants (TWI_100KHZ, TWI_400KHZ) or any value between 100-400kHz.
Start transmission
void TWI_StartTransmission(TWI_t* twi)
This function sends the START command to begin the transmission and so putting the microcontroller in a Master mode. If an error occurs the TWI_ERROR_FLAG is set. The returned status code from TWI hardware is saved in the TWI_STATUS_CODE variable. After this function TWI_ContactDevice() should be used.
Send TWI address
void TWI_ContactDevice(TWI_t* twi, uint8_t address, uint8_t rw, uint8_t nr_of_bytes_to_read)
Sends the SLA+RW packet (7-bit address and read or write bit).
address:
The address of the device to communicate with.
rw
Read or write mode. In read mode the TWI interrupt will be enabled and the
received data can be accessed using TWI_ReadByte().
Can be one of the following constants: TWI_READ_MODE,
TWI_WRITE_MODE.
nr_of_bytes_to_read
How many bytes to read before issuing a STOP condition inside the ISR. Without
a STOP, the TWI will clock the SCL and the Slave device will keep
re-transmitting the same bytes. In write mode this can be 0.
Transmit a byte
void TWI_TransmitByte(TWI_t* twi, uint8_t byte_data)
Transmit a single byte.
byte_data
The byte to send.
Transmit a string of bytes
void TWI_Transmit(TWI_t* twi, const uint8_t *data)
Transmit a string of bytes.
data
Pointer to a string of bytes.
Read byte
uint8_t TWI_ReadByte(TWI_t* twi)
When TWI_ContactDevice() is used in read mode, this function is used to return a received byte. If no byte is available it will return null. The received bytes will be stored by the TWI ISR in a circular buffer and each time this function is executed the next byte will be returned.
Byte ready
bool TWI_ByteReady(TWI_t* twi)
Returns true if new bytes are available. When this returns true, the read byte
function can be used.
Stop transmission
void TWI_StopTransmission(TWI_t* twi)
Issue a STOP command to end the TWI transmission.
Disable TWI
void TWI_Disable(TWI_t* twi)
Disables the TWI. Any ongoing transmissions will be stopped immediately. Using
TWI_StartTransmission() will re-enable the TWI module.
Read error flag
uint8_t TWI_ReadErrorFlag(TWI_t* twi)
Returns the error flag [0:1] in case of a TWI error. Will be set to 0 next time a device is contacted.
Read status code
uint8_t TWI_ReadStatusCode(TWI_t* twi)
Returns the status code. Can be used if the error flag is set to check the
error type. The TWI status codes are defined in the header file.
Reset TWI interface
void TWI_ResetTWIInterface(TWI_t* twi)
Resets the TWI interface by sending a START, 9 of 1's another START and a STOP, in case an error appeared on the bus. The explanation for this is a bit complex and can be found is some data sheets for example the data sheet for MCP4706 DAC page 70.
For this function to work, the TWI_USE_INTERFACE_RESET define must be
set to 1 and then below it set the port and pins that correspond to the TWI
module (0 or 1).
Error flag and status codes
After a function is executed, if the returned status code from the TWI module is not ACK or NACK, the TWI_ERROR_FLAG flag will be set to 1 indicating an error. The flag will be cleared by the same functions that can set it on the next run. It's up to the software designer to decide what actions to take in case of a transmission error.
The same functions will also save the status codes from the TWI module, in the
TWI_STATUS_CODE variable. A list with TWI status codes and what they represent
can be found inside the header file in the status codes section.
Code example - Transmit data to an I2C or TWI device:
#include "twi.h" int main(void){ uint8_t data_byte; TWI_Init(&twi0, TWI_100KHZ); // Start transmission. This enables the Master mode. TWI_StartTransmission(&twi0); if(TWI_ReadErrorFlag()){ // Optional. Code if the TWI_StartTransmission() had an error } // Select the device using it's address and set the write mode TWI_ContactDevice(&twi0, 0x27, TWI_WRITE_MODE, 0); if(TWI_ReadErrorFlag()){ // Optional. Code if the TWI_ContactDevice() had an error } // Transmit a byte data_byte = 123; TWI_TransmitByte(&twi0, data_byte); if(TWI_ReadErrorFlag()){ // Optional. Code if the TWI_TransmitByte() had an error } // ... the transmit function can be used again to transmit // as many bytes is necessary // Stop the transmission TWI_StopTransmission(&twi0); while(1){ } }
Code example - Read data from an I2C or TWI device
#include "twi.h" int main(void){ uint8_t i = 0; uint8_t received_byte = 0; uint8_t bytes_expected = 4; TWI_Init(&twi0, TWI_100KHZ); // Start transmission. This enables the Master mode. TWI_StartTransmission(&twi0); if(TWI_ReadErrorFlag()){ // Optional. Code if the TWI_StartTransmission() had an error } // Select the device by address with a read command. // In this example, 'bytes_expected' is the number of bytes it is expected // from the Slave device. TWI_ContactDevice(&twi0, 0x27, TWI_READ_MODE, bytes_expected); if(TWI_ReadErrorFlag()){ // Optional. Code if the TWI_ContactDevice() had an error } // Read 4 bytes for(i=0; i < bytes_expected; i++){ // Wait for a new byte. while(TWI_ByteReady() == 0){ // If no data will be received, this can be // an infinite loop, so you might want to add // a timeout here. } // The received byte can be processed or saved in an array received_byte = TWI_ReadByte(&twi0); // Check if the received byte is not null if(received_byte != NULL){ // Do something with it }else{ // The byte is null. Here the error flag // could be checked or the status codes. } } // The STOP command will be issued inside the ISR after all bytes // have been received or an error occurred while(1){ } }
Download
v2.0 | |
twi.h | |
twi.c | |
Changelog | |
v2.0 (1-01-2025) |
- Now devices with multiple TWI modules can use them simultaneously by
using object pointers as function parameters. - Some code optimization. |
v1.2 | - Fixed a bug on ATmega328P and similar devices with only one I2C module where registers TWxRn are not defined. |
No comments:
Post a Comment