How to use I2C / TWI (Two Wire Interface) in AVR ATmega32- (Part 36/46)

Code explanation for MASTER Controller:
Step 1: Initialization of master.
Initialization of MASTER means to set the TWI clock frequency (SCL). It is done by setting bit rate in TWBR and pre scaler bits in TWSR.
Fig. 2: Equation Of TWI Clock Frequency to initialize the Master in AVR

                               Fig. 2: Equation Of TWI Clock Frequency to initialize the Master in AVR

void TWI_init_master(void) // Function to initialize master
{
    TWBR=0x01;    // Bit rate
    TWSR=(0<<TWPS1)|(0<<TWPS0);    // Setting prescalar bits
    // SCL freq= F_CPU/(16+2(TWBR).4^TWPS)
}

 

Step 2: Send start condition
The start condition in TWI is explained before. The AVR microcontroller has in built registers which makes this job much easier.
1.      Clear TWINT flag by writing a logic one to it.
2.      Set TWSTA bit to send start condition.
3.      Set TWEN bit to initialize the TWI.
4.      Monitor the status of TWINT flag.
5.      Check the ACK byte (using while condition since the SCL frequency is very small as compared to micro controller clock frequency). The ACK byte can be compared by monitoring the status of TWSR.
void TWI_start(void)
{
    // Clear TWI interrupt flag, Put start condition on SDA, Enable TWI
    TWCR= (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);    
    while(!(TWCR & (1<<TWINT))); // Wait till start condition is transmitted
    while((TWSR & 0xF8)!= 0x08); // Check for the acknowledgement
}
Step 3: Send the slave address, data direction bit (write) and wait for the ACK signal
Start Condition bit in TWI
Fig. 3: Start Condition bit in TWI
These three processes are controlled by AVR’s TWI registers.
1.      Put the seven bit slave address and the direction control bit in TWDR.
2.      Clear TWINT flag.
3.      Enable TWI by writing logic one to TWEN bit.
4.      Monitor the status of TWINT flag, the TWINT flag will get cleared when the data in TWDR is been transmitted.
5.      Check for the correct acknowledgement.
void TWI_read_address(unsigned char data)
{
    TWDR=data;    // Address and read instruction
    TWCR=(1<<TWINT)|(1<<TWEN);    // Clear TWI interrupt flag,Enable TWI
    while (!(TWCR & (1<<TWINT))); // Wait till complete TWDR byte received
    while((TWSR & 0xF8)!= 0x40);  // Check for the acknoledgement
} 
Step 4: Send the 8-bit data and wait for the ACK
Data transfer in TWI of AVR
Fig. 4: Data transfer in TWI of AVR
1.      Put the 8 bit data in TWDR.
8 bits = 7 bit slave address + Data direction bit (write = 0).
2.      Clear TWINT flag.
3.      Set TWEN bit to enable TWI.
4.      Monitor the status of TWINT flag to get data transmission completed.
5.      Check for the acknowledgement.
void TWI_write_data(unsigned char data)
{
    TWDR=data;    // put data in TWDR
    TWCR=(1<<TWINT)|(1<<TWEN);    // Clear TWI interrupt flag,Enable TWI
    while (!(TWCR & (1<<TWINT))); // Wait till complete TWDR byte transmitted
    while((TWSR & 0xF8) != 0x28); // Check for the acknoledgement
}
Step 5: Send the STOP condition
TWI STOP Condition Bit

 

Fig.5: TWI STOP Condition Bit

To send the stop condition use TWSTO
1.      Clear TWINT flag.
2.      Set TWEN bit
3.      Write logic one to TWSTO bit so send STOP condition on SDA and SCL line.
4.      Monitor the status of TWSTO bit, as TWSO bit get cleared means the stop condition has been transmitted.
void TWI_stop(void)
{
    // Clear TWI interrupt flag, Put stop condition on SDA, Enable TWI
    TWCR= (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);    
    while(!(TWCR & (1<<TWSTO)));  // Wait till stop condition is transmitted
}
Up till here the data transmission from slave side is complete, the MASTER is working in mode one. As per the objective the data received by MASTER is displayed on PORTB. The flow chart for MASTER as a Transmitter (mode one) is given below.
Flow Chart of MASTER as Transmitter in TWI Interfacing using AVR

                                     Fig. 6: Flow Chart of MASTER as Transmitter in TWI Interfacing using AVR

