Forum: Mikrocontroller und Digitale Elektronik 12bit, 9-Kanal Soft-PWM für LEDs auf Mega16


von Christoph S. (mcseven)


Lesenswert?

Morgähn zusammen :)
Ja, ich hab die Suche benutzt. Nein, nichts passendes gefunden. Ich habe 
einen LED-Controller gebaut (3 Kanäle a 3 Farben), der die LEDs dimmen 
soll. Zum Steuern wird ein serielles Protokoll verwendet (momolight, 
R1R2R3G1G2G3B1B2B3 byteweise über 19k2 Baud). Prozessortakt 14,7456 Mhz 
(so dass es vom UART her paßt). Gammakorrektur mit Faktor 2,2 ist 
eingebaut.

Problem: 8 Bit SoftPWM hat bei dunklen Farben viel zu wenig Dynamik, 
die dunkelste Einstellung (also 1:1:1) gibt viel zu helle Farben, 0:0:0 
macht die LEDs aber aus.

Ich möchte deswegen eine 12bit PWM verwenden, die Werte der 
Gammakorrektur kann ich ja entsprechend berechnen. Mein Programm schaut 
in etwa so aus:
1
//Compiler: Codevision, Optimize4Speed
2
3
//Gammakorrektur
4
flash unsigned int gammaArray22[256] = {
5
0,0,1,1,2,3,4,.......,4090,4095
6
};
7
8
//Variablen
9
unsigned int c1r, c1g, c1b;
10
unsigned int c2r, c2g, c2b;
11
unsigned int c3r, c3g, c3b;
12
unsigned int pwmCounter;
13
unsigned char rxCounter;
14
15
// Atmega IO konfigurieren
16
DDRA = 0xFF;
17
DDRB = 0xFF;
18
19
//USART
20
UCSRA=0x00;
21
UCSRB=0x10;
22
UCSRC=0x86;
23
UBRRH=0x00;
24
UBRRL=0x2F;
25
26
//PWM
27
while (1) {
28
29
  //Bei 0 alle einknipsen
30
  if (++pwmCounter==4095) {
31
    pwmCounter=0;
32
    PORTA=0xFF; //hier alle
33
    PORTB=0x80; //hier nur einer
34
  }
35
36
  //Bei match pro Kanal LED wieder ausknipsen
37
  if (pwmCounter==c1r) { PORTA.0 = 0; }
38
  if (pwmCounter==c2r) { PORTA.1 = 0; }
39
  //...
40
  if (pwmCounter==c3b) { PORTB.7 = 0; }
41
42
  //Serielle Behandeln
43
  if (UCSRA&RX_COMPLETE){ //RX_COMPLETE ist von CV definiert
44
    rxCounter++;
45
    if (rxCounter==1) { c1r = gammaArray22[UDR]; }
46
    if (rxCounter==2) { c2r = gammaArray22[UDR]; }
47
    //...
48
    if (rxCounter==9) {
49
      c2r = gammaArray22[UDR];
50
      rxCounter=0;
51
    }
52
  }
53
}

Da das ganze über einen FTDI am USB hängt, hab ich mir Code zur 
Erkennung des Sync-Verlustes gespart. MAcht aber auch nichts, es geht so 
ganz gut, bis auf, dass ca. alle 1/4 Sekunde sporadisch die LEDs 
aufblitzen. Ich hab keinerlei Interrupts an, keine Timer, USART wird 
gepollt. Nunja, ein Blitzen der LEDs ist ja nicht tragbar, aber was 
könnte denn falsch sein?

Danke,
Christoph

von Falk B. (falk)


Lesenswert?

@Christoph Söllner (mcseven)

>Ja, ich hab die Suche benutzt. Nein, nichts passendes gefunden.

Dann hast du schlecht gesucht.

Soft-PWM

>Problem: 8 Bit SoftPWM hat bei dunklen Farben viel zu wenig Dynamik,
>die dunkelste Einstellung (also 1:1:1) gibt viel zu helle Farben, 0:0:0
>macht die LEDs aber aus.

LED-Fading

>Ich möchte deswegen eine 12bit PWM verwenden, die Werte der

Naja, 12 Bit als Soft-PWM wird eng. Denn 100 Hz*4096=40kHz = 25us. Macht 
bei 16 MHz Prozessortakt gerade mal noch 400 CPU Takte pro PWM Takt. 
Geht aber mit der Software im Artikel ;-)

