Sunteți pe pagina 1din 18

Interfacing the BASIC Stamp 2 with a DS1302 Real Time Clock

copyright, LaVerne Ervin and Peter H. Anderson, Baltimore, MD, Apr, '99

Introduction

This discussion focuses on the Dallas DS1302 Timekeeping Chip. A data sheet is
available at http://www.dalsemi.com. We have the DS1302 in an 8-pin DIP for $3.00 and
the 32.768 kHz crystal for $0.50.

This effort was initiated by undergraduate student LaVerne Ervin as her capstone design
project (2 credits). Her work was very well done and enabled me to quickly make minor
revisions and build on her work.

The DS1302 performs two functions; a 31 byte RAM and a real time clock. Clearly, most
people probably use the real time clock feature, but the RAM might also be of value. For
example; the Parallax BASIC Stamp 2 provides 26 RAM locations. The DS1302 might
be used to add another 31 much like the BS2SX adds 63 RAM bytes

This discussion attempts to discuss all of the features of the DS1302 in the context of
interfacing with the Parallax BASIC Stamp 2. However, BASIC is pretty "basic" and
hopefully this discussion proves of value to people using other target processors.

This was written over the Christmas, '98 break. All of the routines except the last routines
related to computing the Julian date and the number of seconds since the beginning of the
year were tested on a BASIC Stamp 2. The last material relating to Julian date was not
tested.

Overview of the Operation.

The interface consists of three wires; /RST, SCLK and I/O. /RST and SCLK are inputs to
the DS1302 (outputs as viewed by the controlling processor) and I/O is bi-directional.

Normally, /RST is low. All communication sequences are intitiated by driving /RST high.
A communications sequence is terminated by bringing /RST low. SCLK must be low
when /RST is brought to a logic one to initiate a sequence.

Data, including commands is sent to the DS1302 by first bringing I/O to the appropriate
state while SCLK is low. The data bit is then clocked into the DS1302 on the rising edge
of the SCLK. All data, including commands is sent as a byte, least significant bit first.

Data is read from the DS1302 on lead I/O. Each bit is read on the falling edge of SCLK.
Here again, data is read beginning with the least significant bit.

Note that /RST is high throughout the entire interchange.


As with all intelligent serial devices, a data interchange begins with a command byte of
the form;

7 6 5 4 3 2 1 0

1 RAM/CLK A4 A3 A2 A1 A0 RD/WR
Note that RAM is selected by making bit 6 a 1. A read or write operation is indicated by
bit 0 being either a logic 0 or logic 1, respectively.

Thus if one desires to write to RAM location %0 1100, the command byte is;

7 6 5 4 3 2 1 0

1 RAM/CLK A4 A3 A2 A1 A0 RD/WR

1 1 0 1 1 0 0 0
Note that a general expression for writing to RAM memory is;
%1100 0000 | (ADR << 1)

or $C0 | (ADR << 1)


Similarly, a general expression for reading from RAM memory is;
%1100 0001 | (ADR << 1)

or $C1 | (ADR << 1)


Writing and reading from the CLK is similar, except that bit 6 is a logic zero.

Thus, writing to CLK address %0 0011, which is the date within a month, the command
byte is;

7 6 5 4 3 2 1 0

1 RAM/CLK A4 A3 A2 A1 A0 RD/WR

1 0 0 0 0 1 1 0
Thus, general expressions for writing and reading to the clock;
$80 | (ADR << 1) ' write to clock
$81 | (ADR << 1) ' read from clock
Before, we get anymore confused, lets take a look at a simple routine to write to and read
from RAM memory.

Circuit Connection.

In all of the following discussions, connections between the BS2 and the DS1302 are;

BS2 DS1302

P2 (term 7) <-----------10K R---------------> I/O (term 6)

P1 (term 6) --------------------------------> SCLK (term 7)


P0 (term 5) --------------------------------> /RST (term 5)

Writing to and Reading from RAM.

In program PUT_GET, two subroutines; _PUT and _GET are presented. These are quite
like the PUT and GET commands associated with the BS2SX.

' Program PUT_GET.BS2


'
' Illustrates how to write to and read from the DS1302 RAM.
'
' copyright, LaVerne Ervin, Baltimore, MD, Dec, '98

VAL VAR BYTE


ADDR VAR BYTE

O_BYTE VAR BYTE


N VAR BYTE

MAIN:
DIRS = $0007 ' /RST, SCLK and I/O are outputs

ADR=3
VAL=125
GOSUB _PUT ' write 125 to RAM address 3

ADR=3 ' read from address 3 and display


GOSUB _GET
DEBUG ? VAL

DONE:
GOTO DONE

_PUT: ' writes VAL to ADR. Uses bytes O_BYTE and N


DIR2=1 ' be sure I/O is an output
OUT0=0
OUT1=0
OUT0=1 ' bring /RST high while SCLK is low

O_BYTE=$C0|(ADR<<1) ; write RAM command


GOSUB OUT_BYTE
O_BYTE = VAL
GOSUB OUT_BYTE
OUT0=0 ' /RST brought low to terminate the sequence
RETURN

_GET: ' reads from ADR into VAL. Uses bytes O_BYTE and N
DIR2=1
OUT0=0
OUT1=0
OUT0=1 ' bring /RST high while SCLK is low
O_BYTE=$C1|(ADR<<1) ' read RAM command
GOSUB OUT_BYTE
DIR2=0
GOSUB IN_VAL
OUT0=0 ' /RST low to terminate the sequence
RETURN

OUT_BYTE: ' shift out O_BYTE on I/O, least sig bit first
FOR N=0 TO 7
OUT2= O_BYTE.BIT0
GOSUB CLK_PULSE
O_BYTE = O_BYTE >>1
NEXT
RETURN

IN_VAL: ' shift in on I/O, least sig bit first


FOR N=0 TO 7
GOSUB CLK_PULSE
VAL = VAL >>1
VAL.BIT7 =IN2
NEXT
RETURN

CLK_PULSE:
OUT1=1
OUT1=0

Discussion.

In subroutine OUT_BYTE, variable O_BYTE is shifted out to the DS1302, beginning


with the least significant bit. Note that OUT2 (I/O) is set to the value of .BIT0 followed
by a clock pulse. O_BYTE is then shifted to the right such that the next bit is now in the
least significant bit position.

In subroutine IN_VAL, a clock pulse occurs. VAL is shifted to the right and the data bit
which is read is placed in the most significant bit position.

These routines might also be implemented using the SHIFTOUT and SHIFTIN
commands. I am not a big fan of these commands during the intitial development as I
tend to lose sight of exactly what I am doing.

However, I have to admit that once you are beyond the initial development, the
SHIFTOUT and SHIFTIN are a whole lot more efficient

OUT_BYTE:
SHIFTOUT 2, 1, LSBFIRST, [O_BYTE\8]
RETURN

IN_VAL:
SHIFTIN 2, 1, LSBPOST, [IN_VAL\8]
RETURN
These later implementations will be used in all subsequent discussions.
In higher level routine, _PUT, the /RST terminal is brought high, the command byte is
sent followed by the data byte and /RST is then brought low.

In _GET, the /RST terminal is brought high, the command byte to read is sent, the data is
read and the sequence is terminated by bringing /RST low.

Arrays.

' ARRAY0.BS2
'
' Illustrates how to use DS1302 memory to implement an array.
' Array is dummied up. Average, MAX and MIN are calculated and
' displayed.
'
' LaVerne Ervin, Baltimore, MD, Dec, '98

SUM VAR WORD


AVG_10 VAR WORD ' ten times the average
_MAX VAR BYTE
_MIN VAR BYTE

VAL VAR BYTE


ADDR VAR BYTE

O_BYTE VAR BYTE

FOR ADR=0 TO 29
VAL = ADR//3
GOSUB _PUT ' put some numbers into the DS1302
NEXT

' Compute the average

SUM = 0
FOR ADR=0 TO 29
GOSUB _GET
SUM = SUM + VAL
NEXT

AVG_10 = SUM * 10 / 50
DEBUG "AVG = ", DEC AVG_10/10, ".", DEC AVG_10//10, CR

' Now for the MIN

_MIN = 255

FOR ADR=0 TO 29
GOSUB _GET
IF (VAL>=_MIN) THEN SKIP_1
_MIN = VAL ' VAL is less than _MIN
SKIP_1:
NEXT
DEBUG "MIN = ", DEC _MIN, CR
' Now for the MAX

_MAX = 0

FOR ADR=0 TO 29
GOSUB _GET
IF (VAL<=_MAX) THEN SKIP_2
_MAX = VAL ' VAL is greater than MAX
SKIP_2:
NEXT
DEBUG "MAX = ", DEC _MAX, CR

DONE:
GOTO DONE

'''''''''

_PUT: ' writes VAL to ADR. Uses byte O_BYTE


DIR2=1 ' be sure I/O is an output
OUT0=0
OUT1=0
OUT0=1 ' bring /RST high while SCLK is low

O_BYTE=$C0|(ADR<<1) ; write RAM command


GOSUB OUT_BYTE
O_BYTE = VAL
GOSUB OUT_BYTE
OUT0=0 ' /RST brought low to terminate the sequence
RETURN

_GET: ' reads from ADR into VAL. Uses bytes O_BYTE and N
DIR2=1
OUT0=0
OUT1=0
OUT0=1 ' bring /RST high while SCLK is low
O_BYTE=$C1|(ADR<<1) ' read RAM command
GOSUB OUT_BYTE

DIR2=0
GOSUB IN_VAL
OUT0=0 ' /RST low to terminate the sequence
RETURN

OUT_BYTE:
SHIFTOUT 2, 1, LSBFIRST, [O_BYTE\8]
RETURN

IN_VAL:
SHIFTIN 2, 1, LSBPOST, [VAL\8]
RETURN
RAM Burst Mode.

Note that only RAM locations 0-30 (31 bytes) are available for general purpose RAM.
RAM location 31 is reserved for placing the DS1302 in a burst mode where multiple
values are written to or read from the DS1302 in a single data exchange. Thus, $C0 | ($1F
<< 1) or $FE places the DS1302 in a RAM burst write mode and $C1 | ($1F << 1) or $FF
places it in the RAM burst read mode.

' ARRAY1.BS2
'
' Illustrates how to use DS1302 memory to implement an array.
' Array is dummied up. The average is calculated and displayed.
'
' Illustrates the use of RAM burst write and RAM burst read modes.
'
' copyright, LaVerne Ervin, Baltimore, MD, Dec, '98

SUM VAR WORD


AVG_10 VAR WORD ' ten times the average

VAL VAR BYTE


N VAR BYTE

DIRS=$0007

' write 30 values to RAM in burst mode

DIR2=1 ' be sure I/O is an output


OUT0=0
OUT1=0
OUT0=1 ' bring /RST high while SCLK is low
SHIFTOUT 2, 1, LSBFIRST, [$FE\8]
' RAM burst write command byte

FOR N=0 TO 29
VAL = N//3
SHIFTOUT 2, 1, LSBFIRST, [VAL\8] ' send 30 bytes
NEXT
OUT0=0 ' terminate the data sequence

' Compute the average

SUM = 0

DIR2=1 ' be sure I/O is an output


OUT0=0
OUT1=0
OUT0=1 ' bring /RST high while SCLK is low
SHIFTOUT 2, 1, LSBFIRST, [$FE\8] ' RAM burst read

DIR2=0 ' be sure I/O is an input


FOR N=0 TO 29
SHIFTIN 2, 1, LSBPOST, [VAL\8] ' fetch each byte
SUM = SUM + VAL ' and add to the sum
NEXT
OUT0=0 ' terminate the data sequence
AVG_10 = SUM * 10 / 30
DEBUG "AVG = ", DEC AVG_10/10, ".", DEC AVG_10//10, CR

DONE:
GOTO DONE
Intermediate Summary.

Thus far, I have focused on the RAM portion of the DS1302.

Note that very little code is required; bring /RST high, send the command byte, either
$C0 | (ADR << 1) for reading or $C1 | (ADR << 1) followed by either writing or reading
a byte and then bring /RST low. The burst mode is simply a matter of sending $FE and
then sequentially writing byte by byte or, when reading, sending command byte $FF and
then sequentially reading byte by byte.

The BS2SX adds 63 bytes of scratchpad RAM which are fetched and stored using the
new commands GET and PUT. The DS1302 may be a somewhat less expensive
alternative, particularly if you need the real time clock capability.

Real Time Clock.

Operation is similar to that discussed above except that the command byte addresses the
clock portion of the DS1302.

7 6 5 4 3 2 1 0

1 RAM/CLK A4 A3 A2 A1 A0 RD/WR
Note that bit 6 is set to a zero to address the clock. Thus, the general forms of the
command byte are;
$80 | (ADR <<1) ' write to CLK ADR
$81 | (ADR <<1) ' read from CLK ADR
Each address corresponds to an element of the date and time or control as summarized in
the following constant table;
SEC CON 0
MIN CON 1
HR CON 2
DATE CON 3
MONTH CON 4
DAY CON 5
YEAR CON 6
CONTROL CON 7
TRICKLE CON 8
BURST CON 9
Note that addresses 10-31 are not used.

Thus, to write the "seconds" which might be in Stamp variable VAL

' bring /RST high


ADR = SECS
O_BYTE = $81 | (ADR << 1)
GOSUB OUT_BYTE
O_BYTE = VAL
GOSUB OUT_BYTE
' bring /RST low
Prior to forging into a simple routine that illustrates how to write the time and then
periodically read the time, the are a few bookkeeping matters.

The control byte consists of a single bit, bit 7, which is Write Protect. On powerup, the
state of this bit is not defined. Thus, prior to writing a date and time to the DS1302, the
normal procedure would be to write a zero to bit 7 of address CONTROL, write to the
time and date addresses and then write a logic 1 to the write protect bit.

' CLK_1.BS2
'
' Initializes DS1302 40th sec, 59th min, 11th hour PM, 31st day,
' 12th month, day 5 (Friday), 99th year. (20 secs short of the
' millenium)
'
' Continually reads clock and displays using the debug command.
'
' copyright, LaVerne Ervin, Baltimore, MD, Dec, '98

C_SEC CON 0
C_MINUTE CON 1
C_HOUR CON 2
C_DATE CON 3
C_MONTH CON 4
C_DAY CON 5
C_YEAR CON 6
CONTROL CON 7
TRICKLE CON 8
BURST CON 31

DATE_TIME DATA $40, $59, $23, $31, $12, $5, $00


' secs
DIRS=$0007

ADR VAR BYTE


VAL VAR BYTE

DIRS=$0007
' clear the write protect bit

DIR2=1 ' be sure I/O is an output


OUT0=0
OUT1=0
OUT0=1 ' bring /RST high while SCLK is low

SHIFTOUT 2, 1, LSBFIRST, [$80 | (CONTROL<<1) \8]


SHIFTOUT 2, 1, LSBFIRST, [$00\8]

OUT0=1 ' bring /RST low to terminate

' Now write the date and time, one byte at a time.
FOR ADR=0 TO 6
READ DATE_TIME+ADR, VAL ' fetch value from EEPROM
GOSUB _PUT_TIME ' and write it to the clock
NEXT

' set the write protect bit

DIR2=1 ' be sure I/O is an output


OUT0=0
OUT1=0
OUT0=1 ' bring /RST high while SCLK is low

SHIFTOUT 2, 1, LSBFIRST, [$80 | (CONTROL<<1) \8]


SHIFTOUT 2, 1, LSBFIRST, [$80\8] ' 1 in most sign bit

OUT0=1 ' bring /RST low to terminate

' Now, display the date and time in YY/MM/DD HH:MM:SS format
DISPLAY_DATE_TIME:

ADR = C_YEAR
GOSUB _GET_TIME
DEBUG HEX2 VAL, "/"

ADR = C_MONTH
GOSUB _GET_TIME
DEBUG HEX2 VAL, "/"

ADR = C_DATE
GOSUB _GET_TIME
DEBUG HEX2 VAL, " "

ADR = C_HOUR
GOSUB _GET_TIME
DEBUG HEX2 VAL, ":"

ADR = C_MINUTE
GOSUB _GET_TIME
DEBUG HEX2 VAL, ":"

ADR = C_SEC
GOSUB _GET_TIME
DEBUG HEX2 VAL, CR

PAUSE 1000 ' wait for a second


GOTO DISPLAY_DATE_TIME

'''''
_PUT_TIME:
DIR2=1 ' be sure I/O is an output
OUT0=0
OUT1=0
OUT0=1 ' bring /RST high while SCLK is low

SHIFTOUT 2, 1, LSBFIRST, [$80 | (ADR<<1)\8] ' command byte


SHIFTOUT 2, 1, LSBFIRST, [VAL\8]
OUT0=0 ' terminate the sequence

RETURN

_GET_TIME:
DIR2=1 ' be sure I/O is an output
OUT0=0
OUT1=0
OUT0=1 ' bring /RST high while SCLK is low
SHIFTOUT 2, 1, LSBFIRST, [$81 | (ADR<<1)\8] ' command byte
DIR2=0
SHIFTIN 2, 1, LSBPRE, [VAL\8]
OUT0=0

RETURN

Discussion.

In the above, the write protect bit is set to zero. The time and date info is sequentially
read from the Stamp's EEPROM and written to the DS1302. The write protect bit is then
set to a logic one to protect against accidental writes. The date and time are then read
every second and displayed using the DEBUG command.

I happened to use a time which is 20 seconds short of the millenium and indeed, it did
roll over to the new century. However, there is another distinction between the year 2000
and 1900 in that the year 2000 is a leap year, 1900 wasn't. (A leap year occurs every four
years, except at the turn of the century. However, every 400 years, the turn of the century
is a leap year. Thus, the Y2K problem is a bit more complex than at first glance.) I tried
Feb 28, 00 at 23:59:40 and indeed it rolled over to Feb 29.

