Forum: Mikrocontroller und Digitale Elektronik LED multiplexen


von Tobias D. (tobias92)


Lesenswert?

Hallo,
ich habe eine Uhr gebaut mit einem Display in der mitte und außenrum im 
kreis 60 leds. die leds habe ich mit hilfe einer matrix angesteuert. 
wegen der matrix kann ich ja nur eine led auf einmal ansteuern. also hab 
ich mir gedacht mulriplex ich das, dass zum beispiel bei 53sec auch 53 
leds leuchten. so mein problem ist, dass die leds auch noch bei sehr 
hoher frequenz flimmern. ich habe mal gehört, dass das menschliche auge 
ab einer frequenz von 50hz die led als durchgehend leuchtend wahrnimmt. 
außerdem nimmt bei steigender frequenz die leuchtstärke der leds ab. 
kann ich dem irgendwie abhilfe schaffen, dass die led nicht mehr 
flimmern und noch einigermaßen hell sind?

mfg tobias

von Frank B. (f-baer)


Lesenswert?

Tobias Domhöfer schrieb:
> Hallo,
> ich habe eine Uhr gebaut mit einem Display in der mitte und außenrum im
> kreis 60 leds. die leds habe ich mit hilfe einer matrix angesteuert.
> wegen der matrix kann ich ja nur eine led auf einmal ansteuern. also hab
> ich mir gedacht mulriplex ich das, dass zum beispiel bei 53sec auch 53
> leds leuchten.

Dafür ist deine Matrix zu groß, siehe unten.

> so mein problem ist, dass die leds auch noch bei sehr
> hoher frequenz flimmern. ich habe mal gehört, dass das menschliche auge
> ab einer frequenz von 50hz die led als durchgehend leuchtend wahrnimmt.

Ja, kann ich so bestätigen.

> außerdem nimmt bei steigender frequenz die leuchtstärke der leds ab.
> kann ich dem irgendwie abhilfe schaffen, dass die led nicht mehr
> flimmern und noch einigermaßen hell sind?

Das kommt daher, dass bei zwei LEDs schon näherungsweise die Hälfte der 
Rechenzeit in einer Programmschleife für die Ansteuerung der ersten LED 
genutzt wird, die zweite Hälfte für die zweite LED. Danach startet das 
Programm neu.
Je mehr LEDs du ansteuerst, desto geringer wird das Tastverhältnis.
Steuerst du 2 LEDs an, dann hast du noch ca. 50% Tastverhältnis, bei 50 
LEDs bist du noch bei ~2%.

von Hobbyelektroniker (Gast)


Lesenswert?

Guten Morgen,

schau dir mal diesen Artikel an:
http://www.mikrocontroller.net/articles/LED-Matrix

dort ist alles beschrieben was du benötigst, sollte dann noch etwas 
unverständlich sein dann wieder hier melden :)

Gruß

von Peter (Gast)


Lesenswert?

Ich denke das ist ein Software Fehler. Bei 64 Leds ist das eine 8x8 
Matrix. Warum kannst du dort nur 1 LED gleichzeitig leuchten lassen? 
Normalerweise können dort 8 gleichzeitig leuchten. Jede Gruppe aus 8 
LEDs hat also 1/8 ihrer normalen Helligkeit. Die meisten LEDs kann man 
aber mit dem 4 fachen strom im Pulsbetrieb betreiben, damit sind sie 
schon nur noch halb so hell wie sie sein könnten, aber das fällt meist 
nicht auf.

von Tobias D. (tobias92)


Lesenswert?

ja stimmt da hast du recht. ich kann auch 8 geleichzeitiig leuchten 
lassen. aber ich bekomme das flimmern nicht raus. villt kann es aber 
auch daran liegen, dass ich einen zu großen vorwiderstand drinne habe. 
hab 560R gewählt, damit ist die LED noch hell genug und zeit nur einen 
strom von ~6mA. wenn ich jetzt das ganze multiplex und die LED nur 1/8 
der Helligkeit hat ist das schon ziehmlich dunkel.
also gibt es keine möglichkeit das flimmern rauszubekommen?

von Peter (Gast)


Lesenswert?

