Como usar I2C / TWI (interface de dois fios) no AVR ATmega32- (Parte 36/46)

How to use I2C/TWI (two-wire interface) on the AVR ATmega32- (Part 36/46)

This article explores the TWI interface between two ATmega32 controllers. Readers are advised to consult the TWI Communication and TWI records for the ATmega32 before proceeding.
TWI works in four modes:
1. MASTER as transmitter.
two. MASTER as receiver.
3. SLAVE as receiver.
4. SLAVE as transmitter.
Generally modes 1 and 3 and modes 2 and 4 are used together. This article explains the use of these four modes in an experiment.
Objective: Establish communication between two ATmega32 using TWI interface. First the Master starts sending data and then the slave transmits the complement of data received to the master. When the Master receives the supplemented data, it shifts the original data to the left. This process of transmission and reception continues. As the data value reaches 0x80, the entire process is repeated. At the beginning, the original data value is 0x01. The received value is displayed in PORTB at both ends.
Circuit Description:
Make connections as shown in the circuit diagram.
Explanation of the code for the MASTER controller:
Step 1: Master initialization.
MASTER initialization means setting the TWI clock frequency (SCL). This is done by setting the bitrate in TWBR and the prescaler bits in TWSR.

Equação da frequência do clock TWI para inicializar o mestre no AVR

Fig. 2: TWI clock frequency equation for initializing the master in the 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 initial condition
The initial condition in TWI was explained previously. The AVR microcontroller has built-in registers that make this job much easier.
1. Clear the TWINT flag by writing a logical to it.
two. Set the TWSTA bit to send the start condition.
3. Set the TWEN bit to initialize TWI.
4. Monitor the status of the TWINT flag.
5. Check the ACK byte (using while condition as SCL frequency is very small compared to microcontroller clock frequency). The ACK byte can be compared by monitoring the TWSR status.
 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 acknowledgment
 }
Step 3: Send slave address, data direction (write) bit and wait for ACK signal
Bit de condição inicial no TWI
Fig. 3: Initial condition bit in TWI
These three processes are controlled by the AVR's TWI registers .
1. Put the seven-bit slave address and direction control bit into the TWDR.
two. Clear the TWINT flag.
3. Enable TWI by writing logic one to the TWEN bit.
4. Monitor the status of the TWINT flag, the TWINT flag will be cleared when data in TWDR is transmitted.
5. Check correct recognition.
 void TWI_read_address(unsigned char data)
 {
   TWDR=date;   // Address and read instructions
   TWCR=(1<<TWINT) (1<<TWEN);   // Clear TWI interrupt flag,Enable TWI
   while (!(TWCR & (1<<TWINT))); // Wait until complete TWDR byte received
   while((TWSR & 0xF8)!= 0x40); // Check for the acknowledgment
 }
Step 4: Send 8-bit data and wait for ACK
Transferência de dados em TWI do AVR
Fig. 4: Data transfer in TWI from the AVR
1. Put 8-bit data into TWDR.
8 bits = 7-bit slave address + data direction bit (write = 0).
two. Clear the TWINT flag.
3. Set the TWEN bit to enable TWI.
4. Monitor the status of the TWINT flag to complete data transmission.
5. Check recognition.
 void TWI_write_data(unsigned char data)
 {
   TWDR=date;   // put data in TWDR
   TWCR=(1<<TWINT) (1<<TWEN);   // Clear TWI interrupt flag,Enable TWI
   while (!(TWCR & (1<<TWINT))); // Wait until complete TWDR byte transmitted
   while((TWSR & 0xF8) != 0x28); // Check for the acknowledgment
 }
Step 5: Send the STOP condition

Bit de condição TWI STOP

Fig.5: TWI STOP condition bit

To send the stop condition use TWSTO
1. Clear the TWINT flag.
two. Set TWEN bit
3. Write logic one in the TWSTO bit to send the STOP condition on the SDA and SCL line.
4. Monitor the status of the TWSTO bit, as clearing the TWSO bit 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
 }