MFG
Falk

von Christoph S. (mcseven)


Lesenswert?

Ja, aber wenn noch der Interrupt von der seriellen dazukommt (oder ein 
positiver Match beim Pollen), dann gibts ja wieder geflacker... Im 
Programm oben, das was mit xx% CPU Last läuft (ist mir aber egal :)) 
flackert nix, wenn kein Empfang auf der seriellen Schnittstelle 
stattfindet.

Das wird ja im Artikel nicht behandelt. Denn wenn grade der Timer 
Interrupt die LEDs setzt und da dazwischen die serielle bedient werden 
möchte, flackerts. Auch würde für eine LED ja die Gammakorrektur mittels 
Array benötigt, das kostet wieder CPU.

Gibts denn sowas nicht in Hardware? Hab so ein 4 Kanal-Ding gefunden, 
das aber nicht mehr hergestellt wird (M66240). Die TI Treiber sind für 
Displays gedacht und erwarten, dass an den Ausgängen Strom fließt. Bei 
mir hängt aber ein MOSFET dran, der ne ganze LED-Leiste treibt. Deswegen 
werden die TI Dinger mit ihrer Fehlerkorrektur (Temperatur, kaputte LED) 
wohl nicht funktionieren.

Ein mega AVR 640 würde diese Anforderung zwar erfüllen, aber weder die 
Platine, noch das Löten dieses Monsters trau ich mir zu. Der 1281 hat 
wieder nur 6 OCXn.

Auch die dsPICs haben "nur" 3 DutyCycle Generatoren und könnten nur eine 
LED-Leiste antreiben, ich hätte aber gerne ein einziges IC :)

Naja, vielleicht habt ihr noch Ideen? Bitte nicht FPGA/CLPD. Ich such 
etwas, was ich mit meinem auf MCU/C beschränkten Wissen ^^ und 
vorhandener IDE (CodeVision, AVR Studio/ WinAVR) benutzen kann.... Also 
einen Chip etwa, dem ich seriell die Daten reinschiebe und der schiebt 
sie per PWM am anderen Ende wieder raus...

Danke, Christoph

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Dann nimm doch keinen Interrupt für die Serielle Schnittstelle, sondern 
polle sie in einer Unterroutine. Die PWM sollte per Timer-ISR laufen und 
hat Vorrang, alles andere darf unterbrochen werden. Bei der nicht allzu 
hohen Baudrate von 19200 ist das kein Problem.

von Falk B. (falk)


Lesenswert?

@Christoph Söllner (mcseven)

>Gibts denn sowas nicht in Hardware? Hab so ein 4 Kanal-Ding gefunden,

Ja, TLC5920 & Co.

>Ein mega AVR 640 würde diese Anforderung zwar erfüllen,

Wieso? Der hat ausser viel Flash und RAM nicht mehr als ein Tiny, was 
Soft-PWM angeht.

>vorhandener IDE (CodeVision, AVR Studio/ WinAVR) benutzen kann.... Also
>einen Chip etwa, dem ich seriell die Daten reinschiebe und der schiebt
>sie per PWM am anderen Ende wieder raus...

Hast du schon. AVR. Geht wunderbar. Für deine 9 Kanäle reicht eine 
kleiner ATtiny2313.

MFG
Falk

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

>Hast du schon. AVR. Geht wunderbar. Für deine 9 Kanäle reicht eine
>kleiner ATtiny2313.

ACK.

von Christoph S. (mcseven)


Lesenswert?

Hi, Danke Dir.
> Dann nimm doch keinen Interrupt für die Serielle Schnittstelle, sondern
> polle sie in einer Unterroutine.
Meinst Du damit eine Schleife (while (1) {...}) des Hauptprogramms?

> Die PWM sollte per Timer-ISR laufen und hat Vorrang,
Wie stelle ich "Vorrang" ein? Interrupt-Priorität kennt ein AVR nicht.

> alles andere darf unterbrochen werden. Bei der nicht allzu
> hohen Baudrate von 19200 ist das kein Problem.
So einfach ist das nicht. Realisiere ich das dritte Beispiel im Artikel 
und es passiert, dass alle Kanäle dunkel sind, dann kommt der Timer IR 
sehr oft hintereinander, der serielle Empfang wird dann oft genug 
unterbrochen, um selbst bei 19200=2400 byte / Sekunde einen Datenverlust 
zu haben.

