#define F_CPU 12000000
#define RED   OCR2
#define GREEN OCR1B
#define BLUE  OCR1A

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

typedef struct color_a {
   uint8_t red;
   uint8_t green;
   uint8_t blue;
};
static struct color_a color;

static const uint8_t tbl_f[60] PROGMEM = {
     0,   4,   8,  12,  16,  21,  25,  29,  33,  38,
    42,  46,  51,  55,  59,  63,  67,  72,  76,  80,  
    84,  89,  93,  97, 102, 106, 110, 114, 119, 123, 
   127, 131, 135, 140, 144, 148, 152, 157, 161, 165,
   169, 174, 178, 182, 186, 191, 195, 199, 204, 208, 
   212, 216, 221, 225, 229, 233, 238, 242, 246, 254
};


//////////////////////////////////////////////////////////////
// Standart method for hsv -> rgb would be:                 //
// input: H [0,360], S [0, 1], V [0,1]                      //
// h_i = floor( H / 60 )                                    //
// f = H/60 - h_i                                           //
// p = V * ( 1 - S )                                        //
// q = V * ( 1 - S * f )                                    //
// t = V * ( 1 - S * ( 1 - f )                              //
// red = ... if( h_i = 1).....                              //
//                                                          //
// To spare some memoy I only use full saturation(S=1)      //
// and full value(V=1).                                     //
// This reduces p,q and t to:                               //
// p = 0                                                    //
// q = 1 - f                                                //
// t = 1 + f                                                //
//                                                          //
// Since we want to use 8bit pwm our maximum is 255         //
// therefore f is computed beforehand and multiplied by 255 //
//                                                          //            
// Since the value for f repeats itself every 60 degree we  //
// only need 60 computed 1byte values for this table.       //
//////////////////////////////////////////////////////////////
void hsv_to_rgb(uint16_t H)
{
   uint8_t h = H/60;
   uint8_t f = (uint8_t) pgm_read_byte( tbl_f + (H - h * 60 ) );
   if( h == 1)
   {
      color.red   = 255 - f;
      color.green = 255;
      color.blue  = 1; // computed value would be 0 -- doesnt make a big difference.
   } else if ( h == 2 )
   {
      color.red   = 1; // computed value would be 0
      color.green = 255;
      color.blue  = f;
   } else if( h == 3)
   {
      color.red   = 1; // computed value would be 0
      color.green = 255 - f;
      color.blue  = 255;
   } else if(  h == 4 )
   {
      color.red   = f;
      color.green = 1; // computed value would be 0
      color.blue  = 255;
   }else if( h == 5 )
   {
      color.red   = 255;
      color.green = 1; // computed value would be 0
      color.blue  = 255 - f;
   } else {
      color.red   = 255;
      color.green = f;
      color.blue  = 1;  // computed values would be 0
   }
}


int main( void )
{
   DDRB = 0xff;
   // Init pwm:
   // FastPwmMode -> ATMEGA8!!!! pwm 8bit -> only 2 16 bit 
   //                                        channels avail and we want to drive 3 leds...
   // TCCR1A   COM1A1   COM1A0  COM1B1  COM1B0  FOC1A   FOC1B   WGM11   WGM10
   //           |       |     |     |      |        |      |     +--> Waveform Generation Mode
   //           |       |     |     |      |        |      +--------> Waveform Generation Mode
   //           |       |     |     |      |        +---------------> Force Output Compare
   //           +-------+-----+-----+------+------------------------> CompareMatchModes ASO.
   // COM sets output compare mode depending on WGM (WaveformGenerationMode)
   // FastPwm: COM1A1/COM1B1    COM1A0/COM1B0
   //                1                0         Clear OC1A/OC1B on compare match - set at BOTTOM

   // WaveformGenerationMode 
   // WGM13  WGM12   WGM11   WGM10
   //    0     1       0       1    Fast PWM 8bit -> TOP(0x00ff) UpdateOCR1x(Bottom) TOV(TOP)

   TCCR1A = 0x00;
   TCCR1A |= ( 1 << COM1A1 ) | ( 1 << COM1B1 ) | ( 0 << COM1A0 ) | ( 0 << COM1B0 ) | ( 0 << FOC1A ) | ( 0 << FOC1B ) | ( 0 << WGM11 ) | ( 1 << WGM10 );

   //TCCR1B:  ICNC1 ICES1  xxx  WGM13   WGM12   CS12   CS11   CS10
   //           |       |     |     |      |        |      |     +--> ClockSelect 1 -+{
   //           |       |     |     |      |        |      +--------> ClockSelect 0  +   -> clk/1
   //           |       |     |     |      |        +---------------> ClockSelect 0 -+{
   //           |       |     |     |      +------------------------> WaveformGeneration
   //           |       |     |     +-------------------------------> WaveformGeneration
   //           |       +-------------------------------------------> InputCaptureEdgeSelect
   //           +---------------------------------------------------> InputCaptureNoiseCanceler

   TCCR1B = 0x00;
   TCCR1B |= ( 0 << ICNC1 ) | ( 0 << ICES1 ) | ( 0 << 5 ) | ( 0 << WGM13 ) | ( 1 << WGM12 ) | ( 0 << CS12 ) | ( 0 << CS11 ) | ( 1 << CS10 );

   TCCR2 = 0x00;
   TCCR2 |= ( 0 << FOC2 ) | ( 1 << WGM20 ) | ( 1 << COM21 ) | ( 0 << COM20 ) | ( 1 << WGM21 ) | ( 0 << CS22 ) | ( 0 << CS21 ) | ( 1 << CS20 );

   uint16_t H = 0;
   uint8_t  S = 255;

   sei();
   for( ;; )
   {
      if( H++ == 360 )
      {
         H = 0;
      }
      hsv_to_rgb( H );
      RED   = color.red;
      GREEN = color.green;
      BLUE  = color.blue;
      _delay_ms(50);
   }
   return 0;
}
