#undef HID_ENABLED

// Arduino Due ADC->DMA->USB 1MSPS
// by stimmer
// from http://forum.arduino.cc/index.php?topic=137635.msg1136315#msg1136315
// Input: Analog in A0
// Output: Raw stream of uint16_t in range 0-4095 on Native USB Serial/ACM

// on linux, to stop the OS cooking your data: 
// stty -F /dev/ttyACM0 raw -iexten -echo -echoe -echok -echoctl -echoke -onlcr

volatile int bufn,obufn;
uint16_t buf[4][256];   // 4 buffers of 256 readings

//HV PSU init
const int SS_pin = 42; //tbc
const int SCK_pin = 44;
const int MISO_pin = 22;
const int MOSI_pin = 43;
byte sendValue;   // Value we are going to send


void ADC_Handler(){     // move DMA pointers to next buffer
  int f=ADC->ADC_ISR;
  if (f&(1<<27)){
   bufn=(bufn+1)&3;
   ADC->ADC_RNPR=(uint32_t)buf[bufn];
   ADC->ADC_RNCR=256;
  } 
}

void setup(){
//HV Setup
digitalWrite(SS, HIGH);  // Start with SS high
pinMode(SS_pin, OUTPUT);
pinMode(SCK_pin, OUTPUT);
pinMode(MISO_pin, INPUT); //this is the avalanche pin, not implemented yet
pinMode(MOSI_pin, OUTPUT);

sendValue = 0xFF; //Set the HV here onetime
sendValue = bitBang(sendValue); // Transmit data


//set up the thresholds
pinMode(35, OUTPUT); //setup for the MAX5387 - later set to 0. signal PA0
pinMode(36, OUTPUT);//setup for the MAX5387 - later set to 0. signal PA1
pinMode(37, OUTPUT);//setup for the MAX5387 - later set to 0. signal PA2
digitalWrite(35, LOW);//configure the address of the MAX5387 pot
digitalWrite(36, LOW);//configure the address of the MAX5387 pot
digitalWrite(37, LOW);//configure the address of the MAX5387 pot
pinMode(5, INPUT); //set the interrupt pin for trigger as high impedance, probably not required
 
Wire.begin();
Wire.beginTransmission(byte(0x28)); // transmit to device #112
Wire.write(byte(B00010011)); //set both registers
//Wire.write(byte(B00010001)); //set both register A only
//Wire.write(byte(B00010010)); //set both register B only
Wire.write(byte(0x50));// sets registers to this value (1 step = 13mV)
Wire.endTransmission();      // stop transmitting


  //ADC setup
  
  
  SerialUSB.begin(0);
  while(!SerialUSB);

  pmc_enable_periph_clk(ID_ADC);
  adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST);
  ADC->ADC_MR |=0x80; // free running

  ADC->ADC_CHER=0x80; 

  NVIC_EnableIRQ(ADC_IRQn);
  ADC->ADC_IDR=~(1<<27);
  ADC->ADC_IER=1<<27;
  ADC->ADC_RPR=(uint32_t)buf[0];   // DMA buffer
  ADC->ADC_RCR=256;
  ADC->ADC_RNPR=(uint32_t)buf[1]; // next DMA buffer
  ADC->ADC_RNCR=256;
  bufn=obufn=1;
  ADC->ADC_PTCR=1;
  ADC->ADC_CR=2;
}

void loop(){
  while(obufn==bufn); // wait for buffer to be full
  SerialUSB.write((uint8_t *)buf[obufn],512); // send it - 512 bytes = 256 uint16_t
  SerialUSB.write((uint8_t *)buf[obufn],512); // send it - 512 bytes = 256 uint16_t
    obufn=(obufn+1)&3;    
}