Note that the date and time quantities are in binary coded decimal (BCD). That is, 32
seconds is saved in the DS1302 as %0011 0010, not as %0010 0000. Thus, note that the
values in EEPROM are saved in hexadecimal format and the results are displayed using
the HEX2 modifier.

However, if you plan to be doing calculations, the quantities must be converted from
BCD to natural binary. For example, assume the quantities are in variables SECS, MINS,
DATE, MONTHS and YEARS. Note that HOURS is discussed later.

SECS = SECS & $7F ' take only the lower 7 bits
SECS = SECS.NIB1 * 10 + SECS.NIB0

MINS = MINS & $7F


MINS = MINS.NIB1 * 10 + MINS.NIB0

HOURS = HOURS & $3F


HOURS = HOURS.NIB1 * 10 + HOURS.NIB0

DATE = DATE & $3F ' take only the lower 6 bits
DATE = DATE.NIB1 * 10 + DATE.NIB0
MONTHS = MONTHS & $1F
MONTHS = MONTHS.NIB1 * 10 + MONTHS.NIB0

YEARS = YEARS.NIB1 * 10 + YEARS.NIB0


The hours is a bit more complex.

When writing to C_HRS, bit 7 indicates whether the DS1302 is to operated in 12-hour
(logic 1) or 24 hour (logic zero) mode. In the above program, the number of hours was
programmed as $23. Note that bit 7 is a zero, and thus the clock was in a 24 hour mode.
Thus, in this case, the number of hours as shown above.

