Forum: Mikrocontroller und Digitale Elektronik Snake Projekt: Matrixansteuerung


von Jan S. (spongebob)


Lesenswert?

Moin!
Ich habe mal wieder ein Problem.
Ich bin dabei ein Snake-Spiel zu programmieren.
Dazu habe ich eine LED-Tafel mit 8x16 LEDs aufgebaut.
Die 8 Zeilen werden nacheinander gezeitmultiplext. Dies Multiplexen habe 
ich schon bei verschiedenen Frequenzen versucht.
Vom ding her funktioniert es auch. Wenn ich nun jedoch auf die einzelnen 
Spalten verschiedene LEDs leuchten lassen möchte passiert es mit immer, 
das sich das auf die anderen Zeilen mit auswirkt.
Im folgenden Beispiel versuche ich in der 4. Zeile 8 LEDs leuchten zu 
lassen.
Dabei passiert es jedoch, das die LEDs der anderen Spalten auch dunkel 
leuchten.
1
volatile int8_t zeile = 0;
2
3
/* Interrupt Service Routines (ISRs) */
4
5
ISR(TIMER0_COMP_vect)  //Interruptfrequency: 64kHz -> 8kHz pro Zeile 
6
{
7
  MUXPORT = zeile;  
8
9
    zeile++;
10
  
11
  if(zeile == 8)
12
    zeile = 0;
13
}    
14
15
int main(void)
16
{
17
  DDRA = 0xff;
18
  DDRB = 0x07;
19
  DDRC = 0xff;
20
  
21
  MUXPORT = 0x00;
22
  
23
  init_tmr();
24
  init_intr();
25
  
26
  sei();
27
  
28
    while(1)
29
    {
30
      if (MUXPORT = 0x03)
31
        PORTC = 0xff;
32
      else
33
        PORTC = 0x00;
34
  }
35
}

Wenn ich die if-Abfrage in meinem Timer-Interrupt mache funktioniert 
alles.
Aber es muss doch eine Möglichkeit geben Funktionen zu schreiben, die im 
Hauptprogramm verwendet werden können. Sonst ist das ja alles ein 
bisschen unflexibel.

Hoffe es hat jemand einen Tipp, wie ich vorgehen kann. Vielleicht ist ja 
auch schon meine Grundidee fürn A...

Bin für jede Anregung dankbar.

Grüße Jan

von Karl H. (kbuchegg)


Lesenswert?

Jan S. schrieb:


> Wenn ich die if-Abfrage in meinem Timer-Interrupt mache funktioniert
> alles.

und dort gehört sie auch hin.

> Aber es muss doch eine Möglichkeit geben Funktionen zu schreiben, die im
> Hauptprogramm verwendet werden können. Sonst ist das ja alles ein
> bisschen unflexibel.

Was dir noch fehlt ist ein Array als Speicherfläche, welches gegenüber 
deinem Hauptprogramm das Display darstellt.
Innerhalb der ISR wird die Hardware aus diesem Array 'gefüttert'.
Und dein Hauptprogramm schaltet eine LED ein/aus, indem es in besagtem 
Array die entsprechenden Bits setzt oder löscht.

von Karl H. (kbuchegg)


Lesenswert?

> Dazu habe ich eine LED-Tafel mit 8x16 LEDs aufgebaut.

Wie ist das Hardwaremässig ausgeführt?
Du brauchst an deinem MUXPORT eine Möglichkeit, wie du KEINE Zeile 
leuchten lassen kannst. Das ist dann die erste Aktion innerhalb der ISR: 
die Zeilentreiber abschalten, so dass nichts leuchtet, nichts leuchten 
kann. Egal was du am Port C machst.

Im Prinzip so
1
volatile uint8_t Pixel[8];    // 8 Zeilen mit jeweils 8 Pixel
2
uint8_t  zeile;
3
4
ISR( ... )
5
{
6
  MUXPORT .... alles abschalten
7
8
  zeile++;
9
  if( zeile == 8 )
10
    zeile = 0;
11
12
  PORTC = Pixel[zeile];
13
  MUXPORT = zeile;
14
}
15
16
void setPixel( uint8_t x, uint8_t y )
17
{
18
  Pixel[y] |= ( 1 << x );
19
}
20
21
void clrPixel( uint8_t x, uint8_t y )
22
{
23
  Pixel[y] &= ~( 1 << x );
24
}
25
26
void togglePixel( uint8_t x, uint8_t y )
27
{
28
  Pixel[y] ^= ( 1 << x );
29
}
30
31
32
int main()
33
{
34
  ...
35
36
  for( i = 0; i < 8; ++i )
37
  {
38
    setPixel( 0, i );
39
    _delay_ms( 100 );
40
  }
41
42
  while( 1 )
43
  {
44
    for( i = 0; i < 8; ++i )
45
    {
46
      togglePixel( i, i );
47
      _delay_ms( 100 );
48
    }
49
  }
50
}

von Jan S. (spongebob)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Was dir noch fehlt ist ein Array als Speicherfläche, welches gegenüber
> deinem Hauptprogramm das Display darstellt.

Das ich auf sowas nicht von selbst komme...
Jetzt funktioniert alles.
Ich habe jetzt ein Array Angelegt, welches werte für die einzelnen 
Zeilen aufnehmen kann.


