max7219 Stoppuhr mit Millisekunden

Hi,

ich habe vor, mir eine Stoppuhr zu bauen, die beim Loslassen eines von 2 Tastern losläuft und bei Betätigung beider Taster wieder anhält.
Die Stoppuhr soll wie folgt aussehen: Stunden, Minuten, Sekunden, Millisekunden. 0:00:00:000
Das habe ich auf einen LCD Display auch binbekommen, doch auf mehreren 7 Segmentanzeigen über den max7219 leider nicht.
Ab den Sekunden läuft alles relativ genau, jedoch die Millisekunden springen nicht zum selben Zeitpunkt um, wie die Sekunden und laufen auch langsamer als die Sekunde.

Ich nehme an dass ich es schlecht programmiert habe, hab erst seit 2 Wochen einen Arduino.
Gibt es wie bei dem LCD eine Möglichkeit, dass Zahlen, die größer als 10 sind, automatisch auf dem nächsten Digit angezeigt werden? So könnte ich mir die Millisekunden z.b. auf Digit 7 anzeigen lassen und wenn diese größer als 10 oder 100 sind, wird die Zahl auf Digit 6 und 5 geschrieben.

Mich interessiert trotzdem noch, warum das nicht so funktioniert, wie es gemacht habe, hier der code:

Danke im Voraus.

#include <StopWatch.h>
#include "LedControl.h"

// Pin 12 to Data In, 11 to Clk, 10 to LOAD
LedControl lc=LedControl(12,11,10,1);

int fsec, f1sec, f2sec, f3sec, esecs, esecs1, emin1, emin2, estd1, emillis, emicros;
StopWatch sw_millis;    // MILLIS (default)
StopWatch sw_secs(StopWatch::SECONDS);
StopWatch sw_micros(StopWatch::MICROS);
void setup()
{
  // the zero refers to the MAX7219 number, it is zero for 1 chip
  lc.shutdown(0,false);// turn off power saving, enables display
  lc.setIntensity(0,10);// sets brightness (0~15 possible values)
  lc.clearDisplay(0);// clear screen
  
  fsec = 0;
  f1sec = 0;
  f2sec = 0;
  f3sec = 0;
  emicros = 0;
  emillis =0;
  esecs = 0;
  esecs1 = 0;
  emin1 = 0;
  emin2 = 0;
  estd1 = 0;
sw_micros.start();  
sw_millis.start();
sw_secs.start(); 
}
void loop()
{
  unsigned int emicros = sw_micros.elapsed();
  unsigned int emillis = sw_millis.elapsed(); // Zwischenspeicher für verstrichene Millisekunden  
 unsigned int esecs = sw_secs.elapsed();
 
 if (emicros >= 1000){
    sw_micros.reset();      // resetet Microsekunden
      sw_micros.start();      // Timer für Microsekunden starten
            f1sec++;          // Zähler für Millisekunden einer hochzählen
      lc.setDigit(0,7,(byte)f1sec,false); 
  }
    if (f1sec >=10){  // Wenn Millisekunden einer >= 10 dann...
   f1sec = 0;        // Millisekunden einer auf 0
   f2sec++;          // Millisekunden zehner +1
   lc.setDigit(0,7,(byte)f1sec,false);
   lc.setDigit(0,6,(byte)f2sec,false);
       }
   
 if (f2sec >=10){    // Wenn Millisekunden zehner >= 10 dann...
   f2sec = 0;        // Millisekunden zehner auf 0
   f3sec++;          // Millisekunden hunderter +1
  lc.setDigit(0,6,(byte)f2sec,false);
 lc.setDigit(0,5,(byte)f3sec,false); 
   }
 if (f3sec >=10){    // Wenn Millisekunden hunderter >= 10 dann...
   f3sec = 0;        // Millisekunden hunderter auf 0
    lc.setDigit(0,5,(byte)f3sec,false);
 } 

 
 
 
 // Sekunden, Minuten, Stunden
 if (esecs >= 10){       // Wenn Sekunden einer >= 10 dann..
   sw_secs.reset();      // resetet Sekunden
   sw_secs.start();      // startet Sekunden
 esecs = 0;              // Sekunden einer auf 0
 esecs1 ++;              // Sekunden zehner +1
 }
 if (esecs1 >=6){        // Wenn Sekunden zehner >= 6 dann...
   esecs1 = 0;           // Sekunden zehner auf 0
   emin1 ++;             // Minuten einer +1
  }
   if (emin1 >=10){      // Wenn Minuten einer >= 10 dann...
   emin1 = 0;            // Minuten einer auf 0
   emin2 ++;             // Minuten Zehner +1
   
 }
  if (emin2 >=6){        // Wenn Minuten zehner >= 6 dann...
   emin2 = 0;            // Minuten zehner auf 0
   estd1 ++;             // Stunden einer +1
   
  }
  // Anzeigen
   lc.setDigit(0,4,(byte)esecs,false);
   lc.setDigit(0,3,(byte)esecs1,false);
   lc.setDigit(0,2,(byte)emin1,false);
   lc.setDigit(0,1,(byte)emin2,false);
   lc.setDigit(0,0,(byte)estd1,false);
}

