// I2C Demo for Reading and Writing an I2C real time clock (DS1337)
// This uses the Bit Router Engine to route IO to the I2C bus pins.
// To use different pins for I2C, change the defines.
// To use GPIO0 port instead of GPIO1 change all GPIO1 to GPIO0
//
// Assumptions:
//  - only 1 chip is connected to the I2C bus and VS1000 is master.
//  - no multi-byte read capability is needed
//  - both SDA and SCL pins are in GPIO1 port (can be changed to GPIO0)
//
// The result is quite compact code size:
//
// Function I2cInit             size: 38 words
// Function MyBitDelayFunction  size: 14 words
// Function I2cPutOctetAndAck   size: 36 words
// Function I2cGetOctetAndNack  size: 44 words
// Function RTCReadValue        size: 56 words
// Function RTCWriteValue       size: 35 words


#include <vs1000.h>
#include <stdio.h>

// putchar for console debug output
__y const char hex[] = "0123456789abcdef";
void puthex(u_int16 a) {
  char tmp[8];
  tmp[0] = '0';              tmp[1] = 'x';
  tmp[2] = hex[(a>>12)&15];  tmp[3] = hex[(a>>8)&15];
  tmp[4] = hex[(a>>4)&15];   tmp[5] = hex[(a>>0)&15];
  tmp[6] = ' ';
  tmp[7] = '\0';
  fputs(tmp, stdout);
}


// I2C Device Address (address 0xD0 is for RTC chip DS1337)
#define I2C_DEVICE_WRITE_ADDRESS 0xD0
#define I2C_DEVICE_READ_ADDRESS (I2C_DEVICE_WRITE_ADDRESS + 1)

// I2C Pin Mappings: I2C pins are assumed to be in GPIO1.
#define SDA_PIN_GPIO1 3 /* SDA is GPIO1[3] */
#define SCL_PIN_GPIO1 1 /* SCL is GPIO1[1] */

// GPIO1 Bit Router Engine is used to map I2C pins.
// Engine 0 is for SCL, Engine 1 is for SDA
#define SCL_ENGINE GPIO1_BIT_ENG0
#define SDA_ENGINE GPIO1_BIT_ENG1
// Route SCL to LSB of engine 0
// Route SDA to bit 7 of engine 1
#define BIT_CONF_I2C (/*PIN*/SCL_PIN_GPIO1 << 0) | (/*to BIT*/0 << 4) | \
                     (/*PIN*/SDA_PIN_GPIO1 << 8) | (/*to BIT*/7 << 12)

u_int16 i2c_ddr1_save;
u_int16 i2c_mode1_save;
/** Save initial state of and set up the I2C pins 
 Warning: the bit router engine state is not saved (assume only i2c uses router engine)*/
void I2cInit(){
  // Setup Bit Engine Routing
  PERIP(GPIO1_BIT_CONF) = BIT_CONF_I2C;     
  // Set I2C pins to be outputs and save initial state
  i2c_ddr1_save = PERIP(GPIO1_DDR);
  PERIP(GPIO1_DDR) |= (1 << (SCL_PIN_GPIO1)) | (1 << (SDA_PIN_GPIO1));
  // Set I2C pins to be GPIO controlled and save initial state
  i2c_mode1_save = PERIP(GPIO1_MODE);
  PERIP(GPIO1_MODE) &= ~((1 << (SCL_PIN_GPIO1)) | (1 << (SDA_PIN_GPIO1)));
  // Bring I2C to default state (high-high)
  PERIP(SCL_ENGINE) = -1;
  PERIP(SDA_ENGINE) = -1;
 
}

/** Restore the original condition of the I2C pins */
#define I2cRelease() {PERIP(GPIO1_DDR) = i2c_ddr1_save;PERIP(GPIO1_MODE) = i2c_mode1_save;}

void MyBitDelayFunction(){
  SpiDelay(1000); /* SpiDelay(u_int16 cycles) is ROM function */
}
#define I2C_BIT_DELAY() {MyBitDelayFunction();}
//#define I2C_BIT_DELAY() {BusyWait10();} /*would save 14 words but is very slow*/

