Forum: Mikrocontroller und Digitale Elektronik LED Matrix und 4 Gewinnt


von Stefan (Gast)


Lesenswert?

Hallo,

ich wollte in meinen nächsten Projekt mit gern mal an einer LED Matrix 
versuchen. Dazu bin ich auf eine 8x8 Matrix mit Bicolor LED´s gestoßen.

http://www.ebay.de/itm/2-3inch-8-x-8-Bicolor-LED-Dot-Matrix-Display-Common-Anode-Punktmatrix-Rot-Grun-/270815262120?pt=Bauteile&hash=item3f0dd8f5a8

oder

http://www.ebay.de/itm/32-Pin-8x8-5mm-RGB-LED-Matrix-Modul-6x6cm-Rot-Grun-Blau-f-Arduino-Rainbowduino-/140991933932?pt=Licht_Effekte&hash=item20d3c62dec

Jetzt finde ich zu den abgebildeten Bauteil kein Datenblatt und weiß 
auch sonst nicht wie ich die ansteuere. Den Artikel LED-Matrix - 
http://www.mikrocontroller.net/articles/LED-Matrix hab ich mir 
angeschaut. Mit den Multilexbetrieb komm ich allerdings noch nicht 
zurecht.

Ich wollte mit der Matrix kein laufendes Bild erzeugen, sondern 
ledigliche einzelne LED´s ansteuern.

Ziel des Projektes sollte ein kleines 4-Gewinnt Spiel sein. Wie steuer 
ich denn eine einzelne LED in der Matrix an? z.B. C1/ R7 ? Eingang C1 
auf Low und R7 auf HIGH am Controller? Dann müssen C2 - C5 aber auch auf 
HIGH?

Danke für die Hilfe.

von Rocks (Gast)


Angehängte Dateien:

Lesenswert?

Das angehängte Bild dürfte deine Fragen beantworten

von Stefan (Gast)


Lesenswert?

Rocks schrieb:
> Das angehängte Bild dürfte deine Fragen beantworten

Danke dir erstmal. Leider noch nicht alle. Woher weiß ich dann bei der 
LED MAtrix welche Pins welche sind?

War der Gedanke zum ansteuern einzelner LED´s denn richtig?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Aber ein 4-Gewinnt Spielfeld ist doch nicht 8x8.

von Karl H. (kbuchegg)


Lesenswert?

Stefan schrieb:

> Ziel des Projektes sollte ein kleines 4-Gewinnt Spiel sein. Wie steuer
> ich denn eine einzelne LED in der Matrix an?

Der Trick ist konzeptionell nicht, dass du eine einzelne LED ansteuerst, 
sondern eine komplette Spalte oder Zeile.

Eingen wir uns bei deinem Bild mal auf Spalte.

D.h. mittels C1 Low haben jetzt erst mal grundsätzlich alle LED in 
dieser Spalte eine Chance zu leuchten. Alle andern C sind logischerweise 
auf High.

Welche LED in dieser Spalte leuchten soll, das bestimmst du mit den 
R-Leitungen. Die die leuchten sollen, die kriegen auf ihren R-Leitungen 
ein High, alle anderen ein Low.


Und Mulitplexen bedeutet jetzt nichts anderes, als das reihum alle 
Spalten durchiteriert werden, und das jeweilige Muster zu dieser Spalte 
aus dem Speicher auf die R-Leitungen gegeben wird.

D.h. du hast im Programm ein Array, welches deine LED symbolisiert. Der 
Multiplex-Mechanismus stützt sich auf dieses Array und ackert es laufend 
durch um Spalte für Spalte dauernd auf die realen LEDs zu schalten.

Will dein Programm die LED Spalte 1/Zeile 7 einschalten, dann setzt es 
in diesem Array das bewusste Bit und der Multiplex-Mechanismus ist dafür 
zuständig, dass dann eben beim nächsten Durchgang, wenn diese Spalte 
wieder drann ist, de entsprechende LED eingeschaltet wird (neben all den 
anderen LED dieser Spalte die auch leuchten sollen)

von Stefan (Gast)


Lesenswert?

Johann L. schrieb:
> Aber ein 4-Gewinnt Spielfeld ist doch nicht 8x8.

:) Wahrscheinlich nicht. Aber spielt das eine Rolle? 4 in einer Reihe 
gewinnen :) Ob nun bei 7x6 oder 8x8 oder 200 x 400 ist doch egal.

von Karl H. (kbuchegg)


Lesenswert?

Stefan schrieb:
> Rocks schrieb:
>> Das angehängte Bild dürfte deine Fragen beantworten
>
> Danke dir erstmal. Leider noch nicht alle. Woher weiß ich dann bei der
> LED MAtrix welche Pins welche sind?

Steht im Datenblatt und wenn man keines hat dann nimmt man sich einen 
220 Ohm Widerstand, +5V und probiert es einfach aus.

