/***************************************************************************/

/* muxb8051.c  rev 1.0   7/17/01                                           */

/* This program uses a 8051 type microprocessor (DS2500t) to communicate   */

/* with a DS1687 (or similar) Real Time Clock.  The RTC is memory-mapped   */

/* into the data address space, and is accessed by using two address       */

/* locations.  The even address is used to clock the address data into the */

/* RTC.  The odd address is used to read or write data.  This is an        */

/* example program only and is not supported by Dallas Semiconductor Maxim */

/***************************************************************************/

#pragma code symbols debug

#include <stdio.h>		/* Prototypes for I/O functions           */

#include <DS5000.h>		/* Register declarations for DS5000       */

#include <absacc.h>		/* needed to define xdata addresses       */

#define WRALE		XBYTE[0x0000]     

#define RWDATA	XBYTE[0x0001]

/************************* bit definitions ****************************/

/***************************** Defines ********************************/

#define	CLK_SECS	0x00

#define	CLK_SECS_ALM	0x01

#define	CLK_MINS	0x02

#define	CLK_MINS_ALM	0x03

#define	CLK_HRS	0x04

#define	CLK_HRS_ALM	0x05

#define	CLK_DOW	0x06

#define	CLK_DOM	0x07

#define	CLK_MON	0x08

#define	CLK_YR		0x09

#define	REGA		0x0a

#define	REGB		0x0b

#define	REGC		0x0c

#define	REGD		0x0d

#define	REG4A		0x4a

#define	REG4B		0x4b

#define	DSP_DAT_WR	0xc0

#define	DSP_DAT_RD	0xe0

#define	DSP_INS_WR	0x80

#define	DSP_INS_RD	0xa0

#define	RS2		0x0f

#define	RS4		0x0e

#define	RS8		0x0d

#define	RS16		0x0c

#define	RS32		0x0b

#define	RS64		0x0a

#define	RS128		0x09

#define	RS256		0x08

#define	RS512		0x07

#define	RS1024		0x06

#define	RS2048		0x05

#define	RS4096		0x04

#define	RS8192		0x03

#define	DVX		0x01

#define	C887		0x02

#define	VLDMDL		0x10

#define	CRCSEROK	0x20

#define	CRC50		0x40

#define	CRC64		0x80

#define	VRT0		0x02

#define	DAYS		"Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat\0"

/************************* Global Variables ***************************/

uchar	model = 0, regc;

/*********************** Function Prototypes **************************/

void	init_rtc();

void	writebyte(uchar, uchar);

uchar	readbyte(uchar);

uchar	updcrc(uchar, uchar);

void	clockset();

void	regbinit();

void	reg4binit();

/************************************************************************/

void	writebyte(uchar addr, uchar dat)

{

	WRALE = addr;		/* latch address into RTC */

	RWDATA = dat;		/* write data to RTC */

}

uchar	readbyte(uchar addr)

{

	WRALE = addr;		/* latch address into RTC */

	return(RWDATA);	/* read data from RTC */

}

void	regbinit()

{

	/* Clear SET (update transfers enabled).  Set Periodic Interrupt Enable (PIE) to drive /IRQ at the */

	/* rate selected by RS3-RS0.  Alarm Interrupt Enable (AIE) true, alarm flag will drive /IRQ on match */

	/* Disable (Update Ended Interrupt Enable (UIE).  Disable SQWE, Data Mode (DM) to BCD, 24/12 select */

	/* 24 hour format, Daylight Savings Enable (DSE) is on */



	writebyte(REGB, 0x63);

}

void	reg4binit()

{

uchar	reg, chk;



	/* This example does not use the Auxiliary battery.  Enable 32.768kHz (E32K) output. */

	/* Crystal Select (CS) bit to 0 - no effect if the RTC is a module with a self-contained */

	/* battery.  PAB Reset Select (PRS) bit to '1', Ram Clear Interrupt Enable (RIE) to '0' */

	/* Wake-up Alarm Interrupt Enable (WIE) '0', Kickstart Interrupt Enable (KSE) '0' */



	reg = readbyte(REGA);

	writebyte(REGA, (reg | 0x10) );	/* select bank 1 */

	chk = readbyte(REG4B);

	writebyte(REG4B, (chk ^ 0xff) );	/* try to write to compliment data */

	if( (chk | readbyte(REG4B) ) != 0xff )	/* if all bits can be complimented, it's not reg 4b */

	{

		writebyte(REG4B, 0x40);	/* couldn't write all bits, so it must be reg 4b */

		model = 0;

	}

	else

	{

		model = 0x80;			/* all bits could be written, so this part does not have bank 1 */

		writebyte(REG4B, chk);	/* restore original data */

	}



	writebyte(REGA, reg);		/* select bank 0 */

}

