3

How to get accurate milliseconds?

I need to calculate the delay of sending data from Arduino A to Arduino B. I tried to use DS3231 but I cannot get milliseconds. What should I do to get accurate milliseconds from DS3231?

ahmad
  • 59
  • 1
  • 2
  • 5
  • The [DS3231](https://datasheets.maximintegrated.com/en/ds/DS3231.pdf) only has second resolution provided in the registers. It does have a 32kHz square-wave output for a clock source but you would have to count the pulses in an ISR. The [millis()](https://www.arduino.cc/en/Reference/Millis) function returns Number of milliseconds since the program started as an unsigned long. – Grifplex Jun 20 '17 at 05:49
  • just use the millis() built-in function; it will be pretty consistent for all but the most demanding timings. You can poll the RTC until you spot a roll-over, at which point you memorize `millis() % 1000`; then you can add that offset to any clock reading, and add `((millis() % 1000) - offset)/1000` to the RTC value to get a ms-accurate time reading. – dandavis Jun 20 '17 at 19:02

2 Answers2

1

The comment above is correct, but using millis() when you have a dedicated realtime clock makes no sense. I'll provide you with better instructions.

First thing in any hardware interfacing project is a close reading of the datasheet. The DS3231 datasheeet reveals that there are five possible frequencies of sub-second outputs (see page 13):

  1. 32 KHz
  2. 1 KHz
  3. 1.024 KHz
  4. 4.096 KHz
  5. 8.192 KHz

These last four options are achieved by various combinations of the RS1 and RS2 control bits.

So, for example, to get exact milliseconds, you'd target option 2, 1KHz. You set RS1 = 0 and RS2 = 0 (see page 13 of the datasheet you provided) and INTCN = 0 (page 9). Then you'd need an ISR to capture interrupts from the !INT/SQW pin of the device to a digital input pin on your Arduino.

volatile uint16_t milliseconds;  // volatile important here since we're changing this variable inside an interrupt service routine:
ISR(INT0_vect) // or whatever pin/interrupt you choose
{
    ++milliseconds;
    if(milliseconds == 999)  // roll over to zero
        milliseconds = 0;
}

OR:

const int RTCpin = 3; // use any digital pin you need.
void setup()
{
    pinmode(RTCpin, INPUT);
    // Global Enable INT0 interrupt
    GICR |= ( 1 < < INT0);
    // Signal change triggers interrupt
    MCUCR |= ( 1 << ISC00);
    MCUCR |= ( 0 << ISC01);
}

If these commands in setup() don't work on your Arduino, google 'Arduino external interrupt INT0'. I've shown you two ways, one with Arduino code and one in C.

Once you have this ISR working and pin3 of the DS3231 connected to a digital input pin of your choosing, that pin will be activated at 1KHz, or every millisecond. Perfect!

// down in main program now you have access to milliseconds, you might want to start off by setting:

// When 1-second RTC changes seconds:
milliseconds = 0;  // So you can measure milliseconds since last second.

That's all there is to it. All you need to learn now is how to set the command register using I2C commands and you're all set.

B 7
  • 670
  • 7
  • 23
TomServo
  • 7,248
  • 5
  • 30
  • 47
0

The C code example gains 1ms every second. Should be:

{
    if (milliseconds == 999)  // roll over to zero
        milliseconds = 0;
    else
        ++milliseconds;
}
  • You are probably refering to [this answer](https://stackoverflow.com/a/44650739). Once you have enough reputation, you will be able to comment on that response. While your observation is correct - the code resets to 0 too early, that example was wrong anyway: as commented by @gre_gor, according to the [datasheet](https://datasheets.maximintegrated.com/en/ds/DS3231.pdf) (p. 13), setting RS1 = 0 and RS2 = 0 produces 1Hz output (i.e. that will be a 1000ms period, not 1ms). There is no 1kHz choice for SQW and thus no means to produce a 1ms clock with that chip. – Adrian W Jul 22 '18 at 17:17