Correct millis() with an RTC

Need to make some corrections to my previous posts.

The DS3231 SQW output pin can be configured to produce a square wave output of 1Hz, 1.024kHz, 4.096kHz, or 8.192kHz, there is no option for 32kHz from this pin. The 32K output pin produces a square wave of 32.678kHz.

A bit more difficult to produce the 25fps count with 32.678kHz because it does not divide evenly. A single cycle at 32.678kHz is 30.518 microSeconds, a discrepancy of that amount between one frame and the next will hopefully be tolerable, as long as the long-term timing is correct.

This code seems to work correctly for maintaining correct timing while using FastLED:

#include <FastLED.h>
#include "RTClib.h" //RTClib library

RTC_DS3231 rtc;

#define NUM_LEDS 507
#define DATA_PIN 3
CRGB leds[NUM_LEDS];

CRGB testimage[NUM_LEDS]; //for testing purposes only

volatile uint32_t frameCounter = 0;
uint32_t frameCount;
uint32_t frameCountPrev;

void setup() {
  //create some random LED data to ensure compiler does not remove the FastLED code
  for (size_t i = 0; i < NUM_LEDS; i++) {
    testimage[i] = CRGB(random(256), random(256), random(256));
  }
  
  Serial.begin(115200);
  Serial.println("startup");
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }
  if (rtc.lostPower()) {
    Serial.println("RTC lost power, setting time");
    // If the RTC have lost power it will sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
  
  if (!rtc.isEnabled32K()) { //verify 32K output is enabled
    rtc.enable32K();
  }
  
  TCCR5A = 0; //clear control register A
  TCCR5B = 0; //clear control register B
  TCNT5 = 0;  //clear counter
  OCR5A = 1309; //set value for output compare register A  (32768Hz * 1/25 second) - 1 = 1309
  TCCR5B |= (1 << WGM52); //Set CTC mode (WGM5 = 0100);
  TCCR5B |= (1 << CS52) | (1 << CS51) | (1 << CS50) ; //External Clock mode using D47 as input
  TIMSK5 |= (1 << OCIE5A); //Set the interrupt request
  sei(); //Enable interrupt

  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);  // GRB ordering is assumed
}



void loop() {
  //temporarily disable interrupts while making a copy of frameCounter
  noInterrupts();
  frameCount = frameCounter;
  interrupts();

  if (frameCount != frameCountPrev) { //only execute once per frame
    if ((frameCount - frameCountPrev) != 1) {
      Serial.println("missed frame");
      while (1) {};
    }
    frameCountPrev = frameCount;
    for (size_t i = 0; i < NUM_LEDS; i++) { //copy testimage to LED array
      leds[i] = testimage[i];
    }
    FastLED.show();
    Serial.print(frameCount);
    //print millis every 25 frames to test timing
    if ((frameCount % 25) == 0) {
      Serial.print('\t');
      Serial.print(millis());
    }
    Serial.println();
  }
}

ISR(TIMER5_COMPA_vect) {   //This is the interrupt request
  static byte cycleCount = 24;
  //adjustment to compensate for 32768 not being evenly divisible by 25
  //counter will count ( (7 * 1310) + (18 * 1311) ) = 32768 pulses over 25 frames
  if ((cycleCount & 0x03) == 0) {
    OCR5A = 1309; //1310 clock pulses
  } else {
    OCR5A = 1310; //1311 clock pulses
  }
  if (cycleCount == 0) {
    cycleCount = 25;
  }
  cycleCount--;

  frameCounter++; //actual frame counter
}
1 Like