von Stefan (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Der Trick ist konzeptionell nicht, dass du eine einzelne LED ansteuerst,
> sondern eine komplette Spalte oder Zeile.

Danke für deine ausführliche Antwort. Verwirrend finde ich, wenn du 
sagst du steuerst die ganze Spalte an. Weil du letztendlich zwar der 
Spalte die Möglichkeit gibst zu leuchten (indem du C1 auf Low legst), 
dann aber dennoch nur eine LED zum leuchten bringst mit den R-Leitungen. 
Klar man könnte dann auch mehr leuchten lassen.

Ok also prinzipiell habe ich es verstanden. Ich bestell mir einfach mal 
2 Matritzen und experimentiere damit. Wie ich das ganze in C-Code 
verpacke weiß ich zwar noch nicht, aber vielleicht unterstützt du mich 
dann nochmal Karl-Heinz.

Zum Controller:

Da ich anscheinend 24 Pins also 3 Ports benötige, sollte ich mir auch 
einen Controller (ATMEL) mit 4 Ports z.B. den ATMEGA16 nehmen.

von Karl H. (kbuchegg)


Lesenswert?

Stefan schrieb:
> Karl Heinz Buchegger schrieb:
>> Der Trick ist konzeptionell nicht, dass du eine einzelne LED ansteuerst,
>> sondern eine komplette Spalte oder Zeile.
>
> Danke für deine ausführliche Antwort. Verwirrend finde ich, wenn du
> sagst du steuerst die ganze Spalte an. Weil du letztendlich zwar der
> Spalte die Möglichkeit gibst zu leuchten (indem du C1 auf Low legst),
> dann aber dennoch nur eine LED zum leuchten bringst mit den R-Leitungen.
> Klar man könnte dann auch mehr leuchten lassen.

Weil ich denke, dass du den Kardinalfehler Nummer 1 machst, wenn es um 
eine LED-Matrix geht.

Du zerbrichst dir den Kopf darüber, wie du ein deinem Programm, 
meinetwegen in der Hauptschleife, welche Ausgabepins wie einstellen 
musst, damit genau diese eine LED leuchten kann.

Diese Fragestellung stellt sich aber in Wirklichkeit gar nicht. Denn für 
das Programm, welches dann anhand irgendwelcher Spielregeln eine LED 
einschalten muss, sind die Output-Pins völlig uninteressant. Damit hat 
dieser Programmteil überhaupt nie zu tun. Dieser Programmteil 
hinterlässt seine Ergüsse in Form von 0 oder 1 Bits in besagtem Array.
Und erst der Multiplex-Meachnismus schaufelt dann laufend diese Belegun 
auf die tatsächlichen LEDs raus. Für diesen Programmteil ist aber die 
LED C1/R7 wiedrrum in keiner Weise speziell. Das einzige was diesen 
Programmteil interessiert ist: Jetzt, zu diesem Zeitpunkt ist die Spalte 
1 drann. Muss die LED R7 leuchten ja oder nein? Und ein paar 
Millisekunden später ist dann die Spalte 2 drann und die Fragestellung 
lautet: Muss jetzt, zu diesem Zeitpunkt die LED R7 in dieser Spalte 
leuchten ja oder nein? Und ein paar Millisekunden später ist es die 
Spalte 3, usw. usw.

Die Dinge sind vollständig entkoppelt:
Der 'Spielteil' deines Programms braucht sich nicht darum kümmern, wie 
genau die Portbits stehen müssen und der Multiplexteil kümmert sich 
nicht darum, warum eine LED aufgrund der Spielregeln leuchten soll. 
Bindeglied zwischen beiden ist dieses Array.

von Karl H. (kbuchegg)


Lesenswert?

Stefan schrieb:
> Wie ich das ganze in C-Code
> verpacke weiß ich zwar noch nicht, aber vielleicht unterstützt du mich
> dann nochmal Karl-Heinz.

Dann nutze die Zeit bis dahin und beschäftige dich mit Timer.
Denn der ist der Schlüssel zum ganzen Multiplexing.

FAQ: Timer

>
> Zum Controller:
>
> Da ich anscheinend 24 Pins also 3 Ports benötige, sollte ich mir auch
> einen Controller (ATMEL) mit 4 Ports z.B. den ATMEGA16 nehmen.

Kann zumindest nicht schaden.

von Ingo (Gast)


Lesenswert?

Ich bin nicht sicher ob 4-Gewinnt in ner Matrix nich ne Nummer zu Gross 
is, wenn du schon Schwierigkeiten hast bei der Matrix selbst?!

von Stefan (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich hab mir jetzt einfach selber mal eine 3x3 Matrix aufgebaut. Leider 
ist mir erst jetzt aufgefallen, dass es dann wohl doch nicht so 
funktioniert wie ich mir das vorstellte.

Wenn ich z.B. die unterste Zeile in der mittleren Spalte Rot leuchten 
lassen will und die darüber liegende LED Grün, dann geht das nicht mit 
dem nach Rocks hier geposteten Schaltplan.

Dann leuchtet entweder immer eine Spalte Rot oder Grün. Oder mach ich da 
noch etwas falsch?

von Stefan (Gast)


Lesenswert?

Ok hat sich erledigt :) Dafür brauch ich dann das Multiplexing.

von Stefan (Gast)


Lesenswert?

Hallo,

zu den Bezeichnungen meiner 3x3 Matrix:
1
       S
2
    1  2  3
3
  1 #  #  #
4
5
Z 2 #  #  #
6
7
  3 #  #  #
Die Belegung zu meinen ATMEGA 16:
1
Anode Rot
2
S1 - PA0
3
S2 - PA1
4
S3 - PA2
5
Anode Grün
6
S1 - PA3
7
S2 - PA4
8
S3 - PA5
9
Kathode Rot/Grün
10
Z1 - PB0
11
Z2 - PB1
12
Z3 - PB2
Ich habe angefangen zu programmieren in C. Bin noch nicht so weit 
gekommen.
1
#include <avr/io.h>    
2
3
#define Port_Spalt    PORTA
4
#define S1            PA0
5
#define S2            PA1
6
#define S3            PA2
7
8
#define Port_Zeile    PORTB
9
#define Z1            PB0
10
#define Z2            PB1
11
#define Z3            PB2
12
13
void led_mat(char matrix[3][3]){
14
15
  //Spalten komplett ausschalten
16
  Port_Spalt &= ~(1<<S1);
17
  Port_Spalt &= ~(1<<S2);
18
  Port_Spalt &= ~(1<<S3);
19
20
  for (int i=0; i<1000;i++){
21
    
22
    //Durchlauf durch Zeilen
23
    for (int z=0; z<3;z++){
24
25
      //Durchlauf durch Spalten
26
      for (int s=0; s<3;s++){
27
28
        if(matrix[z][s] == 1){
29
          Port_Spalt |= (1<<s);
30
          
31
        }
32
      }
33
      Port_Zeile &= ~(1<<z);
34
    }
35
  }
36
}
37
      
38
int main (void) {  
39
40
  //Würfel 1
41
  char leds[3][3] =  {\
42
                     {1,0,1},\
43
                     {0,1,0},\
44
                     {1,0,1}};
45
}

Ich wollte erstmal fragen ob die Richtung stimmt. Laut dem Artikel 
LED-Matrix schalte ich zunächst alle Spalten aus. Dann leg ich das 
Muster in den Spalten fest und schalte zuletzt dann die Zeile ein.
Den Durchlauf durch die Spalten und Zeilen wollte ich mit 
verschachtelten for Schleifen machen.
Wo ich mir allerdings absolut nicht sicher bin, ist bei folgender 
Syntax:
1
Port_Zeile &= ~(1<<z);
Wenn z=0 ist, dann steht da:
1
Port_Zeile &= ~(1<<0);
Nimmt er dann vorm Port_Zeile das 0.Bit - also PB0?

von Karl H. (kbuchegg)


Lesenswert?

Stefan schrieb:
> Hallo,
>
> zu den Bezeichnungen meiner 3x3 Matrix:
>
1
>        S
2
>     1  2  3
3
>   1 #  #  #
4
> 
5
> Z 2 #  #  #
6
> 
7
>   3 #  #  #
8
>
> Die Belegung zu meinen ATMEGA 16:
>
1
> Anode Rot
2
> S1 - PA0
3
> S2 - PA1
4
> S3 - PA2
5
> Anode Grün
6
> S1 - PA3
7
> S2 - PA4
8
> S3 - PA5
9
> Kathode Rot/Grün
10
> Z1 - PB0
11
> Z2 - PB1
12
> Z3 - PB2
13
>

Wenn du deine Matrix als eine 6*3 Matrix auffasst würdest du dir 
leichter tun. Ob du jetzt 6 Spalten hast, die alle rot sind, oder ob von 
den 6 Spalten 3 Stück rot und 3 Stück grün sind, ist ja dem Programm 
wieder egal.


>   //Würfel 1
>   char leds[3][3] =  {\
>                      {1,0,1},\
>                      {0,1,0},\
>                      {1,0,1}};

Wenn du nicht für jede LED einen eigenen char benutzt (unsigned char 
wäre sowieso besser), sondern ein Byte pro Spalte reservierst, dann 
repräsentiert jedes Bit in einem Byte eine LED in dieser Spalte.
Die Ausgabe auf die eigentlichen LEDs würde davon ungemein profitieren, 
denn das hier
              Port_Spalt |= (1<<s);
willst du eigentlich nicht haben.

Die Ausgabe der Spalten wäre dann einfach nur das Byte auf den Port 
legen, noch den richtigen Spaltentreiber aktivieren und fertig.

> Ich wollte erstmal fragen ob die Richtung stimmt.

Als erster Test kann man das so machen. Im Endeffekt geht das aber 
timergesteuert über Interrupts.

> Laut dem Artikel
> LED-Matrix schalte ich zunächst alle Spalten aus. Dann leg ich das
> Muster in den Spalten fest und schalte zuletzt dann die Zeile ein.

Ja. Sonst gibts Ghosting.

> Den Durchlauf durch die Spalten und Zeilen wollte ich mit
> verschachtelten for Schleifen machen.

Das Stichwort lautet: Timer!
Da führt kein Weg drann vorbei.
Und nein, das sind keine ineinandergeschachtelten for-Schleifen.

> Wo ich mir allerdings absolut nicht sicher bin, ist bei folgender
> Syntax:
>
1
> Port_Zeile &= ~(1<<z);
2
>
> Wenn z=0 ist, dann steht da:
>
1
> Port_Zeile &= ~(1<<0);
2
>
> Nimmt er dann vorm Port_Zeile das 0.Bit - also PB0?

PB0 ist auch nichts anderes als eine profane Schreibweise für ... 0
Da steckt nichts geheimnisvolles dahinter. Irgendwo im io.h gibt es ein
1
#define PB0 0
2
#define PB1 1
3
...

Aber wie gesagt. Die ganze Operation willst du so mit der variablen 
Verschieberei nicht machen. Und du brauchst sie auch nicht, wenn du 
deine Datenorganisation ordentlich mit Bits und nicht mit Bytes machst.

von Stefan (Gast)


Lesenswert?

Vielen Dank für deine schnelle Antwort Karl Heinz.

Karl Heinz Buchegger schrieb:
> Wenn du nicht für jede LED einen eigenen char benutzt (unsigned char
> wäre sowieso besser), sondern ein Byte pro Spalte reservierst, dann
> repräsentiert jedes Bit in einem Byte eine LED in dieser Spalte.
> Die Ausgabe auf die eigentlichen LEDs würde davon ungemein profitieren,
> denn das hier
>               Port_Spalt |= (1<<s);
> willst du eigentlich nicht haben.

Ok das mit der eindimensionalen Matrix und dann Bytes als Inhalt 
verstehe ich. Ich muss ja bei der 3x3 Matrix auch nicht alle Bits des 
Ports benutzen.

Karl Heinz Buchegger schrieb:
> Das Stichwort lautet: Timer!
> Da führt kein Weg drann vorbei.
> Und nein, das sind keine ineinandergeschachtelten for-Schleifen.

Das versteh ich noch nicht ganz. Kannst du mir das möglichst einfach und 
bildlich erklären :) ?

