#include <dsp.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "xc.h"
#include "math.h"
#include "UART.h"
#include "config.h"
#include "extern.h"
#include "conv.h"
#include "isr.h"
#include "config.h"
#include "CalibrationConstants.h"
#include "libq.h"
#include "LowVoltageDisconnect.h"
#include "SetRelays.h"
#include "BatterySignalDecode.h"
#include "SendCANPackets.h"

extern unsigned int pos;


extern int step;
extern unsigned int halls;



fractional fooQ15;
float foo;
unsigned int *Buff; //Pointer to appropriate DMA Buffer, switched on PingPong


//This DMA interrupt occurs at the rate of the TMR3 interrupt divided by the number of
// analog channels that are scanned.  (i.e TMR3 at 80kHz, 20 conversions of 8 channels = 500kHz
// This interrupt include all dsp work takes about 200uS (measured 5/25/2015)
void __attribute__((interrupt, no_auto_psv)) _DMA0Interrupt(void)
//void __attribute__((__interrupt__(__save__(CORCON)))) _DMA0Interrupt(void)
{
    static int i = 0;
    int j;
    
    static int first_time = 1;
    static fractional VBatt_Offset;
    static fractional Ips_Offset;
    static fractional V_StopCharge_Offset;
    static fractional V_Balance_Offset;
    static fractional Rgroundfault_Offset;
    static fractional H2Sensor_Offset;

    long VBatt;
    long Ips;
    long V_StopCharge;
    long V_Balance;
    long Rgroundfault;
    long H2Sensor;



    __asm__ volatile ("push CORCON");


    if (DMACS1 & 0x0001) //Point to one buffer or the other depending on which one caused interrupt
        Buff = BufferB;
    else
        Buff = BufferA;

    if(first_time)  //Compute analog counts cooresponding to zero's of engineering units.
      {
      VBatt_Offset = ConvertEngUnitsToInt(0.0, VBatt_cnt, VBatt_eng);
      H2Sensor_Offset = ConvertEngUnitsToInt(0.0, H2Sensor_cnt, H2Sensor_eng);
      Ips_Offset = ConvertEngUnitsToInt(0.0, Ips_cnt, Ips_eng);
      V_StopCharge_Offset = ConvertEngUnitsToInt(0.0, V_StopCharge_cnt, V_StopCharge_eng);
      V_Balance_Offset = ConvertEngUnitsToInt(0.0, V_Balance_cnt, V_Balance_eng);
      Rgroundfault_Offset = ConvertEngUnitsToInt(0.0, Rgroundfault_cnt, Rgroundfault_eng);
      first_time = 0;
      }

#ifdef ALLOW_CALIBRATION
    if(raw) //Remove offset if in calibration mode
    {
     VBatt_Offset = 0;
      H2Sensor_Offset = 0;
      Ips_Offset = 0;
      V_StopCharge_Offset = 0;
      V_Balance_Offset = 0;
      Rgroundfault_Offset = 0;
      first_time = 1;
    }
#endif

    // BuffferA/BufferB and Buff are unsigned integers, because the ADC is set up to represent data as unsigned fractionals (integers),
    // this is appropriate.  Below all variables that get set from the ADC values are signed integers, this doesn't introduce a lack
    // of precision for variables that are always positive (like VBus) because it's a 12-bit ADC, so representing it in 15 bits instead
    // of 16 doesn't change much.  The ADC FORMAT setting could be used to produce variables as signed integers, but this isn't used to
    // give some control over the zero-point, which may not fall exactly half-way between 0 and Vref in every case.

    //If the analog inputs used (or their ordering) are changed, the next lines must be changed to match
    //Additionally the analog input pin specification must be changed in init.c
  
     for (j = 0; j < FILTERSAMPLES; j++)
         {
         H2Sensor_ADC[j] = _Q15sub(Buff[0 + NUMAIO * j] >> 1,H2Sensor_Offset); //AN0 - H2 sensor
         Rgroundfault_ADC[j] = _Q15sub(Buff[1 + NUMAIO * j] >> 1,Rgroundfault_Offset); //AN1 -
         V_StopCharge_ADC[j] = _Q15sub(Buff[2 + NUMAIO * j] >> 1,V_StopCharge_Offset);  //AN10
         V_Balance_ADC[j] = _Q15sub(Buff[3 + NUMAIO * j] >> 1,V_Balance_Offset);  //AN11
         VBatt_ADC[j] = _Q15sub(Buff[4 + NUMAIO * j] >> 1,VBatt_Offset);  //AN16
         Ips_ADC[j] = _Q15sub(Buff[5 + NUMAIO * j] >> 1,Ips_Offset);  //AN17
         uiTemp1 = Buff[5 + NUMAIO * j];
         


         }



    //Filter Analog Signals.
    //This uses the dsp library and changes the CORCON register.
    // Therefore interrupts that occur during this time must protect the CORCON and possibly other registers.
    
    FIR(FILTERSAMPLES, &outSamples[0], &Rgroundfault_ADC[0], &ADC_Filter);
    Rgroundfault_Buf[i] = outSamples[FILTERSAMPLES - 1]; //Store current value for use later.
       
    FIR(FILTERSAMPLES, &outSamples[0], &V_Balance_ADC[0], &ADC_Filter);
    V_Balance_Buf[i] = outSamples[FILTERSAMPLES - 1]; //Store current value for use later.
    
    FIR(FILTERSAMPLES, &outSamples[0], &V_StopCharge_ADC[0], &ADC_Filter);
    V_StopCharge_Buf[i] = outSamples[FILTERSAMPLES - 1]; //Store current value for use later.
    
    FIR(FILTERSAMPLES, &outSamples[0], &VBatt_ADC[0], &ADC_Filter);
    VBatt_Buf[i] = outSamples[FILTERSAMPLES - 1]; //Store current value for use later.
    
    FIR(FILTERSAMPLES, &outSamples[0], &Ips_ADC[0], &ADC_Filter);
    Ips_Buf[i] = outSamples[FILTERSAMPLES - 1]; //Store current value for use later.

    FIR(FILTERSAMPLES, &outSamples[0], &H2Sensor_ADC[0], &ADC_Filter);
    H2Sensor_Buf[i] = outSamples[FILTERSAMPLES - 1]; //Store current value for use later.


    if(i == REPORTINGSAMPLES-1)  //Collected a series of samples, now process and send out the data, the code in this if statement runs at the T3 interrupt rate (25Hz)
    {
    time = time + 1.0/CAN_PACKET_RATE;

    AD1CON1bits.ASAM = 0;// Stop ADC, we have what we need.

    //Form data for sending out over CAN.  
    Rgroundfault = 0L;
    V_Balance = 0L;
    V_StopCharge = 0L;
    VBatt = 0L;
    Ips = 0L;
    H2Sensor = 0L;

    for (j = 0; j < REPORTINGSAMPLES; j++)
       {
        Rgroundfault += Rgroundfault_Buf[j]; //N-m
        V_Balance += V_Balance_Buf[j];
        V_StopCharge += V_StopCharge_Buf[j];
        VBatt += VBatt_Buf[j];
        Ips += Ips_Buf[j];
        H2Sensor += H2Sensor_Buf[j];
       }

    Rgroundfault = Rgroundfault/ REPORTINGSAMPLES;
    V_Balance = V_Balance / REPORTINGSAMPLES;
    V_StopCharge = V_StopCharge / REPORTINGSAMPLES;
    VBatt = VBatt / REPORTINGSAMPLES;
    Ips = Ips / REPORTINGSAMPLES;
    H2Sensor = H2Sensor / REPORTINGSAMPLES;

    
  //  SendCANPackets(VBatt, Ips, V_Balance, V_StopCharge, Rgroundfault, H2Sensor);

 
  //  while(C1TR01CONbits.TXREQ0 && C1TR01CONbits.TXREQ1);  //Wait for CAN messages to go.

    i = 0;

    IFS0bits.DMA0IF = 0; //Clear the DMA interrupt flag bit (otherwise it seems to come right back from sleep)

    //Idle();  //This shuts down the main clock, but TMR1 is using the clock from SOSC so will continue to count and cause an interrupt
              // This interrupt, or a wake on bus activity interrupt for the can module will bring the device back from sleep.
    //PORTE = 0;  //Turn both off;

    }
    else  //We haven't collected all the analog i/o needed for averaging, so keep going.
    {
       i++;
     //  PORTE = 3;  //Turn both on.
       IFS0bits.DMA0IF = 0; //Clear the DMA interrupt flag bit
    }


    __asm__ volatile ("pop CORCON");
    

}


