Documente Academic
Documente Profesional
Documente Cultură
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.
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.
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)
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
In program PUT_GET, two subroutines; _PUT and _GET are presented. These are quite
like the PUT and GET commands associated with the BS2SX.
MAIN:
DIRS = $0007 ' /RST, SCLK and I/O are outputs
ADR=3
VAL=125
GOSUB _PUT ' write 125 to RAM address 3
DONE:
GOTO DONE
_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
CLK_PULSE:
OUT1=1
OUT1=0
Discussion.
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
FOR ADR=0 TO 29
VAL = ADR//3
GOSUB _PUT ' put some numbers into the DS1302
NEXT
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
_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
'''''''''
_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
DIRS=$0007
FOR N=0 TO 29
VAL = N//3
SHIFTOUT 2, 1, LSBFIRST, [VAL\8] ' send 30 bytes
NEXT
OUT0=0 ' terminate the data sequence
SUM = 0
DONE:
GOTO DONE
Intermediate Summary.
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.
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.
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
DIRS=$0007
' clear the write protect bit
' 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
' 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
'''''
_PUT_TIME:
DIR2=1 ' be sure I/O is an output
OUT0=0
OUT1=0
OUT0=1 ' bring /RST high while SCLK is low
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
DATE = DATE & $3F ' take only the lower 6 bits
DATE = DATE.NIB1 * 10 + DATE.NIB0
MONTHS = MONTHS & $1F
MONTHS = MONTHS.NIB1 * 10 + MONTHS.NIB0
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
DIRS=$0007
DIRS=$0007
' clear the write protect bit
' Now write the date and time, in the burst mode.
' Note that eight bytes are written
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
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
DISPLAY:
' Now display the day of the week
' map the week day into a pointer that points to beginning of desired
' string
DEBUG 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;
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;
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.
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.
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.