//============================================================================================
//      eeprom2.c             ver 2.2   (7/21/08)
//
//      copyright (c) 2001-2008 Vaux Electronics, Inc.
//============================================================================================


//============================================================================================
//
//  Public routine init_ext_eeprom() inits external eeprom(s) and loads global ext_eeprom_max_addr.
//  The device type has previously been defined (C32...C512), but more than one socket may be populated.
//  This driver can address a max of 64K.  Using the three chip-select address pins for multiple chips:
//       24C32  (4Kx8)      Max 8 chips for 32Kx8 total
//       24C64  (8Kx8)      Max 8 chips for 64Kx8 total
//       24C128 (16Kx8)     Max 4 chips for 64Kx8 total
//       24C256 (32Kx8)     Max 2 chips for 64Kx8 total
//       24C512 (64Kx8)     Max 1 chip  for 64Kx8 total
//  The test data, 0x58, is arbitrary (must have something to do with 1958).
//  Using the retry write, just to be sure a bus glitch does not give a false answer.
//  It retries the write to each address up to three times before failing (about 60 ms per retry failure).
//  Up to eight retry-writes could fail, for a worst-case init time of 8x60 = 240 ms.
//
//  Public routine s=write_retry_ext_eeprom(addr, data) writes byte data to address addr in ext eeprom.
//  Data is read after write, and if not correct, will retry the write up to three times.
//  Returns PASS for good write, or FAIL for bad write (likely invalid address).
//  Write time approx 3.4 ms (when writes first time) at 400KHz FAST I2C.
//  If trying to write invalid address, each write will time out after ~20 ms, so this
//  will take up to ~60 ms for three retries.
//
//  Public routine s=write_ext_eeprom(addr, data) writes byte data to address addr in ext eeprom.
//  Returns PASS for good write, or FAIL for bad write (likely invalid address).
//  Write time approx 3.2 ms at 400KHz FAST I2C.
//
//  Public routine d=read_ext_eeprom(addr) reads byte data from address addr in ext eeprom.
//  Read time approx 180 us at 400KHz FAST I2C.
//
//  Public routine s=page_write_ext_eeprom(addr) writes a 32-byte page starting at address addr in ext eeprom.
//  Global array eep_buf[32] must be fully loaded prior to call.
//  We use a 32-byte page so it is the same size for all devices (even though some have larger pages),
//  and we don't consume too much ram for the global buffer.
//  Address addr must not be less than 32-bytes from the end of the device's page boundary, else the
//  written data will wrap around to the beginning of the page.  To avoid this (for all devices), just
//  ensure that addr is an integer multiple of 32.
//  Returns PASS for good write, or FAIL for bad write (likely invalid address).
//  Write time approx 4.2 ms at 400KHz FAST I2C.
//
//  Public routine page_read_ext_eeprom(addr) reads a 32-byte page starting at address addr in ext eeprom.
//  Global array eep_buf[32] is fully loaded after call.
//  "Page" size really only applies to writes -- this is just a 32-byte sequential read into the same buffer.
//  Address addr does not have the same page-boundary-wrap-around concern as with writes, but to keep things
//  the same between reads and writes, we should ensure that addr is an integer multiple of 32.
//  Read time approx 1.2 ms at 400KHz FAST I2C.
//
//  Public routine d=test_ext_eeprom(eeprom_max) tests all locations in ext eeprom, from address
//  0 to eeprom_max, returning PASS if test passes, or FAIL if not.  For each location,
//  it saves the data, tests the original location (w/ 0x55 and 0xAA), then restores the data.
//  This is a NON-DESTRUCTIVE test.  Test times:
//       24C32  (4Kx8)      45 sec
//       24C64  (8Kx8)      90 sec  (1.5 min)
//       24C128 (16Kx8)     180 sec (3.0 min)
//       24C256 (32Kx8)     400 sec (6.7 min)
//       24C512 (64Kx8)     800 sec (13.3 min)
//
//  Public routine d=fill_test_ext_eeprom(eeprom_max, random_seed) fills eeprom with psuedo-
//  random test bytes, then reads back and verifies.  This tests all locations in ext eeprom,
//  from address 0 to eeprom_max, returning PASS if test passes, or FAIL if not.  This is a
//  destructive test, but it leaves a predictable psuedo-random sequence, 32K-byte length, in eeprom.
//  When calling this routine, set random_seed to the desired seed value (<0x8000).
//  This is a DESTRUCTIVE test.  Test times:
//       24C32  (4Kx8)      15 sec
//       24C64  (8Kx8)      30 sec
//       24C128 (16Kx8)     60 sec  (1.0 min)
//       24C256 (32Kx8)     130 sec (2.2 min)
//       24C512 (64Kx8)     270 sec (4.5 min)
//
//  Public routine dump_ext_eeprom(start_addr, num_lines) dumps locations in ext eeprom, in
//  lines of 16 bytes, starting at start_addr, for num_lines lines
//  This will dump a max of 32K, so num_lines must be 2048 or less.
//
//
//
//      In main prog:
//
//          #DEFINE         I2C_SDA            PIN_C4      // I2C data line
//          #DEFINE         I2C_SCL            PIN_C3      // I2C clock line
//
//          Select only one size for ext-eeprom:
//          #define         EE24C32            0           //  4Kx8
//          #define         EE24C64            0           //  8Kx8
//          #define         EE24C128           1           // 16Kx8
//          #define         EE24C256           0           // 32Kx8
//          #define         EE24C512           0           // 64Kx8
//
//          #define         DEBUG_EXT_EEPROM1  OFF         // ON to turn on ext-eeprom write/read general debugging
//          #define         DEBUG_EXT_EEPROM1A OFF         // ON to turn on ext-eeprom write-ack/retry debugging
//          #define         DEBUG_EXT_EEPROM2  OFF         // ON to turn on ext-eeprom test general debugging
//          #define         DEBUG_EXT_EEPROM2A OFF         // ON to turn on ext-eeprom test failure-mssg debugging
//          #define         DEBUG_EXT_EEPROM2B OFF         // ON to turn on ext-eeprom test address debugging
//
//          #USE I2C(MASTER,SDA=I2C_SDA, SCL=I2C_SCL, FAST, RESTART_WDT, FORCE_HW)
//          ...
//          #INCLUDE  "eeprom2.c"                          // routines for external eeprom
//          ...
//          void main()
//          {
//            ...
//            init_ext_eeprom();
//            ...
//            write_ext_eeprom(a, d);                      // write byte d to address a
//
//            d = read_ext_eeprom(a);                      // read byte d from address a
//            ...
//          }
//
//============================================================================================
//
//  This driver is for 24Cxxx I2C eeproms from 4Kx8 (24C32) to 64Kx8 (24C512).
//  Several brands (listed below) have been tested;  other brands may be used if the specs are
//  compatible (2.5-to-5V min supply range, 2-byte-address protocol, 400KHz min bus speed, write-delay...)
//  The WP write-protect pin must be held low for writes to be allowed.
//  The chip-select address lines should be 000, or for multiple chips, connected as 000, 001...111.
//  This routine places upper address bits properly in the chip-select address locations, so
//  up to eight chips may be contiguously addressed, but note that the 4Kx8 devices (24C32) can
//  only span up to the first 32K of address space when all eight chips are installed.
//
//  Pinout:
//            1=A0    8=VCC
//            2=A1    7=WP
//            3=A2    6=SCL
//            4=VSS   5=SDA
//
// ----- Overview of 4Kx8 to 64Kx8 eeproms:
//          These are readily-available in listed DIP-8 packages, but are also avail in SIOC.
//          All of these will run from minimum of:  2.5 to 5V, at 400KHz I2C.
//          All have three chip-select pins to allow up to eight parts on the I2C bus.
//          All have Write-Protect pin (high = disabled writes)
//
//          The 16Kx8 parts (24C128) are perhaps the best price/availablilty choice for now (2008).
//
//                DIP-8             Size    Page    Qty           SOIC ***
//    Vendor      Device          (bytes)  (bytes)  100           Device
//
//    Microchip   24LC32A-I/P       4Kx8     32    $0.37          24LC32A-I/SN
//    Atmel       AT24C32C-PU       4Kx8     32    $0.43          AT24C32CN-SH
//    Catalyst    CAT24C32LI-G      4Kx8     32    $0.53          CAT24C32WI
//
//    Microchip   24LC64-I/P        8Kx8     32    $0.43          24LC64-I/SN
//    Atmel       AT24C64C-PU       8Kx8     32    $0.55          AT24C64CN-SH
//    Catalyst    CAT24C64LI-G      8Kx8     32    $0.63          CAT24C64WI
//
//    Microchip   24LC128-I/P       16Kx8    64    $0.63          24LC128-I/SN
//    Atmel       AT24C128B-PU      16Kx8    64    $0.74          AT24C128BN-SH
//    Catalyst    CAT24C128LI-G     16Kx8    64    $0.86          CAT24C128WI
//
//    Microchip   24LC256-I/P       32Kx8    64    $0.94          24LC256-I/SN
//    Atmel       AT24C256B-PU      32Kx8    64    $?             AT24C256BN-SH
//    Catalyst    CAT24C256LI-G     32Kx8    64    $1.30          CAT24C256WI
//
//    Microchip   24LC512-I/P       64Kx8    128   $1.68          (not avail)
//    Atmel       AT24C512B-PU25    64Kx8    128   $1.31          AT24C512BN-SH
//
//       *** This is "narrow" soic, aka "jedec" soic  --  0.150" (3.90mm) body width
//                Don't use the "eiaj" or "soij" parts which have 0.200" (5.28mm) body
//
//
//
// ----- Address range of single chip:
//
//       24C32  (4Kx8)      0x0000 to 0x0FFF
//       24C64  (8Kx8)      0x0000 to 0x1FFF
//       24C128 (16Kx8)     0x0000 to 0x3FFF
//       24C256 (32Kx8)     0x0000 to 0x7FFF
//       24C512 (64Kx8)     0x0000 to 0xFFFF
//
//
// ----- This driver can address a max of 64K.
//       Using the three chip-select address pins for multiple chips:
//
//       24C32  (4Kx8)      Max 8 chips for 32Kx8 total
//       24C64  (8Kx8)      Max 8 chips for 64Kx8 total
//       24C128 (16Kx8)     Max 4 chips for 64Kx8 total
//       24C256 (32Kx8)     Max 2 chips for 64Kx8 total
//       24C512 (64Kx8)     Max 1 chip  for 64Kx8 total
//
//============================================================================================
//
//      eeprom2.c to do:
//          - 
//
//      eeprom2.c             ver 2.2   (7/21/08)
//          - new support for 24C32 (4Kx8) to 24C512 (64Kx8) eeproms
//          - created from eeprom.c (which was written for 24LC16)
//
//============================================================================================