void init_rtc()	/* ------------ id the model of the RTC and set the registers ----------------- */

{

uchar	addr, crc = 0, rega, initstat = 0;



	regbinit();



	rega = readbyte(REGA);		/* get contents of control register A */

	if( (rega & 0x60) != 0x20)		/* DV2=0 & DV1=1 for the RTC to keep time */

		initstat = DVX;		/*  if they're not, assume we need to initialize the RTC */



	reg4binit();



	if( !(readbyte(0x0d) & 0x80) ) initstat += VRT0;	/* if VRT bit is 0, warn that RTC is N/G */



	writebyte(REGA, (rega | 0x10) );	/* switch to bank 1 if device allows */



	crc = updcrc(0, readbyte(0x40) );	/* calculate CRC */

	if( readbyte(0x40) > 0x70 & readbyte(0x40) < 0x79 )	initstat += VLDMDL;	/* model number must be between 71h & 78h */



	crc = updcrc(crc, readbyte(0x41) );	/* 48 bit serial # */

	crc = updcrc(crc, readbyte(0x42) );

	crc = updcrc(crc, readbyte(0x43) );

	crc = updcrc(crc, readbyte(0x44) );

	crc = updcrc(crc, readbyte(0x45) );

	crc = updcrc(crc, readbyte(0x46) );

	/* if CRCS do not match, then it cannot be an RTC with a bank 1 (serial number) */

	/* printf("\ncalc CRC:%2.bx readcrc:%2.bx", crc, readbyte(0x47) ); */

	if (crc == readbyte(0x47) && crc)	initstat += CRCSEROK;	/* crc must match and be non-zero */



	crc = 0;

	for(addr = 0x0e; addr < 0x3e; addr++)	/* calc CRC on 50 byte user (CMOS) RAM */

	{

		crc = updcrc(crc, readbyte(addr) );

	}



	writebyte(REGA, (rega & 0xef) );	/* switch to bank 0 */



	if (crc != readbyte(0x3f) )	initstat += CRC50;	/* assumes CRC is used on 64 byte user RAM */

	/* printf("\ncalc CRC: %2.bx stored CRC: %2.bx", crc, readbyte(0x3f) ); */



	crc = 0;

	for(addr = 0x40; addr < 0x7e; addr++)	/* calc CRC on 64 byte user (CMOS) RAM */

	{

		crc = updcrc(crc, readbyte(addr) );

	}

	if (crc != readbyte(0x7f) )	initstat += CRC64;	/* assumes CRC is used on 64 byte user RAM */

	/* printf("\ncalc CRC: %2.bx stored CRC: %2.bx", crc, readbyte(0x7f) ); */



	writebyte(REGA, (rega | 0x10) );	/* back to bank 1 */



	if( initstat & VRT0 )

	{

		printf("\nWarning: RTC battery low - invalid RTC data");

		/* specific routines to terminate or work-around non-functional RTC goes here */

	}

	if( initstat & (CRC50 | CRC64 | DVX) )	/* if crc for 50 or 64 byte user RAM does not match, or osc is not running */

	{

		printf("\nInitializing RTC...");



		if(initstat & CRC50)

		{

			printf("\nStarting osc...");

			writebyte(REGA, (0x20 + RS2) );	/* turn on osc, enable countdown chain, select 2hZ SQW */

			rega = 0x20 + RS2;

		}



		if(initstat & CRC50)

		{

			printf("\nInitializing 50-byte user RAM ...");

			for(addr = 0x0e; addr < 0x40; addr++)	/* clear user (CMOS) RAM */

				writebyte(addr, 0);			/* CRC for 0x3f is 0 */

		}



		writebyte(REGA, (rega & 0xef) );		/* go to bank 0 */



		if(initstat & CRC64)

		{

			printf("\nInitializing 64-byte user RAM ...");

			for(addr = 0x40; addr < 0x80; addr++)	/* clear user (CMOS) RAM */

				writebyte(addr, 0);			/* CRC for 0x7f is 0 */

		}



		writebyte(REGA, (rega | 0x10) );		/* back to bank 1 */



		printf("\ninitstat:%2.bx", initstat);

		if( (initstat & 0x30) == (VLDMDL | CRCSEROK) )		/* if RTC has extended features */

		{

			printf("\nInitializing bank 1...");

			writebyte(REGA, (0x20 | 0x10) );	/* select bank 1 */

			if(!model)	model = readbyte(0x40);		/* save model number, will already be > 0 if C887 */

			writebyte(REG4A, 0x08);		/* PAB: power is active, clear KF bit */

			writebyte(0x4b, 0x00);		/* disable everything */



			/************ use model byte to clear extended RAM here if needed **********/



			writebyte(REGA, (0x20 & 0xef) );	/* select bank 0 */

		}



		clockset();	/* prompt user for time and date */

	}

	writebyte(REGA, (rega & 0xef) );		/* go to bank 0 */

}