Note that the DS1302 may be placed in a 12-hour mode by setting bit 7 to a 1 and using
bit 5 to indicate either AM (logic 1) or PM (logic 0). This discussion does not include any
examples of the 12 hour mode.

Program CLK_2.BS2 performs a similar function as CLK_1, except the burst mode is
used to write the data and the date and time are read into variables. This enables you to
use the DS1302 to time between events or to take an action at a particular time. In
addition, the program illustrates how the date may be expressed in the form;

Friday
Dec 31, '99, 23:59:40
' CLK_2.BS2
'
' Initializes DS1302 40th sec, 59th min, 11th hour PM, 31st day,
' 12th month, day 5 (Friday), 99th year. (20 secs short of the
' millenium) using the burst mode.
'
' Continually reads clock and displays using the debug command.
'
' Illustrates how to display strings.
'
' Note that date and time is saved in variables.
'
' copyright, Peter H. Anderson, Baltimore, MD, Dec, '98

C_SEC CON 0
C_MINUTE CON 1
C_HOUR CON 2
C_DATE CON 3
C_MONTH CON 4
C_DAY CON 5
C_YEAR CON 6
CONTROL CON 7
TRICKLE CON 8
BURST CON 31

DATE_TIME DATA $40, $59, $23, $31, $12, $5, $00

JAN DATA "Jan", 0


FEB DATA "Feb", 0
MAR DATA "Mar", 0
APR DATA "Apr", 0
MAY DATA "May", 0
JUN DATA "Jun", 0

JUL DATA "Jul", 0


AUG DATA "Aug", 0
SEP DATA "Sep", 0
OCT DATA "Oct", 0
NOV DATA "Nov", 0
DEC DATA "Dec", 0

SUN DATA "Sunday", 0


MON DATA "Monday", 0
TUE DATA "Tuesday", 0
WED DATA "Wednesday", 0
THU DATA "Thursday", 0
FRI DATA "Friday", 0
SAT DATA "Saturday", 0

' the following is a table of the days of the week


WEEK_DAYS DATA WORD SUN, WORD MON, WORD TUE, WORD WED
DATA WORD THU, WORD FRI, WORD SAT

DIRS=$0007

ADR VAR BYTE


VAL VAR BYTE

YEARS VAR BYTE


MONTHS VAR BYTE
MO_DAYS VAR BYTE

HOURS VAR BYTE


MINS VAR BYTE
SECS VAR BYTE

WK_DAY VAR BYTE


PTR VAR WORD

DIRS=$0007
' clear the write protect bit

DIR2=1 ' be sure I/O is an output


OUT0=0
OUT1=0
OUT0=1 ' bring /RST high while SCLK is low

SHIFTOUT 2, 1, LSBFIRST, [$80 | (CONTROL<<1) \8]


SHIFTOUT 2, 1, LSBFIRST, [$00\8]

OUT0=1 ' bring /RST low to terminate

' Now write the date and time, in the burst mode.
' Note that eight bytes are written

DIR2=1 ' be sure I/O is an output


OUT0=0
OUT1=0
OUT0=1 ' bring /RST high while SCLK is low

SHIFTOUT 2, 1, LSBFIRST, [$80 | (BURST<<1) \8]

FOR ADR=0 TO 6
READ DATE_TIME+ADR, VAL ' fetch value from EEPROM
SHIFTOUT 2, 1, LSBFIRST, [VAL\0] ' and write it to the
clock
NEXT

' set the write protect bit

SHIFTOUT 2, 1, LSBFIRST, [$80\8] ' 1 in most sign bit

OUT0=1 ' bring /RST low to terminate

' Now, fetch the date and time


GET_DATE_TIME:

ADR = C_YEAR
GOSUB _GET_TIME
YEARS = VAL.NIB1*10 + VAL.NIB0 ' convert BCD to natural binary

ADR = C_MONTH
GOSUB _GET_TIME
VAL = VAL & $1F
MONTHS = VAL.NIB1*10 + VAL.NIB0

ADR = C_DATE
GOSUB _GET_TIME
VAL = VAL & $3F
MO_DAYS = VAL.NIB1*10 + VAL.NIB0

