Back to Top

Digital alarm clock using DS3231 RTC


Introduction

In this project i will present you a digital clock i made, using an extremely accurate Real Time Clock chip (RTC) which is the DS3231 from maxim intergrated.
The DS3231 is a low cost real time clock, with an intergrated temperature compensated crystal oscillator (TCXO) and a crystal of 32KHz.
This chip uses a built-in temperature sensor to periodically measure the crystal's temperature (which is approx~ every 64secs), and by switching different
internal capacitors in and out of the crystal circuit, it can precisely adjust its frequency to remain constant.
Maxim claims an accuracy within 2ppm from 0°C to +40°C, and 3.5ppm from -40°C to +85°C, which this is translated as a clock drift of +-2 mins per year.
This chip also incorporates a battery input, so it can maintain accurate timekeeping when mains power to the device is absent. This is done through
a comparator circuit, where monitors the status of VCC to detect power failures, so to automatically switch to the backup supply when it is necessary.
The RTC counts seconds, minutes, hours, day, date, month, and year information.
The date at the end of the month is automatically adjusted for months with fewer than 31 days, and includes corrections for leap years.
It can be operated in either 24-hour or 12-hour format with an AM/PM indicator. ( In which the 24Hour format is more preferable to me...)
It has also two programmable time-of-day alarms, and one programmable square-wave output.
In addition to all of the characteristics we talked before it has also a major advantage that communication with the chip is accomplished through the I2C bus,
which minimizes the need of multiple I/O ports. Awesome!!!

Digital clock Video

COMING SOON.....

Follow me on tweeter and stay tuned with Electronicsgate.com

DS3231 Module


Image not found...
(Fig1) The RTC module using DS3231 chip
Showing the parts i removed (Read below)
I did all of my experimantations using the module on the right image. This module is named as zs-042.
I must admit that i am very satisfied with the results i got from this module!!!. Although it's price.... which was
1.5€ for the whole module (definately amazing!), that price made me suspicious since, farnell sells only the chip
for !!!.This price difference made me curious, although the drifting results were extremely good!!!
At the beginning of my experimentations i was surfing on the internet looking for DS3231 info, about the operation,
the stability, and the big difference of the price that exists from an authorized reseller.
Then i came accross to some interesting articles, like this one...Click here and after reading two more articles that pete provides, i concluded that it is not clear if there are counterfeit chips of the ds3231 on the market, since maxim
confirmed that the onboard chip that the module had on it was genuine.
But if there are any counterfeit chips on the marker and the results are not far from the genuines, it makes it for sure
a good option for buying, since among performance, that module has also on board an eeprom memory with 32K
storage capacity (actually 32,768 bits), and a CR2032 battery holder, which makes the 1.5€ worth spending.
Be sure to buy the DS3231SN chip instead of DS3231M since their usage is depending on the environment that
the chip will work. the M version is used on high-vibration environments, removing malfunctions due to crystal
mechanical failures, and that's due to the microelectromechanical system (MEMS) oscillator that uses to count time
M version has less accuracy than the SN version with a deviation to each other on +-3ppm.
Also this chip has an internal register called aging offsset register where you can trim the offset by simply adding
or substracting user-defined values, to or from the capacitance array registers, as a result to achive great accuracy
on these chips, by simply using an oscilloscope hooked up on 32K pin to monitor the output oscillation frequency,
after you had enabled it from the control register.

Module Schematic

Image not found...
The schematic of the RTC module.
(Click to enlarge.)




So as always i did my reverse engineering to the pcb and i ended up to this schematic on the left.
I must also mention that i removed some components of the pcb, since it is not
critical for my needs...
These components are the power led indicator, and the battery charging circuitry, which consists of the diode and the resistor.
Have a look at Fig1 above to see exactly which components were removed from
the pcb pointing them by the arrows.
As i said before when i was looking articles for the DS3231, i read that some batteries had an explosion, since it has no intelligent cut-out circuitry to stop
charging the battery when it reaches the full capacity.
What it really does, is to trickle charge the battery using a constant current supply throughout the resistor.
The current that this chip draws makes the battery lasts for years, so i really dont care if i have to change the battery for every (x)Years.





Pinout description

This board as you can clearly see from (Fig1) has a pinout header with 6 pins. Lets look together what each pin does in detail

Pin #
Name
Function
1
(32K)
32kHz Output. This open-drain pin requires an external pullup resistor. When enabled, the output operates on either power supply.
It may be left open if not used.

2
(SQW)
Active-Low Interrupt or Square-Wave Output. This open-drain pin requires an external pullup resistor connected to a supply at 5.5V or less.
This multifunction pin is determined by the state of the INTCN bit in the Control Register (0Eh).
When INTCN is set to logic 0, this pin outputs a square wave and its frequency is determined by RS2 and RS1 bits.
When INTCN is set to logic 1, then a match between the timekeeping registers and either of the alarm registers activates the INT/SQWpin
(if the alarm is enabled). Because the INTCN bit is set to logic 1 when power is first applied, the pin defaults to an interrupt output
with alarms disabled. The pullup voltage can be up to 5.5V, regardless of the voltage on VCC. If not used, this pin can be left unconnected.