von Christoph S. (mcseven)


Lesenswert?

@falk
:) Wenn es nur um PWM ginge, habt ihr Recht. Das tut ja. Siehe oben. 
Aber wenn noch Empfang auf der seriellen dazukommt, mit unregelmäßiger 
(wenig Zeit bei Kanal 2-8, mehr Zeit bei Kanal 1 und 9) Bearbeitungszeit 
der seriellen ServiceRoutine (egal ob ISR oder Hauptprogramm), dann 
flackerts... Wie kann ich das denn beheben?

von Bascomer (Gast)


Lesenswert?

Hallo Christoph,

könnte das Flackern vielleicht von Seiteneffekten der seriellen 
Empfangsroutine verursacht werden? Du benutzt schließlicht 16-Bit 
Zahlen.

von Falk B. (falk)


Lesenswert?

@Christoph Söllner (mcseven)

>> polle sie in einer Unterroutine.
>Meinst Du damit eine Schleife (while (1) {...}) des Hauptprogramms?

Ja.

>Wie stelle ich "Vorrang" ein? Interrupt-Priorität kennt ein AVR nicht.

Indem du keinen anderen Interrupt nutzt, auch nicht für den UART.

>sehr oft hintereinander, der serielle Empfang wird dann oft genug
>unterbrochen, um selbst bei 19200=2400 byte / Sekunde einen Datenverlust
>zu haben.

Mit welchem Takt läuft dein AVR? Für 12 Bit PWM brauchst du schon 16 
MHz.

>Aber wenn noch Empfang auf der seriellen dazukommt, mit unregelmäßiger
>(wenig Zeit bei Kanal 2-8, mehr Zeit bei Kanal 1 und 9) Bearbeitungszeit
>der seriellen ServiceRoutine (egal ob ISR oder Hauptprogramm), dann
>flackerts...

Dann ist deine Routine noch nicht so wie sie sein soll.

Poste mal VOLLSTÄNDIGEN Code im Anhang.

MFG
Falk

von Christoph S. (mcseven)


Angehängte Dateien:

Lesenswert?

Naja, die Empfangsroutine ist ja oben zu sehen. Es hatte keine Rolle 
gespielt, ob ich die in der Hauptschleife oder einer ISR arbeiten lasse. 
Tatsache ist mMn eben, dass der USART mit seinem unregelmäßigen 
Rechenzeitverlangen die 12bit PWM sporadisch unmöglich macht.

Deswegen auch meine Frage, ob es Alternativen gäbe. Ein 640 hätte 
nämlich 12 16bit Output Compares und würde das Problem nicht haben, 
wegen Hardware PWM. Aber das ist ja der reinste Overkill :)

Was ich heute abend noch probieren werde, ist, ob es nicht ausreicht, 
den USART immer bei "0" des pwmCounter zu pollen: 14745600/4096 = 3600, 
bei kontinuierlichem Senden würden aber "nur" 2400 Bytes/sek reinkommen, 
und wegen des PC-Programms zum steuern, dass nur alle 10ms die 9 Byte 
sendet, sogar noch viel weniger. Aber ich fürchte, dass es auch da 
sporadisch zu hellen Flecken kommt, werde aber nochmal berichten.

@falk
Code anbei, es werden nur die ersten 6 Variablen auf R4, R6, R8, R10, 
R12, R14 gemappt, alles andere kommt in RAM, vermutlich, weil der 
Compiler den Rest an Registern für seine Makros braucht.

von Alex Wenger (Gast)


Lesenswert?

Hi,


ich würde die PWM im Timerinterrupt machen.

Die serielle in der Main() pollen und dabei gleich die gammakorrektur 
anwenden und die Bits so umsortieren das kein IF mehr im Interrupt 
notwendig ist.

Du hast ja PORTA und PORTB verwendet, also kannst Du eine Routine haben 
die du in der main() aufruftst die etwa so aussieht:

uint16_t led_array[12];

SetLed(uint8_t num, uint16_t value)
{
   for (i = 0; i < 12; i++)
   {
      if ((_BV(i) & value) != 0)
         led_array[i] |= _BV(num);
      else
         led_array[i] &= !_BV(num);
   }
}

im Interrupt dann sowas:

Int()   // Sollte ein 16Bit Timer sein!
{
   static uint8_t bit_count;
   static uint16_t timer_count;

   bit_count++;
   if (bit_count >= 12) bit_count = 0;

   PORTA = led_array[bit_count] & 0xff;
   PORTB = led_array[bit_count] >> 8;

   TCNT1 += 1 << bit_count;
}

dabei den Timer so einstellen das er abwärts zählt und immer bei 0 einen 
Interrupt auslöst.

Gruß Alex

von Falk B. (falk)


Lesenswert?

@ Christoph Söllner (mcseven)

>Naja, die Empfangsroutine ist ja oben zu sehen. Es hatte keine Rolle
>gespielt, ob ich die in der Hauptschleife oder einer ISR arbeiten lasse.

Das ist aber ein Unterschied.

>Tatsache ist mMn eben, dass der USART mit seinem unregelmäßigen
>Rechenzeitverlangen die 12bit PWM sporadisch unmöglich macht.

Das ist ja auch ein Fehler. Wurde mehrfach gesagt. Die PWM muss in die 
Timer ISR, um in regelmässigen Anständen aufgerufen zu werden. Der Rest 
bleibt in der Hauptschleife. Kann man noch ein wenig verbessern. Anstatt 
der vielen If ein einfacher Array-Zugriff.

Aber es fehlt eine Synchronisarion deiner UART-Daten. Woher weisst du, 
wo in der Reihenfolge Byte 0 ist? Das kann Flackern bzw. direkte 
Farbfehler erzeugen.

>wegen Hardware PWM. Aber das ist ja der reinste Overkill :)

Ein wenig ;-)

>Was ich heute abend noch probieren werde, ist, ob es nicht ausreicht,
>den USART immer bei "0" des pwmCounter zu pollen: 14745600/4096 = 3600,

Falsch. Dort fehlt die PWM-Freqeuenz von z.B. 100 Hz.

>bei kontinuierlichem Senden würden aber "nur" 2400 Bytes/sek reinkommen,
>und wegen des PC-Programms zum steuern, dass nur alle 10ms die 9 Byte
>sendet, sogar noch viel weniger.

Egasl, du musst aber die 9 Byte in einem Ruscht empfangen.

> Aber ich fürchte, dass es auch da
>sporadisch zu hellen Flecken kommt, werde aber nochmal berichten.

Denk mal drüber nach. Das funktioniert soo einfach nicht. Aber pollen 
ist der richtige Weg.

MFG
Falk

von Christoph S. (mcseven)


Lesenswert?

> Aber es fehlt eine Synchronisarion deiner UART-Daten. Woher weisst du,
> wo in der Reihenfolge Byte 0 ist? Das kann Flackern bzw. direkte
> Farbfehler erzeugen.
Ja, ein Schwachpunkt des Momolight-Protokolls, nachdem der serielle Weg 
aber nur ein paar cm lang ist, war's mir egal. Und bei 8 Bit PWM hatte 
er auch nach 11h Film gucken ^^ keinen Sync-Verlust.

Wenn es mit 16bit geht, würde ich einen 2. Timer einbauen, der beim 
Empfang eines Bytes auf 0 gesetzt wird und nach 5ms den rxCounter wieder 
auf 0 setzt. Die PC-SW sendet ja nur alle 10ms die 9 Bytes. 9 Bytes 
dauern 9/2400= 0,00375s, wenn also 1 Bytes flöten geht, würde nach 0,005 
rechtzeitig der Index vor dem nächsten Schwung bei 0,010s zurückgesetzt.

von Falk B. (falk)


Lesenswert?

@Christoph Söllner (mcseven)

>Wenn es mit 16bit geht,

16 Bit?

> würde ich einen 2. Timer einbauen, der beim
>Empfang eines Bytes auf 0 gesetzt wird und nach 5ms den rxCounter wieder
>auf 0 setzt.

Ja, per Timeout kann man recht leicht synchronisieren. Kannst du aber 
auch iohen extra Timer machen, einfach in den bestehen TImerinterrupt 
für die PWM reinpacken. Der wird dann zwar mit 40kHz gezählt, macht aber 
nix.

MFG
Falk

von Christoph S. (mcseven)


Angehängte Dateien:

Lesenswert?