From here the MASTER would be working in mode two i.e., MASTER becomes a receiver. The AVR TWI works in mode 2.
Step 6: Send the START condition on bus lines
This step is as similar to the previous one.
Note: In the Step 6 the START condition is sent after the STOP condition. If one more start condition is sent before the STOP condition in between then it is called as repetitive start condition. The repetitive start condition is same as the START condition but the only difference is between the acknowledgements. For more details about repetitive start refer to the data sheet. If the data is sent continuously in same direction then there would be no need of start condition, repetitive start or stop condition in between. The second data can be transmitted just after receiving the acknowledgement of first data byte (as shown in the above flow chart).
Step 7: Send the slave address and data direction bit (read) and wait for the ACK signal
1.      Put the 8 bit data in TWDR.
8 bits = 7 bit slave address + Data direction bit (read = 1).
2.      Clear TWINT flag.
3.      Set TWEN bit to enable TWI.
4.      Monitor the status of TWINT flag to get data transmission completed.
5.      Check acknowledgement.
void TWI_read_address(unsigned char data)
{
    TWDR=data;    // Address and read instruction
    TWCR=(1<<TWINT)|(1<<TWEN);    // Clear TWI interrupt flag,Enable TWI
    while (!(TWCR & (1<<TWINT))); // Wait till complete TWDR byte received
    while((TWSR & 0xF8)!= 0x40);  // Check for the acknoledgement
}
Step 8: Read the data from SDA bus
1.      Clear TWINT flag
2.      Set TWEN bit, enable TWI
3.      Monitor the status of TWINT flag, as the TIWNT flag get set indicates the value in TWDR has been received.
4.      Check for the acknowledgement. If the master wants to receive the last byte from slave, the status of TWSR register will be 0x58. After receiving the last byte either a repetitive start condition is issued by the master to continue the communication or a stop condition must be given by the master to stop the communication process. Else if the master wants to keep on receiving more byte from slave the status of TWSR register will be 0x50.
To acknowledge salve about last byte TWEA bit is used while transmitting the data. If TWEA bit is set, reception continuous from the MASTER side. And if TWEA bit is low, MASTER orders slave to send the last byte.
5.      Get the received data. And send it on PORTB.
void TWI_read_data(void)
{
    TWCR=(1<<TWINT)|(1<<TWEN);    // Clear TWI interrupt flag,Enable TWI
    while (!(TWCR & (1<<TWINT))); // Wait till complete TWDR byte transmitted
    while((TWSR & 0xF8) != 0x58); // Check for the acknoledgement
    recv_data=TWDR;
    PORTB=recv_data;
}
Step 9: Send STOP condition
The stop condition is already explained.
Flow Chart of MASTER as Receiver in TWI Interfacing using AVR

   Fig. 7: Flow Chart of MASTER as Receiver in TWI Interfacing using AVR