3
(SCL)
Serial Clock Input. This pin is the clock input for the I2C serial interface and is used to synchronize data movement on the serial interface.
Up to 5.5V can be used for this pin, regardless of the voltage on VCC.

4
(SDA)
Serial Data Input/Output. This pin is the data input/output for the I2C serial interface. This open-drain pin requires an external pullup resistor.
The pullup voltage can be up to 5.5V, regardless of the voltage on VCC.

5
(VCC)
Power pin for primary supply of the RTC module. This pin should be decoupled using a 0.1µF to 1.0µF capacitor. If it is not used, then you
should connect it to ground. Datasheet specifies that it has a range from 2.3v to 5.5vdc, with best stability oscillation results on 3.3vdc

6
(GND)
This pin is the power supply ground.

Module Materials

  Resistors
R1 Smd (0805) Resistor 200 Ohm
R2 Smd (0805) Resistor 1K
R1_A Smd (0805) Network Resistor 4.7K
R2_A Smd (0805) Network Resistor 4.7K
  Capacitors
C1 Ceramic Capacitor 1uf
C2 Ceramic Capacitor 0.1uf
  IC's
IC2 DS3231  I2C Real Time Clock Chip (TCXO)
IC1 AT24C32  I2C 32K Serial Eeprom (32,768 bits)
  MISC
LED1 Red Led Smd (0805)
D1 1N4148 Smd Diode
BATT CR2032 + Horizontal Battery Holder

How the TCXO really works? (Very brief analysis.)


Image not found...
(Fig1)Block diagram of the DS3231 chip




The TCXO circuitry consists of the parts that are circled on the left block diagram. These are the temperature sensor the oscillator with the capacitor array and the control logic unit.

The control logic unit reads the temperature sensor's output and by using a predefined lookup table, (which determines the capacitance that it is required on each temperarute), then adds the aging correction (which this value is taken from the age register 0z10 position on the address register map) and the final result of these two parameters sets accordinally the capacitance selection register, where capacitors are added or substracted from the array,
in order to keep stable oscillation in ambient temperature variations.

New values (including changes to the age register) are loaded, when temperarure change is detected by the sensor or when a user-initiated temperature conversion is completed. Temperature conversion occurs on initial application of VCC and once every 64 seconds afterwards.

DS3231 Communication

As we talked earlier this module communicates through the I2C bus. We should also mention that this module acts as a slave device only!!!.
Once again i realized why every bench, where microcontrollers are present, should have a logic analyzer...
Besides some problems i had, which we will discuss later, it is more easier to hook up your analyzer and watch if you follow-up the proper
sequences for read-write operations according to the datasheet.
Below are both modes (read-write operations) though the I2C bus.
Let's analyze both of them one by one, but first let's have a look at the registers address map of the chip.

Register Address Map

Image not found...

The registers in gray color (00-06h) are assosiated with the timekeeping functions.
The registers in yellow color (07-0Ah) are assosiated with the first built-in alarm.
The registers in green color (0B-0Dh) are assosiated with the second built-in alarm.
The registers in white color (0E-0Fh) are assosiated with the control operations of the chip.
The register in brown color (10h) is assosiated with the aging offset.
And the registers in blue color (11-12h) are assosiated with the built in thermometer.


Writing Sequence

Image not found...

In order to write data on the DS3231, master initiates a start condition on the I2C bus.
After that master sends the slave's address which is the 0xD0 or '11000000', where the last bit of the address, indicates whether a read or write operation
will be performed. (In our case 0 since we want to write on the DS3231)
Then you wait for an acknowledge from slave, and when it is received, master sends a pointer byte (word address) which 'points' the beginning
address that you want to start writing to. When an acknowledge is received once more, master starts transmitting data to the slave, waiting for an acknowledge
on every byte that was received successfully by the slave. You can either transmit a single byte or a byte array.
Keep also in mind that the slave's pointer increases automatically by one on every successfull writing, and wraps around to the beginning when it reaches
at the end of the address register map.
(Register position 12h)
And finally master initiates a stop condition, as a result to terminate the writing procedure and release the bus.

In summary :

1) Initiate a start condition
2) Send the slave address followed by the operation 0xD0 (Write operation)
3) Wait for slave to acknowledge
4) Send the pointer (word address) where to start writing to
5) Wait for slave to acknowledge
6) Send (one or multiple bytes) to slave waiting for each byte transmition, the slave to acknowledge.
7) Initiate a stop condition

*** One clarification about single and multiple byte transmittion ***.

If you want to write for example two values, the hour and the year, these registers are not in a row. (Look the address table above.)
So you will have to do the transmition in two parts.

This will be as it follows :
1) Initiate a start condition.
2) Send the slave address followed by the operation 0xD0 (Write operation).
3) Wait for slave to acknowledge.
4) Send the pointer (word address) where to start writing to 0x02(Hour).
5) Wait for slave to acknowledge.
6) Send the hour data ex 0x21 nine o clock at night and wait the slave to acknowledge.
7) Initiate a stop condition.

