/*************************************************************
** Attiny Motor
* Interupt Belegung                             wo              warum
* -------------------------------------------------------------------------------------------
* INT0        External Interrupt Request 0
* PCINT0      Pin Change Interrupt Request 0
* PCINT1      Pin Change Interrupt Request 1
* WDT         Watchdog Time-out
* TIM1_CAPT   Timer/Counter1 Capture Event
* TIM1_COMPA  Timer/Counter1 Compare Match A
* TIM1_COMPB  Timer/Counter1 Compare Match B
* TIM1_OVF    Timer/Counter1 Overflow
* TIM0_COMPA  Timer/Counter0 Compare Match A    main.c   10msec Timer
* TIM0_COMPB  Timer/Counter0 Compare Match B
* TIM0_OVF    Timer/Counter0 Overflow
* ANA_COMP    Analog Comparator
* ADC ADC     Conversion Complete
* EE_RDY      EEPROM Ready
* USI_STR     USI START
* USI_OVF     USI Overflow
*
* Prozessor:  ATtiny84
* Frequenz:   8,000 MHz (interner Clock)
*
* -------------------------------------------------------------------------------------------
* Pin Belegung
*                                       +----------------+
*                                  VCC -|1     XXX     14|- GND
*                                       |                |
* T1           (PCINT8/XTAL1/CLKI) PB0 -|2             13|- PA0 (ADC0/AREF/PCINT0)         Poti analog in 
*                                       |                |
* T2                (PCINT9/XTAL2) PB1 -|3             12|- PA1 (ADC1/AIN0/PCINT1)         LCD_LED
*                                       |                |
* reset         (PCINT11/RESET/dW) PB3 -|4             11|- PA2 (ADC2/AIN1/PCINT2)         LCD_Clock
*                                       |                |
*        (PCINT10/INT0/OC0A/CKOUT) PB2 -|5             10|- PA3 (ADC3/T0/PCINT3)           LCD_Enable
*                                       |                |
*           (PCINT7/ICP/OC0B/ADC7) PA7 -|6              9|- PA4 (ADC4/USCK/SCL/T1/PCINT4)  LCD_Data
*                                       |                |
* f (PCINT6/OC1A/SDA/MOSI/DI/ADC6) PA6 -|7              8|- PA5 (ADC5/DO/MISO/OC1B/PCINT5) 
*                                       +----------------+
* -------------------------------------------------------------------------------------------
* AVR GCC Compiler
*
* -------------------------------------------------------------------------------------------
* 07.01.2013 Start 
* 12.01.2013 li V4: wenn keine menu, dann s2 gleichzeitig ein/aus. default pwm Mode ein 
*/

#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <math.h>
#include "config.h" 
#include "../abc/basis.h"
#include "../abc/lcd.h"

int adc_old = 0;
unsigned int top, comp;
unsigned int ftarget = 0;           // Zielfrequenz
int adc_freq = 0;                   // adc_wert Zielfrequenz

unsigned char volatile tim0_event_flag=0;  // 10 msec Trigger
unsigned char volatile tim200cnt = 0;      // 200 msek

unsigned char display_flag = 0;
  // ----------------------------------------------
  // INDEX test_switch_x()    0     1
  // ----------------------------------------------
unsigned char al_cc[2]  = {    0,    0 };
unsigned char al_bit[2] = { BIT0, BIT1 };

char zeile11[ 21 ] = "                    " ;   // utility
char zeile12[ 21 ] = "PWM Motor Control V4" ;

unsigned char backlight = 1;          // Hintergrundbeleuchtung LCD
unsigned char frequenz  = 0;          // Frequenz Einstellung
unsigned char first     = 3;          // first, BIT0 = frequ, Bit1 = pwm
unsigned char pwmmode = 1;            // 12.1.2013   
unsigned char save_all  =0;
unsigned char power = 0;              // output PWM

                            //012345678    menu 
