Пример реализации I2C Slave на AVR микроконтроллере Atmega8
#include #include #include #define I2CSLAVE_ADDR 0x4E #define PORT_DDR 0xB0 // PORTB Settings #define PORT_IN 0xB1 // Get PINB #define PORT_OUT 0xB2 // Set PORTB unsigned char regaddr; // Store the Requested Register Address unsigned char regdata; // Store the Register Address Data void i2c_slave_action(unsigned char rw_status) { switch(regaddr) { // PORT case PORT_DDR: if (rw_status == 0) // read regdata = DDRB; else // write DDRB = regdata; break; case PORT_IN: if (rw_status == 0) // read regdata = PINB; break; case PORT_OUT: if (rw_status == 1) // write PORTB = regdata; break; } } ISR(TWI_vect) { static unsigned char i2c_state; unsigned char twi_status; // Disable Global Interrupt cli(); // Get TWI Status Register, mask the prescaler bits (TWPS1,TWPS0) twi_status=TWSR & 0xF8; switch(twi_status) { case TW_SR_SLA_ACK: // 0x60: SLA+W received, ACK returned i2c_state=0; // Start I2C State for Register Address required break; case TW_SR_DATA_ACK: // 0x80: data received, ACK returned if (i2c_state == 0) { regaddr = TWDR; // Save data to the register address i2c_state = 1; } else { regdata = TWDR; // Save to the register data i2c_state = 2; } break; case TW_SR_STOP: // 0xA0: stop or repeated start condition received while selected if (i2c_state == 2) { i2c_slave_action(1); // Call Write I2C Action (rw_status = 1) i2c_state = 0; // Reset I2C State } break; case TW_ST_SLA_ACK: // 0xA8: SLA+R received, ACK returned case TW_ST_DATA_ACK: // 0xB8: data transmitted, ACK received if (i2c_state == 1) { i2c_slave_action(0); // Call Read I2C Action (rw_status = 0) TWDR = regdata; // Store data in TWDR register i2c_state = 0; // Reset I2C State } break; case TW_ST_DATA_NACK: // 0xC0: data transmitted, NACK received case TW_ST_LAST_DATA: // 0xC8: last data byte transmitted, ACK received case TW_BUS_ERROR: // 0x00: illegal start or stop condition default: i2c_state = 0; // Back to the Begining State } // Clear TWINT Flag TWCR |= (1
Тактовая частота микроконтроллера должен быть не менее 8 MHz.
В этом примере, обращаясь по шине I2C к устройству по адресу указанному в строке:
#define I2CSLAVE_ADDR 0x4E
можно управлять портом B микроконтроллера, используя адреса:
#define PORT_DDR 0xB0 // PORTB Settings #define PORT_IN 0xB1 // Get PINB #define PORT_OUT 0xB2 // Set PORTB
Для программирования порта на вход, следует записать 0x00 по адресу 0xB0. Для получения состояния входов порта B нужно прочитать один байт по адресу 0xB1. Для программирования порта на выход записать 0xFF по адресу 0xB0. Чтобы установить все выходы порта в 1 надо записать 0xFF по адресу 0xB2.
Используя этот пример можно создавать свои специфические I2C Slave устройства на базе AVR микроконтроллеров. Например, модули сенсоров, дисплеев и т.д., интерфейс которых нужно унифицировать.
Успехов!