Guten Tag, ich habe ein kleines Problem mit der Sofware PWM, die hier in den Artikeln beschrieben wird. Ich habe den Quellcode zur intelligenten Lösung 1 zu 1 von hier übernommen. http://www.mikrocontroller.net/articles/Soft-PWM Klappt auch ansich ganz wunderbar. 0 ist wirklich Null und das Oszi zeigt eine saubere Linie. Das andere extrem funktioniert leider nicht richtig. Die PWM hat 8 Bit (256 Schritte), macht für mich als höchsten einstellbaren Wert 255. Hier sollte das Oszi ja eigentlich eine glatte Linie bei 5V zeigen. Jedoch hat man alle 10ms einen sehr kurzen Peak Richtung Masse drin. An der Hardware liegt es nicht. Das manuelle Setzen auf High- oder Low-Pegel klappt und auch eine einfachere Software PWM funktioniert ohne diesen Fehler. Da ich zum einen diese Software auf 16 Kanäle und 10 Bit erweitern wollte, bin ich schon auf eine performante Software PWM angewiesen. Zum anderen ist das anzusteuernde Element Low-Aktiv, weshalb dieser Fehler nun wirklich störend ist. Wirklich abschalten kann man so nicht. Auch verstehe ich die funktionsweise dieser PWM noch nicht so ganz und bin somit auf euere Hilfe angewiesen. MfG Jürgen
Die PWM funktioniert absolut korrekt. Wenn du mal ins Atmel Datenblatt schaust, wirst du sehen, dass die normalen PWM Ausgänge auch dieses Phänomen haben (solang du die nicht im Glitch free Modus laufen lässt). Du kannst ja einfach abfangen ob der Wert 255 ist und dann die Software PWM "ausschalten". Sprich den Pin dauerhaft auf 1 setzen. Eventuell reicht es aber auch den PWM Zähler nur von 0-254 zählen zu lassen, Je nach Software (Hab oben in den Artikel nicht reingeguckt).
ich denke da verstehst du was falsch:
>> Das andere extrem funktioniert leider nicht richtig. Die PWM hat 8 Bit (256
Schritte), macht für mich als höchsten einstellbaren Wert 255. Hier sollte das
Oszi ja eigentlich eine glatte Linie bei 5V zeigen.
Ein PWM-Kanal ist kein "echter" DAC!
das sagt ja niemand, er hat das osci halt als logic-analyzer benutzt um sich das PWM-Signal anzuschauen. Das Problem ist halt, das man bei pwmwert==curStep abschaltet das funtktioniert bei 0, bei 255 gibts da halt die lücke, wenn man erst bei pwm-wert>curStep abschaltet gehts bei 0 nicht, dafür schaltet er bei 255 nicht ab. man kann das nur mit einer Speziallösung für den Wert 255 umgehen, indem man bei 255 einfach gar nicht den Pin abschaltet. Das brauch dann aber natürlich ein µ mehr rechenzeit
Ok, das mit dem exkludieren von 255 als Wert aus der PWM ist sicherlich eine Lösung. Ich kann mir aber immer noch nicht erklären, warum diese Software dieses Verhalten aufweist. Einen Zusammenhang zwischen der Hardware PWM und diesem Programm kann ich jedenfalls nicht erkennen. Grüße Jürgen
Wenn du erreichen willst, dass eine PWM bei 0..255 zwischen "ganz aus" und "ganz an" schwankt, dann muss die Periode 255 Takte betragen, nicht 256, sonst fehlt immer an einem Ende etwas. 255 von 256 Takten sind halt einer zuwenig, wenn 0 für 0 Takte steht. Merke: 0..256 sind 257 mögliche Werte. Hardware-PWMs sind da auch nicht besser.
Ja, aber 256 klappt mit der Software nicht als Wert. Das gibt nur ein wildes Geflacker. 255 ist der höchste einstellbare Wert. Stellt sich natürlich die Frage ob man da irdenwo dran drehen kann. Muss ich mir morgen mal in Ruhe anschauen.
Ok, ich verstehe immer noch nicht wirklich, wie dieses Programm arbeitet oder warum es zu diesen Spikes kommt. Hat sonst noch jemand eine Idee, wie man das fixen könnte? Grüße Jürgen
JayDee schrieb: > Hat sonst noch jemand eine Idee, > wie man das fixen könnte? Warum sollte man das? Das ist ja nur in Deinem Kopf ein Problem. Keiner merkt, daß die LED mit max 99,6% Helligkeit leuchtet. Peter
Hi. Änder mal die Interrupt routine:
1 | ISR(TIMER1_COMPA_vect) { |
2 | static uint8_t pwm_cnt; |
3 | uint8_t tmp; |
4 | |
5 | OCR1A += isr_ptr_time[pwm_cnt]; |
6 | tmp = isr_ptr_mask[pwm_cnt]; |
7 | |
8 | if (pwm_cnt == 0) { |
9 | PWM_PORT = tmp; // Ports setzen zu Begin der PWM |
10 | pwm_cnt++; |
11 | }
|
12 | else { |
13 | PWM_PORT &= tmp; // Ports löschen |
14 | if (pwm_cnt == pwm_cnt_max-1) { // <-- HIER ÄNDERN!!! -1 |
15 | pwm_sync = 1; // Update jetzt möglich |
16 | pwm_cnt = 0; |
17 | }
|
18 | else pwm_cnt++; |
19 | }
|
20 | }
|
hAb den Code jetzt auch nicht 100 5ig durch, es könnte eber hier haken. Kann aber auch sein, dass es dann gar nicht mehr geht. habs nicht ausprobiert ;-) Bernhard
Im Artikel stehen diverse Lösungen drin, welche du verwendet hast weiss ich also nicht. Zur Illustration nehme ich mal die erste Variante "Einfacher Lösungsansatz". Da kommt raus: für 0: 256x low, 0x high, für 255: 1x low, 255x high. Variante für für 0: 255x low, 0x high, für 255: 0x low, 255x high:
1 | ISR(TIMER1_COMPA_vect) { |
2 | static uint8_t pwm_cnt=0; |
3 | uint8_t tmp=0, i=0, j=1; |
4 | |
5 | OCR1A += (uint16_t)T_PWM; |
6 | |
7 | for (; i<8; i++) { |
8 | if (pwm_setting[i] > pwm_cnt) tmp |= j; |
9 | j<<=1; |
10 | }
|
11 | PWM_PORT = tmp; // PWMs aktualisieren |
12 | if (pwm_cnt==(uint8_t)(PWM_STEPS-2)) //<===== war: PWM_STEPS-1 |
13 | pwm_cnt=0; |
14 | else
|
15 | pwm_cnt++; |
16 | }
|
Bernhard Mayer schrieb:
> hAb den Code jetzt auch nicht 100 5ig durch, es könnte eber hier haken.
Ich glaube, deine Shift Taste setzt aus ;)
Magnus Müller schrieb: > Bernhard Mayer schrieb: >> hAb den Code jetzt auch nicht 100 5ig durch, es könnte eber hier haken. > > Ich glaube, deine Shift Taste setzt aus ;) Hoffentlich wars die Taste und nicht der Finger ;-)
Bernhard Mayer schrieb:
> Hoffentlich wars die Taste und nicht der Finger ;-)
Kann man sich gegen solche Ausfälle (Gliedmaßen) nicht versichern? ;o)
Korrekter ist es natürlich, wenn PWM_STEPS nie grösser wird als 255. Dann steht der Bereich 0..PWM_STEPS für ganzaus..ganzan und die Periode passt auch wieder. Der Kernfehler liegt darin, dass PWM_STEPS=256 zwar intuitiv nahe liegt, aber mit diesem 8bit basierten Code nicht vollständig funktionieren kann.
Also, ich habe die Version mit dem intelligenten Lösungsansatz aus dem oben genannten Artikel verwendet. Mein Problem an der Sache ist, daß ich einen Low-Aktiven Treiber damit ansteuern will. Wenn die PWM also nicht permanent High-Pegel halten kann kriegt man den Treiber nie abgeschaltet. Klar jetzt könnte man noch einen inverter davor setzen, aber Softwarelösungen sind billiger. Zurück zum Thema. Die Software bietet 256 Stufen für die PWM an. Das ist eine 8-Bit Zahl. 0 ist alles aus 255 sollte alles an sein. Sind genau 256 Schritte. Gibt man 255 als Wert für die On-Time an, so hat man halt alle 10ms kurze Peaks im Signal, obwohl ja jetzt permanente On-Time sein sollte. 256 ist kein korrekter Wert für die PWM. Passt ja schon nicht mehr in die 8Bit Zahl und das merkt man auch. Bei 256 gibt es auf allen Ports nur noch wildes geflacker. Mein Problem ist, daß ich die Funktionsweise dieser intelligenten Lösung nicht völlig durchblicke und halt nicht weiß, wo ich eingreifen muss um aus 255 als Wert für die On-Time ein permanentes High-Pegel Signal zu erzeugen. Ich habe jetzt mal den Vorschlag die ISR abzuändern durchprobiert. Klappt aber bei der Version mit dem intelligenten Lösungsansatz alles nicht. Danach klappt die ganze PWM nicht mehr richtig.
JayDee schrieb: > 256 ist kein korrekter Wert für die PWM. Passt ja schon nicht > mehr in die 8Bit Zahl und das merkt man auch. Bei 256 gibt es auf allen > Ports nur noch wildes geflacker. Du musst die Periode der PWM (PWM_STEPS) auf 255 reduzieren.
255 ist sowieso der Maximalwert mit dem ich die Software anspreche. 256 war nur ein Test der wie es zu erwarten ist auch nicht funktioniert, da daß ja keine 8 Bit Zahl mehr ist.
Ok, ich konnte das Problem jetzt auf die ISR Routine eingrenzen. Selbst wenn ich den Port von Hand auf High-Pegel setze und nicht über die PWM setzen lasse, habe ich diese kurzen Peaks Richtung Low-Pegel drin. Der Effekt verschwindet, sobald ich die ISR auskommentiere. Jetzt muss ich mir mal klarmachen, wieso das Programm den Else Teil der ISR Aufruft und wie man es so umbauen kann, daß die Kanäle die permanente On-Time haben sollen davon nicht betroffen sind. Die IST sieht übrigens so aus
1 | ISR(TIMER1_COMPA_vect) { |
2 | static uint16_t pwm_cnt; |
3 | uint16_t tmp; |
4 | |
5 | OCR1A += isr_ptr_time[pwm_cnt]; |
6 | tmp = isr_ptr_mask[pwm_cnt]; |
7 | |
8 | if (pwm_cnt == 0) { |
9 | PWM_PORT = tmp&0x07; // Ports setzen zu Begin der PWM |
10 | PWM_PORT2 = (tmp>>3)&0x3F; |
11 | PWM_PORT3 = (tmp>>4)&0xE0; |
12 | pwm_cnt++; |
13 | }
|
14 | else { |
15 | PWM_PORT &= tmp&0x07; // Ports löschen |
16 | PWM_PORT2 &= (tmp>>3)&0x3F; |
17 | PWM_PORT3 &= (tmp>>4)&0xE0; |
18 | if (pwm_cnt == pwm_cnt_max) |
19 | {
|
20 | pwm_sync = 1; // Update jetzt möglich |
21 | pwm_cnt = 0; |
22 | }
|
23 | else pwm_cnt++; |
24 | }
|
25 | |
26 | }
|
Die oben vorgeschlagenen Lösung mit if (pwm_cnt == pwm_cnt_max) in if (pwm_cnt == pwm_cnt_max-1) zu ändern stoppte das Programm ja leider völlig. Grüße Jürgen
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.