Real Time Clock Module - RV-8803 (Qwiic) Hookup Guide

Pages
Contributors: El Duderino, Englandsaurus
Favorited Favorite 2

Arduino Examples

Example 1 - Set Time

This example demonstrates how to set the time on the RV-8803 RTC to either the compiler time or a custom time. This is necessary to set the RTC's internal clock to keep time so long as the RTC has power. First, the code initializes the RV-8803 on the I2C bus and verifies the microcontroller can talk to it. Next, it sets the internal clock. Take note of the following bits of code to set the RTC time to either the compiler or a custom time:

When setting a custom time, change these values at the beginning of the sketch:

language:c
//The below variables control what the date and time will be set to
int sec = 2;
int minute = 47;
int hour = 14; //Set things in 24 hour mode
int date = 2;
int month = 3;
int year = 2020;
int weekday = 2;

In the setup, this code selects either compiler time or custom time:

language:c
if (rtc.setToCompilerTime() == false) {
Serial.println("Something went wrong setting the time");
}

//Uncomment the below code to set the RTC to your own time
/*if (rtc.setTime(sec, minute, hour, weekday, date, month, year) == false) {
Serial.println("Something went wrong setting the time");
}*/

If everything goes correctly here your RTC's internal clock will either be set to the compiler time or your custom time using the variables defined earlier in the sketch. Open the Arduino Serial Monitor, set the baud rate to 115200 and the code will print out over serial whether or not this was successful. If you have never worked with the Arduino Serial Monitor or other serial terminal programs, check out our Serial Terminal Basics tutorial. You can check the accuracy of the time set here by running the second example.

Note: The compiler time is not set every time the code compiles. In order to ensure the proper time is loaded, re-open the IDE and then upload the code. Also note that due to upload times, the compiler time may be off by several seconds/hundredths of seconds. The Set Hundredths examples below can help with more accurate time-keeping.

Example 2 - Print Time

This example shows how to request the time from the RTC after it has been set. The code starts by initializing the RV-8803 on the I2C bus then attempts to to move the values from the RV-8803's time registers to the microcontroller's _time array using the updateTime(); function. If that retrieval is successful, the code prints the time data (date, hour, mins, etc.) over serial. Open up the Serial Monitor and set the baud to 115200 and watch the time fly by, literally!

The code defaults to printing the date in the US format (mm/dd/yyyy) so if you'd like to switch it to the more commonly used dd/mm/yyyy format, adjust this section of the code:

language:c
String currentDate = rtc.stringDateUSA(); //Get the current date in mm/dd/yyyy format
//String currentDate = rtc.stringDate()); //Get the current date in dd/mm/yyyy format
Note: If you are modifying or writing your own code remember the updateTime(); function must be called before attempting to read the time from the RV-8803.

Example 3 - Set Hundredths

We have split this example into two "sub-examples" to demonstrate the ways to reset the hundredths register. The hundredths register is read only so we cannot write to it like the other time variables (seconds, minutes, etc.). Instead, we "set" this register by writing to the RESET bit on the RV-8803. This function is covered in more detail in section 4.13 of the RV-8803 Application Note.

Example 3A - Set Hundredths (Software)

This example initializes the RV-8803 and then configures the RTC to reset the hundredths register on a serial prompt to the microcontroller. After uploading the code, open up the serial monitor (with the baud set to 115200 like the previous examples) and send an "r". Your microcontroller will write to the RESET bit on the RV-8803 to "set" the hundredths register to 00. If you want to reset the hundredths register again, simply send another "r" through your serial monitor.

Example 3B - Set Hundredths (Hardware)

This example is near identical to the Software example above but also enables resetting the hundredths register using the RV-8803's EVI pin along with resetting it via serial commands. The EVI pin can be toggled either through the on board button or by controlling the EVI pin with a GPIO pin on your microcontroller (or other external control mechanism). Resetting the hundredths register using the EVI pin is enabled by this line of code:

language:c
rtc.setEVICalibration(ENABLE);

