Forum: Mikrocontroller und Digitale Elektronik mehrere Servos


von SV (Gast)


Lesenswert?

Hallo habe eine Frage zum Code:

http://www.mikrocontroller.net/articles/Modellbauservo_Ansteuerung#Signalerzeugung_f.C3.BCr_mehrere_Servos_mittels_Timer_.28C.29



#define MILLISEC_BASE (F_CPU/Prescaler/1000)

gibt ja den Wert für 1ms wieder aber was ich nicht verstehe ist warum

#define CENTER (MILLISEC_BASE/2)

die Mitte sein soll da das ja dann 0,5ms sein müssten oder ?
und ein Servo braucht doch für seine Mittelstellung 1,5ms !


DANKE

von Klaus W. (mfgkw)


Lesenswert?

Dieser Wert wird doch im Testprogramm zum Vergleich der Servowerte 
genommen, und die gehen von 0 bis 255.
Es zählt also nach der immer vorhandenene Millisekunde, nicht ab dem 
Anfang des Pulses.

von Karl H. (kbuchegg)


Lesenswert?

SV schrieb:

> gibt ja den Wert für 1ms wieder aber was ich nicht verstehe ist warum
>
> #define CENTER (MILLISEC_BASE/2)
>
> die Mitte sein soll da das ja dann 0,5ms sein müssten oder ?
> und ein Servo braucht doch für seine Mittelstellung 1,5ms !
>

In der ISR wird bei der Berechnung des nächsten Compare Wertes noch der 
Basiswert für 1 Millisekunde zum Servowert dazugezählt. Aus den 0.5ms 
werden wieder 1.5ms.

Dadurch können die Servo-Positionswerte in einem uint8_t gehalten werden 
und man braucht
* keine 16 Bit speichern, nur damit man den Zahlenbereich hochtreibt
* beim Reinschreiben von neuen Servoewerten in das Arrays sich nicht um 
atomaren Zugriff kümmern.

von SV (Gast)


Lesenswert?

Ahhh ok soweit verstanden, glaub ich ;-)
wenn ich jetzt aber über den ADC Werte an die verschiedenen Servos geben 
will schreibe ich die dann ganz einfach in die "while" schleife unten

also als Beispiel:


...


