/****************************************************************************/
/* Summary  : Test Routines for Dallas DS3234 accurate clock chip	    */
/* Filename : clock.c                                                       */
/* Author   : Robert Herlien (rah)					    */
/* Project  : BEDS (Benthic Event Detection System)			    */
/* Revision : 1.0							    */
/* Created  : 10/4/2011							    */
/****************************************************************************/
/* Modification History:						    */
/* 10oct2011 rah - created						    */
/****************************************************************************/

#include <cfxbios.h>			/* Persistor BIOS definitions	    */
#include <cfxpico.h>			/* Persistor PicoDOS definitions    */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>


#define RTC_INT_LEVEL	2		/* Interrupt level for RTC	    */
#define RTCSLOT		NMPCS2		/* SPI slot for DS3234 chip	    */

typedef short boolean;

#ifndef FALSE
#define FALSE	0
#endif

#ifndef TRUE
#define	TRUE	~FALSE
#endif

#define OK	 0
#define ERROR	-1
#define MLocal	static

#define NumberOf(arr)		((sizeof(arr) / sizeof(arr[0])))


/********************************/
/*	Global Data		*/
/********************************/

const char * const	signon = "DS3234 test program\n\n";

static volatile long	tick = 0;

static uchar	mirrorpins[] = { 1, 15, 17, 19, 21, 22, 23,
				 24, 25, 26, 27, 31, 32, 33, 34, 0 };

static QPBDev clkQPBTemplate =
{
    RTCSLOT,			/* qslot - our qspi slot		    */
    "DS3234",			/* Device name (char[16])		    */
    1000000,			/* Max baud rate			    */
    8,				/* Bits Per Transfer			    */
    iaHighSCK,			/* SPI Clock Polarity (iaHighSCK,iaLowSCK)  */
    captFall,			/* SPI Clock Phase (captLead/captFall)	    */
    true,			/* Continue CS assert between mult xfrs	    */
    true,			/* Auto adjust timing to clock flag	    */
    0,				/* Min. Delay Before SCK (picoSecs)	    */
    0,				/* Min. Delay After Transfer (ps)	    */
    NULL,			/* pointer to received data buffer	    */
				/*   (filled in by QPBInitSlot)		    */
    2 				/* Words transferred			    */
};

static QPB	*clkQPB;

void		printRtcTiming();
void		readReg(void);
void		writeReg(void);
void		readDateTime();
void		repeatReadReg();
void		setDateTime();
void		setPicoDateTime();
void		readAllRegs();


/************************************************************************/
/* Function    : main							*/
/* Purpose     : Main entry point					*/
/* Inputs      : argc, argv						*/
/* Outputs     : 0							*/
/************************************************************************/
int main(int, char **)
{
	// Identify the progam and build
    printf("\nProgram: %s: %s %s \n", __FILE__, __DATE__, __TIME__);
	// Identify the device and its firmware
    cprintf("Persistor CF%d SN:%ld   BIOS:%d.%d   PicoDOS:%d.%d\n", CFX,
	    BIOSGVT.CFxSerNum, BIOSGVT.BIOSVersion, BIOSGVT.BIOSRelease, 
	    BIOSGVT.PICOVersion, BIOSGVT.PICORelease);

    TMGSetSpeed(16000);
    cprintf(signon);

    PIOMirrorList(mirrorpins);
    PIOSet(35);	PIOSet(37);		// LED on R16AU RecipeCard
    PIOClear(29); PIOSet(30);		// Turn off AUX RS-232s
    PIOClear(28);			// A/D shutdown
    
    PITRemoveChore(0);			// get rid of all current chores
    clkQPB = QPBInitSlot(&clkQPBTemplate);

    printRtcTiming();

    while(1)
    {
	cprintf("\n");
	cprintf("Type 'R' to read one RTC register\n");
	cprintf("Type 'W' to write one RTC register\n");
	cprintf("Type 'A' to read all RTC registers\n");
	cprintf("Type 'T' to read the date and time\n");
	cprintf("Type 'S' to set the RTC date/time from PicoDOS date/time\n");
	cprintf("Type 'P' to set the PicoDOS date/time from RTC date/time\n");
	cprintf("Type 'N' to repeatedly read one RTC register\n");
	cprintf("Type 'X' to exit this program\n");

    switch(cgetc())
    {
      case 'x':
      case 'X':
      case 3:
	  cprintf("Goodbye\n");
	  SCITxWaitCompletion();
	  return(0);

      case 'R':
      case 'r':
	  readReg();
	  break;

      case 'W':
      case 'w':
	  writeReg();
	  break;

      case 'A':
      case 'a':
	  readAllRegs();
	  break;

      case 'T':
      case 't':
	  readDateTime();
	  break;

      case 'S':
      case 's':
	  setDateTime();
	  break;

      case 'P':
      case 'p':
	  setPicoDateTime();
	  break;

      case 'N':
      case 'n':
	  repeatReadReg();
	  break;
    }
  }

    return(0);
			      
} /* main() */


