Guten Abend Also ich habe vor einiger Zeit mit AVR angefangen. So mit meinem neuen Wissen würde ich gern ne Idee von mir in die Tat umsetzen d.h. u.a. SoftwarePWM auf 6 Kanälen ---> tja das ist nicht so einfach. Habe das Forum durchsucht, ohne Erfolg. Suche ein Beispiel/Tutorial wo SoftwarePWM auf mehreren Kanälen erklärt wird und das in Assembler --> mit C Codes (wie z.B. hier im Forum) komme ich nicht wirklich klar. In meinem Buch "Franzis` AVR-RISC Mikrocontroller" ist ein Beispiel drin, tja da würde ich 24Register verbrauchen --> nicht akzeptabel (der AVR soll später noch mehr können d.h dazu brauche ich Register) Habe auch schon unter Google gesucht --> tja der Erfolg war nett so besonders. Kann mir jemand weiter helfen ??? herzlichen Dank an alle User.
Hi... Du brauchst erstmal einen Timer-Interrupt, der alle n Takte aufgerufen wird. (Der Einfachheit halber ersmal Timer0-Überlauf.) In der ISR dazu zählst du ein (oberes) Register (nennen wir es PWZ, PWM-Zähler) hoch und vergleichst es mit dem Endwert (Konstante). Ist der Endwert (Zählumfang) erreicht, so wird PWZ auf 0 gesetzt, damit von vorn gezählt wird. Außerdem werden alle 8 PWM-Ausgänge aktiviert. Nun wird in jedem Int-Durchlauf PWZ nacheinander mit den 8 Sollwerten verglichen, die in 8 (unteren) Registern stehen, und bei Gleichheit (besser beim Überschreiten) wird der zugehörige PWM-Ausgang zurückgesetzt. Das kostet ein Zählregister und 8 Sollwertregister, also 9 Register. Dazu kommt noch ein Register für den Timer-Reload und ein Register zum Sichern des SREG. Das sind dann 11 Register und immernoch keine 24... Im Hauptprogramm kannst du dann die Sollwerte manipulieren, Sollten diese per ADC (Potis) eingelesen werden, könnte das auch in der ISR geschehen. Dazu bei jedem PWZ-Überlauf (also wenn PWZ auf 0 gesetzt wird) ADC auslesen, PWM-Sollwert errechnen, per Y-Pointer (indiziert) in das entsprechende Register schreiben und danach Y-Pointer und ADC-Quelle (ADMUX) auf den nächsten einzulesenden Wert einstellen, so dass beim nächsten PWZ-Überlauf der nächste ADC-Wert verfügbar ist. Sind alle ADC-Eingänge abgeklappert, beginnt man von vorn (Vergleich von YL mit Konstante). ...
Moin... Ich kann das Addieren in den ASM-Beispielen (optimierte Lösung für AVR) in Peters Link (noch) nicht nachvollziehen. Das "Einsammeln" der Carry-Zustände ist aber verdammt gut, spart gewaltig Rechenzeit. Ich stelle mal eine weitere 8-Kanal-Software-PWM im Anhang zur Diskussion. Ist mit Mega8535, liest 8 Potis per ADC ein und erzeugt die zugehörige PWM. Könnte für ernsthafte Anwendung mit einer Tabelle nachgerüstet werden, mit der die PWM-Werte verändert werden können (Kennlinie für Motordrehzahl oder Lampen-Helligkeit, je nach Anwendung). ...
Hallöchen Hannes, ich beschäftige mich gerade mit Deinem Programm, es ist sehr interessant, sehr raffiniert und hat mir schon viele Stunden gekostet. Nun meine Frage? Der Sollwert wird in das Sollwert-Register (SRAM) geschrieben . . st y,wl ;und ins Sollwert-Register doch an welcher Stelle erfolgt dann die direkte Zuweisung an "soll0" "soll1" usw? Gruß Bernhard
Tach Bernhard... Da wird nix ins SRAM geschrieben, sondern ins Register. Steht ja auch im Kommentar. Schau dir mal an, welchen Wert y hat. Die Pointer können nämlich nicht nur auf SRAM zeigen. Und bitte das Ergebnis deiner Analyse nicht in der Codesammlung veröffentlichen. Denn so gut ist mein Programmbeispiel nun auch wieder nicht. ...HanneS...
Hi! Wieso muß eigentlich alles über nicht mehr anders verwendbare Register laufen? Und dann noch beim 8535? Geht doch auch so: ldi zl,low(Sollwerte) ldi zl,high(Sollwerte) ldd r16,y+Soll0 cp pwz,r16 ;Sollwert0 erreicht? ror wl ;Ergebnis (Carry) sichern ldd r16,y+Soll1 cp pwz,r16 ;Sollwert1 erreicht? ror wl ;Ergebnis (Carry) sichern ldd r16,y+Soll2 cp pwz,r16 ;Sollwert2 erreicht? ror wl ;Ergebnis (Carry) sichern ldd r16,y+Soll3 cp pwz,r16 ;Sollwert3 erreicht? ror wl ;Ergebnis (Carry) sichern ldd r16,y+Soll4 cp pwz,r16 ;Sollwert4 erreicht? ror wl ;Ergebnis (Carry) sichern ldd r16,y+Soll5 cp pwz,r16 ;Sollwert5 erreicht? ror wl ;Ergebnis (Carry) sichern ldd r16,y+Soll6 cp pwz,r16 ;Sollwert6 erreicht? ror wl ;Ergebnis (Carry) sichern ldd r16,y+Soll7 cp pwz,r16 ;Sollwert7 erreicht? ror wl ;Ergebnis (Carry) sichern com wl ;invertieren wegen L-aktiven Ausgängen out aus,wl ;Ergebnisse ausgeben .dseg SollWerte: .byte 8 .equ Soll0=0 .equ Soll1=1 .equ Soll2=2 .equ Soll3=3 .equ Soll4=4 .equ Soll5=5 .equ Soll6=6 .equ Soll7=7 Möchte man für jeden Kanal dann noch andere Periodenzeiten, dann ist es auch möglich, sich ein kleines Array mit 2 Parametern mal 8 Kanäle zu machen: .dseg .equ Channels=8 PWMRegs=Channels * 2 .equ Sollwert=0 .equ PWZ=1 Mit einer Schleife läuft man dann alle durch: ldi yl,low(PWMRegs) ldi yl,high(PWMRegs) loop: ldd r16,y+ ;Sollwert nach r16 ldd pwz,y+ ;Periodenzeit nach pwz cp pwz,r16 ;Sollwert7 erreicht? ror wl ;Ergebnis (Carry) sichern cpi zl,low(PWMRegs+2*Channels) ;Ist das letzte bearbeitet? brne loop com wl ;invertieren wegen L-aktiven Ausgängen out aus,wl ;Ergebnisse ausgeben Sind dann halt 9 Takte je Kanal (72) aber man hat mehr Möglichkeiten. Und wenn man in der Timer-ISR ein paar 100 Takte Zeit hat, ist as kein Problem. @Hannes: In dem Sammelregister w1 werden die Vergleichswerte gespeichert welche beim OUT gleich den High- oder Low-Pegel für die Hardware entsprechen. Das COM ist nur dazu da, um aus einem high ein low zu machen und umgekehrt. Wenn man die angeschlossene Hardware auf aktiv-low auslegt, kann man sich das COM sparen. Gruß Andi
Ach ja! Die Zugriffe im .dseg-Bereich des ersten Beispieles erfolgen dann z. B. mit "STS Sollwerte+3,r16" auf Kanal 3 oder "STS Sollwerte+7,r16" auf Kanal 7. Oder, bei mehreren Zugriffen sparsammer, mit ldi yl,low(Sollwerte) ldi yh,high(Sollwerte) ldi r16,nnn std y+Soll3,r16 ;Kanal 3 verändern ldi r16,nnn std y+Soll5,r16 ;Kanal 5 verändern ldi r16,nnn std y+Soll7,r16 ;Kanal 7 verändern Im 2. Beispiel geht das dann z. B. mit "STS Channels+5*2,r16" für Kanal 5. Zum ändern der Periodenzeit (PWZ) für Kanal 5 dann mit "STS Channels+5*2+1,r16". Gruß Andi
... mal wieder viiiel zu voreilig :-( Erstes Beispile beginnt mit ldi yl,low(Sollwerte) ldi yh,high(Sollwerte) ldd r16,y+Soll0 cp pwz,r16 ;Sollwert0 erreicht? ror wl ;Ergebnis (Carry) sichern ..... das zweite mit ldi yl,low(PWMRegs) ldi yh,high(PWMRegs) loop: ..... autsch...
Hi Andi... Aber sicher geht das auch im SRAM. Der Grund, warum ich das in Registern ablegte, war der, dass diese Werte bei jedem Timer-Int gebraucht werden. Da oftmals eine hohe PWM-Frequenz gewünscht wird, sollte die ISR möglichst schnell abgearbeitet werden. Da lege ich mir lieber seltener gebrauchte Variablen ins SRAM, falls die Register knapp werden. Bei umfangreicheren Programmen geht diese Denkweise allerdings nicht. Gruß... ...HanneS...
AUUUUUUUUUUUUUUUUUUUAAAAAA! ldi yl,low(PWMRegs) ldi yh,high(PWMRegs) loop: ldd r16,y+ ;Sollwert nach r16 ldd pwz,y+ ;Periodenzeit nach pwz cp pwz,r16 ;Sollwert7 erreicht? ror wl ;Ergebnis (Carry) sichern cpi yl,low(PWMRegs+2*Channels) ;Ist das letzte bearbeitet? brne loop com wl ;invertieren wegen L-aktiven Ausgängen out aus,wl ;Ergebnisse ausgeben .dseg .equ Channels=8 PWMRegs: .byte Channels * 2 .equ Sollwert=0 .equ PWZ=1 In Zukunft lass ichs lieber bleiben! Gruß Andi
Hallo, schaue mal ganz unten in dem beitrag nach, da hatte ich sowas mal mit tabellen erklärt was sehr schnell ist und nicht viel rechenzeit verbraucht und im timerinterupt läuft. CA
@Hannes: Und wie hoch ist die gewünschte PWM-Frequenz? Habe bei 10KHz, für meine Anwendung passend, und 16MHz 1600 Takte je Timer-ISR. Ginge mit der Schleifenmethode auch locker mit 100KHz. Bei 200KHz muß man die Schleife in einzelne Codezeilen auflösen sofern es nicht weniger 8 Kanäle sein sollen. Gruß Andi
Warum Aua??? - Ich habe dich doch nicht gehauen... ;-)) (würde ich niemals nie nicht tun...) Allerdings habe ich auf die Schnelle deine Beispiele auch nicht näher analysiert da ich ja wusste, was du damit ausdrücken wolltest. Eine Bitte: Lass es bitte in Zukunft nicht bleiben, ich habe schon manche gute Idee beim Lesen deiner Beiträge bekommen. Gruß... ...HanneS...
Andi, die 10kHz (bei 16MHz und 1600 Takte) sind ja erstmal der PWM-Zähltakt. Den muss man noch durch den PWM-Zählumfang teilen, dann kommt es hin. Nimmt man eine feine Abstufung, so wird es doch relativ langsam für einen Motor. Es gibt immer Leute, die mögen es nicht, wenn die PWM im Motor pfeift. Mir dem Beispiel wollte ich Einsteigern eigentlich zeigen, dass es auch anders geht, als: - ADC-Quelle umschalten, - Messung wegwerfen, - ADC-Flag abfragen, bis nächste Messung fertig ist, - endlich (nach langem Warten) einen gültigen Messwert einlesen. Und dann wollte ich die bei Peter gesehene Methode mit dem "Einsammeln der Carrys" umsetzen. Es war ein reines Anfängerbeispiel und muss daher nicht als "das einzig Wahre" angesehen werden. ...HanneS...
Das mit dem AUA betraf mich von mir! :-) Da hat man mal lust, schnell was reinzuhacken, und dann Fehler über Fehler. DAS TUT EINFACH WEH wenn mans nacher liest. Aber wie war das mit den Frequenzen? Wie hoch ist (war) bei Dir bisher die gewünschte PWM-Frequenz? Für Licht sind ja 50Hz * z. B. 100 Regelstufen = 5KHz in Ordnung, auch für Motoren. @Dirk Broßwick: Deine Art der PWM-Erzeugung ist ja nicht schlecht aber man benötigt für eine höhere Auflösung mehr RAM. Und CBI und SBI sind auch nicht gerade fix (2 Takte) davor noch der Vergleich mit cpi x,y und davor noch das lesen aus dem RAM. Möchte nicht wissen, wieviel ASM-Code und Taktzyklen aus Deinem C-Code dann je Kanal anfallen. Gruß Andi
@Andi ich weiss nicht ob du meinen asm-code richtig gelesen hast, aber ich finde weder die besagten befehle CBI und SBI, weder ist der code in C geschrieben. Und das du mit einer höheren PWM-auflösung mehr sram verbrauchst ist denke ich mal vertretbar wenn man eine auflösung von 256 als max wählt und 256 byte verbraucht, soviel hat jeder atmega über. Und als bemerkung noch, es werden pro interrupt 8 kanale bearbeitet bei 29 Takte! und es werden keine Register belegt, man kann also im hauptprogramm normal weiter arbeiten, das war ziel dieses programm, weil anfänger meist über schnell hingebaute routinen stolpern wenn sie eigene sachen dazubauen und über die belegten register sich ärgern :-) CA
Bin in Deinem Link versehentlich eins hoch gerutscht zu einem Post von einem Dirk und habe da alles durcheinander gebracht. Mein Fehler. Tschuldigung! Die Idee mit den vorberechneten An/Aus-Phasen ist sehr gut. Allerdings ist das wirklich nur was für die Megas wegen der RAM-Nutzung und wenn jeder PWM-Kanal die selbe Periodenzeit haben kann. Bei bestimmten Anwendungen ist das natürlich auch was für die Tinys mit 128Byte SRAM. Gruß Andi
Hallo, ich habe eine PWM geschrieben, so wie sie ...HanneS... im 2. Beitrag erklärt hat. Wenn ich nur eine Ausgang setzt, läuft alles wie es soll. Verwende ich aber mehrere Ausgänge, stimmt nix mehr. Die PWM Frequenz ist eine andere und die Ein- und Ausschaltdauern stimmten auch nicht mit den erwarteten überein. Verwende ich z.B. für alle Ausgänge die gleich PWM Konstante, die dann mit der Zählvariablen in jedem Interrupt verglichen wird , ist aber trotzdem das PWM Signal an den Ausgängen unterschiedlich. Die Ausgänge 0-3 sind unterschiedlich und die Ausgänge 4-7 sind gleich, wobei die Frequenz (bei 4-7) auch nicht die ist, die man erhält, wenn man nur einen Ausgang verwendet. Woran kann das liegen? (Quelltext im Anhang) Schönes Wochenende! MfG Tim
Ich kann kein C, nehme aber an, dass dein Code zu lange dauern könnte und dadurch Timer-INTs verschluckt werden können. Vielleicht sollte es doch zeitoptimiert in ASM geschrieben werden. ...
Hmm, das könnte sein. Nur bin ich im Assembler nicht so firm... Die Frage ist nur, ob der Code dann soooo viel schneller wird, dass nix mehr verschluckt wird.
Der Code wird in ASM absolut schnell sofern man nicht durch gedankenlose Programmierung Zeit verschenkt. MfG Andi
Hallo, ich glaube es liegt nicht daran, dass ein Interrupt verschluckt wird. Die ISR tritt alle 15µs auf und der ganze Interruptvorgang dauert 6µs. Woran kann es noch liegen?
Und Du bist sicher, das der Code nur 6µS dauert? OK, bei 16MHz wären das dann 96 Takte die das Prog max. in Anspruch nimmt und dazwischen eine Pause von 144 Takte. Aber mgst Du die Frequenz von 66,6KHz nicht mal herabsenken auf z. B. 30KHz? Laufen noch andere, länger dauernde Interrups? Wie schnell ist Dein µC und welcher ist es? MfG Andi
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.