static char m_t1[] PROGMEM = "retur V04"   // 0
                             "Backlight"   // 1
                             "PWM Mode "   // 2 
                             "Frequenz "   // 3
                             "Save all "   // 4 
                             "Output   "   // 5                                   
                             ;
                             
                             //012      sub menu diverses
static char sm_t1[] PROGMEM = "ret"     // 0
                              "aus"     // 1
                              "ein";    // 2
                              
                             //01234    sub menu save
static char sm_t4[] PROGMEM = "ret  "     // 0
                              "aus  "     // 1
                              "ein  "     // 2
                              "saved";    // 3                              
                    
                        // Länge                             
#define LEN_M_T1   9
#define LEN_SM_T1  3  
#define LEN_SM_T4  5  

                        // Anzahl der Eintraege -1
#define ENT_M_T1   5    // basis menu
#define ENT_SM_T1  2   
#define ENT_SM_T4  3           

                        // Index auf den Subtext
#define INDEX_SM_T1  1
#define INDEX_SM_T4  2


volatile unsigned char menu_aktiv = 0;    // menu aktiv
unsigned char mtime = 0;          // menu nach 10 Sekunden weg
unsigned char menu1 = 0;          // mm3 sicherung text1
unsigned char mm3 = 0;            // s3: index auf texte
unsigned char mm2 = 0;            // s2: press counter

unsigned char mm3max[2];          // s3: max index text1
unsigned char mmxlen[2];
unsigned char mmxs1 = 1;          // index auf submenu texte

unsigned int  lcd_timer = 1;      // lcd Timer

// EEPROM Speicher ---------------------------------------------------------
//                           32 * 16Byte = 32 fache Verlängerung der Lebensdauer

uint8_t eeByte[512] EEMEM =  {'\0',                             //  0:       Write Kennung
                              '0', '0', '0', '0', '0',          //  1: 0     Frequenz
                              '0', '0', '0', '0',               //  6: 5     adc_wert
                              '0'                               // 10: 9     prüfzeichen 
                             }; 
// -------------------------------------------------------------------------
char eep[16] ;               // eeprom transfer area
unsigned char eepError = 0;                 

static void timer0_init(void);
static int ADC_Messung( void );
static void timer1_init( int );
static unsigned char test_switch_B( unsigned char i );
void PWM_justieren( void );
void Frequenz_justieren( unsigned char parm ); 
void set_PWM( void );
void edit_display( void);
void read_eeprom( void );
void save_eeprom( void );
int search_pos( unsigned char *pken, unsigned char write );

void s2_todo(void);
void s3_todo(void);
void clear_zeile( char *);
void display_menu(unsigned char m1);
void move2( char l, char pos, char index, unsigned char quelle );
void move1( char l, char index  );
void menu_sw2( void );
void save_eeprom( void );

/*###########################################################################
 * Main
 */