//  ----- prototypes:
void init_ext_eeprom(void);                                       // ext eeprom: init and find max address
int8 write_retry_ext_eeprom(int16 addr, int8 data);               // ext eeprom: write byte data to address addr with retries
int8 write_ext_eeprom(int16 addr, int8 data);                     // ext eeprom: write byte data to address addr
int8 read_ext_eeprom(int16 addr);                                 // ext eeprom: read byte data from address addr
int8 page_write_ext_eeprom(int16 addr);                           // ext eeprom: write 32-byte page starting at address addr
void page_read_ext_eeprom(int16 addr);                            // ext eeprom: read 32-byte page starting at address addr
int8 test_ext_eeprom(int16 eeprom_max);                           // ext eeprom: test eeprom (non-destructive)
int8 fill_test_ext_eeprom(int16 eeprom_max, int16 random_seed);   // ext eeprom: fill/test eeprom (destructive)
void dump_ext_eeprom(int16 start_addr, int16 num_lines);          // ext eeprom: dump eeprom contents


// ----- globals:
int16  ext_eeprom_max_addr;                                       // max address of external eeprom(s)
int8   eep_buf[32];                                               // 32-byte buffer for page write/read
int8   eep_buf_index = 0;                                         // index into eep_buf array


// ----- defines:
#ifndef       PASS
  #define     PASS                        1
  #define     FAIL                        0
