Hallo ich hab ein kleines Verständnisproblem bei der Pulsweitenmodulation(PWM) auf einem AT8535 Contr. mittels Timer1. Ich versuche bei einer Frequenz von 50HZ einen Impuls von ca 1.5ms zu erzeugen (Servoansteuerung). Also erstmal wieweit ich alles Verstanden habe , korrigiert mich wenn etwas nicht stimmt. Ich hab den Timer1 folgendermassen eingestellt Invertierter PWM Interrupt bei Überlauf 10-Bit PWM Betriebsart aktivieren. CPU Takt/256 Also hab ich bei 10 bit Auflösung eine Zählerobergrenze von 1023. Bei einem CPU Takt von 8000000HZ/256HZ=31250HZ d.h ein Takt dauert 1/31250=0.000032Sek=0.032ms Ich möchte eine Frq. von 50HZ also alle 20ms muss ein Impuls erfolgen.In CPU Impulsen heisst das 20ms/0.032ms=625 Takte. Die Impulsdauer soll 1.5 ms betragen also 1.5ms/0.032ms=~47 Takte. Ich hab also den Startwert des Zählers auf 1023-(625)=398 gelegt.Und den Compare Wert auf 1023-(47)=976 Er zählt also vom Startwert(398) hoch schaltet beim erreichen des Comparewertes(976) meinen Pin auf high zählt weiter bis zum Höchstwert (1023). Dann erfolgt der Interrup und ich setze den Zähler wieder auf Startwert. Wenn ich das ganze im VMLab simuliere bekomme ich immer eine Frequenz von 20 Hz also 50ms dauer raus, die Impulsdauer stimmt aber. Jetzt endlich meine Frage, wo liegt der Fehler. Zählt er nach dem Interrupt weiter runter und macht dann einen weiteren Int und zählt dann erst wieder hoch. Also wie komme ich auf meine 50 HZ bei 1.5 ms Impulsdauer. Muss ich mir erst ne eigene Routine basteln? Das ganze soll mit der PWM erfolgen also keine Softwarelösung. P.S. Ich benutze den Compiler von Codevision falls einer nen BSP. hat bitte kein Assembler.Aber es geht ja nur um das Prinzip.
Hi, soll das Servo permanent auf Neutral stehen oder soll es sich auch bewegen? Grüße, Peter
Ne natürlich nicht, doch dafür muss ich nur den Comparewert ändern. Das soll später alles über den ADC laufen.Die Impulslänge ist ja auch nicht das Problem sondern die Frequenz.
Naja, also ich erzeuge die Pulse für meine Servos mit einer ganz kleinen T/C1-Overflow-Routine. Könnte Dir den Code hereinstellen. Grüße, Peter
Klar stell den Code mal rein vielleicht hilft mir das weiter. Aber das muss doch auch mit PWM gehen, ist doch eigentlich für solche Sachen gedacht.
nein mit PWM geht es glaub ich nicht ! das signal was ein servo bekommt ist kein PWM (glab ich ) ich bin mir nicht ganz sicher bei PWM ist der takt ja immer ein verhältnis aus Pulsweite und pause die pause ist bei dem servo IMMER gleich allein die pulsweite bestimmt die position des sevos ich bastel da auchgerade mit herum @peter ich hätte auch interesse an deiner ganz kleinen T/C1-Overflow-Routine. viele grüße lordchen
Hallo, ich habe mal ein Programm geschrieben zum Messen von Servo Pulslängen. Der Timer1 (TMAEGA8) misst dabei die Zeit zwischen zwei Interupts und speichert den Wert. Anschliessend wird wiederrum mit Timer1 ein Puls an einem Ausgang des AVR-s erzeugt. Dabei benutze ich die TC1-Overflow, siehe Anhang. Gruß Zoltan
Hi @all ich hab mir jetzt auf Umwegen eine Lösung gebastelt mit der es möglich ist mit Hardware PWM Servoimpulse auszugeben.Es können 2 Servos unabhängig voneinder angesteuert werden. Das Bsp. wurde mit dem Codevision AVR C Compiler(Vers.1.23.8c) erstellt und mit dem Visual MicroLab simuliert(Vers. 3.6) Wer Intresse hat kann ich mal die kompletten Datein zum simulieren schicken. Die Auflösung ist noch sehr gering ~31 Schritte,was für meine Zwecke ausreichend ist,weil ich nur eine Geschwindigkeitsseuerung brauche keine Positionssteuerung. Müsste aber durch einen kleineren Vorteiler der Quarzfrequenz besser werden. Ist noch nicht perfekt, der erste Impuls ist zu lang (20ms), ich weiss noch nicht woran es liegt.Danach werden Impulse aber korrekt ausgegeben und die Frequenz stimmt auch. Die Frequenz und Impulslänge sind in bestimmten Grenzen variabel.Bevor einer fragt, das Projekt ist noch nicht fertig es werden im Moment noch feste Imulslängen ausgegeben,lassen sich aber über den Compwert A und B ändern. Die genauen Zusammenhänge der Frequenz und Impulslängen mit den im Bsp. angegebenen Werten ist mir noch nicht ganz klar,da muss ich mich wohl doch mal mit der Assemblerprogrammierung auseinander setzen. P.S Die Datei und die Kommentare sind nicht ganz auf dem neuesten Stand.Soll später mal eine Robotsteuerung werden mit "gehackten" Servos, also Servos die sich ständig drehen.
Hallo Leute, die Ansteuerung kann man sehr wohl mit pwm generieren. Die Frequenz, mit der die Impulse erzeugt werden, ist dem Servo völlig egal. Das Signal kann auch alle 5 ms kommen oder aber auch alle 100 ms. lediglich die Pulsbreite muß zwischen 1 ms und 2 ms liegen. Kommt der Puls alle 5 ms, so ist das Servo schneller und kann auch mehr Last halten denn das Servo ist immer nur während der Pulsbreite aktiv. Gruß Mattias
Da hänge ich mich doch auch einmal mit einer kurzen Frage rein, hoffe ihr nehmts mir nicht übel ;) Ich versuche auch gerade, einen Servo mit einem 2313 anzusteuern, und zwar folgendermaßen: Den 16Bit-Timer lasse ich mit einem Vorteiler von 64 laufen und von jeweils 64411 bis zum Überlauf zählen. In der ISR wird der Zähler dann natürlich wieder auf 64411 gesetzt. Das macht also (65535-64411) 1125 Zählungen bis zum Überlauf. Bei 4Mhz Takt und dem Vorteiler heisst das, dass die Timer1-Overflow ISR alle (1/4000000)*64*1125 = 18ms aufgerufen wird. Dort schalte ich den entsprechenden Ausgang jeweils 1ms oder 2ms auf High und danach wieder low (Habe gerade keine zuverlässige delayroutine mit höherer Auflösung zur Hand, für einen ersten Test sollte es aber denke ich langen). Wenn der Impuls nur 1ms lang ist warte ich danach noch brav eine weitere ms, um die 20ms voll zu machen. Soweit so gut, nur funktionierts natürlich nicht so wie ich will ;) Der Servo dreht stump bis zum rechten Anschlag und lässt sich zu nix anderem bewegen, egal ob ich 1ms oder 2ms Pulse ausgebe. Ich bin mir relativ sicher das die Pulse stimmer, jeweils soweit ich das mit meinem Oszi älteren Baujahrs schätzen kann ;). Auf jeden Fall kann ich sehen das sich die Pulslängen auch wirklich ändern wenn ich sie umschalte. An der Aussenbeschaltung des Servos kann es denke ich nicht liegen: Jeweils +5V (von seperatem NT), GND und eben den Pin vom AVR direkt an das Servo. Habe auch schon verschiedene Delayroutinen getestet bzw. selber welche "ausgedacht", das Servo dreht allerdings immer bis zum rechten Anschlag sobald ich Pulse sende.
Hallo Christoph, ich würde sagen, dass Du dein Programm in einen Simulator z.B. AVR Studio oder VMLAB testest. Dort kannst Du sehr schön erkennen, wie lang die Pulslängen "wirklich" sind. Du must noch darauf achten, im Simulator auch die Taktfrequenz von deinem Mikrocontroller einzustellen. Gruß Zoltan
Hallo Christoph, das kann ich mir gar nicht vorstellen. Bis Du Dir sicher, dass die Pegel richtig sind ???( wenn der Puls kommt, muss der Pegel high sein, sonst low).Gelegendlich sieht man die Pegel in Zeitschriften anders herum. Poste doch mal den Code. Gruß Mattias
Hi, hier ist der Code. Das Hauptprogramm habe ich nicht hochgeladen, darin passiert eh nur folgendes: DDRx festlegen, servo_timer_init() aufrufen und ein sei() gefolgt von einer Endlosschleife. Im simulieren bin ich leider nicht so fit, um nicht zu sagen ich finde mich da noch nicht wirklich zurecht... Ach ja, die _delay_loop_2() ist die aus der standard delay.h :)
hi, das sieht eigentlich sehr gut aus. Ich vermute aber den Fehler in der delay Funktion. Wenn Du diese Funktion verwendest, musst Du Deine Taktfrequenz irgendwo mal definieren. Versuch lieber mal eine delay selber zu machen. ZB. void my_delay(unsigned long t) { while(t) t--; } Das t kannst Du dann sehr fein abstimmen. Die echte Zeit kann man schwer errechnen, weil man die Funktion aufruft. Einfach mal Werte einsetzen und messen. Sonst sehe ich keinen Fehler in dem Code. Das muss so gehen. Gruß Mattias
Funktioniert nicht mit avr-gcc, der optimiert das zu sehr weg. <avr/delay.h> ist in der Tat der bessere Weg. Dort steht auch gleich noch geschrieben, wie lange das wirklich braucht.
Hi keine Ahnung wo mein Rechenfehler bei der Delayroutine ist, aber daran liegt es tatsächlich. Ich habs einfach mal so probiert: void delay(unsigned long t) { while(t--) asm volatile("nop"::); } (damit der mir nicht einfach die Schleife wegoptimiert ;)) Jetzt muss ich das ganze nur noch kalibrieren :) Brauch ich um sowas zu simulieren eigentlich unbedingt eine COFF Datei, oder gehts mit der .elf oder .hex auch? Gruss Christoph
Habe den Beitrag über mir eben erst gesehen, leider kann man hier ja nicht editieren: Die Rechnung mit der _delay_loop_2() hatte ich schonmal woanders erwähnt: Laut Beschreibung braucht die Schleife 4 Takte pro Umlauf, d.h. bei 4 Mhz wären das (1/4000000) * 4 = 10^-6 s = 1us. Das heisst, dass ein Aufruf von _delay_loop_2(1500) eine Pause von ziemlich genau 1500us = 1.5ms zur Folge hätte. So habe ich das in meinem geposteten Code ja auch gemacht. Nur sind die Impule/die Pausen einfach deutlich zu kurz....warum weiss ich nicht :(
Weiß ich auch nicht, zumindest habe ich da neulich in der Simulation sehr exakt die 1,5 ms herausbekommen. Übrigens genügt zum Verhindern des Wegoptimierens auch folgender Trick: while (--j > 0) asm volatile(""); ;-) Hier mein Beispiel: #include <avr/delay.h> #include <avr/io.h> int main(void) { DDRB = 0xff; while (1) { _delay_loop_2(1500); PORTB ^= 0xff; } } Hier spaßeshalber mal das aus dem Scope von VMLAB exportierte CSV-File für den Anfang der Simulation TIME;PB0;h:\src\avr\test\my_idea.prj;Full waveform;Thu Jul 31 22:10:56 2003 0.00000000e+00; 2.50 5.12000000e-04; 2.50 5.20500000e-04; 2.50 5.20500000e-04; 0.00 2.02150000e-03; 0.00 2.02150000e-03; 5.00 3.52300000e-03; 5.00 3.52300000e-03; 0.00 5.02450000e-03; 0.00 VMLAB setzt initial den Port auf 2.5 V, um den Zustand als Eingang zu verdeutlichen. Bei 0.52 ms (startup delay) wird der Port initialisiert, bei 2.02 ms togglet er das erste Mal, bei 3.52 ms das zweite Mal, bei 5.02 ms das dritte Mal, usw. Sieht doch OK aus, oder?
Ich hab mich mit der Simulation unter AVR Studio auch mal versucht anzufreunden g Mag vielleicht dilettantisch sein, aber zumindest habe ich irgendwie ein brauchbares Ergebnis bekommen: Ich hab das Programm simuliert laufen lassen und die Port-Ausgabe geloggt, und dann aus den Taktabständen zwischen der Zustandsänderung des Ports die Zeit berechnet: Wenn ich den Code wie oben gepostet laufen lasse, komme ich auf eine Pulsdauer von 0.7125ms in der Mittelstellung, wo es eigentlich 1.5ms sein sollten (Takt war auf 4Mhz eingestellt). Interessant wurde es, als ich einfach mal alles Zeiten verdoppelt habe (also anstatt _delay_loop_2(1000) _delay_loop_2(2000) usw.): die Pulsdauer ist nämlich genau gleich geblieben, ebenfalls wieder 0.7125ms.
Nun, dann hast Du eigentlich nur bewiesen, daß Deine Simulation offensichtlich keinen Bezug zur Realität besitzt. ;-) Was ist eine ,,Mittelstellung'' für Dich? Ich hatte das mal schnell in VMLAB simuliert. AVR Studio läuft bei mir nicht, weil ich kein Windows habe (außerdem finde ich persönlich das GUI gruselig und fast unbenutzbar).
Mittelstellung = Pulsdauer von 1.5ms Und das ich vom simulieren nicht wirklich Ahnung habe ist ja bekannt ;)
Trotzdem, welche Mittelstellung denn? Die Impulsdauer in meinem Beispiel ist doch hart verdrahtet, kein PWM weit und breit.
Es geht doch um die Ansteuerung eines Servomotors, und nach meinen Informationen dreht sich dieser in Richtung seiner "Ausgangsstellung" wenn die Pulsdauer 1.5ms beträgt. Wenn ich da falsche Informationen habe, bitte ich um Berichtigung. Ich habe mittlerweile auch etwas sehr interessantes mit Google gefunden: http://www.avr1.org/pipermail/avr-gcc-list/2003-July/004875.html Mit der korrigierten Version der _delay_loop_2() funktioniert es zumindest in der Simulation jetzt einwandfrei, und in der Praxis nach einem ersten Versuch anscheinend auch :)
Hmm, ja, die korrigierte Version hatte ich dann sicher auch schon... Von den Servomotoren habe ich leider keine Ahnung, sorry.
Nö, ich brauche die ,,korrigierte'' Version nicht. Ich vermute eher einen Compilerbug, der inzwischen behoben worden ist.
hallo Leute, weiss jemand wie ich die Zeit zweischen zwei interrupts möglichst geneau messen und speichern kann. Ich habe ATmega 88 mit 10MHz getaktet. das ganze muss aber leider Assembler realisiert werden!
Erstens: kapere keinen alten Thread dafür, erst recht nicht, wenn er gar nix mit der Sache zu tun hat. Mach also 'nen neuen Thread auf. Zweitens: das ist das GCC-Forum. Wenn du ,,leider in Assembler'' programmieren musst (warum denn?) bist du hier nicht ganz richtig, sprich: du könntest in einem anderen Forum mehr Antworten bekommen. Wer mit dem GCC programmiert, wird nicht notwendigerweise der große Assemblerfreak sein. Drittens: du musst dein Problem schon ordentlich beschreiben.
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.