Code explanation for SLAVE controller:
Step 1: Initialization of the Slave controller
Initialization of the slave controller is done by assigning address to the slave. The seven bit slave address is filled in TWI slave Address Register (TWAR). The LSB of TWAR i.e., TWGCE bit is use to enable the slave to acknowledge for the general call address (0x00).
void TWI_init_slave(void) // Function to initilaize slave
{
    TWAR=0x20;    // Fill slave address to TWAR
} 
Step 2: Check the status of TWSR register
If the value of TWSR is 0x60, it means the data sent by the master in the next step is meant to read by this particular slave only and the slave sends back the acknowledgement to the master corresponding to the read operation. If the TWSR status is 0x70 the SLAVE is requested to read the data at the general call (0x00). At this stage the SLAVE acts as receiver. The AVR TWI is working in mode 3.
1.      Clear TWIN flag.
2.      Enable TWI.
3.      Set TEWA to receive acknowledgement.
4.      Monitor the status of TWINT flag.
5.      Match the status of TWSR. If the status is 0x60 read data or else jump to (1)
void TWI_match_read_slave(void) //Function to match the slave address and slave dirction bit(read)
{
    while((TWSR & 0xF8)!= 0x60)  // Loop till correct acknoledgement have been received
    {
        // Get acknowlegement, Enable TWI, Clear TWI interrupt flag
        TWCR=(1<<TWEA)|(1<<TWEN)|(1<<TWINT);    
        while (!(TWCR & (1<<TWINT)));  // Wait for TWINT flag
    }
} 
Step 3: Read data
Read the data sent by the MASTER.
1.      Clear TWINT flag.
2.      Enable TWI.
3.      Set TWEA for receiving ACK.
4.      Get the data form TWDR, display it on PORTB.

void TWI_read_slave(void)
{
// Clear TWI interrupt flag,Get acknowlegement, Enable TWI
TWCR= (1<<TWINT)|(1<<TWEA)|(1<<TWEN);
while (!(TWCR & (1<<TWINT)));    // Wait for TWINT flag
while((TWSR & 0xF8)!=0x80);        // Wait for acknowledgement
recv_data=TWDR;                    // Get value from TWDR
PORTB=recv_data;                // send the receive value on PORTB
}

Slave as Receiver in TWI Interfacing using AVR

Fig. 8: Slave as Receiver in TWI Interfacing using AVR
From here the slave becomes a transmitter on the request of master to send data. The AVR TWI works in mode 4.
Step 4: Check the status of TWSR register
If the value of TWSR is 0xA8, it means the master wants to receive data from the particular slave and the slave sends back the acknowledgement to the master corresponding to the write operation.
1.      Clear TWIN flag.
2.      Enable TWI.
3.      Set TEWA to receive acknowledgement.
4.      Monitor the status of TWINT flag.
5.      Match the status of TWSR. If the status is 0xA8 send data or else jump to (1)
void TWI_match_write_slave(void) //Function to match the slave address and slave dirction bit(write) 
{
    while((TWSR & 0xF8)!= 0xA8)    // Loop till correct acknoledgement have been received
    {
        // Get acknowlegement, Enable TWI, Clear TWI interrupt flag
        TWCR=(1<<TWEA)|(1<<TWEN)|(1<<TWINT);    
        while (!(TWCR & (1<<TWINT)));  // Wait for TWINT flag
    }
} 
Step 5: Write the data on SDA bus
1.      Put the data in TWDR.
2.      Enable TWI.
3.      Cleat the TWINT flag.
4.      Monitor the TWINT flag. As it get cleared signifies the data has been send.
5.      Check for the ACK. Since the TWEA bit was not set during writing data on SDA bus, it  signifies master that this is the last data to be send and in turn it receives a NOT ACK and the status of TWSR is becomes 0xC0. And if the TWEA bit was set during the data transmission it receives an ACK and the status of TWSR is becomes 0xB8. For more details refer to the data sheet.

void TWI_write_slave(void) // Function to write data
{
TWDR= write_data;              // Fill TWDR register whith the data to be sent
TWCR= (1<<TWEN)|(1<<TWINT);   // Enable TWI, Clear TWI interrupt flag
while((TWSR & 0xF8) != 0xC0); // Wait for the acknowledgement
}

Slave as Transmitter in TWI Interfacing using AVR

Fig. 9: Slave as Transmitter in TWI Interfacing using AVR

 

Source: How to use I2C / TWI (Two Wire Interface) in AVR ATmega32- (Part 36/46)

About The Author

Muhammad Bilal

I am highly skilled and motivated individual with a Master's degree in Computer Science. I have extensive experience in technical writing and a deep understanding of SEO practices.

Scroll to Top