Establecer una comunicación TWI/I2C adecuada entre un par de Atmega32

Soy ingeniero mecánico con ganas de aprender cosas de electrónica.

Estoy trabajando estos días en aprender la comunicación TWI(I2C) en Atmega32. Con mucha búsqueda en Internet he logrado obtener un tutorial que explica cómo puedo hacer una comunicación mutua entre un par de chips Atmega32.

Aquí está el código del chip maestro.

#include<avr/io.h>
#include<util/delay.h>
#include<inttypes.h>

void TWI_start(void);
void TWI_repeated_start(void);
void TWI_init_master(void);
void TWI_write_address(unsigned char);
void TWI_read_address(unsigned char);
void TWI_write_data(unsigned char);
void TWI_read_data(void);
void TWI_stop(void);

unsigned char address=0x20, read=1, write=0;
unsigned char write_data=0x01, recv_data;

int main(void)
{
    _delay_ms(2000);
    DDRB=0xff;
    TWI_init_master();   // Function to initialize TWI
    while(1)
    {
        if(write_data==0x00)    
        write_data=1;

            // Function to send start condition
        TWI_start();
        // Function to write address and data direction bit(write) on SDA
        TWI_write_address(address+write);
        // Function to write data in slave
        TWI_write_data(write_data);
        // Function to send stop condition
        TWI_stop();


        _delay_ms(10);  // Delay of 10 mili second

        TWI_start();    
        // Function to write address and data direction bit(read) on SDA
        TWI_read_address(address+read); 
        TWI_read_data();
        // Function to read data from slave 
        TWI_stop();

        _delay_ms(1000);    

        write_data = write_data * 2;
    }


}

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)
}

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
}

void TWI_repeated_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 restart condition is transmitted
    while((TWSR & 0xF8)!= 0x10); // Check for the acknowledgement
}

void TWI_write_address(unsigned char data)
{
    TWDR=data;  // Address and write instruction
    TWCR=(1<<TWINT)|(1<<TWEN);    // Clear TWI interrupt flag,Enable TWI
    while (!(TWCR & (1<<TWINT))); // Wait till complete TWDR byte transmitted
    while((TWSR & 0xF8)!= 0x18);  // Check for the 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 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 acknowledgement
}

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 acknowledgement
    recv_data=TWDR;
    PORTB=recv_data;
}

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
}

Mientras que el código del chip Slave es:

#include<avr/io.h>
#include<util/delay.h>

void TWI_init_slave(void);
void TWI_match_read_slave(void);
void TWI_read_slave(void);
void TWI_match_write_slave(void);
void TWI_write_slave(void);

unsigned char write_data,recv_data;

int main(void)
{
    DDRB=0xff;
    TWI_init_slave(); // Function to initialize slave
    while(1)
    {
        //Function to match the slave address and slave direction bit(read) 
        TWI_match_read_slave(); 
        // Function to read data
        TWI_read_slave();

        // Togglem the receive data
        write_data=~recv_data;

        //Function to match the slave address and slave direction bit(write) 
        TWI_match_write_slave();
        // Function to write data
        TWI_write_slave();
    }
}

// Function to initialize slave
void TWI_init_slave(void)
{
    // Fill slave address to TWAR
    TWAR=0x20;
}

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

// Function to match the slave address and slave 
void TWI_match_write_slave(void)dirction bit(write) 
{
    // Loop till correct acknowledgement have been received
    while((TWSR & 0xF8)!= 0xA8)
    {
        // Get acknowledgment, Enable TWI, Clear TWI interrupt flag
        TWCR=(1<<TWEA)|(1<<TWEN)|(1<<TWINT);    
        while (!(TWCR & (1<<TWINT)));  // Wait for TWINT flag
    }
}

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

