Forum: Mikrocontroller und Digitale Elektronik Problem mit mehrerern Schieberegistern


von Florian B. (leuginio)


Lesenswert?

Hallo zusammen ich habe ein Problem bei meinem aktuellen Projekt.
Ich möchte 60 LED's im Sekundentakt nacheinander einschalten (JA wieder 
eine LED UHR :)  )
Als Hardware nutze ich 8 hintereinander geschaltete Schieberegister 
74HCT595 mit dahinter geschaltetem LED Treiber ULM2803.
Als uC nutze ich den ArduinoMega

Der Code:
1
//*** zuweisung der PIN's
2
3
int oe=2;
4
int mr=3;
5
int leatch=4;
6
int clock=5;
7
int data=6;
8
//*********************
9
10
int temp=0;
11
long sekuunden=0;
12
13
14
void setup(){
15
pinMode(oe,OUTPUT);
16
pinMode(mr,OUTPUT);
17
pinMode(latch,OUTPUT);
18
pinMode(clock,OUTPUT);
19
pinMode(data,OUTPUT);
20
}
21
22
void loop(){
23
24
digitalWrite(mr,HIGH);
25
digitalWrite(oe;LOW);
26
27
digitalWrite(latch,LOW);
28
   shiftOut(data, clock, LSBFIRST, sekunden >>56 );
29
   shiftOut(data, clock, LSBFIRST, sekunden >>48 );
30
   shiftOut(data, clock, LSBFIRST, sekunden >>40 );
31
   shiftOut(data, clock, LSBFIRST, sekunden >>32 );
32
   shiftOut(data, clock, LSBFIRST, sekunden >>24 );
33
   shiftOut(data, clock, LSBFIRST, sekunden >>16 );
34
   shiftOut(data, clock, LSBFIRST, sekunden >>8 );
35
   shiftOut(data, clock, LSBFIRST, sekunden  );
36
digitalWrite(latch,HIGH);
37
38
 temp=temp+1;
39
    
40
    
41
      sekunden= (sekunden<<1)+1;
42
      delay(1000); 
43
    
44
    if (temp > 61)  
45
    {
46
      temp=0;
47
      sekunden= 0;   
48
    }
49
}
Nun zu meinem Problem:
Das Programm läuft bis zur 32. Sekunde klappt alles, es wird im 
Sekundentakt eine LED hinzu geschaltet. Aber ab der 33. Sekunde macht 
die Uhr nicht mehr das was sie soll. Das liegt vermutlich am Typ der 
Variable Sekunden, da long max. 2^32 schafft. (hab die Variable im 
Monitor angezeigt ab der 33 Sekunde ist der Wert -1)
Meine Frage ist jetzt kennt jemand ne Alternative?? Oder habe ich was 
Übersehen?
Da alle Schieberegister mit dem gleichen LATCH und CLOCK arbeiten wird 
ein Aufteilen in 2*4 Register auch schwer.

Danke für eure Hilfe

: Bearbeitet durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Florian Beckmann schrieb:
>       delay(1000);
Vorneweg: das ist keine Uhr, sondern ein ganz ganz grobes Schätzeisen...

> da long max. 2^32 schafft.
2^32 in Sekunden reicht locker bis an dein Lebensende und darüber 
hinaus. Aber du willst den long als 32 Bit breites Schieberegister 
verwenden, da ist bedeutend früher Schluss.

> Meine Frage ist jetzt kennt jemand ne Alternative??
Ja, du kannst deine Sekunden einfach zählen und die Bitmaske zur Ausgabe 
an die LEDs einfach ausrechnen. Mit Divisionen und Modulo geht das 
ganz einfach...

von Bastler (Gast)


Lesenswert?

>>       delay(1000);
>Vorneweg: das ist keine Uhr, sondern ein ganz ganz grobes Schätzeisen...

Ohja, hab ich selbst mal unterschätzt wie groß die Abweichung sich 
aufsummiert!


>if (temp > 61)

Da muss man zurück in die erste Klasse und nochmal Uhrzeit lesen üben!


Nimm eine RTC (oft intern), sonst wird das nichts.
Ich habe versucht die Abweichung des Quarzes über Software fix zu 
kompensieren. Bei drei Aufbauten waren die Abweichungen jeweils 
verschieden.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Florian Beckmann schrieb:
> Meine Frage ist jetzt kennt jemand ne Alternative??
Einige...
Das wäre ein Ansatz:
1
   unsigned char sled[8]; // 8 x 8 Bit reichen aus
2
   unsigned char i
3
   unsigned char t[8]={1,3,7,15,31,63,127,255};
4
5
   s=sekunden; 
6
   i=7;
7
   do {
8
      if (s>8)       { sled[i]=xff; s-=8; }   // alle Bits setzen
9
      else if (s==0) { sled[i]=0; }           // kein Bit setzen
10
      else           { sled[i]=t[s-1]; s=0; } // die zu setzende Maske aus der Tabelle holen
11
   } while (i--);
12
13
   // die 8 Bytes ausgeben
14
   digitalWrite(latch,LOW);
15
   for (i=0; i<8; i++) shiftOut(data, clock, LSBFIRST, sled[i] );
16
   digitalWrite(latch,HIGH);

Oder das:
1
   unsigned char sled[8]; // 8 x 8 Bit reichen aus
