Wecker mit 1307 - timing frage

Hallo an alle!

Also ich bin gerade dabei mir einen Wecker zu bauen. Jetzt habe ich da mal eine kleine Frage wie ich das mit dem timing genau hinbekommen soll. Zur Zeit läuft mein Programm so, dass mir der 1307 ein 1Hz Signal am SQW/OUT Signal ausgibt, das ich an einen externen Interrupt-Eingang am Arduino (Nano) angeschlossen habe. Wird dieser ausgelöst, so wird der Wert einer Variable, ich nenn sie mal "i", auf 1 gesetzt. In der loop Funktion wird nun immer der Status von "i" abgefragt. Falls "i = 1", dann wird die aktuelle Zeit von 1307 gelesen und am LCD ausgegeben, und anschließen i auf 0 gesetzt. Das dauert insgesamt natürlich weniger als 1 Sekunde, wodurch die Zeit am LCD immer aktuell bleibt.

So nun wird mein Programm um einen Dreh-Decoder erweitert, der beide externen Interrupts benötigt für die Signale A und B. Jetzt frag ich mich wie ich die Zeit am LCD aktuell halten soll. Mir kam die Idee mit einem Timer zu arbeiten, der nach einer Sekunde eine Funktion aufruft die die Daten vom 1307 liest, und das ans LCD schreibt. Nur: Der Timer kann nur ca. 1 Sekunde zählen, da aber der Fehler nicht so groß ist, wird es dann hald irgendwann passieren dass die Zeit zweimal das gleiche anzeigt (oder eine Sekunde überspringt?), da aber die Zeit am 1307 immer richtig is, wird das dann beim nächsten mal lesen korrigiert... Größeres Problem ist: wenn der Timer einen Overflow hat, dann hört er auf zu zählen und ruft die Funktion auf. Diese wird ausgeführt und benötigt dafür "t" Millisekunden. Danach fängt der Timer wieder an zu zählen, ergo die Zeit zwischen zwei Aufrufen der Funktion die die Daten des 1307 liest und ans LCD schreibt ist 1 Sekunde (Timer) + t Millisekunden (Funktion). Oder hört der Timer bei einem Overflow nicht auf zu zählen?

Wie macht man das richtig?

Hoffe ihr könnt mir da helfen,

lg Philip

Den 1307 einmal pro Sekunde auszulesen halte ich persoenlich fuer unnoetig.
Einmal am start des Programms auslesen und dann vielleicht alle paar Stunden oder so. Das LCD einfach im loop aktualisieren und millis() benutzen im die aktuelle Zeit zu berechnen. Und nur aktualisieren wenn die angezeigte Zeit sich aendert. Das heist wenn das Display Stunden und Minuten zeigt (aber keine Sekunden), braucht man nur dann zu Aktualisieren wenn die Minute sich aendert.

Du brauchst keine 2 Interrupt für den Drehencoder. Es genügt wenn Du nur eine Phase auf Interrupt gibst und die zweite dann einliest. Du erhälst so zwar nur die hälfte der Auflösung als wenn Du beide Phasen auf Interrupt gibst, das ist bei einem Eingabeencoder nicht weiter schlimm.
Viele Grüße Uwe

hi,

Du mußt das SQW/OUT-signal nicht mit einem interrupt abfangen. das ist kein "sehr kurzer" impuls, den man übersehen könnte, sondern 1/2 sekunde da, 1/2 sekunde weg.
wenn Du also einfach in der loop abfragst, wirst Du immer schnell genug sein.

gruß stefan

Hallo, danke schon mal für die schnellen Antworten...

Das mit dem Drehencoder ist eine gute Idee, nur hat der zusätzlich ja noch einen push button, aber den könnte ich sonst einfach an einem normal Pin einlesen....

Das mit dem nicht so oft einlesen wäre auch eine Idee, Danke! Nur würde es mich trotzdem interessieren wie man das macht wenn man keinen externen Interrupt verwenden will, und die Zeit wirklich immer exakt sein soll? Oder wird das grundsätzlich immer so umgesetzt?

@Eisebaer: Ich frage aber die Flanke ab, bei deiner Methode würde doch dann die selbe Zeit öfter eingelesen als notwendig?