void	clockset()	/* ---------- user entries to set the time and date registers ------------ */

{

uchar	rega, cn, yr, mn, dt, dy, hr, min, sec, day;



	/* This routing does not check the operator inputs for errors.  In general, the clock registers will */

	/* accept any input, and does not validate the data in any way.  The RTC will NOT convert values if the */

	/* Data Mode (DM) or 24/12 mode bits are changed.  If an invalid value is entered in a register, the */

	/* clock will generally increment the invalid value until the bits that are checked for a valid */

	/* rollover value occurs. */



	printf("\n");

	if(model)

	{

		printf("Enter the century (19,20): ");

		scanf("%bx", &cn);

	}

	printf("Enter the year (0-99): ");

	scanf("%bx", &yr);

	printf("Enter the month (1-12): ");

	scanf("%bx", &mn);

	printf("Enter the date (1-31): ");

	scanf("%bx", &dt);



	/* Unlike most Dallas Real Time Clocks, the day assigned to each value on a clock with a Daylight Savings */

	/* Enable (DSE) does matter.  The Day of Week is used, along with the month and Date of Month registers */

	/* to determine when to adjust for daylight savings.  Operator entries routines should force the */

	/* day of week selection to make Sunday = 1, Monday = 2, etc. */



	printf("Enter the day (1-7): ");

	scanf("%bx", &dy);

	printf("Enter the hour (1-24): ");

	scanf("%bx", &hr);

	printf("Enter the minute (0-59): ");

	scanf("%bx", &min);

	printf("Enter the second (0-59): ");

	scanf("%bx", &sec);



	if(model)

	{

		rega = readbyte(REGA);

		writebyte(0x0a, (rega | 0x10) );	/* select bank 1 */

		if(model == 0x80)

			writebyte(0x32, cn);		/* bank 0, addr 0x32 for C887 */

		else

			writebyte(0x48, cn);		/* bank 1, addr 0x48 for RTCS w/bank 1 */

		writebyte(0x0a, rega);	/* select bank 0 */

	}

	writebyte(0, sec);

	writebyte(2, min);

	writebyte(4, hr);

	writebyte(6, dy);

	writebyte(7, dt);

	writebyte(8, mn);

	writebyte(9, yr);

}

uchar	updcrc(uchar crc, uchar val)	/* ---------- calculate 8-bit CRC ---------- */

{

uchar	inc, tmp;



	for(inc = 0; inc < 8; inc++)

	{

		tmp = crc << 7;		/* save X7 bit value */

		crc >>= 1;			/* shift crc */

		if( (tmp >> 7) ^  (val & 0x01) )	/* if X7 xor X8 (input data) */

		{

			crc ^= 0x8c;		/* xor crc with X4 and X5, X1 = X7^X8 */

			crc |= 0x80;		/* carry */

		}

		val >>= 1;

	}

	return	crc;

}

void Disp_Clk_Regs()		/* --------- check interrupt status via global variable -------------- */

