PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Mehr als 2 Servos



badamtam
27.06.2006, 17:56
Hallo!

Ich möchte gerne 6-8 Servos mit einem Atmega 16 steuern. Gibt es irgendwo fertige Routinen, oder Anregungen für sowas, oder hat jemand vielleicht einen Vorschlag? Dabei wäre es gut, wenn die Servosteuerung den Atmega nicht lahmlegen würde, so das auch die eigentliche Aplikation noch darauf laufen kann.

Dank und Grüße!

franzl
28.06.2006, 21:37
Hallo,
schau dir mal das Beispielprogramm an das kannst du auch auf 6-8 Servos erweitern


'-----------------------------------------------------------------------
' (c) 2001-2005 MCS Electronics
' servo.bas demonstrates the SERVO option
'-----------------------------------------------------------------------

'Servo's need a pulse in order to operate
'with the config statement CONFIG SERVOS we can specify how many servo's we
'will use and which port pins are used
'A maximum of 16 servos might be used
'The SERVO statements use one byte for an interrupt counter and the TIMER0
'This means that you can not use TIMER0 anymore
'The reload value specifies the interval of the timer in uS
'Config Servos = 2 , Servo1 = Portb.0 , Servo2 = Portb.1 , Reload = 10
$regfile = "2313def.dat"

Config Servos = 1 , Servo1 = Portb.0 , Reload = 10
'as an option you can use TIMER1
'Config Servos = 2 , Servo1 = Portb.0 , Servo2 = Portb.1 , Reload = 10 , Timer = Timer1


'we use 2 servos with 10 uS resolution(steps)

'we must configure the port pins used to act as output
Config Portb = Output

'finally we must turn on the global interrupt
Enable Interrupts

'the servo() array is created automatic. You can used it to set the
'time the servo must be on
Servo(1) = 10 '10 times 10 = 100 uS on
'Servo(2) = 20 '20 times 10 = 200 uS on
Do
Loop

Dim I As Byte
Do
For I = 0 To 100
Servo(1) = I
Waitms 1000
Next

For I = 100 To 0 Step -1
' Servo(1) = I
Waitms 1000
Next
Loop
End

E-Fan
28.06.2006, 23:52
Wenn Du die Ausgänge der Ports im nacheinander per Software ansteuerst kannste ohne Probleme 9 Servos ansteuern und hast noch 2ms für andere Dinge Zeit.
Oder Du nimmst die Hardware-PWM. Ich hab damit schon etwas rumgefrickelt, bin aber mit der Auflösung noch nicht so ganz zufrieden.

Hanni
29.06.2006, 11:20
Wie wäre es, die Suchfiunktion dieses Forums zu bemühen ?
Diese Problematik würde nämlich schon zich mal angesprochen.

badamtam
29.06.2006, 20:42
Danke für den Tip, aber so wie ich das verstanden habe, unterstützt Bascom direkt nur 2 Servos. Mir ist das Prinzip der Servoanstuerung zwar grundsätzlich klar, ich bin aber nicht so fit mit dem Atmega, und Bascom, das ich mir jetzt auf die Schnelle zutrauen würde, eine entsprechende Ansteuerung zu basteln.

Prinzipell stelle ichs mir so vor, das die Servosteuerung in einem Timerinterrupt abgearbeitet wird und die Aplikation im Hauptprogramm läuft - sollte es aus irgendeinem Grund umgekehrt besser sein wärs natürlich auch ok.

Im Timerinterrupt müsste jeder Servo etwa alle 20ms, oder so oft nötig seine individuelle Impulsdauer verpasst bekommen.

@e-fan: Kannt Du mir ein Stück Quelltext geben, aus dem ersichtilich ist, wie Du die Servos ansteuerst?










Hallo,
schau dir mal das Beispielprogramm an das kannst du auch auf 6-8 Servos erweitern


'-----------------------------------------------------------------------
' (c) 2001-2005 MCS Electronics
' servo.bas demonstrates the SERVO option
'-----------------------------------------------------------------------

'Servo's need a pulse in order to operate
'with the config statement CONFIG SERVOS we can specify how many servo's we
'will use and which port pins are used
'A maximum of 16 servos might be used
'The SERVO statements use one byte for an interrupt counter and the TIMER0
'This means that you can not use TIMER0 anymore
'The reload value specifies the interval of the timer in uS
'Config Servos = 2 , Servo1 = Portb.0 , Servo2 = Portb.1 , Reload = 10
$regfile = "2313def.dat"