das Flimmern wird ein Software fehler sein! Also zeigt doch bitte mal 
die Software.

von Peter D. (peda)


Lesenswert?

Fürs Multiplexen ist ein Timerinterrupt essentiell.
Ansonsten flimmerts eben.


Peter

von Tobias D. (tobias92)


Angehängte Dateien:

Lesenswert?

okay, hier meine Software:
(als dateianhang)

von Peter (Gast)


Lesenswert?

was hat denn der ganze C code in der H datei verloren? Der ganze code 
ist ja voll von Delays ist ja klar das das nicht sauber geht. Wo wird 
überhaupt die LED funktion aufgerufen?

von Karl H. (kbuchegg)


Lesenswert?

Tobias Domhöfer schrieb:
> okay, hier meine Software:
> (als dateianhang)

Puh.
Den ganzen Code müsste man ..... entsorgen und neu schreiben.

Und das würde ich an deiner Stelle auch tun. Mit diesem Code kommst du 
nicht mehr weiter, du bist in einer Sackgasse.

Konzentrier dich beim neuen Code auf die 8*8 Matrix.
Die muss in einem Interrupt angesteuert werden, jeweils 8 Led bei jedem 
Interrupt Aufruf. Beim nächsten Interrupt kommen dann die nächsten 8 LED 
drann.
Für das Multiplexen ist es unerheblich, ob eine bestimmte LED 
eingeschaltet ist oder nicht, du gehst deine 64 LED in 8-er Gruppen 
durch und lässt die entsprechende LED leuchten oder auch nicht, je 
nachdem was dein Steuerarray vorgibt. Dieser Mechanismus läuft immer und 
ständig.

Und erst darauf aufbauend schaltets du die richtige LED abhängig von der 
aktuellen Sekundenzahl ein oder aus, in dem du in diesem Steuerarray die 
entsprechende Information einträgst.

Und sowas wie wait oder _delay_ms darf es in deinem ganzen Code, mit 
Ausnahme der Hauptschleife nicht geben (mit Ausnahme von ein paar kurzen 
_delay_us in den LCD Funktionen)


Und mach dir mal ein paar Basisfunktionen für Standardsachen wie zb 
LCD-Ansteuerung und Ausgabe auf LCD; Tastenentprellung. Das ist doch 
krank was du da aufführst, indem du alles auf unterster Ebene in die 
Hauptschleife mehr oder weniger direkt einbaust.
Kein Wunder, dass du in dieser Sackgasse gelandet bist. Dein Code hat 
keine Struktur, keinen Aufbau, keine funktionalen Hierarchien, keine 
building Blocks. Das ist ein monolithischer Klotz Code, der unwartbar 
und unveränderbar ist. Greif an einer Stelle ein und alles fällt 
auseinander.

von Tobias D. (tobias92)


Lesenswert?

boah das muss ich erstmal verarbeiten -.-'
ich programmiere erst seit 3 wochen oder so, bin noch nicht so der 
meister... ich mein es hat ja alles bestens funtioniert(bis jetzt eben 
die multiplexgeschichte).
hab gerade versucht parallel zu den LEDs kondensatoren zu schalten, die 
dann die LED für kurze zeit puffern, wenn sie nicht angesteuert wird. 
aber das geht leider auch nicht.
dann werd ich mich mal dranmachen den code neuzuschreiben.

von Karl H. (kbuchegg)


Lesenswert?

Tobias Domhöfer schrieb:
> boah das muss ich erstmal verarbeiten -.-'
> ich programmiere erst seit 3 wochen oder so, bin noch nicht so der
> meister...


das ist schon ok.
keiner ist als Meister zur Welt gekommen.

Aber man muss auch mal sagen (und auch einsehen) wenn man sich in eine 
Sackgasse verrannt hat. Bis jetzt hast du noch nie dein Augenmerk auf 
Dinge wie Codestruktur bzw. Programmaufbau gelegt. Dir war wichtig, dass 
es funktioniert. Nur irgendwann ist dann der Punkt erreicht, an dem man 
ohne Aufbau und Struktur nicht mehr weiterkommt (und dazu gehören auch 
Dinge wie Codeformattierung - deine ist einfach nur grauenhaft)