Enabling this bit tells the RV-8803 to reset the hundredths register when it sees a LOW event on the EVI pin. This event can either be triggered by pressing the button or pulling the EVI pin LOW. The code will print the RV-8803 is ready for a button press or other event on the EVI pin. When that pin is triggered it prints out the the hundredths value pulled from the RV-8803's time register over serial. If you want to verify the register is set, open the serial monitor and toggle the EVI pin and the code should print out: Hundredths set to: 00.

The code also toggles the RESET bit back to 0 so it is ready for another event if you want to set the hundredths register back to 00 again.

Example 4 - Interrupts

Since the RV-8803 has several types of interrupts, we have split the interrupt examples into sub-examples as well. Each one will enable and configure a different interrupt and demonstrate how to use it. To integrate any of these examples to your project, just tie the INT pin to a GPIO on your microcontroller that is usable for external interrupts to trigger whatever behavior you would like when one of these alarms or timers are triggered. If you have never worked with processor interrupts before, we have a tutorial covering how to work with them in Arduino here.

Example 4A - Alarm Interrupt

This example configures the Alarm Interrupt function. The code first defines the variables the RV-8803 will look to match to trigger the Alarm Interrupt and selects which of these variables the RTC will look to match to trigger the alarm. To set it to a different time simply edit these values:

language:c
uint8_t minuteAlarmValue = 55; //0-60
uint8_t hourAlarmValue = 0; //0-24
uint8_t weekdayAlarmValue = 0; //0-6
uint8_t dateAlarmValue = 0; //1-31

#define MINUTE_ALARM_ENABLE true
#define HOUR_ALARM_ENABLE false
#define WEEKDAY_ALARM_ENABLE false
#define DATE_ALARM_ENABLE false

Setting the the various Alarm Enable variables (e.g. MINUTE_ALARM_ENABLE) to true will configure the RV-8803 to check to match that variable to trigger an alarm and, as expected, setting it to false prevents the RV-8803 from matching the variable for the alarm interrupt. The example defaults to trigger an alarm every hour when the minutes value reaches 55.

With the variables defined and enabled/disabled, the code initializes the RV-8803, configures it to trigger an alarm and the calls the values for each time variable (minutes, hours, etc.). The main loop checks if the alarm interrupt flag (FLAG_ALARM) has been triggered using the 'getInterruptFlag` function and, if the alarm is triggered, drives the INT pin LOW and prints over serial the alarm has been triggered.

Lastly, it clears the alarm flag to reset the RTC for the next alarm trigger. If you want to clear the entire flag register on an alarm trigger, uncomment this line:

language:c
//rtc.clearAllInterruptFlags();

Example 4B - Countdown Interrupt

This example demonstrates how to configure a periodic signal interrupt based on a countdown timer and explains how to calculate the proper values for the countdown timer. The countdown timer values require some math to calculate how many clock ticks (0-4095) we want to count along with the length of those ticks (4096 Hz, 64 Hz, 1 Hz, 1/60 Hz) in order to designate the period of the countdown timer. Click the button below to take a look at the table for more information on how the countdown timer interval is calculated:


After initializing the RV-8803, the example configures a ~3.5 second interrupt. To do this, we first choose 60 Hz as our timer frequency since we want our timer longer than 1 second but still want a small enough time range (or resolution) to easily set the interrupt to fire at 3.5 secs and not 3 or 4 seconds. Next, we'll convert 3.5 seconds to 3500 mS since our resolution @60 Hz is 15.625mS/LSB. Finally, to figure out how many ticks are needed for a 3.5 sec interval, we divide the time we want by the resolution of the clock frequency: 3500 / 15.625 = 224 Clock Ticks. With everything calculated, our code configuring the Countdown Timer Interrupt looks like this:

language:c
rtc.disableAllInterrupts();
rtc.setCountdownTimerFrequency(COUNTDOWN_TIMER_FREQUENCY_64_HZ);
rtc.setCountdownTimerClockTicks(224);
rtc.enableHardwareInterrupt(TIMER_INTERRUPT);
rtc.setCountdownTimerEnable(ENABLE); 

lastInterruptTime = millis(); //Change millis() to micros() if you end up using the 4096 Hz counter

The loop checks if the countdown interrupt flag (FLAG_TIMER) has been updated using the getInterruptFlag function. If it has, it clears that flag and prints the time between interrupts (in milliseconds) over serial.

