JChristensen / DS3232RTC

Arduino Library for Maxim Integrated DS3232 and DS3231 Real-Time Clocks
GNU General Public License v3.0
394 stars 137 forks source link

Strange interrupts behavior after switching the power #23

Closed igor-iOS closed 8 years ago

igor-iOS commented 8 years ago

Hi, guys I'm using a very simple sketch where alarm1 fires every second and alarm2 is not used. This is the only difference from @JChristensen's code form this issue: https://github.com/JChristensen/DS3232RTC/issues/5 . I still cannot specify the exact case when it happens, but here's what I found: If I switch the power of the whole board off/on or press "reset" button on Arduino, randomly in some cases alarms stop firing. If I only switch the power of ds3232 (in real, ds3231) shield, alarms always stop firing in all cases. The battery is installed and I've checked the correct work by getting time from the RTC after power off/on and it was definitely correct. Unfortunately, I don't have an oscilloscope to check whether it's some signal on SQW pin or not. What I also found is that if we pull of the jumper from SQW to Arduino INT pin, and then reconnect it, program starts to work correctly. This is really strange for me and I cannot find the solution. I checked this on both power from 5V and 3.3V. Maybe I should use pull-up resistors somewhere? I also tested this on Arduino Micro and Arduino Mega1280, with different interrupt inputs, but result is still the same. I tried to use SQWAVE_1_HZ instead of alarm1, the signal is also handled in the interrupt vector. Made all the types of power switches that are mentioned above, and all works fine constantly. So I guess the problem is about resetting the alarm flags, but have no idea what to add and where.

#include <DS3232RTC.h>        //http://github.com/JChristensen/DS3232RTC
#include <Streaming.h>        //http://arduiniana.org/libraries/streaming/
#include <Time.h>             //http://playground.arduino.cc/Code/Time
#include <Wire.h>             //http://arduino.cc/en/Reference/Wire

#define SQW_PIN 2

void setup(void)
{
    Serial.begin(9600);

//  while(!Serial); 
    //setSyncProvider() causes the Time library to synchronize with the
    //external RTC by calling RTC.get() every five minutes by default.
    setSyncProvider(RTC.get);
    Serial << "RTC Sync";
    if (timeStatus() != timeSet){
        Serial << " FAIL!";
    }
    Serial << endl;
  pinMode(13, OUTPUT);
    printDateTime( RTC.get() );

    //Disable the default square wave of the SQW pin.
    RTC.squareWave(SQWAVE_NONE);

    //Attach an interrupt on the falling of the SQW pin.
    //digitalWrite(SQW_PIN, HIGH);    //redundant with the following line
    pinMode(SQW_PIN, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(SQW_PIN), alarmIsr, FALLING);

    RTC.setAlarm(ALM1_EVERY_SECOND, 1, 0, 0, 1);    //daydate parameter should be between 1 and 7
    RTC.alarm(ALARM_1);                   //ensure RTC interrupt flag is cleared
    RTC.alarmInterrupt(ALARM_1, true);
     RTC.alarm(ALARM_1);

}

volatile boolean alarmIsrWasCalled = false;

void alarmIsr()
{
  digitalWrite(13, !digitalRead(13));
  Serial.println("!");
//  RTC.alarm(ALARM_1);
    alarmIsrWasCalled = true;
}

void loop(void)
{
    if (alarmIsrWasCalled){
        if (RTC.alarm(ALARM_1)) {
            printDateTime( RTC.get() );
            Serial << " --> Alarm 1!" << endl;
        }

        alarmIsrWasCalled = false;
    }
}

void printDateTime(time_t t)
{
    Serial << ((day(t)<10) ? "0" : "") << _DEC(day(t)) << ' ';
    Serial << monthShortStr(month(t)) << " " << _DEC(year(t)) << ' ';
    Serial << ((hour(t)<10) ? "0" : "") << _DEC(hour(t)) << ':';
    Serial << ((minute(t)<10) ? "0" : "") << _DEC(minute(t)) << ':';
    Serial << ((second(t)<10) ? "0" : "") << _DEC(second(t));
}
JChristensen commented 8 years ago

Please provide a link to the DS3231 shield being used.

JChristensen commented 8 years ago