lg Philip

hi,

@Eisebaer: Ich frage aber die Flanke ab

naja, ist ja nicht vorschrift...
ich mache es so:
wenn ((sqw == 1) && (statusvariable == false)) > sekunde + 1, statusvariable = true
wenn (sqw == 0) > statusvariable = false

gruß stefan

Ich habe den SQW/OUT auf 1HZ stehen dann hast du 500ms High und 500ms LOW. Ich habe dann über normalen digital.Read() den Impuls ausgewerten und dann intern bis 60 gezählt und dann die RTC ausgelesen.
Gruß
Der Dani

/*
Attiny84 Big 7 Segment Uhr mit 74HC595
 
 */


#include <TinyWireM.h>
#include <USI_TWI_Master.h>


#define Shiftdata  8          // Pin 8 Datenleiung Schieberegister connected to Data in (DS) of 74HC595
#define Shiftclock 10           // Pin 9 Clockleitung connected to clock pin (SH_CP) of 74HC595
#define Shiftlatch 9         // Pin 10 Latchleitung connected to latch pin (ST_CP) of 74HC595
#define PWMOUT 7
#define BUTTON 1
#define RTCPuls 2
#define LED 0
#define RTC_ADDRESS 0x68
#define TIME 750

byte ie,iz,hz,he,mz,me=0;  // Modulespeicher der stunden Minuten
boolean ldruck,druck=false;   // Stellknopf für Zeit 
boolean longpress,shortpress=false;  // Unterscheidung langer kurzer druck
boolean blinktakt=false;             // Blinktakt 500ms
boolean lpuls,puls=false;            // Sekundenpuls
boolean hell=false;
unsigned long lmillis,start,ptime=0;
byte ss=0;
byte mm=0;
byte hh=0;
byte minuten,stunden=0;
byte setmode=0;
byte PWMArray[7]={
  0,20,40,80,120,160,240};
byte Pointer=5;


byte SDec2Shift(byte x){
  byte xeiner=x%10;
  byte xzehner=(x-xeiner)/10;
  return (xzehner<<4)+xeiner;
  ;
}


void setup(){
  pinMode(Shiftdata,OUTPUT);
  pinMode(Shiftclock,OUTPUT);
  pinMode(Shiftlatch,OUTPUT);
  pinMode(BUTTON,INPUT_PULLUP);
  pinMode(RTCPuls,INPUT);
  pinMode(PWMOUT,OUTPUT);
  pinMode(LED,OUTPUT);
  TinyWireM.begin();                                  // Start I2C Kommunikation
  TinyWireM.beginTransmission(0x68);                  // Beginn Kommunikation auf  Adresse 0x68 
  TinyWireM.send(0x07);                               // Pointer auf Control Register 0x07   
  TinyWireM.send(0x10);                               // Steuerwort Ex "0x30" Bin110000 Ausgang RTC Ausgang auf 1Hz Logic normal Levels setzn
  TinyWireM.endTransmission();                        // Beenden der I2C Kommunitkation
  HourRead();                                         // RTC Funktion lesen der aktuellen zeit Stunden
  MinuteRead();                                       // RTC Funktion lesen der aktuellen zeit Minuten
  SecondsRead();
  RTC_Start();                                        // Start der Uhr
  if (hh>=7){
    hell=true;
  }
}


