/*************************************************************
** Attiny Steckfeld
* Zwei Taster T1 und T2 mit Ausgang
* T2  gedrückt + T1 : tx +1, Anzeige über gelbe LEDs
* tx = 0 Frequenz Einstellung (Ausgang PA6) 100kHz - 2Hz
* Tx = 1 PWM Einstellung linear
* Tx = 2 PWM Einstellung logarithmisch
* TX = 3 Frequenz Einstellung (Ausgang PA6) 2-0,12Hz
* 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   100msec 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:  ATtiny44
* 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 in 
*                                       |                |
* T2                (PCINT9/XTAL2) PB1 -|3             12|- PA1 (ADC1/AIN0/PCINT1)         LED Gelb
*                                       |                |
*               (PCINT11/RESET/dW) PB3 -|4             11|- PA2 (ADC2/AIN1/PCINT2)         LED Gelb
*                                       |                |
* T1 Out (PCINT10/INT0/OC0A/CKOUT) PB2 -|5             10|- PA3 (ADC3/T0/PCINT3)           any out
*                                       |                |
* T2 Out    (PCINT7/ICP/OC0B/ADC7) PA7 -|6              9|- PA4 (ADC4/USCK/SCL/T1/PCINT4)  any in
*                                       |                |
* f (PCINT6/OC1A/SDA/MOSI/DI/ADC6) PA6 -|7              8|- PA5 (ADC5/DO/MISO/OC1B/PCINT5) Jumper
*                                       +----------------+
* -------------------------------------------------------------------------------------------
* AVR GCC Compiler
*
* -------------------------------------------------------------------------------------------
* 23.02.2010 li Start
* 25.02.2010 li Timer gehen wenn "clock vorteiler 8" ausgeschaltet wird
*               dann rennt die kiste mit 8 Mhz
* 02.07.2010 li ADC0 Poti Input
* 04.07.2010 li fertig
* 03.03.2011 li 2 Jumper an Pin8 und Pin9 dazu für Sonderfälle
*               wenn Jumper1 auf Masse, dann analog Servo Signal erzeugen
* 06.03.2011 li
* 16.03.2011 li any in und any out dazu 
*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <math.h>
#include "../abc/basis.h"

unsigned char ll=0;
int adc_old = 0;
unsigned char vt;           // 1 = 1, 2=8, 3=64, 4=256, 5=1024
unsigned int top, comp;

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


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

unsigned char al_bit[2] = { BIT0, BIT1 };
unsigned char t1 = 0;                  // taster 1,  gedrückt = 1
unsigned char t2 = 0;                  // taster 2,  gedrückt = 1
unsigned char tx = 0;                  // tx status

unsigned char t1merker = 13;

static void timer0_init(void);
static int ADC_Messung( void );
static void timer1_init( int, unsigned char );
static unsigned char test_switch_B( unsigned char i );
void schalter_abfragen( void );
void PWM_justieren( void );
void ASS_justieren( void );

/*###########################################################################
 * Main
 */
int main(void)
{
   unsigned char jumper = 0;           // jumper1, Bit0 = 1

   timer0_init();                      // 10 msek timer

   DDRB  |= BIT2;                      // Output BIT2
   PORTB &= ~BIT2;                     // Output T1 auf 0
   PORTB |= BIT0 + BIT1;               // Pullup fuer T1 Und T2

   DDRA  |= BIT1 + BIT2 + BIT3 + BIT6 + BIT7; // Output 
   PORTA &= ~(BIT1 + BIT2 + BIT7 + BIT5);     // reset gelbe LED + t2
   PORTA |= (tx << 1);

   DDRA  &= ~(BIT5+BIT4);              // input Jumper sowie any input
   PORTA |= BIT5+BIT4;                 // Pullup fuer jumper sowie any input

   ADCSRA = 0;                         //  AC/ADC  Init
   ADCSRB = 0;
   DIDR0  |= BIT0;                     // digital Input disabled für ADC Pin0
     
   sei();                              // enable int

   // ++++++++++++++++++++++++++++++++ Main LOOP ++++++++++++++++++++++++++++
   // ++++++++++++++++++++++++++++++++ Main LOOP ++++++++++++++++++++++++++++
   // ++++++++++++++++++++++++++++++++ Main LOOP ++++++++++++++++++++++++++++
   for(;;)
   {
      if ( tim0_event_flag )           // 10 msek
      {
         tim200cnt += tim0_event_flag;
         tim0_event_flag = 0;

         if ( tim200cnt >= 100 )       // 1 sek ??
            tim200cnt = 0;

         if ( PINA & BIT5 )            // jumper offen ?
         {
            jumper = 0;
         }
         else
         {
            jumper = 1;                // Jumper auf 0
         }
         
         if ( PINA & BIT4 )            // any input pin
         {
            PORTA |= BIT3;             // set any output
         }
         else
         {
            PORTA &= ~BIT3;            // reset any output 
         }

         schalter_abfragen();

         if ( jumper )
         {
            ASS_justieren();           // Analog Servo Signal
         }
         else
         {
            PWM_justieren();           // bisherige Funktion
         }
      }
   }
   return 0;                           // dummy
}



