1

Donnerstag, 30. Oktober 2008, 13:48

Projekt: Servo-Reverser

Servus,

als kleines Anwendungsbeispiel aus der RC-Praxis soll hier eine einfache "Plug-In" Lösung zur Drehrichtungs-Umkehr eines Servos behandelt werden. Hier steht allerdings nicht so sehr die Präsentation einer fix- und fertigen Lösung im Vordergrund, sondern der Entwicklungsprozess von der Analyse des Problems bis zur fertigen Lösung.
(ich habe selbst derzeit zwar eine ungefähre Vorstellung, wie es funktionieren soll, aber noch keine fertige Lösung...)

Die Aufgabe:
Ziel ist ein kleiner Baustein, der zwischen Empfänger und Servo gesteckt wird, und dadurch die Drehrichtung des Servos umkehren soll. Die Neutralstellung des Servos soll über zwei Taster o.ä. justierbar sein.

Analyse:
Die Servostellung wird durch die Impulslänge vom Empfänger vorgegeben. Dabei entspricht in der Regel eine Impulslänge von 1,5 ms der Servo-Mittelstellung, und ca. 1ms bzw. 2 ms der einen oder anderen Endstellung. Also muß unsere Schaltung die Impulse nach folgendem Muster verarbeiten:
Eingangs-/Ausgangsimpuls:
1,5ms / 1,5ms
1 ms / 2ms
2 ms / 1 ms
1,75 ms / 1,25 ms
Oder, als Rechenregel ausgedrückt: Wenn der Eingangsimpuls 1,5 ms+X ist, dann ist der Ausgangsimpuls 1,5ms-X.

Lösungsansatz finden:
Auf den ersten Blick ergibt sich daraus schon ein möglicher Lösungsansatz:
Unser PIC-Programm muß den Eingangsimpuls messen und von dem erhaltenen Wert 1,5ms abziehen, dadurch ergibt sich die Differenz (X), wobei X positiv, negativ oder 0 sein kann. Nun muß die neue Impulslänge als 1,5ms - X berechnet werden und mit dem Ergebnis ein Ausgangsimpuls erzeugt werden.

Wenn man mal im Internet nach Schaltungen für Servo-Reverser sucht, stößt man auch auf Schaltpläne für Hardware-Lösungen. Nach oben beschriebenen Algorithmus wäre eine Hardware-Lösung ziemlich kompliziert (Impuls messen und eine "Rechengröße", z.B. Spannung erzeugen, mit dieser dann einen Impulsgenerator steuern...), aber die Hardware-Servo-Reverser sind eigentlich relativ einfach - vielleicht können wir durch Betrachtung von deren Funktionsweise auch unser Servo-Reverser Programm vereinfachen?
Die typische Hardware-Lösung funktioniert so: kommt der Impuls vom Empfänger, wird ein sog. "Monoflop" getriggert, das einen Referenz-Impuls mit einer festen Länge von 3 ms (= 2 x 1,5ms) erzeugt. Dieser wird mit dem Eingangs-Impuls verknüpft, so daß der Ausgang solange auf "0" bleibt, bis der Eingangsimpuls zu Ende ist. Dann geht der Ausgang auf "1", bis auch der 3ms Referenz-Impuls zu Ende ist.
Also: bei einem Eingangsimpuls von 1,5ms sind am Ende davon noch 1,5ms vom 3ms Referenz-Impuls "übrig", bei kürzeren Eingangsimpulsen ist entsprechend mehr, bei längeren Eingangsimpulsen weniger davon "übrig" - die Impulslänge am Ausgang verhält sich also genau wie gewünscht für die Servo-Reverse-Funktion.
Wenn wir das auf unser PIC-Programm übertragen, wird es gleich viel einfacher:
- statt den Eingangs-Impuls zu messen, starten wir an dessen Anfang nur einen internen "Referenz-Impuls" (= Timer) mit immer gleicher Länge (3 ms).
- die Berechnung einer Ausgangs-Impulslänge fällt ganz weg.
- am Ende des Eingangs-Impulses setzen wir den Ausgangs-Pin auf 1 (Start des Ausgangs-Impulses)
- am Ende der Referenz-Zeit (d.h. wenn der Timer abgelaufen ist) wird auch der Ausgang wieder auf 0 gesetzt - Ende des Ausgangs-Impulses.

Fehlt noch die Justierung/Trimmung der Mittelstellung: das kann einfach durch Veränderung der Referenz-Zeit erreicht werden, z.B. im Bereich 2,5 bis 3,5 ms.

