/***************************************************************************/
/* rtc.c                                                                   */
/* Based on code from Dallas Semiconductor.                                */
/* Hope is to code for dev board using same pins and then move to our      */
/* board.                                                                  */
/* This code contains functions to use the 3-wire connection, set the      */
/* time and alarm for the alarm clock, and check the alarm as well.        */
/* Check the main function here for examples of how the final code should  */
/* ultimately use the written functions.                                   */
/***************************************************************************/
#include <ezusb.h>
#include <EZRegs.h>

//the following two things are needed to write to led for testing
#define LED_ADDR        0x21
BYTE xdata Digit[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x98, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e };

// need to set pins that will interface with RTC - DOESN'T WORK WITH EZ-USB SETUP
// but the bits correspond to our setup, so keep this in mind.
//sbit CE        = OUTA ^ 0;
//sbit SCLK      = OUTA ^ 1;
//sbit SDI_IO    = OUTA ^ 2;
//sbit SDO       = OUTB ^ 0;
//sbit SERMODE   = OUTB ^ 5;

/* ----------------------------------------------------------------------- */
// function to reset 3-wire connection
void Reset_3W()
{
    //when using 3-wire, must first enable chip while clock is low.
    OUTA = PINSA & 0xFD;                        // SCLK = 0;
    OUTA = PINSA & 0xFE;                        // CE = 0;
    OUTA = PINSA | 0x01;                        // CE = 1;
}

