Fragen zum Design einer LED Uhr

Hallo zusammen,

das ist mein erster Post hier und ich möchte eine Frage zum technischen Design einer RGB LED Stripe Uhr stellen.

Ich habe eine 60 RGB LED Strip in eine Ziffernuhr eingebaut, so dass die Striche, die die Minuten und Stunden markieren jeweils durch eine LED beleuchtet werden. Die Ansteuerung der LEDs ist kein Problem.

Ich möchte jetzt gerne eine DS1307 RTC verbauen, aus folgenden Gründen:

  • batterigepufferte Uhrzeit
  • Nutzung des Sekunden-Interrupts zum Aktualisieren der LEDs

Die LED-Uhr soll über 3 Taster eingestellt werden können:

  • Anzeigemodus der LEDs
  • Stunden
  • Minuten

Im Moment baue ich das Ganze prototypisch mit einem UNO auf und möchte später die Uhr mit Batterien betreiben.

Meine Frage bezieht sich jetzt darauf, ob die Stromversorgung per Batterie dazu ausreicht bzw. was ich dazu nehmen sollte und, wenn das Ganze läuft, ob ich mit dieser Konfiguration auf einen ATTiny84A switchen kann (um zusätzlich Strom zu sparen).?

Igotcha: Meine Frage bezieht sich jetzt darauf, ob die Stromversorgung per Batterie dazu ausreicht

Rechne es Dir aus, Du benötigst die Batteriekapazität in "Ah" und die Stromaufnahme Deiner Schaltung in "A" oder wahlweise auch die Batteriekapazität in "mAh" und die Stromaufnahme in "mA". Dann rechnest Du:

Ungefähre Batterielaufzeit in Stunden = Batteriekapazität geteilt durch Stromaufnahme

Un schon weißt Du, ob ein Batteriewechsel alle halbe Stunde, alle 2 Stunden oder sogar erst alle 5 oder 10 Stunden fällig ist. Oder wie oft auch immer dabei herauskommt.

Hallo igotcha,

nehme lieber ein DS3231 RTC Modul. Die DS1307 laufen ungenau und bei der Kostendifferenz macht ein angleichen wenig Sinn.
Den Sekunden-Interupt brauchst du nicht zwingend zum aktualisieren der Leds. Wenn doch, ist auf den mir bekannten I2C RTC Modulen mit DS3231 ein Ausgang der ein Rechteck-Signal ausgibt (INT/SQW), welches mit 1Hz arbeitet.

Das mit der Batterie solltest du dir aus dem Kopf schlagen. Das wird nicht klappen. Ob nun ein Mega oder ein kleiner Attiny angeschlossen ist, spielt kaum eine Rolle, da beide mit Abstand die kleinsten Verbraucher sind.

Wenn du keine Autobatterie in der Uhr verschwinden kannst, steht dir keine ausreichende Kapazität zur Verfügung.

Ja, Batterie ist bei den Mengen LEDs aussichtslos.

Und es reicht eigentlich wenn du auf dem Arduino per millis() überprüfst ob eine Sekunde vergangen ist und dann updatest. So super-genau muss da nicht sein.

Ein Uno hat üblicherweise einen Resonator und keinen Quarz --> nach 5-30 Minuten ist der schon locker eine Sekunde nebendran. DS1307 ist besser aber auch nicht das Gelbe vom Ei. DS3231 ist deutlich besser.

Ich meinte nach einer Sekunde die RTC auslesen. Statt den RTC-Interrupt zu verwenden. Da ist es egal wie weit die daneben liegt, solange man mitbekommt, dass eine Sekunde vergangen ist.

Es gibt auch ne Lib die erst mal die Arduino-Zeit nimmt und sich nach einem einstellbaren Intervall mit der RTC synchronisiert.

Die RTCs können alle ein 32768 Hz Signal erzeugen. Das kann man auch per Timer zählen lassen --> keine sonstige Kommunikation mit dem Chip erforderlich.

Warum so kompliziert, wenns nur um eine Uhr geht? Meine rufen einfach im Hauptprogramm ständig die Echtzeit ab und wenn eine Sekunde vergangen ist (der vorherige Sekundenwert wird einfach gespeichert), wird aktualisiert. Ausser ner Variablen braucht es da keinen Zauber weiter..

Du meinst außer einer Library die zum Chip passt und einer Variablen? Hängt davon ab was man einfacher findet.

Ich find einfach die Vorgehensweise, irgendwo her nen Interrupt zu nehmen, nur damit die Anzeige rechtzeitig aktualisiert wird, zu umständlich…den braucht es nicht.