Config Servos = 1 , Servo1 = Portb.0 , Reload = 10
'as an option you can use TIMER1
'Config Servos = 2 , Servo1 = Portb.0 , Servo2 = Portb.1 , Reload = 10 , Timer = Timer1


'we use 2 servos with 10 uS resolution(steps)

'we must configure the port pins used to act as output
Config Portb = Output

'finally we must turn on the global interrupt
Enable Interrupts

'the servo() array is created automatic. You can used it to set the
'time the servo must be on
Servo(1) = 10 '10 times 10 = 100 uS on
'Servo(2) = 20 '20 times 10 = 200 uS on
Do
Loop

Dim I As Byte
Do
For I = 0 To 100
Servo(1) = I
Waitms 1000
Next

For I = 100 To 0 Step -1
' Servo(1) = I
Waitms 1000
Next
Loop
End

Hanni
30.06.2006, 16:03
Danke für den Tip, aber so wie ich das verstanden habe, unterstützt Bascom direkt nur 2 Servos. Mir ist das Prinzip der Servoanstuerung zwar grundsätzlich klar, ich bin aber nicht so fit mit dem Atmega, und Bascom, das ich mir jetzt auf die Schnelle zutrauen würde, eine entsprechende Ansteuerung zu basteln.

Die ist zwar sachon in Bascom drin, aber tu dir keinen Zwang an.

Mit etwas Forensuche wirst du sicherlich auch entsprechende Codebeispiele finden ...
Damit du das nich alleine machen musst, hilft dir dabei die Suchfunktion des Forums (https://www.roboternetz.de/phpBB2/search.php)

Und das Bascom nur 2 Servos direkt unterstützt halte ich übrigens für ein Gerücht.

hrei hat sich dazu schonmal geäußert ... siehe hier (https://www.roboternetz.de/phpBB2/viewtopic.php?p=180921#180921)

In seinem Quelltext steht im übrigen ... up to 14 Servos ... also von demher.

Grüße,
da Hanni.

Dirk
30.06.2006, 19:14
Hallo badamtam,

ich hänge dir 'mal meine Servo10.LIB zur Ansteuerung von 10 Servos mit einem 2313 mit 4 MHz an.

Prinzipell stelle ichs mir so vor, das die Servosteuerung in einem Timerinterrupt abgearbeitet wird und die Aplikation im Hauptprogramm läuft - sollte es aus irgendeinem Grund umgekehrt besser sein wärs natürlich auch ok.

Im Timerinterrupt müsste jeder Servo etwa alle 20ms, oder so oft nötig seine individuelle Impulsdauer verpasst bekommen.
Genau das macht meine Lib. Du kannst die sicher an den M16 anpassen!


copyright = Dirk O.
comment = Ansteuerung von 10 Servos
libversion = 2.00
date = 17.06.2006
statement = ------------------------------------
statement = Muss alle 20ms in einer Interrupt- !
statement = Service-Routine ausgeführt werden. !
statement = Geeignet für 1-10 MHz Taktfrequenz !
statement = ------------------------------------

[Servo10]
;Anschluss der 10 Servos:
.equ PORTB = $18 ;Portb des AT90S2313, ATtiny2313
.equ PINB0 = 0 ;Servo1
.equ PINB1 = 1 ;Servo2
.equ PINB2 = 2 ;Servo3
.equ PINB3 = 3 ;Servo4
.equ PINB4 = 4 ;Servo5
.equ PINB5 = 5 ;Servo6
.equ PINB6 = 6 ;Servo7
.equ PINB7 = 7 ;Servo8
.equ PORTD = $12 ;Portd des AT90S2313, ATtiny2313
.equ PIND5 = 5 ;Servo9
.equ PIND6 = 6 ;Servo10

;Register für die 10 Servo-Positionen:
.def Pos1 = r16
.def Pos2 = r17
.def Pos3 = r18
.def Pos4 = r19
.def Pos5 = r20
.def Pos6 = r21
.def Pos7 = r22
.def Pos8 = r23
.def Pos9 = r24
.def Pos10 = r25

.def Temp = r26
.def Position = r27

;BASCOM-Variablen:
; Servoleftpos -> BYTE für den linken Anschlag (1..255, 0=256)
; Impuls 0,6ms -> 1/4/8/10 MHz: 14/ 59/119 /149
; Impuls 0,8ms -> 1/4/8/10 MHz: 19/ 79/159 /199
; Impuls 1,0ms -> 1/4/8/10 MHz: 24/ 99/199 /249
; Servorightpos -> BYTE für den rechten Anschlag (0..255)
; Impuls 1,0ms -> 1/4/8/10 MHz: 28/111/182*/227*
; Impuls 1,2ms -> 1/4/8/10 MHz: 33/133/218*/---
; Impuls 1,4ms -> 1/4/8/10 MHz: 39/156/255*/---
; Zu *: Mit eingefügter Verzögerung (s.u.!)
; Servopos(10) -> BYTE-Array für die 10 Servo-Positionen
; (0..Servorightpos)

Servo10: ;Alle 20ms (Timer 50Hz)
;Impuls für alle 10 Servos starten:
; Der Abstand von 2 Zyklen zwischen den sbi-Befehlen orientiert sich am
; notwendigen Abstand der cbi-Befehle beim Beenden der Impulse (s.u.!).
sbi PORTB,PINB0 ;Servo1 high
*lds Pos1,{Servopos} ;2+
sbi PORTB,PINB1 ;Servo2 high
*lds Pos2,{Servopos+1}
sbi PORTB,PINB2 ;Servo3 high
*lds Pos3,{Servopos+2}
sbi PORTB,PINB3 ;Servo4 high
*lds Pos4,{Servopos+3}
sbi PORTB,PINB4 ;Servo5 high
*lds Pos5,{Servopos+4}
sbi PORTB,PINB5 ;Servo6 high
*lds Pos6,{Servopos+5}
sbi PORTB,PINB6 ;Servo7 high
*lds Pos7,{Servopos+6}
sbi PORTB,PINB7 ;Servo8 high
*lds Pos8,{Servopos+7}
sbi PORTD,PIND5 ;Servo9 high
*lds Pos9,{Servopos+8}
sbi PORTD,PIND6 ;Servo10 high
*lds Pos10,{Servopos+9}

;Impulsdauer (0,6..1ms) linker Anschlag:
; Starten und Beenden des Impulses brauchen zusammen 46 (40 + 6) Zyklen
; plus Verzögerung Servoleftpos * 40 Zyklen (10us bei 4 MHz) ergibt eine
; Impulsdauer linker Anschlag von (Servoleft * 10 + 11,5) us bei 4 MHz.
; Servoleftpos kann für eine bestimmte Impulsdauer so berechnet werden:
; Servoleftpos = Impulsdauer (us) * Taktfrequenz (MHz) / 40 - 1,15
*lds Temp,{Servoleftpos} ;+2=40 Linker Anschlag
Delay0:
call Wait37 ;37+
dec Temp ;1
brne Delay0 ;+2=40
nop

;Beenden des Impulses (nach 0..1,4ms) je nach Position:
; Die kompl. Schleife für alle 10 Servos besteht im Mittel aus 36 Zyklen.
; Damit beträgt die max. Auflösung 9us bei 4 MHz. Bei einer Impulsdauer
; von 1ms sind dann z.B. 111 Servo-Positionen (1000 / 9) ansteuerbar.
; Servorightpos kann für eine bestimmte Impulsdauer so berechnet werden:
; 1/ 4 MHz: Servorightpos = Impulsdauer (us) * Taktfrequenz (MHz) / 36
; 8/10 MHz: Servorightpos = Impulsdauer (us) * Taktfrequenz (MHz) / 44
ldi Position,255 ;1+
Nextpos:
inc Position ;1 Nächste Position (0..Servorightpos)
cp Pos1,Position ;1
brne Next2 ;1
cbi PORTB,PINB0 ;+2=6 Servo1 low
Next2:
cp Pos2,Position ;1+
brne Next3 ;2
cbi PORTB,PINB1 ;Servo2 low
Next3:
cp Pos3,Position
brne Next4
cbi PORTB,PINB2 ;Servo3 low
Next4:
cp Pos4,Position
brne Next5
cbi PORTB,PINB3 ;Servo4 low
Next5:
cp Pos5,Position
brne Next6
cbi PORTB,PINB4 ;Servo5 low
Next6:
cp Pos6,Position
brne Next7
cbi PORTB,PINB5 ;Servo6 low
Next7:
cp Pos7,Position
brne Next8
cbi PORTB,PINB6 ;Servo7 low
Next8:
cp Pos8,Position
brne Next9
cbi PORTB,PINB7 ;Servo8 low
Next9:
cp Pos9,Position
brne Next10
cbi PORTD,PIND5 ;Servo9 low
Next10:
cp Pos10,Position
brne Next11
cbi PORTD,PIND6 ;Servo10 low
Next11:
;call Wait8 ;Nur für 8/10 MHz: + 8 Zyklen!
*lds Temp,{Servorightpos} ;Rechter Anschlag
cp Temp,Position ;Anschlag erreicht?
brne Nextpos ;+2=32(40 b. 8/10 MHz) Nein -> weiter
;Impuls sicherheitshalber für alle Servos beenden!
; (Nur nötig für Servopos(n) > Servorightpos!)
ldi Temp,0
out PORTB,Temp ;Servo1..8 low
cbi PORTD,PIND5 ;Servo9 low
cbi PORTD,PIND6 ;Servo10 low
Wait8:
ret

Wait37: ;29 + 4 (call) + 4 (ret) = 37 Zyklen
call Wait8 ;8+
call Wait8 ;8
call Wait8 ;8
nop
nop
nop
nop
nop ;+1=29
ret
[end]

;---------------------------------------------------------------------------


Servo10 muss in einer ISR in BASCOM alle 20 ms aufgerufen werden:
Tim0_isr: 'alle 20ms
Timer0 = Startwert
'Impulse für die 10 Servos ausgeben:
Call Servo10
Return

Die Ports deines M16 must du neu anpassen!

Gruß Dirk

Hanni
30.06.2006, 19:48
Hmm ... nichts gegen deine sicherlich gut funktionierende lib ...

Nur, wenn ich das richtig übersehe wird die Servoansteuerung komplett in einer ISR abgearbeitet ...

d.H. alle 20 ms wird der Programmablauf dafür für 2 ms unterbrochen ... Eigentlich ist dieses eher recht ineffizient, vor allem dann wenn der µC noch etwas anderes machen soll ....

Des weiteren sind die Servopositionen doch:
1 ms = links,
1.5 ms = mitte
2 ms = rechts

oder täusche ich mich da sosehr ????

Dirk
30.06.2006, 20:27
Hallo Hanni,


nichts gegen deine sicherlich gut funktionierende lib ...
... jo, funktioniert gut.


d.H. alle 20 ms wird der Programmablauf dafür für 2 ms unterbrochen ... Eigentlich ist dieses eher recht ineffizient, vor allem dann wenn der µC noch etwas anderes machen soll ....
... ja, das stimmt. Andererseits sind 18 von 20ms (d.h. tatsächlich 90%) frei für's Hauptprogramm, das reicht bei mir, wenn da nichts zeitkritisches mehr stattfindet.


Des weiteren sind die Servopositionen doch:
1 ms = links,
1.5 ms = mitte
2 ms = rechts
Ja, genau. Da die Servos aber stark streuen, habe ich einen Rahmen von 0,6..2,4ms vorgesehen, den man aber nicht ausnutzen muss.

Gruß Dirk

Hanni
30.06.2006, 20:57
Nunja, das kann man halten wie ein Dachdecker :D
Ich zumindest würde es nicht so realisieren, zumal bei mir meistens noch "wichtigere" Dinge so nebenbei laufen.

Im übrigen könntest du deinen Assemblercode noch etwas optimieren ... zumindest in der resultierenden Codegröße .. da die Laufzeit an sich bei deinem speziellem Fall eher Wurst ist.

Anstatt :


Nextpos:
inc Position ;1 Nächste Position (0..Servorightpos)
cp Pos1,Position ;1
brne Next2 ;1
cbi PORTB,PINB0 ;+2=6 Servo1 low
Next2:
cp Pos2,Position ;1+
brne Next3 ;2
cbi PORTB,PINB1 ;Servo2 low
Next3:
cp Pos3,Position
brne Next4
cbi PORTB,PINB2 ;Servo3 low
Next4:
cp Pos4,Position
brne Next5
cbi PORTB,PINB3 ;Servo4 low
Next5:
cp Pos5,Position
brne Next6
cbi PORTB,PINB4 ;Servo5 low
Next6:
cp Pos6,Position
brne Next7
cbi PORTB,PINB5 ;Servo6 low
Next7:
cp Pos7,Position
brne Next8
cbi PORTB,PINB6 ;Servo7 low
Next8:
cp Pos8,Position
brne Next9
cbi PORTB,PINB7 ;Servo8 low


könnte man das ganze auch wie folgt schreiben:



Nextpos:
inc Position ;1 Nächste Position (0..Servorightpos)
cp Pos1,Position ;1
ror temp

cp Pos2,Position ;1+
ror temp

cp Pos3,Position
ror temp

cp Pos4,Position
ror temp

cp Pos5,Position
ror temp

cp Pos6,Position
ror temp

cp Pos7,Position
ror temp

cp Pos8,Position
ror temp

out PORTB, temp



aus:


sbi PORTB,PINB0 ;Servo1 high
*lds Pos1,{Servopos} ;2+
sbi PORTB,PINB1 ;Servo2 high
*lds Pos2,{Servopos+1}
sbi PORTB,PINB2 ;Servo3 high
*lds Pos3,{Servopos+2}
sbi PORTB,PINB3 ;Servo4 high
*lds Pos4,{Servopos+3}
sbi PORTB,PINB4 ;Servo5 high
*lds Pos5,{Servopos+4}
sbi PORTB,PINB5 ;Servo6 high
*lds Pos6,{Servopos+5}
sbi PORTB,PINB6 ;Servo7 high
*lds Pos7,{Servopos+6}
sbi PORTB,PINB7 ;Servo8 high
*lds Pos8,{Servopos+7}
sbi PORTD,PIND5 ;Servo9 high
*lds Pos9,{Servopos+8}
sbi PORTD,PIND6 ;Servo10 high
*lds Pos10,{Servopos+9}


könnte man im übrigen das folgende machen:



*lds Pos1,{Servopos} ;2+
*lds Pos2,{Servopos+1}
*lds Pos3,{Servopos+2}
*lds Pos4,{Servopos+3}
*lds Pos5,{Servopos+4}
*lds Pos6,{Servopos+5}
*lds Pos7,{Servopos+6}
*lds Pos8,{Servopos+7}
*lds Pos9,{Servopos+8}
*lds Pos10,{Servopos+9}
ldi temp, 0xff
out PORTB, temp
in temp, PORTD
ori temp, 0b01100000
out PORTD, temp


Warum das auch so geht findest du übrigens hier: Instruktion Set (http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf)

Sicherlich mag dir das vielleicht etwas unnötig erscheinen, allerdings macht es im Zweifelsfall neben ein paar gesparten Bytes eben auch einen gewissen Zeitfaktor aus (welcher ja in deinem Beispiel eh unrelevant ist).


Grüße,

da Hanni.

Dirk
30.06.2006, 22:54
Hallo Hanni,

danke für deine Verbesserungsvorschläge. =D>
Es ist klar, dass man da noch größenmäßig optimieren kann, allerdings hat meine Umsetzung den Vorteil, dass die Code-Laufzeiten "Impuls ein -> Impuls aus" für die 10 Servos sehr genau stimmen. Bei deinen Ansätzen könnte man das aber sicher auch erreichen.

Besonders deutlich wird das bei deiner letzten Anmerkung: Bei mir dienen die *lds PosX,{Servopos} nur als Füllmaterial (2 Takte) zwischen den sbi-Befehlen, die ich genau in dem Abstand haben wollte, da der jeweils zugehörige cbi-Befehl genau in demselben Abstand kommt. Natürlich kann man alle *lds zusammen fassen und auch die Ports schneller nacheinander auf high setzen (ich mache das ja auch am Ende beim löschen aller Impulse bei Überlauf!). Das wollte ich aber an dieser Stelle nicht.

Was mich schon interessiert ist die Variante mit ror temp, da damit eine noch höhere Auflösung (wichtig bes. bei den kleineren Taktfrequenzen) möglich ist. Ich werde da 'mal weiter machen.

Übrigens: Wie sieht z.B. deine Variante aus? Ich lerne nicht nur durch Anregungen zu meinem Code, sondern auch durch Anschauen von anderen Lösungen!

Gruß Dirk

Hanni
30.06.2006, 23:04
Hallo,
eine Möglichkeit bis zu 16 Servos anzusteuern hatte ich schoneinmal hier (https://www.roboternetz.de/phpBB2/viewtopic.php?p=181249#181249) gepostet.

Allerdings fehlt mir mangels eines Servos bis heute der Beweiss der Funktionsfähigkeit.
Sonderlich sauber ist dieser Code auch nicht geschrieben ... (es war mitten in der Nacht :D)

Realisiert wurde es im Übrigen für einen ATmega32 @ 4 MHz.

Nachtrag: So richtig zur Hochform läuft die ror Methode allerdings bei einer Software PWM auf ... 300 Hz sollten leicht drin sein bei 8 Kanälen a 8 Bit und 8 MHz Takt. Regelbar wäre dieses dann übrigens im Bereich von 0-254


Grüße,

da Hanni.