/* -------------  Real Time Clock and 56 byte NVRAM ------------- */ 

#include <intrins.h>
#include "ds1307.h"
#include "display.h"
#include "serial.h"

sbit SDA = 0xE9; /* P4.1 DS1307 serial data */
sbit SCL = 0xEA; /* P4.2 DS1307 serial clock */

#define NOP5() _nop_();_nop_();_nop_();_nop_();_nop_()

#define SCL_HIGH(errmsg) {unsigned char i = 10; SCL = 1;  while(!SCL)\
  if(i-- == 0) halt(13, errmsg);}


bit
ds1307_send(unsigned char value)
{
  unsigned char i = 8;
  bit ack;
  while(i--)
    {
      SDA = value & 0x80 ? 1 : 0;
      value = value << 1;
      SCL_HIGH("SCL ds1307_send()\n#1");
      NOP5();
      SCL = 0;
      NOP5();
    }
  SDA = 1;
  SCL_HIGH("SCL ds1307_send()\n#2");
  ack = SDA;
  SCL = 0;
  return ack;
}

unsigned char
ds1307_receive(bit ack)
{
  unsigned char buf, i = 8;
  SDA = 1;
  NOP5();
  while(i--)
    {
      SCL_HIGH("ds1307_receive()\n#1");
      NOP5();
      buf = buf << 1 | SDA;
      SCL = 0;
      NOP5();
    }
  SDA = ack; /* ack */
  SCL_HIGH("ds1307_receive()\n#2");
  NOP5();
  SCL = 0;
  return buf;
}

void
ds1307_start()
{
  SDA = 1;
  SCL_HIGH("ds1307_start()\n#1");
  if(!SDA)
    halt(14, "ds1307_start()\n#2 SDA fault");
  SDA = 0;
  NOP5();
  SCL = 0;
}

void
ds1307_stop()
{
  SDA = 0;
  SCL_HIGH("ds1307_stop()\nSCL fault");
  NOP5();
  SDA = 1;
}

unsigned char
ds1307_get_byte(unsigned char num)
{
  ds1307_start();

  if(ds1307_send(0xD0))
    halt(23, "DS1307 no ACK\non writing");

  if(ds1307_send(num))
    halt(24, "DS1307 no ACK\non writing");

  ds1307_stop();

  NOP5();

  ds1307_start();

  if(ds1307_send(0xD1))
    halt(25, "DS1307 no ACK\non reading");


  return ds1307_receive(1);
 
  ds1307_stop();

}

void
ds1307_set_byte(unsigned char addr, unsigned char value)
{
  ds1307_start();
  if(ds1307_send(0xD0))
    halt(23, "DS1307 no ACK\in set_byte() #1");

  if(ds1307_send(addr))
    halt(23, "DS1307 no ACK\in set_byte() #2");

  if(ds1307_send(value))
    halt(23, "DS1307 no ACK\in set_byte() #3");
 
  ds1307_stop();
}

void
ds1307_set_ulong(unsigned char addr, unsigned long value)
{
  unsigned char i = 4;

  ds1307_start();
  if(ds1307_send(0xD0))
    halt(23, "DS1307 no ACK\in set_ulong() #1");

  if(ds1307_send(addr))
    halt(23, "DS1307 no ACK\in set_ulong() #2");

  while (i--)
    {
      if(ds1307_send(value & 0xFFUL))
        halt(23, "DS1307 no ACK\in set_ulong() #3");
      value >>= 8;
    }
 
  ds1307_stop();
}

unsigned long
ds1307_get_ulong(unsigned char addr)
{
  unsigned long value;

  ds1307_start();

  if(ds1307_send(0xD0))
    halt(23, "DS1307 no ACK\non writing");

  if(ds1307_send(addr))
    halt(24, "DS1307 no ACK\non writing");

  ds1307_stop();

  NOP5();

  ds1307_start();

  if(ds1307_send(0xD1))
    halt(25, "DS1307 no ACK\non reading");

  value = ds1307_receive(0);
  value |= (unsigned long)ds1307_receive(0) << 8 ;
  value |= (unsigned long)ds1307_receive(0) << 16 ;
  value |= (unsigned long)ds1307_receive(1) << 24 ;

  ds1307_stop();

  return value;
}


void
ds1307_set_int(unsigned char addr, int value)
{
  unsigned char i = 2;

  ds1307_start();
  if(ds1307_send(0xD0))
    halt(23, "DS1307 no ACK\in set_int() #1");

  if(ds1307_send(addr))
    halt(23, "DS1307 no ACK\in set_int() #2");

  while (i--)
    {
      if(ds1307_send(value & 0xFFUL))
        halt(23, "DS1307 no ACK\in set_int() #3");
      value >>= 8;
    }
 
  ds1307_stop();
}