int main(void)
{       
   timer0_init();                      // 10 msek timer

   PORTB |= (BIT0+BIT1+BIT2);          // Pullup fuer T1 Und T2 und offener eingang
   DDRA  |= BIT1 + BIT2 + BIT3 + BIT4 ; // Output 
   PORTA |= BIT5+BIT6;                 // Pullup

   ADCSRA = 0;                         //  AC/ADC  Init
   ADCSRB = 0;
   DIDR0  |= BIT0;                     // digital Input disabled für ADC Pin0
      
   sei();                              // enable int
   
   lcd_init(0);
   lcd_clear();
   lcd_home();
   lcd_beleuchtung( backlight );      // Beleuchtung aus/ein
   edit_display();
   
   read_eeprom();                     // letzte alte Einstellung laden   
   if (  eepError == 1 )
   {
      ftarget = 128;
      adc_freq = 0;
      timer1_init( adc_freq );
   }
   else
   {
      timer1_init( adc_freq );
      Frequenz_justieren(0);          
   }   

   // ++++++++++++++++++++++++++++++++ Main LOOP ++++++++++++++++++++++++++++
   // ++++++++++++++++++++++++++++++++ Main LOOP ++++++++++++++++++++++++++++
   // ++++++++++++++++++++++++++++++++ Main LOOP ++++++++++++++++++++++++++++
   for(;;)
   {
      if ( tim0_event_flag )           // 10 msek event 
      {
         tim200cnt += tim0_event_flag;
         tim0_event_flag = 0;
         
         if ( test_switch_B(1) == 1 )  // ++++  S2 : SET oder EIN/AUS
            s2_todo();

         if ( test_switch_B(0) == 1 )  // ++++  S3 : menu
            s3_todo(); 

         if ( frequenz )
            Frequenz_justieren(1);        
         
         if ( pwmmode )
            PWM_justieren();           // bisherige Funktion
            
         if ( save_all  == 1 )
         {
            save_eeprom();
            save_all  = 2;
         }         
            
         if ( tim200cnt >= 100 )       // 1 sek vorbei ?? *******************
         {
            tim200cnt = 0;      
            
            if ( menu_aktiv )          // reset menu nach 10Sek
            {
               if ( mtime++ > 10 )
               {
                  menu_aktiv = 0;
                  mtime = 0;
                  mm3 = 0;
                  mm2 = 0;
                  edit_display();      // Messung anzeigen
               }
            }

            if ( lcd_timer )
            {
               if ( lcd_timer++ > 3600  )        // 1 Stunde vorbei ?
               {
                  lcd_timer = 0;
                  lcd_beleuchtung( backlight );  // Beleuchtung aus/ein
               }
            }
         }                             // ************************************  
         
         if ( display_flag )
         {
            edit_display();
         }            
      }
   }
   return 0;                           // dummy
}



/*****************************************************************************
** PWM todo
*/
void PWM_justieren( void )
{
   int diff, adc_val = 0;

   adc_val = ADC_Messung();          // poti abfragen
   diff = adc_old - adc_val;
   if ( first & BIT1)                // first mode zuerst diff auf kleiner 3 zulassen
   {   
      if ( diff > 3 || diff < -3)
         return;
      else
         first &= ~BIT1;      
   }

   if ( diff > 1 || diff < -1 )
   {
      adc_old = adc_val;            // neue stellung merken   
      set_PWM();
      display_flag = 1;
      first |= BIT0;                // frequ weich           
   }
}


/*****************************************************************************
** UP PWM todo
** 8.1.2013 V2 pwm invertieren 
*/
void set_PWM( void )
{
   long fpwm;
   int invers = 1024; 
   
   invers -= adc_old;
   fpwm = top; 
   fpwm *= invers;
   fpwm /= 1024;      
   comp = (unsigned int)fpwm;
   if ( comp == 0 )
      comp = 1;
   if ( comp >= top )
      comp = top-1;
   OCR1A = comp;
}



/*****************************************************************************
** Frequenz justieren
*/
void Frequenz_justieren( unsigned char read_adc )
{ 
   int diff, adc_val = 0;
   
   if ( read_adc )
   {
      adc_val = ADC_Messung();          // poti abfragen
      diff = adc_freq - adc_val;
      if ( first & BIT0)                // first mode zuerst diff auf kleiner 3 zulassen
      {   
         if ( diff > 3 || diff < -3)
            return;
         else
            first &= ~BIT0;      
      }
      
      if ( diff > 1 || diff < -1 )
      {      
         adc_freq = adc_val;            // neue stellung merken   
         timer1_init( adc_freq );
         _delay_ms(8);
         set_PWM();                     // auf alten PWM Wert setzen
         display_flag = 1;
         first |= BIT1;                 // pwm weich
      }
   }
   else
   {
      timer1_init( adc_freq );
      _delay_ms(8);
      set_PWM();                        // auf alten PWM Wert setzen
      display_flag = 1;
   }
}