/* ----------------------------------------------------------------------- */
// function to write data onto 3-wire interface
void WBYTE_3W(BYTE W_Byte)
{
    BYTE i;                                     // local variable to count through bits

    for(i=0; i<8; ++i)
    {
        OUTA = PINSA & 0xFB;                    // SDI_IO = 0;
        
        if(W_Byte & 0x01)                       // if next bit to transfer is 1, set io pin to 1
        {
            OUTA = PINSA | 0x04;                // SDI_IO = 1;
        }
        
        //perform rising clock so that device registers input
        OUTA = PINSA & 0xFD;                    // SCLK = 0;
        OUTA = PINSA | 0x02;                    // SCLK = 1;
        
        W_Byte >>= 1;                           // go to next bit in byte
    }
}
/* ----------------------------------------------------------------------- */
// function to read data from 3-wire interface
BYTE RBYTE_3W()
{
    BYTE i;                                     // counter through bits
    BYTE R_Byte;                                // received byte accumulator
    BYTE TmpByte;                               // temporary holder of io pin
    
    R_Byte = 0x00;                              // received byte starts cleared
    OEA=0xFB;                                   // set sdi pin as input
    OUTA = PINSA | 0x04;                        // SDI_IO = 1;

    for(i=0; i<8; ++i)
    {
        // falling edge of clock to change value
        OUTA = PINSA | 0x02;                    // SCLK = 1;
        OUTA = PINSA & 0xFD;                    // SCLK = 0;
        TmpByte = (BYTE) (PINSA & 0xFB);        // read value from data line
        TmpByte <<= 5;                          // move bit to msb of temp
        R_Byte >>= 1;                           // move received bits down 1
        R_Byte |= TmpByte;                      // write temp msb into received's msb
    }

    OEA=0xFF;                                   // return sdi pin to output
    return R_Byte;
}
/* ----------------------------------------------------------------------- */
// this function initializes RTC
void Initialize_DS1305()
{
    Reset_3W();                                 // reset interface
    
    //according to Dallas, must clear write protection to initialize RTC
    //access control register for writing (0x8F)
    WBYTE_3W(0x8F);
    WBYTE_3W(0x00);                             // clear all bits in this register

    Reset_3W();                                 // do the same thing again
    WBYTE_3W(0x8F);
    WBYTE_3W(0x00);

    Reset_3W();                                 // and finally reset again
}
/* ----------------------------------------------------------------------- */
// this function reads the time from the clock
// (and for testing, places second onto LED)
void getTime(*BYTE Min, Min10, Hour, Hour10, PM)
{
    BYTE ReadSec;                               // 4MSB=10s place, 4LSB=1s place for value
    BYTE ReadMin;                               // ditto
    BYTE ReadHour;                              // ditto
    //we don't need the rest of the available info for this clock :o)

    Reset_3W();                                 // reset 3-wire

    // need to read seconds register (0x00), so put this onto 3-wire
    WBYTE_3W(0x00);
    ReadSec = RBYTE_3W();

    // debugging code to write 1s place of second value to LED
    EZUSB_WriteI2C(LED_ADDR, 0x01, &(Digit[(ReadSec & 0x0f)]));
    EZUSB_WaitForEEPROMWrite(LED_ADDR);

    //note that we can now read the next value without specifying its location
    //since it is next in the RTC memory anyway
    ReadMin  = RBYTE_3W();
    ReadHour = RBYTE_3W();

    *Min10  = (ReadMin & 0xf0) >> 4;
    *Min    = ReadMin & 0x0f;
    *Hour10 = (ReadHour & 0x10) >> 4;
    *Hour   = ReadHour & 0x0f;
    *PM     = ReadHour & 0x20;
}
/* ----------------------------------------------------------------------- */
// this function writes time to the clock given the Sec, Min, and Hour, AM/PM
// to input.  If setTime=1, time is set.  Otherwise, alarm is set.
// (Example, if the time setting is for 10:57p, set the values to:
// setTime=1, Min=7, Min10=5, Hour=0, Hour10=1, PM=1
void setValue(BYTE setTime, Min, Min10, Hour, Hour10, PM)
{
    BYTE temp = 0;
    
    Reset_3W();                                 // reset 3-wire
    
    // what was being set?  Time or Alarm?
    if (setTime)
        WBYTE_3W(0x80);
    else
        WBYTE_3W(0x87);
    
    // need to write seconds register, so put this onto 3-wire
    temp = Min10 << 4;                          // shift 10s place of minute to 4MSB
    temp = temp | Min;                          // and get 1s place of minute, too
    WBYTE_3W(temp);
    
    // because of fast transfer capabilities of RTC, just place hour info onto 3-wire.
    // remember, bit 8 = type of alarm, bit 7(MSB)=0, bit 6 = 1 for 12-hour time,
    // bit 5 = 1 if PM, bit 4 = 10s place.  For, now I assume we want a daily alarm,
    // so bit 8 = 0 for all except the final written byte, which gives the "day alarm".
    temp = 0;
    temp = 0x40;                                // set bit 6 for 12-hour
    if (PM)
        temp = temp | 0x20;                     // set bit 5 if time is PM
    if (Hour10)
        temp = temp | 0x10;                     // set bit 4 if time has 10s place
    temp = temp | Hour;
    WBYTE_3W(temp);
    WBYTE_3W(0x80);                             // day alarm byte
}
/* ----------------------------------------------------------------------- */
// this function compares the time in RTC to alarm value and returns 1 if
// the alarm and current time match.
// Keep in mind that we may want to change the system so that an alarm comes
// to the microcontroller every minute to update the display.  If that is the case,
// then we should set Alarm so that the alarm is every minute.
// This requires rewriting this code so that the MSB of the seconds byte is 0,
// and the other MSB for minutes, seconds, and days is 1.  This also means that
// comparing the time to an alarm value must be done on the USB.
BYTE checkAlarm(BYTE alarmMin, alarmMin10, alarmHour, alarmHour10, alarmPM)
{
    BYTE timeMin;
    BYTE timeMin10;
    BYTE timeHour;
    BYTE timeHour10;
    BYTE alarmPM;

    getTime(&timeMin, &timeMin10, &timeHour, &timeHour10, &alarmPM);
    
    if ((timeMin    == alarmMin) &&
        (timeMin10  == alarmMin10) &&
        (timeHour   == alarmHour) &&
        (timeHour10 == alarmHour10) &&
        (timePM     == alarmPM))
        return 1;
    else
        return 0;
}
/* ----------------------------------------------------------------------- */
void main()
{
    BYTE Min;
    BYTE Min10;
    BYTE Hour;
    BYTE Hour10;
    BYTE PM;

    BYTE alarmMin;
    BYTE alarmMin10;
    BYTE alarmHour;
    BYTE alarmHour10;
    BYTE alarmPM;

    
    PORTACFG=0x00;                              // first set PORTA to i/o

    //then set PORTA to output.. note that we have to change
    //pin 2 (i.e., OEA=0xfb) whenever we want to read in a value.
    OEA=0xff;
    
    EZUSB_InitI2C();                            // initialize i2c

    Initialize_DS1305();                        // initialize RTC

    EZUSB_WriteI2C(LED_ADDR, 0x01, &(Digit[1]));    // (Sec & 0x0f)
    EZUSB_WaitForEEPROMWrite(LED_ADDR);

    while(1)
    {
        // if this code is actually working, you should try to make getTime
        // occur every minute.  This can be done by either using an alarm every
        // minute or doing some type of sleep command.
        getTime(Min, Min10, Hour, Hour10, PM);

        // The alarm vs. time match should be done every minute, too.
        if (checkAlarm(alarmMin, alarmMin10, alarmHour, alarmHour10, alarmPM))
            {
                // check type of alarm
                // if no alarm, exit
                // if buzz, play buzz
                // if mp3, make call to mp3 playing function
            }

        // At this point, we should call the LCD function to write the time
        // to the display.  This, too, should be done every minute.
        // This means you should write Value if you want 1s place
        // and Value10 if you want 10s place.

        // If a button is pushed, though, we need to use the I/O Expander's interrupt
        // and USB's external interrupt capabilities to do the requested function,
        // like set the time or alarm.
        // And if a button is pushed to set the time, main code should store
        // the incoming time value and whether we're setting time or alarm.
        // Then, call setTime() using the received values.
    }
}