Du hast die Links auf die verwendeten Bibliotheken vergessen.

Ich kenne die Bibliothek Stopwatch nicht, aber wenn die halbwegs was taugt, sollte nur eine der drei Instanzen notwendig sein. Du hast schon mal was vom Modulo-Operator gehört (%)?

Womit machst Du die Stoppuhr? Ein UNO taugt nicht einmal für eine Genauigkeit von Hundertstel-Sekunden, denn der verwendete Resonator kann Abweichungen von bis zu 2% haben. Die Micros kannst Du Dir aber auf jeden Fall schenken, bei Tastern sowieso.

Einerstelle_Microsekunden = Microsekunden %10
Zehnerstelle_Microsekunden = (Microsekunden /10)%10
Hunderterstelle_Microsekunden = (Microsekunden /100)%10

Uwe

Danke für eure Antworten, das mit dem Micros, Millis, und Secs war nur ein Test, bei der LCD Stoppuhr arbeite ich nur mit Millis.

Habe mir das Modulo Verfahren eben versucht anzulesen, aber ich denke das löst mein Problem nicht.
Worauf ich hinaus wollte ist, wenn ich mir nur die zehnter Sekunde anzeigen lasse, springt sie relativ zeitgleich auf 0 , wenn die Sekunde einen mehr wird. Jedoch wenn ich die hundertstel und tausendstel auch noch mit in den Code einbringe, läuft die zehntel Millisekunde um einiges langsamer und springt nicht mehr zeitgleich mit der Sekunde um, woran kann das liegen, Codelänge?
Ich benutze einen Arduino Uno, die Stoppuhr soll nur bis an die ca. 10 Sekunden gehen, danach wird meistens gestoppt, da kann ich die Ungenauigkeit durch den Resonator vernachlässigen, bei einer richtigen Uhr oder Stoppuhr mit größeren Stoppzeiten würde ich die RTC Variante nehmen.

Hier noch die Links wo ich die Bibliotheken her habe.

http://playground.arduino.cc/Code/StopWatchClass
http://playground.arduino.cc/Main/LedControl

rlpp0r:
Mich interessiert trotzdem noch, warum das nicht so funktioniert, wie es gemacht habe, hier der code:

Dein Code sieht so aus, als wenn Du nicht eine, sondern DREI Stoppuhren gleichzeitig erzeugst und laufen läßt, mit verschiedener zeitlicher Auflösung. Diese drei Stoppuhren scheinst Du außerdem während laufender Zeitmessungen einzeln nach bestimmten zeitlichen Kriterien zu resetten und neu zu starten. Und zum Anzeigen auf dem LCD versuchst Du dann, die Zeiten von den drei verschiedenen Stoppuhren zu einer angezeigten Zeit zusammenzumixen.