/*****************************************************************************
** Aufbereitung der Anzeige
*/
void edit_display( void)
{
   long pp;
   
   display_flag = 0;
   set_cursor( 0, 1 );          // Hier in Zeile 1
   lcd_string( zeile12 );
   
   clear_zeile( zeile11 );
   set_cursor( 0, 2 );          // Hier in Zeile 2
   zeile11[0] = 'O';
   if ( power )
   {
      zeile11[1] = 'n';
   }
   else
   {
      zeile11[1] = 'f';
      zeile11[2] = 'f';
   }   
   zeile11[5] = 'T';
   zeile11[6] = ':';
   edit99( top, zeile11+7, 5 );
   zeile11[13] = '/';
   edit99( comp, zeile11+15, 5 );   
   lcd_string( zeile11 );
   
   clear_zeile( zeile11 );
   set_cursor( 0, 3 );          // Hier in Zeile 3
   zeile11[0] = 'F';
   zeile11[1] = 'r';
   zeile11[2] = 'e';
   zeile11[3] = 'q';
   zeile11[4] = 'u';
   zeile11[5] = ':';
   edit99( ftarget, zeile11+7, 5 );
   edit99( adc_freq, zeile11+15, 4 );
   lcd_string( zeile11 );
   
   clear_zeile( zeile11 );
   set_cursor( 0, 4 );          // Hier in Zeile 4
   zeile11[0] = 'P';
   zeile11[1] = 'W';
   zeile11[2] = 'M';
   zeile11[3] = ' ';
   zeile11[4] = '%';
   zeile11[5] = ':';
   pp = adc_old;
   pp *= 1000; 
   pp /= 1024; 
   edit99( pp, zeile11+7, 4 );
   zeile11[11] = zeile11[10];
   zeile11[10] = ',';
   edit99( adc_old, zeile11+15, 4 );
   zeile11[12] = '%';
   lcd_string( zeile11 );
}



/*****************************************************************************
** clear Zeile
*/
void clear_zeile( char *s )
{
   unsigned char i;
#if LCD_CHARS == 16
   for( i=0; i < 16 ; i++ )
#else
   for( i=0; i < 20 ; i++ )
#endif
   {
      *s = ' ' ;
      s++;
   }
   *s = '\0' ;
}



/*****************************************************************************
** S3 Menu
*/
void s3_todo( void )
{
   lcd_beleuchtung( 1 );         // 10.9.2009 Beleuchtung ein
   lcd_timer = 1;                // timer starten
   menu_aktiv = 1;
   mtime = 0;
   mm3++;

   if ( mm2 == 0 )               // kein s2 switch da
   {                             // default werte einstellen
      mmxlen[0] = LEN_M_T1;
      mm3max[0] = ENT_M_T1;
      mmxlen[1] = LEN_SM_T1;
      mm3max[1] = ENT_SM_T1;
      mmxs1 = INDEX_SM_T1;

   }
   if ( mm3 > mm3max[mm2] )
      mm3 = 0;

   if ( mm2 == 0 )
   {
      switch( mm3 )
      { 
        case 4:
         mmxlen[1] = LEN_SM_T4;
         mm3max[1] = ENT_SM_T4;
         mmxs1 = INDEX_SM_T4;
         break;
         
        default:
         mmxlen[1] = LEN_SM_T1;
         mm3max[1] = ENT_SM_T1;
         mmxs1 = INDEX_SM_T1;    //  sm_t1;
         break;
      }
      display_menu( mm3 );          // text1
   }
   else
      display_menu( menu1 );        // text 1 und text2
}