Danke für eure Antworten, hier ist das Ergebnis: http://e-realm.de/arduino-rgb-led-uhr/

Ich habe mich für die einfache Variante der regelmäßigen Abfrage der Echtzeit in der loop entschieden, funktioniert problemlos.

Aber: Jetzt habe ich ein anderes Problem und muss wieder auf das Thema Interrupt zurückkommen. Ich habe noch einen DS18B20 Temperatursensor verbaut, um temperaturabhängig die Status-LED ändern zu können.

Die Temperatur frage ich auch in der loop ab, aber der DS18B20 reagiert ja nicht sofort, sondern mit einer Verzögerung von 0,5-1 Sekunde - das bringt mir natürlich meine Sekundenanzeige in diesem Moment ins Stocken.

Jetzt kann man natürlich sagen, man muss die Temperatur ja auch nicht jede Sekunde abfragen. Muss man auch nicht, aber egal in welchem Intervall ich abfrage, es stockt eben immer durch die Auslesezeit und ich hätte gerne eine "schöne" Lösung ;)

Schön wäre es, wenn man jetzt die Temperaturabfrage per Interrupt steuern könnte (z.B. alle 5 Minuten) und das Auslesen NICHT in der loop erfolgt, sondern in der Interruptroutine der Temperaturwert ausgelesen und an eine Variable übergeben wird, die wiederum in der loop verarbeitet wird. Beim Googlen habe ich aber nur etwas über Hardwareinterrupts gefunden, also wenn Sensoren o.ä. eine Änderung ausgeben, dass dann reagiert wird und nicht, dass zyklisch softwareseitig etwas abgefragt / durchgeführt wird.

Du willst es eigentlich eher anders herum. Die Sekundenanzeige in einem Interrupt, so dass sie andere Funktionen die gerade ausgeführt werden unterbrechen kann. Dann kannst du deine Temperatur immer noch alle x Minuten auslesen (das kann man einfach mit millis() zählen), aber die Anzeige sollte theoretisch immer noch regelmäßig aktualisiert werden. Es sei denn die DS18B20 Lib blockiert das...

Dafür reicht msTimer2: http://playground.arduino.cc/Main/MsTimer2

Oder wenn du es ganz perfekt mit der RTC synchronisieren willst, eben doch den SQW-Ausgang des RTC-Moduls mit 1Hz (siehe Register 7) nehmen. Das muss aber nicht unbedingt sein.

Serenifly: Du willst es eigentlich eher anders herum. Die Sekundenanzeige in einem Interrupt, ...

Oder wenn du es ganz perfekt mit der RTC synchronisieren willst, eben doch den SQW-Ausgang des RTC-Moduls mit 1Hz (siehe Register 7) nehmen. Das muss aber nicht unbedingt sein.

Ja, da hast Du eigentlich recht ;)

Und dann wäre ich wieder bei meiner Ausgangsfrage :)

Ich werde mich morgen mal dransetzen und den SQW des DS1307 nutzen. Muss dazu aber Pin2 umlöten, da ich da momentan einen Taster dran habe.

So, RTC Hardware Interrupt läuft, jetzt habe ich nur noch ein kleines Codeproblem.

Eingerichtet ist der Interrupt in setup() mit:

RTC_DS1307 rtc;

void setup() {
pinMode(2, INPUT);                  
digitalWrite(2, HIGH);                
attachInterrupt(0, DisplayIntTime, FALLING); 
... mehr Code
}

Meine Interruptroutine sieht wie folgt aus, die auch funktioniert, sofern ich den DateTime-Aufruf weglasse:

void DisplayIntTime()
{
   Serial.println(" Interrupt"); 
   // Uhrzeit aus RTC lesen
  DateTime t = rtc.now();

 ... weitere Code
}

Ich konnte eingrenzen, dass die DateTime-Zeile dazu führt, dass nichts mehr ausgeführt wird. Lösche ich die Zeile, dann funktioniert alles bestens (z.B. die serielle Ausgabe). In der ursprünglichen Fassung mit dem rtc.now()-Aufruf in der Loop funktionierte auch alles.

Ich vermute, es liegt an der Deklaration von "t", aber auch eine Deklaration mit volatile für t vor dem setup() führt nicht zum gewünschten Ergebnis.

Habt ihr dazu eine Idee zu?