Fazit:
Es lohnt sich, nicht gleich mit der erst-besten Idee das Schreiben des Programm-Codes zu beginnen, sondern etwas Überlegung und Recherche in alternative Lösungsmöglichkeiten zu investieren. Wenn man dadurch das Programm vereinfachen kann, kann die Entwicklungszeit manchmal wesentlich verkürzt werden!
Einfachere Programme mit weniger Code sind schneller zu schreiben und erst recht schneller zu debuggen, da in weniger Code rein statistisch auch schon weniger Fehler stecken...

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Ottili« (30. Oktober 2008, 13:53)


2

Freitag, 31. Oktober 2008, 23:20

Servo Reverser: die Hardware

Hier ist das Schaltbild zum Servo-Reverser.

Für eine stabile Taktfrequenz wird ein Quarz verwendet. Die Schaltung funktioniert zwar auch mit dem internen RC-Oszillator, jedoch kann sich dann das Servo bei extremen Temperaturänderungen (z.B. beim Fliegen im Winter) vertrimmen, weil mit einem "Weglaufen" der Taktfrequenz in der Größenordnung von 0,5 bis 1% gerechnet werden muß. Bei einer Abweichung von 1% ändert sich die 3ms Referenzzeit und damit auch die Impulslänge für das Servo um 30 Microsekunden. Das entpricht immerhin schon einer "Vertrimmung" um 3% vom vollen Servo-Weg (1ms Differenz der Impulslänge zw. Vollausschlägen).
Wer mit evtl. Nachtrimm-Bedarf bei extremen Temperaturschwankungen leben kann, kann natürlich im Interesse einer möglichst kompakten Schaltung trotzdem auf den Quarz verzichten (__CONFIG anpassen!)
»Ottili« hat folgendes Bild angehängt:
  • sch.gif

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Ottili« (31. Oktober 2008, 23:21)


3

Samstag, 1. November 2008, 13:36

Servo Reverser: das Programm

So, als erstes habe ich mal das "Herzstück" der Firmware erstellt und (im Sim) getestet:
die Umkehrung der Impulslänge.
Zunächst die üblichen Konfigurationsdaten und ein paar Konstanten definiert:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
;**************************************************************
;*** Servo Reverse als Plug-In zwischen Empfänger und Servo ***
;**************************************************************

#include <P12F629.INC>
 __config _BODEN_ON & _PWRTE_ON & _MCLRE_OFF & _WDT_OFF & _XT_OSC
; __config _BODEN_ON & _PWRTE_ON & _MCLRE_OFF & _WDT_OFF & _INTRC_OSC_NOCLKOUT
 radix dec			;Zahlen ohne Angabe zur Basis = Dezimalzahlen

;============= Konstanten:
;I/O Ports:
PULS_IN		equ	3		;GP3 = Eingang: Servo-Impuls vom Empfänger
PULS_OUT 	equ 2		;GP2 = Impuls-Ausgang zum Servo
S_INC		equ 0		;GP0 = Taster, verlängert den Impuls
S_DEC		equ 1		;GP1 = Taster, verkürzt den Impuls


Wenn statt Quarz oder Keramik-Resonator der interne RC-Oszillator verwendet werden soll, muß nur das Semikolon vor die erste Zeile "__config.... " gesetzt und bei der zweiten Zeile gelöscht werden. Durch das vorangestellte Semikolon wird die Zeile zum Kommentar und somit nicht mehr vom Assembler verarbeitet, dadurch kann man leicht Code-Teile "deaktivieren", ohne sie aus dem Qelltext löschen zu müssen.

Dann kommt das Programm, zunächst Initialisierungen:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
;============= Programm:
 org 0
;Initialisierungen:
    movlw 0x07		
    movwf CMCON      ;Comparator aus, Ports = digital I/O
    clrf GPIO
    bsf STATUS,RP0      ;Registerbank 1 anwählen
    call 0x3ff	       ;falls INTRC_OSC:
    movwf OSCCAL       ;internen Oszillator kalibrieren
    movlw B'111011'
    movwf TRISIO       ;GP2 = Ausgang
    movlw B'000011'
    movwf WPU       ;Pull-Up Widerstände für GP0,GP1
    bcf OPTION_REG,7        ;GPPU-Bit auf 0 setzen
    bcf STATUS,RP0        ;zurück zu Registerbank 0
    clrf T1CON        ;TMR1 zählt Microsekunden, zunächst off