Die Techiken, mit denen man ein Baumhaus baut funktionieren bei einem 
Einfamilienhaus einfach nicht mehr.

von Karl H. (kbuchegg)


Lesenswert?

Kontrollier bitte mal, ob ich aus deinem Code den Anschluss der LED 
richtig herausgelesen habe:
1
                         Bit
2
      \ D |   7   6   5   4   3   2   1   0
3
    B  \--+------------------------------------
4
          |
5
     2    |  54  55  56  57  58  59   0   1
6
 B   1    |   2   3   4   5   6   7   8   9
7
 i   0    |  10  11  12  13  14  15  16  17
8
 t   7    |                  18  19  20  21
9
     6    |  22  23  24  25  26  27  28  29
10
     5    |  30  31  32  33  34  35  36  37
11
     4    |  38  39  40  41  42  43  44  45
12
     3    |  46  47  48  49  50  51  52  53

die Tabelle ist so zu lesen:
Wenn am D-Port das Bit n (Spalte) auf 1 gesetzt ist UND am Port B das 
Bit m (Zeile) gesetzt ist, dann leuchtet die LED mit der Nummer, die am 
Schnittpunkt aus Zeile und Spalte eingetragen ist.

Bsp. Ist am Port D das Bit 5 und am Port B das Bit 4 auf 1 gesetzt, dann 
leuchtet LED Nr. 40

Deine unregelmässige Tabellenstruktur muss jetzt durch Software 
korrigiert werden. Was wir wollen:
Wir wollen ein Array aus 8 Byte, wobei jedes Bit in jedem Byte mit einer 
LED korrespondiert. Jedes Byte ist eine komplette Zeile in der Tabelle. 
In einem Multiplexschritt geben wir einfach das Byte auf den Port D aus 
und setzen im B-Port das zu dieser Zeile gehörende 1 Bit. Somit leuchten 
dann die zugehörenden 8 LED (oder auch nicht, je nachdem wie die Bits im 
Byte das am D-Port ausgegeben wurde aussehen). Nur muss es jetzt 
natürlich eine Vorschrift geben, wie man aus der LED-Nummer die Nummer 
des Bytes und die Nummer des zu setzenden Bits innerhalb dieses Bytes 
errechnen kann. Und um diese Vorschrift zu finden muss diese Zuordnung 
klar sein und am besten irgendwo dokumentiert werden.

Das Ziel ist es
1
uint8_t Leds[8];
2
3
void SetBit( uint8_t LedNr )
4
{
5
   ...
6
}
7
8
void ClearBit( utin8_t LedNr )
9
{
10
  ...
11
}

so mit Leben zu füllen, dass beim Aufruf von
  SetBit( 40 );
das Bit 5 in Leds[4] auf 1 gesetzt wird.

Aber um zu wissen, wie diese Umrechnung auszusehen hat muss dies Tabelle 
erst mal stimmen.

von Tobias D. (tobias92)


Lesenswert?

die Tabelle stimmt auf jedenfall schon mal...

von Karl H. (kbuchegg)


Lesenswert?

OK.
Dann lass uns mal mit dem Multiplexen anfangen

Am Anfang steht .... das rechnen.

Wir brauchen eine ISR die so ca. 400 mal in der Sekunde aufgerufen wird. 
Warum 400 mal? Weil wir für einen Durchgang durch alle 8 LED-Bänke 
logischerweise 8 Zyklen brauchen. Jede LED soll mit 50Hz leuchten, also 
50 mal in der Sekunde angesteuert werden. 8 mal 50 sind 400.

Mal schaun.
Du hast eine Taktfrequenz von 4Mhz und wir nehmen den Timer 0, einen 8 
Bit Timer. Die ISR möchten wir gerne an den Overflow Interrupt hängen.
Wie müssen daher die Timer Einstellungen sein, damit der Overflow 400 
mal in der Sekunde erreicht wird.

Unsere Wunschvorstellung für den Prescaler wäre
1
  4000000 / Prescaler / 256 = 400
daraus ergibt sich, dass wir einen Prescaler von
1
  4000000 / 256 / 400 = 39
