/* * Copyright � 2002 Parallax Inc. All rights reserved. */ package stamp.peripheral.rtc; import stamp.core.*; /** * This class encapsulates the capabilities of the Dallas DS1302 3-wire * real-time clock including the RAM and Super Cap charging. *
* This class only needs a small main class. For example add the constructor
* and the following lines of code:
*
* Memory overhead with the small example above you should see at least 25K free
*
* DS1302 t = new DS1302(CPU.pin1,CPU.pin2,CPU.pin3); // your pins may vary
* t.updateTimeDate();
* System.out.print("Time: ");
* System.out.println(t.printTime(true));
* System.out.print("Date: ");
* System.out.println(t.printDate());
*
* Day of week is represented by 1-7 (Sun-Sat). Zero is not supported by the * DS1302. The time set method will change a 0 for the day of week to a 1 * instead of throwing an error. *
* The constructor does not reset the time/date if invalid from power failure. *
* Use of RAM: The RAM is very useful to store rapidly changing information * like seconds or voltage that would otherwise wear out EEPROM either external * or inside the Javelin chip. The RAM routines are set up so the RAM address * wraps around and therefore any address can be used. Forgiving but still only * 31 bytes in size. *
* Backup power is through pin 8 of the DS1302. By using the charge(true);
* function a cap can be used instead of a battery. This feature is valuable for
* reliability, power failure monitoring or logging in remote locations.
*
* The clock routines whenever called check the timer t1. If t1 has elapsed, a
* time request to the DS1302 chip is made otherwise the time values from hour,
* minute, second... are used. This makes processing faster and keeps the clock
* and data lines free for any other chips on the "bus". The timer is adjusted
* via: UPDATE_PERIOD
.
*
* Future revisions will be made by creating a super class that will abstract * individual chips such as DS1307, DS1302 or the Pocket Watch B. So the * same code could work with different hardware. * *
* Revision History: * 07/29/02: Class originally submitted to Parallax Inc. * by customer Tim Constable of Boston, MA * Original Version 0.81 * 08/22/02: Class modified, enhanced and tested by Steve Dill of Parallax Inc. * Version 1.0 of DS1302 class approved. *
* @author Parallax Inc.
* @version 1.0 August 22, 2002
*/
public class DS1302 {
Timer t1 = new Timer();
// numeric (bin) time values updated internally. Very easy to
// read [class name].hour or [class name].minute
/**
* Numerical time value for hour.
* Range: 0-23, 0 hour = midnight
*/
static int hour;
/**
* Numerical time value for minute.
* Range: 0-59.
*/
static int minute;
/**
* Numerical time value for second.
* Range: 0-59.
*/
static int second;
/**
* Numerical date value for date of month.
* Range: 1-31
*/
static int date;
/**
* Numerical date value for month.
* Range: 1-12
*/
static int month;
/**
* Numerical date value for day of the week (Sun, Mon, Tues..).
* Range: 1-7 (do not use 0-6)
* Sun = 1, Mon = 2, Tue = 3, Wed = 4, Thu = 5, Fri = 6, Sat = 7
*/
static int dayOfWeek;
/**
* Numerical date value for year.
* Range: 0-99
*/
static int year;
/**
* Initialize DS1302 chip for r/w (write protect register)
* clock is not checked for valid time
* @parm dataPin, clockPin, enablePin
* @return Nothing
* @exception If clock chip does not exist
*/
public DS1302(int data, int clock, int enable){
dataPin = data;
clockPin = clock;
enablePin = enable;
// initialize pins -- make low for high-going pulses
CPU.writePin(enablePin,false); // init the bus
CPU.writePin(clockPin, false);
CPU.delay(SETTLE_TIME); // settle down
CPU.writePin(enablePin,true);
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,CLEAR_PROTECT);
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,0);// control byte
CPU.writePin(enablePin,false);
try {
int tmp = readRam(1); // test location 0
}
catch (Exception e) {
System.out.println("DS1302 Read Exception - Check wires");
}
}//end constructor: DS1302
/**
* Takes an integer and formats it to two byte string with a leading zero
*
* @parm num
* @return two character string
*
*/
public String numToString2(int num) {
buf3.clear();
if (num < 10) buf3.append("0");
buf3.append(num);
return (buf3.toString()); // "0" + String.valueOf(num)
}//end method: numToString2
/**
* Charge the Super Cap to allow backup power.
* Charge at least an hour to fully charge the Super Cap.
* This feature is very useful for keeping a valid time without external
* power. When using batteries instead of a Super Cap DO NOT set charge(true);
* this feature is not ment to charge batteries.
*
* @parm boolean
* @return Nothing
*
*/
public void charge(boolean data ){
CPU.writePin(enablePin,true);
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,0x90);
if (data) CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,0xA5);
else CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,0xF0);
CPU.writePin(enablePin,false);
CPU.delay(SETTLE_TIME); // chip settle time
}//end method: charge
/**
* Save one byte to RAM in the DS1302 chip.
* Allows all integers to map to 31 bytes
*
* @parm location (full int range), Data to be stored
* @return Nothing
*
*/
public void writeRam(int location, int data ){
location = Math.abs(location); // remove high order bit
location %= 31; // wrap RAM address
location = (location<<1) | 0xc0; // shifts 1 bit and adds C0 to location
CPU.writePin(enablePin,true);
// tell the DS1302 chip the "location"
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,location);
// tell the DS1302 chip the data you want to save.
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,data);
CPU.writePin(enablePin,false);
CPU.delay(SETTLE_TIME); // chip settle time
}//end method: writeRam
/**
* Get one RAM register from the DS1302 chip.
* Allows all integers to map to 31 bytes.
* This method will autowrap RAM values above 31 bytes.
* Example: RAM location 32 = Ram location 0
*
* @parm Location
* @return Ram data
*
*/
public int readRam(int location){
location = Math.abs(location); // remove high order bit
location %= 31; // wrap RAM address
location = (location<<1) | 0xc1; // shifts 1 bit and adds C1 to location
CPU.writePin(enablePin,true); // enable chip
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,location);
// 8 bits are received then shifted right 8 bits
// then the 8 high order bits are be cleared by & 0x00FF
// then assigned to a 16 bit value
clockIn = (CPU.shiftIn(dataPin,clockPin,8,CPU.PRE_CLOCK_LSB) >> 8) & 0x00FF;
CPU.writePin(enablePin,false); // disable chip
CPU.delay(SETTLE_TIME); // chip settle time
return clockIn; // return RAM register
}//end method: readRam
/**
* Read a specific DS1302 chip register contents in the RAW, unformatted form.
* See DS1302 docs for more detail.
* getRawTime will not generally be used except to further explore how the
* DS1302 works.
*
* Sample DS1302 chip Commands:
* READ_YEAR = 0x8d;
* READ_MONTH = 0x89;
* READ_DAY = 0x8b;
* READ_DATE = 0x87;
* READ_HOUR = 0x85;
* READ_MINUTE = 0x83;
* READ_SECOND = 0x81;
*
* @parm DS1302 chip command
* @return DS1302 chip register contents
*
*/
public int readRawTD(int comand){
CPU.writePin(enablePin,true); // turn on access to chip
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,comand);
// 8 bits are received then shifted right 8 bits
// then the 8 high order bits are be cleared by & 0x00FF
// then assigned to a 16 bit value
clockIn = (CPU.shiftIn(dataPin,clockPin,8,CPU.PRE_CLOCK_LSB) >> 8) & 0x00FF;
CPU.writePin(enablePin,false); // turn off access to chip
// all variables must be converted from BCD to binary
clockIn = (((clockIn & 0xf0)>>4)*10)+(clockIn & 0x0f);
CPU.delay(SETTLE_TIME); // chip settle time
return clockIn;
}//end method: readRawTD
/**
* Read a DS1302 chips' time/date register, for sake of speed, this data is
* unformatted. The RAW data's format follows the DS1302 data sheet.
* array[0] = second; // 0-59
* array[1] = minute; // 0-59
* array[2] = hour; // 0-23
* array[3] = date; // of Month 1-31
* array[4] = month; // 1-12
* array[5] = dayOfWeek; // 1-7 (Sun-Sat)
* array[6] = year; // 0-99
*
* @parm Index of time array 0-7
* @return Contents of time array
*
*/
public int readTD(int index){
updateTimeDate(); // update internal class variables
return byteArray[index]; // return requested value
}//end method: getTD
/**
* Read all time/date variables from DS1302 chip.
* 0 = hour; // 0-23
* 1 = minute; // 0-59
* 2 = second; // 0-59
* 3 = month; // 1-12
* 4 = date; // of Month 1-31
* 5 = year; // 0-99
* 6 = dayOfWeek; // 1-7 (Sun-Sat)
*
* @return array size 7
*
*/
public int[] readTimeDate(){
int array[] = new int[7];
return readTimeDate(array);
}//end method: readTimeDate
/**
* Read all time/date variables from DS1302 chip.
* 0 = hour; // 0-23
* 1 = minute; // 0-59
* 2 = second; // 0-59
* 3 = month; // 1-12
* 4 = date; // of Month 1-31
* 5 = year; // 0-99
* 6 = dayOfWeek; // 1-7 (Sun-Sat)
*
* @return array size 7
* @param array size 7
*
*/
public int[] readTimeDate(int[] array){
updateTimeDate(); // update Time/Date variables
array[0] = hour;
array[1] = minute;
array[2] = second;
array[3] = month;
array[4] = date;
array[5] = year;
array[6] = dayOfWeek;
return array; // return array
}//end method: readTimeDate
/**
* Forces the public time variables to be updated.
* This method is also called from within the DS1302 class
*
* @parm Nothing
* @return Nothing
*
*/
public void updateTimeDate(){
if (t1.timeout(UPDATE_PERIOD)) {
CPU.writePin(enablePin,true); // enable chip
// transfer 7 bytes of data all at once (bulk transfer)
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,BULK_READ); // bulk transfer
// read and convert each of the 7 bytes as they come in off the DS1302 chip
for (int i=0; i<=7; i++) {
// 8 bits are received then shifted right 8 bits
// then the 8 high order bits are be cleared by & 0x00FF
// then assigned to a int array[i]
byteArray[i] = (CPU.shiftIn(dataPin,clockPin,8,CPU.PRE_CLOCK_LSB) >> 8) & 0x00FF;
if (i==0) byteArray[0]&=~0x80; // If clock is halted, filter halt bit
// all variables must be converted from BCD to binary
byteArray[i] = (((byteArray[i] & 0xf0)>>4)*10)+(byteArray[i] & 0x0f);
}//end for
CPU.writePin(enablePin,false);
t1.mark();
}//end if
second = byteArray[0]; // 0-59
minute = byteArray[1]; // 0-59
hour = byteArray[2]; // 0-23
date = byteArray[3]; // 1-31
month = byteArray[4]; // 1-12
dayOfWeek = byteArray[5]; // 1-7 = Sun-Sat
year = byteArray[6]; // 0-99
// control byte, byteArray[7], not used
}//end method: updateTimeDate
/**
* Increment one field on DS1302 chips' time/date register.
* array[0] = second; // 0-59
* array[1] = minute; // 0-59
* array[2] = hour; // 0-23
* array[3] = date; // of Month 1-31
* array[4] = month; // 1-12
* array[5] = dayOfWeek; // 1-7 (Sun-Sat)
* array[6] = year; // 0-99
*
* @parm Index of time array 0-7
*
*/
public void incValue (int index)
{
updateTimeDate();
byteArray[index]++;
writeTime(byteArray[2], byteArray[1], byteArray[0], byteArray[4], byteArray[3], byteArray[6], byteArray[5]);
}
/**
* Decrement one field on DS1302 chips' time/date register.
* array[0] = second; // 0-59
* array[1] = minute; // 0-59
* array[2] = hour; // 0-23
* array[3] = date; // of Month 1-31
* array[4] = month; // 1-12
* array[5] = dayOfWeek; // 1-7 (Sun-Sat)
* array[6] = year; // 0-99
*
* @parm Index of time array 0-7
*
*/
public void decValue (int index)
{
updateTimeDate();
byteArray[index]--;
writeTime(byteArray[2], byteArray[1], byteArray[0], byteArray[4], byteArray[3], byteArray[6], byteArray[5]);
}
/**
* Format the time (12hr/24hr). Useful for a preformatted time stamp.
*
* @parm TRUE for 12hr (am/pm) format
* @parm FALSE for 24 hour (military) format
* @return Time String
*
*/
public String readTime(boolean ap) {
boolean pmFlag = false; // create flag for pm in 12hr mode
updateTimeDate(); // update internal class variables
buf10.clear(); // clear buffer
if (ap && (hour > 12)) { // if 12hr clock and time > 12
pmFlag = true; // set flag to pm
hour -= 12; // adjust time from 24hr clock
}//end if
if (ap && hour == 0) hour = 12; // adjust for 12am
// create string
buf10.append(numToString2(hour));
buf10.append(":");
buf10.append(numToString2(minute));
buf10.append(":");
buf10.append(numToString2(second));
// add am/pm character to return string if displaying 12hr time
if (ap) {
if (pmFlag) buf10.append("p");
else buf10.append("a");
}//end if
return (buf10.toString());
}//end method: readTime
/**
* Format the date to US style, useful for date stamp on screen
*
* @parm Nothing
* @return Date String
*
*/
public String readDate() {
updateTimeDate(); // update internal class variables
buf10.clear();
buf10.append(numToString2(month));
buf10.append("/");
buf10.append(numToString2(date));
buf10.append("/");
buf10.append(numToString2(year));
return (buf10.toString()); // return formatted string
}//end method: readDate
/**
* Returns the day of the week.
* TRUE - returns long format
* FALSE - returns short format
*
* @parm true for long version, false for short 3 character name
* @return string
*
*/
public String readDay(boolean b) {
updateTimeDate(); // update internal class variables
if (b) return (DAY_OF_WEEK_LONG[dayOfWeek]); // return long format
else return (DAY_OF_WEEK_SHORT[dayOfWeek]); // return short format
}//end method: readDay
/**
* Set all the time variables in the DS1302 chip.
* hour - hr (0-23)
* minute - min (0-59)
* seconds - sec (0-59)
* month - mo (1-12)
* date - date (1-31)
* year - yr (0-99)
* dayOfWeek - dayOfWeek (1-7), Sun=1 - Sat=7
*
* Force any Zero values for dayOfWeek, date, and month(mo) to a '1'
* Values exceeding maximums will be changed to minimum values
* Example: if sec > 60 then sec=0.
* @parm sec, min, hr, date, mo, dayOfWeek, yr
* @return Nothing
*
*/
public void writeTime(int hr, int min, int sec, int mo, int date, int yr, int dayOfWeek){
// Force any Zero values for DayOfWeek, date, and Month to a 1
if (mo == 0) {mo = 1;}
if (date == 0) {date = 1;}
if (dayOfWeek == 0) {dayOfWeek = 1;}
// Time setting routines for auto rollover (no carry)
if (sec > 59) {sec = 0;}
if (min > 59) {min = 0;}
if (hr > 23) {hr = 0;}
if (date > 31) {date = 1;}
if (mo > 12) {mo = 1;}
if (dayOfWeek > 7) {dayOfWeek = 1;}
if (yr > 99) {yr = 0;}
CPU.writePin(enablePin,true); // enable chip
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,BULK_WRITE); // load command
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,btoBCD(sec)); // seconds
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,btoBCD(min)); // min
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,btoBCD(hr)); // hour
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,btoBCD(date)); // date of Month
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,btoBCD(mo)); // month
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,btoBCD(dayOfWeek));// day
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,btoBCD(yr)); // year
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,0); // control !!
CPU.writePin(enablePin,false); // disable chip
CPU.delay(UPDATE_PERIOD * 10); // wait for update
}//end method: writeTime
/**
* Halts the dsa1302 chip
* @parm True to halt clock, False to resume.
* @return nothing
*
*/
public void halt(boolean b) {
updateTimeDate(); // Update variables
int sec=second;
sec=btoBCD(sec); // Convert to BCD
if (b) sec |= 0x80; // Set halt bit
else sec &= ~0x80; // Clear halt bit
CPU.writePin(enablePin,true); // enable chip
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,128); // Send command
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,sec); // Send halt bit
CPU.writePin(enablePin,false); // disable chip
}//end method: halt
/**
* Protects the ds1302 chip from accidential writes
* @param TRUE turns on protection (cannot write)
* @param FALSE turns off protection (allow writes)
* @return nothing
*
*/
public void protect(boolean b) {
CPU.writePin(enablePin,true); // enable chip
CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,0x8E);// Send command
if (b) CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,0x80);// Set writeProtect bit
else CPU.shiftOut(dataPin,clockPin,8,CPU.SHIFT_LSB,0); // Set writeProtect bit
CPU.writePin(enablePin,false); // disable chip
}//end method: protect
//============================================================================
// Private methods and fields below this point.
//============================================================================
private int dataPin; // DS1302 pin used for data
private int clockPin; // DS1302 pin used for clocking
private int enablePin; // DS1302 pin used to enable chip
private int clockIn; // storage for clocked data
private final static int BULK_READ = 0xbf; // DS1302 chip command
private final static int BULK_WRITE = 0xbe; // DS1302 chip command
private final static int CLEAR_PROTECT = 0x8e; // DS1302 chip command
private final static int SETTLE_TIME = 500; // delay time, can be tweaked
private int[] byteArray = new int[9]; // transfer Time/Date array
private static StringBuffer buf3 = new StringBuffer(3); // formatted output
private static StringBuffer buf10 = new StringBuffer(10); // formatted output
// Day of week should always be 1-7. Zero is not supported by the DS1302 chip
private final static String[] DAY_OF_WEEK_LONG = {"BAD", "Sunday", "Monday",
"Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
private final static String[] DAY_OF_WEEK_SHORT = {"BAD", "Sun", "Mon", "Tue",
"Wed", "Thr", "Fri", "Sat"};
// Considerations on setting the UPDATE_PERIOD:
// A smaller number will update the time variables faster. Updating
// frequently will slow overall processing when making frequent requests.
// A value between 100 - 500 is best to keep seconds running smooth.
// A higher value will keep the Clock/Data buss free of traffic but also
// make updating seconds occur much slower.
private final static int UPDATE_PERIOD = 333;
// Private binary to BCD used to load data into the DS1302 chips' time registers
private int btoBCD(int data) {
data = ((data / 10)<<4) + (data % 10); // Convert binary to BCD
return data;
}//end method: btoBCD
}//end class: DS1302