So kann das überhaupt nicht funktionieren und es ist mir völlig schleierhaft, wieso Du überhaupt davon ausgehst, dass drei Stoppuhren mit unterschiedler zeitlicher Auflösung, die Du munter im Wechsel resettest und neu startest, Dir am Ende eine gültige Zeit erzeugen können.

Natürlich läßt Du immer nur so viele Stoppuhren laufen wie Du Zeiten stoppen möchtest. Also eine Stoppuhr für eine zu stoppende Zeit, zwei Stoppuhren für zwei zu stoppende Zeiten, etc.

Die Zeiten laufen von einem Startzeitpunkt los und zu jedem Zeitpunkt gibt es eine exakte Zeit in Form einer ganzen Zahl (Anzahl der vergangenen Millisekunden), die Du darstellen kannst. Dazu wird diese Zeit so aufbereitet, wie Du es zum Anzeigen benötigst. Für eine Anzeige, die nur einzelne Ziffern ausgeben kann, brauchst Du Modulo-Arithmetik mit 60 und 10 und ein wenig Substraktion.

Benötigst Du dazu Demo-Code?

Und möchtest Du die Uhr überhaupt nur ab Reset des Controllers laufen lassen, weil Du den Start offenbar in die setup-Funktion legst und in der loop keine Endbedingung erkennbar ist, wann die Zeit wieder angehalten werden soll? Oder soll die Uhr vielleicht bei irgendwelchen Input-Ereignissen gestartet und auch gestoppt werden können?

Mit 3 Stoppuhren, meinst du sicher Micro, Milli und Second.
Das war nur ein Test, da ich es mit Milli alleine nicht hinbekommen habe.
Dass die Zeit direkt losläuft, ist auch nur zum Testen, damit ich nicht immer starten muss, solange der Code für die Uhr noch nicht funktioniert. Das Starten und Stoppen per Flanke habe ich hinbekommen, ist hier nur weggelassen um es übersichtlicher im Forum zu halten, da mein Problem sich nur auf die zu langsam laufende Zeit bezieht.

Ich versuche das die nächsten Tage nochmal mit Modulo, dabei hatte ich auch das Problem, dass die Zeit zu langsam lief, sobald hundert- und Tausendstel Millisekunde dazu kam. Melde mich dann nochmal, ein kleines Beispiel von dir, kann dabei nicht schaden :slight_smile:

rlpp0r:
Das war nur ein Test, da ich es mit Milli alleine nicht hinbekommen habe.
Dass die Zeit direkt losläuft, ist auch nur zum Testen

OK, dann mache ich Dir für die Stoppuhr mal einen Democode.

Die Stoppuhr besteht dabei aus zwei Variablen und zwei Funktionen.

Die zwei Variablen:

unsigned long timerStopwatch=0;
boolean timerActive=false;

Die boolsche Variable "timerActive" steht dafür, ob die Uhr gerade steht oder läuft.
Und die Variable "timerStopwatch" hat eine doppelte Bedeutung: Während die Zeit läuft, ist hier die Startzeit vermerkt, also der Stand des millis() Timers beim Starten der Stoppuhr. Wenn die Zeit steht, ist hier die gestoppte Endzeit vermerkt.

Mit der Funktion "stopwatchStart()" wird die Stoppuhr gestartet und mit der Funktion "stopwatchStop()" wieder gestoppt.

Kompletter Demo-Code:

unsigned long timerStopwatch=0;
boolean timerActive=false;

void stopwatchStart()
{
  if (!timerActive)
  {
    timerStopwatch=millis();
    timerActive=true;
  }
}

void stopwatchStop()
{
  if (timerActive)
  {
    timerStopwatch=millis()-timerStopwatch;
    timerActive=false;
  }
}


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

