# i2cEeprom.cpp

This sketch talks to a small, inexpensive I2C EEPROM chip. This sketch, unfortunately, is somewhat larger and more involved than many of our 'demo sketches'; but unfortunately, talking I2C is a somewhat larger and more involved process than blinking an LED..

In particular, to do I2C one must inevitably be prepared to dive into the datasheet of whatever particular I2C device one is talking to. The I2C bus protocol itself is only about the 'shape' of communications -- how to take turns talking, when 'words' and 'sentences' end, and so forth -- but specifies basically nothing about the actual 'language' being 'spoken'. In true Tower of Babel style, each I2C device basically speaks its own language, described in its datasheet.

    /*                                             -*- mode:C++ -*-
     *
     * Sketch description: Demo connecting an I2C EEPROM to an IXM.  Count
     *   how many times the button has been pushed since reset, and store
     *   that in an external EEPROM.
     *
     *   EEPROM used: M24C02-WBN6P (ST Microelectronics, 256x8bit, $0.47
     *   qty 1 at mouser.com 3/20/11).  All references to 'sections',
     *   below, refer to sections of the ST M24C02 datasheet.
     *
     *   (And yes, talking to a 2Kb external EEPROM is a little silly,
     *   since the IXM has a bigger EEPROM onboard, but the M24C02-WBN6P
     *   was "I2C demo friendly": Low power, through-hole, and cheap.)
     *
     * Sketch author: Dave Ackley
     *
     * Schematic:
     *  U1: IXM
     *  U2: M24C02-WBN6P.  I2C device code is 0x50 (7 bit, section 3.5)
     *
     * +--------------+                +------+
     * | U1   N    GND|-          +--E0|1    8|Vcc--D2
     * |            D3|-/WC       +--E1|2 U2 7|/WC--D3
     * |     LED    Tx|-SDA       +--E2|3    6|SCL--Rx
     * | W   FACE  12V|-          +-Vss|4    5|SDA--Tx
     * |            Rx|-SCL       |    +------+
     * |            D2|-Vcc       |
     * |      S    GND|-Vss      GND
     * +--------------+
     *
     */

    #define I2C_BLOCK I2C_2
    #define I2C_PINS I2C2_PINS_EAST_TX_RX
    #define EEPROM_POWER_PIN EAST_D2_PIN
    #define EEPROM_WRITE_PROTECT_PIN EAST_D3_PIN
    #define I2C_DEVICE_ADDRESS 0x50

    #define EEPROM_COUNT_ADDRESS 124   // Any multiple of four from 0 to 252 is safe..

    SFBHWI2C i2c(I2C_BLOCK);           // Allocate the I2C control object.
    u8 buf[5];                         // Data buffer; only need 1+4 bytes for this sketch

    // I2C transactions run asynchronously; this function just waits until
    // the status says it's done (or some error state occurred).
    void letI2CFinish() {
      while (i2c.status() > I2C_MODE_DONE) delay(1);
    }

    // Here we do a 'page write' to the EEPROM.  Section 3.6.2 says not to
    // cross a 16 byte boundary in a single write -- and a multiple of
    // four starting address guarantees that (but is somewhat overkill).
    void writeCount(u32 newValue) {
      buf[0] = EEPROM_COUNT_ADDRESS;             // EEPROM address at which to start writing
      for (int i = 0; i<4; ++i)                  // Loop to extract bytes..
        buf[i+1] = (newValue>>(i*8))&0xff;       // ..from the value provided
      i2c.transmit(I2C_DEVICE_ADDRESS, 5, buf);  // Start transmission of addr + four bytes
      letI2CFinish();                            // Wait for I2C transaction to finish
      delay(5);                                  // + another 5ms (worst-case EEPROM write time)
    }

    // Here we do a 'random address sequential read' to the EEPROM
    // (sections 3.7.1 and 3.7.3).  This function uses the 'transceive'
    // method which combines a transmitting phase and a receiving phase
    // into a single I2C transaction.  (If there's only a single I2C bus
    // master, this could also be done with sequential transmit() and
    // receive() calls.)
    u32 readCount() {
      buf[0] = EEPROM_COUNT_ADDRESS;             // EEPROM address to start reading from
      i2c.transceive(I2C_DEVICE_ADDRESS, 1, buf, 4, buf);  // xmt buf[0] then rcv buf[0..3]
      letI2CFinish();                            // Wait for I2C transaction to finish

      u32 count = 0;                             // Place to develop count
      for (int i = 0; i<4; ++i)                  // Scan received data
        count |= buf[i]<<(i*8);                  // Pack bytes back into u32
      return count;                              // Return result
    }

    // Count management functions (with unused optional argument, to be reflexes)
    void resetCount(u8* = 0) {
      writeCount(0);                             // Aw, all those counts, forgotten..
      logNormal("Button push count reset\n");    // Announce we did it
    }
    void incrementCount(u8* = 0) {
      u32 count = readCount()+1;                 // Get the count, bump it
      writeCount(count);                         // Store it
      logNormal("Button pushes = %d\n",count);   // Report it
    }

    void setup() {
      pinMode(EEPROM_POWER_PIN, OUTPUT);          // U2 draws <=3mA, so we power it via a pin
      pinMode(EEPROM_WRITE_PROTECT_PIN, OUTPUT);  // Write protect feature, not used in this..
      digitalWrite(EEPROM_WRITE_PROTECT_PIN, LOW);// ..sketch; we allow all EEPROM writes

      i2c.init(I2C_PINS,400);                     // Init I2C at 400KHz

      digitalWrite(EEPROM_POWER_PIN, LOW);        // Power down the EEPROM for good measure
      delay(10);                                  // Wait a bit
      digitalWrite(EEPROM_POWER_PIN, HIGH);       // Power it up

      Body.reflex('r',resetCount);                // Set up reflexes
      Body.reflex('i',incrementCount);
    }

    void loop() {
      if (buttonDown()) {                         // Wait for button push
        incrementCount();                         // Log it
        while (buttonDown()) delay(10);           // Wait for button release
      }
      ledSet(BODY_RGB_BLUE_PIN, millis()&0x480);  // Blink V for victory (in morse code..)
    }

    #define SFB_SKETCH_CREATOR_ID B36_4(d,a,v,e)
    #define SFB_SKETCH_PROGRAM_ID B36_5(i,2,c,e,e)
    #define SFB_SKETCH_COPYRIGHT_NOTICE "2011 Dave Ackley Placed in the public domain"