1) Initiate a start condition.
2) Send the slave address followed by the operation 0xD0 (Write operation).
3) Wait for slave to acknowledge.
4) Send the pointer (word address) where to start writing to 0x06 (Year).
5) Wait for slave to acknowledge.
6) Send the hour data ex 0x19 (2019) and wait the slave to acknowledge.
7) Initiate a stop condition.

And as for writing a byte array will be as it follows :
1) Initiate a start condition.
2) Send the slave address followed by the operation 0xD0 (Write operation).
3) Wait for slave to acknowledge.
4) Send the pointer (word address) where to start writing to 0x00 (Secs).
5) Wait for slave to acknowledge.
6) Send the secs data ex 0x00 and wait the slave to acknowledge.
6a) Send the mins data ex 0x00 and wait the slave to acknowledge.
6b) Send the hours data ex 0x15 and wait the slave to acknowledge.
6c) Send the day data ex 0x01 and wait the slave to acknowledge.
6d) Send the date data ex 0x19 and wait the slave to acknowledge.
6e) ........ As many as you want until 0x12 position.
7) Initiate a stop condition.


Reading Sequence

Now as for the reading, in order to read data from the DS3231, is a little bit more complicated than writing.
Remember that you can either read a single byte or a byte array as it also applies to the writing procedure.
So at first master initiates a start condition on the I2C bus.
After that, master sends slave's address followed by a write operation 0xD0 since we must "tell" to the chip where to start reading from.
When acknowledge is received from slave, then master sends the pointer byte to signify the starting position that the DS3231 will start reading from.
When acknowledge is received once more then a restart condition is generated from the master, sending the slave address followed by reading operation 0XD1
informing DS3231 this time, that we now want to read data.
After acknowledge is received, then on every 8 clocks the sspbuffer holds the data to be read. Then the role is reversed and the master acknoledges to the slave
to confirm that the byte was read successfully, as a result the slave to continue sending data.
This is happening until you read all the bytes you want. Then on the last byte that it has been received the master sends a not acknowledge followed by a stop condition
to terminate the reading procedure and release the bus.



Image not found...


Image not found...
The I2C communication protocol (Reading Procedure).



In summary :

1) Initiate a start condition.
2) Send the slave address followed by the operation 0xD0 (Write operation).
3) Wait for slave to acknowledge.
5) Send the pointer (word address) from where to start reading.
5) Wait for slave to acknowledge.
6) Initiate a restart condition.
7) Send the slave address followed by the operation 0xD1 (Reading operation).
8) Wait for slave to acknowledge.
9) Then the ssp module generates clocks to receive data. Check if the sspbuf is full.
10) When sspbuf is full send an acknowledge to slave that data successully received.(Repeat from step9 for as many bytes as you want to read)
11) Send a NACK (not acknowledge) to "tell" slave that the reading will stop.
12) Initiate a stop condition.

*** A reading example of hour and minutes. ***.

If you want to read for example two values, the minutes and the hour
This will be as it follows :
1) Initiate a start condition.
2) Send the slave address followed by the operation 0xD0 (Write operation).
3) Wait for slave to acknowledge.
5) Send the pointer (word address) from where to start reading. (0x01 minutes)
5) Wait for slave to acknowledge.
6) Initiate a restart condition.
7) Send the slave address followed by the operation 0xD1 (Reading operation).
8) Wait for slave to acknowledge.
9) Then the ssp module generates clocks to receive data. Check if the sspbuf is full.
10) When sspbuf is full (Minutes received.) send an acknowledge to slave that data successully received.
11) Then the ssp module generates clocks to receive data. Check if the sspbuf is full.
12) When sspbuf is full (Hour received.) send an acknowledge to slave that data successully received.
13) Send a NACK (not acknowledge) to "tell" slave that the reading will stop.
14) Initiate a stop condition.

Alarms

This module has 2 built-in alarms but i haven't done any test yet to upload any results. When i have, i will update this page with all of my experimentations i did.
Right now i am using one alarm which was created by me on my programm.
The concept is that on every second, the programm checks if the values that have been stored by the user on eeprom, match with the timekeeping registers of the DS3231.
And when there is a match it activates an output, which in our case is a led.
Of course more info can be found on my assembly listing programm.

Some considerations that are still unsolved...

Image not found...
Enabling the MSSP Module.

Zooming on Saleae logic programm i noticed that when the pic initializes the I2C hardware module, the logic analyzer captured these transitions.
I still don't know why this is happening and of course i don't know, if this is the right thing to do by the module when it initializes.
If anyone has expierienced something similar please let me khow by sending me an e-mail at info@electronicsgate.com or by using the Contact form page.
Meanwhile i will keep searching for an explanation to post.

Digital Clock's Schematic


Image not found...
The schematic of digital clock.
(Click to enlarge.)

Digital clock photos

Image not found... Image not found... Image not found... Image not found...
The clock running on a breadboard with a 18650 battery. The main menu. Time and date menu. Alarm menu.
Image not found... Image not found... Image not found... Image not found...
Total consumption of the clock running from battery. Consumption without the backlight. Consumption with the PIC16LF1939 and DS3231. (Without LCD,Step-Up). On power-up displays the running version.