PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Timer0 beim Atmega32 will nicht



WarChild
22.03.2009, 21:50
Hallo zusammen,

Ich habe ein erstes Testprogramm für meinen Hexapod geschrieben und das funktioniert wunderbar.
SexCrora (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=47127&highlight=)


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

int main (void)
{
DDRA = 0b00000001; //PA0 Servo 12
DDRB = 0b00011111; //PB0-4 Servo 11/10/7/8/9
DDRC = 0b11111100; //PC2-7 Servo 4/5/6/3/2/1
DDRD = 0b11111100; //PD2-7 Servo 15/14/13/16/17/18

while (1)
{
PORTA |= (1 << PA0);
PORTB |= (1 << PB0);
PORTB |= (1 << PB1);
PORTB |= (1 << PB2);
PORTB |= (1 << PB3);
PORTB |= (1 << PB4);
PORTC |= (1 << PC2);
PORTC |= (1 << PC3);
PORTC |= (1 << PC4);
PORTC |= (1 << PC5);
PORTC |= (1 << PC6);
PORTC |= (1 << PC7);
PORTD |= (1 << PD2);
PORTD |= (1 << PD3);
PORTD |= (1 << PD4);
PORTD |= (1 << PD5);
PORTD |= (1 << PD6);
PORTD |= (1 << PD7);
_delay_us(1500); //480=0°/1250=90°/2160=180°
PORTA &= ~(1 << PA0);
PORTB &= ~(1 << PB0);
PORTB &= ~(1 << PB1);
PORTB &= ~(1 << PB2);
PORTB &= ~(1 << PB3);
PORTB &= ~(1 << PB4);
PORTC &= ~(1 << PC2);
PORTC &= ~(1 << PC3);
PORTC &= ~(1 << PC4);
PORTC &= ~(1 << PC5);
PORTC &= ~(1 << PC6);
PORTC &= ~(1 << PC7);
PORTD &= ~(1 << PD2);
PORTD &= ~(1 << PD3);
PORTD &= ~(1 << PD4);
PORTD &= ~(1 << PD5);
PORTD &= ~(1 << PD6);
PORTD &= ~(1 << PD7);
_delay_ms(18);
}
return 1;
}

Nun wollte ich das aber nicht unbedingt mit der delay funktion sondern mit einem Timer realisieren.
Timer0 ist synchron zum 16MHz takt geschaltet. und wird auf 256-160=96 vorgealden. Das ergibt einen Überlauf alle 10µs.
Bei jedem überlauf soll mein timer1 erhöht werden. wenn dieser 250 erreicht hat wird er zurückgesetzt und timer2 (auf 8 vorgeladen) wird um eins vermindert.
timer1 ist ein timer der 0-2,5ms läuft und timer2 ist alle 20ms gleich null.


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

unsigned char timer1=0;
unsigned char timer2=8;
unsigned char pulse=0;

SIGNAL (SIG_OVERFLOW0)
{
timer1++;
if(timer1==250)
{
timer1=0;
timer2--;
}
TCNT0 = 256 - 160; //Timer0 neu vorladen
}

int main (void)
{
DDRA = 0b00000001; //PA0 Servo 12
DDRB = 0b00011111; //PB0-4 Servo 11/10/7/8/9
DDRC = 0b11111100; //PC2-7 Servo 4/5/6/3/2/1
DDRD = 0b11111100; //PD2-7 Servo 15/14/13/16/17/18
TCNT0 = 256 - 160; //Timer0 vorladen (10us)
TCCR0 |= (1 << CS00); //CS02/01/00 mit 001 belegen: Teiler 1
TIMSK |= (1 << TOIE0); //Timer0 Interrupt freigegeben
sei(); //Interrupts freigegeben

while(1)
{
if(timer2 == 0)
{
timer2 = 8;
pulse = 1;
PORTA |= (1 << PA0);
PORTB |= (1 << PB0);
PORTB |= (1 << PB1);
PORTB |= (1 << PB2);
PORTB |= (1 << PB3);
PORTB |= (1 << PB4);
PORTC |= (1 << PC2);
PORTC |= (1 << PC3);
PORTC |= (1 << PC4);
PORTC |= (1 << PC5);
PORTC |= (1 << PC6);
PORTC |= (1 << PC7);
PORTD |= (1 << PD2);
PORTD |= (1 << PD3);
PORTD |= (1 << PD4);
PORTD |= (1 << PD5);
PORTD |= (1 << PD6);
PORTD |= (1 << PD7);
}
if(pulse == 1 && timer1 >= 150)
{
pulse = 0;
PORTA &= ~(1 << PA0);
PORTB &= ~(1 << PB0);
PORTB &= ~(1 << PB1);
PORTB &= ~(1 << PB2);
PORTB &= ~(1 << PB3);
PORTB &= ~(1 << PB4);
PORTC &= ~(1 << PC2);
PORTC &= ~(1 << PC3);
PORTC &= ~(1 << PC4);
PORTC &= ~(1 << PC5);
PORTC &= ~(1 << PC6);
PORTC &= ~(1 << PC7);
PORTD &= ~(1 << PD2);
PORTD &= ~(1 << PD3);
PORTD &= ~(1 << PD4);
PORTD &= ~(1 << PD5);
PORTD &= ~(1 << PD6);
PORTD &= ~(1 << PD7);
}
}
return 1;
}

Vielleicht sieht ja jemand einen fehler bei den Steuerbits, eine einfachen Tippfehler oder einen grundsätzlichen Fehler in der logik.
Vielleicht habe ich mich auch bei der Berechnung der Steuergrößen vertan.
Es ist mein erster µC den ich komplett selbst programmiere. ( habe nur RP6 Erfahrung)

vielen Dank für eure Hilfe

mfg WarChild

radbruch
22.03.2009, 22:35
Hallo

Es wäre hilfreich wenn du näher beschreiben würdest was nicht richtig funktioniert. Nicht jeder hat gleich einen passenden Testaufbau bereit stehen.

Auf den ersten Blick:

volatile unsigned char timer1=0;
volatile unsigned char timer2=8;

volatile immer wenn eine Variable in einer ISR geändert wird. Ich glaube, das hat irgendwas mit der Art der Speicherung der Variablen zu tun (Register/Ram).

Genauer betrachtet könnte die Ausführungszeit der ISR zu lange sein (muss ich erst nachrechen) was zu einem Überlauf mit Abstutz führen würde. Erste Massnahme wäre den 10µs-Interrupt zu verlängern. Dann würde sich zwar die Auflöung deiner Servopositionen verkleinern, aber bei 4% Grundfehler dürfte das auch noch zu verschmerzen sein. Gewinnen würdest du dadurch aber generell mehr Leerlaufzeit weil die Kontroller nicht mehr so häufig mit der ISR beschäftigt wäre.