Dann das eigentliche Programm, das in einer Endlos-Schleife abläuft:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
mainloop
    movlw high -3000	        ;(vorläufig z.Test! Später Wert aus EEPROM!)
    movwf TMR1H        ;Timer mit Referenz-Impulslänge laden
    movlw low -3000
    movwf TMR1L
    bcf PIR1,TMR1IF    ;time-out Flag löschen

waitpulse
    btfss GPIO,PULS_IN        ;warten auf Impuls-Anfang
    goto waitpulse

     bsf T1CON,TMR1ON         ;Timer starten
	
waitpulsend
    btfsc GPIO,PULS_IN        ;warten auf Impuls-Ende
    goto waitpulsend

    bsf GPIO,PULS_OUT        ;start des Ausgabe-Impulses

waittimeout
    btfss PIR1,TMR1IF        ;warten auf Ende d. Ref.-Impulses
    goto waittimeout
	
    bcf GPIO,PULS_OUT        ;Ende des Ausgabe-Impulses
    bcf T1CON,TMR1ON        ;Timer stoppen

    goto mainloop        ;das Ganze von vorn...

 END

Zuerst wird TMR1 auf 3000 Zyklen geladen. Der Timer wird mit dem negativen Zahlenwert geladen, weil er vorwärts zählt und beim Erreichen von 0 das Flag (TMR1IF) im Register (PIR1) auf 1 setzt. Das Laden des Timers mit dem Konstanten Wert ist nur vorläufig, da dieser Wert später variabel sein soll, um die Servo-Mittelstellung justieren zu können.
Der weitere Programmablauf erklärt sich mehr oder weniger von selbst (falls was unklar ist: bitte fragen!).
Die Funktion wurde im Simulator mit mit unterschiedlichen Eingangsimpulslängen untersucht (1,5ms, 1,1ms, 1,9ms) und lieferte nach Beseitigung eines Fehlers (Timer wurde nach Ausgangsimpuls nicht angehalten) das erwartete Ergebnis:
»Ottili« hat folgendes Bild angehängt:
  • signals.gif

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »Ottili« (1. November 2008, 13:41)


4

Sonntag, 2. November 2008, 00:18

Servo Reverser: das Programm

Hier ein paar Vorüberlegungen zum Einbau der "Trimm"-Möglichkeit:
Servo-Mittelstellung über Taster verstellen
Die Taster sollen beim Drücken den Referenz-Impuls verlängern (increment) oder verkürzen (decrement) und dadurch die Servo-Mittelstellung anpassen.
Wenn also einer der Taster gedrückt wird, soll das Servo langsam in die eine oder andere Richtung wandern, bis der Taster wieder losgelassen wird. Zur Vorgabe der Verstell-Geschwindigkeit muß dieser Vorgang mit einer bestimmten Anzahl Schritte pro Sekunde ablaufen, wobei bei jedem Schritt der Referenz-Impuls um einen bestimmten Zeitbetrag verlängert bzw. verkürzt wird. Eine Impulsrate ist ja bereits durch die Servo-Impulse vom Empfänger gegeben, die bekanntlich etwa 50/s beträgt. Somit bietet sich an, einfach nur nach jedem Servo-Impuls die Taster abzufragen und die Referenz-Zeit ggf. um einen kleinen Betrag zu ändern. In 10s sind es dann ca. 500 Schritte, und wenn pro Schritt die Zeit um eine Mikrosekunde geändert wird, sind das 0,5 Millisekunden, was dem vollen Servoausschlag von Mitte in eine Richtung entspricht - also eine perfekte Stellgeschwindigkeit!
Um völlig "verbotene" Impulslängen zu vermeiden wird der Trimmbereich auf +/- 0,5ms oder so begrenzt.

Trimm-Wert speichern
Der Wert muß dann natürlich im EEPROM-Speicher abgelegt werden, damit die Servo-Trimmung auch nach dem Ausschalten erhalten bleibt. Nach einem Reset kopiert das Programm die Impulsdauer aus dem EEPROM in den Arbeits-Speicher (RAM).
Der EEPROM-Speicher kann, im Gegensatz zum RAM, nicht unendlich oft mit neuen Werten überschrieben werden ("nur" min. 1Million, typ. 10Millionen mal). Zudem kann das Überschreiben einer EEPROM-Speicherzelle bis zu 10 ms dauern, was bei zwei Bytes, die für den Timerwert gespeichert werden, schon 20ms sind und damit mehr Zeit beansprucht, als in einem Schleifendurchlauf unseres Programms zur Verfügung steht. Daher ist es sicher unangebracht, die neuen Werte jedesmal sofort in das EEPROM zu schreiben, sondern erst, wenn die Taster schon eine Weile losgelassen sind und der neue Wert stabil ist. Das kann leicht über einen Zähler (8 Bit) gesteuert werden, der bei jeder Wertänderung zurückgesetzt wird und ab da ca. 250 Servo-Impulse zählt. Ist der Zähler abgelaufen (= 5s ohne eine Wert-Änderung), wird der neue Wert ins EEPROM geschrieben. Weil das vielleicht 20 ms dauert, führt das dann evtl. zu einem einzelnen, kaputten Servoimpuls - dieser kleine "Zucker" stört aber nicht wirklich.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Ottili« (2. November 2008, 00:22)