ADR = C_HOUR
GOSUB _GET_TIME
VAL = VAL & 3F
HOURS = VAL.NIB1*10 + VAL.NIB0

ADR = C_MINUTE
GOSUB _GET_TIME
VAL = VAL & $7F
MINS = VAL.NIB1*10 + VAL.NIB0

ADR = C_SEC
GOSUB _GET_TIME
VAL = VAL & $7F
SECS = VAL.NIB1*10 + VAL.NIB0

ADR = C_DAY ' day of the week


GOSUB _GET_TIME
WK_DAY = VAL & $07

DISPLAY:
' Now display the day of the week
' map the week day into a pointer that points to beginning of desired
' string

READ WEEK_DAYS + (2*WK_DAY), PTR.BYTE0


READ WEEK_DAYS + (2*WK_DAY)+1, PTR.BYTE1
GOSUB DISPLAY_STR

DEBUG CR

' Now, the month

PTR = JAN + 4*(MONTHS-1)


GOSUB DISPLAY_STR

' Now the date and time


DEBUG " ", DEC MO_DAYS, ", '", DEC2 YEARS, " "
DEBUG DEC2 HOURS, ":", DEC2 MINS, ":", DEC2 SECS, CR

PAUSE 1000
GOTO GET_DATE_TIME ' continual loop

DISPLAY_STR:
READ PTR, X
IF X=0 THEN STR_OUT_DONE
DEBUG X
P=PTR+1
GOTO DISPLAY_STR
RETURN

_GET_TIME:
DIR2=1 ' be sure I/O is an output
OUT0=0
OUT1=0
OUT0=1 ' bring /RST high while SCLK is low
SHIFTOUT 2, 1, LSBFIRST, [$81 | (ADR<<1)\8] ' command byte
DIR2=0
SHIFTIN 2, 1, LSBPRE, [VAL\8]
OUT0=0

RETURN
Discussion.

In CLK2.BS2, the "write protect" bit on the Control Address is first set to zero.

The "burst" mode is then used to transmit all eight bytes of data are transmitted in a
single data exchange. Note that the the command byte, $80 | ($1F) is followed by eight
bytes for locations 0 through 7. Addresses 0 through 6 correspond to the time and date
and finally $80 is written to the Control register (address 7) to again place the clock in the
write protect mode.

Note that with the RAM burst, the sending of consecutive bytes may be interrupted at any
time. That is, not all 31 bytes need be transferred.
However, with the CLK burst, all eight bytes (0 - 7) must be transmitted. Note that this
includes the Control register.

In CLK2.BS2, the time and date are periodically read and displayed.

The day of the week is first read. This is then mapped into an PTR address of the
beginning of the associated string as follows;

READ WEEK_DAYS + (2*(WK_DAY-1)), PTR.BYTE0


