//********************* Accessing external I2C-EEPROM ********************* #include #include #include "sbit.h" #define SSDA PORT_B0 #define SSDA_in PIN_B0 #define SSDA_oe DDR_B0 #define SSCL PORT_B1 #define SSCL_oe DDR_B1 #define PAGE_SIZE 128 #define EEPROM_ADDR 0xA0 // A2,A1,A0 = GND #define i2c_delay() _delay_us(2.5 / 2) // 2.5us = 400kHz #define ssda_lo() SSDA_oe = 1 #define ssda_hi() SSDA_oe = 0 #define sscl_lo() SSCL = 0 #define sscl_hi() SSCL = 1 static uint16_t si2c_rw(uint16_t val) // line state: SDA = x, SCL = 0 { for (uint8_t i = 9; i; i--) // data + ACK { if (val & 0x8000) ssda_hi(); else ssda_lo(); i2c_delay(); sscl_hi(); i2c_delay(); val <<= 1; if (SSDA_in) val |= 0x80; sscl_lo(); } return val; } static uint8_t si2c_r(uint8_t nack) // input NACK { if (nack) nack = 0x80; // set bit 7 return si2c_rw(0xFF00 | nack) >> 8; // return data } static uint8_t si2c_w(uint8_t val) // input data { return si2c_rw(0x00FF | (val << 8)) & 0xFF; // return NACK } static uint8_t si2c_start_w(uint8_t val) // line state: SDA = 1, SCL = x { SSCL_oe = 1; SSDA = 0; SSDA_oe = 0; sscl_hi(); i2c_delay(); ssda_lo(); i2c_delay(); sscl_lo(); return si2c_w( val ); } static void si2c_stop(void) // line state: SDA = 1, SCL = 0 { ssda_lo(); i2c_delay(); sscl_hi(); i2c_delay(); ssda_hi(); } static uint8_t set_addr(uint16_t addr) { uint8_t i; for ( i = 255; i; i-- ) { if ( !si2c_start_w( EEPROM_ADDR )) // 0 = ACK received { si2c_w( addr >> 8 ); // address high byte si2c_w( addr ); // address low byte break; } _delay_us( 40 ); // timeout: 10.2ms si2c_stop(); } return i; // 0 = timeout } bool eeprom_wr( uint16_t eeaddr, uint8_t* sram, uint16_t len ) { do { if ( !set_addr( eeaddr )) return false; do si2c_w( *sram++ ); while ( --len && (++eeaddr & (PAGE_SIZE - 1))); // end or next page si2c_stop(); } while ( len ); // end return true; } bool eeprom_rd( uint16_t eeaddr, uint8_t* sram, uint16_t len ) { if ( !set_addr( eeaddr )) return false; si2c_start_w( EEPROM_ADDR + 1 ); // read mode do *sram++ = si2c_r( !--len ); // NACK on last byte while ( len ); si2c_stop(); return true; }