brauchen würden.
Den haben wir aber nicht. Ach ja, ich habe jetzt mal einen Mega16 zur 
Entwicklung angenommen, da ich auf die Schnelle nicht gefunden habe, 
welchen Prozessor du tatsächlich hast.

Ein Blick ins Datenblatt verrät uns, das Prescaler die in der Nähe 
liegen entweder 8 oder 64 sind.

Rechnen wir mal durch, was das für Multplexraten ergeben würde.
1
  4000000 / 8 / 256 = 1953 Hz  oder für 1 Ledbank  -> 244 Hz
2
  4000000 / 64 / 256 = 244 Hz  oder für 1 Ledbank  ->  30 Hz
30 Hz ist ein bischen wenig. Das wird man schon flimmern sehen, also 
bleibt uns nicht viel anderes übrig als einen Prescaler von 64 zu 
nehmen. Damit flackert dann jede einzelne Led mit 244 Hz und das wird 
man mit Sicherheit nicht mehr sehen.


Der erste Teil sieht daher so aus
1
#define F_CPU 4000000      
2
3
#include <avr/io.h>
4
#include <util/delay.h>      
5
#include <stdlib.h>
6
#include <stdio.h>        
7
#include <avr/interrupt.h>      
8
9
10
volatile uint8_t Leds[8];
11
12
uint8_t mtpxCnt;
13
uint8_t portBBits[8] = { 1<<PB2, 1<<PB1, 1<<PB0, 1<<PB7, 1<<PB6, 1<<PB5, 1<<PB4, 1<<PB3 };
14
15
ISR( TIMER0_OVF_vect )
16
{
17
  // die bisherig leuchtenden LED abschalten
18
  PORTD = 0x00;
19
  PORTB = 0x00;
20
21
  // die neue Led Belegung ausgeben
22
  PORTD = Leds[ mtpxCnt ];
23
  PORTB = portBBits[ mtpxCnt ];
24
25
  // und den Multiplexcounter um 1 weitersetzen
26
  // damit beim nächsten ISR Aufruf die nächsten 8 Led
27
  // angesteuert werden
28
  mtpxCnt++;
29
  if( mtpxCnt == 8 )
30
    mtpxCnt = 0;
31
}
32
33
int main ()
34
{
35
  DDRB = 0xff;
36
  DDRD = 0xff;
37
38
  TCCR0 = (1<<CS01) | (1<<CS00); // Prescaler 64
39
  TIMSK |= (1<<TOIE0);
40
41
  sei();
42
43
  while( 1 ) {
44
  }
45
}

In der ISR passiert nicht mehr, als das reihum ein anderes Element aus 
dem Leds-Array samt zugehörigem Bit am B Port ausgegeben wird. Für die 
Bits am B-Port hab ich mir noch ein zusätzliches Array gemacht, wobei 
jedes Array Element an der jeweils richtigen Stelle ein 1 Bit aufweist. 
Denn um die Leds in Leds[4] (also der 5. Zeile) auszugeben, muss ja laut 
Tabelle
1
                         Bit
2
      \ D |   7   6   5   4   3   2   1   0
3
    B  \--+------------------------------------
4
          |
5
     2    |  54  55  56  57  58  59   0   1              Leds[0]
6
 B   1    |   2   3   4   5   6   7   8   9              Leds[1]
7
 i   0    |  10  11  12  13  14  15  16  17              Leds[2]
8
 t   7    |                  18  19  20  21              Leds[3]
9
     6    |  22  23  24  25  26  27  28  29              Leds[4]
10
     5    |  30  31  32  33  34  35  36  37              Leds[5]
11
     4    |  38  39  40  41  42  43  44  45              Leds[6]
12
     3    |  46  47  48  49  50  51  52  53              Leds[7]

das Bit 6 im B-Port gesetzt werden.

Beim ersten ISR Aufruf wird daher Leds[0] am PORTD ausgegeben und an 
PORTB wird 0b00000100 ausgegeben, welches laut Tabelle die erste Zeile, 
und damit die LEDS 54 bis 1 aktiviert, sofern in Leds[0] das zugehörige 
1 Bit gesetzt ist. Und genau das testen wir jetzt erst mal.
Dazu wird die main() geändert