So far the slave side data transmission is complete, the MASTER is working in mode one. According to the objective, the data received by MASTER is displayed in PORTB. The flowchart of MASTER as transmitter (mode one) is given below.

Fluxograma do MASTER como transmissor na interface TWI usando AVR

Fig. 6: Flowchart of MASTER as a transmitter on the TWI interface using AVR

From here on, the MASTER would be working in mode two, that is, the MASTER would become a receiver. The AVR TWI operates in mode 2.
Step 6: Send the START condition on the bus lines
This step is so similar to the previous one.
Note : In Step 6, the START condition is sent after the STOP condition. If one more start condition is sent before the intermediate STOP condition, it is called a repeating start condition. The repeating start condition is the same as the START condition, but the only difference is between the commits. For more details on repetitive starting, see the data sheet. If data is sent continuously in the same direction, there is no need for a start condition, repetitive start, or intermediate stop condition. The second data can be transmitted right after receiving acknowledgment of the first byte of data (as shown in the flowchart above).
Step 7: Send slave address and data direction (read) bit and wait for ACK signal
1. Put 8-bit data into TWDR.
8 bits = 7-bit slave address + data direction bit (read = 1).
two. Clear the TWINT flag.
3. Set the TWEN bit to enable TWI.
4. Monitor the status of the TWINT flag to complete data transmission.
5. Check recognition.
 void TWI_read_address(unsigned char data)
 {
   TWDR=date;   // Address and read instructions
   TWCR=(1<<TWINT) (1<<TWEN);   // Clear TWI interrupt flag,Enable TWI
   while (!(TWCR & (1<<TWINT))); // Wait until complete TWDR byte received
   while((TWSR & 0xF8)!= 0x40); // Check for the acknowledgment
 }
Step 8: Read data from the SDA bus
1. Clear TWINT flag
two. Set the TWEN bit, enable TWI
3. Monitor the status of the TWINT flag, as the TIWNT flag set indicates that the value in TWDR has been received.
4. Check recognition. If the master wants to receive the last byte from the slave, the status of the TWSR register will be 0x58. After receiving the last byte, 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. Otherwise, if the master wants to continue receiving more bytes from the slave, the TWSR register status will be 0x50.
To confirm the saving of the last byte, the TWEA bit is used during data transmission. If the TWEA bit is set, reception will be continuous on the MASTER side. And if the TWEA bit is low, the MASTER orders the slave to send the last byte.
5. Get the received data. And send it to PORTB.
 void TWI_read_data(void)
 {
   TWCR=(1<<TWINT) (1<<TWEN);   // Clear TWI interrupt flag,Enable TWI
   while (!(TWCR & (1<<TWINT))); // Wait until complete TWDR byte transmitted
   while((TWSR & 0xF8) != 0x58); // Check for the acknowledgment
   recv_data=TWDR;
   PORTB=recv_data;
 }
Step 9: Send STOP condition
The stop condition has already been explained.

Fluxograma do MASTER como receptor na interface TWI usando AVR

Fig. 7: Flowchart of MASTER as a receiver on the TWI interface using AVR

Code explanation for the SLAVE controller:
Step 1: Initializing the Slave R control
Initialization of the slave controller is done by assigning an address to the slave. The seven-bit slave address is populated in the TWI slave Address Register (TWAR). The LSB of the TWAR, i.e. the TWGCE bit is used to allow the slave to recognize the general call address (0x00).
 void TWI_init_slave(void) // Function to initialize slave
 {
   TWAR=0x20;   // Fill slave address to TWAR
 }