void loop()
{
  char timebuffer[12];  
  long timeElapsed;
  if (timerActive) timeElapsed=millis()-timerStopwatch;
  else timeElapsed=timerStopwatch;
  int milliSeconds=timeElapsed%1000;  // Millisekunden
  timeElapsed=timeElapsed/1000;       // Alle ganzen Sekunden
  int seconds=timeElapsed%60;         // Sekunden
  timeElapsed=timeElapsed/60;         // Alle ganzen Minuten
  int minutes=timeElapsed%60;         // Minuten
  timeElapsed=timeElapsed/60;         // Alle ganzen Stunden
  int hours=timeElapsed%10;           // Einerstelle der Stunden
  // LCD-Formatierung h:mm:ss,mmm in der nachfolgenden Zeile
  // snprintf(timebuffer,sizeof(timebuffer),"%d:%02d:%02d,%03d",hours,minutes,seconds,milliSeconds);
  // Serial.println(timebuffer);
  
  // Alternative Formatierung ohne Trennzeichen
  snprintf(timebuffer,sizeof(timebuffer),"%d%02d%02d%03d",hours,minutes,seconds,milliSeconds);
  // Ausgabe nacheinander weg:
  for (int i=0;i<8;i++)
  {
    timebuffer[i]=timebuffer[i]-'0'; // ASCII-Code in Zahl wandeln
    Serial.print((byte)timebuffer[i]);
  }
  Serial.println();
  
}

In der loop-Funktion wird einfach nur die Zeit ständig ausgelesen, formatiert und ausgegeben. Eine LCD-gerechte Formatierung habe ich mit in den Code geschrieben und auskommentiert. Die aktive Codeversion formatiert einfach in einem Array die acht Stellen Deiner Uhr, die Du in der for-Schleife auf Deiner Segmentanzeige ausgeben könntest.

Die serielle Baudrate von nur 9600 bremst das Programm bzw, die Wiedergabe im seriellen Monitor etwas aus, für das fertige Programm also alles mit "Serial" aus dem Programm herauswerfen (oder auskommentieren) und die Ausgabe auf Serial nur bis dahin zum Debuggen drinbehalten.

Die Zahlen laufen einwandfrei hoch. Ich sehe überhaupt nicht, wieso man für solche Kinkerlitzchen wie "Stoppuhr" eine Library benötigen sollte.

Nachtrag: Ich habe in die for-Ausgabeschleife noch eine Umwandlung von "ASCII-Zeichen" in "Ziffern" eingebaut, so dass Du bei der Ziffernausgabe üblicherweise als "Ziffer an Position i" (byte)timebuffer[i] ausgeben kannst.

Wow, danke, du hast dir ja richtig Mühe gegeben !
Die Library braucht man wenn man nicht so programmieren kann wie du :wink:

Ich habs dank deiner Hilfe hinbekommen, allerdings das Anzeigen mittels for Schleife nicht, was mach ich falsch?

Der Code für Minuten- und Stunden Einer ist bei mir der Gleiche, jedoch Zeigt der eine Minuten und der andere Stunden an, warum ist das so?

Hier noch mein kompletter Code:

#include "LedControl.h"

// Pin 12 to Data In, 11 to Clk, 10 to LOAD
LedControl lc=LedControl(12,11,10,1);


unsigned long timerStopwatch=0;
boolean timerActive=false;

void stopwatchStart()
{
  if (!timerActive)
  {
    timerStopwatch=millis();
    timerActive=true;
  }
}

void stopwatchStop()
{
  if (timerActive)
  {
    timerStopwatch=millis()-timerStopwatch;
    timerActive=false;
  }
}


void setup()
{
  lc.shutdown(0,false);// turn off power saving, enables display
  lc.setIntensity(0,10);// sets brightness (0~15 possible values)
  lc.clearDisplay(0);// clear screen
  stopwatchStart();
}