Hi, habe jetzt mal Programm wie im Anhang "verbrochen". Aber es will 
immer noch nicht. Sobald schnelle Farbwechsel kommen, flackern die LEDs 
sporadisch kurz (~0.5s) hintereinander mit voller Helligkeit auf, was 
insbesondere bei dunklen Szenen sehr nervig ist.

Das Flackern ist unabhängig vom Abstand der 9Byte-Pakete zueinander (von 
10ms bis 100ms einstellbar).

Wo hab ich denn den Denkfehler?

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

@  Christoph Söllner (mcseven)

>Das Flackern ist unabhängig vom Abstand der 9Byte-Pakete zueinander (von
>10ms bis 100ms einstellbar).

>Wo hab ich denn den Denkfehler?

In der Synchronisation von neuen Datenpaketen und deiner PWM. Die PWM 
darf erst dann einen neuen Datensatz "ansaugen", wenn sie einen vollen 
Zyklus erledigt hat. Während dieser Zeit muss deine UART-Empfangsroutine 
die Daten in einen dritten Puffer zeischenspeichern oder muss warten. 
Sonst verschluckt sich die PWM mit den von dir beobachteten Effekten. 
Das hast du bisher nur begrenzt gemacht. Denn in dem Moment, wo gerade 
das 9. Byte per UART empfangen wurde kann es sein, dass die PWM gerade 
neu angefangen hat (loopCounter =1). Dann dauer es ~10ms, bis die neuen 
Daten übernommen werden können. Du schreibst währenddessen aber schon 
wieder Daten in den aktuellen Puffer. Das knallt.

Denn überleg mal. Wen deine Daten im 10ms Raster = 100 Hz ankommen, muss 
mit jedem PWM-Zyklus ein neuer Datensatz aktiviert werden. Das geht aber 
nur, wenn Datenquelle und PWM synchron laufen. Das kann man z.B. 
erreichen, indem deine PWM als Monoflop läuft. Sprich, wenn ein 
kompletter Datensatz empfangen wurde, wird dieser EINMALIG als PWM 
ausgegeben. Ein neuer PWM-Zyklus startet nur dann, wenn neue Daten 
angekommen sind. Jetzt ist deine PWM voll synchron zum Datenatrom. Nun 
musst du nur noch mit 100 Hz Daten senden und alles ist OK.

Ausserdem hast du noch ein paar kleine Fehler drin. In der ISR liest du 
mehrfach PINC und PINA, wo du aber sicher PORTC und PORTA meinst. Ist 
hier im Ergenis gleich, kann aber bisweilen zu komischen Effekten 
führen.

Ich bin mit nicht sicher, ob es eine Gute Idee ist,  takeNewValuesOK  in 
der Hauptschleife zu löschen. Das kann im Ausnahmefall ins Auge gehen 
und die PWM verschluckt sich. Besser so.

Ausserdem sollte man für sowas den CTC Modus verwenden, macht die Sache 
einfacher und genauer.

Probier mal den Code im Anhang, den Namen des Vektors hab ich mal 
geraten.

MFG
Falk

von Christoph S. (mcseven)


Angehängte Dateien:

Lesenswert?

Hi, jetzt hab ich mich zwei Tage lang im Bastelkeller verbuddelt, und 
wieder einiges zu berichten :)

Also Dein Code hatte nicht sooo viel ausgemacht, ich denke zwischen CTC 
und normalem Overflow Modus besteht wenig Unterschied, bei beiden muss 
ich ein 16bit Register setzen. Das Flackern war noch vorhanden.

Entscheidend war aber die Idee mit dem Tripple-Buffering, ich hab das 
Codevision Programm einmal angepaßt (siehe Anhang). Tut einwandfrei, ist 
aber noch immer nicht perfekt.

Ich möchte schließlich ein Ambi-Light bauen, und keine 
Disko-Beleuchtung. Bei 75 Hz waren mit der Software maximal 512 Schritte 
(9bit) möglich, immer noch zu wenig Dynamik bei dunklen Farben.

Im Artikel Soft-PWM gibts ja noch nen dritten Ansatz, der hat aber 
nur 8 Kanäle und zudem gibt's keine Möglichkeit, den Pin für einen Kanal 
zu konfigurieren. Es soll ja so sein, dass sich die Software der 
Hardware anpaßt und nicht umgekehrt :) Es gab also einiges zu ändern und 
langer Rede kurzer Sinn: so ist's meinem Perfektionismus gerecht 
geworden (siehe nächster Artikel)...

