kramann.info
© Guido Kramann

Login: Passwort:










4.8 Lösung zu Laborübung 3: Ansteuern eines Servos

BILDBESCHREIBUNG

Bild 4.8-1: Servo

  • Bei der Laborübung 5 sollte der Timer 0 so konfiguriert werden, dass das entstehende PWM-Signal geeignet dazu ist, einen Servo anzusteuern.
  • Im Anschluß sollten die Taster an PB0 und PB1 so verwendet werden, dass ohne Betätigen der Taster der Servo in Mittelstellung steht, er bei Betätigen von Taster PB0 in Position -450 und bei Betätigen von Taster PB1 in Position +450 geht.

Einstellen der richtigen PWM-Frequenz

  • Das vom Timer erzeugte Phasen- und Frequenz-korrekte PWM-Signal ist eine Rechteckschwingung einer festen Frequenz.
  • Der Servo kann nur PWM-Signale verarbeiten, die eine Periodendauer im Bereich 10 bis 20ms haben.
  • Dies entspricht einer für das PWM-Signal geforderten Frequenz zwischen 50Hz und 100Hz (Kehrwert der Perioden: 10ms = 0,01s 1/0,01 = 100Hz).
  • Im Datenblatt auf Seite 75 ist als Formel zur Bestimmung der PWM-Frequnz die Formel fpwm=fclk/(N*510) angegeben.
  • Bei einer Taktrate von fclk=9216000Hz (Quarztakt) folgt für eine gewünschte PWM-Frequenz von 75Hz: N = 9216000/(510*75) = 240,94
  • Der an diesem N am nächsten liegende realisierbare Wert ist eine Teilung von N=256.
  • Im Datenblatt auf Seite 79/80 ist ablesbar, wie die Vorteilung N über die Bits CS02 CS01 CS00 im Register TCCR0 eingestellt werden kann.
  • Nachfolgende Tabelle gibt diese Tabelle in Teilen wieder und zeigt gleich die resultierende PWM-Frequenz bei dem verwendeten Systemtakt von 9,216MHz:
N     CS02 CS01 CS00   Frequenz bei 9216000Hz Taktfrequenz
1       0    0    1    18070,59Hz
8       0    1    0     2258,82Hz
64      0    1    1      282,35Hz
256     1    0    0       70,59Hz geeignet für Servo-Ansteuerung
1024    1    0    1       17,65Hz
 

Code 4.8-1: Vorteilung N, Konfiguration von N über CS02 CS01 CS00 und die sich ergebende PWM-Frequenz bei einem Systemtakt von 9216000Hz

Realisierung der Winkelstellungen

  • Nun muß noch bestimmt werden, welche Breite des High-Pegels bei dem PWM-Signal eingestellt werden muß, um die geforderten Winkelstellungen zu erreichen.
  • Es ist keineswegs so, dass diese Breite zwischen 0 und 100% der Gesamt-PWM-Periode liegen kann.
  • Vielmehr sagen Herstellerangaben, dass für -45o die Breite des High-Pegels bei 1ms liegen soll, für 0o bei 1,5ms und für +45o bei 2ms.
  • In unserem Programm können wir für zur Einstellung der Breite des High-Pegels einen Wert zwischen 0 und 255 dem Register OCR0 zuweisen.
  • Der Zusammenhang zwischen der Belegung von OCR0 und der Breite des High-Pegels ist linear.
  • OCR0 = 0 heißt: Breite 0%.
  • OCR0 = 0 heißt: Breite 100%, also die ganze Periode einer PWM-Schwingung.
  • Die Periodendauer der PWM-Schwingung für den gewählten Vorteiler N=256 ergibt sich als Kehrwert der PWM-Frequenz:
  • Tpwm = 1/fpwm = (510*256)/9216000 = 0,01416666...s
  • Durch Erhöhen con OCR0 wird der High-Pegel Thigh um 1/255-igstel von Tpwm länger, oder als Formel ausgedrückt:
  • Thigh = OCR1*(2*256)/9216000 s
  • Um für die Mittelstellung Thigh = 1,5 Millisekunden einzustellen, errechnet sich damit für OCR1: OCR1 = 9216000*0,0015/512 = 27.
  • Analog für -45o bzw. Thigh = 1,0 Millisekunden: OCR1 = 9216000*0,001/512 = 18.
  • Und für +45o bzw. Thigh = 2,0 Millisekunden: OCR1 = 9216000*0,002/512 = 36.