//Function to match the slave address and slave direction bit(read)
void TWI_match_read_slave(void)
{
    // Loop till correct acknoledgement have been received
    while((TWSR & 0xF8)!= 0x60)
    {
        // Get acknowlegement, Enable TWI, Clear TWI interrupt flag
        TWCR=(1<<TWEA)|(1<<TWEN)|(1<<TWINT);    
        // Wait for TWINT flag
        while (!(TWCR & (1<<TWINT))); 
    }
}

El código funciona bien en simulación para la primera fase de MasterTransmitter-SlaveReceiver.

Sin embargo, para la segunda fase de MasterReceiver-SlaveTransmitter, la simulación parece no mostrar ninguna acción e incluso no pasa al modo MasterTransmitter-SlaveReceiver.

Respuestas (5)

La función TWI_match_read_slave()me parece mal. Debe habilitar TWAR y TWCR antes de verificar si el esclavo se ha autodireccionado.

ingrese la descripción de la imagen aquí

void TWI_slave_Rx_init()
{
    TWBR=1;         
    //Setting up the SCL frequency by writing a value in TWBR
    TWSR|=(0<<TWPS1)|(0<<TWPS0);    
    //Fscl=     Fcpu/(16+2*(TWBR)*(4^TWPS))
    TWAR=0b00000010;        
    //The first seven bits indicate the slave address 
    TWCR|=(1<<TWINT);
    TWCR|=(1<<TWEN)|(1<<TWEA)|(0<<TWSTA)|(0<<TWSTO);    
    //Enabling Acknowledge function
    while (!(TWCR & (1<<TWINT)));   
    //Wait for the interrupt to be cleared as it will indicate the successful reception
    while(TWCR&(0xF8)!=(0x60)); 
    //Checking if the self-address+W has been received and ACK has been sent

}

Así es como debería ser, ya que configurar TWEA solo no significa que haya enviado el bit de reconocimiento.

La configuración de TWEA generará un pulso de reconocimiento SÓLO SI se cumplen las siguientes condiciones:

ingrese la descripción de la imagen aquí

Esta respuesta sería aún mejor si tuviera referencias . ¿Podrías agregar algunos?
Todo está en la hoja de datos de ATmega16A. Nº de página 196 y 180 respectivamente para las imágenes. El código fue escrito por mí, pero aún no se ha probado. Actualizaré el comentario tan pronto como lo revise. Soy un novato en lo que respecta a I2C y estoy escribiendo una biblioteca para lo mismo.
Cuando haya verificado, edite su respuesta para incluir el resultado, y también incluya un enlace a la hoja de datos a la que se refiere. Después de eso, podemos eliminar estos comentarios.
electronicsdatasheets.com/pdf-datasheets/atmel/ATmega16A La hoja de datos de ATmega16A está aquí para descargar.
¡Lo siento! Las páginas núms. son 192 y 180

Use su DMM y verifique el nivel de voltaje en SDA y SCL. Por lo general, si se atasca, se debe a que el esclavo se perdió y aún mantiene el SDA bajo.

En el archivo Slave: no estoy seguro, pero ambas variables write_data, mrecv_data no están inicializadas, puede ser que ese sea tu problema. En el archivo maestro: recvdata tampoco está inicializado

Gracias por la ayuda, sin embargo, como creo, el compilador establece las variables declaradas no inicializadas en el valor de 0x00. Para asegurarme, inicialicé todas las variables declaradas al valor de 0x00, pero obtengo el mismo comportamiento de salida inesperado.
@DiaaAbidou: probablemente sea una buena idea nunca asumir que las variables no inicializadas tienen un valor específico, a menos que la documentación del compilador lo especifique explícitamente. Sin embargo, parece que no es un problema aquí.

Cambié la función de parada en el maestro y resuelvo el problema:

void TWI_Stop(void)
{
   TWCR=(1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
}

y compruebo este programa en proteus funciona sin problema.

¿Soy el único que ve esta línea?

void TWI_match_write_slave(void)dirction bit(write)

debiera ser

void TWI_match_write_slave(void)

Disculpe mi entumecimiento, pero no entiendo el resto del código en esa línea, se agradece cualquier ayuda