#endif


#ifndef       DEBUG_EXT_EEPROM1
  #define     DEBUG_EXT_EEPROM1           OFF
#endif

#ifndef       DEBUG_EXT_EEPROM1A
  #define     DEBUG_EXT_EEPROM1A          OFF
#endif

#ifndef       DEBUG_EXT_EEPROM2
  #define     DEBUG_EXT_EEPROM2           OFF
#endif

#ifndef       DEBUG_EXT_EEPROM2A
  #define     DEBUG_EXT_EEPROM2A          OFF
#endif

#ifndef       DEBUG_EXT_EEPROM2B
  #define     DEBUG_EXT_EEPROM2B          OFF
#endif


//--------------------------------------------------------------------------------------------
//  void init_ext_eeprom(void)                          ext eeprom: init and find max address
//
//  Public routine init_ext_eeprom() inits external eeprom(s) and loads global ext_eeprom_max_addr.
//  The device type has previously been defined (C32...C512), but more than one socket may be populated.
//  This driver can address a max of 64K.  Using the three chip-select address pins for multiple chips:
//       24C32  (4Kx8)      Max 8 chips for 32Kx8 total
//       24C64  (8Kx8)      Max 8 chips for 64Kx8 total
//       24C128 (16Kx8)     Max 4 chips for 64Kx8 total
//       24C256 (32Kx8)     Max 2 chips for 64Kx8 total
//       24C512 (64Kx8)     Max 1 chip  for 64Kx8 total
//  The test data, 0x58, is arbitrary (must have something to do with 1958).
//  Using the retry write, just to be sure a bus glitch does not give a false answer.
//  It retries the write to each address up to three times before failing (about 60 ms per retry failure).
//  Up to eight retry-writes could fail, for a worst-case init time of 8x60 = 240 ms.