int
ds1307_get_int(unsigned char addr)
{
  int value;

  ds1307_start();

  if(ds1307_send(0xD0))
    halt(23, "DS1307 no ACK\non writing");

  if(ds1307_send(addr))
    halt(24, "DS1307 no ACK\non writing");

  ds1307_stop();

  NOP5();

  ds1307_start();

  if(ds1307_send(0xD1))
    halt(25, "DS1307 no ACK\non reading");

  value = ds1307_receive(0);
  value |= (unsigned long)ds1307_receive(1) << 8 ;
  ds1307_stop();

  return value;
}


void
ds1307_init()
{
  unsigned char tmp;
  ds1307_receive(1); /* clear the buffer */
  SCL_HIGH("ds1307_init()\nSCL FAULT #1");
  NOP5();
  SDA = 1;
  NOP5();

  if (!SDA)
    halt(12, "ds1307_init()\nSDA line FAULT");



  tmp = ds1307_get_byte(0x02);
  if (tmp & 0x40) /* 12hr selected ? */
    ds1307_set_byte(0x02, tmp & 0x3F); /* 24hr */

  tmp = ds1307_get_byte(0x00);

  if(tmp & 0x80) /* clock running ?*/
    ds1307_set_byte(0x00, tmp & 0x7F);

  ds1307_set_byte(0x07, 0x90); /* enable 1Hz SQW signal */
}


void
ds1307_set_date_time(char yy, char mm, char dd, char h, char m, char dow)
{
  ds1307_start();
  if(ds1307_send(0xD0))
    halt(30, "DS1307 no ACK\nset_date() #1");
  if(ds1307_send(0x00))
    halt(30, "DS1307 no ACK\nset_date() #2");
  
  ds1307_send(0); /* seconds to zero. Start clock also */
  ds1307_send(((m/10) << 4) | m%10);
  ds1307_send(((h/10) << 4) | h%10);
  ds1307_send(dow);
  ds1307_send(((dd/10) << 4) | dd%10);
  ds1307_send(((mm/10) << 4) | mm%10);
  ds1307_send(((yy/10) << 4) | yy%10);
  ds1307_stop();
}


void
show_time(void)
{
  unsigned char h, m, s;

  ds1307_start();
  if(ds1307_send(0xD0))
    halt(31, "DS1307 no ACK\nshow_time() #1");
  if(ds1307_send(0x00))
    halt(31, "DS1307 no ACK\nshow_time() #2");
  ds1307_stop();
  NOP5();
  ds1307_start();
  if(ds1307_send(0xD1))
    halt(31, "DS1307 no ACK\nshow_time() #3");
  s = ds1307_receive(0);
  m = ds1307_receive(0);
  h = ds1307_receive(1);
  ds1307_stop();
  lcd_send(0, 0x80 | 0x08); /* set DDRAM address */
  lcd_send(1, (h>>4) + '0');
  lcd_send(1, (h & 0x0F) + '0');
  lcd_send(1, ':');
  lcd_send(1, (m >> 4) + '0');
  lcd_send(1, (m & 0x0F) + '0');
}


/* Fills the buffer with current time and date */
void
syslog_time(void)
{
  unsigned char h, m, s;

  ds1307_start();
  if(ds1307_send(0xD0))
    halt(31, "DS1307 no ACK\nshow_time() #1");
  if(ds1307_send(0x00))
    halt(31, "DS1307 no ACK\nshow_time() #2");
  ds1307_stop();
  NOP5();
  ds1307_start();
  if(ds1307_send(0xD1))
    halt(31, "DS1307 no ACK\nshow_time() #3");
  s = ds1307_receive(0);
  m = ds1307_receive(0);
  h = ds1307_receive(0);
  push_char((h>>4) + '0');
  push_char((h & 0x0F) + '0');
  push_char(':');
  push_char((m >> 4) + '0');
  push_char((m & 0x0F) + '0');
#ifdef LONG_TIME
  push_char('.');
  push_char((s >> 4) + '0');
  push_char((s & 0x0F) + '0');
#endif
  push_char(' ');
  ds1307_receive(0); /* DOW */
  s = ds1307_receive(0); /* day */
  m = ds1307_receive(0); /* month */
  h = ds1307_receive(1); /* year */
  ds1307_stop();
#ifdef LONG_TIME
  push_char((h>>4) + '0');
  push_char((h & 0x0F) + '0');
  push_char('/');
#endif
  push_char((m >> 4) + '0');
  push_char((m & 0x0F) + '0');
  push_char('/');
  push_char((s >> 4) + '0');
  push_char((s & 0x0F) + '0');
  push_char(' ');
}