/****************************************************************************/
/* Copyright 2003-2016 MBARI                                                */
/****************************************************************************/
/* Summary  : Support for DS3232 I2C RTC chip for OASIS5 on PIC32MX470F512L */
/* Filename : rtc.c                                                         */
/* Author   : Robert Herlien (rah)                                          */
/* Project  : OASIS Mooring Replacement (OASIS5)                            */
/* Revision: 1.0                                                            */
/* Created  : 02/25/2016                                                    */
/*                                                                          */
/* MBARI provides this documentation and code "as is", with no warranty,    */
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium  */
/* Research Institute to assist in its use, correction, modification, or    */
/* enhancement. This information should not be published or distributed to  */
/* third parties without specific written permission from MBARI.            */
/*                                                                          */
/****************************************************************************/
/* Modification History:                                                    */
/* 25feb2016 rah - created                                                  */
/* $Log: rtc.c,v $
/* Revision 1.1  2016/04/19 17:08:26  bobh
/* First Rev of fileTest
/*
/* Revision 1.1  2016/03/23 23:45:43  bobh
/* Initial revision of dbgTest.MX470
/*
 */
/****************************************************************************/

#include <xc.h>
#include <GenericTypeDefs.h>
#include "rtc.h"
#include "i2c.h"
#include "sys_timer.h"

#include <stdio.h>
#include <time.h>


/************************************************************************/
/* Function    : rtcInit                                                */
/* Purpose     : Initialize RTC module                                  */
/* Input       : None                                                   */
/* Outputs     : None                                                   */
/* Side Effects: Initializes I2C Bus 1, as RTC is only unit on I2C1     */
/************************************************************************/
void rtcInit(void)
{
    I2C1ADD = 0x68;             /* I2C address for DS3232 RTC chip      */
    i2cInit();
}


/************************************************************************/
/* Function    : earlyAbort						*/
/* Purpose     : Error return						*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
static Errno earlyAbort(Errno errCode)
{
    i2cStop();
    return(errCode);

} /* earlyAbort() */


/************************************************************************/
/* Function    : rtcReadReg                                             */
/* Purpose     : Read One RTC Register                                  */
/* Input       : Register address, ptr to result                        */
/* Outputs     : OK or Error number                                     */
/************************************************************************/
Errno	rtcReadReg(UINT16 addr, BYTE *rdat)
{
    Errno       rtn;

    if ((rtn = i2cStart()) != I2C_SUCCESS)
        return(rtn);

    if ((rtn = i2cSendByte(RTC_ADDR_WR)) != I2C_SUCCESS)
        return(earlyAbort(rtn));

    if ((rtn = i2cSendByte(addr)) != I2C_SUCCESS)
        return(earlyAbort(rtn));

    if ((rtn = i2cRestart()) != I2C_SUCCESS)
        return(earlyAbort(rtn));

    if ((rtn = i2cSendByte(RTC_ADDR_RD)) != I2C_SUCCESS)
        return(earlyAbort(rtn));

    i2cClrRcv();

    if ((rtn = i2cRcvByte(rdat)) != I2C_SUCCESS)
        return(earlyAbort(rtn));

    rtn = i2cSendNack();

    i2cStop();
    return(rtn);
}


/************************************************************************/
/* Function    : rtcWriteReg                                            */
/* Purpose     : Write One RTC Register                                 */
/* Input       : Register address, value to write                       */
/* Outputs     : OK or Error number                                     */
/************************************************************************/
Errno	rtcWriteReg(UINT16 addr, BYTE wdat)
{
    Errno       rtn;

    if ((rtn = i2cStart()) != I2C_SUCCESS)
        return(rtn);

    if ((rtn = i2cSendByte(RTC_ADDR_WR)) != I2C_SUCCESS)
        return(earlyAbort(rtn));

    if ((rtn = i2cSendByte(addr)) != I2C_SUCCESS)
        return(earlyAbort(rtn));

    if ((rtn = i2cSendByte(wdat)) != I2C_SUCCESS)
        return(earlyAbort(rtn));

    i2cStop();
    return(I2C_SUCCESS);
}