Wahrscheinlich braucht die I2C Kommunikation das Interrupt-System. Und auf dem AVR können sich Interrupts nicht gegenseitig unterbrechen (außer man macht es per Hand. Aber das Normalverhalten ist dass sie blockieren).

Setze mal in der ISR nur eine bool-Variable und frage in der loop ab ob diese gesetzt ist. Dann weist du dass du updaten musst.

Hmmm, ich bin nicht so der C-Experte...

Ich habe jetzt eine

volatile bool bCheck = false;

definiert und in der Interrupt-Routine, diese geändert auf:

bCheck = false;

Im SerialMonitor wird mir dafür auch eine "1" angezeigt. Scheint also grds. zu funktionieren.

Bloss das bringt mir ja erstmal nichts, wenn ich zwar durch den SQW der RTC einen Interrupt auslösen, in diesem jedoch nicht die Uhrzeit der RTC abfragen kann. Das war ja der ganze Sinn der Sache =(

In der ISR das: bCheck = true;

Du Uhrzeit fragst du dann in der loop ab. Das geht schnell genug.

void loop()
{
    if(bCheck)
    {
        bCheck = false;

         Serial.println(" Interrupt"); 
         DateTime t = rtc.now();
    }
}

EDIT: Aber, da hast du wieder das Problem, dass die Temperatur-Messung läuft bis sie beendet ist. Also der Aussetzer ist immer noch da. :(

Du kannst glaube ich das Interrupt-Flag am Anfang der ISR mit sei() wieder setzen. Dann kommen andere Interrupts wieder. Aber keine Garantie dass das keine Probleme verursacht. In diesem Fall sollte es eigentlich gehen.

Siehe hier: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

Nested interrupts

The AVR hardware clears the global interrupt flag in SREG before entering an interrupt vector. Thus, normally interrupts will remain disabled inside the handler until the handler exits, where the RETI instruction (that is emitted by the compiler as part of the normal function epilogue for an interrupt handler) will eventually re-enable further interrupts. For that reason, interrupt handlers normally do not nest. For most interrupt handlers, this is the desired behaviour, for some it is even required in order to prevent infinitely recursive interrupts (like UART interrupts, or level-triggered external interrupts). In rare circumstances though it might be desired to re-enable the global interrupt flag as early as possible in the interrupt handler, in order to not defer any other interrupt more than absolutely needed. This could be done using an sei() instruction right at the beginning of the interrupt handler, but this still leaves few instructions inside the compiler-generated function prologue to run with global interrupts disabled.

Danke Dir erstmal für die tolle Unterstützung!

Mir ist gerade noch ein anderer Weg in den Sinn gekommen: Im setup() lese ich die Zeit aus und schreibe diese in die Variablen sekunden, minuten, stunden.

In der ISR zähle ich jetzt nur in jedem Zyklus (=1 Sekunde) die Sekunden hoch. Wenn Sekunden > 59 dann Sekunde = 0 und minuten = minuten + 1 etc.

if (sekunde > 59)
   {
      sekunde = 0;
      minuten = minuten + 1; 
   }
   
   if (minuten > 59)
   {
      minuten = 0;
      stunden = stunden + 1;
   }
   
   if (stunden > 23)
   {
      stunden = 0; 
   }

In der loop() hole ich mir einfach jede Stunde einmal die neue Zeit und schreibe diese wieder in die Variablen.

Das müsste doch ein verträglicher Weg sein, auch wenn die Temperaturmessung dazwischenfunkt.

Ja, das wird das vernünftigste sein. Die RTC nur als Taktgeber verwenden :) Wenn dein Interrupt dann eine Temperatur-Messung unterbrechen kann, stört sich das auch nicht und das Update kommt wirklich jede Sekunde.

Wie gesagt, das Interrupt-Flag wieder zu aktivieren sollte auch gehen, da der Interrupt so langsam kommt. Wenn es generell dieses Problem ist. Kannst es ja mal probieren. Einfach in dem Code wo du alles in der ISR ausliest, am Anfang sei() machen.

Obwohl das auch wieder problematisch sein kann wenn der Sensor gerade auf dem I2C Bus werkelt, du das unterbrichst und was anderes machst.

Grmpf, jetzt habe ich offenbar irgendwie den SQW ausgeschaltet und bekomme ihn nicht mehr an LOL.

Hatte die Uhr neu gestellt und die Batterie rausgenommen.

Edit: Erledigt. Das liegt daran, dass ich 2 DS1307 libs habe. Eine geht bei mir, die andere nicht. In der, die nicht geht, wird aber der SQW angeschaltet ;)