void init_ext_eeprom(void)
{
  int16 test_addr;
  int8 d;


  // ----- init i2c lines (if software I2C used):
  //  output_float (I2C_SDA);
  //  output_float (I2C_SCL);

  // ----- just a dummy read for grins:
  d=read_ext_eeprom(0);


  // ----- find top of eeprom memory:
  #if (EE24C32)
  while(1)
  {
    test_addr = 0x7FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x6FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x5FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x4FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x3FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x2FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x1FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x0FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x0000;
    break;
  }
  ext_eeprom_max_addr = test_addr;
  #endif

  #if (EE24C64)
  while(1)
  {
    test_addr = 0xFFFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0xDFFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0xBFFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x9FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x7FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x5FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x3FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x1FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x0000;
    break;
  }
  ext_eeprom_max_addr = test_addr;
  #endif

  #if (EE24C128)
  while(1)
  {
    test_addr = 0xFFFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0xBFFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x7FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x3FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x0000;
    break;
  }
  ext_eeprom_max_addr = test_addr;
  #endif

  #if (EE24C256)
  while(1)
  {
    test_addr = 0xFFFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x7FFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x0000;
    break;
  }
  ext_eeprom_max_addr = test_addr;
  #endif

  #if (EE24C512)
  while(1)
  {
    test_addr = 0xFFFF;
    if (write_retry_ext_eeprom(test_addr, 0x58))
      break;
    test_addr = 0x0000;
    break;
  }
  ext_eeprom_max_addr = test_addr;
  #endif


  #if (DEBUG_EXT_EEPROM1)
    printf(send_sci_tx_byte, "\r\nDEBUG_init_ext_eeprom -- ext_eeprom_max_addr: %LX\r\n", ext_eeprom_max_addr);
  #endif
}


//--------------------------------------------------------------------------------------------
//  int8 write_retry_ext_eeprom(int16 addr, int8 data) ext eeprom: write byte data to address addr with retries
//
//  Public routine s=write_retry_ext_eeprom(addr, data) writes byte data to address addr in ext eeprom.
//  Data is read after write, and if not correct, will retry the write up to three times.
//  Returns PASS for good write, or FAIL for bad write (likely invalid address).
//  Write time approx 3.4 ms (when writes first time) at 400KHz FAST I2C.
//  If trying to write invalid address, each write will time out after ~20 ms, so this
//  will take up to ~60 ms for three retries.

int8 write_retry_ext_eeprom(int16 addr, int8 data)
{
  int8 d, retries=0;


  while(1)
  {
    ++retries;

    d = write_ext_eeprom(addr, data);                    // going to ignore returned failure code here...

    d = read_ext_eeprom(addr);                           // ...since it will show up here anyway

    // ---should exit here on first loop:
    if (d == data)
      return (PASS);

    // --- alert that retries are needed:
    #if (DEBUG_EXT_EEPROM1A)
      printf(send_sci_tx_byte, "\r\nEXT_EEPROM WRITE RETRY %u at addr 0x%LX\r\n", retries, addr);
    #endif

    // --- alert that retries failed:
    if (retries >= 3)
    {
      #if (DEBUG_EXT_EEPROM1A)
        printf(send_sci_tx_byte, "\r\nEXT_EEPROM WRITE at addr 0x%LX RETRY FAILURE!!!\r\n", addr);
      #endif
      return(FAIL);
    }
  }
}


//--------------------------------------------------------------------------------------------
//  int8 write_ext_eeprom(int16 addr, int8 data)       ext eeprom: write byte data to address addr
//
//  Public routine s=write_ext_eeprom(addr, data) writes byte data to address addr in ext eeprom.
//  Returns PASS for good write, or FAIL for bad write (likely invalid address).
//  Write time approx 3.2 ms at 400KHz FAST I2C.