byte bitBang(byte _send)  // This function is what bitbangs the data
{

  //reception isn't implemented in this version. 
  byte _receive = 0;
   digitalWrite(SS_pin, LOW);        // SS low 
  for(int i=0; i<8; i++)  // There are 8 bits in a byte
  {
    digitalWrite(MOSI_pin, bitRead(_send, 7-i));    // Set MOSI
    //delay(1);
    digitalWrite(SCK_pin, HIGH);                  // SCK high
    //bitWrite(_receive, i, digitalRead(MISO_pin)); // Capture MISO
    digitalWrite(SCK_pin, LOW);                   // SCK low
  //digitalWrite(MOSI_pin, LOW);    // Set MOSI
    
  }
   digitalWrite(SS_pin, HIGH);       // SS high again 
 
  //return _receive;        // Return the received data
}

//timing system

void TimersStart() {

        uint32_t config = 0;

  // Set up the power management controller for TC0 and TC2

        pmc_set_writeprotect(false);    // Enable write access to power management chip
        pmc_enable_periph_clk(ID_TC0);  // Turn on power for timer block 0 channel 0
        pmc_enable_periph_clk(ID_TC3);  // Turn on power for timer block 1 channel 0
        pmc_enable_periph_clk(ID_TC6);  // Turn on power for timer block 2 channel 0

  // Timer block 0 channel 0 is connected only to the PPS 
  // We set it up to load regester RA on each PPS and reset
  // So RA will contain the number of clock ticks between two PPS, this
  // value is the clock frequency and should be very stable +/- one tick

        config = TC_CMR_TCCLKS_TIMER_CLOCK1 |         // Select fast clock MCK/2 = 42 MHz
                 TC_CMR_ETRGEDG_RISING |              // External trigger rising edge on TIOA0
                 TC_CMR_ABETRG |                      // Use the TIOA external input line
                 TC_CMR_LDRA_RISING;                  // Latch counter value into RA

        TC_Configure(TC0, 0, config);                 // Configure channel 0 of TC0
        TC_Start(TC0, 0);                             // Start timer running

        TC0->TC_CHANNEL[0].TC_IER =  TC_IER_LDRAS;    // Enable the load AR channel 0 interrupt each PPS
        TC0->TC_CHANNEL[0].TC_IDR = ~TC_IER_LDRAS;    // and disable the rest of the interrupt sources
        NVIC_EnableIRQ(TC0_IRQn);                     // Enable interrupt handler for channel 0

  // Timer block 1 channel 0 is the PLL for when the GPS chip isn't providing the PPS
  // it has the frequency loaded in reg C and is triggered from the TC0 ISR

  config = TC_CMR_TCCLKS_TIMER_CLOCK1 |         // Select fast clock MCK/2 = 42 MHz
     TC_CMR_CPCTRG;       // Compare register C with count value

        TC_Configure(TC1, 0, config);                 // Configure channel 0 of TC1
        TC_SetRC(TC1, 0, FREQ);       // One second approx initial PLL value
  TC_Start(TC1, 0);                             // Start timer running

        TC1->TC_CHANNEL[0].TC_IER =  TC_IER_CPCS; // Enable the C register compare interrupt
        TC1->TC_CHANNEL[0].TC_IDR = ~TC_IER_CPCS; // and disable the rest
        NVIC_EnableIRQ(TC3_IRQn);     // Enable interrupt handler for channel 0

  // Timer block 2 channel 0 is connected to the RAY event
  // It is kept in phase by the PPS comming from TC0 when the PPS arrives
  // or from TC1 when the PLL is active (This is the so called software diode logic)
 
        config = TC_CMR_TCCLKS_TIMER_CLOCK1 |         // Select fast clock MCK/2 = 42 MHz
                 TC_CMR_ETRGEDG_RISING |              // External trigger rising edge on TIOA1
                 TC_CMR_ABETRG |                      // Use the TIOA external input line
                 TC_CMR_LDRA_RISING;                  // Latch counter value into RA
  
  TC_Configure(TC2, 0, config);                 // Configure channel 0 of TC2
  TC_Start(TC2, 0);           // Start timer running
 
  TC2->TC_CHANNEL[0].TC_IER =  TC_IER_LDRAS;    // Enable the load AR channel 0 interrupt each PPS
  TC2->TC_CHANNEL[0].TC_IDR = ~TC_IER_LDRAS;    // and disable the rest of the interrupt sources
  NVIC_EnableIRQ(TC6_IRQn);                     // Enable interrupt handler for channel 0

  // Set up the PIO controller to route input pins for TC0 and TC2

  PIO_Configure(PIOC,PIO_INPUT,
          PIO_PB25B_TIOA0,  // D2 Input 
          PIO_DEFAULT);

  PIO_Configure(PIOC,PIO_INPUT,
          PIO_PC25B_TIOA6,  // D5 Input
          PIO_DEFAULT);
}

