I am attempting to use a DS3231 to get timestamps for events, however, I need to know the timestamp in ~100ms precision. I have found that the answer might lie in using the SQW pin on the DS3231 set to a 1 Hz square wave connected to an interrupt pin on the arduino, however I am not sure how to actually implement this.
I found this post but I am not following what they are actually suggesting.
So far I have this code:
int sqwPin = 2;
volatile byte flag = false;
void setup() {
// bunch of setup stuff.....
// ..........
//........
// never assume the Rtc was last configured by you, so
// just clear them to your needed state
Rtc.Enable32kHzPin(false);
pinMode(sqwPin, INPUT_PULLUP);
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock);
Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz);
int secondInterrupt = digitalPinToInterrupt(sqwPin);
attachInterrupt(secondInterrupt, rtc_interrupt, RISING);
}
void loop() {
// currently the loop just gets the time and prints it to serial,
// it does not do anything with the interrupt yet.
// That's what I'm unsure of...
}
void rtc_interrupt(void)
{
flag = true;
}
But I am not sure what to do with the ISR when it fires. I have found some things online that suggest I should not use millis() but should use some sort of counter, but I don't follow what they mean.
Note: The code above is only a snippet, it is not the entire sketch. Let me know if that would be more helpful and I will add it, but I didn't want to make the post too long.
Interrupts are supposedly stopped/disabled during an ISR, so capturing millis() wouldn't show any updates, would it?
From 7.7 of the 328P datasheet:
"When an interrupt occurs, the Global Interrupt Enable I-bit is cleared and all interrupts are disabled. The user
software can write logic one to the I-bit to enable nested interrupts. All enabled interrupts can then interrupt the
current interrupt routine. The I-bit is automatically set when a Return from Interrupt instruction – RETI – is
executed."
Does the IDE enable nested interrupts during millis() and micros()? I dont know the IDE that deeply.
I think you'd want to sync up on an edge of the 1 PPS output, then use blink without delay to monitor for 100mS intervals going by, maybe using micros() instead of millis() for more precision?
I've done that for someone who wanted to see 1/10s of a second going by while using an RTC to show time.
traptw1thin, do you want to measure something in 100 ms without the need to know the year and month, or do you have to know the year and month ?
I have read the thread in that link. They suggest to track millis() on top of the DS3231.
// global variable
volatile unsigned long millis_in_interrupt;
void Interrrupt_1Hz()
{
millis_in_interrupt = millis();
}
void loop()
{
noInterrupts();
unsigned long millis_in_interrupt_copy = millis_in_interrupt;
interrupts();
unsigned long time_passed_since_interrupt = millis() - millis_in_interrupt_copy;
}
However, when using I2C for the DS3231 and the 1Hz output together, there might be a sync mismatch. I can not guarantee that it is reliable.
It is possible to use only millis() for a 10 Hz timer. Some Arduino boards have a crystal, that is accurate. But some have an inaccurate resonator. However, the code by CrossRoads does not stay in pace with the accuracy of the crystal and does not prevent the rollover problem. For a good 10 Hz timer it has to be:
In that case the millis() and RTC are seperated and you can not add the real RTC value to that.
The obvious solution is to use a RTC with 0.1s accuracy and SPI interface. I don't know such a RTC.
There are RTCs with 1kHz output. If you are very careful with the code, then even an Arduino Uno can do that.
Perhaps getting the real time during startup and in between the measurements and then let the software take care of it with the 1kHz interrupt.
CrossRoads:
The attached code sends it outputs to shift registers, you can revise to send to the serial monitor instead.
Just need a small change in one area.
I don't have a clue how to follow that code, I'm sorry.
Koepel:
traptw1thin, do you want to measure something in 100 ms without the need to know the year and month, or do you have to know the year and month ?
My goal is to get the RTC time and then also add to that the fractional seconds.
Happy to be corrected by anyone, but I think that your ISR should just be set up to update a global variable lastISR
void rtc_interrupt(void)
{
lastISR = millis();
}
The implementation of millis() in the library essentially just returns the current millis() value (code is in wiring.c), so it looks like it is safe to call from an ISR.
unsigned long millis()
{
unsigned long m;
uint8_t oldSREG = SREG;
// disable interrupts while we read timer0_millis or we might get an
// inconsistent value (e.g. in the middle of a write to timer0_millis)
cli();
m = timer0_millis;
SREG = oldSREG;
return m;
}
If the SQW is set to 1Hz, then once you have the lastISR saved, to get the number of milliseconds on top of the seconds when you record an incident you need to do millis() - lastISR (ie, the difference within this one second time segment in this time interval).
There will be a lag between the 'real' milliseconds and the recorded milliseconds, but this will be consistent and the time between sequential recorded incidents will be correct. If you want to get an accurate absolute value for the time you will need to use something other than the DS3231.