Note: If you select the 4096 Hz counter, make sure to switch all calls to the millis function to call the micros function instead.

Example 4C - Periodic Interrupt

The last interrupt example demonstrates how to generate a periodic pulse from the RV-8803. This is very similar to the previous example but instead of setting a custom time between interrupts, we configure the RV-8803 to pulse the interrupt pin every second or every minute using the setPeriodicTimeUpdateFrequency(); function. The code defaults to a 1 second period but you can change to 1 minute by editing this line:

language:c
rtc.setPeriodicTimeUpdateFrequency(TIME_UPDATE_1_SECOND); //Can also use TIME_UPDATE_1_MINUTE 

The loop checks if the periodic interrupt flag (FLAG_UPDATE) has been updated using the getInterruptFlag function. If it has, it clears that flag and prints the time between interrupts (in milliseconds) over serial.

Example 5 - Timestamp

This example demonstrates how to get a timestamp of an event generated on the EVI pin either by pressing the button on the RTC or toggling the EVI pin from a microcontroller-generated event. First, just like the other examples, we need to initialize the RV-8803 on the I2C bus. Next, we enable and configure the timestamp function:

language:c
rtc.setEVIEventCapture(ENABLE); /
rtc.setEVIDebounceTime(EVI_DEBOUNCE_256HZ); 
//rtc.setEVIEdgeDetection(RISING_EDGE); // Uncomment to set event detection to button release instead of press

The main loop waits for the FLAG_EVI register to update from an external event on the EVI pin using the getInterruptFlag(); function like the other interrupt examples. If that flag is updated and cleared, we capture the date and time stamp of the interrupt and print that data over serial.

Example 6 - Fine Tuning

In order to follow this example you will need an oscilloscope or logic analyzer. If you have never used either of these tools, take a look at these tutorials to get started:

How to Use an Oscilloscope

February 25, 2014

How to work the dials and buttons on an oscilloscope, and a glossary of the o-scope lexicon.

Using the USB Logic Analyzer with sigrok PulseView

June 25, 2018

A quick primer on using the sigrok signal analysis software with our 8-channel, 24MHz USB logic analyzer.

This last example demonstrates how to calibrate the RV-8803's oscillator using the Clock Output pin. Again, we first initialize the RV-8803 on the I2C bus. Next, we zero out any calibration settings that may be present and set the clock output to a 1 Hz square wave:

language:c
rtc.disableAllInterrupts();
rtc.setCalibrationOffset(0); //Zero out any calibration settings we may have
rtc.setPeriodicTimeUpdateFrequency(CLOCK_OUT_FREQUENCY_1_HZ); //Set our clockout to a 1 Hz square wave,
rtc.enableHardwareInterrupt(UPDATE_INTERRUPT); //Enable the interrupt

Next, we'll use our oscilloscope or logic analyzer to look at exactly how precise the 1 HZ square wave is (It could be off by a few microseconds, which can add up over time). In order to do this, you'll need to time the output of the INT pin. Once you have the RTC's output frequency measured carefully, go ahead and replace the "dummy" frequency that we've put in the example by default (1.0000012 Hz) with the one that you've just measured. We then take the difference between this measured frequency and the desired 1 Hz clock signal, and multiply it by 1,000,000 to get the Parts Per Million (PPM) offset that we must give to our oscillator.

language:c
float measuredFrequency = 1.0000012; //Measured frequency in Hz (CHANGE THIS TO YOUR MEASURED VALUE)
float newPPM = (measuredFrequency - 1) * 1000000; //Calculate PPM difference between measuredFrequency and our desired 1 Hz wave

Once we've replaced our measuredFrequency, we can uncomment the below line that actually places this new setting into the RTC to change the offset for the crystal. Make sure you don't do this until you've changed your measuredFrequency. From here, you should be able to upload code and see the square wave oscillator closer to a perfect 1 Hz. If not, recomment the below line, upload, and try again.

language:c
//rtc.setCalibrationOffset(newPPM); //Uncomment this line after you have changed the value of measuredFrequency to load the new calibration into the RTC