/*Servo Ansteuerung
  (c) 2004 by Malte Marwedel
  Version 1.00
  www.marwedels.de/malte
  Die Verwendung geschieht auf eigene Verantwortung, es wird nicht garantiert,
  dass diese Datei fehlerfrei ist.
  Die Datei darf frei verwendet werden.
  Änderungen sind erlaubt solage kenntlich gemacht wird, dass es sich
  nicht mehr um die original Datei handelt.

Getestet wurde das Programm mit:
  -einem AT90S4433
  -8MHZ Quarz
  -Zwei Servos an PORTD.2 und PORTD.3
  -winavr20040404
  
Hinweise für die die Verwendung:
  -Die Benutzung geschieht auf eigene Gefahr
  -Andere Controller haben möglicherweise die Bits in den Registern an anderer
   Stelle, zum Teil haben die Register auch andere Namen
  -Das Programm wurde für die Verwendung mit 8MHZ geschrieben. Für die
   Verwendung bei anderen Frequenzen müssen in der Interrup Routine
   SIGNAL(SIG_OVERFLOW0) einige Werten angepasst werden
  -Bei der Verwendung von mehr oder weniger als zwei Servos muss die letzte
   Delay Schleife ebenfalls angepasst werden.
  -Die verwendeten Pins für die Servos müssen auch in der Routine angepasst
   werden

Funktionsprinziep:
    Servos benötigen Impulse mit einem High Pegel zwischen 1-2ms. Danach kann
    ein mehrere Millisekunden langer Low Pegel folgen.
    Für jeden Servo wird 1 Byte Ram benötigt. Der Servo ist in 255 Schritten
    positionierbar.
 I :Beim erstem Aufruf des TIMER0 Interrups werden alle Ausgänge der Servos auf
    High gesetzt. Der Interrup konfiguriert sich so, dass der nächste Aufruf in
    einer Millisekunde erfolgt.
 II:Beim zweitem Aufruf wird eine Schleife mit 255 Durchläufen ausgeführt.
    Jeder Durchlauf soll nach Möglichkeit 3,9µs (1ms/255) dauern.
    Je nach Wert der zu dem Servo gehörender Variable wird der Pin des Servos
    früher oder später ausgeschalten.
    Hat die Vabiable den Wert 255, wird der Pin sofort dekativiert,
     der Servo hat für 1ms einen High Impuls erhalten.
    Hat die Variable den Wert 1, wird der Pin erst beim letztem Durchlauf
     abgeschaltet und der Pin war 2ms lang High
    Ist die Variable = 0, so bleibt der Pin auf High. Der Pin wechselt also
     nicht mehr zwischen 0 und 1 hin und her. Meine Servos deaktivieren sich
     daraufhin, der Servo versucht nicht seine Position zu halten, der Motor des
     Servos ist immer ohne Strom, egal welche Position der Servo hat.
    Nach dem letztem Durchlauf der Schleife konfiguriert sich der Timer0 so,
    dass er in 20ms wieder aufgerufen wird und beginnt dann von vorn mit
    Schritt I.

    Das hiesige Beispielprogramm steuert die Servos basierend auf den
    empfangenem Byte des UARTs. Die unteren 4 Bit des Bytes steuern den
    einen Servo, die oberen 4 Bit den anderen Servo. So sind zwar blos 15
    Positionen (+ Deaktivieren) für jeden Servo möglich. Als Test ist dies
    jedoch ausreichend.
*/

//Konstanten
#define frequency 8000000 // 8 Mhz, wird für den UART benötigt
#define baudrate 9600     // 9600 Baud
#define uartnumber (frequency/(baudrate*16l)-1)

//Externe Funktionen
#include <io.h>
#include <inttypes.h>
#include <interrupt.h>
#include <sig-avr.h>
#include <avr/delay.h>