#define I2C_CLOCK_HIGH() {PERIP(SCL_ENGINE) = -1; I2C_BIT_DELAY();}
#define I2C_CLOCK_LOW()  {PERIP(SCL_ENGINE) = 0;  I2C_BIT_DELAY();}
#define I2C_DATA_HIGH()  {PERIP(SDA_ENGINE) = -1; I2C_BIT_DELAY();}
#define I2C_DATA_LOW()   {PERIP(SDA_ENGINE) = 0;  I2C_BIT_DELAY();}

//START assumes that the bus is in idle state so don't use it for "repeated start" signal
#define I2C_START() {I2C_DATA_LOW(); I2C_CLOCK_LOW();}
#define I2C_STOP()  {I2C_DATA_LOW(); I2C_CLOCK_HIGH(); I2C_DATA_HIGH();}

#define I2C_RELEASE_SDA() {PERIP(GPIO1_DDR) &= ~(1 << SDA_PIN_GPIO1);}
#define I2C_DRIVE_SDA()   {PERIP(GPIO1_DDR) |=  (1 << SDA_PIN_GPIO1);} 


// Clock out 8 bits from c and one extra "0" for Ack. 
// During the extra "0" the device issues acknowledge, which we don't check.
void I2cPutOctetAndAck(register u_int16 c){
  register u_int16 i;
  I2C_DRIVE_SDA();
  for (i=0; i<9; i++){
    PERIP(SDA_ENGINE) = c;
    I2C_CLOCK_HIGH();
    I2C_CLOCK_LOW();
    c <<= 1;    
  }
}

// Clock in 8 bits and one extra bit. This extra bit is a clock for the
// acknowledge bit, which we don't generate (the SDA line is input inside the
// for loop so the pullup resistor generates "1" state. Thic signals to the
// I2C device that this is the last byte in the transfer. 
u_int16 I2cGetOctetAndNack(){
  register u_int16 result = 0;
  register u_int16 i;
  I2C_RELEASE_SDA();
  for (i=0; i<9; i++){ //change "9" to "8" to not generate ack clock
    I2C_CLOCK_HIGH();
    result <<= 1;
    result |= PERIP(SDA_ENGINE);
    I2C_CLOCK_LOW();
  }
  I2C_DRIVE_SDA();
   return result >> 8; //change "8" to "7" if ack clock is not generated
}


#if 0 // This function is not needed
u_int16 I2cGetAck() {
  register u_int16 i;
  // Release SDA driver for receiving the ACK from device
  I2C_RELEASE_SDA();
  I2C_CLOCK_HIGH();
  i = PERIP(SDA_ENGINE);
  I2C_CLOCK_LOW();
  I2C_DRIVE_SDA();
  return i; //zero for ack, nonzero for nack
}
#endif


// Read a value from the DS1337 data array
auto u_int16 RTCReadValue(u_int16 address){
  // Set Device data pointer to "address"
  I2C_START();
  I2cPutOctetAndAck(I2C_DEVICE_WRITE_ADDRESS);
  I2cPutOctetAndAck(address);/*Address*/
  I2C_STOP();//Don't remove this stop

  // Get data from device
  I2C_START();
  I2cPutOctetAndAck(I2C_DEVICE_READ_ADDRESS);
  {
    u_int16 result = I2cGetOctetAndNack();
    I2C_STOP();    
    return result;
  }
}

// Write a value to the DS1337 data array
u_int16 RTCWriteValue(register __i0 u_int16 address, register __i1 u_int16 databyte){
  // using manually assigned registers saves one word of code RAM
  I2C_START();
  I2cPutOctetAndAck(I2C_DEVICE_WRITE_ADDRESS);
  I2cPutOctetAndAck(address);
  I2cPutOctetAndAck(databyte);
  I2C_STOP();
}



void main(void) {

  I2cInit();
  RTCWriteValue(0,0x42); // set seconds counter to 0x42.

  while(1) {
    puthex(RTCReadValue(0));puts("=rtc");
  }
}
