Ein einfacher Selbstbau Servotester


Wie funktioniert eigentlich ein Modellbau-Servo? Dies ist die erste Frage die man sich stellen muß, wenn man mit Servos arbeiten möchte.

Das System hinter einem Servo erkennt man erst mit einem Oszilloskop gut. Prinzipiell wird ein Servo mit 3 Leitungen angeschlossen, Masse, Plus und Signal. Die aktuelle Servoposition wird durch die Pulsbreite eines wiederkehrenden Pulses festgelegt.

Wie das Servosignal aussieht, kann man auf dem folgenden Oszillogram sehen:
servosignal.jpg

Die Pulsbreite ist auf 1ms bis 2ms definiert. Die Pulsbreite entspricht also proportional dem Drehwinkel des Servos. Dieser variiert von Hersteller zu Hersteller.

Wie funktioniert nun ein Servotester?

Eine einfache Schaltung generiert genau dieses Signal für die Ansteuerung des Servos. Ich bin auch hier wieder den Weg über einem Atmel AVR Mega 8 gegangen. Die Microcontroller haben für mich den großen Vorteil gegenüber einer diskreten Schaltung dass man jederzeit eine neue Software einspielen kann und so das Verhalten der Schaltung jederzeit an die eigenen Bedürftnisse anpassen kann.

servotester.jpg

Die Theorie und das Steckbrett... Chaos ;-)

steckbrett.jpg

Die Schaltung ist extrem einfach. R1, C1 bilden das notwendige Resetsignal, damit der Prozessor sauber startet.

Der Poti R4 gibt den am Servo einzustellenden Wert vor, R2 R3 teilt die Spannung soweit herunter, dass es den ADC komplett ausnützt - 0-2,56V interne Referenzspannung.

Das Programm habe ich diesmal mit Winavr C geschrieben. Dabei gab es 2 Fragen:

Wie programmiert man den ADC des Mega 8 mit Winavr C?

Wie programmiert man den Timer1 des AT Mega 8 mit Winavr C?

Beides hat sich als denkbar einfach herausgestellt, im prinzip bleibt alles gleich wie beim Assembler - die Bits der zuständigen Register werden laut Datenblatt des AVRs gesetzt - und die dazu passenden Interruptroutinen mit Signal definiert.

Es ist immer wieder toll zu sehen, wie wenig Programmcode für so ein Miniprojekt notwendig ist.

 

#define F_CPU 8000000

#include <avr/io.h>
#include <avr/interrupt.h>


void initadc(void);
void inittimer1(void);

unsigned char volatile liADC = 0;
unsigned char volatile liPause = 0;
unsigned int volatile liPreload = 0;

int main()
{

    initadc();
    inittimer1();
    sei();
    DDRB = 255;
    

    while (1==1)
    {
        // Timer Preload berechnen, bei 8 Mhz entspricht 8000 ca. 1ms, +max 1ms des ADC - Da Servosignal zw. 1 und 2ms spezifiziert ist
        liPreload = 65536 - 8000 - ((8000 / 256) * liADC); // Entspricht 1 ms
        
    }

}

SIGNAL (SIG_OVERFLOW1)
{
    // 1 Volldurchlauf dauert ca. 8ms, 2 x 8 = 16 ms Pause, dann port einschalten, dauer einstellen, dann port abschalten
    liPause++;
    if (liPause==2)
    {
        PORTB=255;
        TCNT1=liPreload;
    }
    if (liPause==3)
    {
        PORTB=0;
        liPause=0;
    }
    
}


SIGNAL (SIG_ADC)
{
    liADC = ADCH;
}

void initadc(void)
{

    //interne Referenzspannung und AD-Wandlerkanal 0, ADLAR setzen, 8 Bit des High Registers reichen
    ADMUX = (1<<REFS1) | (1<<REFS0) | (1<<ADLAR);



    // AD Wandler einschalten, Freerunning einschalten, Conversion starten, Interrupt einschalten, Prescaler auf 64
    ADCSRA = (1<<ADEN)|(1<<ADFR)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1);

}

void inittimer1(void)
{
    TCCR1B = (1<<CS10); // Direkter Takt, kein Prescaling
    TIMSK |= (1<<TOIE1); // Overflowinterrupt aktivieren

}


(c) Schoeppl.info - Last modified: 10.07.2006