Hallo,
hab heute meinen ersten Servomotor an meinen Atmega8 angeschlossen gg.
Das mit dem 20ms und 2ms hab ich also verstanden und hin gebracht.
Nun die Frage:
Wie sage ich meinem Servo in welche Richtung er sich bewegen soll.
MfG
Dominik
Dominik Fa schrieb:> Das mit dem 20ms und 2ms hab ich also verstanden>...> Wie sage ich meinem Servo in welche Richtung er sich bewegen soll.
Die Position des Servos ergibt sich aus der Länge des kurzen Impulses.
Der darf zwischen 1ms und 2ms lang sein.
Oliver
So weit ich das verstanden habe schauen die Impulse wie folgt aus:
1ms high, 19ms low: links Position
1.5ms high, 18.5ms low: mittlere Position
2ms high, 18ms low: rechte Position
Wenn des jetzt so stimmt, heißt das für ich muss mir ein Oszi zulegen um
meine Impulse zu prüfen ob ich da keinen rechen Fehler habe.
Dominik Fa schrieb:> So weit ich das verstanden habe schauen die Impulse wie folgt aus:>> 1ms high, 19ms low: links Position> 1.5ms high, 18.5ms low: mittlere Position> 2ms high, 18ms low: rechte Position>
Genau.
Wobei nur die 1ms - 2ms kritisch sind. Ob die Pause 10ms oder 15ms oder
20ms beträgt, ist für die meisten Servos uninteressant. Die Länge des
Pulses ist das was zählt.
> Wenn des jetzt so stimmt, heißt das für ich muss mir ein Oszi zulegen um> meine Impulse zu prüfen ob ich da keinen rechen Fehler habe.
Wie wäre es, wenn du einfach mal dein Programm zeigst? Dann könnten wir
dir helfen, zb indem jemand überprüft, ob dein Timing stimmt.
Dominik Fa schrieb:
Moment!
> Ich habe angenommen das der Takt 4MHz
Was soll das heißen?
Hast du einen 4Mhz Takt oder hast du ihn nicht!
Servo Ansteuerung ist hauptsächlich eine Timing Sache. Wenn man sich
schon auf die allem zugrundeliegende Basisfrequenz nicht verlassen kann,
macht es keinen Sinn weiterzumachen.
Hast du oder hast du nicht?
Hast du das überprüft?
Du brauchst kein Oszi.
Du brauchst eine Einführung in Programmierung. Im speziellen wie das mit
Unterprogramaufrufen funktioniert. Und da wieder im Speziellen, wie das
dann alles mit ISR funktioniert.
AVR-Tutorial
Dein Einsprungspunkt in die ISR ist bei timer0_overflow. Jeder einzelne
Pfad, den das Programm nehmen kann, muss letztendlich in einem reti
münden!
In deinem Programm kommen viel zu viele rjmp wait vor!
Idelaerweise hast du nur eine einzige von diesen Endlosschleifen. Und
die befindet sich in main: Nachdem der Grundaufsatz erledigt ist, geht
main in eine Endlosschleife über.
Durch dein hin und her Springen hast du dir selbst den Überblick über
dein Programm genommen, sodass du nicht mehr nachvollziehen kannst, was
wann wo passiert.
Da hilft dir auch dein
sei
rjmp wait
innerhalt der ISR nichts mehr. Ein sei, der in einer ISR auftaucht, ist
fast immer ein Fehler. Ein Profi, der weiß was er tut, und was er sich
damit einhandelt kann das machen. Aber für einen Newbie ist so etwas
tödlich.
Karl heinz Buchegger schrieb:> Du brauchst kein Oszi.> Du brauchst eine Einführung in Programmierung. Im speziellen wie das mit> Unterprogramaufrufen funktioniert. Und da wieder im Speziellen, wie das> dann alles mit ISR funktioniert.>> AVR-Tutorial
Und schon ist geklärt, woher die Mär stammt, der Mega8 würde ohne
weiteres Zutun mit 4 MHz laufen. ;-) Denn das Tutorial arbeitet nunmal
mit einem Mega8 mit 4 MHz.
Eigentlich braucht Dominik nur mal etwas durch die Threadliste
schmökern, denn da laufen aktuell mehrere Threads zum Thema Servo. Da
ist alles erklärt, was man dazu wissen sollte.
Ich habe mir den Quältext kurz angesehen, kann ihm (betreffs
Programmierstil) aber nichts abgewinnen. Timer-Overflow-Interrupt war
eine nette Sache, als es noch keine Compare-Interrupts gab. Die Zeiten
sind vorbei, zumindest bei aktuellen AVRs. Auch sonst enthält dieser
Quältext allerhand Unsinn.
Also, Dominik, ehe Du ohne Erfahrung weiter draufloswirtschaftest und
dann auf Hilfe hoffst, solltest Du erstmal schaun, wie es andere machen.
Ich meine dabei nicht unverstandenes Abschreiben, sondern Analysieren
fremder Programme. Sollte es an dieser oder jener Stelle zu Fragen
kommen, so wird sich sicher jemand finden (auch ich), der Dir mal einen
entsprechenden Hinweis gibt.
Kluchscheißer Kluchscheißer schrieb:> Karl heinz Buchegger schrieb:>> Du brauchst kein Oszi.>> Du brauchst eine Einführung in Programmierung. Im speziellen wie das mit>> Unterprogramaufrufen funktioniert. Und da wieder im Speziellen, wie das>> dann alles mit ISR funktioniert.>>>> AVR-Tutorial>> Und schon ist geklärt, woher die Mär stammt, der Mega8 würde ohne> weiteres Zutun mit 4 MHz laufen. ;-) Denn das Tutorial arbeitet nunmal> mit einem Mega8 mit 4 MHz.
Schon klar.
Aber auch das Tuorial beginnt damit, einen Quarz bzw. Quarzoszillator
mit 4Mhz an den Mega8 anzuschliessen :-)
> Auch sonst enthält dieser Quältext allerhand Unsinn.
Hab ich zuerst auch gedacht. Aber wenn man das mal durchgeht ist es gar
nicht sooo schlimm. Interrupts und was da in der ISR zu passieren hat
wurde nicht verstanden. Der Code kommt mir vor wie von einem BASIC
Umsteiger: Hauptsache ich kann mit einem GOTO irgendwo hinspringen - das
ist cool.
Wenn man sich das wegdenkt , ist der Rest nicht mehr so schlimm. Das man
das Servo nur auf eine Handvoll Positionen einstellen kann, mangels
Teil-Möglichkeiten durch den Timer-Overflow, ist zwar eine heftige
Einschränkung, aber sollte dem Spass erst mal keinen Abbruch tun.
Marcus B. schrieb:> muss man da nicht auch beachten, dass _delay_m/us(XXX) nicht ganz genau> ist, und auch der interne Quarz nicht ganz genau ist??
Im Prinzip ja.
Allerdings
* ist das ein Assembler Programm
* benutzt er keine _delay_ms
* ist die Ungenauigkeit im Quarz klein genug, dass sie sich in der
Servoposition höchstens um ein paar Hundertstel Grad bemerkbar
machen wird.
Und ob ein Servogetribe mechanisch diese Genauigkeit überhaupt
liefert, darf getrost bezweifelt werden. Für praktische Zwecke
ist das genau genug.
Karl heinz Buchegger schrob im Beitrag #1711490 u.A.:
>> Und schon ist geklärt, woher die Mär stammt, der Mega8 würde ohne>> weiteres Zutun mit 4 MHz laufen. ;-) Denn das Tutorial arbeitet nunmal>> mit einem Mega8 mit 4 MHz.>> Schon klar.> Aber auch das Tuorial beginnt damit, einen Quarz bzw. Quarzoszillator> mit 4Mhz an den Mega8 anzuschliessen :-)
Das ist ein alter Zopf aus der Zeit, als das Tutorial mit dem AT90S4433
gearbeitet hat. Beim Mega8 reicht es nicht, einen Quarz anzuschließen,
man muss ihn auch per Fusebits anmelden. Dies ist für ein Tutorial ein
unnötiges Risiko, denn ein Anfänger (wer sonst braucht ein Tutorial)
kann den AVR leicht verfusen und sich für ISP aussperren. Und das, wo
der Mega8 die meisten Aufgaben ganz gut im Auslieferungszustand (mit
internem Takt von 1MHz) erledigen kann.
> Der Code kommt mir vor wie von einem BASIC> Umsteiger: Hauptsache ich kann mit einem GOTO irgendwo hinspringen - das> ist cool.
GOTO ist auch in BASIC ein alter Zopf. Dafür gibt es Declare
Sub/Function und das einfache Aufrufen mit Namen, sowie Exit Do/For zum
stackverträglichen Verlassen von Schleifen. ASM stelle ich mir aber ohne
JMP recht schwierig vor.
In jeder Sprache kann man schlecht programmieren. In diesem Forum sehe
ich z.B. mehr schlechten C-Code als schlechten Basic-Code, weil hier
einfach mehr C-Anfänger vertreten sind. ;-) Dein Eindruck, dass
BASIC-Leute einen schlechten Programmierstil haben, stimmt aber, denn an
vielen Bildungsstätten wird eine Einführung in BASIC von Ausbildern
gelehrt, die selbst keine Ahnung davon haben.
>> Wenn man sich das wegdenkt , ist der Rest nicht mehr so schlimm. Das man> das Servo nur auf eine Handvoll Positionen einstellen kann, mangels> Teil-Möglichkeiten durch den Timer-Overflow, ist zwar eine heftige> Einschränkung, aber sollte dem Spass erst mal keinen Abbruch tun.
Naja, mit einem Timertakt von 1µs und Compare-Werten zwischen 1000 und
2000 lässt sich ein (oder mehrere!) Servo(s) schon recht präzies
steuern. Und dazu reicht ein CPU-Takt von 1MHz (Auslieferungszustand)
völlig aus.
Gruß in die Berge...
Irgendwie befürchte ich dass ich vor lauter Bäume den Wald nicht sehe.
Gut, am besten ist wohl wenn ich mir noch mal Interrupt auf der Zunge
zergehen lasse.
Dominik Fa schrieb:> Irgendwie befürchte ich dass ich vor lauter Bäume den Wald nicht sehe.>
fang einfach bei timer0_overflow zu lesen an.
verfolge jede Verzweigung, die in diesem Unterprogramm genommen werden
kann. Jeder einzelne Pfad, der durch dieses Unterprogramm existiert,
muss letztendlich in einem reti enden! Egal wieviele Verzweigungen es
gibt (und daher ist es schlau, wenn man nicht wie ein wilder hin und her
springt, denn dann wird es immer schwieriger das nachzuvollziehen)
Hab den Servo jetzt Unterkontrolle.
Ich glaub meine Programmstruktur schaut auch etwas besser aus.
Ein Oszi hab ich auch angehängt um mir das Ganz etwas anschaulicher zu
machen.
Zu dem Kommentar:
die Suchfunktion mit "Servo" liefert fst 1000 Treffer!
Man bekommt vielleicht viele Treffer, aber es würde nicht so viele
Beiträge dazu geben wenn ein Servo irgendwo gut erklärt wäre.
Vielleicht ein Punkt den man noch ins Tutorial einbaut.
Dominik Fa schrieb:> Hab den Servo jetzt Unterkontrolle.>> Ich glaub meine Programmstruktur schaut auch etwas besser aus.
Noch nicht wirklich
Schauen wir uns mal ein paar Stellen an
1
main:
2
out PORTD, r16
3
rjmp main
Warum wird hier an Port D ausgegeben, an dem dein Servo hängt?
Entweder du erledigst alle Servosteuerungen in der ISR oder du erledigst
sie in main.
Aber nicht beides. Das führt über kurz oder lang immer zu unliebsamen
Interaktionen zwischen verschiedenen Programmteilen
1
timer0_overflow:
2
cpi r16, 0b01111111
3
BREQ dec_servo_h
4
BRNE dec_servo_l
Sieh dir das einmal genau an.
Du benutzt einen BREQ um bei Gleichheit einen anderen Weg zu gehen. Wenn
dieser Weg aber nicht genommen wird, weil eben keine Gleichheit
vorhanden ist, dann MUSS das nächste BRNE wahr sein und springen. Es
besteht kein Grund da noch einmal zu testen, weil dieser Test immer wahr
sein wird.
Wenn du die nachfolgenden Codestücke umdrehen würdest, dann bräuchtest
du nur im BREQ Fall springen, im gegenteiligen Fall gehts dann einfach
im Code weiter. Oder aber natürlich im BRNE Fall. Wie auch immer du das
haben willst, aber eine Abfrage ist überflüsig.
1
timer0_overflow:
2
cpi r16, 0b01111111
3
BRNE dec_servo_l
4
5
dec_servo_h:
6
dec servo_h
7
breq set_servo_l
8
ldi r16, 0b01111111
9
out PORTD, r16
10
reti
11
12
dec_servo_l:
13
dec servo_l
14
breq set_servo_h
15
ldi r16, 0b01111110
16
out PORTD, r16
17
reti
Aber im Grunde machst du das ungeschickt.
Denn im Grunde würdest du nur 1 Zähler benötigen, der einfach nur die
'Zeit' runterzählt, bis wieder eine Aktion am Portpin notwendig ist. Ein
Portpin behält sowieso seinen Wert solange, bis du ihn änderst. Das
hätte dann den Vorteil, dass du so schnell wie möglich wieder aus der
ISR rauskommst, nämlich dann, wenn die Zeit noch nicht gekommen ist,
irgendetwas an den Portpins zu machen. Ausserdem brauchst du dann 1
Register weniger.
1
timer0_overflow:
2
dec wait_time
3
BREQ flip_pin
4
reti
5
6
flip_pin:
7
cpi r16, 0b01111111 // wie steht der Pin jetzt
8
BREQ dec_servo_l // auf 1, muss auf 0
9
10
dec_servo_h: // Pin ist low, auf high setzen
11
ldi r16, 0b01111111
12
out PORTD, r16
13
ldi wait_time, 0x05
14
reti
15
16
dec_servo_l: // Pin ist high, auf low setzen
17
ldi r16, 0b01111110
18
out PORTD, r16
19
ldi wait_time, 0x48
20
reti
Der Code ist kürzer, einfacher und es gibt weniger Pfade durch die ISR,
auf die man achten muss. Das ist jetzt noch kein grosses Thema, aber
spätestens dann, wenn Register gesichert und wiederhergestellt werden
müssen (denk zb an das SREG Register) wird das zu einem wichtigen Punkt.
Hab meine Schaltung auf zwei Servos aufgestockt. Welche jeweils 3
Positionen einnehmen.
Pro Servo habe ich dazu zwei Schalter mit denen ich die Postion
festlege.
Um mich auch ein bisschen in logische Operationen zu üben, werden beide
über einen Port ausgegeben.
Da ihr ja schon wisst das ich noch nicht viel Erfahrung habe wollte ich
fragen ob mein Lösung OK ist, oder was besser gemacht werden könnte.