The other difference from my code in issue #5 is the addition of calls to digitalWrite() and Serial.println() in the ISR. These have absolutely no business in an ISR. I'm surprised that it works at all. Actually digitalWrite() would probably be OK in the ISR, but I'd move it out to loop() anyway as there is no downside to doing so.

Secondly, since Alarm2 is not used, we must ensure that it is disabled. Else it will probably trigger at some unexpected time and hold the interrupt line low.

This code works for me.

#include <DS3232RTC.h>        //http://github.com/JChristensen/DS3232RTC
#include <Streaming.h>        //http://arduiniana.org/libraries/streaming/
#include <Time.h>             //http://playground.arduino.cc/Code/Time
#include <Wire.h>             //http://arduino.cc/en/Reference/Wire

#define SQW_PIN 2

void setup(void)
{
    Serial.begin(9600);

//  while(!Serial); 
    //setSyncProvider() causes the Time library to synchronize with the
    //external RTC by calling RTC.get() every five minutes by default.
    setSyncProvider(RTC.get);
    Serial << "RTC Sync";
    if (timeStatus() != timeSet){
        Serial << " FAIL!";
    }
    Serial << endl;
    pinMode(13, OUTPUT);
    printDateTime( RTC.get() );
    Serial << endl;

    //Disable the default square wave of the SQW pin.
    RTC.squareWave(SQWAVE_NONE);

    //Attach an interrupt on the falling of the SQW pin.
    //digitalWrite(SQW_PIN, HIGH);    //redundant with the following line
    pinMode(SQW_PIN, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(SQW_PIN), alarmIsr, FALLING);

    RTC.setAlarm(ALM1_EVERY_SECOND, 1, 0, 0, 1);    //daydate parameter should be between 1 and 7
    RTC.alarm(ALARM_1);                   //ensure RTC interrupt flag is cleared
    RTC.alarmInterrupt(ALARM_1, true);
    RTC.alarmInterrupt(ALARM_2, false);
    RTC.alarm(ALARM_2);                   //ensure RTC interrupt flag is cleared
}

volatile boolean alarmIsrWasCalled = false;

void alarmIsr()
{
//  digitalWrite(13, !digitalRead(13));
//  Serial.println("!");
//  RTC.alarm(ALARM_1);
    alarmIsrWasCalled = true;
}

void loop(void)
{
    if (alarmIsrWasCalled){
        if (RTC.alarm(ALARM_1)) {
            digitalWrite(13, !digitalRead(13));
            printDateTime( RTC.get() );
            Serial << " --> Alarm 1!" << endl;
        }
        alarmIsrWasCalled = false;
    }
}

void printDateTime(time_t t)
{
    Serial << ((day(t)<10) ? "0" : "") << _DEC(day(t)) << ' ';
    Serial << monthShortStr(month(t)) << " " << _DEC(year(t)) << ' ';
    Serial << ((hour(t)<10) ? "0" : "") << _DEC(hour(t)) << ':';
    Serial << ((minute(t)<10) ? "0" : "") << _DEC(minute(t)) << ':';
    Serial << ((second(t)<10) ? "0" : "") << _DEC(second(t));
}
JChristensen commented 8 years ago

Regarding pullup resistors, one is required for the interrupt signal but the code enables the internal pullup and this is sufficient.

Pullups are also required on the I2C SDA and SCL lines. The Wire library enables the internal pullups, and while this will often work, the internal pullups are technically not strong enough, so external pullups must be added. When using the default I2C bus speed of 100kHz, 10K is OK but 4.7K is better. When running the bus at 400kHz, I use 2.2K.

When multiple I2C devices are on the bus, only one pair of pullups is required. When using breakout boards, be sure to check if each has its own pullups. Duplicate pullups are OK as long as the resistance (the resistors would be in parallel) doesn't fall too low and overload the GPIO pins.

igor-iOS commented 8 years ago

Sorry, I was too busy and could not reply. Yes, I know about the fact that ISR procedures should contatin as less code as possible, and it is also strange for me that it works in some cases. I also attemted to add

 detachInterrupt(digitalPinToInterrupt(SQW_PIN));

in setup() function before setting alarms. Seems that it helped. Thanks, I also turned off alarm2, seems to wrk fine. About the shield - I bought it in a local store, so the description is in russian. But here is how it looks: http://www.kosmodrom.com.ua/pic/DS3231-BIG-MODUL-1.jpg Possibly, zs-042 is some chinese noname breakout board for DS3231.