/************************************************************************/
/* Function    : printRtcTiming						*/
/* Purpose     : Discover & print how long RTCGetTime() takes		*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
void printRtcTiming(void)
{
    ushort	i, ticks, tickStart;
    ulong	seconds, secStart;
    long	totTicks;

    RTCGetTime(&secStart, &tickStart);

    for (i = 0; i < 10; i++)
	RTCGetTime(&seconds, &ticks);

    totTicks = 40000L * (seconds - secStart) + ticks - tickStart;

    printf("\n\n10 iterations of RTCGetTime() took %ld ticks (%ld usecs)\n",
	   totTicks, 25 * totTicks);
}


/************************************************************************/
/* Function    : readReg						*/
/* Purpose     : Read a device register					*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
void readReg(void)
{
    short	addr, cmd[2];

    if (!QRshort("\nRegister to read (0 to 19 hex):  ", "%x", FALSE, &addr, 0, 0x19))
	return;

    cmd[0] = addr & 0x7f;
    cmd[1] = 0;

    QPBTransact(clkQPB, 0, 2, cmd);

    cprintf("\nRegister 0x%02x = %x\n", addr, clkQPB->dev->rcvData[1] & 0xff);
    
}   /* readReg() */


/************************************************************************/
/* Function    : writeReg						*/
/* Purpose     : Write a device register				*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
void writeReg(void)
{
    short	addr, wdat, cmd[2];
    
    if (!QRshort("\nRegister to write (0 to 19 hex):  ", "%x", FALSE, &addr, 0, 0x19))
	return;

    if (!QRshort("\nData to write (00 - ff hex):  ", "%x", 0, &wdat, 0, 0xff))
	return;

    cprintf("\nWriting 0x%02x to register 0x%02x\n", wdat & 0xff, addr);

    cmd[0] = addr | 0x80;
    cmd[1] = wdat & 0xff;

    QPBTransact(clkQPB, 0, 2, cmd);

}   /* writeReg() */


/************************************************************************/
/* Function    : readDateTime						*/
/* Purpose     : Read date and time registers				*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
void readDateTime(void)
{
    short	i, cmd[8];

    memset(cmd, 0, sizeof(cmd));

    QPBTransact(clkQPB, 0, 8, cmd);

    for (i = 0; i < 7; i++)
	cprintf("%02x ", clkQPB->dev->rcvData[7-i] & 0xff);

    cprintf("\n");

}   /* readDateTime() */


/************************************************************************/
/* Function    : repeatReadReg						*/
/* Purpose     : Read a device register repeatedly			*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
void repeatReadReg(void)
{
    short	addr, cmd[2];

    if (!QRshort("\nRegister to read (0 to 19 hex):  ", "%x", FALSE, &addr, 0, 0x19))
	return;

    cmd[0] = addr & 0x7f;
    cmd[1] = 0;

    while (!kbhit())
    {
	QPBTransact(clkQPB, 0, 2, cmd);

	cprintf("Register 0x%02x = %x\n", addr, clkQPB->dev->rcvData[1] & 0xff);

	RTCDelayMicroSeconds(500000);
    }

}   /* repeatReadReg() */