/************************************************************************/
/* Function    : rtcReadMultRegs                                        */
/* Purpose     : Read Multiple RTC Registers                            */
/* Input       : Register address, num registers to read,  ptr to result*/
/* Outputs     : OK or Error number                                     */
/************************************************************************/
Errno	rtcReadMultRegs(UINT16 addr, UINT16 numRegs, BYTE *rdat)
{
    int         i;
    Errno       rtn;

    if ((rtn = i2cStart()) != I2C_SUCCESS)
        return(rtn);

    if ((rtn = i2cSendByte(RTC_ADDR_WR)) != I2C_SUCCESS)
        return(earlyAbort(rtn));

    if ((rtn = i2cSendByte(addr)) != I2C_SUCCESS)
        return(earlyAbort(rtn));

    if ((rtn = i2cRestart()) != I2C_SUCCESS)
        return(earlyAbort(rtn));

    if ((rtn = i2cSendByte(RTC_ADDR_RD)) != I2C_SUCCESS)
        return(earlyAbort(rtn));

    i2cClrRcv();

    for (i = 0; i < numRegs-1; i++)
        if ((rtn = i2cRcvByte(rdat+i)) != I2C_SUCCESS)
            return(earlyAbort(rtn));
        else if ((rtn = i2cSendAck()) != I2C_SUCCESS)
            return(earlyAbort(rtn));

    if ((rtn = i2cRcvByte(rdat+i)) != I2C_SUCCESS)
        return(earlyAbort(rtn));

    rtn = i2cSendNack();

    i2cStop();
    return(rtn);
}


/************************************************************************/
/* Function    : rtcWriteMultRegs                                       */
/* Purpose     : Write Multiple RTC Registers                           */
/* Input       : Register address, ptr to result                        */
/* Input       : Register address, num registers, ptr to reg data       */
/* Outputs     : OK or Error number                                     */
/************************************************************************/
Errno	rtcWriteMultRegs(UINT16 addr, UINT16 numRegs, BYTE *wdat)
{
    int         i;
    Errno       rtn;

    if ((rtn = i2cStart()) != I2C_SUCCESS)
        return(rtn);
    
    if ((rtn = i2cSendByte(RTC_ADDR_WR)) != I2C_SUCCESS)
        return(earlyAbort(rtn));

    if ((rtn = i2cSendByte(addr)) != I2C_SUCCESS)
        return(earlyAbort(rtn));

    for (i = 0; i < numRegs; i++)
        if ((rtn = i2cSendByte(wdat[i])) != I2C_SUCCESS)
            return(earlyAbort(rtn));

    i2cStop();
    return(I2C_SUCCESS);
}


/************************************************************************/
/* Function    : toBCD                                                  */
/* Purpose     : Convert binary Byte to BCD                             */
/* Input       : Input byte                                             */
/* Outputs     : BCD Result                                             */
/* Comments    : Works for input range of 00-99                         */
/*               Multiples of 100 get stripped, e.g. 167 converts to 0x67*/
/************************************************************************/
static UINT16 toBCD(UINT16 inw)
{
    return((((inw/10)%10) << 4) | (inw%10));
}


/************************************************************************/
/* Function    : fromBCD                                                */
/* Purpose     : Convert from BCD to binary                             */
/* Input       : BCD byte                                               */
/* Outputs     : Result                                                 */
/************************************************************************/
static UINT16 fromBCD(UINT16 bcd)
{
    return(10*((bcd >> 4) & 0xf) + (bcd & 0xf));
}