while {1} {

ServoValue[0] ADCH;

....

oder wie gehe ich da am Besten vor ?

von SV (Gast)


Lesenswert?

SV schrieb:
> ServoValue[0] ADCH;

ServoValue[0] ADCH*MILLSEC_BASE;

so meinte ich

von Karl H. (kbuchegg)


Lesenswert?

SV schrieb:

> oder wie gehe ich da am Besten vor ?

Du benutzt die ADC Routinen aus dem Tutorial.
Die liefern dir den ADC Wert, der im Bereich 0 bis 1023 liegt.
Das kannst du so nicht brauchen. Du brauchst also eine Umrechnung aus 
dem Bereich 0 bis 1023 auf den Bereich 0 bis 2*CENTER.

Na wenn der Wert 1023 einem Wert von 2*CENTER entsprechen soll, wieviel 
entspricht dann dein Wert 'value', den du vom ACD bekommen hast?

Simpler Dreisatz
1
    1023     ......   2*CENTER
2
    value    ......    x
3
  --------------------------------
4
5
            value * 2 * CENTER
6
      x = ---------------------
7
                     1023

Wenn du anstellen von 1023 1024 nimmst, dann divdiert sich das leichter 
(ist nur Bitschieben).  Also
1
             value * 2 * CENTER
2
     x = -------------------------
3
                  1024
da kann man jetzt gleich mal die 2 wegkürzen
1
          value * CENTER
2
     x = ----------------
3
               512
jetzt muss man noch darauf achten, dass der Ausdruck im Zähler nicht 
größer als 65536 wird. Kann das passieren?

Angenommen CENTER ergibt 64 und value hat als Maximalwert vom ADC 1023 
(größer kann der nicht werden), dann sind wir da knapp an der Grenze 
drann. Wenn sich für CENTER ein Wert von 67 ergibt, dann sind wir schon 
über der Grenze.

So schlimm ist das aber nicht. Man kann zb den Wert für value durch 4 
dividieren und dafür den Nenner durch 4 dividieren, dann kommt 
mathematisch wieder alles richtig raus.
1
            value / 4 * CENTER
2
  x  =   ----------------------
3
                 128
und jetzt haben wir die Gewissheit, dass der Ausdruck im Zähler nicht 
größer als 65536 werden wird, weil value/4 nicht größer als 256 wird und 
CENTER per Definition schon mal nicht größer als 128 werden kann.


Ergo programmieren wir

1
.....
2
3
int main()
4
{
5
   .....
6
7
  ADC_init();
8
9
10
  while( 1 ) {
11
12
    ServoPuls[0] = ADC_read( 0 ) / 4 * CENTER / 128;
13
14
  }
15
}


Gut man könnte das jetzt auch ein wenig effizienter programmieren, weil 
es relativ sinnfrei ist, erst vom ADC ein 10 Bit Ergebnis zu holen, nur 
um dann erst mal die 2 untersten Bit wegzuwerfen. Allerdings ist das so 
(erst mal) alles andere als zeitkritisch, so dass wir uns die paar 
zusätzlichen Prozessortakte durchaus erlauben dürfen.
Aber es steht dir natürlich frei, die ADC Routinen entsprechend mittels 
ADLAR anzupassen und dann die Berechnung nochmal neu zu überarbeiten.

von Hannes L. (hannes)


Lesenswert?

Ohhh, Mann, wie kann man einfache Dinge nur so kompliziert machen...

...

von Karl H. (kbuchegg)


Lesenswert?

Ist doch nicht kompliziert :-)

Wenn man weiß wies geht, ist das eine Sache von 4 Minuten ab Starten des 
AVR-Studios :-) (und die 4 Minuten mein ich ernst, 5 Minuten klingt so 
kitschig. Aber recht viel mehr als Copy/Paste und eine Formel am Papier 
vorbereiten ist das nicht)

Edit: Ich weiß schon, dass dir als Assemblerspezi diese Vorgehensweise 
ein Greul ist :-) Aber das musst du zugeben: Entwicklungszeitmässig 
kommst du da nicht mit. Mit allem nötigen Respekt vor deinen nicht 
gerade geringen Assemblerfähigkeiten.

von Nn N. (jaytharevo)


Lesenswert?

Problematisch sind jetzt nur noch kleine Werte.
Denn durch die Ganzzahldivision kommt leider oft 0 raus und da es eine 
Multiplikation ist, ist alles 0. Also Value muss mal sicher 128 betragen 
damit da was ordentliches raus kommt.
Oder übersehe ich was?
Ich hab mich schon immer gefragt wie man dieses Problem ordentlich 
umgeht, da ich des öfteren schon davor stand^^.

Edit: Ich glaub ich habe meinen Blödsinn gefunden. Es wird zuerst *Value 
gerechnet und dann erst dividiert oder? :)

von Karl H. (kbuchegg)


Lesenswert?

Julian Schild schrieb:

> Oder übersehe ich was?

die Multiplikation mit einem Wert in der Größenordnung von ca 64 (das 
CENTER) vor der Division.

Der begrenzende Faktor ist nicht das Poti oder der ADC, sonder die 
Abstufung der PWM, die sich durch Taktfrequenz und Vorteiler bestimmt. 
Je besser er damit bei seiner Taktfrequenz die Obergrenze von 127 
trifft, umso mehr Servopositionen kann er anfahren. 127 Servopositionen 
hören sich erst mal nicht so prickelnd an, das relativiert sich aber in 
der Praxis. (Man könnte natürlich auch alles auf den 16-Bit Timer 
umbauen. Das Prinzip ist das gleiche)

