Guten Tag,
ich bin noch recht frisch in C und versuche mich gerade an der
Programmierung einer kleinen selbsgebastelten 6x6 LED-Matrix.
Der Aufbau ist so einfach wie möglich gehalten, keine Schieberegister,
keine Transistoren nur Widerstände.
Die Kathoden laufen mit Widerstand auf PB0-5 und die Anoden auf PD2-7.
Die ersten versuche LED's anzusteuern sind auch geglückt, jedoch ist die
Programmierung noch sehr unschön.
Bspl. Buchstabe "H"
1
#include<avr/io.h>
2
#include<util/delay.h>
3
4
intmain(void)
5
{
6
intt;
7
t=5;// Delay
8
9
DDRD=0xFF;// Spalten als Ausgang
10
DDRB=0xFF;// Zeilen als Ausgang
11
12
while(1)
13
{
14
PORTD=0b00000100;
15
PORTB=0b11111111;
16
_delay_ms(t);
17
18
PORTD=0b00001000;
19
PORTB=0b11000001;
20
_delay_ms(t);
21
22
PORTD=0b00010000;
23
PORTB=0b11110111;
24
_delay_ms(t);
25
26
PORTD=0b00100000;
27
PORTB=0b11110111;
28
_delay_ms(t);
29
30
PORTD=0b01000000;
31
PORTB=0b11000001;
32
_delay_ms(t);
33
34
PORTD=0b10000000;
35
PORTB=0b11111111;
36
_delay_ms(t);
37
38
}
39
}
Jetzt würde ich gerne Programmiertechnisch das ganze einen kleinen
Schritt weiter führen und den Buchstaben in einem Array abspeichern und
animieren, so das er von rechts nach links über die Matrix wandert.
Mir fällt es leider schwer passende Beispiele zu finden, da die meisten
für Arduinos Programmiert sind und ich mir mit der anderen Schreibweise
ein wenig schwer tue.
Andere Beispiele sind nur darauf ausgelegt das man sie 1 zu 1 für seine
Matrix übernehmen kann und sind mit ettlichen für mich noch unnötigen
Funktionen aufgbelasen.
Ich stehe gerade aber etwas auf dem Schlauch und weiß nicht wie ich am
besten vorgehen soll. Ich würde mich über Tips und ggf. über einfache
Beispiele freuen.
Das Beispiel bräuchte nicht an meine Matrix angepasst sein, es sollte
nach möglichkeit nur so simpel wie möglich aufgezogen sein.
Für den Anfang würde es mir vermutlich schon helfen, die Darstellung
eines Buchstabens oder sonst eines "Bildes" über ein Array ausgegeben zu
sehen.
Viele Grüße
Timo
Timo B. schrieb:> Mir fällt es leider schwer passende Beispiele zu finden
Na ja, das nennt man Programmieren.
Du hast bereits alles was man braucht, sozusagen den ganzen Legokasten
gefüllt mit Teilen. Du musst sie bloss noch zusammensetzen.
Am Ende der Schleife, nach dem ein Buchstabe ein mal dargestellt wurde,
nicht unbedingt jedes mal sondern vielleicht jedes 33. mal also
sekündlich, kannst du ein wenig zusätzliche Arbeit tun um den nächsten
Buchstaben darzustellen, oder diesen etwas verschoben darzustellen.
Natürlich greifen dann deine 0b Konstanten für PORTB nicht mehr auf
Konstanten zu sondern variable Werte, die du am Ende der Schleife
passend geupdatet hast.
Ich habe auf ebay günstig ein MyAVR Board bekommen, da war der bei.
Ich dachte mir für die ersten Schritte mit µC ist der Ok.
Genau genommen ist es ein Atmega8A-PU, ich kenne mich mit den Modellen
nicht so gut aus, aber ich glaube der ist etwas neuer.
Gruß
Timo
Timo B. schrieb:> Genau genommen ist es ein Atmega8A-PU, ich kenne mich mit den Modellen> nicht so gut aus, aber ich glaube der ist etwas neuer.
Auch das würde z.B. an der Größe des verfügbaren Speichers nichts
ändern.
Timo B. schrieb:> Ich dachte mir für die ersten Schritte mit µC ist der Ok.
Er reicht jedenfalls nicht bloss zur Darstellung eines Buchstabens auf
einer 6 x 6 Matrix, sondern eines ganzen Terminbildschirms (40 x 24?)
auf einem TV (also mit 50Hz wiederholtes BAS Signal), wenn man
programmieren kann.
Das kann Karl wohl nicht.
Einfacher würde man es sich mit einem Arduino machen.
Das kann auch Wolfgang.
Als erstes solltest du die Spalten per Interrupt nacheinander aufrufen.
Danach kannst du eine Funktion schreiben, z.B. LED_on(int x, int y) und
LED_off(int x, int y), welche in das Zeilenarray schreibt.
Wenn das funktioniert, kannst du in der while Schleife deine gewünschte
Animation zur Not mit der Delay Funktion realisieren.
Danke,
ich bin mir noch nicht sicher ob ich es ganz verstanden habe, aber ich
werde morgen mal versuchen das so umzusetzen.
Warum die Interruptroutine?
Gruß
Timo
Weil der Bildaufbau ja eigentlich ein Hintergrund- oder Nebenprogramm
ist und es unabhängig davon ist was du eigentlich für ein Bild ausgeben
willst.
Danach hast du eine leere while(1){} Schleife und kannst dort angeben,
was du eigentlich auf der Matrix ausgegeben willst und kannst zu jedem
Zeitpunkt die jede beliebige LED ein- oder ausschalten. Somit kommt sich
der Bildaufbau und die Animation nicht blöd in die Quere.
... auch wenn du scheinbar eine 5x6 Matrix verwendest könnte dir mein
Post von einer 5x7 Matrix helfen.
Vor einiger Zeit habe ich das hier gepostet:
Beitrag "LED Dot-Matrix 5x7 Mäxle-Würfelspiel / 2er Würfel"
Das ist zwar ein Würfelspiel auf einer LED-Matrix, sie kann aber auch
Buchstaben darauf ausgeben und es wird gezeigt, wie die Matrix mittels
Timerinterrupt angesteuert wird.
Der Quellcode sollte ausreichend dokumentiert sein.
Eine Änderung mußt du hier allerdings machen:
Der ATMega8 hat kein TIMSK1 - Register, sondern lediglich ein TIMSK -
Register, daher mußt du in der Funktion
Ich frage gerade die Tastatur eines alten 4-Kanal Mulittrackers ab und
lasse seine 7 Segments und LED leuchten.
Hier liegt eine 7*8 Matrix vor mit 4 extra Leitungen für die Buttons.
Meine ISR mit Timer0 (wird alle 2ms gezündet) auf dem Mega8 sieht so
aus:
1
// there are 2 Latches data wired to Port B and latched through PortD
2
// PORTD
3
#define CONTROL_PORT PORTD
4
#define CONTROL_DIR DDRD
5
#define CONTROL_PIN PIND
6
#define COL_LATCH 7
7
#define ROW_LATCH 6
8
#define OE_PIN 5
9
// PORTB
10
#define DATA_PORT PORTB
11
// PORTC lower 4 bits are the keyboard return bits
12
#define KEY_PORT PORTC
13
#define KEY_PINS PINC
14
#define KEY_DIR DDRC
15
#define KEY_MASK 0x0F
16
17
// matrix variables for in and output
18
19
volatileuint8_tcharbuff[7];
20
volatileuint8_tkeybuff[7];
21
22
voidset_row(uint8_tdata){
23
DATA_PORT=~(data);// invert bits for negative logic
24
_delay_us(2);
25
CONTROL_PORT|=(1<<ROW_LATCH);
26
_delay_us(2);
27
CONTROL_PORT&=~(1<<ROW_LATCH);
28
_delay_us(2);
29
}
30
voidset_col(uint8_tdata){
31
DATA_PORT=data;
32
_delay_us(2);
33
CONTROL_PORT|=(1<<COL_LATCH);
34
_delay_us(2);
35
CONTROL_PORT&=~(1<<COL_LATCH);
36
_delay_us(2);
37
}
38
// multiplexer routine for Fostex Panel
39
// reads charbuff and sets keybuff
40
ISR(TIMER0_OVF_vect){
41
staticuint8_tcol=0x01;
42
staticuint8_trow=0;
43
// reading is not perfect, will implement PeDas debounce next
44
keybuff[i]=~(KEY_PINS)&KEY_MASK;// first read keys from Fostex Panel
45
CONTROL_PORT|=(1<<OE_PIN);// prevent ghosting by disabling Latch output
46
_delay_us(2);// let OE settle
47
set_col(col);// set the column
48
set_row(charbuff[row]);
49
_delay_us(2);
50
CONTROL_PORT&=~(1<<OE_PIN);// enable latches again
51
col=(col<<1)&0xFE;// lets wander through the bits and make sure of a clear LSB
52
row++;// prepare the next scan
53
// limit the matrix to 7 columns
54
if(col==0x80){
55
col=0x01;
56
row=0;
57
}
58
}
Der Mega 8 hat hier viel zu wenig Ports, deswegen habe ich 2 '573er
Latches. Das Prinzip bleibt aber das gleiche. Ich lasse eine '0x01' von
rechts nach links durch die Spalten wandern und setze entsprechend die
Segmenttreiber. Gesetzt wird dabei das Bytemuster in charbuff[].
Timo B. schrieb:> if (col == 0x07) { // Nach der 6ten Spalte wieder zum Anfang springen> 2-7
Nee, diese Bedingung wird nie erfüllt, denn du schiebts ja nur ein bit
durch.
Es fängt an mit 0x02 = 0b00000010
das nächste ist 0x04 = 0b00000100
dann kommt 0x08 = 0b00001000
usw.
das letzte ist 0x80 = 0b10000000
danach wird das bit rausgeschoben und col ist 0.
also sollte die Bedingung heissen:
1
if(col==0){
2
col=0x02;
3
row=0;
4
}
Es steht dir auch frei, row als Abbruchbedingung zu nehmen, dann würdest
du auf 'if (row > 5)' abfragen.
Übrigens musst du natürlich den Timer initialisieren, sonst kommt da nie
ein Interrupt.
Sowas hier sollte es tun, etwa 480 Hz Wiederholrate bei 1Mhz:
Vielen Dank es funktioniert jetzt :)
Ich hatte erst ein wenig Probleme den Timer richtig einzubinden, ich
hatte zu beginn
1
voidinit_timer0(void){
2
TCNT0=0;
3
TCCR0=(1<<CS01);// div 8 prescale
4
TIMSK=(1<<TOIE0);// Timer 0 IRQ Freigabe
5
}
genau so, außerhalb der main Funktion eingefügt, das hat jedoch nicht
funktioniert.
Außerdem fehlten noch Sachen wie die Definition der Ausgänge und die
Einbindung der header-Datei für die Interrupts.
Hier noch der aktuelle Code.
Timo B. schrieb:> hatte zu beginn void init_timer0(void) {> TCNT0 = 0;> TCCR0 = (1 << CS01) ; // div 8 prescale> TIMSK = (1 << TOIE0); // Timer 0 IRQ Freigabe> }> genau so, außerhalb der main Funktion eingefügt, das hat jedoch nicht> funktioniert.
Ja, das ist ein Unterprogramm. Man ruft es dann mit 'init_timer0();' aus
dem Hauptprogramm auf.
Mit diesem grundlegenden Konzept solltest du dich beschäftigen.
> #define COL_LATCH 6> #define ROW_LATCH 6
Das brauchst du nicht - kannste löschen.
Hat evtl. jmd. einen kleinen Tip zur animation für mich?
Ich bastel seit Stunden herum, aber außer viel gelöschtem Code ist noch
nichts dabei herum gekommen.
In den meisten Fällen passiert garnichts oder der Delay kommt der
Interruptroutine in die Quere.
Mein erster Gedanke dazu war, den über den Array ausgegeben Wert, über
eine variable Bitverschiebung (i) zu ändern und den Vorgang zusammen mit
einem delay() in der while-Schleife zu wiederhohlen.
Dabei passiert aber nichts.
Timo B. schrieb:> while(1)> {> i++; // <--------------------------> _delay_ms(1000); // <--------------------------> }
Die beiden Zeilen hättest du dir in dem aktuellen Programm sparen
können. Was soll es bringen eine sonst nicht verwendete Variable nach
oben zu zählen?
Probier doch mal sowas:
Danke für die ANtwort,
ich habe versucht i nach oben zu zählen, damit ich eine Bitverschiebung
durchführen kann.
1
while(1)
2
{
3
i++;// <--------------------------
4
_delay_ms(1000);// <--------------------------
5
}
1
voidset_col(uint8_tdata)
2
{
3
PORTD=(data<<i);// <--------------------------
4
}
Ich sehe aber gerade das ich oben quatsch geschrieben habe, nicht das
Array wird verschoben sondern die Spalten.
Entschuldigt das war ein früher Versuch mit dem Array, bis ich gemerkt
habe das der Buchstabe dann nach oben wandern würde, anstatt nach links
oder rechts. :)
Der von dir genannte Code erschließt sich mit nicht ganz, aber das wird
wohl an meiner Fehlinformation liegen.
Er zählt zwar irgendetwas aber es ist definitiv kein "H" mehr welches
angezeigt wird. :D
Du kannst das ganze synchronisieren, wenn du in die Hauptschleife eine
Routine einbaust, die auf den Timeroverflow IRQ wartet:
1
// wait for the timer 0 ovf event
2
voidtimerWait(void){
3
TIFR=(1<<TOV0);
4
while(!(TIFR&(1<<TOV0)));
5
}
6
// und dann in der Hauptschleife:
7
8
// hauptschleife wird mit etwa 488 Hz durchlaufen
9
// synchronisiert von Timer 0
10
uint8_tticker;
11
while(1){
12
//
13
timerWait();// wait for the ISR
14
// jetzt könnte man bequem ein neues Muster in den displaybuffer kopieren
15
// hier mit etwa 10 bildern pro Sekunde.
16
ticker++;
17
if(ticker>49){
18
copynextpattern();
19
ticker=0;
20
}
21
}
Als nächstes brauchst du Daten, z.B. ein animiertes Bildchen, das du in
einzelne Frames, nach dem gleichen Muster wie den charbuff, aufbaust.
Die Frames kopierst du dann in den displayspeicher.
Timo B. schrieb:> Der von dir genannte Code erschließt sich mit nicht ganz, aber das wird> wohl an meiner Fehlinformation liegen.
Ich hatte nicht gesehen, dass i noch woanders verwendet wird und hatte
den Code vorgeschlagen, um überhaupt mal irgendetwas zu sehen.
Ich finde die aktuelle Verwendung von i konzeptionell falsch. Die beiden
Funktionen set_row und set_col brauchst du nicht. Eine Funktion mit
einer Zeile Code, die nur einmalig aufgerufen wird, macht das ganze
nicht viel übersichtlicher.
Meine Empfehlung: Die ISR sollte ausschließlich auf das charbuff Array
und der Spaltenzählvariable zugreifen. Ein i wird dafür nicht gebraucht.
In der main Funktion kannst du dann dein charbuff Array bearbeiten.
Außerdem sollte man die LED Matrix vielleicht um 90° drehen, damit man
den Buchstaben 'H' auch in dem Quellcode wiedererkennt. Bisher sieht die
Initialisierung des charbuff ja eher aus wie ein 'I'.
So sieht das ganze doch schon viel übersichtlicher aus und sollte
funktionieren: