
/* MalZeit 06/2014 */

#include "ad_wandler.h"

#include <stddef.h>

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

void AdWandlerInit(void);
void AdWandlerRegistriereCb(void (*callback) (uint16_t analog));
void AdWandlerRegistriereCbInternal(void (*callback) (uint16_t analog));
void AdWandlerStart(void);

static void (*gpAdWandlerCallback) (uint16_t analog) = NULL;
static void (*gpWdtFeed) (uint8_t id) = NULL;

void Timer1Init(void);











/**
 * 
 * AD-Wander für maximale Wiederholrate von 4 kHz initialisieren
 * Dabei wird ADC5 benutzt und die interne Referenzspannung aktiviert
 * Timer1 dient als Trigger zum Starten einer AD-Wandlung
 * 
 */


void AdWandlerInit(void)
{
  // TODO Implement Watchdog

  gpWdtFeed = gpWdtFeed; // WARNING avoid compiler warning
// erste Umwandlung starten
// Versorgungsspannung als Referenz
// ADC0 auswaehlen
// Daher   
  Timer1Init();
  
  // ADC5 und Interne-Referenz
  ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX2) | _BV(MUX0);
// Autotrigger (ADATE)
// AD-Wandler benötig 13 Taktzyklen für Wandlung
// Eine Maximale Wandlerrate von 4kHz wird angestrebt.
// 8 MHz / (128* 13) = 4,8 kHz (maximale Wandlerrate)
  ADCSRA = _BV(ADEN) | _BV(ADATE) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);
  
// Autotrigger auf OCR1B 
  ADCSRB = _BV(ADTS0) | _BV(ADTS2);

  // Pin ADC5 als Eingang
  DIDR0 = _BV(ADC5D);
//  ADCSRA |= _BV(ADEN) | _BV(ADIE) | _BV(ADPS2);

// Ersten AD-Vorgang starten und IF loeschen für Initialisierung der Hardware
  ADCSRA |= _BV(ADSC);
  _delay_us(80);
  while((ADCSRA & _BV(ADSC)) != 0) {
    _delay_us(1);
  }
  ADCSRA |= _BV(ADIF); // Flag loeschen

} 

/**
 * 
 * Timer1 mit Wiederholrate von 2 kHz initialisieren
 * 
 */

void Timer1Init(void)
{
  TCCR1A = 0; // WGM10 + WGM11
  // Clear Timer on Compare Match OCR1A
  TCCR1B = _BV(WGM12); // WGM12 + WGM13 / CS10..CS12
  // Soll: f_ISR = 2 kHz
  // prescaler = clk_io/64
  // 61,5= 8 MHz / (prescaler * f_ISR) -1 // Formel frei nach Datenblatt mega88
  OCR1A = 62;  // f_ISR = 2,016 kHz
  // f_ISR = f_clk / [N(1+OCRA)] <=> OCRA = f_clk / (N * f_int) - 1
  OCR1B = 15; // ADC-Trigger
  
  TCCR1C = 0;
  
  TIMSK1 = _BV(OCIE1B); // Triggerquelle für den AD-Wandler
}

/**
 *
 *
 *
 */ 

void AdWandlerRegistriereCb(void (*callback) (uint16_t analog))
{
  AD_WANDLER_START_ATOMIC;
  AdWandlerRegistriereCbInternal(callback);
  AD_WANDLER_STOP_ATOMIC;
}


void AdWandlerRegistriereCbInternal(void (*callback) (uint16_t analog))
{
  gpAdWandlerCallback = callback;
}

/**
 * 
 * 
 * 
 */

void AdWandlerStart(void)
{
  ADCSRA |= _BV(ADIE); // AD Interrup Enable
  // Starte Timer1
  // prescaler = clk_io/128
  TCCR1B = (TCCR1B & ~_BV(CS12)) | _BV(CS11) | _BV(CS10);  
}

/**
 * 
 * 
 * 
 * 
 */

ISR(ADC_vect)
{
  uint16_t result = ADC & 0x03ff;
  if(gpAdWandlerCallback != NULL) {
    gpAdWandlerCallback(result); //TODO
  }
}

/**
 * 
 * 
 * Dummy der benötigt wird damit der AD-Wandler auslöst
 * 
 */
EMPTY_INTERRUPT(TIMER1_COMPB_vect);