von Christoph S. (mcseven)


Angehängte Dateien:

Lesenswert?

Also hier jetzt wie versprochen die finale Version. Ich kann einstellen, 
wieviele Kanäle ich haben möchte (1-16), kann für jeden Kanal das 
Bitmuster für maximal 2 Ports definieren (bin also frei in der Zuordnung 
von Kanal zu Portpin(s)) und kann bei 19k2 bps mit 100Hz die Datenpakete 
von Boblight (Version von 2007, Visual Studio Programm) in Empfang 
nehmen.

Wie im Vorgänger hab ich drei Buffer für die Helligkeitswerte (MAIN, 
SERIAL, TISR) und jeweils zwei für die Bitmuster pro PWM-Schritt (MAIN, 
TISR).

Funktion:
1. Timer1 läuft im Interruptbetrieb mit 1024 PWM Schritten und lädt
   sich für jeden Schritt der PWM a) den Zeitwert des Schrittes, b)
   das auszugebende Bitmuster für PORT0 und c) das auszugebende Bit-
   muster für PORT1.
   Dann einfach nur noch CompareA auf den Wert setzen, Timercounter
   auf 0 zurücksetzen, die Bitmuster auf die Ports ausgeben.

2. USART läuft auch im Interruptbetrieb und schreibt die eintreffenden
   Helligkeitswerte (0-255) über die Gammakorrektur (0-1023) in den
   seriellen Puffer. Wenn 9 Bytes eingetroffen sind, wird das Hauptpro-
   gramm benachrichtigt.

3. Hauptprogramm: Es wartet darauf, den seriellen Buffer mit dem MAIN
   Buffer zu vertauschen, dann werden auf dem MAIN Buffer und aus den
   MAIN Portbitmustern jeweils die Zeiten und die Bitmuster für die
   PWM Schritte berechnet und sobald der Timer mit einem PWM Zyklus
   fertig ist, diese in die TISR Werte übernommen.

Durch USART Interrupt geht so kein Byte verloren, und es kann höchstens
passieren, dass einmal ein kompletter Frame vom USART verloren geht, 
aber das ist besser, als ein Flackern der LEDs. Ich wollte noch 
ausprobieren, ob auch 2048 Schritte gehen, um die LEDs noch dunkler zu 
machen, aber das gibts erst nach Weihnachten :)

von Falk B. (falk)


Lesenswert?

@  Christoph Söllner (mcseven)

>Also Dein Code hatte nicht sooo viel ausgemacht, ich denke zwischen CTC
>und normalem Overflow Modus besteht wenig Unterschied,

Jain.

> bei beiden muss
>ich ein 16bit Register setzen.

Abert beim CTC ist das Timing 100% sauber, bei deiner Relaod Methode ist 
der Abstand der Timerinterrupts bisweilen abhängig von evtl. anderen 
aktiven Interrupts und der Interrupt-Reaktionszeit.

> Das Flackern war noch vorhanden.

Klar.

>nur 8 Kanäle und zudem gibt's keine Möglichkeit, den Pin für einen Kanal
>zu konfigurieren.

Kann man ja spielend ändern. Einfach statt 8 Bit Werten 16 oder 32 Bit 
Werte nehmen. Und statt der linearen Bitmaskenberechung (Schieben) feste 
Muster in einem Konstantenarray ablegen. Hast du ja fast so gemacht.

>1. Timer1 läuft im Interruptbetrieb mit 1024 PWM Schritten und lädt
>   sich für jeden Schritt der PWM a) den Zeitwert des Schrittes, b)

Ist aber immer noch ungünstig, dein Timing ist nicht wasserdicht. Mach 
es wie im Artikel. Einfacher und besser.

>2. USART läuft auch im Interruptbetrieb und schreibt die eintreffenden
>   Helligkeitswerte (0-255) über die Gammakorrektur (0-1023) in den
>   seriellen Puffer. Wenn 9 Bytes eingetroffen sind, wird das Hauptpro-
>   gramm benachrichtigt.

Schlecht. Wenn dir der UART-Interrupt in den Timer-Interrupt reinspuckt 
hast du vor allem bei dunklen Farben (kleine Pulsbreiten) deutliche 
Störungen. Mach es per Polling in der Hauptschleife, die CPU hat sowieso 
nix zu tun ;-)