Warum ist das mit For-Schleifen nicht so gut und mit Timern besser? Mit 
denen hab ich noch nicht viel zu tun gehabt. Ich hab mal einen µC mit 
einen Timer geweckt. Das war aber auch nur mal so am Rande eines 
Projektes.
Wie stell ich mir den Ablauf mit Timern vor? Was sollen die bezogen auf 
die Matrix machen?

von Stefan (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wenn du deine Matrix als eine 6*3 Matrix auffasst würdest du dir
> leichter tun. Ob du jetzt 6 Spalten hast, die alle rot sind, oder ob von
> den 6 Spalten 3 Stück rot und 3 Stück grün sind, ist ja dem Programm
> wieder egal.

Achso ich wollte bevor ich mit den Duo Leds arbeite und mich an 4 
Gewinnt versuche, doch erstmal nur eine einfarbige Matrix als Würfel 
gestalten. ISt zum Anfang sicher nicht schlecht.

von Karl H. (kbuchegg)


Lesenswert?

Stefan schrieb:

> Karl Heinz Buchegger schrieb:
>> Das Stichwort lautet: Timer!
>> Da führt kein Weg drann vorbei.
>> Und nein, das sind keine ineinandergeschachtelten for-Schleifen.
>
> Das versteh ich noch nicht ganz. Kannst du mir das möglichst einfach und
> bildlich erklären :) ?
>
> Warum ist das mit For-Schleifen nicht so gut und mit Timern besser?

