Forum: Compiler & IDEs PWM 50HZ 1.5ms (Servo)


von Toto (Gast)


Lesenswert?

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.

von franz (Gast)


Lesenswert?

Hi,
soll das Servo permanent auf Neutral stehen oder soll es sich auch 
bewegen?

Grüße,
Peter

von Toto (Gast)


Lesenswert?

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.

von franz (Gast)


Lesenswert?

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

von Toto (Gast)


Lesenswert?

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.

von lordchen (Gast)


Lesenswert?

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

von Zoltan Gradwohl (Gast)


Angehängte Dateien:

Lesenswert?

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

von Toto (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Mattias (Gast)


Lesenswert?

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

von Christoph (Gast)


Lesenswert?

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.

von Zoltan Gradwohl (Gast)


Lesenswert?

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

von Mattias (Gast)


Lesenswert?

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

von Christoph (Gast)


Angehängte Dateien:

Lesenswert?

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 :)

von Mattias (Gast)


Lesenswert?

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

von Joerg Wunsch (Gast)


Lesenswert?

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.

von Christoph (Gast)


Lesenswert?

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

von Christoph (Gast)


Lesenswert?

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 :(

von Joerg Wunsch (Gast)


Lesenswert?

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?

von Christoph (Gast)


Lesenswert?

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.

von Joerg Wunsch (Gast)


Lesenswert?

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).

von Christoph (Gast)


Lesenswert?

Mittelstellung = Pulsdauer von 1.5ms

Und das ich vom simulieren nicht wirklich Ahnung habe ist ja bekannt
;)

von Joerg Wunsch (Gast)


Lesenswert?

Trotzdem, welche Mittelstellung denn?  Die Impulsdauer in meinem
Beispiel ist doch hart verdrahtet, kein PWM weit und breit.

von Christoph (Gast)


Lesenswert?

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 :)

von Joerg Wunsch (Gast)


Lesenswert?

Hmm, ja, die korrigierte Version hatte ich dann sicher auch schon...

Von den Servomotoren habe ich leider keine Ahnung, sorry.

von Joerg Wunsch (Gast)


Lesenswert?

Nö, ich brauche die ,,korrigierte'' Version nicht.  Ich vermute
eher einen Compilerbug, der inzwischen behoben worden ist.

von daniel (Gast)


Lesenswert?

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!

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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
Noch kein Account? Hier anmelden.