PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Servo:Stellung-Richtung-Geschwindigkeit?



sulu
06.12.2005, 16:12
Hi, Leute.
Meien Servos sind heute gekommen und jetzt möchte ich sie mit einer Rn control ansteuern.
Ich hab mir auch schonmal die BascomHilfe angeschaut. Da steht folgendes Programm . Ich werd daraus aber nicht schlau:
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
'we use 2 servos with 10 uS resolution

'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) = 100 '1000 uS on
Servo(2) = 200 ' 2000 uS on


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

For I = 200 To 0 Step -1
Servo(1) = I
Waitms 1000
Next
Loop
End
[code]

KAnn mir einer erklären womit jezt genau Richtung ,Stellung etc. programmiert werden?

Danke

-=[Cassiopeia]=-
06.12.2005, 18:46
Servus sulu,
das mit den servos ist eigentlich recht simpel.
wenn du die wie in dem beispiel in der Hilfe configuriert hast sollte folgender test code eigentlich gehen.
Bei mir war servo(1)= 100 ca die Mittelstellung
und Servo(1)=50 ca linkes Maximum
und Servo(1)=150 ca rechtes Maximum.
das hängt aber immer vom Servo ab und musst du einfach ausprobieren.

Du kannst natürlich auch alle werte dazwischen nehmen, das sind dann eben die Stellungen dazwischen. ist echt recht simpel.
Wenn du ihn jetzt langsamer von links nach rechts bewegen willst musst du das über zwischenschritte machen. also 50-pause-60-pause-70-pause-80-pause-90-pause-100... etc.

Hier mal ein kleines testprogramm (sub):


dim servopos as integer
dim left as integer
dim right as integer
left=50
right=150
center=100

print "Servo Test"
Wait 1

print = "Mitte"
Servopos = Center
Servo(1) = Servopos
Wait 1

Servopos = left 'nach links fahren
Servo(1) = Servopos
Wait 1

print "Rechtsdrehung"
For Servopos = left To right Step 10
Servo(1) = Servopos
Waitms 10
Next
Wait 1

print "Linksdrehung"
For Servopos = right To left Step -10
Servo(1) = Servopos
Waitms 10
Next
Wait 1

print "Mitte"
Servopos = Center
Servo(1) = Servopos

sulu
07.12.2005, 16:15
hi, funktioniert irgendwie nicht. Das Programm hat keinen Fehler.
Ich hab nen Conrad servo.Also Kabel rot schwarz und gelb. Wie muss ich die anschließen?

-=[Cassiopeia]=-
07.12.2005, 16:43
also
gelb auf den signal ausgang. (bei dir b0 und b1)
rot auf +5V
schwarz auf masse
dann sollte es laufen.
ich hab das ganze auch für nen 5€ servo von conrad gemacht...

sulu
07.12.2005, 17:33
Mit dem Programm hab ich das jetzt laufen lassen:
geht aber nicht . Hab schon eine Stunden lang versucht das zunm laufen zu bringen:



Dim Center As Integer
dim servopos as integer
dim left as integer
dim right as integer
left=50
right=150
center=100

Config Servos = 2 , Servo1 = Portb.1 , Servo2 = Portb.0 , Reload = 10
'we use 2 servos with 10 uS resolution

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

'finally we must turn on the global interrupt
Enable Interrupts



print "Servo Test"
Wait 1


Servopos = Center
Servo(1) = Servopos
Wait 1

Servopos = left 'nach links fahren
Servo(1) = Servopos
Wait 1

print "Rechtsdrehung"
For Servopos = left To right Step 10
Servo(1) = Servopos
Waitms 10
Next
Wait 1

print "Linksdrehung"
For Servopos = right To left Step -10
Servo(1) = Servopos
Waitms 10
Next
Wait 1

print "Mitte"
Servopos = Center
Servo(1) = Servopos

-=[Cassiopeia]=-
07.12.2005, 22:53
also wenn du das rncontrol 1.4 hast und testweise einen servo an pina0 hinhängst, dann muss folgender code eigentlich gehen:



$regfile = "m32def.dat"
$crystal = 16000000
$baud = 9600

Dim Center As Integer
dim servopos as integer
dim left as integer
dim right as integer
left=50
right=150
center=100
servopos=center


'servo config
Config Servos = 1 , Servo1 = Porta.0 , Reload = 10
Config Pina.0 = Output
enable interrupts
'ende servo config

do
print "Servo Test"
Wait 1


Servopos = Center
Servo(1) = Servopos
Wait 1

Servopos = left 'nach links fahren
Servo(1) = Servopos
Wait 1

print "Rechtsdrehung"
For Servopos = left To right Step 10
Servo(1) = Servopos
Waitms 10
Next
Wait 1

print "Linksdrehung"
For Servopos = right To left Step -10
Servo(1) = Servopos
Waitms 10
Next
Wait 1

print "Mitte"
Servopos = Center
Servo(1) = Servopos
loop

-=[Cassiopeia]=-
07.12.2005, 22:56
oder noch simpler:



$regfile = "m32def.dat"
$crystal = 16000000
$baud = 9600

'servo config
Config Servos = 1 , Servo1 = Porta.0 , Reload = 10
Config Pina.0 = Output
enable interrupts
'ende servo config

do
servo(1)=50
wait 1
servo(1)=100
wait 1
servo(1)=150
wait 1

loop

sulu
08.12.2005, 13:35
Funktioniert auch damit nicht. Ich habe keine Ahnung warum nicht. Es scheint fast so als wäre da was am Board abgeraucht

sulu
08.12.2005, 14:18
Wie kann ich denn prüfen ob das Board noch in Ordnung ist?
Ich vermute da ist iergendein Widerstand zu heiß geworden ,oder so

-=[Cassiopeia]=-
08.12.2005, 14:58
also wenns nur ein widerstand ist dann hast du ja glück gehabt.
du könntest evtl mal das testprogramm aufspielen was ursprünglich drauf ist und schauen ob das läuft.
va ob die leds an port c blinken.
dann weißt du zumindest das port c io ist.
so leicht kann des aber nicht abrauchen... was hast du denn noch damit gemacht?
einfach nen servo anzuschließen glaub ich dann nicht...
aber was du noch mal checken kannst sind die servos.
ich hab da auch schon kaputte vom conrad erwischt.
dann mal aufgemacht und die schaun innen aus als hätten das 3 jährige kinder zu übungszwecken gelötet -> aussen hui innen pfui...
wirklich fies.
vielleicht liegts daran

sulu
08.12.2005, 15:16
Also die Leds blinken alle , und die Motoren laufen auch gut. Das mit den Servos glaube ich weniger, denn ich hab da sjetzt schn mit zweien ausprobiert. Aber ich werde die jetzt mal aufschrauben

-=[Cassiopeia]=-
08.12.2005, 15:27
was du machen kannst ,ich kann für den tip aber keine haftung übernehmen,
servo am netzteil mit +5v und gnd anschliessen und dann 5v pulse manuell auf die steuerleitung geben...
hat bei mir gefunzt und dann sollte sich der servo auf jedenfall etwas bewegen bzw hin und herzucken...

sulu
08.12.2005, 19:05
AHHHHH, Heureka!

Jetzt klapt's. Ich gab nochmal alle Kabel neu verbunden, danach liefs.
Danke Casiopeia , hast mir sehr geholfen!
Manchmal hat man eben ein Brett vor dem Kopf ](*,) ](*,)

sulu
08.12.2005, 19:06
AHHHHH, Heureka!

Jetzt klapt's. Ich gab nochmal alle Kabel neu verbunden, danach liefs.
Danke Casiopeia , hast mir sehr geholfen!
Manchmal hat man eben ein Brett vor dem Kopf ](*,) ](*,)

sulu
08.12.2005, 19:06
AHHHHH, Heureka!

Jetzt klapt's. Ich gab nochmal alle Kabel neu verbunden, danach liefs.
Danke Casiopeia , hast mir sehr geholfen!
Manchmal hat man eben ein Brett vor dem Kopf ](*,) ](*,)

-=[Cassiopeia]=-
08.12.2005, 19:58
ja super wunderbar..
am anfang macht man eben mal ein paar fehler oder hat so den einen oder anderen balken vor dem kopf ging mir auch so...

sulu
09.12.2005, 17:13
Ach so noch was:
wie kann ich svos gleichzeitig "laufen" lassen"?

-=[Cassiopeia]=-
09.12.2005, 17:19
mit laufen meinst dur drehen?
einfach nacheinander die position übergeben wohin sie fahren sollen.
servo(1)=150
servo(2)=150
wait 1
servo(1)=50
servo(2)=50
dürfte so gehen oder?
ich hab selber nur einen servo dran...

Space Teddy
28.04.2006, 16:55
Hallo, habe auch einen conrad servo (5€) damit dreht er hin und her...



$regfile = "2313DEF.dat"
$crystal = 8000000

$hwstack = 32 ' default use 32 for the hardware stack
$swstack = 10 ' default use 10 for the SW stack
$framesize = 40 ' default use 40 for the frame space

'
Config Servos = 1 , Servo1 = Portd.6 , Reload = 10
'
Config Portd = Output

Enable Interrupts
Dim I As Byte

Do

For I = 32 To 135
Servo(1) = I
'Waitms 10
Next

waitms 500
For I = 135 To 32 Step -1
Servo(1) = I
'Waitms 10
Next
Waitms 500
Loop
End

Goblin
30.04.2006, 08:19
TACH!

Ich beende mal meinen andern Servo-Meckerthread und schließe mich diesem an. Auch ein Conrad-Servo, folgendes Problem:



$regfile = "m8def.dat"
$crystal = 1000000
$baud = 4800
Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 1
Config Servos = 2 , Servo1 = Portc.0 , Servo2 = Portc.1 , Reload = 10
Config Portc = Output
Enable Interrupts
Dim A As Byte
Dim Eins As Byte
Let Eins = 15

Servo(1) = Eins
Servo(2) = Eins

Schleife:

Inputbin , A
If A = "l" Then Gosub Links
If A = "r" Then Gosub Rechts

Goto Schleife

Links:
Let Eins = Eins + 1
Servo(1) = Eins
Servo(2) = Eins
Print Eins
Return

Rechts:
Let Eins = Eins - 1
Servo(1) = Eins
Servo(2) = Eins
Print Eins
Return


Ich steige mit 15 ein. Mit "l" und "r" kann ich den Puls modifizieren und er wird mir auch zurückgegeben. Dabei fand ich raus dass der Servo mit Werten von 5 - 35 funzt und ausserhalb dieser Bereiche ruckelt. Woran liegt das? Hab überigens 3 Conradservos getestet, immer das gleiche

Goblin
05.05.2006, 09:00
Warum keine Antwort? z.B. auf die Frage "Wieso steuern alle ihre Servos mit 100-200 an und ich mit 5-35?" oder "Wieso macht der Servo nur 15 Steps pro 90°?" Kann der nicht mehr? Weil das ist mir viel zu wenig. Und wenn ich den Servo von 30 auf 15 fahren lasse, wieso ruckelt er dabei so stark? Bin frustriert... ;)

hrei
05.05.2006, 09:55
Hallo,


Warum keine Antwort? z.B. auf die Frage "Wieso steuern alle ihre Servos mit 100-200 an und ich mit 5-35?" oder "Wieso macht der Servo nur 15 Steps pro 90°?" Kann der nicht mehr? Weil das ist mir viel zu wenig. Und wenn ich den Servo von 30 auf 15 fahren lasse, wieso ruckelt er dabei so stark? Bin frustriert... ;)

nun, die allgemeine Verwirrung beruht vermutlich auf mehreren Tastsachen:

1) Im allgemeinen ist es so, daß Bascom verlässlich mit unterschiedlichen Taktfrequenzen zurechtkommt und die vorgefertigten Funktionen aus der Angabe "Crystal" die jeweils passenden Parameter selbst errechnet. Das ist bei der Funktion Servos nicht so!

2) Das von MCS gelieferte Programmbeispiel enthält in seinen Erklärungen zu Timing etc. fast nur Schrott.

3) MCS geht mit keinem Wort auf die Einzelheiten der PWM-Erzeugung in diesem Fall ein.

Ich selbst habe für Servos nur selten Verwendung, habe mir den Quatsch aber nun mal genauer angesehen. Dabei kommt als Zwischenstand folgender Text heraus, der die Beispielbeschreibung ersetzen sollte:


'Servos need a positive pulse in order to operate. In general the pulse has to
'have a width of 1 mS (left) to 2 mS (right). This pulse has to be repeated
'approximately every 20 mS.
'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 14 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 repetition interval of the pulse.
'
'To gain the requierd intervall of 20 mS, a reload value of 10 will work with
'a crystall of 16 Mhz, a reload value of 5 with a crystal of 8 Mhz. 4 Mhz and
'lower changes the prescaler internally, you have to find the right value your-
'self (as long as I don't find time to mesure this :-)).

'The pulsewidth depends on the value we write to the corresponding servo array
'-Servo(n)-. In praxis this means for 8 MHz 84 to 168 (1 mS to 2 mS), for 16
'MHz 78 to 156.

Hier 2 Messergebnisse, die das Gesagte verbildlichen:

http://www.henrik-reimers.de/control/Downloads/servo1.gif

http://www.henrik-reimers.de/control/Downloads/servo2.gif


Viele Grüße
Henrik

Goblin
05.05.2006, 17:50
ahaaa, sehr aufschlussreich, danke!

was glaubst du in welchem bereich die richtige reload-zeit bei dem internen 1mhz-takt ist? zwischen 1 und 5?

hrei
05.05.2006, 18:16
Hallo,


ahaaa, sehr aufschlussreich, danke!

was glaubst du in welchem bereich die richtige reload-zeit bei dem internen 1mhz-takt ist? zwischen 1 und 5?

bitte gib mir Zeit bis morgen. Dann kann ich es Dir genau sagen. Glaube ich mache mal eine Tabelle mit den gängigen Frequenzen, wenn etwas mehr Zeit ist.

BTW: Meine "Erkenntnisse" sind mittlerweile nicht nur theorethischer Natur, sondern mit den allseits beliebten 5,-- EUR Conrad Servos praktisch erprobt. Da zappelt nix, da rappelt nix, alles lauft sanft und zuverlässig.

Viele Grüße
Henrik

Goblin
05.05.2006, 21:01
das wäre supi. dann bewegt sich meine kamera auch endlich vernünftig....

hrei
05.05.2006, 21:39
Hallo,


das wäre supi. dann bewegt sich meine kamera auch endlich vernünftig....

da habe ich leider schlechte Nachrichten für Dich, wenn Du bei 1 MHz bleiben willst - es geht nicht, nie und nimmer.. Kann auch nicht, wenn ich mir die angelegte Interruptroutine ansehe.

Mit 4 MHz geht es dann mit Ach und Krach, die Wiederholrate von 31 mS verträgt z.B. der Servo von Conrad netterweise gerade noch.

Die Werte für 4 MHz im Einzelnen:

Reload = 1
Servo Mitte: 80
Servo Rechts: 125
Servo Links: 40

Zu viel anderem ist der Prozessor dann nicht mehr zu gebrauchen, weil er vollauf mit der Servosteuerung beschäftigt ist.
Bei dem Thema Servo dürfte mir im Augenblick kein MCS-Verantwortlicher persönlich begegnen. Nicht weil es nicht funktioniert, das tut es ja innerhalb der gegebenen Grenzen., sondern weil die Beschreibung so grottenfalsch ist, daß man erst mal das Oszilloskop hernehmen muss, um der Sache auf die Schliche zu kommen.
Das war ja nun schon öfter Thema und ist Mark auch bekannt, scheint aber nicht zu interessieren.

Das benutzte Testprogramm hier (englisch, weil ich es mit Rüffel demnächst ins englische Forum stellen will - aber erst wenn ich mich abgekühlt habe):



'--------------------------- Servotest -----------------------------------------

$regfile = "m8def.dat"
$crystal = 4000000 'internal choosen
'$crystal = 7372800
'$crystal = 14745600
'$crystal = 16000000

$hwstack = 64
$swstack = 64
$framesize = 64

$lib "mcsbyteint.lbx"

'-------------------------------------------------------------------------------
'The following remarks are valid and tested with BASCOM V.1.11.5.2

'Servos need a positive pulse in order to operate. In general the pulse has to
'have a width of 1 mS (left) to 2 mS (right). This pulse has to be repeated
'approximately every 20 mS.
'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 14 servos can 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 repetition interval of the pulse.
'
'To gain the requierd intervall of 20 mS, a reload value of 10 will work with
'a crystall of 16 Mhz, a reload value of 5 with a crystal of 8 Mhz.
'
'4 Mhz works with a reload of 1 but we never ever will get the standard repe-
'titon rate of 20 mS. The best to be reached is 31 mS. This still will work
'with most servos, but side effects may occur.
'
'Lower clockfrequencies simply don't work!
'
'The pulsewidth depends on the value we write to the corresponding servo array
'-Servo(n)-. In praxis and as a rule of thumb this means for 8 MHz 84 to 168
'-(1 mS to 2 mS), for 16 MHz 78 to 156 and for 4 Mhz 35 to 130.


Const S_middle = 80
Const S_right = 125
Const S_left = 40

Config Servos = 1 , Servo1 = Portd.3 , Reload = 1

Config Portd.3 = Output

Enable Interrupts


Servo(1) = S_middle

Do

Waitms 20

Servo(1) = S_middle
Waitms 100
Servo(1) = S_right
Waitms 100
Servo(1) = S_middle
Waitms 100
Servo(1) = S_left
Waitms 100

Loop

End


'The relevant ISR:
'+0000002F: 930F PUSH R16 Push register on stack
'+00000030: 938F PUSH R24 Push register on stack
'+00000031: B78F IN R24,0x3F In from I/O location
'+00000032: 938F PUSH R24 Push register on stack
'+00000033: 93AF PUSH R26 Push register on stack
'+00000034: 93BF PUSH R27 Push register on stack
'+00000035: 939F PUSH R25 Push register on stack
'+00000036: E6A0 LDI R26,0x60 Load immediate
'+00000037: E0B0 LDI R27,0x00 Load immediate
'+00000038: 918D LD R24,X+ Load indirect and postincrement
'+00000039: 919C LD R25,X Load indirect
'+0000003A: 9601 ADIW R24,0x01 Add immediate to word
'+0000003B: 939C ST X,R25 Store indirect
'+0000003C: 938E ST -X,R24 Store indirect and predecrement
'+0000003D: 2700 CLR R16 Clear Register
'+0000003E: 3081 CPI R24,0x01 Compare with immediate
'+0000003F: 0790 CPC R25,R16 Compare with carry
'+00000040: F411 BRNE PC+0x03 Branch if not equal
'+00000041: 9A93 SBI 0x12,3 Set bit in I/O register
'+00000042: C00F RJMP PC+0x0010 Relative jump
'+00000043: 3090 CPI R25,0x00 Compare with immediate
'+00000044: F041 BREQ PC+0x09 Branch if equal
'+00000045: 3D80 CPI R24,0xD0 Compare with immediate
'+00000046: E007 LDI R16,0x07 Load immediate
'+00000047: 0790 CPC R25,R16 Compare with carry
'+00000048: F048 BRCS PC+0x0A Branch if carry set
'+00000049: 2700 CLR R16 Clear Register
'+0000004A: 930D ST X+,R16 Store indirect and postincrement
'+0000004B: 930C ST X,R16 Store indirect
'+0000004C: C005 RJMP PC+0x0006 Relative jump
'+0000004D: 9612 ADIW R26,0x02 Add immediate to word
'+0000004E: 919D LD R25,X+ Load indirect and postincrement
'+0000004F: 1789 CP R24,R25 Compare
'+00000050: F008 BRCS PC+0x02 Branch if carry set
'+00000051: 9893 CBI 0x12,3 Clear bit in I/O register
'+00000052: EF8D LDI R24,0xFD Load immediate
'+00000053: BF82 OUT 0x32,R24 Out to I/O location
'+00000054: 919F POP R25 Pop register from stack
'+00000055: 91BF POP R27 Pop register from stack
'+00000056: 91AF POP R26 Pop register from stack
'+00000057: 918F POP R24 Pop register from stack
'+00000058: BF8F OUT 0x3F,R24 Out to I/O location
'+00000059: 918F POP R24 Pop register from stack
'+0000005A: 910F POP R16 Pop register from stack
'+0000005B: 9518 RETI Interrupt return


BTW: Das alles gilt für die aktuelle Version V1.11.8.2

Grüße
Henrik

Hanni
05.05.2006, 21:48
Hmm .. sieht sehr ineffektiv aus das ganze ...

Zumal ich persönlich ein Problem damit habe, nen Mikrocontroller nur für einen Servo zu verwenden ...

Diese ISR die dort anhängt und wohl vom Compiler erstellt wurde ist wirklich nicht das gelbe vom Ei .... den Rest vom betreffendem Code möchte ich in dem Fall lieber nicht sehen ....

Wie wäre es diese Routinen selbst und effizient zu schreiben (vorzugsweise direkt in assembler) ?

Ach ja, das ist übrigens einer der Punkte wieso ich Bascom nicht wirklich mag ....

hrei
05.05.2006, 22:10
Hmm .. sieht sehr ineffektiv aus das ganze ...

Zumal ich persönlich ein Problem damit habe, nen Mikrocontroller nur für einen Servo zu verwenden ...

Diese ISR die dort anhängt und wohl vom Compiler erstellt wurde ist wirklich nicht das gelbe vom Ei .... den Rest vom betreffendem Code möchte ich in dem Fall lieber nicht sehen ....

Wie wäre es diese Routinen selbst und effizient zu schreiben (vorzugsweise direkt in assembler) ?

Ach ja, das ist übrigens einer der Punkte wieso ich Bascom nicht wirklich mag ....

Warum schreibst Du dann hier und nutzt Bascom? Du wirst lachen, für mich selbst schreibe ich solche Scherze auch selbst und anders. Nur wird das so oder so eng mit 1 MHz. wenn man die Spezifikationen einhalten will.

Da das Problem aber hier öfter auftaucht, wollte ich wissen, woran es liegt. Im Gegensatzt zu Dir mag ich Bascom nämlich. Daran ändern auch solche Kunstfehler in der Beschreibung/ im Manual nichts.
Von "nur für einen Servo nutzen" kann auch nicht die Rede sein, bis zu 14 Servos können angesprochen werden. Wenn Du gelesen hättest was ich geschrieben habe, sollte Dir das auch klar sein. Da es aber eine gute Idee[tm] ist, sowas erst mal mit der schlichtesten Möglichkeit anzutesten, habe ich das auch getan.

Henrik

Hanni
05.05.2006, 22:33
Warum schreibst Du dann hier und nutzt Bascom? Du wirst lachen, für mich selbst schreibe ich solche Scherze auch selbst und anders.

Wer sagt das ich Basecom nutze ... ich schrieb nicht umsonst dieses eingeklammerte Assembler :D
Ich sehe nur gelegentlich hier im Board, WIE Bascom scheinbar einfache Dinge in derartig ineffiziente Routinen packt ...

BTW: Der Controller könnte durchaus mehr wie die paar Servos steuern vor allem dann, wenn man vom "waitms" System weg geht ... und sich gewisse Routinen evtl doch lieber selber coded.

Zumal die Aufgabenstellung ansich ja recht einfach ist ... eine bestimmte Anzahl von Impulsen einer gewissen Impulsbreite und mit einer gewissen Pulsfolgefrequenz zu generieren ...

Ich wage zu behaupten, das mit dem richtigem Code das Ganze auch mit 1 MHz bestens läuft.

Mal grob übern Daumen gepeilt:

1 MHz entspricht einer Taktzeit von 1 µs
1 ms = 1000 Takte
20 ms = 20000 Takte ...

und das ganze soll nicht funktionieren .. das glaube ich nicht.

Grüße,

da Hanni.


€dit: Wie gesagt, nix gegen Bascom, nur ist es in dem Fall doch recht ineffizient ...

hrei
05.05.2006, 22:53
Wer sagt das ich Basecom nutze ... ich schrieb nicht umsonst dieses eingeklammerte Assembler :D

Ach so... einer von denen... Muss Spaß machen sich selbst zu lesen, wenn man von Dingen schreibt, die man selbst in der Praxis nicht oder nur unzureichend ausprobiert hat. Mir gehen die Assemblerfreaks auf den Keks, die den Bascom Neulingen nix besseres zu bieten haben als: "Ist doch ganz einfach in Assembler".


Zumal die Aufgabenstellung ansich ja recht einfach ist ... eine bestimmte Anzahl von Impulsen einer gewissen Impulsbreite und mit einer gewissen Pulsfolgefrequenz zu generieren ...

Ich wage zu behaupten, das mit dem richtigem Code das Ganze auch mit 1 MHz bestens läuft.


Laber nicht... mach! Alles andere ist doch Geschwätz.

Edit: Genauer lesen und das Programm verstehen wär' auch nicht schlecht. Die WAITMS sind doch nur dazu da um den Servo in der Demo kurz anzuhalten. Mit der eigentlichen Problemstellung hat das nix, aber auch garnix zu tun.
Henrik

Hanni
06.05.2006, 11:18
Ich wage zu behaupten, das mit dem richtigem Code das Ganze auch mit 1 MHz bestens läuft.


Laber nicht... mach! Alles andere ist doch Geschwätz.

Ich setzt mich mal ran, wenn die Sonne vom Himmel verschwunden ist.

Bis Dahin, schönen Samstag :D

da Hanni.

Goblin
06.05.2006, 13:36
na jetzt bin ich aber gespannt. ich werd erstmal probieren, den atmega mit externem takt laufen zu lassen. sind für mich gottseidank nur 2 jumper (hardwareseitig). nur dann kommt wieder diese ätzende fusebitterei...

Hanni
07.05.2006, 04:34
Guten Morgen.

Wenn man von dem Ursprunglichem Problem ausgeht (der Post von Goblin am 0.04.2006 um 08:19 Uhr) bieten sich generell 2 Lösungswege an. (es ging darum, wieso er seine zwei Servos nur mit relativ wenigen Schritten ansteuern kann.

Die erste Möglichkeit wäre eine reine Software Realisierung:

Diese dürfte in seinem Fall nur eine marginale bis gar keine Verbesserung bringen.
Dieses dürfte vor allem dann zutreffen, wenn man innerhalb des gewissen 20 ms Fensters als Vorgabe für die Pulsfolgefrequenz bleiben möchte.


Eine weiter Möglichkeit ist die reine Hardware Realisierung:

In diesem Fall ist es sogar die einzige, die bis zu 4 Servos mit nur 1 MHz ausreichend genau ansteuern kann. (bis zu 6 bei den ATmega x8 Mikrocontrollern).
Das hier verwendete Prinzip ist die Nutzung der integrierten Hardware PWM. Da es sich in diesem Fall nur um 2 Servos handelt bietet sich der Timer 1 mit seinen 2 PWM Kanälen direkt an. Desweiteren kann dieser den Servos bis zu 1001 verschiedene Stellungen Vorgeben (ich hab keine Ahnung ob und inwiefern diese Auflösung noch Sinn macht).
Bei 4 Servos, der damit verbundenen Nutzung aller Timer sowie einer gleichen Auflösung können es immernoch 125 Schrittte sein (welche wohl sinnvoller sein dürfte).

Nachfolgend ein Codebeispiel für 2 Servos mit Timer 1 ... leider in Assembler .. da ich mich mit Bascom noch nicht wirklich beschäftigt habe.



; #
; # Testroutine für 2 Servos
; #
; # Controller: ATmega 32
; # Takt: 1 MHz
; #
; # Methode: - PWM über 16 Bit Timer (Timer 1)
; # - Timermode: 14 (siehe Datenblatt Seite 109)
; #
; # Pinbelegung: PD4 - Ausgang - Servo 2
; # PD5 - Ausgang - Servo 1
; #
; # Servoregelbereich: 1001 Einzelschritte (kA ob das nen Servo unterstützt ...)
; # im Detail 999 - 1999 im nachfolgendem Programm (1ms - 2 ms)
; #

;
; Definitionsfile
.include "m32def.inc"

;
; Registeraliase
.undef XL ; hier unnötig und anderweitig genutzt
.undef XH

.def temp = r16

.def servo_1_l = r24 ; Stellung Servo 1
.def servo_1_h = r25
.def servo_2_l = r26 ; Stellung Servo 2
.def servo_2_h = r27

.cseg
.org 0x00
;
; ISR Vektoren
jmp isr_reset ; Reset

.org 0x2A
;
; HW Initialisierung
isr_reset:

ldi temp, high(RAMEND) ; Stackpointer
out SPH, temp
ldi temp, low(RAMEND)
out SPL, temp

ldi temp, 0xFF ; alle ungenutzten Ports -> Eingang + Pullup
out PORTA, temp
out PORTB, temp
out PORTC, temp

ldi temp, (1<<DDD4) | (1<<DDD5) ; Port D4 & D5 -> Ausgang, Rest -> Eingang + PU
out DDRD, temp
ldi temp, (1<<PD0) | (1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD6) | (1<<PD7)
out PORTD, temp

ldi temp, high(1499) ; Timer 1 Setup
out OCR1AH, temp
ldi temp, low(1499)
out OCR1AL, temp
ldi temp, high(1499)
out OCR1BH, temp
ldi temp, low(1499)
out OCR1BL, temp

ldi temp, high(19999)
out ICR1H, temp
ldi temp, low(19999)
out ICR1L, temp

ldi temp, (1<<COM1A1) | (1<<COM1B1) | (1<<WGM11)
out TCCR1A, temp

ldi temp, (1<<WGM13) | (1<<WGM12) | (1<<CS10)
out TCCR1B, temp

;
; Main Loop
loop:
ldi servo_1_h, high(1999) ; Servo 1 - ganz nach rechts
ldi servo_1_l, low(1999)

ldi servo_2_h, high(999) ; Servo 2 - ganz nach links
ldi servo_2_l, low(999)

rcall set_servos ; Werte übergeben

rcall warten_1s

ldi servo_1_h, high(1499) ; Servo 1 - Mittelstellung
ldi servo_1_l, low(1499)

ldi servo_2_h, high(1499) ; Servo 2 - Mittelstellung
ldi servo_2_l, low(1499)

rcall set_servos ; Werte übergeben

rcall warten_1s

ldi servo_1_h, high(999) ; Servo 1 - ganz nach links
ldi servo_1_l, low(999)

ldi servo_2_h, high(1999) ; Servo 2 - ganz nach rechts
ldi servo_2_l, low(1999)

rcall set_servos ; Werte übergeben

rcall warten_1s

ldi temp, high(1499)

loop_a: ; Servos sanft in Mittelstellung fahren - dauert ca. 25 Sekunden
sbiw servo_2_l, 1
adiw servo_1_l, 1

rcall set_servos
rcall warten_50ms

cpi servo_1_l, low(1499)
cpc servo_1_h, temp
brne loop_a

rcall warten_1s
rcall warten_1s
rcall warten_1s

jmp loop

set_servos:
out OCR1AH, servo_1_h
out OCR1AL, servo_1_l

out OCR1BH, servo_2_h
out OCR1BL, servo_2_l

ret


warten_50ms: ; 50 ms Warteschleife ...
ldi YL, 0xD2
ldi YH, 0x30
sbiw YL, 1
brne PC-1

ret

warten_1s: ; 1 s Warteschleife
ldi YL, 0x3E
ldi YH, 0x0D
ldi ZL, 0x03
subi YL, 1
sbci YH, 0
sbci ZL, 0
brne PC-3
nop

ret

Wie man sieht, ist der Code doch etwas umfangreicher, was wohl auch auf die Testroutinen für die 2 Servos zurückzuführen ist.

Nun zum zweitem Problem,
es geht darum, 16 Servos (oder auch mehr) mit 4 MHz mit einer ausreichenden Schrittweite anzusteuern.
Eine Lösung mit HardwarePWM scheidet schon aufgrund der hohen Anzahl der benötigten direkt PWM fähigen Ausgänge aus.

In diesem Beispiel gehe ich einfach mal davon aus, das an allen Pins der Ports A und B je ein Servo angeschlossen ist, der angesteuert werden soll.

Aufgrund der 'Tatsachen, daß hrei mit der in Basecom impletierten Bibliothek, bei nur einem Servo ?, eine Interuptlast von ca 150% haben dürfte* bin ich einen etwas anderen Weg gegangen.

Ich habe die 20 ms der verfügbaren Zeit in 20 Slots mit je einer Millisekunde aufgeteilt.

Hier einmal der prinzipielle Zeitliche Ablauf:

1. ms: nix großartiges tun
2. ms: die ersten 8 Servowerte laden und den jeweiligen PORT komplett auf High setzten, anschließen nix großartiges tun.
3. ms: die entsprechenden Pulswerte ausgeben, bei Slotende - den jeweiligen PORT komplett auf Low setzen
4. ms: die zweiten 8 Servowerte laden und den jeweiligen PORT komplett auf High setzten, anschließen nix großartiges tun.
5. ms: die entsprechenden Pulswerte ausgeben, bei Slotende - den jeweiligen PORT komplett auf Low setzen
5. - 20. ms: nix großartiges tun

Anmerkung: theoretisch könnte man das ganze noch weiter Aufsplitten, also z.B. 8x2 Zeitfenster zu je 2 Servos, ob das allerdings Sinn macht weiß ich im Moment nicht (ich bin gerade zu faul das zu coden).

Die Interuptlast bewegt sich so um die 80% - 90%, was auch nicht wirklich ein guter Wert ist.

Zeitkritsche Anwendungen, wie z.B. eine Software USART oder längere Interuptroutinen sind bei diesem Code auch nicht mehr wirklich empfehlenswert.

Ach ja, und hier der Code (wieder in Assembler):



; #
; # Testroutine für 16 Servos
; #
; # Controller: ATmega 32
; # Takt: 4 MHz
; #
; # Methode: - Software PWM mittels 8 Bit Timer (Timer 0)
; # - Timermode: 2 (siehe Datenblatt Seite 80)
; #
; # Pinbelegung: PA0 - Ausgang - Servo 1
; # PA1 - Ausgang - Servo 2
; # PA2 - Ausgang - Servo 3
; # PA3 - Ausgang - Servo 4
; # PA4 - Ausgang - Servo 5
; # PA5 - Ausgang - Servo 6
; # PA6 - Ausgang - Servo 7
; # PA7 - Ausgang - Servo 8
; #
; # PB0 - Ausgang - Servo 9
; # PB1 - Ausgang - Servo 10
; # PB2 - Ausgang - Servo 11
; # PB3 - Ausgang - Servo 12
; # PB4 - Ausgang - Servo 13
; # PB5 - Ausgang - Servo 14
; # PB6 - Ausgang - Servo 15
; # PB7 - Ausgang - Servo 16
; #
; # Servoregelbereich: 0 - 85 = 85 Schritte (die scheinbare Ungereimtheit liegt am Software PWM Algo ...)
; # Pulsfolgefrequenz: 20399.75 µs
; #

;
; Definitionsfile
.include "m32def.inc"

;
; Registeraliase
.def sregsave = r5
.def null = r6
.def voll = r7

.def servo_1 = r8
.def servo_2 = r9
.def servo_3 = r10
.def servo_4 = r11
.def servo_5 = r12
.def servo_6 = r13
.def servo_7 = r14
.def servo_8 = r15

.def status = r16 ; Statusregister
; 7 =
; 6 =
; 5 =
; 4 =
; 3 =
; 2 =
; 1 = Preload Servo 9 - 16 done
; 0 = Preload Servo 1 - 8 done
.def temp = r17
.def pulswidth = r18
.def cycle = r19
.def output = r20

;
; Speicher reservieren
.dseg

servo_data: .byte 16


.cseg
.org 0x00
;
; ISR Vektoren
jmp isr_reset ; Reset
.org 0x02
reti ; Int 0
.org 0x04
reti ; Int 1
.org 0x06
reti ; int 2
.org 0x08
reti ; T2 Compare
.org 0x0A
reti ; T2 Overflow
.org 0x0C
reti ; T1 Capture
.org 0x0E
reti ; T1 Compare A
.org 0x10
reti ; T1 Compare B
.org 0x12
reti ; T1 Overflow
.org 0x14
jmp isr_t0_co ; T0 Compare
.org 0x16
reti ; T0 Overflow
.org 0x18
reti ; SPI Transfer Complete
.org 0x1A
reti ; USART - RX Complete
.org 0x1C
reti ; USART - UDR Empty
.org 0x1E
reti ; USART - RX Complete
.org 0x20
reti ; ADC Complete
.org 0x22
reti ; EEPROM Ready
.org 0x24
reti ; Analog Comperator
.org 0x26
reti ; TWI
.org 0x28
reti ; Store Program Memory Ready

.org 0x2A
; HW Initialisierung
isr_reset:
ldi temp, high(RAMEND) ; Stackpointer
out SPH, temp
ldi temp, low(RAMEND)
out SPL, temp

ldi temp, 0xFF ; alle ungenutzten Ports -> Eingang + Pullup
out PORTC, temp
out PORTD, temp

out DDRA, temp ; Port A & B -> Ausgang
out DDRB, temp

ldi temp, 47 ; Timer 0 konfigurieren
out OCR0, temp

ldi temp, (1<<WGM01) | (1<<CS00)
out TCCR0, temp

in temp, TIMSK
ori temp, (1<<OCIE0)
out TIMSK, temp

;
; Speicher und Register initialisieren
ldi temp, 42
ldi ZH, high(servo_data)
ldi ZL, low(servo_data)

st Z+, temp
st Z+, temp
st Z+, temp
st Z+, temp
st Z+, temp
st Z+, temp
st Z+, temp
st Z+, temp
st Z+, temp
st Z+, temp
st Z+, temp
st Z+, temp
st Z+, temp
st Z+, temp
st Z+, temp
st Z, temp

clr pulswidth
clr cycle
clr status
clr null
ldi temp, 0xFF
mov voll, temp
out TCNT0, null

clr temp

sei

;
; Mainloop
loop:
ldi temp, 85

sts servo_data, null ; Servo 1 nach links
sts servo_data+1, temp ; Servo 2 nach rechts
sts servo_data+2, null ; Servo 3 nach links
sts servo_data+3, temp ; Servo 4 nach rechts
sts servo_data+4, null ; Servo 5 nach links
sts servo_data+5, temp ; Servo 6 nach rechts
sts servo_data+6, null ; Servo 7 nach links
sts servo_data+7, temp ; Servo 8 nach rechts
sts servo_data+8, null ; Servo 9 nach links
sts servo_data+9, temp ; Servo 10 nach rechts
sts servo_data+10, null ; Servo 11 nach links
sts servo_data+11, temp ; Servo 12 nach rechts
sts servo_data+12, null ; Servo 13 nach links
sts servo_data+12, temp ; Servo 14 nach rechts
sts servo_data+14, null ; Servo 15 nach links
sts servo_data+15, temp ; Servo 16 nach rechts

rcall warten ; ne Weile Warten

sts servo_data, temp ; und nun genau andersrum ....
sts servo_data+1, null
sts servo_data+2, temp
sts servo_data+3, null
sts servo_data+4, temp
sts servo_data+5, null
sts servo_data+6, temp
sts servo_data+7, null
sts servo_data+8, temp
sts servo_data+9, null
sts servo_data+10, temp
sts servo_data+11, null
sts servo_data+12, temp
sts servo_data+13, null
sts servo_data+14, temp
sts servo_data+15, null

rcall warten ; ne Weile Warten

ldi temp, 42 ; und nun alle Servos auf Mittelstellung

sts servo_data, temp
sts servo_data+1, temp
sts servo_data+2, temp
sts servo_data+3, temp
sts servo_data+4, temp
sts servo_data+5, temp
sts servo_data+6, temp
sts servo_data+7, temp
sts servo_data+8, temp
sts servo_data+9, temp
sts servo_data+10, temp
sts servo_data+11, temp
sts servo_data+12, temp
sts servo_data+13, temp
sts servo_data+14, temp
sts servo_data+15, temp

rcall warten ; ne Weile Warten

jmp loop

;
; Subroutinen
warten: ; Warteschleife
ldi XL, 0x3E ; geschätzte Wartezeit aufgrund der
ldi XH, 0x0D ; hohen Interuptlast ca. 5-6 Sekunden
ldi YL, 0x03 ;
subi XL, 1
sbci XH, 0
sbci YL, 0
brne PC-3
nop

ret

;
; ISR Routinen
isr_t0_co:
in sregsave, SREG

ldi ZH, high(isr_t0_oc_table)
ldi ZL, low(isr_t0_oc_table)
add ZL, cycle
adc ZH, null
ijmp

isr_t0_oc_table:
rjmp isr_t0_oc_0
rjmp isr_t0_oc_1
rjmp isr_t0_oc_2
rjmp isr_t0_oc_3
rjmp isr_t0_oc_4
rjmp isr_t0_oc_0
rjmp isr_t0_oc_0
rjmp isr_t0_oc_0
rjmp isr_t0_oc_0
rjmp isr_t0_oc_0
rjmp isr_t0_oc_0
rjmp isr_t0_oc_0
rjmp isr_t0_oc_0
rjmp isr_t0_oc_0
rjmp isr_t0_oc_0
rjmp isr_t0_oc_0
rjmp isr_t0_oc_0
rjmp isr_t0_oc_0
rjmp isr_t0_oc_0
rjmp isr_t0_oc_19

isr_t0_oc_0:
andi status, 0b11111100

inc pulswidth
cpi pulswidth, 85
brne PC+3
clr pulswidth
inc cycle

out SREG, sregsave

reti

isr_t0_oc_1:
sbrc status, 0
rjmp isr_t0_oc_1_done

lds servo_1, servo_data
lds servo_2, servo_data+1
lds servo_3, servo_data+2
lds servo_4, servo_data+3
lds servo_5, servo_data+4
lds servo_6, servo_data+5
lds servo_7, servo_data+6
lds servo_8, servo_data+7
ori status, 0b00000001

isr_t0_oc_1_done:
out PORTA, voll
inc pulswidth
cpi pulswidth, 85
brne PC+3
clr pulswidth
inc cycle

out SREG, sregsave

reti

isr_t0_oc_2:
cp pulswidth, servo_1
ror output
cp pulswidth, servo_2
ror output
cp pulswidth, servo_3
ror output
cp pulswidth, servo_4
ror output
cp pulswidth, servo_5
ror output
cp pulswidth, servo_6
ror output
cp pulswidth, servo_7
ror output
cp pulswidth, servo_8
ror output

inc pulswidth
cpi pulswidth, 85
brne PC+4
clr pulswidth
inc cycle
clr output
out PORTA, output

out SREG, sregsave

reti

isr_t0_oc_3:
sbrc status, 0 ; Preload schon erledigt ?
rjmp isr_t0_oc_3_done ; - Ja

lds servo_1, servo_data+8
lds servo_2, servo_data+9
lds servo_3, servo_data+10
lds servo_4, servo_data+11
lds servo_5, servo_data+12
lds servo_6, servo_data+13
lds servo_7, servo_data+14
lds servo_8, servo_data+15
ori status, 0b00000010

isr_t0_oc_3_done:
out PORTB, voll
inc pulswidth
cpi pulswidth, 85
brne PC+3
clr pulswidth
inc cycle

out SREG, sregsave

reti

isr_t0_oc_4:
cp pulswidth, servo_1
ror output
cp pulswidth, servo_2
ror output
cp pulswidth, servo_3
ror output
cp pulswidth, servo_4
ror output
cp pulswidth, servo_5
ror output
cp pulswidth, servo_6
ror output
cp pulswidth, servo_7
ror output
cp pulswidth, servo_8
ror output

inc pulswidth
cpi pulswidth, 85
brne PC+4
clr pulswidth
inc cycle
clr output
out PORTB, output

out SREG, sregsave

reti



isr_t0_oc_19:
inc pulswidth
cpi pulswidth, 85
brne PC+6
clr pulswidth
inc cycle
cpi cycle, 20
brne PC+2
clr cycle

out SREG, sregsave

reti


Wie man sieht gehe ich in diesem Beispiel aus Gründen der Geschwindigkeit in der ISR Routine recht verschwenderisch mit den vorhanden Resourcen (sprich den Registern) um. auch ist das ganze nicht wirklich nach Codegröße optimiert.

Allerdings bietet der Code mit kleineren Änderungen auch die Möglichkeit 32 Servos anzusprechen ... nur, wo sollen dann die Daten für diese herkommen ?

Eines ist vielleicht noch recht interessant: Ein ATmega 8 würde den selben Code ein klein wenig schneller abarbeiten (1-2 Takte je Interupt .. bin mir da nicht so ganz sicher).

@ hrei:
Ich denke, ich habe meine Lektion gelernt.

Fazit:
es gibt immer mehrere mögliche Wege die zum Ziel führen.
selbst scheinbar viel Zeit kann bei genauerer Betrachtung sehr knapp werden... So hat man bei einem MHz und einer Auflösug von 100 Schritten auf einmal nur noch 10µs Zeit um die entsprechenden Werte einzustellen. 10 µs entsprechen nunmal aber exakt 10 Takten ... und die braucht der ATmega 32 schon um nur in die ISR und wieder raus zu springen ... ohne irgendetwas zu tun. (3 für den Sprung zum ISR Vektor, 3 für den Sprung zur ISR Routine und 4 für den Rücksprung zum Programm)
16 Servos bei 1 MHz gehen mit einer vernünftigen Auflösung in einem vernünftigem Zeitraster nicht wirklich. (siehe weitere ausführungen zu Punkt 2)
ich denke, bei genauerer Betrachtung ist auch mein Code wesentlich verbesserungsbedürftig. Insbesondere die doch recht hohe Interuptlast stört mich schon etwas ....

hrei, wenn es dir nichts ausmacht würde ich dich bitten, diese beiden Programme einmal mit einem Oskar und / oder Servos zu überprüfen und mir mitzuteilen, ob meine Programme wirklich funkionieren. Laut Simulation im AVR studio tun sie es, ich hätte allerdings gerne Gewissheit.

Für die, die diese Codes dennoch recht interessant finden, ist das ganze gezippt nocheinmal im Anhang zu finden.


Und nun noch nen schönen und vor allem sonnigen Sontag,

da Hanni.

------------------------------------------------------------------

* Interuplast: meine Auslegung: Zeit die die ISR von der theoretisch maximal möglichen Zeit** belegt.
** maximal mögliche Zeit: Zeit zwischen 2 Interupts des gleichen Types

hrei
07.05.2006, 11:43
Hallo Hanni,

zunächst mal meinen uneingeschränkten Respekt für Deine Bemüngen, die Theorie in die Praxis umzusetzten. Endlich mal jemand, der es nicht beim Labern lässt.

Für jetzt und hier nur ganz kurz dazu (es gibt zu viel gutes Wetter und anderes außerhalb des Forums):

Wenn man es praktisch betreibt, fällt auf, daß Bascom hier gar nicht so übel abschneidet, was die Effizienz anbelangt. Es ist ja immer im Auge zu behlten, daß von einer Hochsprache im allgemeinen und von Bascom im besonderen die weitestgehende Portabilität verlangt wird. Also vom Tiny 2313 bis zum Mega128 soll derselbe Befehl/dieselbe Konfiguration benutzt werden können.

Das führt zum benutzen eines Timers, mit trotzdem freier Zuordnung der Portpins. Unter diesen Umständen muss man eben auf jeden Fall einen höheren Takt als 1 Mhz benutzen, um zu halbwegs ordentlichen Auflösungen und Ausführungsgeschwindigkeiten zu kommen.

Ist ja auch kein Problem, Goblin zum Beispiel braucht ja dafür keinen externen Quarz, er muss nur den internen Oszillator auf 8 Mhz "umfusen".

Deine Routinen werde ich mir bei etwas mehr Zeit genauer ansehen und messen (sie haben es verdient) und schaun, inwieweit davon nicht das ein oder andere für Sonderfälle als Lib ausgestaltet werden kann.

Viele Grüße
Henrik

Hanni
07.05.2006, 12:52
Deine Routinen werde ich mir bei etwas mehr Zeit genauer ansehen und messen (sie haben es verdient) und schaun, inwieweit davon nicht das ein oder andere für Sonderfälle als Lib ausgestaltet werden kann.

Danke für die Blumen :D
Für ein derartiges Projekt bin ich sicherlich zu haben, auch wenn meine Zeitplanung zum Teil doch recht eng gesteckt ist.

Grüße.

da Hanni.

Hanni
22.05.2006, 21:34
Hmm .. was ist dabei rausgekommen ?

(also beim Anshauen ?)

Grüße,

da Hanni.