//Um zu wissen für was uint8_t u.s.w. steht siehe inttypes.h
typedef uint8_t  u08;
typedef int8_t   s08;
typedef uint16_t u16;
typedef int16_t  s16;
typedef uint32_t u32;
typedef int32_t  s32;

//Globale Variablen
u08 volatile servo1 = 128;   //Mittelstellung
u08 volatile servo2 = 128;

SIGNAL(SIG_OVERFLOW0) { //Ein und Ausschalten der Servo Pins
/* 12 Takte zum Sichern der Register */
u08 countdown;
if (TCCR0 == 5) {    //Beide Pins an, 1ms warten                    (3-4 Takte)
  /*Timer0 so setzen, dass es 1ms bis zum erneutem Auslösen des Interrupt dauert
  8M *1000m = 8000; 1ms sind bei 8MHZ 8000 Takte
  8000 / 64 = 125; 8000 Takte durch Prescaler = 64 bedeutet der Timer muss 125
    mal weiter zählen, damit 1ms um ist
  256 -125 = 131; Bei 256 läuft der Timer über und er muss 125 mal weiter zählen
    bis das passiert, also muss er bei 131 anfangen zu zählen.
  Um den Bewegungsbereich etwas zu verbessern senken wir die Zeit um
    20*64 Takte (131+20= 151)
  */
  TCNT0 = 151;       //                                                (2 Takte)
  TCCR0  = 3;        //Prescaler = 64                                  (2 Takte)
  sbi(PORTD,2);      //Pin von Servo 1 an                              (1 Takt )
  sbi(PORTD,3);      //Pin von Servo 2 an                              (1 Takt )
}                    //                                  (Sprungbefehl: 2 Takte)
else  { //Nach definierter Zeit aus, 1ms Schleife (jeder Durchgang 32 Takte)
  for (countdown = 255;countdown != 0;countdown--) {
   /*1 Takt countdown initialisieren
   1 Takt runterzählen + 2 Takte springen (wird erst am Ende der Schleife
    ausgeführt)
   */
    if (servo1 == countdown) {  //                                   (4-5 Takte)
      cbi(PORTD,2);             //Servo 1 Pin aus                      (1 Takt )
    }                           //                                   (4-5 Takte)
    if (servo2 == countdown) {  //                                     (1 Takt )
      cbi(PORTD,3);             //Servo 2 Pin aus
    }
    /*Die Schleife braucht 5*2+3= 13 Takte für einen Durchlauf
    32 Take - 13 Takte = 19 Takte (maximal wären 3 weitere Servos möglich)
    Um den Auschlangereich zu erhöhen können wir den Servos noch etwas mehr Zeit
      geben: 19/3 -> 6; 30/3 -> 10; 256*10 Takte mehr Zeit
    Die zusätzliche Zeit kann auch für weitere Servos benutzt werden
      (jetzt wären ohne Warteschleife insgesammt 8 Servos möglich)
      
    */
    _delay_loop_1(30/3);
  }
  // Wartet 20 ms bis zum erneutem Auslösen des Interrupt
  TCNT0 = 100;                                       //                (2 Takte)
  TCCR0 = 5;                                         //                (2 Takte)
}
/* 12 Takte zum Zurückschreiben der Register */
}


int main( void ){
u08 stemp1,stemp2,utemp;

DDRD = 0x0c;//Servo Ausgänge
TIMSK = 2;//Interrupt für Überlauf aktivieren, (90S4433: Bit 2; Mega8: Bit 1)
TCNT0 = 0;//Timer0 resetten
TCCR0 = 5;//Prescaler einstellen
UBRR = uartnumber; //Geschwindigkeit des UARTS
UCSRB = 0x18;      //Transmitter und Reciver enabled
sei();    //Global Interrupts aktivieren
for (;;) {
utemp = UDR;
stemp1 = (utemp&0x0f)<<4;
stemp2 = (utemp&0xf0);
if ((utemp > 0) && (utemp < 255)) {
  servo1 = stemp1;
  servo2 = stemp2;
}
}
}