BILDBESCHREIBUNG

Bild 4.8-2: PWM-Signal für Servo, hier zu Stellung 0o

Objektorientierte Umsetzung

  • In Kapitel 8.4 wird die Klasse PWMtimer0 entwickelt, die den Timer 0 als PWM-Geber verwendet.
  • Nach Erzeugen eines Objektes von PWMtimer0 und Aufruf der Objektmethode start(), wird ein Frequenz- und Phasenkorrektes PWM-Signal auf Pin 4 (PB3 / OC0) ausgegeben, dessen Breite mit der Objektmethode setzePulsbreitePin4(...) eingestellt werden kann.
  • In der in Kapitel 8.4 gegebenen Variante der Klasse PWMtimer0, wird eine Vorteilung N von 64 verwendet. Dies muß auf N=256 geändert werden. Ansonsten kann die Klasse genau so benutzt werden.
  • Die modifizierte Methode start() lautet dann:
        void start()
        {
            TCCR0 = (0<<FOC0) | (1<<WGM00) | (1<<COM01) | (0<<COM00) | (0<<WGM01) | (1<<CS02) | (0<<CS01) | (0<<CS00);
            DDRB |= (1<<PB3); //auf Ausgang setzen.            
            OCR0 = 0; //PWM-Breite auf Null setzen.
        }
 

Code 4.8-1: Modifizierte Methode start() mit Vorteilung N = 256 (CS02=1 CS01=0 CS00=0)

  • Das Hauptprogramm verwendet diese modifizierte Klasse und fällt dadurch sehr kurz aus. Es implementiert außerdem die Reaktion auf die Taster:
servo.zip - Projekt zur Servoansteuerung mit Hauptprogramm und der modifizierten Klasse PWMtimer0.
#include <avr/io.h>
#include "PWMtimer0.h"

int main(void)
{
    PWMtimer0 pwm0;
    uint8_t akku;

    DDRB = 0b00001000;
    DDRC = 0b11111111;

    pwm0.start();

    while(true)
    {
        akku = PINB;
        if( (akku & 0b00000011) == 1) //Taster PB0 gedrückt
        {
            PORTC = 0;
            pwm0.setzePulsbreitePin4(18); //Servo auf -45Grad
            while( (akku & 0b00000011) == 1)
                akku = PINB;
        }
        else if( (akku & 0b00000011) == 2) //Taster PB1 gedrückt
        {
            PORTC = 2;
            pwm0.setzePulsbreitePin4(36); //Servo auf +45Grad
            while( (akku & 0b00000011) == 2)
                akku = PINB;
        }
        else
        {
            PORTC = 1;
            pwm0.setzePulsbreitePin4(27); //Servo auf +45Grad
        }
    }
    return 0;
}
 

Code 4.8-2: Hauptprogramm für Servo-Ansteuerung

Prozedurale Umsetzung

  • Zur leichteren Orientierung soll im folgenden das gleiche Programm prozedural umgesetzt dargestellt werden:
servo_proz.zip - Prozedurales Projekt zur Servoansteuerung mit Hauptprogramm und der modifizierten Klasse PWMtimer0.
#include <avr/io.h>
int main(void)
{
    uint8_t akku;

    DDRB = 0b00001000;
    DDRC = 0b11111111;

    TCCR0 = (0<<FOC0) | (1<<WGM00) | (1<<COM01) | (0<<COM00) | (0<<WGM01) | (1<<CS02) | (0<<CS01) | (0<<CS00);
    DDRB |= (1<<PB3); //auf Ausgang setzen.            
    OCR0 = 0; //PWM-Breite auf Null setzen.

    while(true)
    {
        akku = PINB;
        if( (akku & 0b00000011) == 1) //Taster PB0 gedrückt
        {
            PORTC = 0;
            OCR0=18; //Servo auf -45Grad
            while( (akku & 0b00000011) == 1)
                akku = PINB;
        }
        else if( (akku & 0b00000011) == 2) //Taster PB1 gedrückt
        {
            PORTC = 2;
            OCR0=36; //Servo auf +45Grad
            while( (akku & 0b00000011) == 2)
                akku = PINB;
        }
        else
        {
            PORTC = 1;
            OCR0=27; //Servo auf +45Grad
        }
    }
    return 0;
}
 