Weil das Multiplexing der Matrix auch dann laufen muss, wenn der µC 
gerade irgendwas anderes macht. Zb die Pixel berechnen, die leuchten 
müssen um den Zeiger einer Uhr darzustellen.
Und das Multiplexing muss REGELMÄSSIG und GLEICHMÄSSIG laufen. Sonst 
flackert deine Matrix wie Sau.

> Mit
> denen hab ich noch nicht viel zu tun gehabt.

Dann wirds Zeit
FAQ: Timer

> Wie stell ich mir den Ablauf mit Timern vor? Was sollen die bezogen auf
> die Matrix machen?

Eine Spalte anzeigen.
Und wenn die ISR das nächste mal aufgerufen wird - die nächste Spalte 
anzeigen.
Und beim nächsten Aufruf der ISR, dann eben wieder die nächste.
Reihum. Eine Spalte nach der anderen.
Die leuchtet dann so lange, bis die ISR das nächste mal aufgerufen wird. 
Dann wird sie wieder abgeschaltet und die nächste Spalte kommt drann.
Und das ganze so schnell, dass jede Spalte mindestens 50, besser 100 mal 
in der Sekunde drann kommt. D.h. bei 6 Spalten sollte die 
Spaltenumschaltung ca 300 mal (eher mehr) pro Sekunde erfolgen.

von Stefan (Gast)


Lesenswert?

Ich bin auf eine Funktion von dir für Zufallszahlen gestoßen. Wenn ich 
die nutze erhalte ich zwar Zufallszahlen, allerdings immer die gleichen 
Zahlenfolgen. Das gleiche Problem hab ich auch bei rand()%6+1. Was mache 
ich falsch?
1
int myRand()
2
{
3
  int x;
4
5
  while( (x = rand()) >= RAND_MAX - (RAND_MAX % UpperBound) )
6
    ; 
7
  return x % UpperBound;
8
}

von Karl H. (kbuchegg)


Lesenswert?

Stefan schrieb:
> Ich bin auf eine Funktion von dir für Zufallszahlen gestoßen. Wenn ich
> die nutze erhalte ich zwar Zufallszahlen, allerdings immer die gleichen
> Zahlenfolgen.

Logisch.
Sind ja auch keine echten Zufallszahlen sondern dahinter steckt eine 
Formel. Geht man von den gleichen Startwerten aus, dann entsteht auch 
immer die gleiche Folge von Zahlen. Diese Folge hat statistische 
Eigenschaften von Zufallszahlen.

srand() einmalig am Programmstart aufrufen. Aber auch dazu brauchst du 
eine erste Zahl. Die kann man zb im EEPROM speichern und bei jedem 
Programmstart um 1 erhöhen, für srand() hernehmen und wieder ins EEPROM 
speichern.
Oder man kann den Benutzer bitten, eine Taste zu drücken und die 
Drückdauer als Startwert für srand() benutzen.