2
   unsigned char i,v,m,sekunden;
3
   
4
   v=sekunden/8; // volle Bytes setzen
5
   for (i=0; i<8; i++) {
6
      if (i<v) sled[i]=xff; 
7
      else     sled[i]=0; 
8
   }
9
   
10
   m=0x01;       // "halbes" Byte berechnen
11
   for (i=0; i<sekunden%8; i++) {
12
      sled[v] |= m ;
13
      m <<= 1;
14
   }
15
16
   // die 8 Bytes ausgeben
17
   digitalWrite(latch,LOW);
18
   for (i=0; i<8; i++) shiftOut(data, clock, LSBFIRST, sled[i] );
19
   digitalWrite(latch,HIGH);

Oder gleich einen bitweisen Zähler:
1
   unsigned char sekundenled[8]; // 8 x 8 Bit reichen aus
2
   unsigned char msk,idx;
3
   
4
   while (1) {
5
      ....
6
      delay(1000); // das war nicht meine Idee...
7
      // wieder mindestens eine Sekunde vorbei
8
      if (idx==7 && msk==0x04) { // hier ist eine Minute vorbei: 7*8 + 3 =59
9
         idx=0;
10
         msk=0x01;
11
         for (i=0; i<8; sekundenled[i++]=0); // Feld auf 0 setzen
12
         ...
13
         // hier die Minute verwalten oder Minutenflag setzen...
14
         ...
15
      }
16
      else if (msk==0x80) { // nächstes Byte?
17
            idx++;
18
            msk=0x01;
19
            sekundenled[idx]|=msk;
20
      }
21
      else {                // innerhalb des Bytes weiterschieben
22
           msk<<=1;
23
           sekundenled[idx]|=msk;
24
      }
25
      ....
26
   } // while(1)
27
28
   // die 8 Bytes ausgeben
29
   digitalWrite(latch,LOW);
30
   for (i=0; i<8; i++) shiftOut(data, clock, LSBFIRST, sled[i] );
31
   digitalWrite(latch,HIGH);
Kleiner Hinweis: das sind keine fertigen Kopiervorlagen, sondern nur 
Denkanstöße. Und das mit dem delay(1000) ist immer noch Murks. Du hast 
doch millis(). Mit ein wenig Nachdenken lässt sich damit eine ganggenaue 
Uhr basteln...

Extrem einfach wäre es, wenn du nicht eine byteweise shiftOut() Funktion 
verwenden würdest, sondern die Pins selber ansteuerst:
1
   unsigned char sekunden,i;
2
3
   digitalWrite(latch,LOW);
4
   for (i=0; i<60; i++) {
5
      if (i<sekunden) digitalWrite(data,HIGH); // die nötigen LEDs anschalten
6
      else            digitalWrite(data,LOW);  // die anderen ausschalten
7
      digitalWrite(clock,HIGH);
8
      digitalWrite(clock,LOW);
9
   }
10
   digitalWrite(latch,HIGH);
Das wäre hier auch die eleganteste Methode...

: Bearbeitet durch Moderator
von Peter D. (peda)


Lesenswert?

Noch etwas einfacher:
1
unsigned char sekunden,i;
2
3
   digitalWrite(latch,LOW);
4
   digitalWrite(data,HIGH);
5
   for (i=0; i<60; i++) {
6
      if (i==sekunden)
7
        digitalWrite(data,LOW);
8
      digitalWrite(clock,HIGH);
9
      digitalWrite(clock,LOW);
10
   }
11
   digitalWrite(latch,HIGH);

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
> Noch etwas einfacher:
Hatte ich auch schon, finde aber die Default-Einstellung "LED an" mit 
einmaliger Umschaltung auf "LED aus" für Anfänger schlechter 
verständlich. Immerhin ist schon die "Nichtverwendung" der shiftOut() 
Funktion sicher eine mentale Herausforderung... ;-)

von Florian B. (leuginio)


Lesenswert?

So danke für guten Beiträge.
Habe das Problem jetzt lösen können.

Hab einfach die variable sekunden als long long definiert. So habe ich 
64 Bit zur Verfügung.

von Hein (Gast)


Lesenswert?

Florian Beckmann schrieb:
> Habe das Problem jetzt lösen können.

"Hingewurschtelt" würde ich das nennen.
Du hast ein wahnsinniges Glück, dass die Minute nur 60s hat.

von Christian M. (Gast)


Lesenswert?

Lothar Miller schrieb:
> Florian Beckmann schrieb:
>>       delay(1000);
> Vorneweg: das ist keine Uhr, sondern ein ganz ganz grobes Schätzeisen...

Bei Arduino habe ich alle Beispiele so gelöst gesehen, hab mich schon 
gewundert, aber bei denen kam es auch nicht so drauf an.
Gibt es denn eine einfache Möglichkeit, dies genau zu lösen? Interrupt?
Kenne C fast nicht und kann höchstens ein paar Anpassungen machen.

Gruss Chregu

von Wolfgang (Gast)


Lesenswert?

Christian Müller schrieb:
> Gibt es denn eine einfache Möglichkeit, dies genau zu lösen? Interrupt?

Ja, über einen Timer

Beitrag "Die genaue Sekunde / RTC"

Christian Müller schrieb:
> Kenne C fast nicht und kann höchstens ein paar Anpassungen machen.

Dann mach es direkt in Assembler

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.