/*************************************************************
* Attiny25 Dreimpulsgeber Auswertung
* 29.6.2013 V3
*
* Es werden die beiden impulssignale ausgewertet und bei Drehung ein
* Impuls erzeugt, je nach Drehrichtung an einem anderen Ausgang.
*
*
* 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   main.c     500 µsec Timer
* TIM1_COMPB  Timer/Counter1 Compare Match B
* TIM1_OVF    Timer/Counter1 Overflow
* TIM0_COMPA  Timer/Counter0 Compare Match A
* 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:  ATtiny25
* Frequenz:   8,000 MHz (interner Clock)
*
* -------------------------------------------------------------------------------------------
* Pin Belegung Attiny25
*                                            +----------------+
* Reset ISP      (PCINT5/RESET/ADC0/dW) PB5 -|1     XXX      8|- VCC
*                                            |                |
* OUT1    (PCINT3/XTAL1/CLKI/OC1B/ADC3) PB3 -|2              7|- PB2 (SCK/USCK/SCL/ADC1/T0/INT0/PCINT2)   INPUT1
*                                            |                |
* OUT2    (PCINT4/XTAL2/CLKO/OC1B/ADC2) PB4 -|3              6|- PB1 (MISO/DO/AIN1/OC0B/OC1A/PCINT1)      INPUT2
*                                            |                |
*                                       GND -|4              5|- PB0 (MOSI/DI/SDA/AIN0/OC0A/OC1A/AREF/PCINT0)
*                                            +----------------+
*--------------------------------------------------------------------------------------------
* FUSE: Attiny25    FF DF E2
*       mit CLKOUT  FF DF A2

* -------------------------------------------------------------------------------------------
* AVR GCC Compiler
*
* 26.06.2013 li V2
* 28.06.2013 li Bemerkungen verbessert
* 29.06.2013 li V3, bits durch unsigned char ersetzt und die varablen in der main definiert
*               und test_switch_B mit 2 parametern ergibt nur 446 Bytes Code gegen V2=502 Bytes  
* -------------------------------------------------------------------------------------------
*/

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

unsigned char volatile t1_event = 0;        // 500 µsec Trigger

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

unsigned char count1 =0;
unsigned char count2 =0;

#define SWBOTH  (swr_low+swl_low) // beide signale addieren 

#define DIG_IMP_DDR   DDRB        // output DDR
#define DIG_IMP_PORT  PORTB       // ouput Port
#define DIG_IMP_RIGHT BIT3        // ouput Bit right Impuls
#define DIG_IMP_LEFT  BIT4        // ouput Bit left Impuls

static void timer1_init(void);                      // timer 2000 Hz
static unsigned char test_switch_B( unsigned char i, unsigned char bit );

// #define IMPULS_CNT 64          // 38 = 38 msek
// #define IMPULS_CNT 87          // 87 = 52 msek
#define IMPULS_CNT 101            // 101 = 60 msek

