/*
  virtuelle Kerze aus 2 LEDs
  Timer0: PWM für LEDs (manuell)
  Timer1: PWM für Boost-Converter 
  10/2009 HS
 */

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

const unsigned char anzahl_leds = 2, led_start = 2;

int volatile c, del, count;
unsigned char volatile ocr = 64;

int volatile bright[3]= {      // Helligkeitswert der LEDs als Array
  0,0,0 };
int volatile diff[3] = {       // die jeweilige Laufrichtung: 1 für aufwärts, -1 für abwärts (pro LED
  1,1,1 };
unsigned volatile port = 0;

void setup() {
  int i;  
  // LED-Ports initialisieren
  for (i = 0; i <= anzahl_leds; i++) {
    DDRB |= 1<<(i + led_start);		// für ATtiny
    _delay_ms(1);
  }

  DDRB |= DDRB | _BV(PORTB1);	// PWM_Port

  PORTB = 0;

  srand(anzahl_leds);    // Zufallszahlen initialisieren

/*****  Timer0 initialisieren - Software PWM für LEDs *****/
  
  // TCCR0A - Timer/Counter Control Register A: normal mode, OC0x disconnected
  TCCR0A = 0;
  // TCCR0B - Timer/Counter Control Register B: Prescaler = clk/8
  TCCR0B = _BV(CS01);

  // TIMSK - Timer/Counter Interrupt Mask Register: Timer/Counter0 Overflow Interrupt Enable
  TIMSK = _BV(TOIE0);


/***** PWM-Signal über Timer1 generieren - für Boost-Converter *****/
  
  // GTCCR - General Timer/Counter1 Control Register: 0
  GTCCR = 0;
  TCCR1 = 0;  // stop timer1
  // PLLCSR - PLL Control and Status Register: PCK disable, use synchronous mode (CPU clock)
  PLLCSR &= ~(_BV(PCKE)|_BV(PLLE));
//  PLLCSR = _BV(PLLE);  // PLL enable
//  _delay_ms(1);	 
//  loop_until_bit_is_clear (PLLCSR, PLOCK);	// wait until PLL unlocked
//  PLLCSR |= _BV(PCKE) | _BV(LSM);  // PCKE enable, low speed mode (32 MHz)

  OCR1C = 128; // TOP-Wert setzen, 128 = ca. 60 kHz
  OCR1A = ocr; // 4 = 800 kHz / 100 kHz
  // TCCR1 - Timer/Counter1 Control Register: Pulse Width Modulator A Enable, Toggle the OC1A output line, no prescaler
  TCCR1 = _BV(PWM1A) | _BV(COM1A1) | _BV(CS10);//| _BV(CS12);
  
  sei();
}

/*** Hauptprogramm ***/

int main(void) {
  int j, rnd, ca;
  float spannung = 0.0;
  uint16_t adc_raw;

  setup();

  ca = 0;						// Analog-Digital-Converter initialisieren
  ADMUX = _BV(MUX3)|_BV(MUX2);	// VCC ist Referenz, Bandgap 1,1V wird gemessen
  ADCSRA = _BV(ADEN);			// ADC einschalten

  while(1)
  {
    rnd = rand() % 200 + 101;     // Zufallszahl zwischen 1000 und 3000 ermitteln
    for (j = 0; j < anzahl_leds; j++) {
      diff[j] = rand() % 6 - 2;     // Zufallszahl zwischen -2 und 3 ermitteln
      _delay_ms(rnd); 
    }

	adc_raw = 0;
    for (j = 0; j < 5; j++) {  // 5 Werte lesen, für Stabilisierung der Messung
   	  ADCSRA |= _BV(ADSC);	// ADC Messung starten
	  loop_until_bit_is_clear (ADCSRA, ADSC);	// warten bis ADC Messung zu Ende ist
	  adc_raw += ADC; // umgwewandelten Wert lesen
    }
	  
    adc_raw /= 5L; // Mittelwer bilden

	spannung = 1126L / adc_raw; // 1.1 * 1024 / ADC-Wert

    if (spannung > 3.4) {
      //port |= _BV(PORTB3);			// LED einschalten
	  if (ocr > 2) { // wenn zu hoch, Duty-Cycle verringern
	    ocr-=1; 
	  }
	}
	else {
  	  //port &= ~_BV(PORTB3);			// LED löschen
	  if ((spannung < 3.1) && (ocr < 120)) { // wenn Spannung zu niedrig wird, Duty Cycle erhoehen
	    ocr+=1; 
	  }
    }
    OCR1A = ocr;

  }  // while
}


//------------------------------------------------------------
// Interrupt-Service-Routine
// der ATtiny läuft mit 8 MHz
// = 1/ 8000000 / 8 / 256 = 1/4000
ISR(TIMER0_OVF_vect) {
  int b, i;

  count = 0;
  TCNT0 = 0; //Timer Reset
  if (++c > 32) {
    c = 0;
    for (i = 0; i < anzahl_leds; i++) {    // Helligkeitswerte für alle LEDs verändern
      b = bright[i];
      b = b + diff [i];  // negative Zahlen!
      if (diff[i] < 0) { 
        if (b < 5) {
          b = 15;
          diff[i] = rand() % 2 + 1;     // Zufallszahl zwischen 1 und 2 ermitteln
        }  
      } // diff
      else {
        if (b > 64) {
          b = 64;
          diff[i] = rand() % 2 - 2;     // Zufallszahl zwischen -2 und -1 ermitteln
        }  
      } // else diff
      bright[i] = b;
    }  // for i
  }
  b = 0;
  for (i = 0; i < anzahl_leds; i++) {
    b |= (bright[i] > c) << i;    //! LED ein- oder ausschalten
  }
  port = PORTB & 3; // alle Bits bis auf 0 und 1 löschen
  PORTB = port | (b << led_start);	// LEDS ein- oder ausschalten (0-1 bleibt frei wg. PWM)
};


/* Interrupt für Compare-Register Overflow - wird benutzt um den Duty Cycle zu setzen

ISR(TIM0_COMPA_vect )
{
  if (PINB & 1) { 	// wenn PWM = 1 ist, folgt die fallende Flanke
    OCR0A = ocr;
  }
  else {
    OCR0A = 21;
  }
  TCNT0 = 0;  // Timer manuell zuruecksetzen
}

*/

