Hallo
Eine Anfrage per PN hat mich dazu verleitet hier mal die Ansteuerung der LED-Matrix genauer zu erklären. Basis der Betrachtung ist natürlich der Schaltplan:
Bild hier Bild hier
(Plan und Bild von http://www.elo-web.de/elo/mikrocontr...und-selbsttest)
Sowohl im Plan wie auch auf der Platine erkennt man klar die Aufteilung in 10 Zeilen und 12 Spalten. Jede Zeile ist über einen Widerstand mit einem Pin des Mega8 verbunden, jede Spalte mit einem Ausgang der Schieberegister. Die LEDs sind mit der Anode an einer Zeilenleitung angeschlossen, die Kathode hängt an einer Spaltenleitung. Eine LED leuchtet, wenn am Portpin ihrer Zeile ein High und am Schieberegister ihrer Spalte ein Low ausgegeben wird.
Weil es etwas einfacher ist, betrachten wir zuerst die Funktion der Schieberegister (4094D). Diese werden mit drei Signalen angesteuert: Daten, Clock und Strobe (eigentlich sind es vier Signale, aber Enable ist mit Vcc gebrückt und deshalb sind die Schieberegister immer aktiv. Schade eigentlich, denn so kann man den Z-State des 4094D nicht nutzen...) Die Schieberegister haben jeweils acht Ausgänge und sie sind kaskadiert (über QS an IC2). Sie verhalten sich deshalb wie ein grosses Schieberegister mit 16 Ausgängen, von denen wir aber nur 12 für die Spaltenansteuerung nutzen.
Um nun die Ausgänge der Schieberegister anzusteuern, muss man zuerst eine Art Schattenregister mit dem gewünschten Bitmuster füllen. Dazu legt man am Datenanschluß den gewünschten Pegel des Bits an, das man als nächstes in das Schattenregister schieben möchte. Dann übernimmt man mit einem High-Low-Impuls an der Clockleitung dieses Bit an die ganz linke Stelle des Schattenregisters. Zuvor werden aber alle schon im Schattenregister gespeicherten Bits um eine Stelle nach rechts verschoben. Das Bit ganz rechts fällt dabei raus. Man kann beliebig viele Bits in das Schattenregister schieben. Wenn es das gewünschte Bitmuster enthält, kann man mit deinem High-Low-Impuls auf der Strobeleitung das Bitmuster an den Ausgängen ausgeben.
Wie man am AVR die Ausgänge steuert sollte eigentlich klar sein, deshalb hier nur ein kleines Beispiel für die oberste Zeile an PC0:
// Pin auf Ausgang schalten
DDRC |= (1<<PC0);
// Auf Eingang schalten
DDRC &= ~(1<<PC0);
// Pin high
PORTC |= (1<<PC0);
// Pin low
PORTC &= ~(1<<PC0);
Soweit zur Theorie, nun steuern wir mal ein Muster an:
Code:
#include <avr/interrupt.h>
#include <util/delay.h>
#include <inttypes.h>
uint8_t c, spalte, bremsen=5;
int main(void)
{
// initialisieren
DDRB = 0x1f; // PB4 bis PB0 sind Ausgang 76543210
PORTB &= ~0x1f; // und low 0b000DCS00
DDRC = 0x0f; // PC0 bis PC3
PORTC &= ~0x0f;
DDRD = 0xf0; // PD4 bis PD7
PORTD &= ~0xf0;
// alle Ausgänge der Schieberegister auf high schalten
PORTB |= (1<<4); // Datenleitung auf high
for(c=0; c<12; c++) // Schattenregister mit Einsen füllen für 12 Spalten
{
PORTB |= (1 << 3); // Clockleitung high Schiebt das Datenbit in
PORTB &= ~(1 << 3); // Clockleitung low das Schattenregister
}
PORTB |= (1 << 2); // Strobe high gibt das Schattenregister
PORTB &= ~(1 << 2); // Strobe low an die Ausgänge
// ein Low am ersten Ausgang der Schieberegister ausgeben für erste Spalte
PORTB &= ~(1<<4); // Datenleitung auf low
PORTB |= (1 << 3); // Clockleitung high Schiebt das Low links in
PORTB &= ~(1 << 3); // Clockleitung low das Schattenregister und
PORTB |= (1 << 2); // Strobe high gibt es an die Ausgänge
PORTB &= ~(1 << 2); // Strobe low weiter.
// alle Zeilen einschalten
PORTC |= 0x0f; // Zeile 0 bis 3
PORTD |= 0xf0; // Zeile 4 bis 7
PORTB |= 3; // Zeile 8 und 9
_delay_ms(1000); // eine Sekunde leuchten
// Zeilenansteuerung ausschalten
PORTC &= ~0x0f; // Zeile 0 bis 3
PORTD &= ~0xf0; // Zeile 4 bis 7
PORTB &= ~3; // Zeile 8 und 9
// Ein High ins Schattenregister schieben. Dadurch wandert das Low nach rechts
PORTB |= (1<<4); // Datenleitung auf high
PORTB |= (1 << 3); // Clockleitung high Bits schieben
PORTB &= ~(1 << 3); // Clockleitung low
PORTB |= (1 << 2); // Strobe high und ausgeben
PORTB &= ~(1 << 2); // Strobe low
// Ausgänge für Spalte 2 setzen
PORTC |= 0b00001010;
PORTD |= 0b10100000;
PORTB |= 0b00000010;
_delay_ms(1000); // eine Sekunde leuchten
// Zeilenansteuerung ausschalten
PORTC &= ~0x0f; // Zeile 0 bis 3
PORTD &= ~0xf0; // Zeile 4 bis 7
PORTB &= ~3; // Zeile 8 und 9
// Low weiterschieben
PORTB |= (1<<4); // Datenleitung auf high
PORTB |= (1 << 3); // Clockleitung high Bits schieben
PORTB &= ~(1 << 3); // Clockleitung low
PORTB |= (1 << 2); // Strobe high und ausgeben
PORTB &= ~(1 << 2); // Strobe low
// Ausgänge für Spalte 3 setzen
PORTC |= 0b00000101;
PORTD |= 0b01010000;
PORTB |= 0b00000001;
_delay_ms(1000); // eine Sekunde leuchten
spalte=2;
while(1)
{
// Zeilenansteuerung ausschalten
PORTC &= ~0x0f; // Zeile 0 bis 3
PORTD &= ~0xf0; // Zeile 4 bis 7
PORTB &= ~3; // Zeile 8 und 9
// Low weiterschieben
if(spalte) PORTB |= (1<<4); //Nur wenn spalte 0 ist wird ein Low geschoben
else PORTB &= ~(1<<4); // sonst füllen wir mit high auf
PORTB |= (1 << 3); // Clockleitung high Bits schieben
PORTB &= ~(1 << 3); // Clockleitung low
PORTB |= (1 << 2); // Strobe high und ausgeben
PORTB &= ~(1 << 2); // Strobe low
// Muster erzeugen
if(spalte & 2)
{
PORTC |= 0b00000011; // Ausgänge für ungerade Spalten setzen
PORTD |= 0b00110000;
PORTB |= 0b00000011;
}
else
{
PORTC |= 0b00001100; // Ausgänge für gerade Spalten setzen
PORTD |= 0b11000000;
PORTB |= 0b00000000;
}
if(bremsen) _delay_ms(100*bremsen); // variable Leuchtdauer
spalte++;
if(spalte>11)
{
spalte=0;
if(bremsen) bremsen--;
}
}
return(0);
}
Das Programm beginnt mit der Initialisierung der Ports des Mega8. An Port B werden neben den Bits 0 und 1 für die Matrix auch die Bits 2 bis 4 für die Ansteuerung der Schieberegister als Ausgang definiert. Dann wird das Schieberegister mit 12 Highs gefüllt und diese ausgegeben. Dadurch werden alle Kathoden auf High geschaltet und die LEDs sind ausgeschaltet.
Nun werden am Mega8 die Ausgänge für die Zeilen mit dem Muster für die erste Spalte gesetzt, high bedeutet "LED an", low bedeutet "LED aus" für die jeweilige Zeile. Da wir bei der ersten Spalte beginnen, füllen wir zuerst ein Low in das Schattenregister und geben es aus (D=0 -> clock -> strobe). Nun leuchten die ausgewählten Bits der ersten Spalte, im Programm sind das alle. Nach einer kurzen Wartezeit schaltet das Programm alle Ausgänge des Mega8 auf Low, dadurch gehen die Leds wieder aus.
Für die zweite und jede weitere Spalte müssen wir nun ein High(D=1 -> clock -> strobe) nachschieben und ausgeben damit immer nur eine Spalte mit Low angesteuert wird. Nach dem Schieben folgt dann wieder Zeilenmuster ausgeben, warten und Zeilenmuster wieder löschen. Nach 12 mal schieben sind alle Spalten abgearbeitet und alles beginnt wieder von vorne mit Low reinschieben...
Das muss jetzt erst mal reichen. Wie man alternativ zeilenorientierte Bitmuster ausgeben kann, wie man einen Bildspeicher aufbaut, wie man das in eine ISR packt, wie man Zeichen, Werte oder Grafiken darstellen kann, beschreibe ich bei Interesse gerne in einem anderen Beitrag.
Gruß
mic
Lesezeichen