// Timer chip interrupt handlers try to get time stamps to within 4 system clock ticks

static uint32_t displ = 0;  // Display values in loop

static uint32_t rega0 = FREQ,   // RA reg
    stsr0 = 0,  // Interrupt status register
    ppcnt = 0,  // PPS count
    delcn = 0;  // Synthetic PPS ms

static uint32_t rega1, stsr1 = 0;

static uint32_t stsr2 = 0;

boolean pll_flag = false; 

int old_ra = 0;
int new_ra = 0;
#define DEAD_TIME 42000 // 1ms

// Handle the PPS interrupt in counter block 0 ISR

void TC0_Handler() {

  // In principal we could connect a diode
  // to pass on the PPS to counter blocks 1 & 2. However for some unknown
  // reason this pulls down the PPS voltage level to less than 1V and
  // the trigger becomes unreliable !! 
  // In any case the PPS is 100ms wide !! Introducing a blind spot when
  // the diode creates the OR of the event trigger and the PPS.
  // So this is a software diode

  TC2->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG; // Forward PPS to counter block 2
  TC1->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG; // Forward PPS to counter block 1

  rega0 = TC0->TC_CHANNEL[0].TC_RA; // Read the RA reg (PPS period)
  stsr0 = TC_GetStatus(TC0, 0);     // Read status and clear load bits

  if (rega0 < MFRQ)     // Sanity check against noise
    rega0 = FREQ;     // Use nominal value
  
        TC_SetRC(TC1, 0, rega0);    // Set the PLL count to what we just counted

  SwapBufs();       // Every PPS swap the read/write buffers
  ppcnt++;        // PPS count
  displ = 1;        // Display stuff in the loop
  gps_ok = true;        // Its OK because we got a PPS  
  pll_flag = true;      // Inhibit PLL, dont take over PPS arrived

  old_ra = 0;       // Dead time counters
  new_ra = 0;
  
  IncDateTime();        // Next second

  if (pps_led) {    
    digitalWrite(PPS_PIN,HIGH);
    pps_led = false;
  } else {
    digitalWrite(PPS_PIN,LOW);
    pps_led = true;
  }
}

// Handle PLL interrupts
// When/If the PPS goes missing due to a lost lock we carry on with the last measured
// value for the second from TC0

void TC3_Handler() {

  stsr2 = TC_GetStatus(TC1, 0);     // Read status and clear interrupt
#if FLG_PIN
  digitalWrite(FLG_PIN,HIGH);   // Flag set (for debug)
  digitalWrite(FLG_PIN,LOW);
#endif

  if (pll_flag == false) {    // Only take over when no PPS

    TC2->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG; // Forward PPS to counter block 2
    SwapBufs();       // Every PPS swap the read/write buffers
    ppcnt++;        // PPS count
    displ = 1;        // Display stuff in the loop
    gps_ok = false;       // PPS missing

    IncDateTime();        // Next second
  }
  pll_flag = false;       // Take over until PPS comes back
}

// We need a double buffer, one is being written by the ISR while
// the other is read from user space within one second.