READ WEEK_DAYS + (2*(WK_DAY-1)+1, PTR.BYTE1
For example, if the week day is Sunday, WK_DAY is 1. Thus, the pointer PTR is read
from WEEK_DAYS + 0 (low byte) and WEEK_DAYS + 1 (high byte).

Computing the Julian Date.

Note that the following material was developed some time ago and unfortunately I put it
away in favor of something more interesting. Thus, this has not been tested nor is it
complete. I hesitate to simply delete it as it may be of use to someone.

The Julian Date is simply the day of the year. Thus, Jan 31 is day 31. Feb 28 is day 59. If
the year is not a leap year, Dec 31 is day 365.

The importance of the Julian date is that it enables one to calculate the number of seconds
between two times. For example, it is mighty hard to calculate the number of days
between Mar 1 and Nov 25, but mighty simple to calculate the number of days between
Julian day 61 and 325. (Note that I don't really know if Mar 1 and Nov 25 days 61 and
325.)

In the following code fragment, an array of the days in each month is implemented in
EEPROM. Note that there are 13 entries, with element 0 being any value. That is, it really
isn't used. Thus, the number of days in month N month may be read into TMP;

READ DAYS_IN_MONTH+N, TMP


Lets avoid the leap year problem for the moment. Assume the date is Jan 7 (MONTH=1,
DAY=7). You really don't care how many days there are in January. The Julian date is
simply the DAY. However, if the day is March 7, the Julian date is the number of days in
Jan (310) plus the number of days in Feb (28), plus DAY.

Thus, in the following, the Julian date is initialized to DAY. If, the MONTH is 1 (Jan), we
are done. Otherwise, we must also add the number of days in each month up through
MONTHS-1. That is, in the case of March 7, we have 7 + 31 + 28.

DAYS_IN_MONTH DATA 00, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
' 1 2 3 4 5 6 7 8 9 10 11 12

JULIAN_DATE = DAY
IF MONTH=1 THEN JULIAN_DONE
FOR N=1 TO MONTH-1
READ DAYS_IN_MONTH+N, TMP
JULIAN_DATE = JULIAN_DATE + TMP
NEXT
But, we do have the added confusion of the leap year and it is a bit more complex than
many people realize. A leap year occurs every four years, but there is no leap year at the
turn of the century, but every 400 years, there is. Thus, the year 2000 is a leap year and a
leap year occurs every four years thereafter. The year 2100 will not be a leap year, but I
doubt any reader will have to reckon with this. Thus, for now until 2099, a leap year
occurs every four years.

Clearly, if the MONTH is less than 3 (March), we need not be concerned with whether it
is a leap year or not and we are done. Otherwise, if the year is not equally divisible by 4,
we are done. But if MONTH >= 3 and it is a leap year, we add 1 to calculate the Julian
date.

IF (MONTH < 3) THEN JULIAN_DONE


IF (YEAR//4 !=0) THEN JULIAN_DONE ' not a leap year
JULIAN = JULIAN+1

JULIAN_DONE:
By knowing the Julian date, we can calculate the number of seconds that have elapsed
since the beginning of the year. Note that the following statements involve 32 bit
arithmetic and will not work on a BS2 as they are written.
ELAPSED_SECS = JULIAN_DATE * (24 * 3600)
+ (HOURS * 3600) + (MINS * 60) + SECS

There is one problem here in that 24 * 3600 = 86,400 which is too large to be
accommodated in a word. However, we can multiply the Julian date by 2;
ELAPSED_SECS = (JULIAN_DATE * 2) * (12 * 3600)
+ (HOURS * 3600) + (MINS * 60) + SECS
Note that ELAPSED_SECS is a 32-bit quantity which cannot be directly handled as it is
written above. Rather, we must define two 16-bit words.
ES1_H = ((JULIAN_DATE * 2) ** (12*3600))
+ (HOURS ** 3600) ' calculate the high word
Note that the operator ** returns the high 16 bits of the operation.

Now, for the lower 16 bits;

ES1_L = (MINS * 60) + SECS


TMP = ES1_L + (HOURS * 3600)
IF (ES1_L >= TMP) THEN EL_TIME_1 ' no carry occurred
ES1_H = ES1_H + 1
EL_TIME_1:
ES1_L = TMP

TMP = ES1_L + (JULIAN_DATE * 2) * (12*3600)


IF (ES1_L >= TMP) THEN EL_TIME_2 ' no carry occurred
ES1_H = ES1_H + 1
EL_TIME_2:
ES1_L = TMP
Note that in calculating the lower 16 bits, a summing of four terms is required. Clearly,
(MINS * 60) + SECS will not cause an overflow. However, in summing the other two
terms, a check is required to ascertain if an overflow occurred and if so, ES1_H is
incremented.

Note that in the above, ES1_H and ES1_L uniquely identify what might be termed the
current "Julian second". That is, the number of seconds since the beginning of the year.
This might be useful in causing a periodic event to happen or in efficiently logging time
data to EEPROM. That is, log the elapsed time since the beginning of the year and then
after dumping the data to a PC convert this back to a date and time.

S-ar putea să vă placă și