FYI: Alarm interrupts with DS3234 and DS3231 real time clocks

I had some code set up for the DS3234 (SPI) which interrupts every minute and it works fine. I then switched to using the DS3231 (I2C) and had set up the interrupt the same way but couldn't get it to work. I was clearing the control status register (to clear the interrupt flag) in the interrupt routine and that works for SPI. But you can't do that with I2C, presumably because the I2C library uses interrupts?
Anyway, the way to get around it is that for the I2C DS3231, the interrupt routine should set a flag and then a piece of code in the loop() function clears the CSR if that flag is set - the same method could be used for SPI too.
Here's a code snippet to illustrate what I did for the I2C version:

#define LED_PIN 13
volatile short outbit = 0;
volatile unsigned char interrupted = 0;
//=====================================
void rtc_interrupt(void)
{
  // Toggle the LED pin for debugging
  outbit ^= 1;
  digitalWrite(LED_PIN, outbit);
  // Notify the loop() function that an alarm interrupt has occurred
  interrupted = 1;
}
...
void loop(void)
{
  // Clear the interrupt status flag for the alarms
  // so that they can interrupt again
  if(interrupted) {
    // Write a zero to the Control Status Register
    RTC_write_register(RTC_CTLSTAT,0x00);
    interrupted = 0;
  }
  .
  .
  .
}

Pete

Hi -
Thanks for your excellent solution. I am responding to this post because I finally learned how to correctly handle interrupts on the DS3231, and likely other devices. I am writing this to answer the original question and to also serve as a very brief, and hopefully clear to read, tutorial. The code example below is based using Alarm 1 on a DS3231.

  1. Create a variable that will indicate the interrupt occurred
    volatile boolean clockInterrupt = false; //This will be set TRUE when there is an interrupt

  2. Instruct the Arduino what subroutine to invoke if the interrupt is detected.
    // Interrupt #1 is Pin 3 on the UNO. you can also use Interrupt #0 and Pin 2
    // clockTrigger is the subroutine that will be called when the Interrupt occurs

attachInterrupt(1, clockTrigger, FALLING);

3)Set up the interrupt subroutine. Keep this as short as possible. Ideally it should have nothing more than the statement to set flag that says the interrupt occurred
void clockTrigger() {
clockInterrupt = true; //don't do any other processing in this subroutine
}

  1. In the loop routine set if an IF statement to detect if the interrupt flag is set
    void loop() {
    ....
    if(clockInterrupt){
    .... //Put the code you want to execute when the interrupt occurs
    //such as turn on a light or display a message
    clearClockTrigger(); //Finally after the code you want to execute because of the
    // interrupt completes, call a subroutine to clear the interrupt flag
    // so if there is another interrupt, the interrupt code will run again.
    }
    ....
    }

  2. Clearing the alarm flag. This step is very poorly documented in many examples on the web. Before you can write to and clear the alarm flag you have to read the flag first!

In following example:

  • the address of the DS3231 is 0x68
  • Bit 0 (the least significant bit) in status register 0F needs to be set to zero to reset the
    alarm bit.

void clearClockTrigger()
{
Wire.beginTransmission(0x68); //Tell devices on the bus we are talking to the DS3231
Wire.write(0x0F); //Tell the device which address we want to read or write
Wire.endTransmission();
Wire.requestFrom(0x68,1); // Read one byte
dummyRegister=Wire.read(); // In this example we are not interest in actually using the bye
Wire.beginTransmission(0x68); //Tell devices on the bus we are talking to the DS3231
Wire.write(0x0F); //Tell the device which address we want to read or write
Wire.write(0b00000000); //Write the byte. The last 0 bit resets Alarm 1
Wire.endTransmission();
clockInterrupt=false; //Finally clear the flag we use to indicate the trigger occurred
}

Hope this helps

I have been using code which clears the flag by writing zero to the control register. I do not read the register first and it has been working for at least nine months now. I can't find anything in the datasheet which says that the register must be read first but I may have missed it.

Pete