void loop(){
  /*=>Routine OneButton Control<=*/
  druck=digitalRead(BUTTON);            // Einlesen des Button (Achtung invertierte Logik da intrener Pull Up Widerstand aktiviert)
  if (druck==false&&ldruck==true){      // Steigende Flanke des Button
    start=millis();                     // Startzeit des Bettätigen
    ldruck=false;                       // Hilfsflag zur Flanken erkennung
  }
  if (druck==true&&ldruck==false){      // Falende Flanke des Buttons
    ptime=millis()-start;               // Die Länge in ms des Gedückten Zustandes
    ldruck=true;
  }
  if (ptime>=TIME){                    // Wenn der Druck länger (Hier750ms) 
    longpress=true;                    // War ein Langer druck
    ptime=0;                           // Zeit wieder Zurücksetzten
  }
  if (ptime<TIME&&ptime>=50) {         // Wenn der Druck kürzer 750ms und länger 50ms (gegen prellen)
    shortpress=true;                   // War ein kurzer Durck
    ptime=0;                           // Zeit wieder zurücksetzuten
  }
  /*One Button End*/
  puls=digitalRead(RTCPuls);                 // Einlessen des RTC Sekunden Puls
  if (puls==true&&lpuls==false){             // wenn Sekundenpuls da und vorher nicht da war (steigende Flanke)
    lpuls=true;
    ss++;                                    // intern Sekunden zählen
  }
  if (ss>=60&&setmode==0){                   // Nach 60 Sekunden
    HourRead();                              // RTC Funktion lesen der aktuellen zeit Stunden
    MinuteRead();                            // RTC Funktion lesen der aktuellen zeit Minuten
    ss=0;                                    // Rücksetzzeten internen Sekunden Zähler auf 0
  }
  if (puls==false){                          // RTC Sekunden Hilfspuls
    lpuls=false;
  }
  if (shortpress==true){                    // Routine Kurzes drücken
    if (setmode==0){                        // In Normalmodus 
      Pointer++;                            // ändern des Pointer der Helligkeit
      if (Pointer>=7){                      // Bei Pointer 6 und größer auf Pointer Null
        Pointer=0;
      }
    }
    if (setmode==1){                        // Minuten stellen
      mm++;
      if (mm>=60){
        mm=0;
      }
    }
    if (setmode==2){                        // Stunden stellen
      hh++;
      if(hh>=24){
        hh=0;
      }
    }
    shortpress=false;
  }
  if (longpress==true){                      // Routine Langes drücken
    setmode++;                               // Stellmodus erhöhen
    if (setmode==3){                         // Bei erreichen des Modus 3
      RTC_Set();                             // Neue Uhrzeit übernehmen in die RTC
  //    delay(250);
  //    RTC_Start();                           // RTC Starten
      setmode=0;                             // Rücksetzten des Setmodes auf 0 (Normale Anzeige)
    }
    longpress=false;                         // Rücksetzten des Flags Langes Drücken
  }
  if (millis()-lmillis>=500){                // Aktalisiernung alle halbe Sekunden
    blinktakt= !blinktakt;                   // Blinktakt der Stellen anzeige
    minuten=SDec2Shift(mm);
    stunden=SDec2Shift(hh);
    if (blinktakt==false){                  // Blinkroutine des Stellmodus
      if(setmode==1){                       // Minuten stellen
        minuten=B10101010;
      }
      if (setmode==2){                      // Stunden Stellen
        stunden=B10101010;
      }
    }
    digitalWrite(Shiftlatch, LOW);     // Latch aussetzten um in den Storage speicher zu schreiben 
    shiftOut(Shiftdata, Shiftclock, MSBFIRST,  stunden);                    // Shift out der einzelenen Puffer wobei im letzten
    shiftOut(Shiftdata, Shiftclock, MSBFIRST,  minuten);                  // Shift out der einzelenen Puffer wobei im letzten
    digitalWrite(Shiftlatch, HIGH);                                        // Aktiverien des Latches (Schieben des Shift ind Storageregister) zum Anzeigen
    lmillis=millis();  
  }
  if (hh==19&&hell==true){                         // wenn Später 22Uhr und Früher als 8Uhr und es vorher Hell war dann dunkelschlaten
    Pointer=0;
    hell=false;
  }
  if (hh==7&&hell==false){                       // Wenn vor 22Uhr und nach 8 Uhr und vorher dunkel war dann Hell Schalten
    Pointer=5;
    hell=true;
  }
  analogWrite(PWMOUT,PWMArray[Pointer]);
  if (Pointer==0){
    digitalWrite(LED,LOW);
  }
  else
    digitalWrite(LED,HIGH);
}


void HourRead(){
  TinyWireM.beginTransmission(RTC_ADDRESS);
  TinyWireM.send(0x02);               // Pointer auf Sekunden
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(RTC_ADDRESS, 1);
  hh= bcdToDec(TinyWireM.receive());
}