/*****************************************************************************
** Analog servo Signal
** bei 33,3msek cycle Time ( 30 Hz )
** tx = 0 = normale 90° Ansteuerung
**
** Trim Möglichkeiten ausloten
** tx = 1  1100 = Stellbereich, von 950 bis 2050
** tx = 2  1200 = Stellbereich, von 900 bis 2100
** tx = 3  1300 = Stellbereich, von 850 bis 2150
*/
void ASS_justieren( void )
{
   float fpwm;
   int diff;
   int adc_val = 0;

   adc_val = 1024 - ADC_Messung();  // poti abfragen (drehrichtung umkehren)
   diff = adc_old - adc_val;

   if ( t1merker != 4 )
         timer1_init( 30, 4 );   // 30 Hz Signal ( 33,3msek Cycle )

   if ( diff > 1 || diff < -1 )
   {
      adc_old = adc_val;         // neue stellung merken

      if ( t1merker != 4 )
         timer1_init( 30, 4 );   // 30 Hz Signal ( 33,3msek Cycle )

      switch( tx )
      {
        case 0:                     // Vorteiler = 8 = 1 tick = 1µsek
         fpwm = 1000.0 / 1024.0;    // 1000 = Stellbereich, von 1000 bis 2000
         fpwm *= adc_old;           // current value
         comp = fpwm;
         OCR1A = comp + 1000;
         break;

        case 1:                     // Vorteiler = 8 = 1 tick = 1µsek
         fpwm = 1100.0 / 1024.0;    // 1100 = Stellbereich, von 950 bis 2050
         fpwm *= adc_old;           // current value
         comp = fpwm;
         OCR1A = comp + 950;
         break;

        case 2:                     // Vorteiler = 8 = 1 tick = 1µsek
         fpwm = 1200.0 / 1024.0;    // 1200 = Stellbereich, von 900 bis 2100
         fpwm *= adc_old;           // current value
         comp = fpwm;
         OCR1A = comp + 900;
         break;

        case 3:                     // Vorteiler = 8 = 1 tick = 1µsek
         fpwm = 1300.0 / 1024.0;    // 1300 = Stellbereich, von 850 bis 2150
         fpwm *= adc_old;           // current value
         comp = fpwm;
         OCR1A = comp + 850;
         break;
      }
   }
}



/*****************************************************************************
** PWM todo ( wenn kein Jumper gesetzt )
*/
void PWM_justieren( void )
{
   float fpwm;
   int diff, adc_val = 0;

   adc_val = ADC_Messung();          // poti abfragen
   diff = adc_old - adc_val;

   if ( t1merker == 4 )
      timer1_init( adc_old, 0 );

   if ( diff > 1 || diff < -1 )
   {
      adc_old = adc_val;   // neue stellung merken
      switch( tx )
      {
        case 0:            // Frequenz justieren
        case 3:
         timer1_init( adc_old, tx );
         break;

        case 1:             // PWM linear         
         fpwm = top;
         fpwm /= 1024;
         fpwm *= 1024 - adc_old;
         comp = fpwm;
         if ( comp == 0 )
            comp = 1;
         if ( comp >= top )
            comp = top-1;
         OCR1A = comp;
         break;

        case 2:             // PWM logarithmisch
         fpwm = log(top);
         fpwm /= 1024;
         fpwm *= 1024 - adc_old;
         comp = exp( fpwm );
         if ( comp == 0 )
            comp = 1;
         if ( comp >= top )
            comp = top-1;
         OCR1A = comp;
         break;

        default:
         break;
      }
   }
}