Die -160 beim Timersetup bei 16MHz ohne Vorteilung sind richtig weil der Timer ja nach oben zählt bis er wieder 0/256 ist, vorsichthalber würde ich aber die übrigen Bits im TCCR0-Register nicht einfach so übernehmen. Da du den normalen Mode verwendest kannst du die restlichen Bits mit TCCR0 = (1 << CS00); einfach mitlöschen.

btw: Ein nettes Krabbeltierchen hast du dir da zusammengezaubert. Gefällt mir gut :)

Gruß

mic

WarChild
22.03.2009, 23:19
nun ja keiner der servos reagiert

das is meine einzige Kontrolle des timers, ob an den ausgängen ein PWM signal entsteht

danke für das Lob und den ersten Tipp!

Ich bin am überlegen das grundprinzip zu verändern.
wie du schon sagtest, frisst der ISR grob geschätzt 8% meiner Rechenleistung (alle 100 Takte ein interrupt mit ca 8 Assembler befehlen) uns zusätzlich die permanente Überprüfung der Timer mit den Vergleichswerten.
Daher wäre eine andere Lösung um 18 PWMs zu erzeugen ratsam.
Ich überlege meinen 16MHz quartz durch ein 6,4MHz Quartz zu tauschen.
Dadurch bin ich in der Lage die Timer 0 und 2 bei einem Prescaler von 64 zu 10µs timern zu nutzen (alternativ ginge auch 25,6MHz bei einem Prescaler von 256, aber das unterstützt der µC nicht :( ). Ein Pin wird auf High gesetzt, die Pulsdauer wird einfach vorgeladen und wenn der Überlauf erreicht wird, wird der entsprechende Pin zurück gesetzt. dann folgt der nächste kanal. da es 18 servos sind und maximal 18*2,5ms=45ms vestreichen können müssen zwei Timer gleichzeitig impulse erzeugen, dann kann es höchstens 22,5ms dauern alle Signale zu erzeugen und dann können sie auch gleich schon wieder mit den nächsten Signalen loslegen. Im Falle, dass die Impulssumme kleiner als 20ms war muss entsprechend gewartet werden. Hier besteht die auslastung des µC nur darin in alle 20ms 18mal in den ISR zu springen, die Ports zu schalten und die timer neu vorzuladen. Also kaum arbeit.
Das einzige was mich an dieser Idee stört ist, die Tatsache dass ich 60% meiner Geschwindigkeit verliere, weil ich nurnoch mit 6,4MHz arbeite. Aber ansonsten bekommt man mit den prescalern und den 8 Bit-Timern nicht auf einen Timer der 0-2,55ms abdeckt.
Diese Signale hätten jedoch gegenüber der ersten Lösung den vorteil, dass sie aufgrund der tastsache, dass jedes signal durch einen ISR direkt ausgelöst wird sehr präzise ist.

Was haltet ihr von der alternativen Idee?

mfg Sirvivor

Willa
23.03.2009, 07:35
Ich verstehe leider kein C, vielleicht ist aber der Beitrag im Wiki zur Servoansteuerung hilfreich für dich? Das entspricht der von dir beschriebenen Idee. Damit gehen bestimmt auch noch 18 Servos. Ist halt in BASCOM beschrieben, aber das ist ja ziemlich leicht verständlich:
https://www.roboternetz.de/wissen/index.php/Servoansteuerung

Du musst unbedingt bis 2.55ms gehen? bei 1-2ms Pulslänge komme ich auf max 36ms, im Mittel jedoch auf 27 ms (oder sind deine Servos alle gleichzeitig am oberen Anschlag?) 36-27ms sollte eigentlich noch gehen als frametime.

WarChild
23.03.2009, 11:21
@ Radbruch
Du hattest recht: es ware der falsche datentyp für meine Zähler

VIELEN DANK!!!

mfg WarChild

radbruch
23.03.2009, 16:07
Hallo

Obwohl hier im RN-Forum schon einige Möglichkeiten gezeigt werden wie man viele Servos mit wenig Microkontroller ansteuert möchte ich hier noch meinen Weg vorstellen und testen lassen. Ich verwende das aber erst seit ein paar Tagen mit nur 4 Servos und kann deshalb nicht sagen ob es mit 8 (oder mehr) Servos auch so gut funktioniert.

Ohne viele Worte hier der Code:

// Servos ansteuern mit 8MHz Mega32 und 8-Bit Timer2 Overflow-ISR 22.3.2008 mic

// Die Servosimpulse werden nacheinander erzeugt. Die Impulsdauer jedes Servos
// setzt sich aus einem Grundimpuls (der für alle Servos gleich ist) und seinem
// Positionswert zwischen 0 und 255 zusammen.

// In der ISR werden im Wechsel ein Grundimpuls und ein Positionswert erzeugt
// und zum jeweiligen Servo gesendet. Nach den Servoimpulsen wird eine
// Pause eingefügt um die 50Hz Wiederholfrequenz (20ms) zu erzeugen.

// Diese auf acht Servos aufgebohrte Version scheint zu funktionieren,
// ich habe es allerdings nur mit angeschlossenen Servos 1-4 ausprobiert.

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

// Servoausgänge 1-8
#define servoinit {DDRB |= (1<<PB7); PORTB &= ~(1<<PB7); DDRC |= 0b01110000; PORTC &= ~0b01110000;}
#define servo1on PORTC |= (1<<PC4)
#define servo1off PORTC &= ~(1<<PC4)
#define servo2on PORTC |= (1<<PC5)
#define servo2off PORTC &= ~(1<<PC5)
#define servo3on PORTC |= (1<<PC6)
#define servo3off PORTC &= ~(1<<PC6)
#define servo4on PORTB |= (1<<PB7)
#define servo4off PORTB &= ~(1<<PB7)

#define servo5on PORTB |= (1<<PB0) // Dummyservos 4-8 an SL6
#define servo5off PORTB &= ~(1<<PB0)
#define servo6on PORTB |= (1<<PB0)
#define servo6off PORTB &= ~(1<<PB0)
#define servo7on PORTB |= (1<<PB0)
#define servo7off PORTB &= ~(1<<PB0)
#define servo8on PORTB |= (1<<PB0)
#define servo8off PORTB &= ~(1<<PB0)

uint8_t servo1, servo2, servo3, servo4, servo5, servo6, servo7, servo8;

int main(void)
{
servoinit; // Datenrichtung der Servopins einstellen

//Timer2 Initialisierung
// für 8MHz Takt:
TCCR2 = (0 << WGM21) | (0 << COM20) | (1 << CS22); // Normal Mode, prescaler /64
// für 16MHz Takt:
//TCCR2 = (0 << WGM21) | (0 << COM20) | (1 << CS22) | (1 << CS20); // /128
TIMSK |= (1 << TOIE2); // Timer2 Overflow-Interrupt erlauben -> Servos an
//TIMSK &= ~(1 << TOIE2); // Timer2 Overflow-Interrupt verbieten -> Servos aus
sei();

servo1=125; // Mittelposition, Drehbereich ist von 0-255!
servo2=125;
servo3=125;
servo4=125;
servo5=125;
servo6=125;
servo7=125;
servo8=125;

while(1) // Hauptschleife
{
}
return(0);
}
ISR (TIMER2_OVF_vect)
{
static uint8_t servo_nr=0, grundimpuls=0; // Gestartet wird am Ende der Pause
static uint16_t impulspause;
if(servo_nr)
{
// Endweder wird hier der Grundimpuls erzeugt (Länge 56 Einheiten)
if(grundimpuls++ & 1) { TCNT2=200; impulspause-=256-200; } else
// Oder der zur Servoposition gehörende Impuls (0-255, 0 ist der längste Impuls!)
{
if(servo_nr==1) {TCNT2=servo1; servo1on; impulspause-=servo1;}
if(servo_nr==2) {TCNT2=servo2; servo1off; servo2on; impulspause-=servo2;}
if(servo_nr==3) {TCNT2=servo3; servo2off; servo3on; impulspause-=servo3;}
if(servo_nr==4) {TCNT2=servo4; servo3off; servo4on; impulspause-=servo4;}

if(servo_nr==5) {TCNT2=servo5; servo4off; servo5on; impulspause-=servo5;}
if(servo_nr==6) {TCNT2=servo6; servo5off; servo6on; impulspause-=servo6;}
if(servo_nr==7) {TCNT2=servo7; servo6off; servo7on; impulspause-=servo7;}
if(servo_nr==8) {TCNT2=servo8; servo7off; servo8on; impulspause-=servo8;}
if(servo_nr==9) {servo8off; servo_nr=0;}
if(servo_nr) servo_nr++;
}
}
else
// Anschliessend wird die Impulspause erzeugt. Sie ergibt sich aus der Startlänge-
// der Summe der einzelnen Impulslängen. Bei acht Servos errechnet sich der
// kleinste benötigte Startwert für Impulspause etwa so:

// 8*56 + 8*256 = 2496 (Summe der Grundimpulse + Summe der Positionsimpulse)
{
if(impulspause>256) impulspause-=256; // Gesamtpause in 256er-Schritten
else {TCNT2=-impulspause; servo_nr++; impulspause=3000;} // die Restpause
}
}(Das wird bei mir mit genau 500 Bytes übersetzt, ist das ein Ohmen?)

Es wäre nett wenn das mal jemand testen würde, bei Interesse kann ich auch noch ein bisschen mehr über die Funktion plaudern.

Gruß

mic

WarChild
24.03.2009, 02:05
Ich hatte mir das ja auch schon überlegt, die impulse seriell zu produzieren, aber dafür wären mindestens zwei timer nötig um widerum immer zwei signale gleichzeitg zu erzeugen, da 18*2,5ms=54ms zu lange für eine serielle ausführung ist.
Jetzt läuft aber auch die parallele Erzeugung von 18 Servoimpulsen.
Das einzige kleine problem ist:
den Stellbereich kann man ja als lineare funktion interpretieren:
0°=tmin //minimale Impulsdauer
180°=tmax //maximale Impulsdauer
wenn man nun den stellwinkel in grad vorgibt berechnet sich die Impulsdauer wie folgt:
tmin+(tmax-tmin)/180*x //standard Geradengleichung, zwei Punkte Form.
der konstante kalirierungsfaktor vor dem x um die umrechnug von ° in 10µsekunden der Impulsdauer ist ein wert, der bei meinen servos besipielweise (220-58 )/180=0.9 beträgt.

wenn ich diese rechnung mit 0.9 ausführen lasse, dann stimmen die stellwinkel absolut nicht mehr.
selbst wenn ich nur bei einem winkel diesen faktor zu korrektur eingebe, verändern sich alle anderen winkel auch. Je mehr Faktoren ich hinzufüge, desto schlimmer wird es.

Ist die multiplikation mit feskommazahlen so kompliziert für den prozessor,dass dadurch mein gesammtes Timing durcheinander gerät?

Aber abgesehen von dieser Problematik, kann ich jetzt hervorragend 18 verschiedene Impulse simultan erzeugen, und dass mit nur einem Timer.
Der Code ist noch stark optimierungsbedürftig, er ist so im Verlauf des Experimentierens entstanden, was schonmal sehr schlecht ist.



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

//minimale Impulsdauer
#define tmin1 50
#define tmin2 50
#define tmin3 50
#define tmin4 50
#define tmin5 50
#define tmin6 50
#define tmin7 50
#define tmin8 50
#define tmin9 50
#define tmin10 50
#define tmin11 50
#define tmin12 50
#define tmin13 50
#define tmin14 50
#define tmin15 50
#define tmin16 50
#define tmin17 50
#define tmin18 50
//maximale Impulsdauer
#define tmax1 220
#define tmax2 220
#define tmax3 220
#define tmax4 220
#define tmax5 220
#define tmax6 220
#define tmax7 220
#define tmax8 220
#define tmax9 220
#define tmax10 220
#define tmax11 220
#define tmax12 220
#define tmax13 220
#define tmax14 220
#define tmax15 220
#define tmax16 220
#define tmax17 220
#define tmax18 220
//Skalierungsfaktor von Grad auf Impulssekunden (tmax_i-tmin_i)/180 funktionier nicht, daher zunächst korrektur durch addition von impulsdauer
#define pulsfaktor1 0.9 //... läuft nicht



//Wert zur korrektur der schief sitzenden Servohörner --> Überflüssig, wenn die skalierung läuft
signed int calibrate[19] = {0,1,1,0,8,-8,-4,-6,2,-8,1,0,-6,-6,6,4,-1,-4,-7};



volatile unsigned char timer1 = 0;
volatile unsigned char timer2 = 8;

#define ready 0
#define nextpulse 1
#define pulsestarted 2
#define pulseready 3
volatile unsigned char state = ready;

//Das Element [0] ist leer, damit die Indizes zu den servobezeichnern passen
// später sind die arrays mit richtigen winkeln gefüllt, jetzt habe dich die winkel vorab mit meinem skalierungsfaktor multipliziert.
//unsigned char angle[19] = {0,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60 ,60,60};
unsigned char angle[19] = {0,45,140,95,32,135,85,45,140,95,32,135,85,45,140, 95,36,135,85};//stehen tief
//unsigned char angle[19] = {0,95,80,95,85,70,85,95,80,95,85,70,85,95,80,95,85 ,70,85};//stehen hoch

//eigentlich waren die pulse auch ein Array, aber der compiler hat gemeckert, weil ich die Einträge als Vergleichswert in einer if Bedingung benutzen wollte.
unsigned char pulse1 = 135; //Mittelstellung
unsigned char pulse2 = 135;
unsigned char pulse3 = 135;
unsigned char pulse4 = 135;
unsigned char pulse5 = 135;
unsigned char pulse6 = 135;
unsigned char pulse7 = 135;
unsigned char pulse8 = 135;
unsigned char pulse9 = 135;
unsigned char pulse10= 135;
unsigned char pulse11= 135;
unsigned char pulse12= 135;
unsigned char pulse13= 135;
unsigned char pulse14= 135;
unsigned char pulse15= 135;
unsigned char pulse16= 135;
unsigned char pulse17= 135;
unsigned char pulse18= 135;


SIGNAL (SIG_OVERFLOW0) //frisst ~7% der ProzessorLeistung
{
TCNT0 = 256 - 160; //Timer0 mit 96 neu vorladen
timer1++;
if(timer1 == 250)
{
timer1=0; //timer1 endet bei 2,5ms
timer2--; //timer2 wird im 2,5ms Intervall herabgezählt
}
if(timer2 == 0){
timer2 = 8;
state = nextpulse;} //
}

void pulsegenerator(void)
{
if(state == nextpulse) //alle 20ms alle Signalleitungen auf 1 setzen
{
PORTA |= (1 << PA0);
PORTB |= (1 << PB0);PORTB |= (1 << PB1);PORTB |= (1 << PB2);PORTB |= (1 << PB3);PORTB |= (1 << PB4);
PORTC |= (1 << PC2);PORTC |= (1 << PC3);PORTC |= (1 << PC4);PORTC |= (1 << PC5);PORTC |= (1 << PC6);PORTC |= (1 << PC7);
PORTD |= (1 << PD2);PORTD |= (1 << PD3);PORTD |= (1 << PD4);PORTD |= (1 << PD5);PORTD |= (1 << PD6);PORTD |= (1 << PD7);
state = pulsestarted;
}
if(state == pulsestarted)
{ //Signal des jeweiligen Servos beenden
if(timer1 >= pulse1){
PORTC &= ~(1 << PC7);}
if(timer1 >= pulse2){
PORTC &= ~(1 << PC6);}
if(timer1 >= pulse3){
PORTC &= ~(1 << PC5);}
if(timer1 >= pulse4){
PORTC &= ~(1 << PC2);}
if(timer1 >= pulse5){
PORTC &= ~(1 << PC3);}
if(timer1 >= pulse6){
PORTC &= ~(1 << PC4);}
if(timer1 >= pulse7){
PORTB &= ~(1 << PB2);}
if(timer1 >= pulse8){
PORTB &= ~(1 << PB3);}
if(timer1 >= pulse9){
PORTB &= ~(1 << PB4);}
if(timer1 >= pulse10){
PORTB &= ~(1 << PB1);}
if(timer1 >= pulse11){
PORTB &= ~(1 << PB0);}
if(timer1 >= pulse12){
PORTA &= ~(1 << PA0);}
if(timer1 >= pulse13){
PORTD &= ~(1 << PD4);}
if(timer1 >= pulse14){
PORTD &= ~(1 << PD3);}
if(timer1 >= pulse15){
PORTD &= ~(1 << PD2);}
if(timer1 >= pulse16){
PORTD &= ~(1 << PD5);}
if(timer1 >= pulse17){
PORTD &= ~(1 << PD6);}
if(timer1 >= pulse18){
PORTD &= ~(1 << PD7);}
}
if (timer2 <= 7){ // nach 2,5ms ist die pulsgenerierung beendet
state = pulseready;}
}


void pulsecalculator(void) //in dieser funktion bestehen die probleme mit dem skalierungsfaktor, daher zunächst die ungenaue berechnung mit konstanter anpassung angle ist jetzt kein winkel sondern eine impulsdauer, bei der 0-190 = 0-1,9ms = 0°-180° gilt. (unpraktisch)
{
pulse1 = (tmin1 + calibrate[1] + (180-angle[1])); //bei andersherum montierten servos: (180-angle)
pulse2 = (tmin2 + calibrate[2] + (180-angle[2])); //*
pulse3 = (tmin3 + calibrate[3] + (180-angle[3])); //*
pulse4 = (tmin4 + calibrate[4] + angle[4]);
pulse5 = (tmin5 + calibrate[5] + angle[5]);
pulse6 = (tmin6 + calibrate[6] + angle[6]);
pulse7 = (tmin7 + calibrate[7] + (180-angle[7]));
pulse8 = (tmin8 + calibrate[8] + (180-angle[8]));
pulse9 = (tmin9 + calibrate[9] + (180-angle[9]));
pulse10= (tmin10 + calibrate[10] + angle[10]);
pulse11= (tmin11 + calibrate[11] + angle[11]);
pulse12= (tmin12 + calibrate[12] + angle[12]);
pulse13= (tmin13 + calibrate[13] + (180-angle[13]));
pulse14= (tmin14 + calibrate[14] + (180-angle[14]));
pulse15= (tmin15 + calibrate[15] + (180-angle[15]));
pulse16= (tmin16 + calibrate[16] + angle[16]);
pulse17= (tmin17 + calibrate[17] + angle[17]);
pulse18= (tmin18 + calibrate[18] + angle[18]);
}
int main (void)
{
//Ausgänge definieren
DDRA = 0b00000001; //PA0 Servo 12
DDRB = 0b00011111; //PB0-4 Servo 11/10/7/8/9
DDRC = 0b11111100; //PC2-7 Servo 4/5/6/3/2/1
DDRD = 0b11111100; //PD2-7 Servo 15/14/13/16/17/18

//Timer0 Konfigurieren
TCNT0 = 256 - 160; //Timer0 mit 96 vorladen (10µs Timer)
TCCR0 |= (1 << CS00); //CS02/01/00 mit 001 belegen: Teiler 1
TIMSK |= (1 << TOIE0); //Timer0 Interrupt freigegeben
sei(); //Interrupts freigegeben


while(1) //Arbeitsschleife
{
pulsegenerator(); // Funktion, die alle 20ms die Servo PWMs erzeugt
if (state == pulseready) //hier können unterprogramme in der freizeit ausgeführt werden, aber nichts blockendes oder zu Zeitaufwendiges.
{
pulsecalculator(); // Wandelt Werte von Angle in Pulsweiten um.
}
}
return 1;
}


Der code ist ein horror, aber vielleicht weiß ja jemand, weshalb die multiplikation mit gebrochenrationalen zahlen so ein problem ist.

mfg sirvivor

WarChild
24.03.2009, 02:36
@radbruch
ich habe dein Programm mal auf den Ports B0-B4 und C4-6 ausgeführt und hat einwandfrei funktioniert.

glückwunsch!

sehr schön überischtlicher code

mfg WarChild

WarChild
24.03.2009, 09:25
ich hatte vor dein programm mal testweise auf 18 servos zu erweitern,
allerdings brauche ich dafür timer0 oder timer1 und die kann ich nicht auf /128 einstellen.
Du als Entwickler des programms: denkst du es ist möglich, dass ich durch Veränderung der Variablen (alles verdoppeln) mit timer1 prescaler /64 das gleiche wie mit timer2 erzeugen kann?

Ich habe es soweit auf 9 servos pro timer erweitert.
wenn ich den timer0 (timer1 kann ich nicht) laufen lasse, dann funktionieren die servos, allerdings ist das Signal logischerweise wegen des falschen taktes verfälscht. (pause verdoppeln war kein problem, also stimmt zumindst die frequenz)
Wenn nur ein timer läuft geht alles, wenn beide laufen, dann zuckt jeweils der servo, der das erste signal bekommt. (also 1 und 10)
Da die zahlen wegen der verdopplung zu groß für den timer0 werden muss ich das noch auf time 1 modifizieren.



// Servos ansteuern mit 16MHz Mega32 und 8-Bit Timer2 Overflow-ISR 22.3.2008 mic

// Die Servosimpulse werden nacheinander erzeugt. Die Impulsdauer jedes Servos
// setzt sich aus einem Grundimpuls (der für alle Servos gleich ist) und seinem
// Positionswert zwischen 0 und 255 zusammen.

// In der ISR werden im Wechsel ein Grundimpuls und ein Positionswert erzeugt
// und zum jeweiligen Servo gesendet. Nach den Servoimpulsen wird eine
// Pause eingefügt um die 50Hz Wiederholfrequenz (20ms) zu erzeugen.

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

// Servoausgänge 1-18
//PA0 Servo 12, PB0-4 Servo 11/10/7/8/9, PC2-7 Servo 4/5/6/3/2/1, PD2-7 Servo 15/14/13/16/17/18
#define DDRAinit { DDRA = 0b00000001;DDRB = 0b00011111;DDRC = 0b11111100;DDRD = 0b11111100;}
#define servo1on PORTC |= (1<<PC2)
#define servo1off PORTC &= ~(1<<PC2)
#define servo2on PORTC |= (1<<PC3)
#define servo2off PORTC &= ~(1<<PC3)
#define servo3on PORTC |= (1<<PC4)
#define servo3off PORTC &= ~(1<<PC4)
#define servo4on PORTC |= (1<<PC7)
#define servo4off PORTC &= ~(1<<PC7)
#define servo5on PORTC |= (1<<PC6)
#define servo5off PORTC &= ~(1<<PC6)
#define servo6on PORTC |= (1<<PC5)
#define servo6off PORTC &= ~(1<<PC5)
#define servo7on PORTB |= (1<<PB2)
#define servo7off PORTB &= ~(1<<PB2)
#define servo8on PORTB |= (1<<PB1)
#define servo8off PORTB &= ~(1<<PB1)
#define servo9on PORTB |= (1<<PB0)
#define servo9off PORTB &= ~(1<<PB0)
#define servo10on PORTB |= (1<<PB3)
#define servo10off PORTB &= ~(1<<PB3)
#define servo11on PORTB |= (1<<PB4)
#define servo11off PORTB &= ~(1<<PB4)
#define servo12on PORTA |= (1<<PA0)
#define servo12off PORTA &= ~(1<<PA0)
#define servo13on PORTD |= (1<<PD5)
#define servo13off PORTD &= ~(1<<PD5)
#define servo14on PORTD |= (1<<PD6)
#define servo14off PORTD &= ~(1<<PD6)
#define servo15on PORTD |= (1<<PD7)
#define servo15off PORTD &= ~(1<<PD7)
#define servo16on PORTD |= (1<<PD4)
#define servo16off PORTD &= ~(1<<PD4)
#define servo17on PORTD |= (1<<PD3)
#define servo17off PORTD &= ~(1<<PD3)
#define servo18on PORTD |= (1<<PD2)
#define servo18off PORTD &= ~(1<<PD2)


uint8_t servo1, servo2, servo3, servo4, servo5, servo6, servo7, servo8, servo9, servo10, servo11, servo12, servo13, servo14, servo15, servo16, servo17, servo18;
// hilfswerte zur Anpassung an die geänderte timer frequenz
uint16_t servo10a, servo11a, servo12a, servo13a, servo14a, servo15a, servo16a, servo17a, servo18a;

int main(void)
{
DDRAinit; // Datenrichtung der Servopins einstellen
//Timer0 Initialisierung
TCCR0 = (0 << WGM01) | (0 << COM00) | (1 << CS01) | (1 << CS00); // /64
TIMSK |= (1 << TOIE0); // Timer0 Overflow-Interrupt erlauben -> Servos an
//TIMSK &= ~(1 << TOIE0); // Timer0 Overflow-Interrupt verbieten -> Servos aus

//Timer2 Initialisierung
TCCR2 = (0 << WGM21) | (0 << COM20) | (1 << CS22) | (1 << CS20); // /128
TIMSK |= (1 << TOIE2); // Timer2 Overflow-Interrupt erlauben -> Servos an
//TIMSK &= ~(1 << TOIE2); // Timer2 Overflow-Interrupt verbieten -> Servos aus
sei();

servo1=125; // Mittelposition, Drehbereich ist von 0-255!
servo2=125;
servo3=125;
servo4=125;
servo5=125;
servo6=125;
servo7=125;
servo8=125;
servo9=125;
servo10=125;
servo11=125;
servo12=125;
servo13=125;
servo14=125;
servo15=125;
servo16=125;
servo17=125;
servo18=125;
// hier wird der stellwert an die geänderte timer frequenz angepasst (zum schutz vor einem überlauf des timer0 zunächst durch 8 geteilt, später für timer 1 mit 2 multipliziert
servo10a = servo10/8;
servo11a = servo11/8;
servo12a = servo12/8;
servo13a = servo13/8;
servo14a = servo14/8;
servo15a = servo15/8;
servo16a = servo16/8;
servo17a = servo17/8;
servo18a = servo18/8;

while(1) // Hauptschleife
{
}
return(0);
}

ISR (TIMER0_OVF_vect) //variablen mit index a bzw. 1 versehen
{
static uint8_t servo_nra=0, grundimpuls1=0; // Gestartet wird am Ende der Pause
static uint16_t impulspause1;
if(servo_nra)
{
// Entweder wird hier der Grundimpuls erzeugt (Länge 56 Einheiten)
if(grundimpuls1++ & 1) { TCNT0=200; impulspause1-=256-144; } else //2*pause
// Oder der zur Servoposition gehörende Impuls (0-255, 0 ist der längste Impuls!)
{
if(servo_nra==1) {TCNT0=servo10a; servo10on; impulspause1-=servo10a; impulspause1-=servo10a}
if(servo_nra==2) {TCNT0=servo11a; servo10off; servo11on; impulspause1-=servo11a;impulspause1-=servo11a;}
if(servo_nra==3) {TCNT0=servo12a; servo11off; servo12on; impulspause1-=servo12a;impulspause1-=servo12a;}
if(servo_nra==4) {TCNT0=servo13a; servo12off; servo13on; impulspause1-=servo13a;impulspause1-=servo13a;}

if(servo_nra==5) {TCNT0=servo14a; servo13off; servo14on; impulspause1-=servo14a;impulspause1-=servo14a;}
if(servo_nra==6) {TCNT0=servo15a; servo14off; servo15on; impulspause1-=servo15a;impulspause1-=servo15a;}
if(servo_nra==7) {TCNT0=servo16a; servo15off; servo16on; impulspause1-=servo16a;impulspause1-=servo16a;}
if(servo_nra==8) {TCNT0=servo17a; servo16off; servo17on; impulspause1-=servo17a;impulspause1-=servo17a;}
if(servo_nra==9) {TCNT0=servo18a; servo17off; servo18on; impulspause1-=servo18a;impulspause1-=servo18a;}
if(servo_nra==10) {servo18off; servo_nra=0;}
if(servo_nra) servo_nra++;
}
}
else
// Anschliessend wird die Impulspause erzeugt. Sie ergibt sich aus der Startlänge-
// der Summe der einzelnen Impulslängen. Bei acht Servos errechnet sich der
// kleinste benötigte Startwert für Impulspause etwa so:

// 8*56 + 8*256 = 2496 (Summe der Grundimpulse + Summe der Positionsimpulse)
{
if(impulspause1>256) impulspause1-=256; // Gesamtpause in 256er-Schritten
else {TCNT2=-impulspause1; servo_nra++; impulspause1=6000;} // die Restpause
}
}

ISR (TIMER2_OVF_vect) //variablen mit index b bzw. 2 versehen
{
static uint8_t servo_nrb=0, grundimpuls2=0; // Gestartet wird am Ende der Pause
static uint16_t impulspause2;
if(servo_nrb)
{
// Endweder wird hier der Grundimpuls erzeugt (Länge 56 Einheiten)
if(grundimpuls2++ & 1) { TCNT2=200; impulspause2-=256-200; } else
// Oder der zur Servoposition gehörende Impuls (0-255, 0 ist der längste Impuls!)
{
if(servo_nrb==1) {TCNT2=servo1; servo1on; impulspause2-=servo1;}
if(servo_nrb==2) {TCNT2=servo2; servo1off; servo2on; impulspause2-=servo2;}
if(servo_nrb==3) {TCNT2=servo3; servo2off; servo3on; impulspause2-=servo3;}
if(servo_nrb==4) {TCNT2=servo4; servo3off; servo4on; impulspause2-=servo4;}

if(servo_nrb==5) {TCNT2=servo5; servo4off; servo5on; impulspause2-=servo5;}
if(servo_nrb==6) {TCNT2=servo6; servo5off; servo6on; impulspause2-=servo6;}
if(servo_nrb==7) {TCNT2=servo7; servo6off; servo7on; impulspause2-=servo7;}
if(servo_nrb==8) {TCNT2=servo8; servo7off; servo8on; impulspause2-=servo8;}
if(servo_nrb==9) {TCNT2=servo9; servo8off; servo9on; impulspause2-=servo9;}
if(servo_nrb==10) {servo9off; servo_nrb=0;}
if(servo_nrb) servo_nrb++;
}
}
else
// Anschliessend wird die Impulspause erzeugt. Sie ergibt sich aus der Startlänge-
// der Summe der einzelnen Impulslängen. Bei acht Servos errechnet sich der
// kleinste benötigte Startwert für Impulspause etwa so:

// 8*56 + 8*256 = 2496 (Summe der Grundimpulse + Summe der Positionsimpulse)
{
if(impulspause2>256) impulspause2-=256; // Gesamtpause in 256er-Schritten
else {TCNT2=-impulspause2; servo_nrb++; impulspause2=2900;} // die Restpause
}
}

Ich konnte schon herausfinden, dass das Zucken abhängig vom Verhältnis der Restpausen ist.
Wahrscheinlich liegen die pararallel laufenden timer nicht ganz synchron und daher haken sie gelegentlich.
Aber ich hoffe, dass das verschwindet, sobalt der timer 1 exakt läuft. (also beide timerdurchläufe gleich lange benötigen)

kann mir jemand nen rat geben, wie ich:

if(servo_nra==2) {TCNT0=servo11a; servo10off; servo11on; impulspause1-=servo11a}
also speziell TCNT1A,B vorladen muss? mit 65536- das doppelte meiner eigentlichen impulsdauer oder einfach nur mit der doppelten impulsdauer.
Oder mit (65536-512)+2*stellwert, also timermax-pulsmax+sollwert
bei timer 0 funktioniert die formel 255-255+sollwert=sollwert
das ist wegen der umgekehrten logik etwas verwirrend...

Wie kann man das am schnellsten schreiben? man kann bestimmt kein 16bit int in TCNT1A schreiben und beschreibt B gleich mit oder?
oder so: 16bitint/256->A 16bitint%256-->B?

mfg WarChild

radbruch
24.03.2009, 17:31
Hallo

Hier ein Versuch mit Timer1 und 18 Servos. Da steigt dann mein RP6 aus, vermutlich ein Zeitüberlauf in der ISR:

// 18 Servos ansteuern mit Mega32 und 16-Bit Timer1 24.3.2008 mic

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

#define systemtakt 1 // 1 bei 8MHz, 2 bei 16MHz-Prozessortakt
#define grundimpuls 47 // grundimpuls + 125 sollte Servomitte sein

// Servoausgänge 1-8
#define servoinit {DDRB |= (1<<PB7); PORTB &= ~(1<<PB7); DDRC |= 0b01110000; PORTC &= ~0b01110000;}
#define servo1on PORTC |= (1<<PC4)
#define servo1off PORTC &= ~(1<<PC4)
#define servo2on PORTC |= (1<<PC5)
#define servo2off PORTC &= ~(1<<PC5)
#define servo3on PORTC |= (1<<PC6)
#define servo3off PORTC &= ~(1<<PC6)
#define servo4on PORTB |= (1<<PB7)
#define servo4off PORTB &= ~(1<<PB7)

#define servo5on PORTB |= (1<<PB0) // Dummyservos 4-9 an SL6
#define servo5off PORTB &= ~(1<<PB0)
#define servo6on PORTB |= (1<<PB0)
#define servo6off PORTB &= ~(1<<PB0)
#define servo7on PORTB |= (1<<PB0)
#define servo7off PORTB &= ~(1<<PB0)
#define servo8on PORTB |= (1<<PB0)
#define servo8off PORTB &= ~(1<<PB0)
#define servo9on PORTB |= (1<<PB0)
#define servo9off PORTB &= ~(1<<PB0)

volatile uint8_t p; // 20ms-Timer
uint16_t servo1, servo2, servo3, servo4, servo5, servo6, servo7, servo8, servo9;

/************************* Ausgabe an Terminal ********************************/
void writeChar(char ch) {while (!(UCSRA & (1<<UDRE))); UDR = (uint8_t)ch;}
void writeString(char *string) {while(*string) writeChar(*string++);}
void writeInteger(int16_t number, uint8_t base)
{char buffer[17]; itoa(number, &buffer[0], base); writeString(&buffer[0]);}
/************************************************** ****************************/

int main(void)
{
/************************ UART-Setup für RP6 *******************************/
#define BAUD_LOW 38400 //Low speed - 38.4 kBaud
#define UBRR_BAUD_LOW ((F_CPU/(16*BAUD_LOW))-1)

UBRRH = UBRR_BAUD_LOW >> 8; // Baudrate is Low Speed
UBRRL = (uint8_t) UBRR_BAUD_LOW;
UCSRA = 0x00;
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
UCSRB = (1 << TXEN) | (1 << RXEN) | (1 << RXCIE);
/************************************************** *************************/

//Timer1 Initialisierung
TCCR1A = 0;
TCCR1B = (0<<CS12) | (1<<CS11) | (1<<CS10); // Prescaler /64
TCCR1B|= (1<<WGM12); // CTC-Mode
OCR1A=100; // 100 Takte bis zum ersten Interrupt
OCR1B=100;
TIMSK |= (1 << OCIE1A); // Das klappt so leider nur mit Timer1A?
//TIMSK |= (1 << OCIE1B);

servo1=60; // Drehbereich ist ca. 10-245!
servo2=125;
servo3=190;
servo4=245;
servo5=125;
servo6=125;
servo7=125;
servo8=125;
servo9=125;
//servo1=servo2=servo3=servo4=servo5=servo6=servo7=s ervo8=servo9=60; // Test

servoinit; // Datenrichtung der Servopins einstellen
sei(); // ... und los!

while(1) // Hauptschleife
{
servo4=125; // ab hier funktioniert es mit meinen RP6 nicht mehr!
p=50; while(p); // Das sollte ungefähr 50*20ms=1 Sekunde verzögern
servo4=125;
p=50; while(p);
}
return(0);
}
ISR (TIMER1_COMPB_vect)
{
uint16_t temp=grundimpuls;
static uint8_t servo_nr=1;
static uint16_t impulspause=3000;

if(servo_nr==1) {temp+=servo1; servo1on; if(p) p--;}
if(servo_nr==2) {temp+=servo2; servo1off; servo2on;}
if(servo_nr==3) {temp+=servo3; servo2off; servo3on;}
if(servo_nr==4) {temp+=servo4; servo3off; servo4on;}
if(servo_nr==5) {temp+=servo5; servo4off; servo5on;}
if(servo_nr==6) {temp+=servo6; servo5off; servo6on;}
if(servo_nr==7) {temp+=servo7; servo6off; servo7on;}
if(servo_nr==8) {temp+=servo8; servo7off; servo8on;}
if(servo_nr==9) {temp+=servo9; servo8off; servo9on;}
if(servo_nr >9) {temp =impulspause; servo9off; servo_nr=0;}

OCR1B=temp*systemtakt;

if(servo_nr) impulspause-=temp; else impulspause=3000;
servo_nr++;
}

ISR (TIMER1_COMPA_vect)
{
uint16_t temp=grundimpuls;
static uint8_t servo_nr=1;
static uint16_t impulspause=3000;

switch(servo_nr)
{
case 1: temp+=servo1; servo1on; if(p) p--; break;
case 2: temp+=servo2; servo1off; servo2on; break;
case 3: temp+=servo3; servo2off; servo3on; break;
case 4: temp+=servo4; servo3off; servo4on; break;

case 5: temp+=servo5; servo4off; servo5on; break;
case 6: temp+=servo6; servo5off; servo6on; break;
case 7: temp+=servo7; servo6off; servo7on; break;
case 8: temp+=servo8; servo7off; servo8on; break;
case 9: temp+=servo9; servo8off; servo9on; break;
default:temp =impulspause; servo9off; servo_nr=0; break;
}
OCR1A=temp*systemtakt;

if(servo_nr) impulspause-=temp; else impulspause=3000;
servo_nr++;
}
Mit systemtakt kann man zwischen 8 oder 16MHz wählen. Das ist einfach nur ein Multiplikator der im 16MHz-Modus alle Timerwerte mit 2 multipliziert. Ob das wie gedacht funktioniert kann ich nicht testen weil ich keinen 16MHz-Mega32 habe.

Grundsätzlich funktionieren so immerhin 9 Servos an Timer1A. Allerdings wird bei mir dann die Hauptschleife nicht mehr ausgeführt. Eigentlich sollten die minimalen (64*grundimpuls)-Takte zur Ausführung der ISR ausreichen, ich weiß noch nicht, warum mein RP6 bockt.

Wenn ich (nur) die ISR für COMPB aktiviere funktioniert nichts (nur der Impuls für das erste Servo wird gestartet aber nie beendet) An den unterschiedlichen Varianten der ISRs liegts anscheinend nicht denn wenn man die über kreuz umbenennt bleibt das Verhalten gleich. Scheinbar ist da ein Unterschied zwischen TIMER1A und TIMER1B dessen Beschreibung ich im Datenblatt noch nicht gefunden habe. Oder ich mache einen blöden Denk- oder Anfängerfehler den ich nicht erkenne :(

Gruß

mic

WarChild
24.03.2009, 20:46
Hi,

Ich habe mich nochmal daran gemacht mein Programm von Grundauf neu zu überdenken. Es ist sch**ße, wenn die Impulse von einer funktion erzeugt werden, die von der main Funktion aus aufgerufen wird. Die Gefahr, dass irgendeine andere funktion einmal zu lange braucht, ist zu groß.
Daher habe ich die impulserzeugung jetzt in den ISR verlegt.
Eine kleine Erläuterung steht im Header. Ein standardmäßiger Grundimpuls zur erhöhung der Auflösung, sowie weitere Calibrierungsfunktionen folgen.
Allerdings muss da noch irgendwo ein minimaler zufallskinken drin sein. gelegentlich muss ch mehrmals den Reset button drücken, bis das Programm Ordnungsgemäß läuft. Wenn ich den ISP entferne ist der Fehler weg.


// Servos ansteuern mit 16MHz Mega32 und 8-Bit Timer0 und Timer2 (als ms stopwatch verwendbar)

// Die Servosimpulse werden simultan erzeugt. Die Impulsdauer jedes Servos
// besteht nur aus einem Wert, der zwischen 0 und 255 liegen muss. Der Stellbereich liegt dann bei
// ungefähr zwischen 50 und 230. --> ~180 werte für 180 grad, also eine auflösung bis auf ein Grad.

// In der ISR wird ein 1 ms Timer2 verwendet, um einen Zähler alle 20ms zurückzusetzen und den impulsmessenden Timer0 zu starten.
// Während Timer0 läuft ist die maximale Auslastung: alle 160 Takte ein interupt, mit maximal 100 Befehlen 63% im Durchschnitt 30% nur für den ISR
// Daher wird der Timer0 auch diret nach der Pulserzeugung gestoppt und erst ~18ms später von Timer2 aufgerufen.
// Während der Pause läuft also nichts außer einem 1ms Timer. Viel Zeit um andere Dinge zu erldigen.

// Diese auf acht Servos aufgebohrte Version scheint zu funktionieren,
// ich habe es allerdings nur mit angeschlossenen Servos 1-4 ausprobiert.

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

// Servoausgänge 1-8
//PA0 Servo 12, PB0-4 Servo 11/10/7/8/9, PC2-7 Servo 4/5/6/3/2/1, PD2-7 Servo 15/14/13/16/17/18
#define DDRAinit { DDRA = 0b00000001;DDRB = 0b00011111;DDRC = 0b11111100;DDRD = 0b11111100;}
#define servo1on PORTC |= (1<<PC7)
#define servo1off PORTC &= ~(1<<PC7)
#define servo2on PORTC |= (1<<PC6)
#define servo2off PORTC &= ~(1<<PC6)
#define servo3on PORTC |= (1<<PC5)
#define servo3off PORTC &= ~(1<<PC5)
#define servo4on PORTC |= (1<<PC2)
#define servo4off PORTC &= ~(1<<PC2)
#define servo5on PORTC |= (1<<PC3)
#define servo5off PORTC &= ~(1<<PC3)
#define servo6on PORTC |= (1<<PC4)
#define servo6off PORTC &= ~(1<<PC4)
#define servo7on PORTB |= (1<<PB2)
#define servo7off PORTB &= ~(1<<PB2)
#define servo8on PORTB |= (1<<PB1)
#define servo8off PORTB &= ~(1<<PB1)
#define servo9on PORTB |= (1<<PB0)
#define servo9off PORTB &= ~(1<<PB0)
#define servo10on PORTB |= (1<<PB3)
#define servo10off PORTB &= ~(1<<PB3)
#define servo11on PORTB |= (1<<PB4)
#define servo11off PORTB &= ~(1<<PB4)
#define servo12on PORTA |= (1<<PA0)
#define servo12off PORTA &= ~(1<<PA0)
#define servo13on PORTD |= (1<<PD5)
#define servo13off PORTD &= ~(1<<PD5)
#define servo14on PORTD |= (1<<PD6)
#define servo14off PORTD &= ~(1<<PD6)
#define servo15on PORTD |= (1<<PD7)
#define servo15off PORTD &= ~(1<<PD7)
#define servo16on PORTD |= (1<<PD4)
#define servo16off PORTD &= ~(1<<PD4)
#define servo17on PORTD |= (1<<PD3)
#define servo17off PORTD &= ~(1<<PD3)
#define servo18on PORTD |= (1<<PD2)
#define servo18off PORTD &= ~(1<<PD2)


uint8_t servo1, servo2, servo3, servo4, servo5, servo6, servo7, servo8, servo9, servo10, servo11, servo12, servo13, servo14, servo15, servo16, servo17, servo18;

volatile unsigned char timer1 = 0;
volatile unsigned char timer2 = 0;
volatile unsigned char signals = 0;

int main(void)
{
DDRAinit; // Datenrichtung der Servopins einstellen
//Timer 0
TCCR0 |= (1 << CS00); //normal mode, prescaler 1
//TIMSK |= (1 << TOIE0); //Timer0 Interrupt freigegeben (wird von timer 1 freigegeben und vorgeladen)
//TCNT0 = 256 - 160; //Timer0 mit 96 vorladen, 1/16MHz*160 = 10µs

//Timer 2
TCCR2 |= (1 << CS22); //normal mode, prescaler 64
TCNT2 = 256-250; //Timer2 mit 6 vorladen, 64/16MHz*250=1ms
TIMSK |= (1 << TOIE2); //Timer2 Interrupt freigeben
sei(); //Interrupts freigegeben

servo1=210; // Mittelposition
servo2=210;
servo3=210;
servo4=210;
servo5=210;
servo6=210;
servo7=210;
servo8=210;
servo9=210;
servo10=210;
servo11=210;
servo12=210;
servo13=210;
servo14=210;
servo15=210;
servo16=210;
servo17=210;
servo18=210;

while(1) // Hauptschleife
{
}
return(0);
}
SIGNAL (SIG_OVERFLOW2) //frisst ~7% der ProzessorLeistung
{
TCNT2 = 256 - 250; //Timer2 mit 6 neu vorladen
timer2++;
if(timer2 == 20)
{
timer2 = 0; //timer2 endet bei 20ms
timer1 = 0;
TCNT0 = 256 - 160; //Timer0 mit 96 vorladen, 1/16MHz*160 = 10µs
//alle Impulse starten
servo1on;
servo2on;
servo3on;
servo4on;
servo5on;
servo6on;
servo7on;
servo8on;
servo9on;
servo10on;
servo11on;
servo12on;
servo13on;
servo14on;
servo15on;
servo16on;
servo17on;
servo18on;
TIMSK |= (1 << TOIE0); //Timer0 Interrupt freigegeben
signals = 18; //Anzahl der laufenden Signale
}
}

SIGNAL (SIG_OVERFLOW0) //frisst ~95% der ProzessorLeistung
{
TCNT0 = 256 - 160; //Timer0 mit 96 neu vorladen
timer1++; //timer1 Zähler misst die impulsdauer in 10µs Schritten
//Bei erreichen der Sollpulsdauer wird der jeweilige Impuls beendet
if(timer1 == servo1){servo1off;signals--;}
if(timer1 == servo2){servo2off;signals--;}
if(timer1 == servo3){servo3off;signals--;}
if(timer1 == servo4){servo4off;signals--;}
if(timer1 == servo5){servo5off;signals--;}
if(timer1 == servo6){servo6off;signals--;}
if(timer1 == servo7){servo7off;signals--;}
if(timer1 == servo8){servo8off;signals--;}
if(timer1 == servo9){servo9off;signals--;}
if(timer1 == servo10){servo10off;signals--;}
if(timer1 == servo11){servo11off;signals--;}
if(timer1 == servo12){servo12off;signals--;}
if(timer1 == servo13){servo13off;signals--;}
if(timer1 == servo14){servo14off;signals--;}
if(timer1 == servo15){servo15off;signals--;}
if(timer1 == servo16){servo16off;signals--;}
if(timer1 == servo17){servo17off;signals--;}
if(timer1 == servo18){servo18off;signals--;}
if(signals == 0){TIMSK &= ~(1 << TOIE0);} //Timer0 stoppen, sobald alle Impulse erzeugt sind
}


mfg WarChild