2 Interruptgetriebene Zähler im Zeitformat auf einem LCD-Display

Hallo,

ich bin seit kurzem im besitz eines Arduino Mega 2560 Boards und habe schon ein paar Sketches ausprobiert und modifiziert.
Als Anzeige nutze ich ein 20x4 DotMatrix VFD-Display das sich bis auf Sonderbefehle bequem mit der LiquidCrystal-Lib ansteuern lässt.

Würde jetzt gerne zwei Zählerwerte im Uhrzeitformat auf dem Display ausgeben deren Takt von jeweils einem der externen Interrups kommt.
Leider steh ich da schon komplett auf dem Schlauch... eine einzelne Uhrzeit, angetrieben durch einen externen Interrupt, bekäme ich noch hin aber 2?

Der erste Interrupt bekommt seinen Sekundentakt von einem RTC-Modul und soll als "Real: hh:mm:ss" in der 3. Zeile angezeigt werden.
Der zweite Interrupt bekommt seinen Takt von der Bandlaufrolle* eines Tonbandgerätes, muß intern noch durch 2 geteilt werden und soll dann in der 4. Zeile als "Tape: hh:mm:ss" angezeigt werden.

Wer von euch wäre so nett mir auf die Sprünge zu helfen?

*Die Bandlaufrolle wird direkt vom Band angetrieben und liefert daher immer einen zur Bandgeschwindigkeit (Abspielen, Umspulen) proportionalen Takt.

Grüße
Thilo

Bei deinem Arduino hast du ja mehrere interrupt-pins zur verfügung!
Welche das jetzt genau sind kann ich dir nicht sagen aber abfragen kannst du sie in etwa so:
attachInterrupt(0, rtcINT, RISING);
attachInterrupt(1, tapeINT, RISING);

Dann machst du zwei kurz gehaltene routinen
Wo du nur hoch zählst mehr nicht. rtcVal++ und tapeVal++
im loop passiert der rest.
TapeVal/2
Und die umrechnung in minuten und stunden
Naja und das display immer aktualisieren wenn sich ein wert geändert hat .
Also entweder tapeVal oder rtcVal dann aktualisiert sich dein display auch nur wenn sich was ändert.
Max 2 mal in der Sekunde.
Machst du das nicht wird sich dein display immer aktulasieren wenn die loop durch ist und das ist zu schnell.

Was dann noch fehlt sind so Sachen wie reset für die rtcVal und die TapeVal.
Ist aber auch nicht das Problem

Hab jetzt mal einen Counter realisiert doch auf dem Display bekomm ich als Zählerwert nur 000 angezeigt, sehe nur nicht wo der Fehler liegt...

Hier mein Sketch:

/*
  The circuit:
 * LCD RS pin to digital pin 7
 * LCD Enable pin to digital pin 6
 * LCD D4 pin to digital pin 5
 * LCD D5 pin to digital pin 4
 * LCD D6 pin to digital pin 3
 * LCD D7 pin to digital pin 2
 * LCD R/W pin to ground
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)
 */

// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);

int counter0    = 0;     // Incremented by switch0
int counter1    = 0;     // Incremented by switch1

void setup() {
  pinMode(20, INPUT);             // Pin20 is input
  digitalWrite(20, HIGH);         // Switch on pull up
  pinMode(21, INPUT);             // Pin21 is input
  digitalWrite(21, HIGH);         // Switch on pull up
  //attachInterrupt(2, tapeINT, FALLING);
  attachInterrupt(3, rtcINT, FALLING);
  
  // set up the LCD's number of columns and rows: 
  lcd.begin(20, 4);
  // Print a message to the LCD
  lcd.print("Tape vs. Real Time");
}

// Zähler 0
void rtcINT(){
  if (rtcINT == LOW) {
    counter0++;
  }
}

void loop(){
  char Cnt[4];
  lcd.setCursor(0, 2);
  lcd.print("Real: ");
  sprintf(Cnt, "%03d", counter0);
  lcd.print(Cnt);
}

Wofür ist das rtcINT== LOW?
Soweit ich weiß bringt/geht das nicht.
Das ist ja eine routiene und keine variable.
In der rtcINT brauchst du nur deinen counter ++ machen

Glaube ich.

Habe jetzt nach längerem googlen auch den Zählerstand im Zeitformat drinnen, allerdings hab ich da noch einen Denkfehler weil die Sekunden immer weiter hochzählen, hab aber grad keinen Plan wie ich das ändern kann.

Hier mein Sketch:

