/////////////////////////////////////////////////////////////////////////// 
//  Name   : ds12887.c                                                     // 
//  Author : Timothy Reitmeyer                                           // 
//  Notice: 
//         :                                       // 
//  Date   : 05-05-2005                                                  // 
//  Version: 1.00                       // 
///////////////////////////////////////////////////////////////////////////// 
//05/09/2005 07:25AM initial program with driver.  This file is the Driver 
/* time, date & alarm regs */ 
#define CLK_SECS     0 //MSbit of secs is read only 
#define CLK_SECS_ALM 1 //MSbit of secs is read only 
#define CLK_MINS     2 
#define CLK_MINS_ALM 3 
#define CLK_HRS      4 
#define CLK_HRS_ALM  5 
#define CLK_DOW      6 
#define CLK_DOM      7 
#define CLK_MON      8 
#define CLK_YR       9 
#define REGA         10  //BIT 7 READ-ONLY all others r/w 
#define REGB         11  //READ/WRITE REGISTER 
#define REGC         12  //READ-ONLY REGISTER 
#define REGD         13  //READ-ONLY REGISTER 
#define nvram_min    14  //addresses 14-127 available for use as nv ram 
#define nvram_max    127 
//Comment out the text if your not using it to save on ram 
const char dow_text[8][4] = 
{  "---",// -0- 
  "Mon",// -1- 
  "Tue",// -2- 
  "Wed",// -3- 
  "Thr",// -4- 
  "Fri",// -5- 
  "Sat",// -6- 
  "Sun" // -7- 
}; 
//Comment out the text if your not using it to save on ram 
const char month_text[13][4] = 
{  "---",// -0- 
  "Jan",// -1- 
  "Feb",// -2- 
  "Mar",// -3- 
  "Apr",// -4- 
  "May",// -5- 
  "Jun",// -6- 
  "Jul",// -7- 
  "Aug",// -8- 
  "Sep",// -9- 
  "Oct",// -10- 
  "Nov",// -11- 
  "Dec"// -12- 
}; 

struct rtc_pin_def 
{ //Motorola timing/bus (must have mot(pin1) tied to Vdd. 
  //Read is data from ds12887 to micro. Write is data from micro to ds12887 
  BOOLEAN cs_bar;     // C0 CS must be kept in the active state during DS and AS for mot 
  BOOLEAN as;         // C1 falling edge of AS latches. 
                      // The next rising AS clears address regardless of whether CS is asserted. 
  BOOLEAN rw_bar;     // C2 Read,Write_bar 
  BOOLEAN ds;         // C3 During read cycles, DS signifies the time that the DS12887 is to drive the bidirectional bus. 
                      // In write cycles the trailing edge of DS causes the DS12887 to latch the written data. 
  BOOLEAN reset_bar;  // C4 Reset active low 
  BOOLEAN irq_bar;    // C5 IRQ input active low,read reg C to return irq_bar=1 
  BOOLEAN rclr_bar;   // C6 Unused ..rclr_bar 
  BOOLEAN swq;        // C7 Square Wave out of ds12887 
  int8 ad;            // PortD=Address and Data bus 
}; 