Allenfalls könnte man noch eine Rundung einbauen, aber ich bezweifle, 
dass er sein Poti so genau einstellen kann, dass sich das bemerkbar 
macht.

von Michael (Gast)


Lesenswert?

Julian Schild schrieb:
> Problematisch sind jetzt nur noch kleine Werte.
Das ist nicht problematischer als bei den großen Werten, da sowohl ADC 
als auch Servo linear arbeiten.
Welche Genauigkeit schafft denn die Servo-Mechanik? Es nützt nichts, 
wenn die Ansteuerung wesentlich feiner ist.

von SV (Gast)


Lesenswert?

Also hab das jetz mal so alles umgesetzt der Servo bewegt sich,
aber in ziemlich großen deutlich sichtbaren Schritten kann man da noch 
etwas verbessern ?

von Nn N. (jaytharevo)


Lesenswert?

Hab doch "Edit" geschrieben :)

von Karl H. (kbuchegg)


Lesenswert?

SV schrieb:
> Also hab das jetz mal so alles umgesetzt der Servo bewegt sich,
> aber in ziemlich großen deutlich sichtbaren Schritten kann man da noch
> etwas verbessern ?

welche Taktfrequenz hast du? Welchen Vorteiler hast du genommen?

Du willst die beiden Werte so aneinander anpassen, dass der Ausdruck
1
F_CPU / PRESCALER / 1000

einen Wert möglichst nahe an 128 ergibt, aber auch nicht größer als 128 
wird. Wenn das nicht vernünftig möglich ist, dann könnt man auf den 
Timer 1 ausweichen. Der ist ein 16 Bit Timer und dementsprechend kann 
man dann die Abstufung feiner machen um mit dem zur Verfügung stehenden 
Timer-Zählbereich die 2ms abzudecken. Mit dem 8-Bit Timer stehen nun mal 
nur 256 Abstufungen dafür zur Verfügung, von denen 128 für die 
Basis-Millisekunde drauf gehen und somit nur 128 Servopositionen (die 
restliche Millisekunde auf 2ms) übrig bleiben.

Edit: Du musst natürlich dann auch die Bits für den Vorteiler 
entsprechend anpassen. Siehe Datenblatt.

von SV (Gast)


Lesenswert?

Habe 1 Mhz, Vorteiler 128,
da eigentlich nur beschrieben ist das ich unter 128 bleiben soll !

von Karl H. (kbuchegg)


Lesenswert?

SV schrieb:
> Habe 1 Mhz, Vorteiler 128,
> da eigentlich nur beschrieben ist das ich unter 128 bleiben soll !

Ist richtig.
generell solltest du allerdings Code nicht einfach nur übernehmen, 
sondern auch ein wenig durchsehen, was mit den Werten gemacht wird. Und 
dann stellt sich schnell raus, dass du mit 1Mhz und 128 einen Wert von
1
  1000000 / 128 / 1000 = 7
für 1 Millisekunde bekommst. d.h. 1 Millisekunde wird in 7 Abstufungen 
aufgeteilt und demenstprechend hast du auch nur 7 Servopositionen.

Also: Beim übernehmen von Code immer mitdenken. Hirn ausschalten war 
gestern, als Mutti noch mit aufs Klo kam. Wer programmieren will, muss 
wissen was er tut.

Bei 1Mhz wäre ein Vorteiler von 8 angebracht.
1
1000000 / 8 / 1000 = 125
also schon ganz gut am Maximum drann.

beim Mega16 wäre das
1
#define PRESCALER      8
2
#define PRESCALER_BITS (1<<CS21)

von SV (Gast)


Lesenswert?

Ahh Ok ! ich versuche seit Nachmittag so n bischen den Code zu verstehen 
weil ich das eig generell nicht möchte, Kopieren :-)
aber ich muss mich morgen nochmal ausgeschlafen dahintersetzten :-))

