Da ich mich darüber aufgeregt hab, dass ich bei meiner Binär-Uhr, an der
ich 17 Pins für LEDs benötige, genau einen Pin zu wenig frei hab, hab
ich im Internet mal nach Charlieplexing gegoogelt und wenigstens
folgende Seite gefunden:
http://www.josepino.com/pic_projects/index?how_control_leds.jpc
Ich frag mich nun ob es irgendwie möglich ist dafür einen einigermaßen
intelligenten Algorithmus in C zu schreiben. Hab mich vorhin nur mal
kurz mit 3-Pin Charlieplexing für 6 LEDs beschäftigt und bin zum
Ergebnis gekommen, dass es da verschiedene LED- Paare gibt, die jeweils
gleichzeitig leuchten können, somit wär es möglich alle LEDs in nur 3
Charlieplex-schritten zum leuchten zu bringen.
Natürlich besteht auch die Möglichkeit, die LEDs einfach alle einzeln
hintereinander aufleuchten zu lassen, allerdings erhöht sich dabei der
Spitzenstrom durch einen PortPin weil eine der 6 LEDs jeweils nur 1/6
der Zykluszeit an sein kann. Mit intelligenterem Verfahren, d.h. man
findet raus welche Paare gleichzeitig angesteuert werden können.
Anstatt, dass nun zwei LEDs mit dem 6-fachen Standardstrom
hintereinander aufleuchten, können nun zwei LEDs mit dem 3-fachen
Standardstrom gleichzeitig und doppelt so lange aufleuchten.
Das Problem ist halt, es kann nicht jedes X-beliebige Paar aufleuchten.
Man beachte dieses Bild:
http://www.josepino.com/pic_projects/control_leds/led-table.gif
gleichzeitig gehen immer nur
A & D
A & E
C & E
C & B
B & F
F & D
wenn man nun eine Speicherstelle hat in der die Bits die leuchtenden
LEDs repräsentieren, dann müsste man erstmal die leuchtenden Paare
rauspicken bzw das ganze irgendwie aufteilen.
Hier leuchten z.B. alle Paare:
ABCDEF
111111
A E
D F
BC
A D
B F
C E
Einzelne LEDs können ja ausgeblendet werden, ich schätze jedoch bei 4
oder 5 Portpins wird das ganze noch wesentlich komplizierter.
Meint ihr das ist hinzubekommen, so dass es im µC noch mit ausreichender
Geschwindigkeit läuft? LEDs ein und ausschalten ist ja relativ
langweilig, eine 8-Bit software PWM wär dazu auch noch ganz nett.
Ist alles nur eine Idee aber vielleicht lohnt es sich, dieser mal
nachzugehen.
Vorteile:
- Weniger Spitzenstrom => Mehr LEDs treibbar bzw. heller
- Eventuell sogar schneller
Nachteile:
- Aufwändiger Algorithmus
lg PoWl
schau mal hier rein:
Beitrag "Glühwürmchen in Rotkohlglas gefangen"
Beitrag "10 Duo LEDs mit ATTiny15 über 5 Pins steuern"
im ersten Projekt werden an 4 Pin also 3*4=12 LEDs getrieben, im 2.
Projekt an 5 Pins = 4*5=20 LEDs.
Bei dem Glühwürmchenprojekt wurde in WinAVR GCC und die zeitkritischen
ISR in Assembler programmiert. Mit 8 Mhz Takt und Prescaler 64 bekommt
man bei 12 LEDs eine Wiederholrate von 122 Hz. Alle PWM Berechnungen und
das "Nachladen" der PWM Muster (Wavetable) findet im Himtergrund in den
ISRs statt und die Auslastung der MCU beträgt dabei maximal 1.48%.
Beide Projekte benutzen Charlieplexing. Für die Anzeige in deinem
Projekt kannst du auch mit kürzeren Dutycycles noch gute Ergebnisse im
Leuchten der LEDs erzielen.
Du kannst im Glühwürmchenprojekt in der TOV ISR natürlich das Laden der
PWM Muster für die LEDs aus der Wavetable rausbauen, sind die ersten
drei ASM Blöcke im Code. Stattdessen baust du im SRAM eine Tabelle für
deine 12/20/30 LEDs in denen der aktuelle Dutycycle als Byte
abgespeichert wird. Das wird den Code dann sogar einfacher und schneller
machen, so ca. 0.8% Rechenleistung der MCU wäre dann noch nötig für die
PWM.
>Nachteile:
>- Aufwändiger Algorithmus
nicht grundsätzlich. Der Algo. ist ansich ziemlich einfach, wenn man ihn
erstmal durchschaut hat. Dessen Umsetzung würde ich aber nicht pur in C
realisieren da du mit ca. 50% Unoptimierungsoverhead im Vergleich zu
handmade Assembler erwarten musst (so war es bei meiner ersten reinen C
Version des aktuellen Glühwürmchencodes). Am Ende benötigte die Routine
in Assembler mit 2 ISRs um die 200 Taktzyklen.
Achso: beide Projekt können die Helligkeit der LEDs auch einstellen.
Falls dies für dein Projekt nicht erforderlich ist so vereinfacht sich
die Sache nochmals gewaltig.
Du könntest auch alle möglichen Leuchtmuster der LEDs offline
vorausberechnen und im FLASH als Tabelle abspeichern. Dann nur noch die
Timer ISR programmieren die für das aktuelle Zeichen diese Muster
nacheinander aus dem FLASH lädt und die LEDs am Port ansteuert.
Gruß Hagen
Stimmt, eigentlich muss man ja nur in einer Tabelle die LED-"paare" und
die jeweilige Kombination zum Einstellen der I/O Ports einspeichern. Bei
meinem Dreifach-Charlieplexing wären das.
A & D
B & F
C & E
Bei einem Vierfach-Charlieplexing könnten sogar drei LEDs gleichzeitig
leuchten.
Eine Frage bevor ich mir das anschau noch: Wird das bei dir "berechnet"
welche Paare jeweils zusammen leuchten können? Oder leuchtet sowieso
immer nur eins?
Oder wird mit einer Tabelle gearbeitet so wie ich das grad beschrieben
hab?
lg PoWl
>Eine Frage bevor ich mir das anschau noch: Wird das bei dir "berechnet"
>welche Paare jeweils zusammen leuchten können? Oder leuchtet sowieso
>immer nur eins?
>Oder wird mit einer Tabelle gearbeitet so wie ich das grad beschrieben
>hab?
Du kannst es berechnen oder als Tabelle machen, es hängt eigentlich nur
davon ab an welchen Pins und Ports du die LEDs angeschlossen hast.
Angenommen du benutzt nur PORTB und alle PINs aufsteigend beginnend bei
PB0 bis PB?. Dann kann man das super einfach berechnen, denn wenn PB0
auf Ausgang/High ist dann können die anderen Pins PB1 bis BP? nur auf
Ausgang/Low oder High-Z/Eingang geschaltet sein, jenachdem ob die LEDs
die von PB0 mit iherer Anode hin zu den restlichen Pins PB1 bis PB?
leuchten sollen oder nicht.
Schau dir den 2. Link oben an da fiondest du einen Schaltplan.
Zeichne dir auf einem Blatt zb. 5 waagerechte Linien. Dann bename diese
Linien links von PB0 bis PB4 durch. Nun zeichnest du von PB0 Linie zu
allen anderen Linien 4 Dioden mit Anode an PB0 Line ein. Dann mit PB1
Linie das gleiche Spiel, von dieser 4 Dioden nach PB0,PB2,PB3,PB4
einzeichnen, jeweils Anode an PB1 Linie. Und das machst du so weiter bis
du bei PB4 fetig bist. Und schwups hast du 5*(5-1) = 20 LEDs
eingezeichnet.
Wenn nun PB0 auf Ausgang/High ist dann kann ein Strom durch die Dioden
nach PB1,PB2,PB3,PB4 fließen. Jenachdem ob diese Pins nun auf
Ausgang/Low oder Eingang/High-Z geschaltet sind leuchten diese LEDs auf.
Bei 5 Pins und 20 LEDs hast du also 5 Unterteilungen=Zeilen mit jeweils
4 LEDs die gleichzeitig leuchten können. Nach 5 Multiplexingschritten
ist ein kompletter Durchlauf fertig. Heist das in diesem Fall die LEDs
mit 20% Duty angesteuert werden können. Bei 20mA pro Pin also bei einer
LED ein Dauerstrom von 4mA, und wenn alle 4 gleichzeitig leuchten sollen
dann 1mA Effektivstrom im Dauerbetrieb. Die meisten LEDs leuchten dann
aber noch ordentlich.
Bei so einer Anordnung kann man natürlich alles sehr einfach berechnen,
siehe 2. Projekt in dem die Zeile=Pin der grade treibt in einem Register
gespeichert wurde und in jedem Schritt einfach geshiftet/rotiert wird.
Dieses Zeilenregister wird auch als AND-Maske benutzt um das DDRB
Register korrekt anzusteueren.
In beiden Projekten benutze ich eine kleine Tabelle im FLASH die die
anzusteuerenden Pins enthält. Damit kann man in Grenzen die Zuordnung
der LEDs zu einem "Index" festlegen und verändern, also die Reihenfolge
mit der die LEDs angesteuert werden. Das geht natürlich nur Zeilenweise
umzusortieren. Der zweite Vorteil dieser Tabelle ist das man nun Pins an
einem PORT oder mehren PORTs benutzen kann die nicht sequentiell
aufeinanderfolgend sind. Zb. im Glühwürmchenprojekt habe ich
PB0,PB1,PB2,PB5 benutzt, also nicht PB4.
Gruß Hagen
Du hast also folgendes (wir bleiben man bei obigen Beispiel mit PORTB
und PB0-PB4).
1.) Zeilenregister = 0x01 und dieser Wert landet immer in PORTB
2.) Spaltenmaskeregister = xx or Zeilenregister und dere landet immer in
DDRB
1.) Phase, Zeile PB0 treibt 4 LEDs nach PB1-PB4
PB0 auf Ausgang/Highlevel
PB1-PB4 im DDRB Register jenachdem ob sie leuchten sollen = 1 oder nicht
= 0.
PORTB = (1 << PB0)
DDRB = (1 << PB0) | (? << PB1) | (? << PB2) | (? << PB3) | (? << PB4)
2.) Phase, Zeile PB1 treibt 4 LEDs nach PB0,PB2-PB4
PORTB = (1 << PB1)
DDRB = (? << PB0) | (1 << PB1) | (? << PB2) | (? << PB3) | (? << PB4)
3.) Phase, Zeile PB2 treibt 4 LEDs nach PB0,PB1,PB3,PB4
PORTB = (1 << PB2)
DDRB = (? << PB0) | (? << PB1) | (1 << PB2) | (? << PB3) | (? << PB4)
4.) Phase, Zeile PB3 treibt 4 LEDs nach PB0,PB1,PB2,PB4
PORTB = (1 << PB3)
DDRB = (? << PB0) | (? << PB1) | (? << PB2) | (1 << PB3) | (? << PB4)
5.) Phase, Zeile PB4 treibt 4 LEDs nach PB0,PB1,PB2,PB3
PORTB = (1 << PB4)
DDRB = (? << PB0) | (? << PB1) | (? << PB2) | (? << PB3) | (1 << PB4)
erkennst du das Muster ?
Kurz also so
1.) in jeder Phase rotiere einen Wert von 1 nach 2 nach 4 nach 8 und
schreibe es in PORTB
2.) berechne DDRB Wert und OR-verknüpfe PORTB-Wert mit diesem und
schreibe ihn in DDRB
Nachteile:
- an den 5 Pins müssen die LED Vorwiderstände
- somit fließt zb. bei PB0 = on ein Strom über diesen Vorwiderstand über
maximal 4 LEDs und zurück über 4 identische Vorwiderstände in die 4
anderen Pins. Der Strom wird begrenzt durch den Vorwiderstand an PB0 und
teilt sich durch 4 auf.
- die Helligkeit der LEDs sinkt also immer dramatischer je mehr Pins man
benutzen möchte
- auf Grund dessen das wir mit 3 Pin Zuständen arbeiten müssen ->
Ausgang/High + Ausgang/Low und Eingang/High-Z kann man nicht so ohne
weiteres mit externen Treibertransistoren/MOSFETs arbeiten.
- sollte zb. bei PB0=on nicht alle 4 LEDs leuchten so fließt ein
größerer Einzelstrom über diejenigen LEDs die leuchten sollen in der
aktuellen Phase, ergo bei einer LED fließt der 4 fache Strom im
Vergleich wenn alle 4 LEDs leuchten. Man müsste dies kompensieren indem
man die Leuchtzeit an die Anzahl der leuchtenden LEDs anpasst. Leuchten
alle 4 LEDs in einer Phase so ist die Leuchtzeit maximal lang, leuchtet
nur einer der 4 LEDs so sollte die Leuchtdauer nur noch 1/4'tel betragen
Gruß Hagen
Stimmt, das ist gut.
Zu dem Stromproblem: Das liegt ja aber nur darin begründet, dass ein
gemeinsamer Vorwiderstand für eine variable Anzahl an LEDs (variablem
Strom) verwendet wird. Entsprechend ist auch der Spannungsabfall immer
unterschiedlich. Das ganze per Software zu kompensieren ist mir in
meinem Stadium noch etwas zu kompliziert ;-) Wie wäre es, wenn man jeder
LED einen eigenen Vorwiderstand verpasst? Das ist zwar nicht besonders
elegant, dürfte das Problem aber lösen.
Noch eine Allgemeine Frage: Wenn ich an PB0 Low habe, an PB2 High und
PB1 als Eingang schalte. Würde nicht theoretisch ein Strom über die
beiden, nun in Reihe geschalteten LEDs zwischen PB0 und PB2 fließen?
lg PoWl
>Wie wäre es, wenn man jeder
>LED einen eigenen Vorwiderstand verpasst? Das ist zwar nicht besonders
>elegant, dürfte das Problem aber lösen.
Nein hilft nicht sonderlich, denn der Ausgangs-Pin ist der einzigste Pin
der treibt und dessen Strom ist im AVR auf 20mA begrenzt (bzw. sollte
begrenzt werden). Du müsstest dann schon den Vorwiderstand so hoch
machen das nur maximal 1mA fließen kann, wenn wir bei obigen Beispiel
bleiben.
>Noch eine Allgemeine Frage: Wenn ich an PB0 Low habe, an PB2 High und
>PB1 als Eingang schalte. Würde nicht theoretisch ein Strom über die
>beiden, nun in Reihe geschalteten LEDs zwischen PB0 und PB2 fließen?
Jain ;) Es gewinnt die Diode zwischen PB2 und PB0 und leuchtet. Die
Dioden zwischen PB2-PB1 und PB1-PB0 sind in Serie, haben doppelt so hohe
Durchbruchspannung und benötigen den doppelten Strom im Vergleich zur
Diode zwischen PB2-PB0. Sie leuchten also nicht da Dioden quasi den
Strom "aufsichziehen".
Es ist ansich ja kein Problem die Leuchtdauer innerhalb einer
Multiplexingphase zu regeln. Wenn man die ISRs in meinem
Glühwürmchenprojekt umbaut dann hat man eine PWM=Helligkeit pro LED die
man von 0 bis 256 einstellen kann. Nun unterteilt man diese PWM in 4
Bereiche, man kann also die Leuchtstärke der einzelnen LEDs nur von 0
bis 63 einstellen und hat noch 4 weitere PWM Schritte zur Verfügung um
die Anzhal der gleichzeitg leuchtenen LEDs ausgleichen zu können. Diese
Unterteilung ist recht simpel, man multipliziert den Helligkeitwert der
zwischen 0 bis 63 liegen kann einfach mit der Anzahl (0-3) der gerade
leuchtenden LEDs und subrahiert das von 255 und schups hat man den
korregierten PWM Timerwert um ein gleichmäßiges leuchten zu bekommen.
Somit kannst du jede einzenle LED noch in iherer Helligkeit in 64
Schritten einstellen, sinnvoll nutzbar dürften wohl dann 32 Schritte
sein (logarithmisches Helligkeitsempfinden unserer Augen).
Ich empfehle aber es erstmal in der Praxis auszuprobieren, denn du wirst
feststellen das selbst mit der einfachen Methode (ohne Korrektur) und
entsprechenden Vorwiderständen du denoch ein gleichmäßiges Leuchten
hinbekommst.
Gruß Hagen
Ja, dennoch verliere ich dadurch an PWM-Auflösung, die gerade bei den
unteren Helligkeitswerten ziemlich wichtig ist wegen der
Empfindlichkeitskennlinie des Auges.
Wieso gerade 1mA? Wenn der Port 20mA treiben kann und jeweils maximal 4
LEDs gleichzeitig an sind dann gibt das bei mir 5mA :-)
Ich bin schon dabei mir auf meinem Steckboard einen kleinen Test
aufzubauen.
lg PoWl
>Wieso gerade 1mA? Wenn der Port 20mA treiben kann und jeweils maximal 4
>LEDs gleichzeitig an sind dann gibt das bei mir 5mA :-)
korrekt, aber diese 4 LEDs sind nur 1/5'tel der Zeit wirklich on, da wir
ja 20 LEDs multiplexen in 5 Phasen a 4 LEDs. Ergo: 5mA / 5 = 1mA
Effektivstrom. Die maximale Helligkeit der so multiplexten LEDs ist
identisch zu 1mA Dauerstrom.
Ich habe schon einigemale dieses Verfahren zum Treiben verschiedenster
LED-Gimmecks benutzt, siehe die beiden Links oben. Bisher habe ich keine
Unterscheide in den Helligkeiten pro Column bemerkt, egal ob nun 1,2,3,4
LEDs on waren. Deswegen meinte ich ja das du für dein Projekt es einfach
mal ausprobieren solltest. Ich denke ja das du garkeine PWM für die
individuelle Helligkeitregelung der LEDs benötigst. Dh. mit der
Korrektur pro Column über die Anzahl der ON-LEDs hast du pro Phase 4 PWM
Stufen * 5 Phasen = 20 Timerzyklen um alle 20 LEDs ein/ausschalten zu
können und das mit gleichen Helligkeiten.
Gruß Hagen
ach so meintest du das ;-) ok ich probiere mal und melde michw enn ich
fertig bin. morgen schau ich mir auch dein projekt genauer an
So, ich habe mir hier alles nochmal durchgelesen. Also es wird wohl so
laufen, dass ich da nicht viel berechnen kann und einfach aus einer
Tabelle das jeweilige Ansteuermuster rausfischen muss.
Beispiel: 4fach Charlieplexing
Dass ich einen der 4 Pins auf Masse treibe und dann jeweils noch 3
andere LEDs habe, die ich damit zum leuchten bringen kann, ist klar.
Allerdings brauche ich eine Routine, die die entsprechenden Bits der
Speicherstelle, die angibt, welche LEDs grade leuchten, den
entsprechenden Phasen zuteilt.
Bzw. wenn ich da einfach eine 1 durch den Port hindurchshifte, muss ich
mir die entsprechenden Bits der Speicherstelle rauspicken.
Wenn ich den Schaltplan mal nach dem Schema von dir aufmale und von
links nach rechts mit ABCD... die LEDs durchnummeriere komme ich auf
folgende Tabelle:
IO1 IO2 IO3 IO4 LEDs:
1 0 0 0 A C E
0 1 0 0 B G I
0 0 1 0 D H K
0 0 0 1 F J L
In der Speicherstelle werden die einzelnen Bits den jeweiligen Lichtern
von links nach rechts zugeordnet:
ABCDEFGHIJKL
Jedoch muss ich nun die jeweiligen Leuchtbits auf die einzelnen Phasen
aufteilen:
ABCDEFGHIJKL
1. A C E
2. B G I
3. D H K
4. F J L
Wenn ein IO-Pin auf Vcc liegt, dann repräsentieren die anderen Pins, die
auf Masse liegen, die LEDs die leuchten, und die, die Hochohmig
geschaltet sind, diejenigen, die nicht leuchten.
IO1 IO2 IO3 IO4
- A C E
B - G I
D H - K
F J L -
Ob ich die PORT und DDR register Werte vorraus- oder on-the-fly berechne
ist ja wurscht. Es bietet sich an sie vorraus zu berechnen um
zwischendurch Rechenleistung zu sparen.
Was bleibt mir aber anderes übrig die einzelnen Leuchtbits auf die
entsprechenden Phasen "von Hand" zu verteilen? Die Muster sind ja immer
unterschiedlich. Entweder viele Ifs nach dem Motto:
1 | // wenn A -> 1 dann IO2 -> 1
| 2 | if(bit_is_set(leuchtbits, 1)) { Phase1_DDR |= (1 << 2) }
| 3 | // wenn C -> 1 dann IO3 -> 1
| 4 | if(bit_is_set(leuchtbits, 3)) { Phase1_DDR |= (1 << 3) }
| 5 | // wenn E -> 1 dann IO4 -> 1
| 6 | if(bit_is_set(leuchtbits, 5)) { Phase1_DDR |= (1 << 4) }
|
...usw...
lg PoWl
Schau dir mal den Glühwürmchensource an, in FireFly.h findest du
1 | // LED Rows, Pins connected to LEDs
| 2 | #define R0 (1 << PB0)
| 3 | #define R1 (1 << PB1)
| 4 | #define R2 (1 << PB2)
| 5 | #define R3 (1 << PB5)
| 6 |
| 7 | prog_uint8_t ddr_data[16] = {
| 8 | R0, R0^R1, R0^R2, R0^R3,
| 9 | R1, R1^R0, R1^R2, R1^R3,
| 10 | R2, R2^R0, R2^R1, R2^R3,
| 11 | R3, R3^R0, R3^R1, R3^R2};
|
Das ist für 12 LEDs an 4 Pins. ddr_data[] enthält die Definitionen
welche Pins zu steuern sind. Der erste Wert (1. Spalte mit R0, R1 usw.)
ist der Wert den du direkt in PORTB setzt. Die nachfolgenden 3 Werte
sind für DDRB und beziehen sich auf jeweils eine LED.
In Phase 1. also PORTB = R0 und DDRB = R0^R1 or R0^R2 or R0^R3 wenn alle
3 LEDs leuchten sollen.
Gruß Hagen
ja klar aber darum ging es mir nicht, sondern, dass in einer phase nicht
A, B C, in der nächsten nicht D, E, F Leuchten, sondern dass die Lichter
bunt auf die Phasen aufgeteilt sind. Wenn ich nun in einer
Speicherstelle die Bitinformationen der Reihenfolge nach ABCDEFG.. drin
stehen hab, dann muss ich die einzelnen Bits ihrer eigenen Phase
zuordnen. Und welcher Phase die an welche Position zugeordnet werden
müssen, das muss ich ja im vorraus bestimmen. Entweder ich mach das mit
ganz vielen IFs, oder ich lege mir eine tabelle an in der drin steht
welches bit zu welcher phase und an welche position gehört.
lg PoWl
Achso, das ist einfach. Du benutzt ein Array das als Mapping zwischen
logischer LED zu Index in Timer-PWM-Phase dient. Dieses Array benutzt du
beim Setzen der PWM bzw. ON/OFF einer logischen LED.
Beispiel:
1 | uint8_t LED_PWM[12];
| 2 | uint8_t LED_IDX[12] = {0,3,5,7,1,4,8...}
| 3 |
| 4 | main() {
| 5 |
| 6 | LED_PWM[LED_IDX[8]] = 123;
| 7 | }
|
Gruß hagen
So, das hier ist mal mein test-code:
1 | #include <avr/io.h>
| 2 | #include <util/delay.h>
| 3 |
| 4 | uint16_t lights;
| 5 |
| 6 | uint8_t phase_DDR[4];
| 7 |
| 8 | uint8_t map[] = {
| 9 | 0, 1,
| 10 | 1, 0,
| 11 | 0, 2,
| 12 | 2, 0,
| 13 | 0, 3,
| 14 | 3, 0,
| 15 | 1, 2,
| 16 | 2, 1,
| 17 | 1, 3,
| 18 | 3, 1,
| 19 | 2, 3,
| 20 | 3, 2
| 21 | };
| 22 |
| 23 | int main(void)
| 24 | {
| 25 | uint16_t counter;
| 26 | uint8_t phase;
| 27 | uint8_t port;
| 28 |
| 29 | uint8_t a;
| 30 | uint8_t b;
| 31 |
| 32 | while(1)
| 33 | {
| 34 | phase_DDR[0] = 0;
| 35 | phase_DDR[1] = 0;
| 36 | phase_DDR[2] = 0;
| 37 | phase_DDR[3] = 0;
| 38 |
| 39 |
| 40 | a = 0;
| 41 | for(b=0; b<12; b++)
| 42 | {
| 43 | if(lights & (1 << b))
| 44 | {
| 45 | phase_DDR[map[a]] |= (1 << map[a+1]);
| 46 | }
| 47 |
| 48 | a += 2;
| 49 | }
| 50 |
| 51 |
| 52 | phase = 0;
| 53 | port = 1;
| 54 |
| 55 | for(counter=0; counter<100; counter++)
| 56 | {
| 57 | PORTA = port;
| 58 | DDRA = phase_DDR[phase] | port;
| 59 |
| 60 | if(phase < 3)
| 61 | {
| 62 | phase++;
| 63 | port <<= 1;
| 64 | }
| 65 | else
| 66 | {
| 67 | phase = 0;
| 68 | port = 1;
| 69 | }
| 70 |
| 71 | _delay_ms(1);
| 72 | }
| 73 |
| 74 | lights++;
| 75 |
| 76 | if(lights == 0b0001000000000000)
| 77 | {
| 78 | lights = 0;
| 79 | }
| 80 | }
| 81 | }
|
Scheint zu funktionieren, die Leuchtstärke der LEDs ändert sich wirklich
kaum, wenn ich die Vorwiderstände noch kleiner mach (sind im moment 220
Ohm bei 5V) dann müsste der Effekt durch die Empfindlichkeitskennlinie
des Auges noch kleiner werden..
Als nächstes kommt dann noch die PWM und ein Timer ins Spiel :-) ich
freue mich schon darauf über das Thema später einen Artikel auf meiner
Homepage zu verfassen^^ finde das interessant. Danke für die Hilfe!
Btw: Ich habs erst mit phase_DDR[map[a++]] |= (1 << map[a++]); versucht,
hat jedoch nicht funktioniert. Ist das nicht C-konform? In PHP gehts,
wobei PHP auch sehr tolerant gegenüber nicht standardkonformem
Programmierstil ist, deshalb frag ich an dieser Stelle nochmal.
lg PoWl
Paul Hamacher wrote:
> Btw: Ich habs erst mit phase_DDR[map[a++]] |= (1 << map[a++]); versucht,
Uff, mit den unary operators stellst du dir nur Beinchen. Bist du dir
sicher, dass es das machen soll, was du denkst?
Ich glaube zu wissen, dass es nicht legal ist eine variable den
postfix-operator ++ oder -- auf der linken Seite, sowie auch auf der
rechten Seite anzuwenden.
Ich weiß es aber nicht. Und ehrlich gesagt würde ich es eh nicht
behalten, wenn ich es wüsste, da ich so etwas nie schreiben würde ;)
Meiner Meinung nach eher verwirrend und fehlerträchtig (erst recht, wenn
nicht im C-Standard festgeschrieben).
Deshalb immer weise anwenden diese operators. Ein gutes Beispiel dass
mir gerade einfällt und meiner Meinung nicht legal ist, ist folgendes:
ist vielleicht nicht gültig, würde aber sinn ergeben.
a = a++;
Weise der Variable a den Inhalt a zu und Inkrementiere a.
a = ++a;
Inkrementiere a und weise der Variable a diesen Wert zu.
Beides praktisch natürlich quatsch. Aber meinetwegen programmier ichs
auch getrennt :-)
So, ich wollte mein Charlieplexing gerademal auf meinem ATtiny26 um ein
Fading erweitern. Allerdings erweißt sich das als nicht allzu einfach.
Gelegentlich flackert jedoch eine LED-Gruppe (bestehend, bei meinem
4-Fach Charlieplexing, aus 3 LEDs). Das seltsame daran ist, dass das
während dem Fading passiert, und zwar dann wenn OCR1A irgendwo zwischen
0 und 255 liegt, der Leuchtstärke nach eher mittig, manchmal jedoch hell
wenn es ganz dunkel ist.
Ich kann mir das nicht so wirklich erklären. Irgendwo ist da der Wurm
drin. Es flackert immer zu anderen Zeitpunkten und mit anderen
LED-Gruppen. Der Fehler ist reproduzierbar, d.h. wenn ich die
Stromversorgung kurz trenne und wieder anschließe, finden die gleichen
Flackereffekte nach der gleichen Zeit statt und alles läuft wieder
genauso ab. Ich schätze, es gibt hier wohl eine Überschneidung zwischen
dem Hauptprogramm, das alle 10ms das OCR1A register inkrementiert bzw.
dekrementiert. Weil der Counter das OCR1A register beim dekrementieren
eventuell schon überholt hat?
Der AVR-Simulator bringt mich auch nicht weiter, da dauert es, wenn ich
breakpoints setze, vom anfang der Overflow-ISR bis zum Anfang der
Compare-ISR bei Compare-Match von 0 oder 1 immer konstante 128 Takte
(was ja zu lang wäre)!? Auch wenn ich in die Overflow-ISR noch was
einbaue.
1 | #include <avr/io.h>
| 2 | #include <avr/interrupt.h>
| 3 | #include <util/delay.h>
| 4 |
| 5 |
| 6 | volatile uint16_t lights = 0b0000111111111111;
| 7 |
| 8 | volatile uint8_t phase_DDR[4];
| 9 |
| 10 | volatile uint8_t phase = 0;
| 11 | volatile uint8_t port = 1;
| 12 |
| 13 |
| 14 | uint8_t map[] = { 0, 1, 1, 0, 0, 2, 2, 0, 0, 3, 3, 0, 1, 2, 2, 1, 1, 3, 3, 1, 2, 3, 3, 2 };
| 15 |
| 16 | int main(void)
| 17 | {
| 18 | // Timer aktivieren
| 19 | TCCR1B = (1 << CS12) | (1 << CS11) | (1 << CS10);
| 20 | TIMSK = (1 << OCIE1A) | (1 << TOIE1);
| 21 | OCR1A = 0;
| 22 |
| 23 |
| 24 | // Daten fürs Charlieplexing vorbereiten
| 25 | phase_DDR[0] = 0;
| 26 | phase_DDR[1] = 0;
| 27 | phase_DDR[2] = 0;
| 28 | phase_DDR[3] = 0;
| 29 |
| 30 | uint8_t a;
| 31 | uint8_t b;
| 32 |
| 33 | a = 0;
| 34 | for(b=0; b<12; b++)
| 35 | {
| 36 | if(lights & (1 << b))
| 37 | {
| 38 | phase_DDR[map[a]] |= (1 << map[a+1]);
| 39 | }
| 40 |
| 41 | a += 2;
| 42 | }
| 43 |
| 44 | sei();
| 45 |
| 46 | uint8_t fade = 0;
| 47 |
| 48 | // Fading-Schleife
| 49 | while(1)
| 50 | {
| 51 | if(fade)
| 52 | {
| 53 | OCR1A--;
| 54 |
| 55 | if(OCR1A == 0)
| 56 | {
| 57 | fade = 0;
| 58 | }
| 59 | }
| 60 | else
| 61 | {
| 62 | OCR1A++;
| 63 |
| 64 | if(OCR1A == 255)
| 65 | {
| 66 | fade = 1;
| 67 | }
| 68 | }
| 69 |
| 70 | _delay_ms(10);
| 71 | }
| 72 | }
| 73 |
| 74 | ISR(TIMER1_CMPA_vect)
| 75 | {
| 76 | PORTA = 0;
| 77 | }
| 78 |
| 79 | ISR(TIMER1_OVF1_vect)
| 80 | {
| 81 | PORTA = port;
| 82 | DDRA = phase_DDR[phase] | port;
| 83 |
| 84 | if(phase < 3)
| 85 | {
| 86 | phase++;
| 87 | port <<= 1;
| 88 | }
| 89 | else
| 90 | {
| 91 | phase = 0;
| 92 | port = 1;
| 93 | }
| 94 | }
|
lg PoWl
Ändere folgendes:
1.) globale Variable
volatile uint8_t OCR1AValue = 0;
2.) in Main nun OCR!A Zugriff ersetzen durch OCR1AValue Zugriff
3.) in ISR(TIMER1_CMPA_vect) Zeile hinzufügen mit
OCR1A = OCR1AValue;
Nun ist das Updating des OCR1A Registers synchron zum Timer1 und es kann
nicht zu deinen besagten Aussetzern kommen. Die passieren nämlich immer
dann wenn der in Main() OCR1A verkleinert wird und kleiner als der
aktuelle TNCT1 Wert wird. Dann läuft der Timer nicht mehr bis OCR1A
sondern er läuft darüber hinaus bis der Timer1 überläuft und erst nach
diesem Überlauf wird er wieder durch das OCR1A register als neuen TOP
begrenzt.
Wenn ich deinen Source richtig interpretiere dann änderst du über das
OCR1A Register die Helligkeiten aller LEDs. Dh. alle LEDs, wenn sie ON
sind, leuchten mit der gleichen Helligkeit. Mit deinem Source kannst du
also nicht jede LED individuell in der Helligkeit steuern.
Gruß Hagen
Dankeschön, auf solche einfachen Ideen komm ich irgendwie garnicht. Ich
hätte da jetzt eine aufwändige Überprüfung, ob der Timer das OCR1A
Register schon überholt hat, reingebaut. Dabei wäre ich auf die Idee
gekommen, eine Variable vom Timer steuern zu lassen die angibt, wann der
Timer bereit für eine Änderung des OCR1A Registers ist. Unnötig
kompliziert^^
Nach einer weiteren kleinen Änderung in der ISR funktioniert nun alles
ohne flackern :-)
1 | ISR(TIMER1_OVF1_vect)
| 2 | {
| 3 | if(OCRvalue > 0)
| 4 | {
| 5 | PORTA = port;
| 6 | DDRA = phase_DDR[phase] | port;
| 7 | }
| 8 |
| 9 | OCR1A = OCRvalue;
|
danke!
lg PoWl
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|