struct rtc_rega_struc 
{ 
  BOOLEAN   RS0;//RS0-3 //1) Enable the interrupt with the PIE bit; 
  BOOLEAN   RS1;        //2) Enable the SQW output pin with the SQWE bit; 
  BOOLEAN   RS2;        //3) Enable both at the same time and the same rate; or 
  BOOLEAN   RS3;        //4) Enable neither. 
  BOOLEAN   DV0;//DV0-2 A pattern of 010 to turn on RTC first time 
  BOOLEAN   DV1;//DV0-2 A pattern of 11X enables the oscillator but holds the countdown in reset. 
  BOOLEAN   DV2; 
  BOOLEAN   UIP;//UIP The update-in-progress status flag to be monitored 
} rega_var; 
struct rtc_regb_struc 
{ BOOLEAN   DSE; //1=Daylight Savings Enable 
  BOOLEAN   MIL; //1=24 hour military clock or 12 standard household clock 
  BOOLEAN   DM;  //1=binary data while a 0 =BCD data. 
  BOOLEAN   SQWE;//1=a square-wave signal at the frequency set by bits RS3-RS0 
  BOOLEAN   UIE; //1=IRQ on update-ended. 
  BOOLEAN   AIE; //1=IRQ on alarm flag,when three time bytes equal the three alarm bytes. 
  BOOLEAN   PIE; //1=IRQ at a rate RS3-RS0.0=IRQ blocked,but the periodic flag (PF) bit is still set. 
  BOOLEAN   SET; //1=update transfer is inhibited and the program can initialize the time and calendar bytes without an update occurring,0=normal advancing, 
} regb_var;//0b00001100 
struct rtc_regc_struc 
{ BOOLEAN   bit0; 
  BOOLEAN   bit1; 
  BOOLEAN   bit2; 
  BOOLEAN   bit3; 
  BOOLEAN   UF;//bit is set after each update cycle. 
  BOOLEAN   AF;//1=current time has matched the alarm time. 
  BOOLEAN   PF;//periodic-interrupt flag. Read-only bit that is set to a 1 when an edge is detected 
  BOOLEAN   IRQF; 
} regc_var; //Read only 
struct rtc_regd_struc 
{ BOOLEAN   bit0; 
  BOOLEAN   bit1; 
  BOOLEAN   bit2; 
  BOOLEAN   bit3; 
  BOOLEAN   bit4; 
  BOOLEAN   bit5; 
  BOOLEAN   bit6; 
  BOOLEAN   VRT;//Valid RAM time.  If zero the lithium battery is dead 
} regd_var;//Read only 
struct rtc_pin_def  rtc; //pins for the rtc 
struct rtc_pin_def  rtc_tris;//tris for the rtc 
#byte rtc  = 0xF82      //note: no semicolin 0x82=C0 on a 18F452 
#byte rtc_tris = 0xF94  //tris location for port C pin 0 
#define rtc_tris_r() rtc_tris=0;rtc_tris.ad=0xFF//read data is input 
#define rtc_tris_w() rtc_tris=0;rtc_tris.ad=0//write data is output 
//============Prototypes=================================// 
void init_rtc(void); 
//void model_num(void); 
char read_rtc(char addr); 
void write_rtc(char addr,char data); 
void set_time(void); 
//============Funtions===================================// 
void write_rtc(char addr,char data) 
{ 
  //C7=swq,6=rclr_bar,5=irq_bar,4=reset_bar,3=ds,2=rw_bar,1=as,0=cs_bar 
  rtc_tris_w(); 
  rtc.cs_bar=0;//chip active 
  rtc.ad=addr; //addr is on bus 
  rtc.rw_bar=0;//write mode 
  rtc.ds=0;    //data strob idle 
  rtc.as=1;    //addr strob 
  delay_cycles(1); // pause 
  rtc.as=0;    //latch address 
  rtc.ds=1;    //data strob idle 
  rtc.ad=data; //data is on bus 
  delay_cycles(1); // pause 
  rtc.ds=0;    //latch data 
  rtc_tris_r(); //set the tris of C  and D to ALL INPUTS 
} 
//=======================================================// 
char read_rtc(char addr) 
{ //C7=swq,6=rclr_bar,5=irq_bar,4=reset_bar,3=ds,2=rw_bar,1=as,0=cs_bar 
  char data; 
  rtc_tris=0b11100000;//set the tris of C for setting address 
  rtc_tris.ad=0x00; //set the tris of D for setting address 
  rtc     =0b00011110;//set C for for setting address 
  rtc.ad=addr;      //put address on bus 
  #asm nop #endasm    //pause 
  rtc.as=0;           //latch 
  delay_cycles(1); // pause 
  rtc_tris.ad=0xFF; //set the tris of D for reading data 
  rtc.ds=0;           //release 
  #asm nop #endasm    //pause 
  data=rtc.ad;      //read the data from the bus 
  rtc_tris_r(); //set the tris of B  and D to ALL INPUTS 
  return(data); } 
