mikrocontroller.net

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


Autor: Toto (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: franz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,
soll das Servo permanent auf Neutral stehen oder soll es sich auch 
bewegen?

Grüße,
Peter

Autor: Toto (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: franz (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Toto (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: lordchen (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Zoltan Gradwohl (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Toto (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: Mattias (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Christoph (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Zoltan Gradwohl (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Mattias (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Christoph (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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 :)

Autor: Mattias (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Joerg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Christoph (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Christoph (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 :(

Autor: Joerg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Christoph (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Joerg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Christoph (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mittelstellung = Pulsdauer von 1.5ms

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

Autor: Joerg Wunsch (Gast)
Datum:

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

Autor: Christoph (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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-Ju...

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

Autor: Joerg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hmm, ja, die korrigierte Version hatte ich dann sicher auch schon...

Von den Servomotoren habe ich leider keine Ahnung, sorry.

Autor: Joerg Wunsch (Gast)
Datum:

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

Autor: daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.