Alarm clock with DS1302 RTC

 Author:   Posted on:   Updated on:  2018-12-24T10:27:15Z

DS1302 is a timekeeping chip with battery backup and general purpose RAM. It has been replaced by newer variants like DS1307 or DS3231 which have improved accuracy. The newer ones have I2C interface, but DS1302 does not. It communicates with the host using a serial protocol that resembles SPI. Since I had a module with this chip, I decided to build and test an alarm clock. I added an Arduino, the alphanumeric LCD, an active buzzer and some buttons.

There are some great advantages when using an RTC (real time clock) module in your project. You don't have to do timekeeping in your code (if you read my previous post, you can see it's not quite easy) and, very important, time keeping is performed from backup battery when your development board is not powered. This means you don't have to set date everytime you plug in the Arduino. The advantage is obvious for projects that depend on accurate time to operate correctly.

Arduino alarm clock with DS1302 RTC module

To build this circuit, I used an ATmega328p Arduino Nano compatible board. I wired an alphanumeric LCD with 2 rows of 16 characters. Some buttons are needed to let the user set date and time and a buzzer can ring the alarm. I used an active buzzer (from a kit) because it can be wired directly to a digital pin and beeps when powered. No need to generate beep frequency in code, no need for additional parts.

DS1302 modules uses 3 Arduino pins for serial clock, serial data I/O and chip reset. The protocol resembles SPI, but it's a bit different. I chose to bitbang the serial protocol on any digital pins. The code is inspired from RTClib by NeiroNx. DS1302 also features integrated charger for the backup battery, but the module comes with CR2032 socket and the battery I used is not rechargeable. Anyway, the datasheet states DS1302 uses less than 300nA to keep the clock running. The code will not turn on the charger. Let's have a look at the schematic.

Schematic of Arduino alarm clock with DS1302 RTC module

Schematic of Arduino alarm clock with DS1302 RTC module

A preset resistor is needed to set LCD contrast and a current limiting resistor is required for backlight. To keep the number of additional parts to a minimum, the four push buttons pull low pins with internal pull-ups enabled. Even though those are analog pins, they can be used as well for digital I/O. I had some issues with the RTC module. I could not read valid data from it until I took it out from the socket and soldered it directly on the module PCB. Also I had to test different connection wires until I managed to get consistent data from DS1302.

DS1302 stores and updates time in registers, in BCD (binary coded decimal) format. Registers base address is 0x80. You have acces to a register's value by first sending its address to DS1302. If you send a specific register address, you can read or write all registers consecutively (burst mode). The address is 0xBF if you want to read the RTC and 0xBE when setting the date and time (write mode).

Overview of DS1302 RTC registers

Overview of DS1302 RTC registers (from datasheet)

My sketch reads all date and time variables in burst mode and displays them. But when you adjust time, each variable is sent to DS1302 as soon as you set it. It's easy to send a serial byte using the Arduino function shiftOut(). Reading a byte still needs to be performed one bit at a time.

void rtc_write(byte val) {
  pinMode(RTC_DAT, OUTPUT);
  shiftOut(RTC_DAT, RTC_CLK, LSBFIRST, val);
}

byte rtc_read() {
  pinMode(RTC_DAT, INPUT);
  byte value = 0;
  for (byte i = 0; i < 8; i++) {
    value |= (digitalRead(RTC_DAT) << i);
    digitalWrite(RTC_CLK, HIGH);
    digitalWrite(RTC_CLK, LOW);
  }
  return value;
}

The main loop() increments seconds (once per second, obviously) and when they reach 60, all date and time variables are set from RTC. I consider there is no need to poll the RTC once per second for all variables. After 300 lines of code I had a fully functional clock with date and time display and possibility for adjustment using three buttons.

The SET button cycles through time variables and alows you to adjust them using UP and DOWN buttons. Cycling is performed in the following order: year > month > day > day of week > hour > minute. When you push the SET button, the variable that you had displayed on LCD and you may have adjusted is sent to DS1302. The next one to adjust is displayed. Minutes are the last to be edited and when you press SET after adjusting them, the seconds are also reset to zero and clock halt is cleared just in case. Write protection is disabled once you enter edit mode and then it is restored. The clock remains forever in edit mode unless you push SET button to return to normal mode.

Let's have a look at some fragments of code. Bringing up variables for adjustment is done in a switch() statement. Here is the case for day:

    case 3: // day
      rtc_write_reg(0x88, bin2bcd(MM));

      lcd.setCursor(4, 0);
      lcd.print("Day");
      lcd.setCursor(6, 1);
      if (dd < 9) lcd.print('0');
      lcd.print(dd, DEC);
      break;

See the first thing that it is done is to send month to DS1302 because that's what you were editing before you entered day edit mode. Then the day is printed on LCD. The adjustment is performed when you press UP/DOWN buttons. UP calls a function with numeric argument set to 1, while DOWN calls the same function with -1. A switch() statement performs adjustment of selected variable.

    case 3: // day
      t = dd; t += a;
      if ((t > 0) && (t <= daysOfMonth[MM])) dd = t;
      if (dd < 10) lcd.print('0');
      lcd.print(dd, DEC);
      break;

A temporary (signed) variable t stores the current time variable that will get adjusted. This is adjusted and then checked for validity. If it remains between boundaries, it is copied to the local time variable and displayed. Only after you push SET it will be sent to DS1302 in the previously explained routine.

For the alarm setting I had two options. I can store it in the EEPROM of ATmega328p or I can use the RAM of DS1302. I chose the second option. There are three variables (alarm enabled, alarm hour and alarm minute) which are stored in the first three addresses of the RAM. Alarm is set with the ALARM button. When it is enabled you will see the * sign at the right of the first row. When the current time matches alarm setting, the alarm turns on and off for one minute unless you push the ALARM button to stop it.

In the end, this project needed more than 400 lines of code. The complete sketch can be downloaded from GitHub. DS1302 modules can be found here.

No comments :

Post a Comment

Please read the comments policy before publishing your comment.