von Stefan (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Die kann man zb im EEPROM speichern und bei jedem
> Programmstart um 1 erhöhen, für srand() hernehmen und wieder ins EEPROM
> speichern.

Sehr gute Idee, wie ich finde. Bei der Umsetzung hab ich wiedermal 
Probleme.
Hier ein Ausschnit vom Programmcode. Die Funktionen zum Schreiben und 
Lesen vom eeprom benötigen ein uint8_t als Datentyp. Das stellt aber 
einen Konflikt zur Funktion srand da, wie mir die Fehlermeldung 
mitteilt.

Wenn ich als erstes einen Wert ins EEPROM schreibe und den dann erhöhe, 
habe ich beim nächsten Programmstart das gleiche Problem. Denn dann wird 
ja wieder zuerst die gleiche Zahl reingeschrieben.
1
int main (void) {  
2
3
  uint8_t myByte;
4
5
  char leds[3] =  {0b00000010,\
6
           0b00000010,\
7
           0b00000010};
8
  int zahl = 0;
9
10
  myByte = eeprom_read_byte (0x00);
11
  eeprom_write_byte(0x00, myByte+1);
12
  void srand(uint8_t  myByte);

von Karl H. (kbuchegg)


Lesenswert?

Stefan schrieb:

>   myByte = eeprom_read_byte (0x00);
>   eeprom_write_byte(0x00, myByte+1);
>   void srand(uint8_t  myByte);


Das hier ist kein Funktionsaufruf.
Das ist die Deklaration einer Funktion die ein uint8_t Argument 
übernimmt und nichts zurückliefert - ein Funktionsprototyp.
Ein Funktionsaufruf sieht anders aus

    srand( myByte );

Sag mal, lest ihr eigentlich keine C-Bücher und seht euch dort mal an, 
wie die Syntax von verschiedenen Dingen aussieht, übt mit dem Buch ein 
bißchen (ein gutes Buch hat nach jedem Kapitel Übungsbeispiele), ehe ihr 
reale Programme schreiben wollt? Das ist schon erschreckend, was du 
alles NICHT weißt, aber wissen solltest, wenn du dir zutraust eine 
LED-Matrix machen zu können.
(war eine rhetorische Frage - ich kenne die Antwort: Du hast kein Buch. 
Business as usual)


Im ganzen Zusammenhang
1
   myByte = eeprom_read_byte(0x00);
2
   myByte++;
3
   eeprom_write_byte(0x00, myByte);
4
   srand( myByte );

Und ob ein Byte da so gut ist. Na ja. es gibt ja auch Word-Funktionen 
aus dem EEPROM Lager.
1
uint16_t seed;
2
3
4
   seed = eeprom_read_word(0x00);
5
   seed++;
6
   eeprom_write_word(0x00, seed);
7
   srand( seed );

Aber wahrscheinlich sind 256 verschiedene Zufallszahlenfolgen für den 
Hausgebrauch auch so gut genug.

von Stefan (Gast)


Lesenswert?

Hallo,

danke erstmal für deine Unterstützung! Natürlich hab ich kein C-Buch. 
Mit dem Funktionsprototypen ist mir allerdings ein Begriff. Damit kann 
ich vorab dem Compiler mitteilen, dass weiter unten im Programm eine 
Funktion dieses Typs vorhanden ist. Hoffe doch das ich mich da jetzt 
noch richtig erinnere.
Ich habe nur im Studium mal C programmiert und das ist auch schon eine 
Weile her. Ich arbeite dann mit den Unterlagen, aber wenn man es nicht 
regelmäßig nutzt, dann vergisst man gerne auch mal sowas.

Karl Heinz Buchegger schrieb:
> Dann wirds Zeit
> FAQ: Timer
>
>> Wie stell ich mir den Ablauf mit Timern vor? Was sollen die bezogen auf
>> die Matrix machen?
>
> Eine Spalte anzeigen.
> Und wenn die ISR das nächste mal aufgerufen wird - die nächste Spalte
> anzeigen.
> Und beim nächsten Aufruf der ISR, dann eben wieder die nächste.
> Reihum. Eine Spalte nach der anderen.
> Die leuchtet dann so lange, bis die ISR das nächste mal aufgerufen wird.
> Dann wird sie wieder abgeschaltet und die nächste Spalte kommt drann.
> Und das ganze so schnell, dass jede Spalte mindestens 50, besser 100 mal
> in der Sekunde drann kommt. D.h. bei 6 Spalten sollte die
> Spaltenumschaltung ca 300 mal (eher mehr) pro Sekunde erfolgen.

Ich habe mir den Artikel über TIMER durchgelesen. Die Idee die Matrix so 
anzusteuern gefällt mir. So wie ich es verstehe, läuft parallel zum 
Programm immer der TIMER mit und ruft je nach Konfiguration (Takt mit 
Vorteiler oder ggf. SW Teiler) zu bestimmten festgelegten Zeiten die ISR 
auf. Jetzt kann ich mich erinnern, dass man die ISR vom Code so klein 
wie möglich halten soll. Warum kann ich allerdings nicht mehr sagen.
Das Problem was ich sehe, wenn die ISR jetzt aller paar ms aufgerufen 
wird und ich pro Aufruf eine Spalte anzeigen lassen will, wie übergeb 
ich der ISR mein Array für die Matrix? Oder soll das Array als globale 
Variable angelegt werden?

von Stefan (Gast)


Lesenswert?

Habe jetzt den Artikel zum Interrupt durchstöbert und bin da auf 
folgenden Vorschlag gestoßen.

Global wird eine Variable definiert.
1
volatile uint8_t flag;
Diese wird dann in der ISR gesetzt und im Hauptprogramm wird diese 
zurücksetzt und ausgewertet mit einer if-Abfrage.

Das klingt für mich nach einen Weg, da hiermit auch die ISR sehr kurz 
gehalten wird.

von Stefan (Gast)


Lesenswert?

So es läuft. Du wirst sicher noch etwas im Code finden, was optimiert 
werden könnte. Ich bin für jede Kritik dankbar. Vielleicht läuft es auch 
nur durch Zufall :)

Was ich nicht so glücklich finde ist die Darstellung der einzelnen 
Würfelzahlen, da hier einfach zuviele If-Bedingungen den Speicher 
belasten. Wobei das hier bei dem kleinen Programm keine Rolle spielt. 
Aber wenn ich jetzt Laufschriften programmieren würde, dann würde das ja 
ins Unendliche verlaufen. Das geht sicher anders.
1
#define F_CPU 1000000UL
2
3
#include <avr/io.h>   
4
#include <stdlib.h> 
5
#include <stdio.h>
6
#include <util/delay.h>
7
#include <avr/interrupt.h>
8
#include <avr/eeprom.h>
9
10
#define Port_Spalt    PORTA
11
#define S1        PA0
12
#define S2        PA1
13
#define S3        PA2
14
15
#define Port_Zeile    PORTB
16
#define Z1        PB0
17
#define Z2        PB1
18
#define Z3        PB2
19
20
#define PIN_Taster    PINB
21
#define TasterWurf    PB3
22
23
//globale Variable zur Auwertung bei Interrupts
24
volatile uint8_t isrFlag;
25
//globale Variable für Zeilendurchlauf (veränderbar in der ISR (volatile))
26
volatile uint8_t isrZeile;
27
28
29
ISR( TIMER0_OVF_vect )       // Overflow Interrupt Vector
30
{
31
  //Interrupt erfolgt
32
  isrFlag = 1;
33
  //Inkrementieren der Zeilen
34
  isrZeile++;
35
  //Nach der 3.Zeile wieder auf 0 zurücksetzen
36
  if (isrZeile > 2)
37
    isrZeile =0;
38
}
39
40
//Gleichverteilte Zufallszahl ermitteln #Karl Heinz
41
int myRand()
42
{
43
  int x;
44
45
  while( (x = rand()) >= RAND_MAX - (RAND_MAX % 6+1) )
46
    ; 
47
  return x % 6+1;
48
}
49
50
//Ausgabe einer Zeile
51
void led_mat(char matrix[3]){
52
53
      //Spalte festlegen
54
      Port_Spalt = matrix[isrZeile];
55
      
56
      //Zeile anschalten
57
      Port_Zeile &= ~(1<<isrZeile);
58
      //1ms Warten
59
      _delay_ms(1);
60
      //Zeile ausschaltemn
61
      Port_Zeile |= (1<<isrZeile);
62
63
}
64
      
65
66
//Matrix komplett dunkel schalten
67
void matrixAus(void){
68
69
  Port_Spalt &= ~(1<<S1);
70
  Port_Spalt &= ~(1<<S2);
71
  Port_Spalt &= ~(1<<S3);
72
  Port_Zeile |= (1<<Z1);
73
  Port_Zeile |= (1<<Z2);
74
  Port_Zeile |= (1<<Z3);
75
}
76
77
78
int main (void) {  
79
  //Datenrichtungen PORTA/PORTB festlegen
80
  DDRA  = 0xff;     
81
  DDRB  = 0b00000111;  
82
83
  //Timer konfigurieren
84
  TIMSK |= (1<<TOIE0);  // den Overflow Interrupt des Timers freigeben
85
  TCCR0 = (1<<CS01);    // Vorteiler 8, jetzt zählt der Timer bereits
86
   
87
  //Interrupts aktivieren
88
  sei();                
89
90
  //Startwert zur Berechnung der Zufallszahlen
91
  uint16_t seed;
92
  
93
  //Array für die Matrix
94
  char leds[3] =  {0b00000010,\
95
           0b00000010,\
96
           0b00000010};
97
  //Gewürfelte Zahl
98
  int zahl = 0;
99
100
  //Startwert wird aus EEPROM vom ATMEGA Adresse 0x00 gelesen
101
  seed = eeprom_read_word(0x00);
102
  //Startwert erhöhen
103
  seed++;
104
  //neuen Startwert im EEPROM ablegen
105
  eeprom_write_word(0x00,seed);
106
  //Startwert für Zufallszahlen festlegen
107
  srand(seed);
108
109
  while(1){
110
111
    matrixAus();
112
113
    //Wenn Taster betätigt wird, dann wird neue Zufallszahl berechnet
114
    if (PIN_Taster & (1<<TasterWurf)){
115
      zahl=myRand();
116
    }
117
  
118
    //Darstellung der Würfelzahlen
119
    if (zahl == 1){
120
    leds[0] =  0b00000000;
121
    leds[1] =  0b00000010;
122
    leds[2] =  0b00000000;
123
124
    }
125
    if (zahl == 2){
126
    leds[0] =  0b00000001;
127
    leds[1] =  0b00000000;
128
    leds[2] =  0b00000100;
129
    }
130
    if (zahl == 3){
131
    leds[0] =  0b00000001;
132
    leds[1] =  0b00000010;
133
    leds[2] =  0b00000100;
134
    }
135
    if (zahl == 4){
136
    leds[0] =  0b00000101;
137
    leds[1] =  0b00000000;
138
    leds[2] =  0b00000101;
139
    }
140
    if (zahl == 5){
141
    leds[0] =  0b00000101;
142
    leds[1] =  0b00000010;
143
    leds[2] =  0b00000101;
144
    }
145
    if (zahl == 6){
146
    leds[0] =  0b00000101;
147
    leds[1] =  0b00000101;
148
    leds[2] =  0b00000101;
149
    }
150
151
    if (isrFlag){
152
      //Interrupt Flag zurücksetzen
153
      isrFlag = 0;
154
      //Anzeige einer Zeile
155
      led_mat(leds);
156
    }
157
  }
158
}

von Karl H. (kbuchegg)


Lesenswert?

Stefan schrieb:
> So es läuft. Du wirst sicher noch etwas im Code finden, was optimiert
> werden könnte. Ich bin für jede Kritik dankbar.

Falscher Ansatz

Die ganze Sache mit dem Flag ist zwar in den meisten Fällen korrekt, 
hier aber nicht.
1
ISR( ... )
2
{
3
   aktuelle Spalte abschalten
4
   Spaltenzähler 1 weiterstellen
5
   neue Spalte anzeigen
6
}

so geht das.
Und ein _delay_ms hat da drinnen schon gar nichts verloren.
Der ganze Inhalt der ISR sind eine halbe Handvoll Programmzeilen. Mehr 
ist das nicht.

Im Grunde deine Funktion led_mat, nur ein bischen umgestellt.
1
ISR( ... )
2
{
3
  Port_Zeile |= (1<<isrZeile);
4
5
  isrZeile++;
6
  if (isrZeile > 2)
7
    isrZeile =0;
8
9
  Port_Spalt = leds[isrZeile];
10
  Port_Zeile &= ~(1<<isrZeile);
11
}

Die Leds leuchten vom Ende der ISR, bis die ISR das nächste mal 
aufgerufen wird. Daher werden sie am ANFANG der ISR abgeschaltet, dann 
die nächste Konfiguration eingestellt und zum Leuchten gebracht. Während 
dann die ISR wieder abgibt und das Hauptprogramm seine nächsten 
Berechnungen macht, leuchtet diese eine Zeile. Bis eben dann, ein paar 
Milliskunden später, die ISR erneut aufgerufen wird, die leuchtende 
Zeile abschaltet und die nächste Zeile leuchten lässt.


> //Matrix komplett dunkel schalten
> void matrixAus(void){
>
>   Port_Spalt &= ~(1<<S1);
>   Port_Spalt &= ~(1<<S2);
>   Port_Spalt &= ~(1<<S3);
>   Port_Zeile |= (1<<Z1);
>   Port_Zeile |= (1<<Z2);
>   Port_Zeile |= (1<<Z3);
> }

Wenn du die LEDs alle aushaben willst, dann schreibst du das in das Leds 
Array hinein. Ab sofort bedeutet jede LED, die leuchtet oder nicht 
leuchten soll, eine entsprechende Manipulation im Leds Array. Einzig und 
alleine die ISR macht sich an den Ports/Portpins zu schaffen, die die 
Matrix steuern. Und sonst niemand.

von Karl H. (kbuchegg)


Lesenswert?

Stefan schrieb:

> Das Problem was ich sehe, wenn die ISR jetzt aller paar ms aufgerufen
> wird und ich pro Aufruf eine Spalte anzeigen lassen will, wie übergeb
> ich der ISR mein Array für die Matrix? Oder soll das Array als globale
> Variable angelegt werden?

Exakt

von Karl H. (kbuchegg)


Lesenswert?

Stefan schrieb:

> Jetzt kann ich mich erinnern, dass man die ISR vom Code so klein
> wie möglich halten soll.

Das ist etwas zu plakativ vereinfacht dargstellt. Du machst in einer ISR 
was du machen musst. Nicht zuviel, aber auch nicht zu wenig. Wenn du 
eine Garantie brauchst, dass tatsächlich Code regelmässig ausgeführt 
wird, und zwar vollständig ausgeführt wird, dann muss der Code dafür 
auch vollständig in die ISR.

zb wird das Weiterschalten einer Uhr komplett in der ISR gemacht. Du 
kannst dich nicht drauf verlassen, dass die Hauptschleife in main() 
genügend Zeit hat, dass sie alle Sekunden dann die Sekunden überprüft 
und gegebenenfalls die Minuten/Stunden/Tage weiterschaltet. Wenn das 
Hauptprogramm in einer Schleife hängt, in der sie auf Benutzereingaben 
wartet, dann muss die Uhr trotzdem weiterlaufen! Und wenn sich der 
Benutzer eine halbe Stunde dafür Zeit nimmt, dann tut er das eben. Aber 
deswegen darf die Uhr nicht 1/2 Stunde verlieren, weil in dieser Zeit 
das entsprechende Flag nicht abgefragt wurde.

So auch hier: Egal was passiert - die Matrix MUSS weitergeschaltet 
werden. Egal was die Hauptschleife gerade kompliziertes berechnet, zb. 
den Gegenzug zur einer Benutzereingabe bei 4-gewinnt - die Matrix MUSS 
weitergeschaltet werden. Du kannst nicht drauf warten dass die 
Berechnung irgendwann fertig wird um dann das Flag abzufragen. Kommt die 
ISR dann wird die Matrix weitergeschaltet - ohne Diskussion und ohne 
wesentliche Verzögerung.

von Karl H. (kbuchegg)


Lesenswert?

> (1<<isrZeile)

Das ist eine Operation, die du auf einem AVR nicht haben willst. Denn 
die muss der Compiler in Form einer Schleife auflösen, in der er jeweils 
um 1 Bit schiebt.
Da bist du mit einem Array von vorgefertigten Bitmustern besser bedient
1
uint8_t bitMasks[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
2
3
...
4
5
ISR( ... )
6
{
7
  Port_Zeile |= bitMasks[ isrZeile ];
8
9
...
10
11
12
  Port_Zeile &= ~(bitMasks[ isrZeile ]);
13
}

In deinem Fall kannst du dir auch das gezielte 'Setzen' nur der 
Zeilenbits auch sparen, denn wenn du einfach (in deinem Fall) alle 3 
setzt, dann sparst du dir dadurch etwas.
1
ISR( ... )
2
{
3
  Port_Zeile |= 0x07;
4
...

bzw. besser in Form der tatsächlich benutzten Portbits ausgedrückt
1
ISR( ... )
2
{
3
  Port_Zeile |= (1<<PB0) | (1<<PB1) | (1<<PB2);
4
...

wenn du dann mal alle 8 Bits setzen musst, dann kann man überlegen ob 
0xFF dann nicht einfacher wäre.

Zudem hat dieses Vorgehensweise dann auch noch den Vorteil, dass du das 
bitMask Array nur noch für die Verundung brauchst, also hier
1
  Port_Zeile &= ~(bitMasks[ isrZeile ]);
2
}

wodurch du die Möglichkiet kriegst, die boolsche Negierung auch gleich 
noch mit ins Array reinzuziehen.
1
uint8_t bitMasks[] = { ~0x01, ~0x02, ~0x04, ~0x08,
2
                       ~0x10, ~0x20, ~0x40, ~0x80 };
3
4
...
5
6
ISR( ... )
7
{
8
  Port_Zeile |= (1<<PB0) | (1<<PB1) | (1<<PB2);
9
10
  isrZeile++;
11
  if (isrZeile > 2)
12
    isrZeile =0;
13
14
  Port_Spalt = leds[isrZeile];
15
  Port_Zeile &= bitMasks[ isrZeile ];
16
}

von Stefan (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

so der Würfel hat mit deiner Hilfe ganz gut geklappt Karl-Heinz. Danke 
nochmal.

Meine 8x8 RGB LED-Matrix ist mittlerweile auch aus China eingetroffen. 
Ich wollte vorher allerdings nochmal eine einfarbige 5x8 Matrix von 
Kingbright TA 12-11 probieren. Wenn ich die zum laufen bekomme, dann 
mach ich mich an die 8x8 RGB.

Bei der 3x3 Matrix hat der µC den Strom für die Led´s noch treiben 
können. Wenn ich jetzt allerdings schon 5x8 Led´s verwende, ist es 
sicherlich besser das ganze mit FET´s oder irgendwelchen 
Treiberbausteinen zu realisieren?
Wobei im Datenblatt zum ATMEGA8 steht etwas von 40mA pro I/O.

>DC Current per I\O Pin .......................... 40.0mA


Ich hab versucht einfach den Code für den Würfel zu nehmen und 
entsprechend der neuen MAtrix anzupassen. Dargestellt wird bei folgenden 
Code der äußere Rahmen, der zunächst in leds steht. Die Änderung in der 
while(1) Schleife bekommt der µC allerdings nicht mehr mit. Ich bin 
gerade etwas ratlos und seh den Fehler nicht.

1
#define F_CPU 1000000UL
2
3
#include <avr/io.h>   
4
#include <stdlib.h> 
5
#include <stdio.h>
6
#include <util/delay.h>
7
#include <avr/interrupt.h>
8
9
#define Port_Zeile    PORTD
10
#define Z1        PD0
11
#define Z2        PD1
12
#define Z3        PD2
13
#define Z4        PD3
14
#define Z5        PD4
15
#define Z6        PD5
16
#define Z7        PD6
17
18
#define Port_Spalt    PORTC
19
#define S1        PC0
20
#define S2        PC1
21
#define S3        PC2
22
#define S4        PC3
23
#define S5        PC4
24
25
26
//globale Variable zur Auwertung bei Interrupts
27
volatile uint8_t isrFlag;
28
//globale Variable für Zeilendurchlauf (veränderbar in der ISR (volatile))
29
volatile uint8_t isrZeile;
30
31
32
//Array für die Matrix
33
char leds[7] =  {0b00011111,\
34
                 0b00010001,\
35
                 0b00010001,\
36
                 0b00010001,\
37
                 0b00010001,\
38
                 0b00010001,\
39
                 0b00011111};
40
41
42
ISR( TIMER0_OVF_vect )       // Overflow Interrupt Vector
43
{
44
45
  //Zeile ausschalten
46
    Port_Zeile |= (1<<isrZeile);
47
48
  //Inkrementieren der Zeilen
49
    isrZeile++;
50
51
  //Nach der 7.Zeile wieder auf 0 zurücksetzen
52
    if (isrZeile > 6)
53
      isrZeile =0;
54
55
  //Spalte festlegen
56
    Port_Spalt = leds[isrZeile];
57
58
  //Zeile einschalten
59
    Port_Zeile &= ~(1<<isrZeile);
60
}
61
62
63
64
65
int main (void) {  
66
  //Datenrichtungen PORTD/PORTC festlegen
67
  DDRD  = 0xff;       
68
  DDRC  = 0xff;    
69
  
70
71
  //Timer konfigurieren
72
  TIMSK |= (1<<TOIE0);  // den Overflow Interrupt des Timers freigeben
73
  TCCR0 = (1<<CS01);    // Vorteiler 8, jetzt zählt der Timer bereits
74
   
75
  //Interrupts aktivieren
76
  sei();                
77
78
79
80
  while(1){
81
82
    leds[0] =  0b00000000;
83
    leds[1] =  0b00000000;
84
    leds[2] =  0b00000000;
85
    leds[3] =  0b00000000;
86
    leds[4] =  0b00000000;
87
    leds[5] =  0b00000000;
88
    leds[6] =  0b00000000;
89
90
  }
91
 
92
}

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.