Periodische Langzeit-Timer

Timer mit ATtiny85 und ATmega328P

Für einige Anwendungen werden immer wieder längerperiodische Pausen benötigt, währenddessen eine elektronische Schaltung nichts tun soll, z.B. periodische Messungen, LED-Kerzen (sollen nur eine bestimmte Zeit "brennen"), Timelapse-Fotoaufnahmen, etc.

Watchdog-Timer

Verwendete Bauteile

Der Watchdog-Timer (WDT) ist für die Überwachung des Mikrocontrollers entwickelt worden, kann aber auch als Langzeit-Timer verwendet werden. Vorteil bei dieser Umsetzung ist, dass der Mikrocontroller während der Ruhezeit in Sleep-Mode versetzt werden kann und die Schaltung somit Strom sparen kann (v.a. wichtig bei batteriebetriebenen Anwendungen). Beim ATmega ist der WDT für 16 Millisekunden bis 8 Sekunden ausgelegt, so dass längere Intervalle mit mehreren WDT-Zyklen umgesetzt werden müssen.

Aufbau der Schaltung

Hier wird eine möglichst einfache Schaltung verwendet, d.h. beim Arduino können wir die interne LED zu Demonstrationszwecken hernehmen (Beim ATtiny wird an einen beliebigen Pin ebenfalls eine LED mit entsprechendem Vorwiderstand geschaltet).

Sketch

Der folgende Sketch lässt eine LED einige gewisse Zeit lang Leuchten und geht dann für längere Zeit in eine Ruhephase. Umgesetzt wird dies, indem der Mikrocontroller in den Sleep-Modus versetzt wird und der WDT nach seiner maximalen Überwachungszeit überprüft, ob die LED wieder erneut leuchten soll. Großer Nachteil dieser Schaltung ist, dass der WDT recht unpräzise ist und mit ca. 5%-10% Abweichung von der realen Zeit arbeitet. Somit ergeben sich besonders bei langen Ruhezeiten große Abweichungen der Schaltintervalle. Also ist diese Lösung weniger geeignet, wenn die Intervalle halbwegs genau sein sollen.

#include <avr/sleep.h>
#include <avr/wdt.h>

#define PIN_LED 13

#define LIGHT_TIME 10   // in seconds
#define PAUSE_TIME 7200 // in seconds

#define lmillis() ((long)millis())

bool isLedLit;
long nextAction;
volatile long ledTimer;

void setup()
{
    pinMode(PIN_LED, OUTPUT);
    digitalWrite(PIN_LED, HIGH);
    isLedLit = true;
    ledTimer = 0;

    cli();
    wdt_reset(); // reset watchdog timer
    MCUSR &= ~(1 << WDRF); // remove reset flag
    WDTCR = (1 << WDCE); // set WDCE, access prescaler
    WDTCR = 1 << WDP0 | 1 << WDP3; // set prescaler bits to to 8s
    WDTCR |= (1 << WDIE); // access WDT interrupt
    sei();
}

void loop()
{
    if (isLedLit) {
        if (ledTimer >= LIGHT_TIME) {
            ledTimer = 0;
            digitalWrite(PIN_LED, LOW);
            isLedLit = false;
        }
    } else {
        if (ledTimer >= PAUSE_TIME) {
            ledTimer = 0;
            digitalWrite(PIN_LED, HIGH);
            isLedLit = true;
        }
    }
    enterSleepMode();
}

// Watchdog timer interrupt service routine
ISR(WDT_vect)
{
    ledTimer += 8;
}

void enterSleepMode()
{
    byte adcsra;

    adcsra = ADCSRA; // save ADC control and status register A
    ADCSRA &= ~(1 << ADEN); // disable ADC

    MCUCR |= (1 << SM1) & ~(1 << SM0); // Sleep-Modus = Power Down
    MCUCR |= (1 << SE); // set sleep enable
    sleep_cpu(); // sleep
    MCUCR &= ~(1 << SE); // reset sleep enable

    ADCSRA = adcsra; // restore ADC control and status register A
}

Realtime Clock PCF8563

RTC PCF8563

Sogenannte RTC-ICs ("real time clock") sind recht präzise arbeitende Zeitgeber, mit denen man einen Mikrocontroller nach einer vorgegebenen Zeit aus dem Sleep-Mode wecken kann. In diesem Experiment wird der RTC PCF8563 verwendet, der einen Arduino mit seiner eigebauten Alarm-Funktion aufwecken soll.

Verwendete Bauteile

Aufbau

Die Schaltung kann vom Beitrag über Schreiben eines Registers des PCF8563 übernommen werden. Die LED am Pin #7 des RTC wird hier nicht benötigt. Allerdings muss zusätzlich eine Verbindung vom Pin #3 des RTC zum Pin D2 zum Arduino gemacht werden, damit der Interrupt auslösen kann.

Sketch I.

Die Library Rtc_Pcf8563 bietet die Möglichkeit, recht einfach den Alarm des PCF8563 zu stellen. Im Arduino kann man dann durch einen Interrupt den Pin #3 des PCF8563 (=Interrupt-Ausgang; active LOW) überwachen und entsprechend darauf reagieren.
Der folgende Sketch stellt den Alarm des RTC auf 30 in die Zukunft und zeigt dann in der seriellen Konsole des Arduino eine entsprechende Meldung, wenn der Alarm auslöst.

#include <Wire.h>
#include <Rtc_Pcf8563.h>

#define PIN_RTC_INT 2

