PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Libverträgliche Servo-ISR



radbruch
27.02.2009, 21:15
Hallo

Wie der Titel vermuten läßt geht es wieder mal um eine weitere Möglichkeit Servos mit dem RP6 anzusteuern. Da die Library des RP6 alle Timer belegt und dort auch die entsprechenden ISRs liegen kann man interruptgesteuerte Ansteuerungen schwer ohne Änderungen in der Lib einsetzen.

Eine Möglichkeit ist eine andere Betriebsart eines Timers zu verwenden für die die ISR noch nicht von der Lib belegt ist. Hier bietet sich der Overflow-Interrupt des Timers2 an. In der Lib wird der Timer2 im CTC-Mode betrieben und erzeugt die Trägerfrequenz für die IR-Funktionen. Wenn man nun diesen Timer umprogrammiert darf man zwar die ACS- und RC5-Funktionen nicht mehr verwenden, kann aber ohne Änderungen in der Lib eine eigene ISR im Programm anlegen. Mit der klassischen Ansteuerung (wie im RN-Wissen gezeigt) sieht das dann so aus:

//Servos mit Timer2 Overflow-ISR (klassisch) 27.2.2008 mic

#include "RP6RobotBaseLib.h"

volatile uint8_t p=0;
uint8_t pos[3]={125,125,125};

void pause(uint8_t pause_20ms)
{
p=pause_20ms;
while(p);
}
int main(void)
{
initRobotBase();
DDRA |= 16;
PORTA &= ~16;
DDRC |= 3;
PORTC &= ~3;

// Initialize Timer2 - 100kHz für Servos mit Overflow-ISR
TCCR2 = (0 << WGM21) | (0 << COM20) | (1 << CS20); // Normal Mode, no prescaler
TCNT2 = -80; // alle 80 Takte ein Interrupt
TIMSK |= (1 << TOIE2); // Timer2 Overflow-Interrupt erlauben

while(1)
{
pos[0]=65;
pause(50);
pos[0]=185;
pause(50);
}
return(0);
}
ISR (TIMER2_OVF_vect)
{
static uint16_t count=1; // Zykluszähler
TCNT2 = -80; // alle 80 Takte ein Interrupt
if (count>pos[0]) PORTC &= ~1; else PORTC |= 1;
if (count>pos[1]) PORTC &= ~2; else PORTC |= 2;
if (count>pos[2]) PORTA &= ~16; else PORTA |= 16;
if(count<2000) count++; else {count=1; if(p) p--; } // Zyklus fertig?
}

Das ist übersichtlich und funktioniert auch grundsätzlich. Einzig die sleep()/mSleep()-Funktionen hängen sich aus einem noch nicht gefundenen Grund auf, deshalb auch die zusätzliche Pause()-Funktion im Programm oben. Ein weiteres Problem dabei ist aber der gewaltige Overhead beim zyklischen Aufrufen der ISR im 100kHz-Takt!

Viel resourcenschonender ist dieser Ansatz:

//Servos mit Timer2 Overflow-ISR 27.2.2008 mic

#include "RP6RobotBaseLib.h"

uint8_t pos[3]={255,170,85};

int main(void)
{
initRobotBase();
DDRA |= 16;
PORTA &= ~16;
DDRC |= 3;
PORTC &= ~3;

//Timer2 Initialisierung
TCCR2 = (0 << WGM21) | (0 << COM20) | (1 << CS22); // Normal Mode, prescaler /64
TIMSK |= (1 << TOIE2); // Timer2 Overflow-Interrupt erlauben

while(1)
{
pos[0]=pos[1]=pos[2]=85;
mSleep(1000);
pos[0]=pos[1]=pos[2]=255;
mSleep(1000);
}
return(0);
}
ISR (TIMER2_OVF_vect)
{
static uint8_t servo_nr=0;
static uint16_t impulspause;
if(servo_nr)
{
if(servo_nr==1) {TCNT2=-pos[0]; PORTC |= 1; impulspause-=pos[0];}
if(servo_nr==2) {TCNT2=-pos[1]; PORTC &= ~1; PORTC |= 2; impulspause-=pos[1];}
if(servo_nr==3) {TCNT2=-pos[2]; PORTC &= ~2; PORTA |= 16; impulspause-=pos[2];}
if(servo_nr==4) {PORTA &= ~16; servo_nr=0;}
if(servo_nr) servo_nr++;
}
else
{
if(impulspause>256) impulspause-=256;
else {TCNT2=-impulspause; servo_nr++; impulspause=2000-256;}
}
}
Eher zufällig ergeben die 8Mhz des Mega32 mit Vorteiler 64 und einem Timerstartwert (in TCNT2) von -255 eine Impulslänge die meine Servos nahezu an den Anschlag bewegen. Und ein Wert von -85 bewegt sie an den anderen Anschlag, besser kanns ja fast nicht mehr sein :) Die Impulspause wird in 256er-Schritten abgearbeitet, das geschied ohne Eingriff am TCNT2 weil der Timer nach dem Überlauf von selbst bei 0 startet. Wenn ich mich nicht verrechnet habe, wird die ISR während des 20ms-Zyklus der Servosimpulse einmal pro Servo und Restpausezeit/256+1 mal angesprungen. Rein rechnerisch sind so mindestens 8 Servos gleichzeitig ansteuerbar, das sollte für den RP6 eigentlich ausreichen. Ein erneuter Aufruf von initRobotBase() stoppt die Servos und schaltet den Timer2 wieder in den 36kHz-Betrieb. Alternativ kann man natürlich auch die Initialisierung des Timers aus der Lib kopieren und in eine eigene Funktion einbauen. Beim Erweiterungsmodul sollte ein Vorteiler von 128 den höheren 16MHz-Takt ausgleichen.

Gruß

mic