#include <msp430x14x.h>
#include <stdio.h>
#include "basic.h"
#include "config.h"
#include "eventq.h"
#include "timerb.h"
#include "adc12.h"

#if defined(CONFIG_PLATFORM_SIDEARM)
#  define ADC_SEL_MASK (0x08)
#endif

#if defined(CONFIG_PLATFORM_RFIO)
#  define ADC_SEL_MASK (0xFF)
#endif

#ifndef ADC_SEL_MASK
#  warning "adc12: no platform specified"
#  define ADC_SEL_MASK (0x00)
#endif

static int kvhHead;
static int kvhTail;
static int kvhLen;
static int kvhMaxLen;
static int kvhSize;
static int kvhNotifyThreshold;
static int kvhSamplesDropped;
static unsigned int kvhSamples[CONFIG_ADC12_KVH_SAMPLES];

static unsigned int gfiHiRawSample;
static unsigned int gfiLoRawSample;
static unsigned int msp430TempRawSample;
static unsigned int adcMemOverflow;
static unsigned int convTimeOverflow;
static unsigned int dummy;
static int isrEntry;

static boolean scanEnable;

int adc12GetKvh(unsigned int *pResult)
{
  if ((kvhLen == 0) || (pResult == NULL))
    return -1;

  *pResult = (kvhSamples[kvhTail] & 0x0fff);
  circInc(kvhTail, 0, (CONFIG_ADC12_KVH_SAMPLES - 1));
  _DINT();
  kvhLen--;    
  _EINT();
  
  return 0;
}

int adc12GetGfiHi(unsigned int *pResult)
{
  if (pResult == NULL)
    return -1;

  *pResult = (gfiHiRawSample & 0x0fff); // capture 12 LSBs

  return 0;
}

int adc12GetGfiLo(unsigned int *pResult)
{
  if (pResult == NULL)
    return -1;

  *pResult = (gfiLoRawSample & 0x0fff); // capture 12 LSBs

  return 0;
}

static void hardwareInit(void)
{
  ADC12CTL0   = MSC + ADC12ON;        // ADC12 on
  ADC12CTL1   = CSTARTADD_0 + SHS_3 + CONSEQ_3; // Start MEM0, TB1, Rpt Seq
  // ADC12CTL1   = CSTARTADD_0 + SHS_3 + CONSEQ_3 + ISSH; // Start MEM0, TB1, Rpt Seq

  ADC12MCTL0  = INCH_0; // low side fault
  ADC12MCTL1  = INCH_1; // high side fault
  ADC12MCTL2  = INCH_3; // kvh analog output
  ADC12MCTL3  = INCH_10 + EOS; // MSP430 internal temp
  ADC12MCTL4  = 0;
  ADC12MCTL5  = 0;
  ADC12MCTL6  = 0;
  ADC12MCTL7  = 0;
  ADC12MCTL8  = 0;
  ADC12MCTL9  = 0;
  ADC12MCTL10 = 0;
  ADC12MCTL11 = 0;
  ADC12MCTL12 = 0;
  ADC12MCTL13 = 0;
  ADC12MCTL14 = 0;
  ADC12MCTL15 = 0;
  
  ADC12IE = 0x0009;                           // Enable IFGs for ADC12MEM5

  ADC12CTL0 |= ENC;                           // Enable conversions
}

void adc12Init(void)
{
  P6SEL |= ADC_SEL_MASK; // ensure we can get at the A/D

  kvhHead = 0;
  kvhTail = 0;
  kvhLen = 0;
  kvhMaxLen = -1;
  kvhSize = CONFIG_ADC12_KVH_SAMPLES;
  kvhNotifyThreshold = (CONFIG_ADC12_KVH_NOTIFY_THRESHOLD - 1);
  kvhSamplesDropped = 0;

  gfiHiRawSample = 0;
  gfiLoRawSample = 0;
  msp430TempRawSample = 0;
  adcMemOverflow = 0;
  convTimeOverflow = 0;
  isrEntry = 0;  
  scanEnable = false;
}

int adc12ScanEnable(void)
{
  if (scanEnable == false) {
    // should re-initialize the ADC hardware

    hardwareInit();
    timerbStart(); 
    scanEnable = true;
    }
    
  return 0;
}

// disable ADC scanning so values can be polled
int adc12ScanDisable(void)
{
  if (scanEnable == true) {
    timerbStop();
    ADC12CTL0 &= ~ENC;                           // Disable conversions
    scanEnable = false;    
    }
    
   return 0;
}

interrupt[ADC_VECTOR] void ADC12ISR (void)
{
  isrEntry++;
  switch( ADC12IV )
  {
    case 0x02: adcMemOverflow++;
               break;

    case 0x04: convTimeOverflow++;
               break;

    case 0x06: P1OUT = 0x80;
               gfiLoRawSample = ADC12MEM0;
               break;
               
    case 0x0C: gfiHiRawSample = ADC12MEM1;
               if (kvhLen != CONFIG_ADC12_KVH_SAMPLES) {
                 kvhSamples[kvhHead] = ADC12MEM2; 
                 circInc(kvhHead, 0, (CONFIG_ADC12_KVH_SAMPLES - 1));
                 kvhLen++;
                 if (kvhLen > kvhMaxLen) {
                   kvhMaxLen = kvhLen;
                   }
                 if (kvhLen == kvhNotifyThreshold) {
                   eventqPut(EVENTQ_EVENT_ADC12_KVH);
                   }
                 }
               else {
                 kvhSamplesDropped++;
                 eventqPut(EVENTQ_EVENT_ADC12_KVH);
                 }
               msp430TempRawSample = ADC12MEM3;
               ADC12CTL0 &= ~ENC;             // Clear ENC
               dummy = ADC12CTL0;             // gratuitous read 
               ADC12CTL0 |= ENC;              // Re-enable conversions
               P1OUT = 0x00;
               break;
    default:
      break;
  }
}