Mein Code sieht jetzt wie folgt aus:
1
volatile int8_t zeile = 0;
2
int16_t matrix[8];
3
4
/* Interrupt Service Routines (ISRs) */
5
6
ISR(TIMER0_COMP_vect)  //Interruptfrequency: 4MHz 
7
{  
8
  MUXPORT = zeile;
9
10
  PORTC = matrix[zeile];
11
  PORTA = (matrix[zeile]>>8);
12
  
13
  zeile++;
14
  
15
  if(zeile == 8)
16
    zeile = 0;
17
}    
18
19
int main(void)
20
{
21
  DDRA = 0xff;
22
  DDRB = 0x07;
23
  DDRC = 0xff;
24
  
25
  matrix[ A ] = 0b1110011111011111;
26
  matrix[ B ] = 0b1001010000010000;
27
  matrix[ C ] = 0b1001010000010000;
28
  matrix[ D ] = 0b1001011110011110;
29
  matrix[ E ] = 0b1001010000010000;
30
  matrix[ F ] = 0b1001010000010000;
31
  matrix[ G ] = 0b1001010000010000;
32
  matrix[ H ] = 0b1110011111011111;
33
  
34
  MUXPORT = 0x00;
35
  
36
  init_tmr();
37
  init_intr();
38
  
39
  sei();
40
  
41
    while(1)
42
    {
43
44
  }
45
}

keine Angst die "0b000..." geschichten sind nur für die ersten 
Testzwecke.
Wird alles noch hübsch gemacht.
Jetzt kann man ja für alle möglichen dinge funktionen schreiben, welche 
das Array manipulieren.
Das leichte leuchten von einigen umliegenden LEDs ist jetzt so gut wie 
weg. warum das immer noch teilweise entsteht ist mir noch nicht wirklich 
klar.

Karl Heinz Buchegger schrieb:
> Du brauchst an deinem MUXPORT eine Möglichkeit, wie du KEINE Zeile
> leuchten lassen kannst.

Keine Zeile leuchten lassen tue ich einfach indem ich für die zeilen die 
Spalten-Bits auf null setze.

Die Ansteuerung läuft wie folgt.
mit einem Logic Baustein (74237) lasse ich nacheinander die Kathoden der 
LED-Zeilen per Transistor auf Masse ziehen. Die Anoden werden über zwei 
PORT Register gesetzt oder halt nicht.

Danke für die Tipps!!! Hat echt geholfen. Aber ich denke es ist nur eine 
Frage der Zeit wann Ihr wieder was von mir hört ;-)

Grüße Jan

von Karl H. (kbuchegg)


Lesenswert?

Jan S. schrieb:


> Das leichte leuchten von einigen umliegenden LEDs ist jetzt so gut wie
> weg. warum das immer noch teilweise entsteht ist mir noch nicht wirklich
> klar.

Weil du hier
1
ISR(TIMER0_COMP_vect)  //Interruptfrequency: 4MHz 
2
{  
3
  MUXPORT = zeile;
4
5
  PORTC = matrix[zeile];
6
  PORTA = (matrix[zeile]>>8);

durch die Zuweisung an MUXPORT die aktuelle Belegung von PORTC und PORTA 
kurzzeitig bereits an die nächste Zeile ausgibst. Du änderst zwar danach 
gleich PORTC und PORTA auf das, was in dieser Zeile angezeigt werden 
soll, aber ein paar µs lang leuchtet da das Muster aus der 
vorhergehenden Zeile. Und das reicht. Man kann tatsächlich derartig 
kurze Zeiten sehen!

Du musst also dafür sorgen, dass zu diesem Zeitpunkt, an dem du die 
nächste zeile aktivierst, garantiert nix mehr leuchtet

> Keine Zeile leuchten lassen tue ich einfach indem ich für die zeilen die
> Spalten-Bits auf null setze.

D.h. wenn du an PORTA bzw. PORTC 0 ausgibst, leuchtet nix?

Dann ist klar, wie der Beginn der ISR aussehen muss
1
ISR(TIMER0_COMP_vect)  //Interruptfrequency: 4MHz 
2
{  
3
  PORTC = 0;
4
  PORTA = 0;
5
6
  MUXPORT = zeile;
7
  PORTC = matrix[zeile];
8
  PORTA = (matrix[zeile]>>8);
9
  ...

WEnn du jetzt am MUXPORT umstellst, leuchtet garantiert nichts mehr. Und 
damit kann es dann auch kein Geisterleuchten mehr geben.

von Karl H. (kbuchegg)


Lesenswert?

> ISR(TIMER0_COMP_vect)  //Interruptfrequency: 4MHz

Du hast hier aber nicht wirklich eine Aufruffrequenz von 4Mhz am Timer 
eingestellt. Oder?

1kHz oder 2kHz tuns auch bei einem 8-er Multiplex. Dann kostet dich das 
Multiplexen praktisch gesehen so gut wie keine Rechenzeit.

von Karl H. (kbuchegg)


Lesenswert?

> keine Angst die "0b000..." geschichten sind nur für die
> ersten Testzwecke.

:-)
weiß ich doch.
Was ich sagen wollte:
Du machst gute Fortschritte. Es ist schön zu beobachten, wie du deine 
persönlichen Grenzen immer weiter rausschiebst.

von Jan S. (spongebob)


Lesenswert?

So, hab das jetzt mal umgebaut mit null-setzen der PORTS und umstellen 
auf 2kHz Interrupt-Frequenz. Kein Geisterleuchten mehr.
Danke fürs Kompliment! Sowas motiviert mich echt sehr!!!

Grüße Jan

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.