/*
  The circuit:
 * LCD RS pin to digital pin 7
 * LCD Enable pin to digital pin 6
 * LCD D4 pin to digital pin 5
 * LCD D5 pin to digital pin 4
 * LCD D6 pin to digital pin 3
 * LCD D7 pin to digital pin 2
 * LCD R/W pin to ground
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)
 */

// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);

int counter0    = 0;     // Incremented by switch0
int counter1    = 0;     // Incremented by switch1

//Initial time set
  int sec0=00;          
  int min0=00;
  int hour0=00;
  int sec1=00;          
  int min1=00;
  int hour1=00;

// Setup
void setup() {
  pinMode(13, OUTPUT);            // Pin13 as output
  pinMode(20, INPUT);             // Pin20 is input
  //digitalWrite(20, HIGH);         // Switch on pull up
  pinMode(21, INPUT);             // Pin21 is input
  digitalWrite(21, HIGH);         // Switch on pull up
  attachInterrupt(2, tapeINT, FALLING);
  attachInterrupt(3, rtcINT, FALLING);
  
  // set up the LCD's number of columns and rows: 
  lcd.begin(20, 4);
  // Print a message to the LCD
  lcd.print("Tape vs. Real Time");
}

// Zähler 0
void rtcINT(){
    counter0++;
}

// Zähler 1
void tapeINT(){
    counter1++;
}

void rtcZeit(){
  char Cnt[30];
  sec0=counter0;
    if(sec0==60){
      min0++;
      sec0=0;
    }  
    if(min0==60){
      hour0++;
      min0=0;
    }
    if(hour0==24){
     hour0=0;
    }
  lcd.setCursor(0, 2);
  lcd.print("Real: ");
  sprintf(Cnt,"%02d:%02d:%02d",hour0,min0,sec0);
  lcd.print(Cnt);
}

void tapeZeit(){
  char Cnt[30];
  sec1=counter1;
    if(sec1==60){
      min1++;
      sec1=0;
    }  
    if(min1==60){
      hour1++;
      min1=0;
    }
    if(hour1==24){
     hour1=0;
    }
  lcd.setCursor(0, 3);
  lcd.print("Tape: ");
  sprintf(Cnt,"%02d:%02d:%02d",hour1,min1,sec1);
  lcd.print(Cnt);
}

void loop(){
  rtcZeit();
  tapeZeit();
  digitalWrite(13, HIGH);
  delay(950);
  digitalWrite(13, LOW);
  delay(50);
}

Mit dem digitalWrite im loop hab ich einen 1Hz-Taktgeber realisiert da das RTC-Modul im Auslieferungszustand keinen Referenztakt ausgibt, den muß man erst mit einer Codesequenz aktivieren und dafür hab ich grad keine Lust.

Gruß
Thilo

Ich weiß nicht ob das das problem ist aber es könnte sein:
Durch dein delay störst du den gesammten ablauf.
Und du setzt zwar sec0 auf 0 wenn es 60 war aber überschreibst dies im nächsten durchlauf wieder mit counter.
Ich würde vielleicht in deinen rechnung einfach den counter immer durch 60 bzw durch 3600 teilen um vom counter direkt die minuten bzw die stunden zu bekommen.

Hallo,

luemmel:
Habe jetzt nach längerem googlen auch den Zählerstand im Zeitformat drinnen, allerdings hab ich da noch einen Denkfehler weil die Sekunden immer weiter hochzählen, hab aber grad keinen Plan wie ich das ändern kann.

gibt denn dein Impulsgeber an der Capstanwelle z.b. bei Ruecklauf einen negativen Wert aus?
Sonst können sie ja nur größer werden. Wenn das Band zurueckläuft muß dein Zähler irgendwie decrementiert werden.
Welches Tonbandgerät gibt eigentlich genau 1 Impuls pro Sekunde aus?

Nachfolgend mal ein Codebeispiel (Auszug) aus meinem Fernbedienungsprogramm fuer meine Otari MX55, vielleicht kannst du damit was anfangen.