/*****************************************************************************
** S2 Menu
*/
void s2_todo( void )
{
   lcd_beleuchtung( 1 );         // 10.9.2009 Beleuchtung ein
   lcd_timer = 1;                // timer starten

   if ( menu_aktiv )             // s3 da gewesen  ??
   {
      mtime = 0;                 // restart mtime
      mm2++;
      if ( mm2 == 1 )            // erster text da
      {
         menu1 = mm3;            // save level 1
                                 // das was da ist anzeigen
         switch( menu1 )
         {
           case 0: mm3 = 0; menu_sw2(); break;    // DCF77 Menu break;
           case 1: mm3=backlight+1; break;        // licht
           case 2: mm3=pwmmode+1;  break;         // PWM Mode
           case 3: mm3=frequenz+1;  break;        // frequenz
           case 4: mm3=save_all+1;  break;        // save all 
           case 5: mm3=power+1;  break;           // power            
         }
          
         if ( mm3 )
         {
            display_menu( menu1 );   // text1 und text2
         }
      }
      else
      {
         mm2 = 1;                // mm2 auf 1 halten
         menu_sw2();             // Menu
      }
   }
   else                    // v4: s2 wenn kein menu, ein/aus
   {
      power ^= 1;          // toggle ein / aus 
      if ( power )
         DDRA  |= BIT6;      
      else
         DDRA &= ~BIT6;
      display_flag = 1;    // zustand anzeigen         
   }
}


/*****************************************************************************
** die text elemente sind so angeordnet, dass bei "ein" todo=1 ist, "aus" todo=0
**
** menu1 = 0  und mm3 = 0
*/
void menu_sw2( void )
{
   unsigned char rc=1;
   unsigned char todo;

   mm2 = 0;             // mm2 immer auf 0
   if ( menu1 )                      // menu 1
   {
      todo = mm3 - 1;
      if ( todo == 0xff )            // retur ausgewaehlt ?
      {
         display_menu( menu1 );
         rc=0;
      }
      else
      {
         switch ( menu1 )
         {
           case 0:
            display_flag = 1;
            break;
                       
           case 1:
            backlight = todo;
            lcd_beleuchtung( backlight );
            display_flag = 1;
            break;  // licht

           case 2:                  // pwm_mode;
            pwmmode  = todo;
            if ( pwmmode == 1 )
            {               
               frequenz = 0;
            }                
            display_flag = 1;  
                          
            break;                  

           case 3:
            frequenz = todo; 
            if ( frequenz == 1 )
               pwmmode = 0; 
            display_flag = 1;               
            break;                  

           case 4:
            if ( save_all  < 2 )
            {
               save_all  = todo;
               frequenz = 0;
               display_flag = 1;
            }               
            break;        // trace
            
           case 5:
            power = todo; 
            if ( power )
               DDRA  |= BIT6;
            else
               DDRA &= ~BIT6;            
            display_flag = 1;   
   
            break;        // trace
         }
      }
   }
   if ( rc )
   {
      mm3 = 0;          // mm3 wieder auf 0
      mtime = 0;
      menu_aktiv = 0;  
      display_flag = 1;      
   }
}



/*****************************************************************************
**  Menu Zeile erzeugen und anzeigen
*/
void display_menu( unsigned char m1 )
{
   clear_zeile(zeile11);

   move1( mmxlen[0], m1  );      // move text1

   if ( mm2 )
   {
      move2( mmxlen[1], mmxlen[0]+1, mm3, mmxs1 ); // move text2
   }
   set_cursor( 0, 1 );          // Hier in Zeile 1
   lcd_string( zeile11 );
}



/*****************************************************************************
**  Text nach zeile11 kopieren
*/
void move1( char l, char index  )
{
   unsigned char n,q;
   prog_char *addr;

   addr = (prog_char*)&m_t1;   // DCF77 Menu

   q = index * l;                 // quell Position
   addr += q;
   for( n = 0; n < l ; n++)
   {
      zeile11[n] = pgm_read_byte( addr++ );
   }
}



/*****************************************************************************
**  Text2 nach zeile11 kopieren
**  9.3.2010 pgmspace
*/
void move2( char l, char pos, char index, unsigned char quelle )
{
   char n,q;
   prog_char *addr;   
   
   switch ( quelle )
   {
     case INDEX_SM_T4: addr = (prog_char*)&sm_t4; break;
     default:  addr = (prog_char*)&sm_t1; break;
   }
   
   q = index * l;            // quell Position
   addr += q;
   for( n = 0; n < l ; n++)
   {
      zeile11[pos+n] = pgm_read_byte( addr++ );
   }
}