Rtc_Pcf8563 rtc;
volatile byte alarmFlag = 0;

void alarmISR()
{
    alarmFlag = 1;
}

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

    pinMode(PIN_RTC_INT, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(PIN_RTC_INT), alarmISR, FALLING);

    rtc.initClock();

    // set current date/time of the RTC
    rtc.setDate(9, 6, 10, 0, 20); // day, weekday, month, century, year
    rtc.setTime(14, 15, 30); // hours, minutes, seconds

    /* set an alarm for 30 secs later.
     alarm pin goes low when match occurs
     this triggers the interrupt routine
    */
    rtc.setAlarm(16, 99, 99, 99); // minutes, hours, days, weekday (99 = no alarm)
}

void loop()
{
    Serial.print(rtc.formatDate());
    Serial.print(" ");
    Serial.print(rtc.formatTime());
    Serial.print(" - 0x");
    Serial.println(rtc.getStatus2(), HEX);
    if (alarmFlag == 1) {
        execAlarm();
    }
    delay(1000);
}

void execAlarm()
{
    detachInterrupt(digitalPinToInterrupt(PIN_RTC_INT));
    Serial.println("ALARM!");

    rtc.clearAlarm();
    alarmFlag = 0;
}
Ausgabe auf der seriellen Konsole
Abb.: Ausgabe auf der seriellen Konsole: Nach einer gewissen Zeit wird der Alarm des RTC ausgelöst und löst am Arduino einen Interrupt aus.

Sketch II.

Kombiniert mit dem Sleep-Mode des Arduino kann man folgenden Sketch verwenden. Hier wird zunächst die Zeit wie auch der Alarm des RTC gestellt. Danach geht der Arduino in den Sleep-Mode und überwacht den Pin #2. Sobald der Alarm des RTC ausgelöst wird, wird auf dem Interrupt eine fallende Flanke detektiert und der Arduino wird aus dem Sleep-Mode geholt. Um dies anzuzeigen wird die interne LED des Arduino für 2 Sekunden beleuchtet. Außerdem wird der Interrupt sowie der Alarm des RTC ausgeschaltet.

#include <avr/sleep.h>
#include <Wire.h>
#include <Rtc_Pcf8563.h>

#define PIN_RTC_INT  2
#define PIN_LED      13

Rtc_Pcf8563 rtc;
volatile bool isLedLit = false;

void isrAwake()
{
    isLedLit = true;
}

void enterSleepMode()
{
    attachInterrupt(digitalPinToInterrupt(PIN_RTC_INT), isrAwake, FALLING);
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    sleep_mode();
    sleep_disable();
}

void setup()
{
    pinMode(PIN_RTC_INT, INPUT_PULLUP);
    pinMode(PIN_LED, OUTPUT);

    rtc.initClock();

    // set current date/time of the RTC
    rtc.setDate(9, 6, 10, 0, 20); // (2020-10-09) day, weekday, month, century, year
    rtc.setTime(12, 15, 30);      // (12:15:30) hours, minutes, seconds

    // set an alarm for 30 secs later
    rtc.setAlarm(16, 99, 99, 99); // minutes, hours, days, weekday (99 = no alarm)
}

void loop()
{
    if (isLedLit) {
        detachInterrupt(digitalPinToInterrupt(PIN_RTC_INT));
        rtc.clearAlarm();
        isLedLit = false;
        digitalWrite(PIN_LED, HIGH);
        delay(2000);
        digitalWrite(PIN_LED, LOW);
    }
    enterSleepMode();
}

Schaltung mit Sensoren

Natürlich kann man die Ruhephasen auch von anderen äußeren Einflüssen abhängig machen, z.B. von der Intensität des Umgebungslichtes. Dafür bieten sich LDRs oder Fotodioden bzw. Fototransistoren an.
Einige der folgenden Beiträge haben dies schon realisiert:

Verwendung des NTP

Atomuhr

Geht es weniger um minimalen Stromverbrauch, aber mehr um präsize Zeitintervalle, so bietet sich ein internetfähiger Mikrocontroller (z.B. ESP32) an, der sich mit einem NTP-Service (Network Time Protocol) im WWW verbindet und dort die genau Atomzeit ausliest.

Externer Puls-/Zeitgeber (NE555)

NE555

Eine weitere Möglichkeit ist auch, den Mikrocontroller in Sleep-Mode zu versetzen und dann in bestimmten Intervallen wieder aus dem Tiefschlaf aufwecken zu lassen. Dies könnte man beispielsweise mit einem NE555 als astabile Kippstufe oder auch mit einem CD4060-Oszillator-IC realisieren.
Hier muss allerdings die Stromaufnahme des ICs geprüft werden, denn beispielsweise verbraucht der NE555 ca. 5-10mA, kann aber in der CMOS-Version bis auf 250µA (bei 5V) heruntergedrückt werden. Beim NE555 hängt die Präzision des Timers jedoch an den externen Komponenten (Widerstand und Kondensator), die dann vor der Verwendung genau geprüft werden müssen, um möglichst präzise Ergebnisse zu erzielen.

Externer Langzeit-Timer LTC6995

Langzeit-Timer LTC6995

Eine weitere Möglichkeit ist, einen externen, sogenannten "Long Timer, Low Frequency Oscillator" wie den LTC6995 zu verwenden. Dieser besitzt laut Hersteller folgende Merkmale:

Bisher habe ich diese Möglichkeit noch nicht ausprobiert, ein Beitrag ist aber unter LTC6995 – Long Timer verfügbar.

zurück