/*###########################################################################
** Main
** Der Drehimpulsgeber hat pro Umdrehung 24 Einrastungen mit je 24 Impulsen   
** Die beiden Drehkontakte sind versetzt angeordnet um die Drehrichtung zu erkennen 
** In der Einrastung sind beide Drehkontakte offen, über R auf high 
** Wenn Kontakt geschlossen dann ist er Masse verbunden = low.
**
** -----+      +---------R-----+      +--------   
**      +------+         R     +------+           
**                       R
** ---------+      +-----R--------+      +-----
**          +------+     R        +------+  
**                       Rast Position  
*/
int main(void)
{
   unsigned char sr;
                                       // Impulsgeber Drehimpulse
   unsigned char swr_first = 0;        // first low impuls turn right
   unsigned char swl_first = 0;        // first low impuls turn left

   unsigned char swr_low = 0;          // low impuls turn right
   unsigned char swl_low = 0;          // low impuls turn left
   
   timer1_init();                      // 0,5 msek timer
   DDRB  |= BIT3+BIT4;
   PORTB |= BIT0+BIT1+BIT2;            // Input mit internem Pullup Widerstand
   PORTB |= BIT3+BIT4;                 // Output auf 1, da negative Impulse gefordert

   sei();                              // enable int


   // ++++++++++++++++++++++++++++++++ Main LOOP ++++++++++++++++++++++++++++
   // ++++++++++++++++++++++++++++++++ Main LOOP ++++++++++++++++++++++++++++
   // ++++++++++++++++++++++++++++++++ Main LOOP ++++++++++++++++++++++++++++
	for(;;)
	{
	   if ( t1_event )                 // 0,5 msek vorbei ?
	   {
         t1_event = 0;                // reset event flag

                        // ************** Impuls shut down ****************
         if ( count1 )                        // on ?
         {
            count1--;
            if ( count1 == 0 )                // end ?
               DIG_IMP_PORT |= DIG_IMP_RIGHT; // output = 1 = off
         }

         if ( count2 )                        // on ?
         {
            count2--;
            if ( count2 == 0 )                // end ?
               DIG_IMP_PORT |= DIG_IMP_LEFT;  // output = 1 = off
         }

         if ( (sr = test_switch_B( 0, BIT2 ))) // Input Test
         {
            if ( sr == 2 )               // low wenn laenger als 5 msek
            {
               if ( SWBOTH == 0 )
               {
                  swr_first = 1;
                  swl_first = 0;
               }   
               swr_low = 1;
            }
            else if ( sr == 1 )          // 1 wenn laenger als 5 msek
            {
               swr_low = 0;
               if ( swl_first )
               {
                  swl_first = 0;
                  if ( count2 == 0 )
                  {
                     count2 = IMPULS_CNT;
                     DIG_IMP_PORT &= ~DIG_IMP_LEFT;    // Impuls on
                  }
               }
            }
         }

         if ( (sr = test_switch_B( 1, BIT1 ))) // Input Test
         {
            if ( sr == 2 )               // low wenn laenger als 5 msek
            {
               if ( SWBOTH == 0 )
               {
                  swl_first = 1;
                  swr_first = 0;
               }
               swl_low = 1;
            }
            else if ( sr == 1 )          // 1 wenn laenger als 5 msek
            {
               swl_low = 0;
               if ( swr_first )
               {
                  swr_first = 0;
                  if ( count1 == 0 )
                  {
                     count1 = IMPULS_CNT;
                     DIG_IMP_PORT &= ~DIG_IMP_RIGHT;    // Impuls on
                  }
               }
            }
         }
	   }
   }
   return 0;           // dummy
}



/*****************************************************************************
** Abfrage PORTB
** Filtern von Prellungen ( 10 mal )
** Aufruf alle 500 µsek
*/
static unsigned char test_switch_B( unsigned char i, unsigned char bit )
{ 
   if ( PINB & bit )                // Signal = 1 ??
   {
      if ( PINB & bit && al_cc[i] < 128 )
      {
         if ( al_cc[i]++ > 10 )     // > 5 msek ?
         {
            al_cc[i] = 128;         // pressed
            return 1;
         }
      }
   }
   else                             // Signal = 0
   {
      if ( al_cc[i] >= 128 )
      {
         if ( !(PINB & bit) )
         {
            if ( al_cc[i]++ > 138 ) // > 5 msek ?
            {
               al_cc[i] = 0;        // no pressed
               return 2;
            }
         }
      }
      else
         al_cc[i] = 0;             // no pressed
   }
   return 0;
}



/*****************************************************************************
**  set Timer1  compare A, Ziel = 500µsek Interrupt = 4000 Takte
*/
static void timer1_init( void )
{
   TCCR1 |= (1<<CTC1) ;                         // CTC mode
   GTCCR |= (1<<PSR1) ;                         // Reset Timer
   TCCR1 |= (1<<CS10) + (1<<CS12);              // prescaler = 16
   OCR1A  = 249;                                // compare match bei 250-1
   TCNT1 = 0;
   t1_event = 0;
   TIMSK |= (1<<OCIE1A);
}



/*****************************************************************************
*  8 Bit TIMER0 Compare Match
*  alle ca. 0,5 msek
*/
ISR ( TIM1_COMPA_vect )
{
   t1_event=1;         // set flag
}
