I am able to set up one hz interrupts using millis and other timers, but would like to do it instead using the RTC I have attached (DS3231)
I am not sure how I can do that. Can someone point me to a reference for this?
EDIT/UPDATE
I tried to use the square wave to trigger a call back/interrupt, but it's not working for me. I followed what I thought was the wiring diagram here and here and here
I also added a led in the circuit to see if I could figure out something that way, but no dice.
Here is some simple code for a sketch that I think should trigger a 1 second interrupt and call my function:
#include <DS3232RTC.h> //http://github.com/JChristensen/DS3232RTC
#include <Time.h> //http://www.arduino.cc/playground/Code/Time
#include <Wire.h> //http://arduino.cc/en/Reference/Wire (included with Arduino IDE)
const int rtcTimerIntPin = 2;
bool flag = false;
void setup(void)
{
Serial.begin(9600);
setSyncProvider(gRTC.get); // the function to get the time from the RTC
if (timeStatus() != timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
bool stopped = gRTC.oscStopped();
if (stopped)
{
Serial.println("Detected clock power loss - resetting RTC date");
time_t newTime = cvt_date(__DATE__, __TIME__);
adjustTime(newTime);
setTime(newTime);
}
else {
Serial.println("Clock did not lose power");
}
pinMode(13, OUTPUT); // onboard LED
pinMode(rtcTimerIntPin, INPUT);
attachInterrupt(rtcTimerIntPin, rtc_interrupt, RISING);
gRTC.squareWave(SQWAVE_1_HZ);
digitalClockDisplay();
}
void rtc_interrupt(void)
{
flag = true;
}
void loop(void)
{
//digitalClockDisplay();
if (flag) {
digitalWrite(LED_BUILTIN, HIGH); // flash the led
digitalClockDisplay(); // this just prints time to serial port
delay(500); // wait a little bit
digitalWrite(LED_BUILTIN, LOW); // turn off led
flag = false; // clear the flag until timer sets it again
}
}
void digitalClockDisplay()
{
// do something
}
-
What sort of precision are you looking for? The interrupt (once you get that working) should fire within a few microseconds of when the clock actually generates the signal. Are you looking for microseconds, or milliseconds? What is the purpose of this? If you are just flashing an LED very high precision is probably not required.Nick Gammon– Nick Gammon ♦2016年10月08日 03:04:45 +00:00Commented Oct 8, 2016 at 3:04
-
And if you just want to flash an LED every second, connect it to the clock output directly! (via a resistor). :PNick Gammon– Nick Gammon ♦2016年10月08日 03:05:38 +00:00Commented Oct 8, 2016 at 3:05
-
I want to have a signal so that I do some check/processing every second. For this project tens of milliseconds is fine. It is for a timer to do time lapse photography.Tim– Tim2016年10月08日 20:40:45 +00:00Commented Oct 8, 2016 at 20:40
4 Answers 4
This answer addresses the original question of why the interrupts didn't work. I happened to have a DS3231 lying around so I made up a test.
Interrupt vs pin number
First, this is wrong in your code:
attachInterrupt(rtcTimerIntPin, rtc_interrupt, RISING);
You need an interrupt number, not a pin number. This is correct:
attachInterrupt (digitalPinToInterrupt (rtcTimerIntPin), rtc_interrupt, CHANGE);
I also made it a CHANGE interrupt so you get both the rising and falling pulse.
Pull-up resistor
Next, the 1 Hz output needs a pull-up resistor, so you should add that or make it INPUT_PULLUP like this:
pinMode (rtcTimerIntPin, INPUT_PULLUP);
Testing
I used this library from Adafruit.
Adapting one of their examples, and putting in your interrupt code (with my modifications), it worked!
#include <Wire.h>
#include "RTClib.h"
RTC_DS3231 rtc;
const byte rtcTimerIntPin = 2;
volatile byte flag = false;
void rtc_interrupt ()
{
flag = true;
} // end of rtc_interrupt
void setup () {
Serial.begin(115200);
while (!Serial); // for Leonardo/Micro/Zero
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
if (rtc.lostPower()) {
Serial.println("RTC lost power, lets set the time!");
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
// enable the 1 Hz output
rtc.writeSqwPinMode (DS3231_SquareWave1Hz);
// set up to handle interrupt from 1 Hz pin
pinMode (LED_BUILTIN, OUTPUT);
pinMode (rtcTimerIntPin, INPUT_PULLUP);
attachInterrupt (digitalPinToInterrupt (rtcTimerIntPin), rtc_interrupt, CHANGE);
}
void loop () {
if (flag) {
digitalWrite(LED_BUILTIN, HIGH); // flash the led
delay(100); // wait a little bit
digitalWrite(LED_BUILTIN, LOW); // turn off led
flag = false; // clear the flag until timer sets it again
}
}
-
I was missing two things: I used INPUT rather than INPUT_PULLUP and I did not have the digitalPinToInterrupt() in the first param for attachInterrupt() thanks!Tim– Tim2016年10月08日 18:35:46 +00:00Commented Oct 8, 2016 at 18:35
Use this DS3231 library. It has a method to set the squarewave RTC.squareWave(SQWAVE_1_HZ)
-
Thanks Gerben - I will try to set up the squarewave to an input/interrupt. No success yet.Tim– Tim2016年10月07日 06:14:09 +00:00Commented Oct 7, 2016 at 6:14
-
You could temporarily add an led, to see if you are getting any signal.Gerben– Gerben2016年10月07日 13:46:04 +00:00Commented Oct 7, 2016 at 13:46
-
Yep, I tried that - and no luck there. In fact, I used two different 3231 'boards' to see if it was my wiring/soldering. It may very well be that I wrecked two boards, but it seems more likely I am doing something else wrong. I will try to post a stripped down version of what i am doing.Tim– Tim2016年10月07日 19:07:55 +00:00Commented Oct 7, 2016 at 19:07
-
You should add a little more detail.Dat Ha– Dat Ha2016年10月08日 22:22:06 +00:00Commented Oct 8, 2016 at 22:22
I tried to use the square wave to trigger a call back/interrupt, but it's not working for me.
bool flag = false; ... void rtc_interrupt(void) { flag = true; }
Flags set in interrupts should be declared volatile
otherwise the compiler may optimize away the comparison to the flag. That is:
volatile bool flag = false;
If you are using the Adafruit library (from the page you linked to) then it should have a couple of functions that can be used to detect when the second has changed, for example:
DateTime now = rtc.now();
int second = now.second();
unsigned long unixtime = now.unixtime();
In either case you just have to check if either one has changed in loop
. For example:
void loop(void)
{
static unsigned long previousTime = 0;
DateTime now = rtc.now();
unsigned long timeNow = now.unixtime();
if (timeNow != previousTime) {
digitalWrite(LED_BUILTIN, HIGH); // flash the led
digitalClockDisplay(); // this just prints time to serial port
delay(500); // wait a little bit
digitalWrite(LED_BUILTIN, LOW); // turn off led
previousTime = timeNow; // remember previous time
}
}
-
thanks @nick - I fixed the volatile bit. Note that I want a callback/trigger/squarewave - not to delay()/check the time each iteration of the loop.Tim– Tim2016年10月08日 02:52:38 +00:00Commented Oct 8, 2016 at 2:52
-
Does it work once you added
volatile
? The delay was from your own code. The delay is not part of working out if the second is up. I don't see a huge difference between checking the time from the clock, as you go aroundloop
and checking a flag, each time you go aroundloop
. Reading the time from the clock might take slightly longer, I suppose.2016年10月08日 03:03:28 +00:00Commented Oct 8, 2016 at 3:03 -
Volatile didn't fix the desired method of having an ISR triggered by a square wave, but I changed the loop() code to look like what you suggested. That works. Thanks for the help. It's going to bother me about the interrupt though...Tim– Tim2016年10月08日 04:07:59 +00:00Commented Oct 8, 2016 at 4:07
Try the following code. It pretty much detects a change in time and when a change has been detected, it will do something. In this case, it will print "do something".
#include <DS3231.h>
DS3231 rtc(SDA, SCL);
String t; //time as a string
String s; //second
String Ps; //previous second
void setup() {
Serial.begin(115200);
rtc.begin();
}
void loop() {
t = rtc.getTimeStr(); //finding time
s = t.substring(6, 8); //finding seconds
if(s != Ps) //if the time has changed
{
Serial.println("do something"); //put whatever you want here
Ps = t.substring(6, 8); //redeclaring the previous second because it has advanced
}
}
-
1I advise against using the
String
class for something done every time aroundloop
.String
uses dynamic memory allocation, and if you aren't careful you will get heap fragmentation. To calculate seconds just use the "modulus" operator (ie. time mod 60).2016年10月07日 22:14:10 +00:00Commented Oct 7, 2016 at 22:14 -
I don't think it is possible because the DS3231 libray that I have only returns strings.Dat Ha– Dat Ha2016年10月07日 22:29:41 +00:00Commented Oct 7, 2016 at 22:29
-
Can you give a link to the library please? You should be able to get something other than Strings.2016年10月07日 23:38:50 +00:00Commented Oct 7, 2016 at 23:38
-
RinkyDinkElectronics.com Your the pro so see if you can find anything. Even I would like for it to be able to return intsDat Ha– Dat Ha2016年10月07日 23:41:28 +00:00Commented Oct 7, 2016 at 23:41
-
Well for one thing the prototype is
char *getTimeStr(uint8_t format=FORMAT_LONG);
which means you get back achar *
not aString
. You can usechar *
without needing dynamic memory allocation. Also there is a functionlong getUnixTime(Time t);
which gives you along
. AFAIK Unix Time is in seconds, so just call that and take modulus 60 of the result.2016年10月07日 23:56:09 +00:00Commented Oct 7, 2016 at 23:56