void MinuteRead(){
  TinyWireM.beginTransmission(RTC_ADDRESS);
  TinyWireM.send(0x01);               // Pointer auf Sekunden
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(RTC_ADDRESS, 1);
  mm= bcdToDec(TinyWireM.receive());
}

void SecondsRead(){
  TinyWireM.beginTransmission(RTC_ADDRESS);
  TinyWireM.send(0x00);               // Pointer auf Sekunden
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(RTC_ADDRESS, 1);
  ss= bcdToDec(TinyWireM.receive());
}


void RTC_Start(){
  TinyWireM.beginTransmission(RTC_ADDRESS);
  TinyWireM.send(0x00);
  TinyWireM.send(decToBcd(ss));    // 0 to bit 7 starts the clock
  TinyWireM.endTransmission();
}


void RTC_Set(){
  TinyWireM.beginTransmission(RTC_ADDRESS);
  TinyWireM.send(0x00);
  TinyWireM.send(decToBcd(ss));    // 0 to bit 7 starts the clock
  TinyWireM.send(decToBcd(mm));
  TinyWireM.send(decToBcd(hh));      // If you want 12 hour am/pm you need to set
  TinyWireM.endTransmission();
}

byte decToBcd(byte val)
{
  return ((val/10)<<4)+(val%10);
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ((val>>4)*10)+val%16;
}

Das Programm ist aus meiner Nixieclock mit einem attiny84, aber die Routine kannst du verwenden. :wink:
Das ist auch einen onebutton Logik drin also langer durck um die setmodes (minuten stellen und stunden stellen) kurzer druck um Wert zu änden +1.

Größeres Problem ist: wenn der Timer einen Overflow hat, dann hört er auf zu zählen und ruft die Funktion auf. Diese wird ausgeführt und benötigt dafür "t" Millisekunden. Danach fängt der Timer wieder an zu zählen, ergo die Zeit zwischen zwei Aufrufen der Funktion die die Daten des 1307 liest und ans LCD schreibt ist 1 Sekunde (Timer) + t Millisekunden (Funktion). Oder hört der Timer bei einem Overflow nicht auf zu zählen?

Welcher Timer ? millis() läuft immer durch. Was brauchst du mehr ?

Und wofür sollen für Drehencoder und DS1307 Interrupts gut sein ?

" die Zeit wirklich immer exakt " darf Udo aber nicht sehen,
sonst musst du erklären was das Ganze mit Nano-Arduino und DS1307 zu tun haben soll.

@eisebaer: achja so geht das natürlich... hmpf da bin ich auf der Leitung gestanden...

@volvodani: super danke, werd ich mir zu Hause anschauen!!

@michael_x: naja, der interne hardware timer/counter von einem atmega328. Die Interrupts brauchst du beim Drehcoder damit du die Flanken von A und B bekommst, nur dann hast du die volle Auflösung. Für den 1307 brauch ich den Interrupt jz e nicht mehr :slight_smile:

phil0702:
naja, der interne hardware timer/counter von einem atmega328. Die Interrupts brauchst du beim Drehcoder damit du die Flanken von A und B bekommst, nur dann hast du die volle Auflösung. Für den 1307 brauch ich den Interrupt jz e nicht mehr :slight_smile:

Ein atmega328 hat 3 timer, aber um die Innereien brauchst du dich normalerweise nicht zu kümmern.
Und meiner Meinung nach kriegst du die 2 Pulse eines Encoders auch so mit, wenn du willst. Aber wenn du die Arduino Pins 2+3 frei hast, ist das evtl. einfacher, ok.
Im Playground gibt es die verschiedensten Libraries (mit und auch ohne Interrupts ) ...
http://playground.arduino.cc/Main/RotaryEncoders

Eine andere Möglichkeit ist die Time Library:
http://www.pjrc.com/teensy/td_libs_Time.html

Die zählt die Zeit lokal und man kann einstellen wie oft sie sich mit der RTC synchronisiert, z.B. alle paar Minuten oder Stunden.

Gibt dazu auch TimeAlarms, mit der man schön Methoden zeitgesteuert ausführen kann:
http://www.pjrc.com/teensy/td_libs_TimeAlarms.html