Da man eine funktionierende PWM immer gebrauchen kann um mal ein
schnelles LED-Projekt aufzuziehen habe ich mir mal den Soft-PWM Artikel
angeschaut. Allerdings war ich zu "faul" den Code durchzupflücken und
hab mir eine eigene PWM geschrieben. Das widerspricht sich zwar nun aber
meine Priorität lag beim Lerneffekt durch das Selbstschreiben.
Das ganze ist sicherlich noch optimierungsfähig aber es funktioniert auf
meinem ATmega8 bei 8Mhz Systemtakt mit 122Hz PWM-Frequenz.
Es können theoretisch soviele Kanäle wie freie Portpins mit einbezogen
werden. Wenn man noch einen weiteren Port einbeziehen will muss man an
den entsprechenden Stellen den Code selbst dazuprogrammieren, ist
allerdings hauptsächlich eine Sache von Copy&Paste.
Die CPU-Last ist nicht besonders hoch. Verwendet werden können alle
Controller die einen Timer besitzten, der zwei Compare-Register besitzt
und im CTC-Modus laufen kann.
Das Demoprogramm lässt eine Sinuswelle auf 10 LEDs laufen die an PB0-PB7
und PC0-PC1 gegen Masse angeschlossen sind.
lg PoWl
Blöde Sache mit dem Anhang hier.. dann eben als Zip beide in einem.
Hier ist das ganze auch nochmal als Version für einen einzigen Port mit
maximal 8 Kanälen.
Hier nochmal mit 3 Ports und 22 Kanälen. Baut das mal auf, ist wirklich
schön anzusehen :-)
Über Feedback (konstruktive Kritik) freue ich mich natürlich!
lg PoWl
Das kommt von meinem Handy, die meisten Handys speichern ihre Videos in
3gp. Das hat irgendwas mit MPEG4 zu tun, glaub ich zumindest. Der
Qualität und der Dateigröße nach zu Urteile kann die Komprimierung aber
nicht wirklich so hypergut sein.
Ton ist im Video eh keiner drin ausser den Geräuschen die ich beim
Filmen mache ;-)
lg PoWl
Bitte bitte, wollte schon immer mal was coden, das es wert ist, in die
Codesammlung gestellt zu werden :-)
Leider hat sich da noch ein Denkfehler eingeschlichen. Da sind ein paar
unnötige Codezeilen drin, ausserdem noch ein weiterer Fehler, den ich
noch nicht ganz ergründet hab.
Hier nochmal die drei korrigierten Versionen.
Wenn man bei der 3-Port die Delay-Zeit in der while-schleife auf 10ms
setzt dann stockt das ganze ziemlich seltsam. Mit 9ms und 8ms
funktioniert es dann komischerweise wieder, unterhalb 8ms stockt es
wieder. Es ist zwar möglich, dass durch die PWM-Frequenz von 122,5Hz hin
und wieder mal ein Schritt übersprungen wird aber stocken sollte da
eigentlich nichts.
Interessanterweise hilft es, wenn man der COMPB-ISR etwas Arbeit abnimmt
indem man die Masken schon in der pwm_update-Funktion negiert. Ich komme
allerdings nicht drauf was die Ausführzeit der ISR mit der
Updatefrequenz zu tun hat.
lg PoWl
Paul,
erstmal vielen Dank für den Code. Funktioniert wunderbar. Ich habe ihn
umgeschrieben für einen ATMEGA1280 und würde gerne 56 LEDs antreiben.
Leider funktioniert das nicht so wie ich mir das wünsche. Sie blinken
nur nach einem sehr komischen Rhytmus – alle gleichzeitig.
Was mache ich falsch?
Vielen Dank,
Hans
Puh, du benutzt da wirklich verdammt viele Ports. Ich schätze mal, dass
die ISR einfach total überfordert ist und dadurch andere Ereignisse
blockiert. Hast du mal versucht den Code zu simulieren? Wenn ein
PWM-Zyklus durchlaufen wird, müssen an dessen Ende die Zeiger getauscht
werden. Im ungünstigsten Fall muss das nach einem Timer-Takt erledigt
sein, da dann wieder der neue PWM-Zyklus anfängt. Du könntest den
Prescaler des Timers jetzt versuchsweise mal ganz hoch setzen um zu
gucken ob es dann wenigstens normal läuft. Außerdem steht da irgendwo
noch
1
/*
2
// Der ISR etwas Arbeit abnehmen
3
for(uint8_t i=1; i<(CHANNELS + 1); i++)
4
{
5
ptr_PORTA_main[i] = ~ptr_PORTA_main[i];
6
ptr_PORTB_main[i] = ~ptr_PORTB_main[i];
7
ptr_PORTC_main[i] = ~ptr_PORTC_main[i];
8
ptr_PORTD_main[i] = ~ptr_PORTD_main[i];
9
ptr_PORTE_main[i] = ~ptr_PORTE_main[i];
10
ptr_PORTF_main[i] = ~ptr_PORTF_main[i];
11
ptr_PORTG_main[i] = ~ptr_PORTG_main[i];
12
ptr_PORTH_main[i] = ~ptr_PORTH_main[i];
13
ptr_PORTK_main[i] = ~ptr_PORTK_main[i];
14
ptr_PORTL_main[i] = ~ptr_PORTL_main[i];
15
}
16
*/
Da machst du die Kommentarzeichen mal weg und hier
1
ISR(TIMER1_COMPB_vect)
2
{
3
pwm_cycle++;
4
5
PORTA&=~ptr_PORTA_isr[pwm_cycle];
6
PORTB&=~ptr_PORTB_isr[pwm_cycle];
7
PORTC&=~ptr_PORTC_isr[pwm_cycle];
8
PORTD&=~ptr_PORTD_isr[pwm_cycle];
9
PORTE&=~ptr_PORTE_isr[pwm_cycle];
10
PORTF&=~ptr_PORTF_isr[pwm_cycle];
11
PORTG&=~ptr_PORTG_isr[pwm_cycle];
12
PORTH&=~ptr_PORTH_isr[pwm_cycle];
13
PORTK&=~ptr_PORTK_isr[pwm_cycle];
14
PORTL&=~ptr_PORTL_isr[pwm_cycle];
ersetzt du jedes PORTX &= ~ptr_PORTX_isr[pwm_cycle]; durch PORTX =
ptr_PORTX_isr[pwm_cycle];
Soweit ich mich daran erinnern kann, was ich da damals gemacht hab,
müsste das zu ner Verbesserung verhelfen.
Hey Powl,
leider hat das alles nicht so geklappt, aber eigentlich sollte das ja
möglich sein, oder? Alleine die doppelte Rechenleistung (16Mhz) sollte
doch die doppelten Bänke aushalten, oder?
Notfalls hat der ATMEGA1280 auch noch 3 weitere 16Bit Timer an Board.
Ich kann mir leider keinen Reim machen, wie man das Programm verändern
müsste, um mehrer Interrupts zu nutzen… (Bringt das eigentlich was?)
Merci & Tsschüss!
Nein, da du nur einen Prozessor hast. Ob du die Aufgaben in drei ISRs
oder in eine packst dürfte egal sein.
Ich kann dir da jetzt leider nicht helfen, das war schon eine ganze
Weile her seitdem ich mich das letzte mal damit beschäftigt hatte und
auch da habe ich nicht ergründen können warum da teilweise so seltsame
Phänomene auftreten.
Versuch doch mal schrittweise jeweils einen Port hinzuzufügen und guck,
ab wann es nicht mehr funktioniert. Und simulier das mal im AVR
simulator oder sowas, wenn das geht (ging bei mir damals leider nicht,
das AVR studio war einfach zu buggy). Eventuell kollidieren da halt noch
irgendwelche Interrupts miteinander. Anders kann ich mir das nun auch
nicht erklären. Vielleicht dauert auch die Update-routine immens lange
bei den vielen Ports.
Eventuell lässt sich das alles auch noch optimieren. Die Update-Funktion
könnte vielleicht auch ein Double-Buffering vertragen. Während durch die
ISR die Zeiger getauscht werden kann nicht geupdated werden bzw. während
geupdated wird können keine Zeiger getauscht werden.