volatile long zaehler = 0;
volatile long runden = 0;
boolean richtung = 0;
long daten[10];
// Konstante fuer Bandlänge pro Impuls (in cm)
const double meter = 0.15875;
void setup()
{
// Interrupt mit Pin 2 verbinden
  attachInterrupt(0, zaehlen, CHANGE);
}
void loop()
{
  // if an incoming client connects, there will be bytes available to read:
  EthernetClient client = server.available();
  /* Speed abfragen 
  // speeda = 0 & speedb = 0 entspricht 3,75 ips
  // speeda = 0 & speedb = 1 entspricht 7,5 ips
  // speeda = 1 & speedb = 1 entspricht 15 ips*/
  // speed A abfragen
  daten[0] = digitalRead(16);
  // speed B abfragen  
  daten[1] = digitalRead(17);
  // Laufrichtung abfragen
  richtung = digitalRead(18);
// Sekunden der Laufzeit uebermitteln
  daten[8] = runden;  
// Verbrauchte Bandlänge uebermitteln
  long lang = 0;
  lang = zaehler * meter;
  daten[9] = lang;  
}
void zaehlen()
{
      // Impulszaehler inkrementieren fuer Wiedergabe, Aufnahme und schnellen Vorlauf
      if (richtung == 0){
        zaehler++;
      }
      // Impulszaehler dekrementieren fuer schnellen Ruecklauf
      else {
        zaehler--;
      }
      // zaehler auf- oder abrunden und in der Variablen "runden" speichern
      // Fuer 3,75 ips, 30 Impulse pro Sekunde
      if (daten[0] == 0 & daten[1] == 0){
        runden = zaehler/60;
        if((zaehler % 60) >= 30){
          runden++;
        }
      } 
      // Fuer 7,5 ips, 60 Impulse pro Sekunde
      if (daten[0] == 0 & daten[1] == 1){
        runden = zaehler/120;
        if((zaehler % 120) >= 60){
          runden++;
        }
      }        
      // Fuer 15 ips, 120 Impulse pro Sekunde
      if (daten[0] == 1 & daten[1] == 1){
        runden = zaehler/240;
        if((zaehler % 240) >= 120){
          runden++;
        }
      }
 }

Die Daten werden, zusammen mit vielen anderen, im Array Daten [] gespeichert und an ein Java-Programm zur Anzeige und Steuerung via Ethernet uebergeben.

Gruß
Christoph

@MueThoS:

Ich möchte erstmal klein anfangen, bin ja blutiger Einsteiger in die Welt der Microprozessoren und deren Programmierung.
Möchte erstmal nur schauen wie genau die Bandlaufzeit mit einer realen Zeit synchron bleibt.

@Christoph:

Super das hier noch jemand mit dem Tonbandfieber unterwegs ist :slight_smile:
Ich habe die Akai GX-77 die über eine Impulsrolle verfügt die direkt vom Tonband angetrieben wird. Pro Umdrehung gibt diese zwei Inkrementalpulse aus, da ich aber eine externe Steuerungseinheit plane mit der ich die Akai dann komplett steuere, gibt der Arduino ja dann die Laufrichtung selbst an.

Die schwierigkeit die ich habe ist den Zählerwert in die Zeiteinheiten Stunden, Minuten und Sekunden umzuwandeln.

Gruß
Thilo

Hallo Thilo

ich würde mich der Meinung von MueThoS anschließen.
Die beiden durch die Interrupts gesteuerten Zähler musst Du auch mit bearbeiten. Also nach 60 Sek auf null setzen, oder über den Weg mit 3600 Sek bearbeiten und dann zurücksetzen.
“delay“ stört eigentlich nur bedingt, weil die Zählerberechnung immer nur im nach Ablauf der delay- Zeiten erfolgt.Unter Umständen musst Du „int counter0 = 0; // Incremented by switch0“ durch „volatile int counter0 = 0; // Incremented by switch0“ ersetzen um die bearbeitung des counters vom normaln Programm aus zu erledigen.

Gruß Uwe5802

Hallo Thilo,

wie bereits weiter oben geschrieben sollten (laut Arduino-Referenz) alle Variablen, die in einer Interrupt-Funktion benutzt werden, als "volatile" deklariert werden. Ganz verstanden habe ich das zwar auch noch nicht, aber bei mir funktioniert das nur so. Vielleicht kann ja ein anderer näheres dazu sagen, wuerde mich interessieren.
Die Umrechnung in Std., Min. wird bei mir (noch) von dem Java-Programm erledigt. Der Arduino sendet nur den Wert in Sekunden.
Ich vermute aber ein Problem in deinem Code hier:

if(sec0==60)
if(min0==60)
if(hour0==24){

Die Interrupts und dein "loop" laufen unabhängig voneinander.
Durch den Vergleich (genau 60) wird aber z.b. der Minutenzähler nur erhöht, wenn die Sekunden genau bei 60 sind. Was passiert aber, wenn wir schon bei 61 sind? Dann wird die Bedingung niemals war.
Versuche es vielleicht mal so:

if(sec1 >= 60) { 
  min1++; 
  sec1 -= 60; 
  if (min1 >= 60){
    hour1++; 
    min1 -= 60;           
    }
  }

Gruß
Christoph

mx55:

if(sec1 >= 60) { 

min1++;
  sec1 -= 60;
  if (min1 >= 60){
    hour1++;
    min1 -= 60;           
    }
  }

Das sehe ich genau so!

Wenn ich mich nicht vertue (ich habs nicht getestet und weiß nicht genu ob oder wie gerundet wird) aber ich glaube man könnte das so rechnen:

h=counter/3600
m=(counter/60)-(h60)
s=counter-(h
3600)-(m*60)

Ich hoffe das stimmt? An die Profis: Geht das auch besser?
Dann könntest du dir deine IFs sparen.

und wenn du jetzt noch dein delay für den Takt (den du ja im mom selber erstellst mit millies auslößt ist diese unschöne Sache auch noch raus.
Schau dir hierzu mal blinkwithoutdelay aus dem Playground an. Das ist genau das was du brauchst.

Habe mein Sketch entsprechend euren Hinweisen und Anmerkungen geändert und es läuft so wie gewünscht.
Zwischendurch hatte ich noch mit einem DS1307-Testsketch den Rechteckausgang mit 1Hz aktiviert wodurch der Blinkerteil in meinem Sketch wegfällt.

/*
  The circuit:
 * LCD RS pin to digital pin 7
 * LCD Enable pin to digital pin 6
 * LCD D4 pin to digital pin 5
 * LCD D5 pin to digital pin 4
 * LCD D6 pin to digital pin 3
 * LCD D7 pin to digital pin 2
 * LCD R/W pin to ground
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)
 */

// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);

volatile int counter0    = 0;     // Incremented by switch0
volatile int counter1    = 0;     // Incremented by switch1

const int RTCLED = 13;
const int RTC = 20;
const int TAPE = 21;
int RTCstate = 0;
int sec0 = 00;          
int min0 = 00;
int hour0 = 00;
int sec1 = 00;          
int min1 = 00;
int hour1 = 00;

// Setup
void setup() {
  pinMode(RTCLED, OUTPUT);            // Pin13 as output
  pinMode(RTC, INPUT);             // Pin20 is input
  digitalWrite(RTC, HIGH);         // Switch on pull up
  pinMode(TAPE, INPUT);             // Pin21 is input
  digitalWrite(TAPE, HIGH);         // Switch on pull up
  attachInterrupt(2, tapeINT, FALLING);
  attachInterrupt(3, rtcINT, FALLING);
  
  // set up the LCD's number of columns and rows: 
  lcd.begin(20, 4);
  // Print a message to the LCD
  lcd.print("Tape vs. Real Time");
}

// Zähler 0
void rtcINT(){
    counter0++;
}

// Zähler 1
void tapeINT(){
    counter1++;
}

void rtcZeit(){
  char Cnt[30];
  hour0=counter0/3600;
  min0=(counter0/60)-(hour0*60);
  sec0=counter0-(hour0*3600)-(min0*60);
  lcd.setCursor(0, 2);
  lcd.print("Real: ");
  sprintf(Cnt,"%02d:%02d:%02d",hour0,min0,sec0);
  lcd.print(Cnt);
}

void tapeZeit(){
  char Cnt[30];
  hour1=counter1/3600;
  min1=(counter1/60)-(hour1*60);
  sec1=counter1-(hour1*3600)-(min1*60);
  lcd.setCursor(0, 3);
  lcd.print("Tape: ");
  sprintf(Cnt,"%02d:%02d:%02d",hour1,min1,sec1);
  lcd.print(Cnt);
}

void loop(){
  rtcZeit();
  tapeZeit();
  RTCstate = digitalRead(RTC);
  if (RTCstate == HIGH) {     
    // turn LED on:    
    digitalWrite(RTCLED, HIGH);  
  } else {
    // turn LED off:
    digitalWrite(RTCLED, LOW); 
  }
}

Gruß
Thilo

Nochwas was ich vielleicht machen würde:
mach in deine Interruptroutienen noch jeweils eine Variable rein:
rtcChanged=1
tapeChanged=1

und in deiner loop

If (rtcChanged==1){
rtcZeit();
rtcChanged=0;
}
if (tapeChanged==1{
tapeZeit();
tapeChanged=0;
}

Dann wird dein Display nur aktualisiert wenn sich auch was getan hat.