Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] PWM für Servo mit eingebautem PWM gen. (Atmel 2313)
Philipp_c
27.09.2004, 23:18
Hallo
kann ich zum ansteuern eines Modellbau Servos, den eingebauten PWM des 2313 nutzen oder muss ich das selber basteln? Ich programmiere in ASM (bzw fange gerade damit an). Ideal wäre natrürlich wenn ich irgendwo dann einfach mein Byte reinschreiben könnte was dann direkt in die Servostellung umgesetzt wird.
Vielen Dank für die Hilfe
Gottfreak
28.09.2004, 01:30
Das Hardware-PWM könnte für EIN Servo funktionieren(steht im Datenblatt, wie man das einstellt(das Intervall sollte irgendwas um 20ms sein, ist aber unkritisch.). Ich nehme an, du weisst, wie ein Servosignal aufgebaut ist?). Wenn du die genauen Werte haben willst, die du in die Register schreiben musst, poste mal deine Taktfrequenz.
Bei der geringen Frequenz kannst du aber(zumal in ASM) auch ohne weiteres eine Software-Version erzeugen(hängt davon ab, was du sonst noch gleichzeitig machen willst.). Damit lassen sich dann auch mehrere Servos stellen(was hast du eigentlich damit vor?).
Ich hab' mal den Code von meinem Software-PWM drangehängt(das Programm kann gleichzeitig noch Servosignale verstehen. Mehr war bei mir nicht nötig, deshalb lässt sich wahrscheinlich noch viel verfeinern.).
.include "2313def.inc" ;Definitionsdatei laden
.Def RXtemp =r27
.Def S1Pos = r28 ;Servo1 invertiert an PB0 - Position in 64tel
.Def S2Pos = r29
.equ S1Offset=4 ;Offset in 256µS
.equ S2Offset=4
.equ CLOCK = 8000000
.equ BAUD = 9600
.equ UBRRVAL = CLOCK/(BAUD*16)-1
.org 0x000
rjmp start
.org 0x00B
rjmp onRXD ;Interupt für RX
start: ldi r16,low(ramend) ;Stackpointer auf
out spl,r16 ;RAM-Ende setzen
ldi r16,0x0f
out ddrb,r16 ;PortB.0-3 aus Ausgang setzen
ldi r16,0
out portb,r16 ;Interne Pullups von PortD abstellen, Ausgänge auf low
ldi r16,0b00000010
out TCCR1B,r16 ;Prescaler für Timer1 auf 8 stellen
ldi r16, UBRRVAL
out UBRR, r16 ;Baudrate einstellen
ldi r16, 0b10011000
out UCR, r16 ;TX anmachen, RX anmachen, RX-Int anmachen
ldi S1Pos,32 ;Startpositionen setzen
ldi S2Pos,32
sei ;interupts an
loop:
ldi r16,0 ;Timer1 zurücksetzen
out TCNT1H,r16
;ldi r16,0
out TCNT1L,r16
LDI r16,0
out portb,r16 ;Signal an
anloop: ;Endlosschleife die die Servopulse überwacht und nach ca. 20ms zu loop zurückspringt
IN r18,TCNT1L
IN r16,TCNT1H
;ÜBERPRÜFEN,OB PULS FÜR SERVO1 FERTIG
SBIC portb,0
rjmp s1fertig ;Wenn der Puls schon aus ist, Überprüfung skippen
mov r17,s1pos
ANDI r17,0xF0 ;Low-Nibble von S1pos löschen
SWAP r17 ;Und Nibbles tauschen - effektiv durch 16 teilen
SUBI r17,-S1Offset ;Offset hinzufügen
cp r16,r17 ;S1pos mit Timer vergleichen, Ausmachen skippen, wenn Timer kleiner
brlo S1Fertig
mov r17,s1pos ;Sonst mit dem Low-Teil des Timers vergleichen
ANDI r17,0x0F ;H-Nibble von S1pos löschen
SWAP r17 ;Und Nibbles tauschen - effektiv mit 16 multiplizieren
cp r18,r17
brlo S1Fertig ;Wenn Timer kleiner, anlassen
SBI portb,0 ;Servo1 Puls ausmachen
S1fertig:
;ÜBERPRÜFEN,OB PULS FÜR SERVO2 FERTIG
SBIC portb,1
rjmp s2fertig ;Wenn der Puls schon aus ist, Überprüfung skippen
mov r17,s2pos
ANDI r17,0xF0 ;Low-Nibble von S2pos löschen
SWAP r17 ;Und Nibbles tauschen - effektiv durch 16 teilen
SUBI r17,-S2Offset ;Offset hinzufügen
cp r16,r17 ;S2pos mit Timer vergleichen, Ausmachen skippen, wenn Timer kleiner
brlo S2Fertig
mov r17,s2pos ;Sonst mit dem Low-Teil des Timers vergleichen
ANDI r17,0x0F ;H-Nibble von S2pos löschen
SWAP r17 ;Und Nibbles tauschen - effektiv mit 16 multiplizieren
cp r18,r17
brlo S2Fertig ;Wenn Timer kleiner, anlassen
SBI portb,1 ;Servo2 Puls ausmachen
S2fertig:
cpi r16,80 ;Nach Ablauf von ca. 20ms neuen Zyklus starten
brsh loop
rjmp anloop
onRXD:
IN S1POS,UDR
IN rxtemp,TCNT1L
OUT UDR,rxtemp
reti
Philipp_c
28.09.2004, 14:11
Danke erstmal. Ja, das Prinzip der Ansteuerung ist mir klar. Ich wollte damit das Gas bei meinem Auto betätigen (Sicherheitsfunktionen kommen da noch rein). Letzendlich würde ich gern die Drehzahl mit dem Controller messen und auf Knopfdruck soll er diese dann halten können (quasi Tempomat).
Ich habe mal ein wenig mit dem Servo rumgespielt dabei das ganze allerdings einfach so gemacht ohne Timer usw. (wenig erfahrung bisher mit ASM). Jetzt würde ich es gern so weit wie möglich Interrupt gesteuert machen so das der Controller möglichst nicht in irgendwelchen Warteschleifen hängt sondern noch andere Dinge tun kann.
Wieviele Abstufungen sind eigentlich sinnvoll mit so einem Servo, dachte mal daran ihm seriell ein Byte zu senden meinetwegen 0x00 linksanschlag und 0xFF rechts aber warscheinlich kann der eh nicht so feinabstufen oder?
Gottfreak
28.09.2004, 14:44
Jetzt würde ich es gern so weit wie möglich Interrupt gesteuert machen so das der Controller möglichst nicht in irgendwelchen Warteschleifen hängt sondern noch andere Dinge tun kann.
Wenn du nur ein Servo pro Timer nimmst, kannst du den Timer immer so einstellen, dass er überläuft(und den entsprechenden Interrupt triggert), wenn das nächste mal eine Änderung am Servo-Pin nötig ist. Du schaltest also z.B. den Pin auf High und stellst den Prescaler so ein, dass ein Überlauf mindestens 2ms dauert. Dann stellst du über den Wert, den du in den Timer lädst, ein, dass er überläuft, wenn der Port auf low soll(also ja nach Stellung nach 1-2ms). Das machst du beim nächsten Überlauf und stellst dann den Timer so ein, dass er nach 18-19ms überläuft. Wenn du den 16-Bit-Timer dafür über hast, brauchst du nur immer einen anderen Wert in den Timer zu laden und kannst immer den gleichen Prescaler(meist wohl 1) nehmen. Mit 8 bit musst du den Prescaler nach jedem Schalten verändern(wenn 256 Schritte mindestens 20 ms dauern sollen, hast du bei 1 ms nurnoch 12 Stufen.).
Wieviele Abstufungen sind eigentlich sinnvoll mit so einem Servo, dachte mal daran ihm seriell ein Byte zu senden meinetwegen 0x00 linksanschlag und 0xFF rechts aber warscheinlich kann der eh nicht so feinabstufen oder?
Die Fernsteuerungen, mit denen Modellservos normalerweise angesteuert werden, können höchstens 64 Stufen. Also werden die Servos wohl in der Praxis auch nicht viel genauer gehen. Die Servoelektronik an sich ist(zumindest bei dem Servo, das ich auseinandergenommen hab' und den Plänen, die ich kenne) völlig analog(ein Poti misst die Stellung und stellt darüber ein Tastverhältnis ein, das über die Stellung des Servos an das PWM-Signal angeglichen wird.).
Philipp_c
28.09.2004, 14:58
Hmm, ich wollte eigentlich den 8bit Timer nehmen, weil ich den 16Bit Timer für die Drehzahl benötige (mit 8Bit zu ungenau von 800-6500 Um/min). Wie kritisch ist das Timing eiegntlich bei den Servos? Muss ich es so machen das ein Zyklus immer genau 20ms hat? (Also 1,6 Signal und danach dann 18,4 Pause?) oder kann ich die Pause zwischen den Pulsen fest auf 18ms oder so lassen?
Zu dem Prescaler: Ich kann ja einen hohen Teiler einstellen um die ca 20ms zu warten und dann einen kleineren damit ich mit den 8bit die 1ms besser auflösen kann oder? und der Impuls ist ja zwischen 1 und 2ms. dann könnte ich ja zB:
18 ms Pause
1 ms High
und dann zwischen 0 und 1ms genau einstellbar machen oder sehe ich da was falsch?
Gottfreak
28.09.2004, 15:14
Die Pause kannst du fest lassen(aber lieber 19ms als 18, bei zu kurzen Zyklen sollen manche Servos flattern).
Zu dem Prescaler: Ich kann ja einen hohen Teiler einstellen um die ca 20ms zu warten und dann einen kleineren damit ich mit den 8bit die 1ms besser auflösen kann oder?
Genau! Beim 8Bit-Timer kannst du es so machen(oder auch einfach die Überläufe für die langen Pausen zählen. Dann läuft deine ISR aber unnötig oft ab).
18 ms Pause
1 ms High
und dann zwischen 0 und 1ms genau einstellbar machen oder sehe ich da was falsch?
Das geht. Je nachdem, welche Taktfrequenz du verwendest, kannst du die 1-2ms auch direkt erzeugen(ohne die 1ms High einzeln), da es, wenn du nicht extra für den Zweck deinen Quarz aussuchst, den "perfekten" Prescaler wahrscheinlich ohnehin nicht gibt.
die eine ms wollte ich nur extra erzeugen weil die ja eh konstant ist und ich so die vollen 8bit zum variieren der Servo Stellung hätte. Sonst müsste ich ja, wenn 0xff genau 2ms währen die hälfte verschenken, weil der Servo sich erst bei 0x0f anfängt so bewegen.
Gottfreak
28.09.2004, 15:34
Sonst müsste ich ja, wenn 0xff genau 2ms währen die hälfte verschenken
eben
Je nachdem, welche Taktfrequenz du verwendest, kannst du die 1-2ms auch direkt erzeugen(ohne die 1ms High einzeln), da es, wenn du nicht extra für den Zweck deinen Quarz aussuchst, den "perfekten" Prescaler wahrscheinlich ohnehin nicht gibt.
Wahrscheinlich musst du ohnehin etwas verschenken, weil du einen Prescaler von 1ms/(CLK*255) nicht genau einstellen kannst(es sei denn, du orderst dir einen genau passenden Quarz).
Jetzt nochmal ob ich alles richtig verstanden hab. Nehmen wir mal an ich habe ein 4MHz Quarz. Dann dauert ein Clk 0,00025ms. Nun stelle ich den Prescaler auf 1024 dann dauert ein TimerCount 0,256ms. Wenn ich nun möchte das der Timer Interrupt nach 19ms kommt müssen also ca 74 TimerCounts vergehen (18,944ms). Jetzt setze ich den TimerCounter also auf 255-74=181 und starte den Timer. Jetzt sollte der Interrupt nach ziemlich genau 19ms auslösen oder?
Gottfreak
28.09.2004, 16:28
Ja
Philipp_c
28.09.2004, 19:00
erstmal 1000 Dank für die schnelle und gute Hilfe. Eine Frage hab ich jetzt nochmal. Ich habe mir vorgestellt den Timer und Prescaler einmal so zu setzen das es quasi 4 Zustände gibt.
1: Prescale 1024 Count 255-74 (19ms)
2: Prescale 256 Count 255-15 (1ms)
3: Prescale 8 Count 255-x (0-0,5ms)
4: Prescale 8 Count 255-x (0-0,5ms)
Es gibt ja leider kein 16er Prescaler so das ich den 8er benutzen wollte und die Zeit doppelt solange halten wollte.
Bei einer sehr kleinen Servostellung (zB x=1) würde der Interrupt ja extrem oft aufgerufen werden. Kann es da zu irgendwelchen Problemen kommen?
Gottfreak
29.09.2004, 01:45
Das könnte ein Problem sein, wenn der Controller mit der Ausführung nicht mitkommt. Da der Interrupt dann gleich nach Beendigung der ISR wieder ausgelöst wird, läuft das Hauptprogramm nicht weiter.
In deinem Fall ist das aber nicht so schlimm, da nach dem zweiten Ablauf der Prescaler und die Vorbelegung des Timers geändert werden.
Die Mindestdauer für den Teil des Signals, der nach der konstanten ms kommt, ist natürlich die Zeit, die deine ISR braucht um zweimal abzulaufen(und das ist auch die Zeit, die das Hauptprogramm bei sehr kleinen Werten unterbrochen wird.).
Es ist übrigens möglich, ein Servo durch Pulslängen ausserhalb von 1-2ms über die Vollausschläge hinaus zu steuern. Das tut dem Getriebe dann garnicht gut (erkennt man dann an einem knirschenden Geräusch).
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.