5

Sonntag, 2. November 2008, 13:01

Servo Reverser: Unterprogramme für EEPROM-Zugriff

Da das Lesen und, vor allem, das Schreiben ins EEPROM deutlich komplizierter ist, als das Lesen/Schreiben von normalen Registern bzw. RAM-Speicher, erledigt man das am Besten in Unterprogrammen. Diese können ggf. auch so noch in anderen Projekten verwendet werden, daher ist die notwendige Sperrung evtl. Interrupts schon mit eingebaut.

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
;----------------- Unterprogramm:
;liest Daten aus EEPROM 
;Eingabe:
;W = EEPROM-Adresse, von der gelesen werden soll
;Ausgabe:
;W = Daten aus EEPROM

ee_read
    bsf STATUS,RP0      ;select Register-bank 1
 errorlevel -302         ;(Warnmeldungen vermeiden)
    movwf EEADR
    bsf EECON1,RD        ;Daten auslesen -> EEDATA
    movf EEDATA,W        ;Daten -> W
    bcf STATUS,RP0       ;zurück -> Bank 0
 errorlevel +302
    return

;----------------- Unterprogramm:
;schreibt Daten ins EEPROM (wenn nötig)
;Eingabe:
;EEADR = Adresse (kann über "call ee_read" gesetzt werden)
;W = zu schreibende Daten

ee_write
    bsf STATUS,RP0        ;select Register-bank 1
 errorlevel -302        ;(Warnmeldungen vermeiden)
    bsf EECON1,RD        ;alte Daten auslesen
    xorwf EEDATA,F        ;sind neue Daten identisch?
    btfsc STATUS,Z
    goto ee_skipwrite        ;Daten sind gleich, Schreibvorgang überspringen 
    movwf EEDATA        ;= neue Daten

    bsf EECON1,WREN        ;Schreibschutz lösen
    movlw 0x55
    bcf INTCON,GIE       ;55/AA/WR nicht durch Interrupt unterbrechen!
    movwf EECON2        ;Schreibvorgang auslösen...
    movlw 0xAA
    movwf EECON2
    bsf EECON1,WR
    bsf INTCON,GIE       ;Interrupt wieder erlauben
    bcf EECON1,WREN        ;Schreibschutz wieder aktivieren
ee_wait4wr
    btfsc EECON1,WR
    goto ee_wait4wr        ;warten, bis Schreibvorgang fertig

ee_skipwrite
    bcf STATUS,RP0        ;zurück -> Bank 0
 errorlevel +302
    return


Zum Lesen von Daten aus dem EEPROM muß im Hauptprogramm nur die EEPROM-Adresse, die ausgelesen werden soll, ins W-Register geladen werden und das Unterprogramm aufgerufen werden. Im W-Register befinden sich dann die Daten.
Beispiel:

Quellcode

1
2
    movlw eeprom_adresse
    call ee_read


Sollen Daten im EEPROM abgelegt werden, muß die Adresse zunächst in das Register EEADR geladen werden und die zu schreibenden Daten ins W-Register.
Dann wird das Unterprogramm "ee_write" aufgerufen. Zum Schreiben der Daten ins Register EEADR (liegt in Bank 1!) kann auch einfach das Unterprogramm "ee_read" aufgerufen werden:

Quellcode

1
2
3
4
    movlw eeprom_adresse
    call ee_read        ;(nur zum Einstellen der Adresse)
    movf data,W
    call ee_write

Das Unterprogramm führt die Schreib-Operation nur aus, wenn sich die zu schreibenden Daten tatsächlich von den bereits gespeicherten unterscheiden. Der wiederholte Aufruf von ee_write mit den gleichen Daten führt also nicht zum "Verbrauch" der maximalen Anzahl von EEPROM-Schreibzyklen.