/************************************************************************/
/* 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 ushort toBCD(ushort val)
{
    return(((val/10) << 4) | (val%10));
}

/************************************************************************/
/* Function    : fromBCD						*/
/* Purpose     : Convert from BCD to binary				*/
/* Input       : BCD byte						*/
/* Outputs     : Result							*/
/************************************************************************/
static ushort fromBCD(ushort bcd)
{
    return(10*((bcd >> 4) & 0xf) + (bcd & 0xf));
}


/************************************************************************/
/* Function    : setDateTime						*/
/* Purpose     : Set date and time registers				*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
void setDateTime(void)
{
    time_t	secs;
    ushort	ticks;
    struct tm	*tmp;
    ushort	cmd[8];

    do
    {
	Delay1ms();
	RTCGetTime(&secs, &ticks);

    } while (ticks > 2000);

    tmp = gmtime(&secs);

    cmd[0] = 0x80;
    cmd[1] = toBCD(tmp->tm_sec);
    cmd[2] = toBCD(tmp->tm_min);
    cmd[3] = toBCD(tmp->tm_hour);
    cmd[4] = toBCD(tmp->tm_wday + 1);
    cmd[5] = toBCD(tmp->tm_mday);
    cmd[6] = toBCD(tmp->tm_mon + 1);
    cmd[7] = toBCD(tmp->tm_year % 100);

    QPBTransact(clkQPB, 0, 8, &cmd);

    cprintf("\n");

    readDateTime();

}   /* setDateTime() */


/************************************************************************/
/* Function    : setPicoDateTime					*/
/* Purpose     : Set PicoDOS date and time from RTC			*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
void setPicoDateTime(void)
{
    time_t	accurateTod;	    /* DS3234 time of day	*/
    struct tm	accurateTm;	    /* DS3234 time of day as struct tm*/
    short	i, cmd[8];

    cmd[0] = 0x8e;
    cmd[1] = 5;
    QPBTransact(clkQPB, 0, 2, cmd);	/* Set 1PPS interrupt		*/

    cmd[0] = 0x8f;
    cmd[1] = 0;
    QPBTransact(clkQPB, 0, 2, cmd);	/* Clear the interrupt		*/

    for (i = 0; (i < 1000) && (PIORead(IRQ2) != 0); i++ )
	Delay1ms();			/* Wait for new second		*/

    memset(cmd, 0, sizeof(cmd));

    QPBTransact(clkQPB, 0, 8, cmd);

    accurateTm.tm_sec = fromBCD(clkQPB->dev->rcvData[1] & 0xff);
    accurateTm.tm_min = fromBCD(clkQPB->dev->rcvData[2] & 0xff);
    accurateTm.tm_hour = fromBCD(clkQPB->dev->rcvData[3] & 0xff);
    accurateTm.tm_mday = fromBCD(clkQPB->dev->rcvData[5] & 0xff);
    accurateTm.tm_mon = fromBCD(clkQPB->dev->rcvData[6] & 0xff) - 1;
    accurateTm.tm_year = fromBCD(clkQPB->dev->rcvData[7] & 0xff) + 100;
    accurateTm.tm_wday = 0;
    accurateTm.tm_yday = 0;
    accurateTm.tm_isdst = 0;
    accurateTod = mktime(&accurateTm);

    RTCSetTime(accurateTod, 1);
}


/************************************************************************/
/* Function    : readAllRegs						*/
/* Purpose     : Read all registers					*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
void readAllRegs(void)
{
    short	i, cmd[16];

    memset(cmd, 0, sizeof(cmd));

    QPBTransact(clkQPB, 0, 16, cmd);

    for (i = 1; i < 16; i++)
	cprintf("%02x ", clkQPB->dev->rcvData[i] & 0xff);
    cprintf("\n");

    memset(cmd, 0, sizeof(cmd));
    cmd[0] = 0xf;

    QPBTransact(clkQPB, 0, 6, cmd);

    for (i = 1; i < 6; i++)
	cprintf("%02x ", clkQPB->dev->rcvData[i] & 0xff);
    cprintf("\n");

}   /* readAllRegs() */
