Hallo zusammen, Ich habe folgendes Problem. Ich will einen Servo Motor mittels PWM ansteuern. Die Grundlagen dafür sind mir bekannt und ich habe auch eine erste Ansteuerung bereits realisiert. Jedoch habe ich da die unschöne Variante über delays benutzt. Das soll so nicht bleiben. Da der Atmega8 ja über eine interne PWM verfügt möchte ich die im folgenden verwenden. Trotz mehrmaligen lesens verschiedener Forenbeiträge dazu bekomme ich es noch nicht hin die benötigten Register sinnvoll zu setzen. Zur Info: Die Erzeugung des PWM Signals für den Servo geschieht über ein POTI das mir über den ADC einen digitalen Wert zwischen 0 und 1024 gibt. Dieser Wert liegt mir bereits vor: 0 soll dabei den linken Anschlag darstelle, sprich PWM Signal mit 1ms High 512 den Mittelanschlag, sprich PWM Signal mit 1,5 ms High und 1024 den rechten Anschlag 2ms High Alle Werte dazwischen ergeben die entsprechenden PWM Signale Meine Frage ist jetzt, hat jemand von euch schonmal den internen PWM Modus vom Atmega8 benutzt und kann mir vllt sogar ein kleines Beispielprogramm dazu schicken? (Ich weiß es ist unschön nach einem fertigen Beispielprogramm zu fragen, aber daran versteh ich es immer am besten) Ich danke euch schon jetzt für die Mühe.
Gast
#1710836
http://derjulian.net/mikrocontroller#servo Ich hab keine Ahnung von Atmel Zeugs,aber der Link könnte helfen.
Gast
#1710840
Und das hier : http://mil.ufl.edu/~achamber/servoPWMfaq.html Eine google suche hilft.
Gast
#1710841
Auch hier im Forum ist das Thema nicht neu. Beitrag "Probleme mit PWM bei AtMega8"
Ok Danke für Eure Hilfe: Mein Code für ein PWM Signal sieht jetzt folgendermaßen aus:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
Wird mir so nun ein PWM Signal mit einer Frequenz von 50Hz am PIN OC1A in Abhängigkeit von dem Wert in OCR1A erzeugt? Oder sieht noch jemand einen Fehler? Danke für Eure zahlreiche Hilfe
Gast
#1710934
Du bekommst mit diesen Werten eine Wiederholrate von 100 Hz, entsprechend 10ms, OC1A ist während 8,2ms High und 1,8ms Low. Also nicht ganz richtig geraten :D Auch gibt's ne Formel für CTC im ATM8 DB, schau die mal nach und rechne damit, die 2 im Teiler muss weg, da hier nix getoggelt wird, sondern eine komplette Schwingung innerhalb eines CTC Zyklus entsteht. Dann siehst Du übrigens auch noch daß OCRn = fClock/fOCn -1 sein muss. D.h., wäre 0x4E20 der fast korrekte Wert, so müsste er eigentlich 0x4E1F lauten. Wobei ein Fehler um 1 hier nicht auffallen würde, das ist nur für's Protokoll ;-)
Und noch was. WEnn du das Register mit 20000 laden willst, dann schreib auch 20000
1 | |
2 | |
Das ist doch Unsinn, wenn du da Hex-Schreibweise benutzt um dir dann im Kommentar hinzuschreiben, was der Zahlenwert in Dezmal ist. Man nimmt immer die Schreibweise, die dem Problem angemessen ist!
> und kann mir vllt sogar ein kleines Beispielprogramm dazu schicken? Aber sicher doch...: http://www.hanneslux.de/avr/mobau/7ksend/7ksend02.html ...
Hallo Herr Buchegger, Danke für Ihre Hilfe. Tut mir leid wenn ich in in Ihren Augen vllt etwas überflüssige Fragen stelle, aber ich bin Neuling auf dem Gebiet und versuche mich jetzt nach und nach an die Probleme heran zu tasten. Jedoch habe ich noch einige Fragen zu ihrer Hilfestellung. 1) Warum bekomme ich eine Wiederholrate von 100Hz? Ich habe mich an die Seite http://mil.ufl.edu/~achamber/servoPWMfaq.html gehalten. Dort wurde es mit der Formel: f_pwm = f_cpu/(2*Prescaler*TOP) berechnet. Diese Formel finde ich auch im Datenblatt wieder und die gibt in meinen Augen genau 50Hz wenn man es nachrechnet?! Wo liegt da der Fehler? Was muss ich da anders machen? 2) Wo kommt diese Formel her? OCRn = fClock/fOCn -1 Was für einen Wert entspricht da f0Cn ?? 3) Die Dezimalschreibweise sehe ich ein. Aber ich weiß noch nicht genau warum 20000 hier genau 20ms entspricht? Kann mir das noch wer erklären? Habe den Wert dem vorherigen Link entnommen Danke für Eure Mühe!
@ Hannes Lux Danke für den Link. GIbt es den Code auch in C???
Gast
#1711130
Auch wenn ich nicht der Herr Buchegger bin, aber ich hab' Dir das mit den 100Hz und der Formel geschrieben :D Die Formel zu CTC findest Du im ATMega8 Datenblatt, wenn's das 2486V–AVR–05/09 ist, dann auf Seite 89. Faktor 2 wenn der Ausgang bei jedem CTC Durchlauf getoggelt wird, denn dann braucht's 2 Durchläufe um eine komplette Schwingung zu erhalten. Hier ist's aber anders, denn in einem CTC Durchlauf wird bei Compare Match der Ausgang gesetzt und bei Zähler 0 wieder gelöscht. Damit findet eine vollständige Schwingung in einem CTC Durchlauf statt. Und deswegen muss die 2 raus.
@MWS Sorry, dachte der Beitrag von Herrn Buchegger ist ein Nachtrag zu dem vorherigen ;-) Also ich hab auf Seite 89 lediglich die f_pwm = f_clocl / 2*N*(1+OCRA) Aber ok, angenommen deine Formel stimmt. WIe muss ich dann jetzt meine Register anpassen das ich einen 1,5ms HIGH Impuls bekomme und den Rest der 20ms LOW habe?? So?
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
Tobias F. schrieb: > 1) > Warum bekomme ich eine Wiederholrate von 100Hz? Ich habe mich an die > Seite http://mil.ufl.edu/~achamber/servoPWMfaq.html gehalten. Dort wurde > es mit der Formel: f_pwm = f_cpu/(2*Prescaler*TOP) > berechnet. Diese Formel finde ich auch im Datenblatt wieder und die gibt > in meinen Augen genau 50Hz wenn man es nachrechnet?! Wo liegt da der > Fehler? Was muss ich da anders machen? Vergiss mal die Formeln, wir kriegen das auch ohne Formeln, nur durch das Verständnis dessen, was bei deiner PWM passiert, raus. Ich bin nämlich der fixen Überzeugung, dass es viel wichtiger ist, wenn du weißt wie der Timer arbeitet als dass du eine Formel irgendwo abschreiben kannst. Die WGM Bits, die du eingestellt hast, ergeben eine Fast-PWM. Im Datenblatt gibt es eine Tabelle, dort sieht man, dass deine gesetzten WGM Bits den Modus 14, Fast PWM mit ICR1 als Top Wert ergeben Was heißt das jetzt im Klartext. Fast PWM bedeutet, dass der Timer einfach vor sich hinzählt. Wenn er bei seiner Obergrenze angelangt ist, dann beginnt er wieder bei 0 (und macht gleichzeitig eine Operation: Den Ausgangspin auf 1 Pegel bringen) Wie weit zählt der Timer? So weit wie es ihm das ICR1 Register vorgibt. Genau das ist die Bedeutung von 'ICR1 ist Top Wert' Der Timer zählt also vor sich hin 0, 1, 2, 3, 4 ... 5000, 50001, 50002, ... , 19997, 19998, 19999 jetzt ist die Übereinstimmung mit dem ICR1 Register da, der Timer wird auf 0 gesetzt (und gleichzeitig der Ausgangspin auf 1) und weiter gehst 0, 1, 2, ... 19997, 19998, 19999, 0, 1, 2, ... 19998, 19999, 0, ... OK. Nachdem jetzt klar ist, wie der Timer zählt, wollen wir uns mal dem Timing zuwenden. Jeder dieser Zählschritte braucht ja eine gewisse Zeit. Wie lang ist denn das? Ganz einfach: Das ist zunächst einfach mal der CPU-Takt. Bei dir 16Mhz. Aber: Da ist noch ein Vorteiler. In deinem Fall ist der 8. D.h. nur bei jedem 8ten CPU-Takt wird er Timer um 1 weitergeschaltet. Wenn nun in 1 Sekunde 16 Millionen CPU-Takte erfolgen, dann macht der Timer in 1 Sekunde daher nur 2 Millionen Zählschritte (16/8). D.h. der Timer würde in 1 Sekunde bis 2 Millionen zählen. Wenn er könnte. Kann er aber nicht. Denn wir haben ja schon gesehen: Jedes mal wenn der Timer 19999 erreicht hat (also 20000 Zählschritte gemacht hat) wird er wieder auf 0 zurückgesetzt. Das heißt: Wenn wir wissen wollen, wie oft der Timer daher in 1 Sekunde wieder auf 0 zurückgesetzt wird, dann rechnen wir 2 Millionen / 20000 und das macht 100 D.h. In 1 Sekunde beginnt der Timer 100 mal wieder bei 0 zu zählen. Jedesmal wenn er das tut wird der Ausgang auf 1 gesetzt (und irgendwann später wieder auf 0). Das heißt aber auch, du hast am Ausgangspin damit 100 komplette 'Schwingungen' in der Sekunde. Also 100Hz Fazit: Entweder du, oder derjenige bei dem du abgeschrieben hast, hat die falsche Formel benutzt. So jetzt bist du dran: Ganz ohne Formel: Welchen Wert musst du in ICR1 einstellen, damit der Timer in 1 Sekunde nur 50 mal rundum zählen kann? Oder anders ausgedrückt: Wie gross muss das x in 2 Millionen / x = 50 sein?
Tobias F. schrieb: > Also ich hab auf Seite 89 lediglich die f_pwm = f_clocl / 2*N*(1+OCRA) Gut. Jetzt nimm das Datenblatt, geh auf Seite 89, such die Formel und sieh nach wie die Kapitelüberschrift von diesem Kapitel lautet. Da steht "Clear Timer on Compare Match Mode" Hast du diesen Modus? Nein! Du hast Fast-PWM
Gast
#1711172
Nun, wie ich sehe wurde alles bereits bis in's Detail erläutert :-)
> f_pwm = f_clocl / 2*N*(1+OCRA)
Musst doch nur mal Deine Werte einsetzen und ausrechnen, wobei die
Formel aus den erklärten Gründen ohne 2 zu schreiben ist:
f_pwm = f_clock / N*(1+OCRA), wobei N = Prescaler = 8 ist.
damit ist: f = 16.000.000 / (8 * (20.000 +1)) = 99,995 Hz
Was glaubst Du, musst Du jetzt in OCRA einsetzen ?
Wobei ich gerade gesehen habe, daß ich die Formel mit OCRn = fClock/fOCn
-1
auch nicht korrekt umgestellt hab', hab' den Prescaler vergessen,
richtig ist:
OCRn = (fClock/(N*fOCn)) -1
wobei fOCn die gewünschte Frequenz ist, bzw. 1/fOCn die Periodendauer.
WOW dein vorheriger Beitrag war TOP!! Ich glaube so langsam fällt der Cent ;-) Also nach deiner Rechnung muss
1 | |
2 | |
Ist das soweit korrekt? Das ich mich jetzt im Fast PWM Mode befinde verstehe ich auch. Aber warum muss ich die WGM13 und WGM12 in TCCR1B initialisieren und WGM11 in TCCR1A??? oder ist das auch noch falsch?
1 | |
2 | |
Gast
#1711181
> Aber warum muss ich die WGM13 und WGM12 in TCCR1B initialisieren und WGM11 > in TCCR1A??? Weil der Controller-Hersteller diese Bits nun mal so plaziert hat ...
Ok dann hab ichs jetzt vollständig verstanden und konnte auch alles mit dem Datenblatt nachvollziehen. Toll das man hier einem hier so weitergeholfen wird. Als Newbe ist es halt am Anfang schon schwierig sich da durchzuwühlen. Also vielen Dank nochmal an Euch alle.
Tobias F. schrieb: > @ Hannes Lux > Danke für den Link. Nichts zu danken... > GIbt es den Code auch in C??? Nein, wie kommst Du darauf? Der AVR kann kein C, der C-Compiler generiert auch nur ASM-Code (der dann in einem weiteren Pass in Maschinencode assembliert wird). Da ich den AVR halbwegs kenne, C aber kryptisches Neuland für mich wäre, spare ich mir den Umweg über C und schreibe meine Programme gleich in Assembler. Also wird es von mir keinen C-Quelltext geben. Kurze Betrachtung zum Timer: Mein Programm läuft auf einem Mega48 (dem Mega8 sehr ähnlich, ich nutze nur Features, die der Mega8 auch hat) mit 1MHz internem Takt (Du brauchst 16 MHz, ich vermute aus PC-Erfahrung, "nur das Beste ist gerade gut genug"). Ich lasse Timer1 mit Controllertakt laufen, er zählt also jede µs einen Schritt weiter. Timer1 hat zwei Compare-Einheiten (A und B). Eine Compare-Einheit nutze ich, um alle 20ms ein neues Telegramm (für 7 Servos) zu beginnen. Die zweite Compare-Einheit nutze ich, um nacheinander (wie bei einer echten RC-Anlage) die Impulsbreiten der einzelnen Servos abzuzählen. Den Timer lasse ich dabei frei durchlaufen, im Interrupt addiere ich einfach das gewünschte Intervall auf den Termin des aktuellen Interrupts drauf, was dann als neuer Interrupt-Termin gilt. Dies sieht im ersten Moment vielleicht komplizierter aus, ist es aber nicht, denn der Timer läuft dabei frei durch (als Ring Modulo 65536), was mir die Möglichkeit gibt, auch noch den ICP-Interrupt nebenher zu nutzen, um z.B. Servoimpulse einer RC-Anlage oder DCC-Signale einer digitalen Modellbahnsteuerung einzulesen. Die 20ms Telegrammabstand sind übrigens nicht so kritisch. In einem Projekt für eine Modellbahnschranke steuert ein Mega8 zwei Servos (für die Schrankenbäume) unabhängig voneinander, wobei die Telegramme nur alle 64ms wiederholt werden um etwas Strom zu sparen. Der Mega8 generiert nebenher noch zwei unabhängige Blinklichter und gibt über Hardware-PWM den Sound der Glocke aus (modifizierte WAV-Datei). Achja, Formeln für Timer nutze ich nicht, das Verständnis für Taktquelle, Vorteiler, Zählumfang, Interrupt bzw. Hardware-Anbindung reicht mir dazu völlig aus. Klar, ich mach' das schon ein paar Jahre und ich musste die entsprechenden Kapitel im Datenblatt mehrfach lesen um die Zusammenhänge richtig zu verstehen, von nix kommt halt nix. ...
Zum Abschluss habe ich doch nochmal eine Frage: Gibts es für einen Arm Controller LPC 21xx auch eine Fast PWM oder heißt die da anders? Ich besitze nämlich ein Testboard für AVR und ARM Controller.
Gast
#1711268
Tobias, also, ich hab' mich jetzt hingesetzt und es auf einem ATMega32 zum Laufen gebracht. Die Register und Waveform Modes sind kompatibel zum ATMega8. Der OC1A Pin ist allerdings woanders. Verwendet habe ich Deinen Initialisierungscode hier:
1 | |
2 | |
Allerdings auf Bascom geschrieben, hab' zur Verdeutlichung die Bitnotation verwendet.
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
Dieser Code erzeugt bei 8MHz Takt die 50Hz/20ms Wiederholrate, mit einem positiven Puls von 1-2ms durchlaufend. (Zugegeben, der interne R/C läuft bei meinem ATM32 auf 8270000Hz, den Fehler hab' ich rausgerechnet) Da mein Controller mit internen R/C Oszillator eben auf ~8MHz läuft, musst Du für 16MHz die Werte für ICR1/OCR1A auf 39999, resp. 3999 setzen. Alles mit Oszi überprüft, ein Servo hab' ich auch mal schnell dran gehängt, alles einwandfrei. ;-)
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.