int8 write_ext_eeprom(int16 addr, int8 data)
{
  int8 device_select, addr_hi, ack_status, max_tests;


  restart_wdt();

  // ----- handle all the address bit combinations for the various supported devices:
  //         (only one of the devices should have been defined as non-zero in main)
  #if (EE24C32)
      device_select = 0xA0 | ((int8)(addr>>11) & 0x0E);  // put A14/A13/A12 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x0F;                  // remove A15/14/13/12 from addr_hi
  #endif
  #if (EE24C64)
      device_select = 0xA0 | ((int8)(addr>>12) & 0x0E);  // put A15/A14/A13 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x1F;                  // remove A15/A14/A13 from addr_hi
  #endif
  #if (EE24C128)
      device_select = 0xA0 | ((int8)(addr>>13) & 0x06);  // put 0/A15/A14 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x3F;                  // remove A15/A14 from addr_hi
  #endif
  #if (EE24C256)
      device_select = 0xA0 | ((int8)(addr>>14) & 0x02);  // put 0/0/A15 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x7F;                  // remove A15 from addr_hi
  #endif
  #if (EE24C512)
      device_select = 0xA0;                              // only for chip-select pin addr = 0
      addr_hi = (int8)(addr>>8);
  #endif


  #if (DEBUG_EXT_EEPROM1)
    printf(send_sci_tx_byte, "\r\nWR - a: %LX, a_hi: %X, sel: %X, d: %X", addr, addr_hi, device_select, data);
    delay_ms(2);
  #endif


  i2c_start();
  i2c_write(device_select);                              // this includes chip-select pin addr, if applicable
  i2c_write(addr_hi);                                    // address upper byte
  i2c_write((int8)(addr));                               // address lower byte

  i2c_write(data);

  i2c_stop();


  // ----- Acknowledge-Polling (wait for ack bit indicating that write is complete):
  //           (this is faster than just waiting fixed 5-ms or more delay)
  //           (timeout failure after 20ms )
  max_tests = 0;
  i2c_start();
  ack_status = i2c_write(device_select);                 // write is complete when ACK bit is 0
  while (ack_status == 1)
  {
    i2c_start();
    ack_status = i2c_write(device_select);               // write is complete when ACK bit is 0
    delay_us(100);                                       // 100us x 200 = 20ms max
    if (++max_tests >= 200)
    {
      #if (DEBUG_EXT_EEPROM1A)
        printf(send_sci_tx_byte, "\r\nEXT_EEPROM WRITE ACK FAILURE!!!\r\n");
      #endif

      return(FAIL);
    }

  }
  return (PASS);
}


//--------------------------------------------------------------------------------------------
//  int8 read_ext_eeprom(int16 addr)                    ext eeprom: read byte data from address addr
//
//  Public routine d=read_ext_eeprom(addr) reads byte data from address addr in ext eeprom.
//  Read time approx 180 us at 400KHz FAST I2C.

int8 read_ext_eeprom(int16 addr)
{
  int8 device_select, addr_hi, data;


  restart_wdt();

  // ----- handle all the address bit combinations for the various supported devices:
  //         (only one of the devices should have been defined as non-zero in main)
  #if (EE24C32)
      device_select = 0xA0 | ((int8)(addr>>11) & 0x0E);  // put A14/A13/A12 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x0F;                  // remove A15/14/13/12 from addr_hi
  #endif
  #if (EE24C64)
      device_select = 0xA0 | ((int8)(addr>>12) & 0x0E);  // put A15/A14/A13 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x1F;                  // remove A15/A14/A13 from addr_hi
  #endif
  #if (EE24C128)
      device_select = 0xA0 | ((int8)(addr>>13) & 0x06);  // put 0/A15/A14 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x3F;                  // remove A15/A14 from addr_hi
  #endif
  #if (EE24C256)
      device_select = 0xA0 | ((int8)(addr>>14) & 0x02);  // put 0/0/A15 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x7F;                  // remove A15 from addr_hi
  #endif
  #if (EE24C512)
      device_select = 0xA0;                              // only for chip-select pin addr = 0
      addr_hi = (int8)(addr>>8);
  #endif


  i2c_start();
  i2c_write(device_select);                              // this includes chip-select pin addr, if applicable
  i2c_write(addr_hi);                                    // address upper byte
  i2c_write((int8)(addr));                               // address lower byte

  i2c_start();
  i2c_write(device_select | 0x01);                       // begin read mode

  data = i2c_read(0);                                    // 0 param for no ack, so eeprom stops sending

  i2c_stop();


  #if (DEBUG_EXT_EEPROM1)
    printf(send_sci_tx_byte, "\r\nRD - a: %LX, a_hi: %X, sel: %X, d: %X", addr, addr_hi, device_select, data);
    delay_ms(2);
  #endif

  return(data);
}