/*****************************************************************************
** Schalter Abfrage PORTB
** funktionieren nur wenn Aufruf alle 10 msek
*/
unsigned char test_switch_B( unsigned char i )
{
   unsigned char bit;

   bit = al_bit[i];           // Bit holen

   if ( !(PINB & bit) )       // pressed = 0 ??
   {
      if ( !(PINB & bit) && al_cc[i] < 128 )
      {
         if ( al_cc[i]++ > 3 )
         {
            al_cc[i] = 128;    // pressed
            return 1;
         }
      }
   }
   else
   {
      if ( al_cc[i] >= 128 )
      {
         if ( PINB & bit )
         {
            if ( al_cc[i]++ > 131 )
            {
               al_cc[i] = 0;  // no pressed
               return 2;
            }
         }
      }
   }
   return 0;
}



/*****************************************************************************
** read eeprom 
*/
void read_eeprom( void )
{
   int epos;
   unsigned char n, p2;
   unsigned char kennung;                     // not used da read

   epos = search_pos( &kennung, 0 );

   for( n=0; n < 14; n++)
   {
      eep[n] = eeprom_read_byte( &eeByte[ epos+n+1 ] );
   }
   p2  = berechne_PZ( eep, 13) + 0x30 ;
   if ( eep[13] == p2)          // pruefzahl OK ?
   {      
      ftarget = a_to_i( eep, 5);
      adc_freq = a_to_i( eep+5 ,4);
      adc_old = a_to_i( eep+9 ,4);
   }
   else
   {
      eepError++;
      if ( eepError > 200 )
         eepError = 200; 
   }
   
   clear_zeile(zeile11);
   for( n=0; n < 14; n++)
      zeile11[n] = eep[n];
      
   edit99( eepError,  zeile11+15, 3 ); 
   zeile11[19] = p2;   
   set_cursor( 0, 2 );          // Hier in Zeile 1
   lcd_string( zeile11 );         
}



/*****************************************************************************
** Save der wichtigen Parameter im internen eeprom
** durch die Verteilung der Daten auf das komplette eeprom
** wird eine längere Lebensdauer erreicht.
*/
void save_eeprom( void )
{
   unsigned char kennung, n;
   int epos;
   
   epos = search_pos( &kennung, 1 );   // write pos holen
   eeprom_write_byte( &eeByte[ epos ], kennung );

   edit99( ftarget,  eep, 5 );
   edit99( adc_freq, eep+5, 4 );
   edit99( adc_old,  eep+9, 4 );
   eep[13] = berechne_PZ( eep, 13) + 0x30 ;
   for ( n=0; n < 14; n++)
   {
      eeprom_write_byte( &eeByte[ epos+n+1 ], eep[n] );
   } 
   clear_zeile(zeile11);
   for( n=0; n < 14; n++)
      zeile11[n] = eep[n];
   zeile11[15] = 's';
   zeile11[16] = 'a';   
   zeile11[17] = 'v';   
   zeile11[18] = 'e';   
   zeile11[19] = 'd';         
   set_cursor( 0, 2 );          // Hier in Zeile 2
   lcd_string( zeile11 );            
}