aber das hilft schon mal wieder ein bischen weiter Danke

von Karl H. (kbuchegg)


Lesenswert?

SV schrieb:
> Ahh Ok ! ich versuche seit Nachmittag so n bischen den Code zu verstehen
> weil ich das eig generell nicht möchte, Kopieren :-)

Er ist im Grunde sehr sehr simpel.

Die Frage ist: wie weit muss der Zähler zählen, damit er dafür genau die 
vorgesehene Zeit braucht.

Damit das aber möglich ist, muss der Vorteiler so eingestellt werden, 
dass der Timer möglichst 2ms braucht um von 0 bis 255 zu zählen. 
Erreicht er die 2ms schon, wenn er erst bei 240 ist, dann ist das auch 
ok. Aber 300 geht nicht, weil der Timer nicht so weit zählen kann.

Muss der Timer also von 0 bis 240 zählen um damit 2ms abzumessen, dann 
teilen sich diese 240 so auf

   120          für die konstanten 1ms, die obligatorisch sind
   weitere 120  für den variablen Teil, der die Servoposition darstellt.
                ja nachdem ob zu den konstanten 120 dann noch 0 oder 120
                oder ein Wert dazwischen dazukommt, ergibt sich dann
                in Summe ein Wert zwischen 120 bis 240. Also Zeiten
                zwischen 1 und 2 ms.

Die 120 finden sich im Code als MILLISEC_BASE wieder ("Basis für 1 
Millisekunde")

Und es ist auch klar, dass du diesen Wert so hoch wie möglich treiben 
willst. Denn wenn der Timer schon bei einem Zählerwert von 
beispielsweise 8 bei 2ms angelangt ist, dann hast du nur die Zählerwerte 
4, 5, 6, 7 und 8 übrig, mit denen du die variable Zeit von 1 
Millisekunde aufteilen kannst. Demenstprechend hast du dann natürlich 
aber auch nur diese Servopositionen zur Verfügung. Und 5 Stück 
Servopositionen sind dann ein bischen arg wenig.


Und so läuft es dann:
Pin einschalten, Timer läuft los.
Der Timer ist so eingestellt, dass er nach der berechneten Zahl (zb 180 
für 1.5ms) einen Interrupt auslöst. Im Interrupt wird der Pin wieder 
abgeschaltet und der nächste Pin für das nächste Servo eingeschaltet. 
Wieder wird der Timer mit dem diesem Servo zugeordneten Endwert versorgt 
und der Timer zählt für sich diese Zeit ab. Udn so gehts immer reihum. 
Wird ein Pin abgeschaltet wird sofort der nächste Pin eingeschaltet und 
der Timer mit dem diesem Servo zugeordneten Zeitwert (in Form des 
Vergleichsewrtes) versorgt. Nach dieser Zeit kommt ein Interrupt und es 
wird auf das nächste Servo weitergeschaltet.

Die Pulse für 8 Servos werden also hintereinander generiert und nicht 
gleichzeitig nebeneinander.


Edit: die 240 bzw. 120 sind jetzt natürlich nur beispielsweise. Bei dir 
kommen da andere Zahlen raus. Abhängig von Taktfrequenz und Vorteiler.

von Klaus (Gast)


Lesenswert?

Sag mal Karl Heinz, warst du in einem früheren Leben mal ein 
Tibetanischer Schweigemönch oder was muß man kauen, um so eine 
Engelsgeduld zu bekommen? Chapeau !

MfG Klaus

von SV (Gast)


Lesenswert?

Klaus schrieb:
> Sag mal Karl Heinz, warst du in einem früheren Leben mal ein
> Tibetanischer Schweigemönch oder was muß man kauen, um so eine
> Engelsgeduld zu bekommen? Chapeau !
>
> MfG Klaus


:-))) Ja die hat er mit mir

Respekt

und vorallem sehr nett !

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.