//--------------------------------------------------------------------------------------------
//  int8 page_write_ext_eeprom(int16 addr)             ext eeprom: write 32-byte page starting at address addr
//
//  Public routine s=page_write_ext_eeprom(addr) writes a 32-byte page starting at address addr in ext eeprom.
//  Global array eep_buf[32] must be fully loaded prior to call.
//  We use a 32-byte page so it is the same size for all devices (even though some have larger pages),
//  and we don't consume too much ram for the global buffer.
//  Address addr must not be less than 32-bytes from the end of the device's page boundary, else the
//  written data will wrap around to the beginning of the page.  To avoid this (for all devices), just
//  ensure that addr is an integer multiple of 32.
//  Returns PASS for good write, or FAIL for bad write (likely invalid address).
//  Write time approx 4.2 ms at 400KHz FAST I2C.
//
//    24C32       4Kx8     32-byte page
//    24C64       8Kx8     32-byte page
//    24C128     16Kx8     64-byte page
//    24C256     32Kx8     64-byte page
//    24C512     64Kx8    128-byte page

int8 page_write_ext_eeprom(int16 addr)
{
  int8 device_select, addr_hi, ack_status, max_tests;


  // ----- handle all the address bit combinations for the various supported devices:
  //         (only one of the devices should have been defined as non-zero in main)
  #if (EE24C32)
      device_select = 0xA0 | ((int8)(addr>>11) & 0x0E);  // put A14/A13/A12 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x0F;                  // remove A15/14/13/12 from addr_hi
  #endif
  #if (EE24C64)
      device_select = 0xA0 | ((int8)(addr>>12) & 0x0E);  // put A15/A14/A13 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x1F;                  // remove A15/A14/A13 from addr_hi
  #endif
  #if (EE24C128)
      device_select = 0xA0 | ((int8)(addr>>13) & 0x06);  // put 0/A15/A14 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x3F;                  // remove A15/A14 from addr_hi
  #endif
  #if (EE24C256)
      device_select = 0xA0 | ((int8)(addr>>14) & 0x02);  // put 0/0/A15 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x7F;                  // remove A15 from addr_hi
  #endif
  #if (EE24C512)
      device_select = 0xA0;                              // only for chip-select pin addr = 0
      addr_hi = (int8)(addr>>8);
  #endif


  i2c_start();
  i2c_write(device_select);                              // this includes chip-select pin addr, if applicable
  i2c_write(addr_hi);                                    // address upper byte
  i2c_write((int8)(addr));                               // address lower byte

  for (eep_buf_index=0; eep_buf_index<=31; eep_buf_index++)   // write 32-byte page
    i2c_write(eep_buf[eep_buf_index]);

  i2c_stop();


  // ----- Acknowledge-Polling (wait for ack bit indicating that write is complete):
  //           (this is faster than just waiting fixed 5-ms or more delay)
  //           (timeout failure after 20ms )
  restart_wdt();
  max_tests = 0;
  i2c_start();
  ack_status = i2c_write(device_select);                 // write is complete when ACK bit is 0
  while (ack_status == 1)
  {
    i2c_start();
    ack_status = i2c_write(device_select);               // write is complete when ACK bit is 0
    delay_us(100);                                       // 100us x 200 = 20ms max
    if (++max_tests >= 200)
    {
      #if (DEBUG_EXT_EEPROM1A)
        printf(send_sci_tx_byte, "\r\nEXT_EEPROM WRITE ACK FAILURE!!!\r\n");
      #endif

      return(FAIL);
    }

  }
  return (PASS);
}


//--------------------------------------------------------------------------------------------
//  void page_read_ext_eeprom(int16 addr)              ext eeprom: read 32-byte page starting at address addr
//
//  Public routine page_read_ext_eeprom(addr) reads a 32-byte page starting at address addr in ext eeprom.
//  Global array eep_buf[32] is fully loaded after call.
//  "Page" size really only applies to writes -- this is just a 32-byte sequential read into the same buffer.
//  Address addr does not have the same page-boundary-wrap-around concern as with writes, but to keep things
//  the same between reads and writes, we should ensure that addr is an integer multiple of 32.
//  Read time approx 1.2 ms at 400KHz FAST I2C.