void loop()
{
  char timebuffer[12];  
  long timeElapsed;
  if (timerActive) timeElapsed=millis()-timerStopwatch;
  else timeElapsed=timerStopwatch;
  
  // Millisekunden Anzeige
  int milliSeconds=timeElapsed%1000;  // Millisekunden
  int mseconds=timeElapsed%10;          // 1000stel Millisekunden
  int m1seconds=(timeElapsed/10)%10;    // 100stel Millisekunden
  int m2seconds=(timeElapsed/100)%10;   // 10tel Millisekunden
 // lc.setDigit(0,7,(byte)mseconds,false);
  //lc.setDigit(0,6,(byte)m1seconds,false);
 // lc.setDigit(0,5,(byte)m2seconds,false);
 
  // Sekunden Anzeige 
  timeElapsed=timeElapsed/1000;         // Alle ganzen Sekunden
  int Seiner=timeElapsed%10;            // Einerstelle der Sekunden
  int Szehner=(timeElapsed/10)%6;       // Zehnerstelle der Sekunden
  //lc.setDigit(0,4,(byte)Seiner,false);
  //lc.setDigit(0,3,(byte)Szehner,false);
  
  // Minuten Anzeige
  timeElapsed=timeElapsed/60;           // Alle ganzen Minuten
  int Meiner=timeElapsed%10;            // Einerstelle der Minuten
  int Mzehner=(timeElapsed/10)%6;       // Zehnerstelle der Minuten
 // lc.setDigit(0,2,(byte)Meiner,false);
 // lc.setDigit(0,1,(byte)Mzehner,false);
  
  // Stunden Anzeige
  timeElapsed=timeElapsed/60;           // Alle ganzen Stunden
  int Stdeiner=timeElapsed%10;          // Einerstelle der Stunden
 // lc.setDigit(0,0,(byte)Stdeiner,false);
 
 
 snprintf(timebuffer,sizeof(timebuffer),"%d%02d%02d%03d",Stdeiner,Meiner,Szehner,milliSeconds);
  for (int i=0;i<8;i++)
   {
   timebuffer[i]=timebuffer[i]-'0'; // ASCII-Code in Zahl wandeln
  lc.setDigit(0,i,i,true); 
    }
 }

Der Code für Minuten- und Stunden Einer ist bei mir der Gleiche, jedoch Zeigt der eine Minuten und der andere Stunden an, warum ist das so?

Weil Du dazwischen aus den Minuten Stunden machst:

  timeElapsed=timeElapsed/60;           // Alle ganzen Stunden

Insofern ist es normal, dass der Code derselbe ist, da ja auch das Gleiche gemacht wird.

rlpp0r:
Der Code für Minuten- und Stunden Einer ist bei mir der Gleiche, jedoch Zeigt der eine Minuten und der andere Stunden an, warum ist das so?

Offenbar möchtest Du die Formatierung des Arrays aus 8 statt aus 4 Variablen machen, also jede Stelle einzeln.

In dem Fall müßtest Du das dann aber auch machen und die richtige Formatierung für Deine 8 Variablen angeben:

snprintf(timebuffer,sizeof(timebuffer),"%d%d%d%d%d%d%d%d",Stdeiner,Mzehner, Meiner,Szehner,Seiner, m2seconds, m1seconds, mseconds);

In dem Fall, dass Du aber sowieso die 8 Stellen bereits einzeln vorliegen hast, kannst Du das mit der ASCII-Formatierung und rausechnen des ASCII-Codes auch sparen und gibst die Stellen direkt aus.

Wenn ich Deine lc-Library richtig verstanden habe ungefähr so:

  // Anzeigen
   lc.setDigit(0,7,Stdeiner,false);
   lc.setDigit(0,6,Mzehner,false);
   lc.setDigit(0,5,Meiner,false);
   lc.setDigit(0,4,Szehner,false);
   lc.setDigit(0,3,Seiner,false);
   lc.setDigit(0,2,m2seconds,false);
   lc.setDigit(0,1,m1seconds,false);
   lc.setDigit(0,0,mseconds,false);

Und dann kannst Du das char-Array "timebuffer[12]" und "snprintf" komplett aus dem Code rauswerfen.