/***************************************************************************/ /* 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 /* Prototypes for I/O functions */ #include /* Register declarations for DS5000 */ #include /* 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; } }