//=======================================================// 
void init_rtc() 
{ rtc_tris.reset_bar=0;//set the tris of C 
  rtc.reset_bar=0;     //reset 
  delay_ms(200);// delay the required time for reset 
  rtc.reset_bar=1;  //release 
  rtc_tris_r(); //set the tris of C & D to ALL INPUTS 
  while(rega_var.UIP);//wait for update to finish 
  regd_var=read_rtc(REGD); 
  if(regd_var.VRT) fprintf(DEBUG,"Lithium battery OK\n\r"); 
  else fprintf(DEBUG,"Lithium battery dead\n\r"); 
  rega_var=0; 
  //  //RS3:0 0=None,1=256Hz,2=128Hz,3=8.192kHz,4=4.096kHz,5=2.048kHz,6=1.024kHz 
  //  //7=512Hz,8=256Hz,9=128Hz,A=64Hz,B=32Hz,C=16Hz,D=8Hz,E=4Hz,F=2Hz 
  //  //RS3:0=5 =2kHz sqr wave 
  //  rega_var.RS0=1;//RS0-3 //1) Enable the interrupt with the PIE bit; 
  //  rega_var.RS1=1;        //2) Enable the SQW output pin with the SQWE bit; 
  //  rega_var.RS2=1;        //3) Enable both at the same time and the same rate; or 
  //  rega_var.RS3=1;        //4) Enable neither. 
  //  rega_var.DV0=0;//DV0-2 A pattern of 010 to turn on RTC first time 
  //  rega_var.DV1=1;//DV0-2 A pattern of 11X enables the oscillator but holds the countdown in reset. 
  //  rega_var.DV2=0; 
  //  rega_var.UIP=0;//UIP The update-in-progress status flag to be monitored 
  //  write_rtc(REGA,(int8)rega_var); //set reg A 
  write_rtc(REGA,0b11110100); //set reg A 
  //  //write_rtc(REGB,0b00001000); //set reg B 
  //  //write_rtc(REGB,0b01000100); //(msb) SET,PIE,AIE,UIE,SQWE,DM,MIL,DSE (lsb) 
  //  regb_var.DSE=0; //1=Daylight Savings Enable 
  //  regb_var.MIL=0; //1=24 hour military clock or 12 standard household clock 
  //  regb_var.DM=1;  //1=binary data while a 0 =BCD data. 
  //  regb_var.SQWE=1;//1=a square-wave signal at the frequency set by bits RS3-RS0 
  //  regb_var.UIE=0; //1=IRQ on update-ended. 
  //  regb_var.AIE=1; //1=IRQ on alarm flag,when three time bytes equal the three alarm bytes. 
  //  regb_var.PIE=0; //1=IRQ at a rate RS3-RS0.0=IRQ blocked,but the periodic flag (PF) bit is still set. 
  //  regb_var.SET=0; //1=update transfer is inhibited and the program can initialize the time and calendar bytes without an update occurring,0=normal advancing, 
  //  write_rtc(REGB,(int8)regb_var); 
  write_rtc(REGB,0b11111100); //(msb) SET,PIE,AIE,UIE,SQWE,DM,MIL,DSE (lsb) 
} 