void page_read_ext_eeprom(int16 addr)
{
  int8 device_select, addr_hi;


  // ----- handle all the address bit combinations for the various supported devices:
  //         (only one of the devices should have been defined as non-zero in main)
  #if (EE24C32)
      device_select = 0xA0 | ((int8)(addr>>11) & 0x0E);  // put A14/A13/A12 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x0F;                  // remove A15/14/13/12 from addr_hi
  #endif
  #if (EE24C64)
      device_select = 0xA0 | ((int8)(addr>>12) & 0x0E);  // put A15/A14/A13 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x1F;                  // remove A15/A14/A13 from addr_hi
  #endif
  #if (EE24C128)
      device_select = 0xA0 | ((int8)(addr>>13) & 0x06);  // put 0/A15/A14 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x3F;                  // remove A15/A14 from addr_hi
  #endif
  #if (EE24C256)
      device_select = 0xA0 | ((int8)(addr>>14) & 0x02);  // put 0/0/A15 into chip-select pin addr
      addr_hi = (int8)(addr>>8) & 0x7F;                  // remove A15 from addr_hi
  #endif
  #if (EE24C512)
      device_select = 0xA0;                              // only for chip-select pin addr = 0
      addr_hi = (int8)(addr>>8);
  #endif


  i2c_start();
  i2c_write(device_select);                              // this includes chip-select pin addr, if applicable
  i2c_write(addr_hi);                                    // address upper byte
  i2c_write((int8)(addr));                               // address lower byte

  i2c_start();
  i2c_write(device_select | 0x01);                       // begin read mode

  for (eep_buf_index=0; eep_buf_index<=30; eep_buf_index++)   // read 31-bytes
    eep_buf[eep_buf_index]=i2c_read(1);                  // 1 param for ack, so eeprom keeps sending

  eep_buf[eep_buf_index]=i2c_read(0);                    // 32nd byte:  0 param for no ack, so eeprom stops sending

  i2c_stop();
}


//--------------------------------------------------------------------------------------------
//  int8 test_ext_eeprom(int16 eeprom_max)             ext eeprom: test eeprom (non-destructive)
//
//  Public routine d=test_ext_eeprom(eeprom_max) tests all locations in ext eeprom, from address
//  0 to eeprom_max, returning PASS if test passes, or FAIL if not.  For each location,
//  it saves the data, tests the original location (w/ 0x55 and 0xAA), then restores the data.
//  This is a NON-DESTRUCTIVE test.  Test times:
//       24C32  (4Kx8)      45 sec
//       24C64  (8Kx8)      90 sec  (1.5 min)
//       24C128 (16Kx8)     180 sec (3.0 min)
//       24C256 (32Kx8)     400 sec (6.7 min)
//       24C512 (64Kx8)     800 sec (13.3 min)

int8 test_ext_eeprom(int16 eeprom_max)
{
  int8 d;
  int16 a;

  #if (DEBUG_EXT_EEPROM2)
    printf(send_sci_tx_byte, "\r\nDEBUG_test_ext_eeprom to max: %LX\r\n", eeprom_max);
  #endif


  // ----- this odd-looking do-while needed to index a from 0x0000 to max of 0xFFFF:
  //        (by using "for (a=0; a <= eeprom_max; a++)" would need a to be int32, or would wrap to 0 and not end)
  a = 0xFFFF;                                         // will wrap to zero at start of loop
  do
  {
    ++a;

    #if (DEBUG_EXT_EEPROM2B)
      printf(send_sci_tx_byte, "a:%LX  ", a);
      delay_us(400);
    #endif

    restart_wdt();                                    // this loop takes a while


    // --- save original data:
    d = read_ext_eeprom(a);


    // --- write 0x55 test byte to address a:
    if (write_retry_ext_eeprom(a, 0x55) != PASS)      // and verify
    {
      #if (DEBUG_EXT_EEPROM2A)
        printf(send_sci_tx_byte, "\r\nDEBUG_test_ext_eeprom -- FAIL at addr %LX, dat s/b 0x55\r\n\r\n", a);
      #endif
      return(FAIL);
    }

    // --- write 0xAA test byte to address a:
    if (write_retry_ext_eeprom(a, 0xAA) != PASS)      // and verify
    {
      #if (DEBUG_EXT_EEPROM2A)
        printf(send_sci_tx_byte, "\r\nDEBUG_test_ext_eeprom -- FAIL at addr %LX, dat s/b 0xAA\r\n\r\n", a);
      #endif
      return(FAIL);
    }

    // --- restore original data to address a:
    if (write_retry_ext_eeprom(a, d) != PASS)         // and verify
    {
      #if (DEBUG_EXT_EEPROM2A)
        printf(send_sci_tx_byte, "\r\nDEBUG_test_ext_eeprom -- FAIL at addr %LX, orig-dat s/b %X\r\n\r\n", a, d);
      #endif
      return(FAIL);
    }

  } while (a < eeprom_max);

  #if (DEBUG_EXT_EEPROM2)
    printf(send_sci_tx_byte, "\r\nDEBUG_test_ext_eeprom -- PASS\r\n\r\n");
  #endif

  return(PASS);
}