1
int main ()
2
{
3
  .....
4
5
  while( 1 ) {
6
    Leds[0] = 0x1000000;
7
  }
8
}
das müss die Led Nr 54 aufleuchten lassen

1
int main ()
2
{
3
  .....
4
5
  while( 1 ) {
6
    Leds[0] = 0x0100000;
7
  }
8
}
Led Nr 55 leuchtet

wir können auch beide leuchten lassen
1
int main ()
2
{
3
  .....
4
5
  while( 1 ) {
6
    Leds[0] = 0x1100000;
7
  }
8
}

oder alle
1
int main ()
2
{
3
  .....
4
5
  while( 1 ) {
6
    Leds[0] = 0x11111111;
7
  }
8
}

oder nur jede 2.te
1
int main ()
2
{
3
  .....
4
5
  while( 1 ) {
6
    Leds[0] = 0x10101010;
7
  }
8
}

Alles was wir tun müssen ist, in Leds[0] bis Leds[7] das jeweils 
richtige Bit auf 1 zu setzen (laut Tabelle) und die ISR sorgt dafür, 
dass die zueghörige Led aufleuchtet. Das geht dann auch mit mehreren.

Aber ehe ich da jetzt weiterarbeite, solltest du das erst mal testen 
(denn ich programmiere deine Hardware blind und kann daher nicht sagen, 
ob das bei dir tatsächlich funktioniert)

von Tobias D. (tobias92)


Lesenswert?

Danke für deine Hilfe. ja ich nutze einen mega16 ;). hab das multiplexen 
jetzt mit dem timer2 gemacht. da funzts.
super jetzt läuft alles :)

gruß tobi

von Peter (Gast)


Lesenswert?

Dann bauch aber mal noch den code aus der header datei aus, kannst ihn 
ja in eine andere *.c Datei machen, aber nicht in einen header.

von Karl H. (kbuchegg)


Lesenswert?

Tobias Domhöfer schrieb:
> Danke für deine Hilfe. ja ich nutze einen mega16 ;). hab das multiplexen
> jetzt mit dem timer2 gemacht. da funzts.
> super jetzt läuft alles :)

Ich hoffe mal du hast dir eine schöne Umrechnungsfunktionen gemacht, so 
das du schreiben kannst
1
void SetLed( uint8_t LedNr )
2
{
3
  uint8_t mpxNr;
4
  uint8_t mpxMask;
5
6
  calcMpxNrMask( &mpxNr, &mpxMask );
7
  Leds[mpxNr] |= mpxMask;
8
}
9
10
void ResetLed( uint8_t LedNr )
11
{
12
  uint8_t mpxNr;
13
  uint8_t mpxMask;
14
15
  calcMpxNrMask( &mpxNr, &mpxMask );
16
  Leds[mpxNr] &= ~mpxMask;
17
}
18
19
void calcMpxNrMask( uint8_t * Nr, uint8_t * Mask )
20
{
21
  ...
22
}


räum den Rest vom Code auch auf. zb alles was mit LCD zu tun hat in 
Funktionen verpacken. Deine Ausgabefunktionen für Stunden, Minuten, 
Sekunden auf dem LCD sind im Grunde alle gleich, hat man eine 
Ausgabefunktion die eine Zahl ausgeben kann, kann man die 3 Funktionen 
wesentlich vereinfachen. etc. etc.

von Tobias D. (tobias92)


Lesenswert?

wieso hat das nachteile? oder macht man das einfach nicht?

von Karl H. (kbuchegg)


Lesenswert?

Tobias Domhöfer schrieb:
> wieso hat das nachteile? oder macht man das einfach nicht?

Das ist genau das von dem ich oben geschrieben habe.
Irgendwann bist du an einem Punkt angelangt, an dem ohne vernünftige 
Struktur nichts mehr geht.

Ausserdem will man nicht bei jedem neuen Projekt das Rad neu erfinden. 
Die LCD Funktionen an einer Stelle beisammen werden dann einfach ins 
nächste Projekt übernommen und schon hast du in 10 Sekunden im nächsten 
Projekt dein LCD am laufen. Und zwar ohne dass du dir die Einzelteil 
erst aus dubiosem Code zusammensuchen musst.

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.