/************************************************************************/
/* Function    : rtcSetTime                                             */
/* Purpose     : Set the DS3232 Highly Accurate Clock                   */
/* Input       : time_t for new time                                    */
/* Outputs     : None                                                   */
/************************************************************************/
void rtcSetTime(time_t newTime)
{
    BYTE        clkRegs[7];
    struct tm   *tmp = gmtime(&newTime);

    clkRegs[0] = (BYTE)(toBCD(tmp->tm_sec) & 0x7f);
    clkRegs[1] = (BYTE)(toBCD(tmp->tm_min) & 0x7f);
    clkRegs[2] = (BYTE)(toBCD(tmp->tm_hour) & 0x3f);
    clkRegs[3] = (BYTE)((tmp->tm_wday & 7) + 1);
    clkRegs[4] = (BYTE)(toBCD(tmp->tm_mday) & 0x3f);
    clkRegs[5] = (BYTE)(toBCD(tmp->tm_mon + 1) & 0x1f);
    clkRegs[6] = (BYTE)(toBCD(tmp->tm_year % 100));
    rtcWriteMultRegs(RTC_SEC, 7, clkRegs);

} /* rtcSetTime() */


/************************************************************************/
/* Function    : rtcTime                                                */
/* Purpose     : Read Time of Day from the DS3232 Accurate RTC          */
/* Input       : None                                                   */
/* Outputs     : Time of Day                                            */
/* Comment     : Returns -1 if mktime() fails                           */
/************************************************************************/
time_t  rtcTime(struct tm *tmptr)
{
    BYTE        clkRegs[7];
    struct tm	accurateTm, *tmp;

    tmp = (tmptr == NULL) ? &accurateTm : tmptr;

    rtcReadMultRegs(RTC_SEC, 7, clkRegs);
    tmp->tm_sec = fromBCD(clkRegs[0]);
    tmp->tm_min = fromBCD(clkRegs[1]);
    tmp->tm_hour = fromBCD(clkRegs[2]);
    tmp->tm_mday = fromBCD(clkRegs[4]);
    tmp->tm_mon = fromBCD(clkRegs[5]) - 1;
    tmp->tm_year = fromBCD(clkRegs[6]) + 100;
    tmp->tm_wday = 0;
    tmp->tm_yday = 0;
    tmp->tm_isdst = 0;
    return(mktime(tmp));

} /* rtcTime() */


#if 0
/************************************************************************/
/* Function    : clkInitializeAccurateTime                              */
/* Purpose     : Initialize and range-check  time of day from the DS3234*/
/* Input       : None                                                   */
/* Outputs     : TRUE if checks OK, else FALSE                          */
/* Side Effects: Fills in accurateTod and accurateTm with DS3234 time   */
/************************************************************************/
MLocal MBool clkInitializeAccurateTime(Void)
{
    Nat16       clkRegs[7];

    spiReadMultRTCRegs(RTC_SEC, 7, clkRegs);
    accurateTm.tm_sec = fromBCD(clkRegs[0]);
    accurateTm.tm_min = fromBCD(clkRegs[1]);
    accurateTm.tm_hour = fromBCD(clkRegs[2]);
    accurateTm.tm_mday = fromBCD(clkRegs[4]);
    accurateTm.tm_mon = fromBCD(clkRegs[5]) - 1;
    accurateTm.tm_year = fromBCD(clkRegs[6]) + 100;
    if ((accurateTm.tm_mon > 11) || (accurateTm.tm_mday < 1) ||
        (accurateTm.tm_mday > 31) || (accurateTm.tm_hour > 23) ||
        (accurateTm.tm_min >= 60) || (accurateTm.tm_sec > 60))
    {
        return(FALSE);
    }

    accurateTm.tm_wday = 0;
    accurateTm.tm_yday = 0;
    accurateTm.tm_isdst = 0;
    if ((accurateTod = mktime(&accurateTm)) == (time_t)(-1))
        return(FALSE);

    systemMsOffset = 0;
    return(TRUE);

} /* clkInitializeAccurateTime() */


/************************************************************************/
/* Function    : clk_printtime                                          */
/* Purpose     : Print date & time to serial port                       */
/* Input       : time_t                                                 */
/* Outputs     : None                                                   */
/************************************************************************/
Void clk_printtime(time_t ptime)
{
  struct tm     *tmptr;

  tmptr = gmtime(&ptime);

  printf("%04d/%02d/%02d %02d:%02d:%02d ", tmptr->tm_year + 1900,
          tmptr->tm_mon+1, tmptr->tm_mday, tmptr-> tm_hour,
          tmptr->tm_min, tmptr->tm_sec);

} /* clk_printtime() */

#endif