>3. Hauptprogramm: Es wartet darauf, den seriellen Buffer mit dem MAIN
>   Buffer zu vertauschen, dann werden auf dem MAIN Buffer und aus den
>   MAIN Portbitmustern jeweils die Zeiten und die Bitmuster für die
>   PWM Schritte berechnet und sobald der Timer mit einem PWM Zyklus
>   fertig ist, diese in die TISR Werte übernommen.

Ob das mit deinem Tripple-Buffering jetzt wasserdicht kann ich auf die 
Schnelle nicht überblicken. Aber ist alles in Allem schon mal ein 
grosser Schritt in die richtige Richtung.

Frohes Fest und Happy LED-Dimming
Falk

von Christoph S. (mcseven)


Angehängte Dateien:

Lesenswert?

Hi, jetzt ist's wieder ein paar Basteltage her :) Und wieder kann ich 
berichten etwas mehr aus dem Reiche der LED-Dimmung.

Langer Rede kurzer Sinn: Es geht mit 75 Hertz bei 2048 PWM-Schritten und 
die Dynamik am unteren Ende ist in Ordnung. Subjektiv sieht man's aber 
gaaaanz leicht flimmern. Also noch nicht optimal.

> Abert beim CTC ist das Timing 100% sauber, bei deiner Relaod Methode ist
> der Abstand der Timerinterrupts bisweilen abhängig von evtl. anderen
> aktiven Interrupts und der Interrupt-Reaktionszeit.
Mag sein. Ich hab's also umgestellt auf CTC 12 mit ICR1 als 
Compare-Register. Läuft auch recht sauber, der Frequenzzähler vom TEC 
zittert nur auf der 4. Stelle hinterm Komma, das sollte zu verschmerzen 
sein :)

> Schlecht. Wenn dir der UART-Interrupt in den Timer-Interrupt reinspuckt
> hast du vor allem bei dunklen Farben (kleine Pulsbreiten) deutliche
> Störungen. Mach es per Polling in der Hauptschleife, die CPU hat sowieso
> nix zu tun ;-)
Wage gleich zweimal widersprechen zu dürfen, wenn ich die serielle polle
und zufällig gerade die Timer ISR dran ist und das Programm dann in der 
Hauptschleife nicht schnell genug zum Abfragen der Seriellen kommt, 
verliert man die Synchronisation. Passiert bei 1024 Schritten 
gelegentlich, bei 2048 nach spätestens 2 Sekunden.

Auswirkung bei USART ISR-Betrieb: Selbst wenn Start/Ende einer 
LED-Einschaltzeit ein wenig jittern (sieht man am Oszi recht gut: Ohne 
Daten steht die PWM wie eine eins, mit Daten über USART wie gesagt 
Jitter an der 4. Stelle hinterm Komma), so sieht man das nicht.

Es kommt also weniger darauf an, wieviel die ALU im Schnitt zu tun hat, 
sondern vielmehr, wie die einzelnen Ereignisse zeitlich zueinander 
gesehen abgearbeitet werden können. Schließlich kann sie sich nicht 
zweiteilen =)

> Ob das mit deinem Tripple-Buffering [...]
Ja, das sollte soweit passen.

Nun zum Eingemachten: Ich möchte gerne die PWM-Frequenz erhöhen. Wenn 
die 2m breite LED-Leiste nämlich passiv an eine Wand strahlt, dann sieht 
man auch bei 75Hz gaaanz leicht flimmern.

Ich hab die PWM-Formeln aus dem Software-Artikel übernommen, die machen 
ja Sinn. Außerdem hab ich die Cycles der Timer-ISR im Simulator gemessen 
und komme auf 89. Wenn ich also die PWM-Frequenz erhöhen möchte, muss 
die Timer ISR weiter optimiert werden, und ich sehe da als einzige 
Möglichkeit nur noch, das (nur im ASM/*.lss) sichtbare Sichern der 
Register auszuschalten.
Unter CodeVision ging das mit "#pragma regsave off" oder so ähnlich. Wie 
kann ich das denn im WinAVR machen und wie kann ich dann dem Compiler 
sagen, er soll nicht die Register verwenden, die er gesichert hätte 
(die werden ja offensichtlich woanders gebraucht, sonst würden sie ja 
nicht gesichert)?

Es würden 32 Takte eingespart werden können und das würde dann locker 
für die 100 Hz reichen...

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.