{

/* With the periodic interrupt rate set to 2Hz, we have 250mS (1/2 Tpi) maximum to execute the routines */

/* that get us here and to execute the code below before we would have a problem with an update occuring */

/* while we read the clock.  We could also monitor UIP or inhibit update transfers with SET.  */



uchar rega, Cn = 0x20, Sec, Min, Hrs, Dte, Mon, Day, Yr;



	if(regc & 0x40)	/* if PF is true, display time & date, then clear the flag in the variable */

	{

		if(model)	/* if device supports century byte, read it */

		{

			rega = readbyte(REGA);

			writebyte(REGA, (rega | 0x10) );	/* select bank 1 */



			if(model == 0x80)

				Cn = readbyte(0x32);		/* bank 0, addr 0x32 for C887 */

			else

				Cn = readbyte(0x48);		/* bank 1, addr 0x48 for RTCS w/bank 1 */



			writebyte(REGA, rega);		/* select bank 0 */

		}



		Sec = readbyte(0);

		Min = readbyte(2);

		Hrs = readbyte(4);

		Day = readbyte(6);

		Dte = readbyte(7);

		Mon = readbyte(8);

		Yr  = readbyte(9);



		printf("\r%3s", DAYS + ( (Day - 1) *4) );

		printf(" %02.bX-%02.bX-%02.bX%02.bX", Mon, Dte, Cn, Yr);

		printf(" %02.bX:%02.bX:%02.bX  ", Hrs, Min, Sec);



		regc &= 0xbf;		/* clear PF bit in the variable */

	}



	if(regc & 0x20)	/* if AF is true, display time & date, then clear the flag */

	{

		if(model)	/* if device supports century byte, read it */

		{

			rega = readbyte(REGA);

			writebyte(REGA, (rega | 0x10) );	/* select bank 1 */



			if(model == 0x80)

				Cn = readbyte(0x32);		/* bank 0, addr 0x32 for C887 */

			else

				Cn = readbyte(0x48);		/* bank 1, addr 0x48 for RTCS w/bank 1 */



			writebyte(REGA, rega);		/* select bank 0 */

		}



		Sec = readbyte(0);

		Min = readbyte(2);

		Hrs = readbyte(4);

		Day = readbyte(6);

		Dte = readbyte(7);

		Mon = readbyte(8);

		Yr  = readbyte(9);



		printf("\n%3s", DAYS + ( (Day - 1) *4) );

		printf(" %02.bX-%02.bX-%02.bX%02.bX", Mon, Dte, Cn, Yr);

		printf(" %02.bX:%02.bX:%02.bX  Alarm\n", Hrs, Min, Sec);



		regc &= 0xdf;		/* clear PF bit in the variable */

	}

}

void	writereg()	/* ----------------- write to single register ----------------- */

{

uchar	addr, dat;



	printf("\renter address (hex): ");

	scanf("%bx", &addr);

	printf("\renter data (hex): ");

	scanf("%bx", &dat);

	writebyte(addr, dat);

}

void	external0_int(void) interrupt 0	/* --- display time/date on interrupt from RTC --- */

{

	WRALE = 0x0c;		/* latch register C address into RTC */

	regc = RWDATA;	/* read data from RTC */

}

main (void)		/* ----------------------------------------------------- */

{

uchar M, M1;



	EA = 1;	/* enable all interrupts */

	EX0 = 1;	/* enable interrupt 0 */

	EX1 = 0;	/* disable interrupt 1 */

	IT0 = 1;	/* edge activated */

	PX0 = 0;	/* low priority */



	init_rtc();



	printf("\nmuxb8051\n");

	printf("CS. Clock Set CW. Write Byte\n");

	printf("\nEnter Menu Selection:");



	while (1)

	{ 

		Disp_Clk_Regs();



		if(RI)

		{

			EX0 = 0;	/* disable interrupt 0 while processing user inputs */

			M = _getkey();	/* if a key is pressed, capture it */



			switch(M) 

			{

				case 'C':

				case 'c':

				printf("\rEnter Clock Routine to run: C");

				M1 = _getkey();



				switch(M1) 

				{

					case 'S':

					case 's':	clockset();	break;



					case 'W':

					case 'w':	writereg();	break;

				}

				break;



			}	/* end switch(M) */

			EX0 = 1;	/* enable interrupt 0 */

		}	/* end if(RI) */

		RI = M = 0;

	}

}