Code 4.8-1: Prozedurale Umsetzung des Servo-Programms

Programmverbesserung: Verfeinerte Winkelauflösung

  • In der vorangestellten Version des Programms darf OCR0 Werte zwischen 18 und 36 annehmen.
  • Dies ergibt nur 19 mögliche Positionen, bzw. 18 Schritte und damit eine Winkelschrittweite in dem Bereich 90o von 5o.
  • Timer 0 und Timer 2 sind 8-Bit-Timer, wogegen Timer 1 ein 16-Bit-Timer ist, mit dem u.a. auch ein 10-Bit Phasenkorrektes PWM-Signal erzeugt werden kann.
  • Dies soll im folgenden geschehen.
Erarbeitung einer 10 Bit PWM Servoansteuerung mit Timer1 aufgrund des Datenblattes.


Phasen- und Frequenz korrektes 10-Bit-PWM:
Bits:    WGM13 WGM12 WGM11 WGM10
Belegung:  0     0     1     1

TOP = 03FF16 = 1023

fpwm = fclk/(2*N*TOP)

=> N = 64

fpwm = 9216000/(2*64*1023) = 70,381Hz

=>

Vorteilung N=64:
Bits:      CS12 CS11 CS10
Belegung:    0    1    1


TCCR1A = (1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (0<<FOC1A) | (0<<FOC1B) | (1<<WGM11) | (1<<WGM10);
TCCR1B = (0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (0<<WGM12) | (0<<CS12) | (1<<CS11) | (1<<CS10);

Timer 1 kann zwei PWM-Signale gleichzeitig mit unterschiedlichen Pulsbreiten aber gleicher
Frequenz erzeugen:

Betroffene Pins: 18 (PD4/OC1B) 19 (PD5/OC1A)

PWM-Breite mit 16-Bit Registern
OCR1A und OCR1B einstellen.

Die Breite kann hier von 0..1023 eingestellt werden.
Zuordnungen:
Periodendauer T = 1/fpwm = 0,014208333 s
1/1023 Periode: 0,0000138889 s
1ms benötigt darum eine Einstellung der Pulsbreite auf 72,
2ms auf 144
1,5ms auf 108

Damit ergibt sich die Möglichkeit 73 Positionen anzufahren, was einer Auflösung von 90/72 = 1,25o entspricht.
 

Code 4.8-3: Einstellungen suchen

Prozedurales Programm zur feineren Winkelauflösung

  • Die 73 Positionen können inkrementell mit den Tastern angefahren werden und werden im Display angezeigt.
servo_proz_fein.zip - Projekt zu Servoansteuerung mit feinerer Auflösung.
#include <avr/io.h>
int main(void)
{
    uint8_t akku;
    uint16_t position = 108;


    DDRB = 0b00000000;
    DDRC = 0b11111111;

    TCCR1A = (1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (0<<FOC1A) | (0<<FOC1B) | (1<<WGM11) | (1<<WGM10);
    TCCR1B = (0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (0<<WGM12) | (0<<CS12) | (1<<CS11) | (1<<CS10);
    DDRD |= (1<<PB5) | (1<<PB4); //auf Ausgang setzen.            
    OCR1A = position; //PWM-Breite setzen (entspricht für Servo auf 0 Grad).
    OCR1B = position; //PWM-Breite setzen (entspricht für Servo auf 0 Grad).
    PORTC = position;

    while(true)
    {
        akku = PINB;
        if( position<144 && (akku & 0b00000011) == 1) //Taster PB0 gedrückt
        {
            position++;
            PORTC = position;
            OCR1A=position; 
            OCR1B=position; 
            while( (akku & 0b00000011) == 1)
                akku = PINB;
        }
        else if( position>72 && (akku & 0b00000011) == 2) //Taster PB1 gedrückt
        {
            position--;
            PORTC = position;
            OCR1A=position; 
            OCR1B=position; 
            while( (akku & 0b00000011) == 2)
                akku = PINB;
        }
    }
    return 0;
}
 

Code 4.8-4: Prozedurales Servo-Ansteuerungsprogramm mit einer Auflösung von 1,25o