//================set the time and date======================// 
void set_time() 
/* Note: NO error checking is done on the user entries! */ 
{  char yr, mn, dt, dy, hr, min, sec; 
  char input[10]; 
  regb_var=read_rtc(REGB); 
  if(regb_var.DM=1) /* Binary data */ 
  { fprintf(DEBUG,"\nEnter the year (0-99): "); 
    fgets(input,DEBUG);//scanf("%bd", &yr); 
    yr=atoi(input); 
    fprintf(DEBUG,"\n\rread %u\n\r",yr); 

    fprintf(DEBUG,"Enter the month (1-12): "); 
    fgets(input,DEBUG);// scanf("%bd", &mn); 
    mn=atoi(input); 
    fprintf(DEBUG,"\n\rread %u\n\r",mn); 


    fprintf(DEBUG,"Enter the date (1-31): "); 
    fgets(input,DEBUG);// scanf("%bd", &dt); 
    dt=atoi(input); 
    fprintf(DEBUG,"\n\rread %u\n\r",dt); 

    fprintf(DEBUG,"Enter the day (1-7): "); 
    fgets(input,DEBUG);// scanf("%bd", &dy); 
    dy=atoi(input); 
    fprintf(DEBUG,"\n\rread %u\n\r",dy); 

    if(regb_var.MIL=1) /* if 24 hour mode */ 
    { fprintf(DEBUG,"Enter the hour (1-23): "); 
      fgets(input,DEBUG);//scanf("%bd", &hr); 
      hr=atoi(input); 
      fprintf(DEBUG,"\n\rread %u\n\r",hr);    } 
    else { fprintf(DEBUG,"Enter the hour (1-11): "); 
      fgets(input,DEBUG);//scanf("%bd", &hr); 
      hr=atoi(input); 
      fprintf(DEBUG,"\n\rread %u\n\r",hr); 
      fprintf(DEBUG,"A)M or P)M (A/P) " ); 
      fgets(input,DEBUG);//scanf("%1bs", &min); 
      if(input == 'P' || input == 'p') 
      hr |= 0x80; /* add PM indicator */ 
    } 
    fprintf(DEBUG,"Enter the minute (0-59): "); 
    fgets(input,DEBUG);//scanf("%bd", &min); 
    min=atoi(input); 
    fprintf(DEBUG,"\n\rread %u\n\r",min); 
    fprintf(DEBUG,"Enter the second (0-59): "); 
    fgets(input,DEBUG);//scanf("%bd", &sec); 
    sec=atoi(input); 
    fprintf(DEBUG,"\n\rread %u\n\r",sec); 
  } 
  else /* BCD data mode */ 
  { 
//    fprintf(DEBUG,"\nEnter the year (0-99): "); 
//    scanf("%bx", &yr); 
//    fprintf(DEBUG,"Enter the month (1-12): "); 
//    scanf("%bx", &mn); 
//    fprintf(DEBUG,"Enter the date (1-31): "); 
//    scanf("%bx", &dt); 
//    fprintf(DEBUG,"Enter the day (1-7): "); 
//    scanf("%bx", &dy); 
//    if(REGB & 2) /* if 24 hour mode */ 
//    { 
//      fprintf(DEBUG,"Enter the hour (1-23: "); 
//      scanf("%bx", &hr); 
//    } 
//    else 
//    { 
//      fprintf(DEBUG,"Enter the hour (1-11:) "); 
//      scanf("%bx", &hr); 
//      fprintf(DEBUG,"A)M or P)M (A/P) "); 
//      scanf("%1bs", &min); 
//      if(min == 'P' || min == 'p') 
//      hr |= 0x80; /* add PM indicator */ 
//    } 
//    fprintf(DEBUG,"\nEnter the minute (0-59): "); 
//    scanf("%bx", &min); 
//    fprintf(DEBUG,"Enter the second (0-59): "); 
//    scanf("%bx", &sec); 
  } 


  regb_var.SET=1;//  REGB |= 0x80; /* inhibit update while writing to clock */ 
  write_rtc(REGB,(int8)regb_var); //set reg B 

  write_rtc(CLK_SECS,sec);//  CLK_SECS = sec; 
  write_rtc(CLK_MINS,min);//  CLK_MINS = min; 
  write_rtc(CLK_HRS,hr);//  CLK_HRS = hr; 
  write_rtc(CLK_DOW,dy);//  CLK_DOW = dy; 
  write_rtc(CLK_DOM,dt);//  CLK_DOM = dt; 
  write_rtc(CLK_MON,mn);//  CLK_MON = mn; 
 write_rtc(CLK_YR,yr);//  CLK_YR = yr; 
  //irq on alarm,  alarm set to don't care in all 3 bytes..ie: irq on each second 
  write_rtc(CLK_HRS_ALM,0xFF); //  CLK_HRS_ALM= 0xFF;//ie:don't care 
  write_rtc(CLK_MINS_ALM,0xFF);//  CLK_MINS   = 0xFF;//ie:don't care 
  write_rtc(CLK_SECS_ALM,0xFF);//  CLK_MINS   = 0xFF;//ie:don't care 

  regb_var.SET=0;//  REGB &= 0x7f; /* allow update of buffered set of time & date registers */ 
  write_rtc(REGB,(int8)regb_var); //set reg B 
} 
//=======================================================// 
//void model_num(void) /* ------ determine the model number of the RTC ------ */ 
//{ 
//  REGA |= 0x10; /* select bank 1 */ 
//  XBYTE[0x40] ^= 0xff; /* try to write to compliment data */ 
//  if( (XBYTE[0x40] ^ 0xff) != XBYTE[0x40] ) /* if bits cannot be written, it's 
//  bank 1 */ 
//  { 
//    model = XBYTE[0x40]; 
//  } 
//  else 
//  { 
//    XBYTE[0x40] ^= 0xff; /* restore original data */ 
//    model = 0; /* default if device does not support model number */ 
//  } 
//  REGA &= 0xef; /* select bank 0 */ 
//  printf("\nModel: "); 
//  switch(model) 
//  { 
//    case 0x71: printf("DS1687"); break; 
//    case 0x72: printf("DS17287"); break; 
//    case 0x73: printf("DS1691/93"); break; 
//    case 0x74: printf("DS17487"); break; 
//    case 0x78: printf("DS17887"); break; 
//    default: printf("DS12887"); 
//  } 
//} 
