 
 
 
/* Übernommen vom Projekt LeckerWecker
 * TODO Namenskonvention überarbeiten 
 * TODO Evtl. seperaten 50ms und einen fixen 1ms Timer?
 * MalZeit 06/2014 */

#include "systemzeitgeber.h"

#include <stddef.h>

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

#include "debug.h"

void SystemzeitgeberRegistriereCbIntern(void (*callback) (void), int8_t ms) __attribute__ ((noinline));

/**
 * 
 * void SystemzeitgeberRegistriereCbIntern(void (*callback) (void), int8_t ms) __attribute__ ((noinline));
 * 
 * inline function action as optimization fence
 * 
 */

struct Callback {
  void (*p_function) (void);
  int8_t ms;
  int8_t count;
};


static struct Callback g_callback[SYSTEMZEITGEBER_CB_ARRAY] = {{NULL, -1, 0}, {NULL, -1, 0}, {NULL, -1, 0}};
// no volatile since no direct interaction between ISR and non-ISR
// atomic access necessary
static void (*gp_wdt_feed) (uint8_t id) = NULL;
static uint8_t g_wdt_id = 0;
 
/**
 * 
 * INIT
 * 
 */
void SystemzeitgeberInit()
{
  // WGM #2
  TCCR0A &= ~_BV(WGM00);
  TCCR0A |= _BV(WGM01);
  TCCR0B &= ~_BV(WGM02);
  
  // 1000Hz
  // F_CPU/1000/prescaler(1024)
  OCR0A = 8;
  
  // enable interrupt
  TIMSK0 |= _BV(OCIE0A);
}

/**
 * 
 */

uint8_t SystemzeitgeberHoleWdtId(void)
{
  return g_wdt_id;
}


/**
 * 
 * non-ISR only
 * 
 * NOTE: optimization fence with SystemzeitgeberRegistriereCbIntern() 
 * 
 */
void SystemzeitgeberRegistriereCb(void (*callback) (void), int8_t ms)
{
  SYSTEMZEITGEBER_ATOMIC_START;
  SystemzeitgeberRegistriereCbIntern(callback, ms);
  SYSTEMZEITGEBER_ATOMIC_ENDE;
  
}

/**
 * 
 * non-ISR only
 * 
 * Registriere eine Callback-Funktion mit zugehöriger Aufrufrate in ms
 * 
 */

void SystemzeitgeberRegistriereCbIntern(void (*callback) (void), int8_t ms)
{
  uint8_t i = 0;
  while (g_callback[i].p_function != NULL && i < SYSTEMZEITGEBER_CB_ARRAY) { // TODO off-by-one?
    i+=1;
  }
  
  g_callback[i].p_function = callback;
  g_callback[i].ms = ms;

}  

/**
 * 
 * Init only
 * 
 */

void SystemzeitgeberRegistriereWdtManagerCb(void (*callback) (uint8_t id))
{
    gp_wdt_feed = callback;
}

/**
 * 
 * Init and main loop
 * 
 * ISR-safe against ISR(TIMER0_COMPA_vect)
 * 
 */

void SystemzeitgeberSetzeWdtId(uint8_t id)
{
  g_wdt_id = id; // atomic by 8-Bit AVR architecture 
}

/**
 * 
 * non-ISR only
 * 
 */

void SystemzeitgeberStart(void)
{
  // prescaler 1024 / 101
  TCCR0B = (TCCR0B & ~_BV(CS01)) | _BV(CS02) | _BV(CS00);
}

/**
 * 
 * non-ISR only
 * 
 */
void SystemzeitgeberStop(void)
{
  TCCR0B = TCCR0B & ~(_BV(CS02) | _BV(CS01) |_BV(CS00));  
}

 
/**
 * 
 * ISR alle 1 ms @ 10 MHz && 3 Callbacks
 * 
 * cpu load xx% (under development)
 * 
 */
ISR(TIMER0_COMPA_vect)
{
  TOGGLE_TP1;
  if(gp_wdt_feed != NULL) {
    gp_wdt_feed(g_wdt_id);
  }

  for(int8_t i = 0; i < SYSTEMZEITGEBER_CB_ARRAY; i++) {
    if(g_callback[i].p_function != NULL && g_callback[i].ms > 0) {
      g_callback[i].count += 1;
      if(g_callback[i].count >= g_callback[i].ms) {
        g_callback[i].count = 0;
        g_callback[i].p_function();
      }
    }  
  }
  TOGGLE_TP1;
}

// EOF