/**************************************************************
* Suche eeprom position
* write=1 : Position zum schreiben
* write=0 : letzte write Position = read position
* 16 * 32  = 512 Bytes used
*/
int search_pos( unsigned char *pken, unsigned char write )
{
   int pos=0;
   unsigned char start0, curr, kennung=0x33;

#define EEP_LAENGE 16

   start0 = eeprom_read_byte( &eeByte[pos]);     // position 0 Kennung lesen

   if ( start0 == 0xFF )     // first ??
   {
      *pken = kennung;       // Übergabe 0x33
      return pos;
   }
   else
   {
      kennung = start0;      // return
   }
                             // search
   for ( pos=0; pos < 512; pos += EEP_LAENGE )
   {
      curr = eeprom_read_byte( &eeByte[pos]);
      if ( curr == 0xFF || curr != start0 )      // wechsel oder first
         break;
   }

   if ( write )              // wenn write dann neue Position
   {
      if ( pos == 512 )      // loop ende ?
      {
         kennung ^= 0x55;    // change kennung  0x33 / 0x66
         pos = 0;            // restart bei 0
      }
      *pken = kennung;       // Übergabe an aufrufer
   }
   else                      // == read
   {
      pos -= EEP_LAENGE;     // letzte Position
   }

   return pos;
}



/*****************************************************************************
** Read ADC0 Pin
** VRef = Vcc
*/
static int ADC_Messung(void)
{
   unsigned char i;
   unsigned int result = 0;
                                 // ADC Noise Reduction
   MCUCR  |= (1<<SM0);
                                 // first dummy reading, VRef=Vcc
   ADMUX  = 0;                   // ADC0 Pin
                                 // Teiler auf 64, enable adc
   ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);

   ADCSRA |=  (1<<ADSC );        // Start conversion
   while(ADCSRA & (1<<ADSC));    // dummy read = warten bis ADSC = 0

   for( i = 0; i < 3; i++ )      // 3 mal lesen  
   {
      ADCSRA |= (1<<ADSC);       // start conversion
      while(ADCSRA & (1<<ADSC)); // warten bis fertig
      result += ADCW;            // ergebnis auslesen
   }
   ADCSRA = 0;                   // disable ADC

   return ( result/i );          // ergebnis 
}



/*****************************************************************************
**  Timer1 generate PWM Signal
**  Clock intern auf 8 MHz
**
**  maximale Frequenz = 25*1023 + 128 = 25703 Hz
**  minimale Frequenz = 128 Hz

**  Input: 10 Bit Poti wert, mode
*/
static void timer1_init( int f )
{  
   float f1,f2;
   float ln;                           // ln Zielfrequenz
   long temp = 8076000;                // Interne frequenz 
 
   f1 = log(128);    
   f2 = ( log(25000)-f1) / 1023;       // bis 100 kHz in 1023 log. Schritten
   ln = (f2 * f) + f1;                 // Ziel als ln
   ftarget = (long)exp(ln);            // Zielfrequenz berechnen      
   temp /= ftarget;
   top = (unsigned int) temp;          // top value
   top -= 1;   
   comp = top / 2;                     // Fehlwert 50 % PWM

   TCCR1B = (1<<WGM13) + (1<<WGM12) + (1<<CS10);   // Mode 14 setzen und vorteiler
   TCCR1A |= (1<<WGM11) + (1<<COM1A1);             // OC1A setzen wenn compare match
   OCR1A = comp;
   ICR1 = top;
   TCNT1 = 0;
}



/*****************************************************************************
**  Timer0 compare match interupt setzen
**  Clock intern auf 8 MHz, Fuse Clock /8 gesetzt = 1 MHz
**
**  1 / 8000000  * 78 * 1024 = 0,009984 msec
*/
static void timer0_init( void )
{
   TCCR0A |= 0x02 ;          // clear timer on compare match = CTC Modus
   TCCR0B |= 0x05 ;          // prescaler = 1024 setzen
   OCR0A  = 77;              // compare match bei 77 + 1 = 78 Takte
   TCNT0 = 0;
   tim0_event_flag = 0;      // flag
   CLKPR = (1<<CLKPCE);      // Clock-division-faktor auf 1 setzen (default=8 -> 1MHz))
   CLKPR = 0;
   TIMSK0 = (1 << OCIE0A);   // enable compare match A interrupt
}



/*****************************************************************************
*  8 Bit TIMER0 Compare Match
*  alle ca. 10 msek*
*/
ISR ( TIM0_COMPA_vect )
{
   tim0_event_flag++;         // timer0 flag
}