// TMR1 Interrupt  25Hz, Starts ADC sampling, after enough samples are collected (specified by FILTERSAMPLES and REPORTINGSAMPLES),
// the dma interrupt sends out the CAN packet and stops adc sampling until this isr starts it again.  If sleep mode is being used, it is this
// interrupt that wakes the processor from sleep generally, and the dma interrupt puts it back to sleep after the can packets have been sent.
void __attribute__((interrupt, no_auto_psv)) _T1Interrupt(void)
//void __attribute__((__interrupt__(__save__(CORCON))))     _T1Interrupt(void)
{
    __asm__ volatile ("push CORCON");

AD1CON1bits.ASAM = 1; //Auto-sample start bit
    _T1IF = 0;
    __asm__ volatile ("pop CORCON");
   

}

void __attribute__((interrupt, no_auto_psv)) _T4Interrupt(void)
//void __attribute__((__interrupt__(__save__(CORCON))))     _T4Interrupt(void)
{
    __asm__ volatile ("push CORCON");
    if(microsec_timer >  0)
        microsec_timer--;
    _T4IF = 0;
    __asm__ volatile ("pop CORCON");
   

}



void __attribute__((interrupt, no_auto_psv)) _C1Interrupt(void)
//void __attribute__((__interrupt__(__save__(CORCON))))    _QEIInterrupt(void)
{
    CANmsg RcvdMessage;
    char s[256];
    
    __asm__ volatile ("push CORCON");
    //PORTAbits.RA3 = 1;
    
 
    if (C1INTFbits.RBIF) /* check to see if the interrupt is caused by receive */ {
        if (C1RXFUL1bits.RXFUL8) 
        {  /*check to see if buffer 8 is full, indicating CmdID was 10, and a cell load change is being requested*/
            DisAssembleCAN_Packet(8, &RcvdMessage);
            //sprintf(s,"Buf8 %d %d %d %d\r\n",RcvdMessage.data[0],RcvdMessage.data[1],RcvdMessage.data[2],RcvdMessage.data[3]); putsU1(s);
            if(RcvdMessage.data_length == 4)  //Add range checking here.
            {
              //sprintf(s,"Got cmd:  %lu \r\n",CellLoadCmd); putsU1(s);
            }
            C1RXFUL1bits.RXFUL8 = 0;
        }
        
        if (C1RXFUL1bits.RXFUL9)
        {  /*check to see if buffer 9 is full, indicating CmdID was 00, and a load change is being requested. */
         //   DisAssembleCAN_Packet(9, &RcvdMessage);
         //   sprintf(s,"Buf9 %d \r\n",256*RcvdMessage.data[1]+RcvdMessage.data[0]);  putsU1(s);
            //if(RcvdMessage.data_length == 8)  //There should be 8 bytes in a valid message
          //  {
           //   Current =  256*RcvdMessage.data[1]+RcvdMessage.data[0];  //Update current for use in PS On/Off Algorithm
          //  }
            C1RXFUL1bits.RXFUL9 = 0;
        }

        /* clear flag */
        C1INTFbits.RBIF = 0;
    }
    else {
        if (C1INTFbits.TBIF) //If interrupt is caused by transmit. 
        {
            /* clear flag */
            C1INTFbits.TBIF = 0;
        }
    }
    /* clear interrupt flag */
    IFS2bits.C1IF = 0;


    __asm__ volatile ("pop CORCON");

}


void __attribute__((interrupt, no_auto_psv)) _SPI1Interrupt(void) {
    __asm__ volatile ("push CORCON");
    
    static int char_num = 0;
        
    spi_rxbuf[char_num] = SPI1BUF;
        
    if(char_num >= spi_data_len-1) //No more bytes to send
    {
       char_num = 0; // Set up for next time 
       spi_data_len = 0;
    }
    else  // More bytes to send
    {
        char_num++;
        SPI1BUF = spi_txbuf[char_num]; 
    }
        
    _SPI1IF = 0; // Clear the Interrupt flag
    __asm__ volatile ("pop CORCON");
}
