Hallo,
ich will, hauptsächlich ums Multiplexen zu kapieren, eine Ansteuerung
für eine kleine 3x3 LED-Matrix bauen. Im Anhang der "Schaltplan" - ich
betreibe dass Ding an einem Arduiono Duemilanove, programmiert über ISP
mit AVRISP mkII, also am Bootloader vorbei. Deswegen so spartanisch.
Nachfolgend die Software:
/*über iRowCnt als Index werden im Interrupt die Bitmuster für eine jeweils aktive Zeile auf PORTB geschrieben */
14
uint8_tpattern[3][3]={{0,1,1}, \
15
{1,0,1}, \
16
{1,1,0}};
17
intiRowCnt=0;
18
intcnt001=0;
19
ISR(TIMER0_OVF_vect){
20
PORTD=0x00;
21
PORTB=rowSelect[iRowCnt%3];//s.o.
22
23
iRowCnt++;
24
intcnt002=0;
25
for(cnt002;cnt002<3;cnt002++){
26
if(pattern[cnt001%3][cnt002]==1){
27
28
PORTD|=(1<<(cnt002));//wenn '1' im pattern-Array, Bit-# setzen
29
}
30
31
else
32
;
33
}
34
35
36
cnt001++;
37
38
39
40
}
41
42
intmain(void){
43
44
45
DDRB=0x07;// ZEILEN - 0b00000111
46
DDRD=0x3F;// SPALTEN - 0b00111111
47
48
PORTB=0xFF;
49
PORTD=0x00;
50
51
#ifdef MEGA16
52
TCCR0=_BV(CS02);//clk/256 == 122 interrupts/sek
53
timer_enable_int(_BV(TOIE0));
54
#else
55
TCCR0B=_BV(CS02);//clk/256 == 122 interrupts/sek
56
//TCCR0B = _BV(CS00);
57
TIMSK0=_BV(TOIE0);
58
#endif
59
60
sei();
61
62
while(1){
63
asmvolatile("nop");
64
}
65
66
return(0);
67
}
Leider tut das Ganze nicht so wirklich. Es leuchten konstant die linke
und mittlere Spalte. Wenn ich mir die ersten paar Schritte beim Debuggen
im Simulator anschaue, scheint er die Ports aber richtig zu schalten.
Könnte mir jmd. einen Tipp geben? Über einen JTAG-Debugger verfüge ich
leider nicht, und im Simulator macht dass nicht wirklich Spass, man
sieht halt nicht, was dabei rumkommt.
Danke!
Du weißt schon das du die Matrix beim multiplexen dauerhaft neu
ansteuern musst? Warum ist der kram nicht in einer Schleife?
€: Arbeite doch mal mit leerzeilen ^^ das steckt im Timerint, ok vergiss
was da oben steht.
Außerdem muss ich sagen, dass ich den Code besonders oben nicht ganz
kapiere da brauch ich noch etwas Zeit für.
Gruß ErgoProxy
> Du weißt schon das du die Matrix beim multiplexen dauerhaft neu> ansteuern musst? Warum ist der kram nicht in einer Schleife?
Gegen Ansteuerung mit 2 for-Schleifen in main hab ich mich entschieden,
weil es hier in diversen Threads hies, man bräuchte fürs Multiplexen
eine halbwegs konstante Zeitbasis. Und bei Schleifen wäre eine
Abarbeitung in einem konstanten Zeitraum X eben nicht garantiert.
> Außerdem muss ich sagen, dass ich den Code besonders oben nicht ganz> kapiere da brauch ich noch etwas Zeit für.
Wo hakts denn? Ich kann gern nochmal nachkommentieren.
Im Prinzip läufts im Interrupt wie folgt:
1. Ausgänge PortD (Spalten) alle Low
2. Neues Bitmuster für PortB laden (iRowCnt % 3 ergibt einen Wert
kleiner 3, wird am Ende des Interrupts inkrementiert), dadurch wird das
passende Muster für die jeweils aktive Zeile aus dem Array gewählt
3. cnt002 iteriert in der Schleife dann nur noch die jeweils benötigten
Spaltenzustände aus dem Array heraus; wenn Zustand 1 ist, wird das
entsprechende Register im PortD auf High gesetzt, sonst halt nicht.
Also wenn ich das jetzt richtig geschrieben hab (hab leider die Hardware
zum testen ned da) sollte das hier gehen:
Ich hab die Zeiten gelassen usw nur die Ansteuerung ist komplett neu.
Jetzt wird es Zeilenweise angesteuert. D.h. erste Spalte leuchtet, wie
in der Variablen (von hinten nach vorne) ->
erste Spalte zur Zeit:
LED1 aus LED2 an LED3 an
zweite Spalte:
LED1 an LED2 aus LED3 an
3te:
LED1 an LED2 an LED3 aus
1
#define F_CPU 8000000
2
3
//#define MEGA16 // Target
4
#define MEGA168 // Arduino-Testboard
5
6
#include<inttypes.h>
7
#include<avr/io.h>
8
#include<avr/interrupt.h>
9
#include<compat/deprecated.h>
10
#include<util/delay.h>
11
12
volatileuint8_tzeile[3]={0b00000110,0b00000101,0b00000011};//Jede LED 1BIT, ist effizienter als 8Bit für 1 LED
13
volatileuint8_tzeilennummer=0;
14
15
16
17
ISR(TIMER0_OVF_vect){
18
19
//setzt alles auf Ursprung
20
PORTD=0x00;
21
DDRB=0x00;
22
PORTB=0x00;
23
24
PORTD|=(1<<zeilennummer);//Spalte auf high
25
26
for(uint8_ti=0;i<3;i++)//Geht jede LED durch
27
{
28
if(zeile[zeilennummer]&(1<<i))//ist das Bit in der Variable gesetzt, wird die LED eingeschaltet
29
{
30
DDRB|=(1<<i);//Als output (low), Kathode LED auf - LED leuchtet
31
}
32
}
33
zeilennummer++;//nächste Zeile
34
35
if(zeilennummer=4)zeilennummer=0;//ist die Var. größer als die Zeilenanzahl wird sie zurückgesetzt
36
}
37
38
intmain(void){
39
40
DDRD=0b00000111;
41
PORTD=0x00;
42
43
44
#ifdef MEGA16
45
TCCR0=_BV(CS02);//clk/256 == 122 interrupts/sek
46
timer_enable_int(_BV(TOIE0));
47
#else
48
TCCR0B=_BV(CS02);//clk/256 == 122 interrupts/sek
49
//TCCR0B = _BV(CS00);
50
TIMSK0=_BV(TOIE0);
51
#endif
52
53
54
sei();
55
56
while(1){asmvolatile("nop");}
57
}
€: Haken tats an der Art des Schreibens. Ich bin nunmal meine Art des
c-Schreibens gewöhnt, da dauert es was anderes zu verstehn.
So viel ich weiß Steht % doch für den Rest einer Division oder? Wenn ja
würde ich das vermeiden, da divisionen im µC nur umständlich gerechnet
werden können. Das geht auch mit andern Schleifen.
Gruß ErgoProxy
HAB WAS ÜBERSEHN DIE LEDs SIND BEI DIR UMGEKEHRT IN DER SCHALTUNG MUSS
DAS NOCH UMÄNDERN - So fertig spaltennummer könntest du noch durch
zeilennummer ersetzen wär glaube ich übersichtlicher.
Ne so wird das nix.
Die DDRx-Register müssen an der jeweiligen Position eine 1 haben, da
alle Pins als Ausgänge benutzt werden. Einmal für 5V - PORTx =1 und 0V -
PORTx =0.
Damit kann DDRB nicht mit 0x00 initialisiert werden. Und die
Grundeinstellung (alles aus) ist dann PORTD=0xff also auf high und
PORTD=0x00 also auf low. Wenn jetzt die Spalte ausgewählt wird muss der
PIN in PORTD auf 1 gesetzt werden und für die Zeile muss der Pin in
PORTB auf 0 gesetzt werden.
An den DDRx Registern muss also nach der Initialisierung nicht mehr
rumgefummelt werden.
Danke für deine Mühen schonmal!
> HAB WAS ÜBERSEHN DIE LEDs SIND BEI DIR UMGEKEHRT IN DER SCHALTUNG MUSS> DAS NOCH UMÄNDERN
Hab ich schon gemerkt, ist bereits geändert.
> So viel ich weiß Steht % doch für den Rest einer Division oder?
Modulo-Operator halt, jo.
@ich:
Damit wir über den selben Code sprechen:
1
#define F_CPU 8000000
2
3
//#define MEGA16 // Target
4
#define MEGA168 // Arduino-Testboard
5
6
#include<inttypes.h>
7
#include<avr/io.h>
8
#include<avr/interrupt.h>
9
#include<compat/deprecated.h>
10
#include<util/delay.h>
11
12
volatileuint8_tzeile[3]={0b00000110,0b00000101,0b00000011};//Jede LED 1BIT, ist effizienter als 8Bit für 1 LED
13
volatileuint8_tzeilennummer=0;
14
15
16
17
ISR(TIMER0_OVF_vect){
18
19
//setzt alles auf Ursprung
20
PORTD=0x00;
21
PORTB=0xFF;//Zeilen standardmäßig auf HIGH, damit nix anderes blinkt
22
23
PORTB|=(1<<zeilennummer);//nur aktive Zeile auf LOW setzen
24
uint8_ti=0;// Definition direkt in der for-Schleife will mein Compiler nicht
25
for(i;i<3;i++)//Geht jede LED durch
26
{
27
if(zeile[zeilennummer]&(1<<i))//ist das Bit in der Variable gesetzt, wird die LED eingeschaltet
28
{
29
PORTD|=(1<<i);
30
}
31
}
32
zeilennummer++;//nächste Zeile
33
34
if(zeilennummer=4)zeilennummer=0;//ist die Var. größer als die Zeilenanzahl wird sie zurückgesetzt
35
}
36
37
intmain(void){
38
39
DDRB=0b00000111;
40
DDRD=0b00000111;
41
42
43
#ifdef MEGA16
44
TCCR0=_BV(CS02);//clk/256 == 122 interrupts/sek
45
timer_enable_int(_BV(TOIE0));
46
#else
47
TCCR0B=_BV(CS02);//clk/256 == 122 interrupts/sek
48
//TCCR0B = _BV(CS00);
49
TIMSK0=_BV(TOIE0);
50
#endif
51
52
53
sei();
54
55
while(1){asmvolatile("nop");}
56
}
Passt das so? DDRx-Register sind angepasst und werden nur einmal
gesetzt, Rest Andreas' Lösung nachempfunden.
Ich setz die auf Input, dann ist die LED auch aus, da der
Innenwiderstand eines PINs eigendlich hoch genug sein müsste, dass da
nix leuchtet. Außerdem setze ich doch jede die an sein muss auf output.
Glaub ich zumindest ich hab das recht schnell geschrieben und da es für
die falsche LED Richtung war auch nochmal schnell umgeschrieben ^.^
Aber eigendlich müsste es so gehen.
Folgende Zeile im Code von Andreas war problematisch:
1
if(zeile[zeilennummer]&(1<<i))
Erstmal von mir wie folgt erweitert:
1
2
if(zeile[zeilennummer]&(PORTD|=(1<<i)))
(Oder funktioniert auch die erste Version? Imo eher nicht, im Debugger
siehts auch eher nicht danach aus.)
Nun wurde hier ja einfach der Inhalt des pattern-Arrays mit den Bits,
die ich über die Schleife gesetzt hab, UND-Verknüpft.
Wegen der nicht genutzten fünf hohen Bits wird also zwangsläufig etwas
!= 0 rauskommen, sodass die relevanten Bits von PORTD alle HIGH gesetzt
werden (was dann eh nicht mehr notwendig wäre, weil das ja schon im
Schleifenkopf passiert ist - aber das könnte man ja fixen).
Hab ich da was falsch, oder kann das so einfach nicht funktionieren?
Im selben Zug hab ich mich auch von dem eindimensionalen Array getrennt.
Den mehrdimensionalen kann ich einfach durchiterieren, und schauen, ob
ein bit gesetzt ist, oder nicht. Oder gibts auch eine elegante
Möglichkeit, innerhalb weniger Takt den Status einzelner Bits im eindim.
Array zu ermitteln?
Letzte Frage: Ists überhaupt ok, dass alles im Interrups abzuarbeiten?
Bei 16MHz und einem Prescaler von 256 (1024 fängt dann schon arg an zu
flimmern) hab ich ja nur 244 Takte, bis der nächste Interrups
aufschlägt, und ich fertig sein sollte. Aktuell verbrate ich im
Interrups schon 100 davon.
Könnte problematisch werden, wenn die Matrix größer wird, oder?
Oder sollte ich die for-Schleife einfach nach main packen? dann bräuchte
ich ja aber auch wieder eine Art Synchronisierung mit der ISR, damit mir
die Vorberechnung in main nicht davon läuft? Oder gleich einen zweiten
Timer hernehmen?
Schönen Abend noch.
Ich verstehe leider nicht, was an dieser Zeile für den Compiler ein
Problem sein soll:
1
if(zeile[zeilennummer]&(1<<i))
Die Funktion der Zeile ist ja ganz einfach: Es wird überprüft ob an
einer bestimmten Stelle (i) der Variable (zeile[zeilennummer]) eine 1
steht oder nicht.
(0b00001000 & (1<<3)) = false
(0b00001000 & (1<<4)) = true
Mit dem Compiler den ich hab geht das eigendlich. Ich benutz das auch
sehr oft ^.^ habe es sogar mal vor etwa 2 Jahren hier im Forum erfragt
wie man einzelne Bits maskiert und abfragt, um halt nicht für An und
Auszustände jeweils 8Bit zu opfern.
Das was du dann genommen hast mit PORTD usw gehört da nicht hin.
Wenn du halt genügend Ram hast kannst du auch pro LED ein eigenen Char
nehmen. Ich find halt nur es ist Recourcenverschwendung.
Ab einer gewissen Größe wird es nicht mehr möglich sein die Matrix ohne
flackern anzusteuern.
Gruß ErgoProxy
> Mit dem Compiler den ich hab geht das eigendlich.
Knirsch. Mit meinem nun auch, keine Ahnung, was ich vorher verbockt
hatte.
Muss irgendwo beim Debuggen der Kleinigkeiten (in deinem Code oben waren
1-2 kleinere Fehler("zeilennummer = 4") untergegangen sein. Trotzdem
danke.
> (0b00001000 & (1<<3)) = false> (0b00001000 & (1<<4)) = true
Danke, da hatte ich was missverstanden.
> if(zeile[zeilennummer] & (1<<i))
Dazu hätte ich aber noch eine Frage:
Wo landen die Bits, die ich da setze?
Da werden keine Bits gesetzt, da wird nur geschaut ob das Bit an dieser
Stelle gesetzt ist. Sry wegen der Fehler ich hab das in nem normalen
Texteditor abgetippt ^^ da schleichen sich leider manchmal Fehler ein.
Gruß ErgoProxy
@ergoproxy: was muss man den anders machen bzw. ändern um auch größere
Matrixen flackerfrei softwaremäßig anzusteuern? Bin für Tips dankbar:)
Kennt jemand gute Codebeispiele?