6

Sonntag, 2. November 2008, 18:54

Servo Reverser: der komplette Code

So, es ist vollbracht!
Nachdem der Code erst im Simulator getestet und dann auch ein Servo-Reverser in Hardware aufgebaut und getestet wurde, kann das Programm als komplett und funktionstauglich veröffentlicht werden. Der aktuelle Sourcecode ist angehängt. Viel Spass beim Nachbau!

Grüße,

Thomas
»Ottili« hat folgende Datei angehängt:
  • Reverse.zip (2,4 kB - 45 mal heruntergeladen - zuletzt: 2. November 2016, 19:58)

7

Sonntag, 25. Oktober 2009, 19:18

RE: Servo Reverser: der komplette Code

Hallo Thomas,

erstmal vielen Dank für das Bereitstellen der Software :)

ich wollte Deinen Servo Reverser mal nach bauen. Aber irgenwie habe ich da wohl einen Fehler drin. Ich wollte das gerne ohne Quarz machen. Die Config ist angepaßt. Es ist bei mir nur für ein Funktionsmodell. Daher ist die kleine Differenz nicht so tragisch.

Mein Problem ist, dass sich die Drehrichtung nicht ändert. Betätige ich S1 oder S2, so läuft das Servo in die eine bzw. andere Richtung. Während ein Taster betätigt ist, dreht sich das Servo auch anders rum. Wie es eigentlich sein sollte. Sobald ich den Trimmer los lasse, arbeitet das Servo wieder "normal". Also parallel zum Empfängerausgang.

Woran könnte das liegen ?

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Leo-P-Art« (25. Oktober 2009, 19:18)


8

Montag, 26. Oktober 2009, 00:21

RE: Servo Reverser: der komplette Code

Hallo Thomas,

ich ziehe alles zurück. Es war ein "stupid user error" .... Der Servo Reverser funktioniert einwandfrei ! :ok:

9

Montag, 26. Oktober 2009, 11:48

RE: Servo Reverser: der komplette Code

Hi Leo,

alles klar - freut mich, daß Du es hinbekommen hast! Das o.g. Verhalten (Reverse nur während der Taster gedrückt ist) hätte ich auch nicht erklären können.

Grüße,

Thomas

robernd

RCLine Neu User

Wohnort: Flaches Oberbayern

  • Nachricht senden

10

Samstag, 28. August 2010, 19:02

Hallo,
ich bin erst jetzt auf dieses Forum gestoßen. Voller Freude habe ich die Ecke über PIC-Programmierung gefunden. Schade, dass sich hier so lange nichts gerührt hat.

Ich habe mit PIC-Controllern schon viel gemacht und auch eine ganze Menge Erfahrung damit. Auch ich hatte schon darüber nachgedacht, einen Baustein für die Servo-Umkehr zu basteln. Wie so oft fehlte mir die Zeit dafür.

Einerseits schön, dass Du das Problem elegant gelöst hast. Richtig, an der verdammt ungenauen Mittelstellung kann man sich die Zähne ausbeißen.
Andererseits gibt es das auch fertig, auch mit PIC (wie lange schon?). Die Lösung ist zwar teurer aber kostet keine Zeit. Verzoegerungsbausten mit Schaltbild
Bei dem Ding lassen sich die Zeitverzögerung auf Null stellen und die Drehrichtung umkehren. Wie gut die Nullpunkteinstellung gelöst ist, weiß ich nicht.

Nein, ich will dich nicht demotivieren (hoffentlich ist das nicht schon geschehen). Dein Programm ist bestens als Grundlage geeignet, die Servoimpulse auch auf andere Weise zu manipulieren. Häufiger wiederholen oder exponentielle Ausgabe.
Gruß RoBernd

11

Sonntag, 29. August 2010, 14:11

Hallo Bernd,

erstmal herzlich willkommen hier im Forum!

Wie ich schon im Eingangsposting schrieb, geht es ja hier nicht primär um die Präsentation einer Nachbaulösung, sondern den Versuch, anhand einer relativ einfachen, überschaubaren Aufgabe den Entwicklungsweg zu zeigen.

Abgesehen davon, wäre der Conrad-Baustein als simpler Servo-Reverser für kleine Modelle ganz schon groß und schwer. Als Minimallösung hätte hier ein fliegend verdrahteter 8-Beiner, direkt am Kabel eingeschrumpft, sicher seine Vorteile...

Grüße,

Thomas

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Ottili« (29. August 2010, 14:17)