//--------------------------------------------------------------------------------------------
//  int8 fill_test_ext_eeprom(int16 eeprom_max, int16 random_seed)
//                                                    ext eeprom: fill/test eeprom (destructive)
//
//  Public routine d=fill_test_ext_eeprom(eeprom_max, random_seed) fills eeprom with psuedo-
//  random test bytes, then reads back and verifies.  This tests all locations in ext eeprom,
//  from address 0 to eeprom_max, returning PASS if test passes, or FAIL if not.  This is a
//  destructive test, but it leaves a predictable psuedo-random sequence, 32K-byte length, in eeprom.
//  When calling this routine, set random_seed to the desired seed value (<0x8000).
//  This is a DESTRUCTIVE test.  Test times:
//       24C32  (4Kx8)      15 sec
//       24C64  (8Kx8)      30 sec
//       24C128 (16Kx8)     60 sec  (1.0 min)
//       24C256 (32Kx8)     130 sec (2.2 min)
//       24C512 (64Kx8)     270 sec (4.5 min)

int8 fill_test_ext_eeprom(int16 eeprom_max, int16 random_seed)
{
  int8 d, s;
  int16 a;


  init_random_32K_seed(random_seed);                  // seed the random number generator

  #if (DEBUG_EXT_EEPROM2)
    printf(send_sci_tx_byte, "\r\nDEBUG_fill_test_ext_eeprom to addr max: %LX,  with seed: %LX,  ran_int32: %LX\r\n", eeprom_max, random_seed, ran_int32);
  #endif


  // ----- this odd-looking do-while needed to index a from 0x0000 to max of 0xFFFF:
  //        (by using "for (a=0; a <= eeprom_max; a++)" would need a to be int32, or would wrap to 0 and not end)
  a = 0xFFFF;                                         // will wrap to zero at start of loop
  do
  {
    ++a;

    restart_wdt();                                    // this loop takes a while


    // ---get next psuedo-random test byte:
    d = random8_32K();

    #if (DEBUG_EXT_EEPROM2B)
      printf(send_sci_tx_byte, "a:%LX d:%X  ", a, d);
      delay_us(400);
    #endif


    // --- write test byte to address a:
    if (write_retry_ext_eeprom(a, d) != PASS)         // and verify
    {
      #if (DEBUG_EXT_EEPROM2A)
        printf(send_sci_tx_byte, "\r\nDEBUG_fill_test_ext_eeprom -- FAIL at addr %LX, dat s/b %X\r\n\r\n", a, d);
      #endif

      return(FAIL);
    }

  } while (a < eeprom_max);


  #if (DEBUG_EXT_EEPROM2)
    printf(send_sci_tx_byte, "\r\nDEBUG_fill_test_ext_eeprom -- PASS\r\n\r\n");
  #endif

  return(PASS);
}


//--------------------------------------------------------------------------------------------
//  void dump_ext_eeprom(int16 start_addr, int16 num_lines)  ext eeprom: dump eeprom contents
//
//  Public routine dump_ext_eeprom(start_addr, num_lines) dumps locations in ext eeprom, in
//  lines of 16 bytes, starting at start_addr, for num_lines lines
//  This will dump a max of 32K, so num_lines must be 2048 or less.
//
//  eg: dump_ext_eeprom(0,128) = start at addr 0, dump 128 lines x 16 bytes = 2K bytes)
//  eg: dump_ext_eeprom(0,256) = start at addr 0, dump 4K bytes)
//  eg: dump_ext_eeprom(0,512) = start at addr 0, dump 8K bytes)
//  eg: dump_ext_eeprom(0,1024) = start at addr 0, dump 16K bytes)
//  eg: dump_ext_eeprom(0,2048) = start at addr 0, dump 32K bytes)

void dump_ext_eeprom(int16 start_addr, int16 num_lines)
{
  int8  b;
  int16 a, l;


  restart_wdt();

  if (num_lines > 2048)                                      // 2048 max lines
    num_lines = 2048;

  for (l=0; l<num_lines; l++)                                // eg: 128 lines = 2048 total bytes
  {
    delay_ms(100);
    for (b=0; b<=15; b++)                                    // 16 bytes/line
    {
      a = start_addr + l*16 + (int16)(b);

      if (b == 0)
        printf(send_sci_tx_byte, "\r\n%04LX: ", a);

      printf(send_sci_tx_byte, " %X", read_ext_eeprom(a));
    }
  }
  printf(send_sci_tx_byte, "\r\n");
}






