2.9 TWI - Two-Wire Serial Interface

The TWI peripheral allows the systems designer to interconnect up to 128 individually addressable devices using only two bidirectional bus lines: one for clock (SCL) and one for data (SDA). The functionality of the AVR Dx TWI peripheral in Host/Client mode is similar to ATmega128 for basic operations, but the peripheral architecture, register names, and bit order are different. Because of those differences, the software procedures that interact with the TWI peripheral must be updated and tested during the integration phase.

The following code snippets show the usage of the TWI peripheral in Host mode:

megaAVR® - TWI in Host Mode

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

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 acknowledgment */
}

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 acknowledgment */
}

unsigned char TWI_read_data(void)
{
unsigned char recv_data;
    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 acknowledgment */
    recv_data = TWDR;
    return 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 */
}

AVR® Dx - TWI in Host Mode

void TWI0_init()
{
	TWI0.MBAUD = (uint8_t)TWI0_BAUD(100000, 0); /* set MBAUD register */
	TWI0.MCTRLA = 1 << TWI_ENABLE_bp   /* Enable TWI Master: enabled */
	              | 0 << TWI_QCEN_bp   /* Quick Command Enable: disabled */
	              | 0 << TWI_RIEN_bp   /* Read Interrupt Enable: disabled */
	              | 0 << TWI_SMEN_bp   /* Smart Mode Enable: disabled */
	              | TWI_TIMEOUT_DISABLED_gc /* Bus Timeout Disabled */
	              | 0 << TWI_WIEN_bp;  /* Write Interrupt Enable: disabled */
}

void TWI0_start(void)
{
    /* The start condition is generated by hardware, kept for compatibility */
}

static uint8_t TWI0_WaitW(void)
{
    uint8_t state = 0;
    do
    {
        if(TWI0.MSTATUS & (TWI_WIF_bm | TWI_RIF_bm))
        {
            if(!(TWI0.MSTATUS & TWI_RXACK_bm))
            {
                /* slave responded with ack - TWI goes to M1 state */
                state = I2C_ACKED;
            }
            else
            {
                /* address sent but no ack received - TWI goes to M3 state */
                state = I2C_NACKED;
            }
        }
        else if(TWI0.MSTATUS & (TWI_BUSERR_bm | TWI_ARBLOST_bm))
        {
            /* get here only in case of bus error or arbitration lost - M4 state */
            state = I2C_ERROR;
        }
    } while(!state);
    return state;
}

uint8_t TWI0_write_address(uint8_t address)
{
    uint8_t state = 0;
    TWI0.MADDR = address;     /* Transmitting the slave address */
    state = TWI0_WaitW();     /* wait for error code */
    return state;
}

uint8_t TWI0_write_data(uint8_t data)
{
   uint8_t state = 0;
   TWI0.DATA = data;         /* Transmitting the data */
   state = TWI0_WaitW();     /* wait for error code */    
   return state;
}

static uint8_t TWI0_WaitR(void)
{
    uint8_t state = I2C_INIT;
    do
    {
        if(TWI0.MSTATUS & (TWI_WIF_bm | TWI_RIF_bm))
        {
            state = I2C_READY;
        }
        else if(TWI0.MSTATUS & (TWI_BUSERR_bm | TWI_ARBLOST_bm))
        {
            /* get here only in case of bus error or arbitration lost - M4 state */
            state = I2C_ERROR;
        }
    } while(!state);
    return state;
}

uint8_t TWI0_read_data(uint8_t* data, bool last_byte)
{
   uint8_t state;
   state = TWI0_WaitR();
   if(state == I2C_READY) 
      {
           *data = TWI0.DATA;
           if (last_byte) TWI0.MCTRLB = TWI_ACKACT_bm | TWI_MCMD_STOP_gc;
           else TWI0.MCTRLB = TWI_MCMD_RECVTRANS_gc;   
       }
    return state;
}

void TWI0_stop(void)
{
	TWI0.MCTRLB |= TWI_MCMD_STOP_gc;
}