User Tools

Site Tools


Example of I2C slave on AVR

I can not just take and leave two boards without communication. =)

In this example is implemented a machine for storing/reading 16 registers.

i2cslv.c
#include <avr/io.h>
#include <compat/twi.h>
#include <avr/interrupt.h>
#include <util/delay.h>
 
/*
    Usage example, on master side.
 
    Write to slave device:
 
    #define SL_ADDR 0x43
    uint8_t regaddr = 0x01;
    uint8_t regdata = 0x78;
    i2c_start_wait((SL_ADDR << 1) | I2C_WRITE);
    data = i2c_write(regaddr);
    data = i2c_write(regdata);
    i2c_stop();
 
    Read from slave:
 
    #define SL_ADDR 0x43
    uint8_t regdaddr = 0x01;
    uint8_t regdata = 0;
    i2c_start_wait((SL_ADDR << 1) | I2C_WRITE));
    i2c_write(regaddr);
    i2c_rep_start((SL_ADDR << 1) | I2C_READ);
    regdata = i2c_readNak();
    i2c_stop();
 
 */
 
#define I2CSLAVE_ADDR 0x43
 
#define PORT_DDR 0xB0         /* PORTB Settings */
#define PORT_IN  0xB1         /* Get PINB */
#define PORT_OUT 0xB2         /* Set PORTB */
 
#define REG_ARR_SIZE 16
 
uint8_t regaddr = 0;         /* Store the Requested Register Address */
uint8_t regdata[REG_ARR_SIZE];         /* Store the Register Address Data */
volatile uint8_t state;
 
#define ST_NONE_RCVD    0
#define ST_ADDR_RCVD    1
#define ST_DATA_RCVD    2
 
int8_t regdata_get(int8_t *regdata, uint8_t regdata_size, uint8_t addr) {
    if (addr < regdata_size) {
        return regdata[addr];  /* Direct mapping received address to array index */
    }
}
 
void regdata_put(int8_t *regdata, uint8_t regdata_size, uint8_t addr, int8_t data) {
    if (addr < regdata_size) {
        regdata[addr] = data + 2; /* Some operation with received data, for example increment */
    }
}
 
 
ISR(TWI_vect) {
    uint8_t twi_status;
 
    /* Disable Global Interrupt */
    cli();
 
    /* Get TWI Status Register, mask the prescaler bits (TWPS1,TWPS0) */
    twi_status = TWSR & 0xF8;
 
    int8_t data;
 
    switch (twi_status) {
        case TW_SR_SLA_ACK:         /* 0x60: SLA+W received, ACK returned */
            break;
 
        case TW_SR_DATA_ACK:        /* 0x80: data received, ACK returned */
            switch (state) {
                case ST_NONE_RCVD:
                    regaddr = TWDR;
                    state = ST_ADDR_RCVD;
                    break;
                case ST_ADDR_RCVD:
                    data = TWDR;
                    regdata_put(regdata, sizeof(regdata), regaddr, data);
                    state = ST_NONE_RCVD;
                    break;
            }
            break;
 
        case TW_ST_SLA_ACK:        /* 0xA8: SLA+R received, ACK returned */
            if (state == ST_ADDR_RCVD) {
                TWDR = regdata_get(regdata, sizeof(regdata), regaddr);
                state = ST_NONE_RCVD;
            }
            break;
 
 
        case TW_BUS_ERROR:         /* 0x00: illegal start or stop condition */
            state = ST_NONE_RCVD;
            break;
 
        case TW_SR_STOP:           /* 0xA0: stop or repeated start condition received while selected */
        case TW_ST_DATA_ACK:       /* 0xB8: data transmitted, ACK received */
        case TW_ST_DATA_NACK:      /* 0xC0: data transmitted, NACK received */
        case TW_ST_LAST_DATA:      /* 0xC8: last data byte transmitted, ACK received */
        default:
            break;
    }
    /* Clear TWINT Flag */
    TWCR |= (1 << TWINT);
    /* Enable global Interrupt */
    sei();
}
 
 
void i2cs_init (void) {
    /* TWI Pull UP */
    PORTC |= ((1 << PINC4) | (1 << PINC5));
 
    /* Initial I2C Slave */
    TWAR = (I2CSLAVE_ADDR << 1) & 0xFE;         /* Set I2C Address, Ignore I2C General Address 0x00 */
    TWDR = 0x00;                                /* Default Initial Value */
 
    /* Start Slave Listening: Clear TWINT Flag, Enable ACK, Enable TWI, TWI Interrupt Enable */
    TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWEN) | (1 << TWIE);
}
 
 
int main(void) {
 
    i2cs_init();
    sei();
 
    while (1) {
        _delay_ms(10);
    }
}
/* EOF */

First PagePrevious PageBack to overviewNext PageLast Page