/*****************************************************************************
** Schalter todo
*/
void schalter_abfragen( void )
{
   unsigned char ts;

   if ( (ts = test_switch_B(0)) )  // ++ T1 +++
   {
      if ( ts == 1 )
      {
         PORTB |= BIT2;             // Output auf 1
         t1 = 1;
         if ( t2 == 1 )
         {
            tx++;
            tx &= 0x03;             // 0-3 zulassen
            PORTA &= ~(BIT1+BIT2);  // reset gelbe LED
            PORTA |= (tx << 1) & (BIT1 + BIT2);
         }
      }
      else if ( ts == 2)
      {
         t1 = 0;
         PORTB &= ~BIT2;            // Output auf 0
      }
   }

   if ( (ts = test_switch_B(1)))   // ++ T2 +++
   {
      if ( ts == 1 )
      {
         PORTA |= BIT7;             // Output auf 1
         t2 = 1;
      }
      else if ( ts == 2)
      {
         t2 = 0;
         PORTA &= ~BIT7;            // Output auf 0
      }
   }
}



/*****************************************************************************
** 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 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  = (1<<MUX2) | (1 << MUX1) | (1 << MUX0);     // ADC7 Pin
   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 < 1; i++ )
   {
      ADCSRA |= (1<<ADSC);       // start conversion
      while(ADCSRA & (1<<ADSC)); // warten bis fertig
      result += ADCW;            // ergebnis auslesen
   }
   ADCSRA = 0;                   // disable ADC

   return ( result/i );
}



/*****************************************************************************
**  Timer1 generate PWM Signal
**  Clock intern auf 8 MHz
**
**  maximale Frequenz = 100000 kHz
**  minimale Frequenz = 2 Hz
**  Input: 10 Bit Poti wert, mode
**
*/
static void timer1_init( int f, unsigned char txc )
{
   float ftarget;                     // Zielfrequenz
   float ln;                          // ln Zielfrequenz
   float fteiler;                     // Teiler
   float f1;
   float f2;

   t1merker = txc ;

   switch ( txc )
   {
     case 4:                          // Analog Servo Signal erzeugen
      vt = 2;                         // vorteiler auf 8 = je Takt eine µsek
      fteiler = 33333.0 ;             // cycle time 33 msek bzw. 30Hz
      break;

     case 0:
      f1 = log(2);                    // Frequenz Bereich 2 Hz
      f2 = (log(100000)-f1) / 1023;   // bis 100 kHz in 1023 log. Schritten

      ln = (f2 * (1023-f)) + f1;      // Ziel als ln
      ftarget = exp(ln);              // Zielfrequenz berechnen
      fteiler = 8000000.0 / ftarget;
      if ( fteiler > 65535.0 )
      {
         vt = 3;                      // Vorteiler auf 64
         fteiler /= 64.0;
      }
      else
      {
         vt = 1;
      }
      break;

     default:                         // txc = 3
      f1 = log(0.12);                 // Frequenz Bereich 0,12 Hz
      f2 = (log(2)-f1) / 1023;        // bis  2 Hz in 1023 log. Schritten

      ln = (f2 * (1023-f)) + f1;      // Ziel als ln
      ftarget = exp(ln);              // Zielfrequenz berechnen
      fteiler = (8000000.0 / 256.0 ) / ftarget;
      if ( fteiler > 65535.0 )
      {
         vt = 5;                      // Vorteiler auf 1024
         fteiler /= 4.0;
      }
      else
      {
         vt = 4;                      // Vorteiler auf 1024
      }
      break;
   }

   top = fteiler;                     // timer 1 count bis top

   if ( txc == 4 )
      comp = 1500;                    // server Mitte
   else
      comp = top / 2;                 // Fehlwert 50 % PWM

   TCCR1B = (1<<WGM13) + (1<<WGM12);   // Mode 14 setzen
   TCCR1B &= ~0x07;                    // clear vorteiler
   TCCR1B |= vt;                       // vorteiler setzen
   TCCR1A |= (1<<COM1A1) + (1<<WGM11); // 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
}