Step 2: Check TWSR registration status
If the value of TWSR is 0x60, it means that the data sent by the master in the next step should be read only by this specific slave and the slave sends back the acknowledgment to the master corresponding to the read operation. If the TWSR status is 0x70, SLAVE is requested to read the data in the general call (0x00). In this phase, the SLAVE acts as a receiver. AVR TWI is operating in mode 3.
1. Clear the TWIN flag.
two. Enable TWI.
3. Configure TEWA to receive recognition.
4. Monitor the status of the TWINT flag.
5. Match TWSR status. If status is 0x60 read data or skip to (1)
void TWI_match_read_slave(void) //Function to match the slave address and slave dirction bit(read)
 {
   while((TWSR & 0xF8)!= 0x60) // Loop until correct acknowledgment has been received
   {
      // Get acknowledgment, Enable TWI, Clear TWI interrupt flag
      TWCR=(1<<TWEA) (1<<TWEN) (1<<TWINT);
      while (!(TWCR & (1<<TWINT))); // Wait for TWINT flag
   }
 }
Step 3: Read the data
Read the data sent by MASTER.
1. Clear the TWINT flag.
two. Enable TWI.
3. Configure TWEA to receive ACK.
4. Get the TWDR data form, display it on PORTB.


 

empty TWI_read_slave(empty)
{
// Clear TWI interrupt flag, get acknowledgment, enable TWI
TWCR= (1< while (!(TWCR & (1< while((TWSR & 0xF8)!=0x80); //Wait for confirmation
recv_data=TWDR; // Gets TWDR value
PORTB=recv_dados; // send the value received in PORTB
}

Escravo como receptor na interface TWI usando AVR
Fig. 8: Slave as receiver on TWI interface using AVR
From here, the slave becomes a transmitter at the request of the master to send data. The AVR TWI operates in mode 4.
Step 4: Check TWSR registration status
If the value of TWSR is 0xA8, it means that the master wants to receive data from the specific slave, and the slave sends back the acknowledgment to the master corresponding to the write operation.
1. Clear the TWIN flag.
two. Enable TWI.
3. Configure TEWA to receive recognition.
4. Monitor the status of the TWINT flag.
5. Match TWSR status. If status is 0xA8 send data or skip to (1)
 void TWI_match_write_slave(void) //Function to match the slave address and slave dirction bit(write)
 {
   while((TWSR & 0xF8)!= 0xA8) // Loop until correct acknowledgment has been received
   {
      // Get acknowledgment, Enable TWI, Clear TWI interrupt flag
      TWCR=(1<<TWEA) (1<<TWEN) (1<<TWINT);
      while (!(TWCR & (1<<TWINT))); // Wait for TWINT flag
   }
 }
Step 5: Write data to the SDA bus
1. Put the data into the TWDR.
two. Enable TWI.
3. Glue the TWINT flag.
4. Monitor the TWINT flag. As it clears, it means the data has been sent.
5. Check ACK. As the TWEA bit was not set when writing data to the SDA bus, it means that the master is the last data to be sent and in turn receives a NOT ACK and the TWSR status becomes 0xC0. And if the TWEA bit was set during data transmission, it receives an ACK and the TWSR status becomes 0xB8. For more details, see the technical sheet.


 

void TWI_write_slave(void) // Function to write data
{
TWDR= write_data; // Fills the TWDR register with the data to be sent
TWCR= (1<
while((TWSR & 0xF8) != 0xC0); //Wait for recognition
}

Escravo como Transmissor e Interface TWI usando AVR

Fig. 9: Slave as Transmitter on TWI Interface using AVR

Project source code

###


 //Program for Master Mode
// Check Code2 for program in slave mode #include #include #include void TWI_start(void); void TWI_repeated_start(void); void TWI_init_master(void); void TWI_write_address(unsigned character); void TWI_read_address(unsigned character); void TWI_write_data(unsigned character); void TWI_read_data(void); void TWI_stop(void); unsigned character address = 0x20, read = 1, write = 0; unsigned character write_data = 0x01, recv_data; int main(empty) { _delay_ms(2000); DDRB=0xff; TWI_init_master ; //Function to initialize TWI while(1) { if(write_data==0x00) write_data=1; TWI_start ; //Function to send start condition TWI_write_address(address+write); //Function to write bit(write) of address and data direction in SDA TWI_write_data(write_data); //Function to write data to the slave TWI_stop ; //Function to send stop condition _delay_ms(10); // 10 millisecond delay TWI_start ; TWI_read_address(address+reading); //Function to write address and data direction bit (read) to SDA TWI_read_data ; //Function to read data from the slave TWI_stop ; _delay_ms(1000); write_data = write_data * 2; } } void TWI_init_master(void) // Function to initialize the master { TWBR=0x01; //Bitrate TWSR=(0< //Setting prescalar bits // SCL Frequency= F_CPU/(16+2(TWBR).4^TWPS) } empty TWI_start(empty) { // Clear TWI interrupt flag, set start condition in SDA, enable TWI TWCR= (1< while(!(TWCR & (1< while((TWSR & 0xF8)!= 0x08); //Check recognition } empty TWI_repeated_start(void) { // Clear TWI interrupt flag, set start condition in SDA, enable TWI TWCR= (1< while(!(TWCR & (1< while((TWSR & 0xF8)!= 0x10); //Check recognition } void TWI_write_address (unsigned character data) { TWDR=data; //Address and write instruction TWCR=(1< while (!(TWCR & (1< while((TWSR & 0xF8)!= 0x18); //Check recognition } void TWI_read_address (unsigned character data) { TWDR=data; // Address and read instruction TWCR=(1< while (!(TWCR & (1< while((TWSR & 0xF8)!= 0x40); //Check recognition } void TWI_write_data (unsigned character data) { TWDR=data; //put data into TWDR TWCR=(1< while (!(TWCR & (1< while((TWSR & 0xF8) != 0x28); //Check recognition } empty TWI_read_data(void) { TWCR=(1< while (!(TWCR & (1< while((TWSR & 0xF8) != 0x58); //Check recognition recv_data=TWDR; PORTB=recv_dados; } empty TWI_stop(empty) { // Clear TWI interrupt flag, place stop condition on SDA, enable TWI TWCR= (1< while(!(TWCR & (1< }

###

Project source code

###



 //Program for slave mode
#include #include void TWI_init_slave(void); void TWI_match_read_slave(void); void TWI_read_slave(void); void TWI_match_write_slave(void); empty TWI_write_slave(void); unsigned character write_data,recv_data; int main(empty) { DDRB=0xff; TWI_init_slave ; //Function to initialize the slave while(1) { TWI_match_read_slave ; //Function to combine slave address and slave direction bit (read) TWI_read_slave ; //Function to read data write_data=~recv_data; // Toggle received data TWI_match_write_slave ; //Function to combine slave address and slave direction bit (write) TWI_write_slave ; //Function to write data } } void TWI_init_slave(void) // Function to initialize the slave { TWAR=0x20; //Fills in the slave address for TWAR } void TWI_write_slave(void) // Function to write data { TWDR= write_data; // Fills the TWDR register with the data to be sent TWCR= (1< while((TWSR & 0xF8) != 0xC0); //Wait for recognition } void TWI_match_write_slave(void) //Function to match slave address and slave direction bit(write) { while((TWSR & 0xF8)!= 0xA8) // Loop until correct acknowledgment is received { // Get acknowledgment, enable TWI, clear TWI interrupt flag TWCR=(1< while (!(TWCR & (1< } } empty TWI_read_slave(empty) { // Clear TWI interrupt flag, get acknowledgment, enable TWI TWCR= (1< while (!(TWCR & (1< //Wait for the TWINT flag while((TWSR & 0xF8)!=0x80); //Wait for confirmation recv_data=TWDR; // Gets TWDR value PORTB=recv_dados; // send the value received in PORTB } void TWI_match_read_slave(void) //Function to match the slave address and the slave direction bit (read) { while((TWSR & 0xF8)!= 0x60) // Loop until correct acknowledgment is received { // Get acknowledgment, enable TWI, clear TWI interrupt flag TWCR=(1< while (!(TWCR & (1< } }

###

Circuit diagrams

Circuit Diagram of How to Use-I2C-TWI-Two-Wire Interface on-AVR-ATmega32

Project Components

  • ATmega32
  • LED

Project video

Back to blog

Leave a comment

Please note, comments need to be approved before they are published.