/*
 * File:		pdb_adc_demo.c
 * Purpose:		
 *
 */



#include "common.h"
#include "adc16.h"

#define PDB_irq_no 72          
#define MAX_SAMPLES   128u

#define DisableInterrupt()  asm("CPSID i \n")
#define EnableInterrupt()  asm("CPSIE i \n")

extern void delay(void);

//globle variable
tADC_Config Master_Adc_Config;  
tADC_Cal_Blk CalibrationStore[2];

volatile uint16  wSamplesBuf[MAX_SAMPLES];
volatile uint16  wSamplesBufOffline[MAX_SAMPLES];

volatile uint16  wSamplesCounter;
volatile uint8   bSamplesReady;
volatile uint32  bSeqErr;

// Prototypes
void adc_init(void);
void pdb_init(void);   
void pdb_seq_error(uint16 channel,uint16 pretrigger);
void process_samples_offline(void);
void handle_pdb_seq_err(void);
void deinit_pdb_adc(void);

// ASSUME: IPBus clock is 48MHz
void adc_pdb_demo(int a,char** b)
{
    printf("Welcome to PDB trigger ADC demo!\r\n");
    printf("Please make sure signal on ADC1 channel1 is ready, press any key to continue...!");
    printf("\r\n");
    
    // Initialize global variables
    wSamplesCounter = 0;
    bSamplesReady = 0;
    bSeqErr = 0;
    
    /* Turn on the ADC0 and ADC1 clocks as well as PDB clock
    */
    SIM_SCGC6 |= (SIM_SCGC6_ADC0_MASK | SIM_SCGC6_PDB_MASK);
    SIM_SCGC3 |= (SIM_SCGC3_ADC1_MASK );
    
    
    //enable adc0 interrupt --73
    //enable adc1 interrupt --74
    /*
    adc0 interrupt vector number 73
    IRQ value: 73-16 = 57
    IRQ VEC reg: 57/32 = 1.xx
    bit to set : 57%32 = 25
    */
    NVICICPR1      |=  (1<<25);        //Enable interrupts for adc0       
    NVICISER1      |=  (1<<25);        //Clear any pending interrupts on adc0
    NVICICPR1      |=  (1<<26);        //Enable interrupts for adc1       
    NVICISER1      |=  (1<<26);        //Clear any pending interrupts on adc1  
    /*
    PDB interrupt vector number 88
    IRQ value: 88-16 = 72
    IRQ VEC reg: 72/32 = 2.xx
    bit to set : 72%32 = 8
    */
    NVICICPR2      =  (1<<8);        //Enable interrupts for PDB       
    NVICISER2      =  (1<<8);        //Clear any pending interrupts on PDB
    
    adc_init();
    pdb_init();
    while(1)
    {
    
        if(bSamplesReady)
        {
            // process samples
            bSamplesReady = 0;     
            // DisableInterrupt();
            process_samples_offline();
            // EnableInterrupt();
        }  
        if(bSeqErr)
        {
            handle_pdb_seq_err();
        }
        if(char_present())
        {// if pressing any key, exit this demo
            break;
        }
        delay();
    }
    in_char();
    printf("\r\n");
    deinit_pdb_adc();
}


void process_samples_offline(void)
{
  int i;
  
 // printf("Output of samples in current sampling period:\r\n");
//  printf("=============================================\r\n");
  for(i=0;i<MAX_SAMPLES;i++) {
    printf("%05d\r",wSamplesBufOffline[i]);
    }  
 // printf("=============================================\r\n");
}

void handle_pdb_seq_err(void)
{
  printf("Error: PDB Sequence error!\r\n");
}


void adc_init(void)
{  
  /* 48MHz IPBus clock
   * ADC clock = 48M/2/8 = 3MHz
   * Total conversion time: 56N+4ADCK
   * Given sampling rate Fs = 6.4K, 156us/sample,  156*3= 468 ADCK
   * the maximum h/w average number = 8
   * use h/w average number = 4
   * Total conversion time: 56*4+4 = 228 ADC clocks,76us
   * There are 468-228 = 240 ADC clocks (ie. 80us) free for post processing
   */
 
  // Initialize ADC1
  // Do calibration first with 32 h/w averages
  Master_Adc_Config.CONFIG1  = ADLPC_NORMAL | ADC_CFG1_ADIV(ADIV_8) | ADLSMP_LONG | ADC_CFG1_MODE(MODE_16)
                              | ADC_CFG1_ADICLK(ADICLK_BUS_2);  
  Master_Adc_Config.CONFIG2  = MUXSEL_ADCA | ADACKEN_DISABLED | ADHSC_HISPEED | ADC_CFG2_ADLSTS(ADLSTS_20) ;
  Master_Adc_Config.COMPARE1 = 0x1234u ; 
  Master_Adc_Config.COMPARE2 = 0x5678u ;
  Master_Adc_Config.STATUS2  = ADTRG_SW | ACFE_DISABLED | ACFGT_GREATER | ACREN_ENABLED | DMAEN_DISABLED | ADC_SC2_REFSEL(REFSEL_EXT);
  Master_Adc_Config.STATUS3  = CAL_OFF | ADCO_SINGLE | AVGE_ENABLED | ADC_SC3_AVGS(AVGS_32);
  Master_Adc_Config.PGA      = PGAEN_DISABLED | PGACHP_NOCHOP | PGALP_NORMAL | ADC_PGA_PGAG(PGAG_64);
  Master_Adc_Config.STATUS1A = AIEN_OFF | DIFF_SINGLE | ADC_SC1_ADCH(31);       
  Master_Adc_Config.STATUS1B = AIEN_OFF | DIFF_SINGLE | ADC_SC1_ADCH(31);  
  
  ADC_Config_Alt(ADC1_BASE_PTR, &Master_Adc_Config);  // config ADC
  ADC_Cal(ADC1_BASE_PTR);                    // do the calibration
  ADC_Read_Cal(ADC1_BASE_PTR,&CalibrationStore[1]);   // store the cal
  
  // Now do normal ADC configuration with 4 h/w averages and h/w trigger from PDB
  Master_Adc_Config.CONFIG1  = ADLPC_NORMAL | ADC_CFG1_ADIV(ADIV_8) | ADLSMP_LONG | ADC_CFG1_MODE(MODE_16)
                              | ADC_CFG1_ADICLK(ADICLK_BUS_2);  
  Master_Adc_Config.CONFIG2  = MUXSEL_ADCA | ADACKEN_ENABLED | ADHSC_HISPEED | ADC_CFG2_ADLSTS(ADLSTS_20) ;
  Master_Adc_Config.COMPARE1 = 0x1234u ; 
  Master_Adc_Config.COMPARE2 = 0x5678u ;
  Master_Adc_Config.STATUS2  = ADTRG_HW | ACFE_DISABLED | ACFGT_GREATER | ACREN_DISABLED | DMAEN_DISABLED | ADC_SC2_REFSEL(REFSEL_EXT);
  Master_Adc_Config.STATUS3  = CAL_OFF | ADCO_SINGLE | AVGE_ENABLED | ADC_SC3_AVGS(AVGS_4);
  Master_Adc_Config.PGA      = PGAEN_DISABLED | PGACHP_NOCHOP | PGALP_NORMAL | ADC_PGA_PGAG(PGAG_64);

  Master_Adc_Config.STATUS1A = AIEN_ON | DIFF_SINGLE | ADC_SC1_ADCH(0); 
  
#define   USE_ADC_SINGLE_MODE  
#if defined(USE_ADC_SINGLE_MODE)
  // use single end for others
  Master_Adc_Config.STATUS1B = AIEN_ON | DIFF_SINGLE | ADC_SC1_ADCH(19);    // ADC1_DM1
#else  
  // use differential mode for channel 1 on tower boards
  Master_Adc_Config.STATUS1B = AIEN_ON | DIFF_DIFFERENTIAL | ADC_SC1_ADCH(1);  
#endif
  
  ADC_Config_Alt(ADC1_BASE_PTR, &Master_Adc_Config);  // config the ADC again to default conditions   
  
 
 // Initialize ADC0
  // Do calibration first with 32 h/w averages
  Master_Adc_Config.CONFIG1  = ADLPC_NORMAL | ADC_CFG1_ADIV(ADIV_8) | ADLSMP_LONG | ADC_CFG1_MODE(MODE_16)
                              | ADC_CFG1_ADICLK(ADICLK_BUS_2);  
  Master_Adc_Config.CONFIG2  = MUXSEL_ADCA | ADACKEN_ENABLED | ADHSC_HISPEED | ADC_CFG2_ADLSTS(ADLSTS_20) ;
  Master_Adc_Config.COMPARE1 = 0x1234u ; 
  Master_Adc_Config.COMPARE2 = 0x5678u ;
  Master_Adc_Config.STATUS2  = ADTRG_SW | ACFE_DISABLED | ACFGT_GREATER | ACREN_ENABLED | DMAEN_DISABLED | ADC_SC2_REFSEL(REFSEL_EXT);
  Master_Adc_Config.STATUS3  = CAL_OFF | ADCO_SINGLE | AVGE_ENABLED | ADC_SC3_AVGS(AVGS_32);
  Master_Adc_Config.PGA      = PGAEN_DISABLED | PGACHP_NOCHOP | PGALP_NORMAL | ADC_PGA_PGAG(PGAG_64);
  Master_Adc_Config.STATUS1A = AIEN_OFF | DIFF_SINGLE | ADC_SC1_ADCH(31);       
  Master_Adc_Config.STATUS1B = AIEN_OFF | DIFF_SINGLE | ADC_SC1_ADCH(31);  
  
  ADC_Config_Alt(ADC0_BASE_PTR, &Master_Adc_Config);  // config ADC
  ADC_Cal(ADC0_BASE_PTR);                    // do the calibration
  ADC_Read_Cal(ADC0_BASE_PTR,&CalibrationStore[1]);   // store the cal
  
  // Now do normal ADC configuration with 4 h/w averages and h/w trigger from PDB
  Master_Adc_Config.CONFIG1  = ADLPC_NORMAL | ADC_CFG1_ADIV(ADIV_8) | ADLSMP_LONG | ADC_CFG1_MODE(MODE_16)
                              | ADC_CFG1_ADICLK(ADICLK_BUS_2);  
  Master_Adc_Config.CONFIG2  = MUXSEL_ADCA | ADACKEN_ENABLED | ADHSC_HISPEED | ADC_CFG2_ADLSTS(ADLSTS_20) ;
  Master_Adc_Config.COMPARE1 = 0x1234u ; 
  Master_Adc_Config.COMPARE2 = 0x5678u ;
  Master_Adc_Config.STATUS2  = ADTRG_HW | ACFE_DISABLED | ACFGT_GREATER | ACREN_DISABLED | DMAEN_DISABLED | ADC_SC2_REFSEL(REFSEL_EXT);
  Master_Adc_Config.STATUS3  = CAL_OFF | ADCO_SINGLE | AVGE_ENABLED | ADC_SC3_AVGS(AVGS_4);
  Master_Adc_Config.PGA      = PGAEN_DISABLED | PGACHP_NOCHOP | PGALP_NORMAL | ADC_PGA_PGAG(PGAG_64);

  Master_Adc_Config.STATUS1A = AIEN_ON | DIFF_SINGLE | ADC_SC1_ADCH(0);       
  Master_Adc_Config.STATUS1B = AIEN_ON | DIFF_SINGLE | ADC_SC1_ADCH(1);    

  ADC_Config_Alt(ADC0_BASE_PTR, &Master_Adc_Config);  // config the ADC again to default conditions    

}



void pdb_init(void)
{
 //use ptd10 11 to indicate program status
 //enable ptd10 11 as rgpio funciton
 PORTD_PCR11  = PORT_PCR_MUX(1)|PORT_PCR_DSE_MASK;
 PORTD_PCR10  = PORT_PCR_MUX(1)|PORT_PCR_DSE_MASK;
 
 //enable ptd10 11 as output
 GPIOD_PDDR  |= 0xC00;
 //output 0
 GPIOD_PDOR =0;
 
  //-------------------------pdb setup---------------------------------
 //1.Set to software trigger input 
 //2.Enable pdb module
 //3.Prescale set to 0
 //4. LDMOD = 0: load immediately after setting LDOK
 //5. enable PDB sequence error interrupt
 //Only enable pdb module then you can write in all channel delay value
 
 PDB0_SC = 0x00000000;
 
 // NOTE: PDB must be enabled before writting to buffered registers
 // otherwise, it will be always previous value.
 //Enable PDB
 PDB0_SC |= PDB_SC_PDBEN_MASK;
 
 PDB0_SC |= PDB_SC_TRGSEL(0xF);
   
  //Enable continuous mode
 PDB0_SC |= PDB_SC_CONT_MASK;
 

 //Enable pre-trigger out to ADC0/1
 PDB0_CH0C1 = PDB_C1_TOS(3)|PDB_C1_EN(3);
 PDB0_CH1C1 = PDB_C1_TOS(3)|PDB_C1_EN(3);
 
 //Set CH0 DLY0/1
 PDB0_CH0DLY0 = 0;
 PDB0_CH0DLY1 = 3648;    // delay the conversion time = 76us
 
 //Set CH1 DLY0/1
 PDB0_CH1DLY0 = 0;
 PDB0_CH1DLY1 = 3648;    // delay the conversion time = 76us
 
 //Set PDB_MOD to 156us corresponding to sampling frequency Fs=6.4KHz
 PDB0_MOD = 7488;
 
 //Set interrupt delay value 
 PDB0_IDLY = 7488;
 
 //Load delay value
 PDB0_SC |= PDB_SC_LDOK_MASK;
 
 //Enable interrupt 
 PDB0_SC |= PDB_SC_PDBIE_MASK;
 PDB0_SC |= PDB_SC_PDBEIE_MASK;

 //Set software trigger
 PDB0_SC |= PDB_SC_SWTRIG_MASK;
 }
 

void adc0_isr(void){
  uint16 adc_sample0;
  uint16 adc_sample1;  

  if((ADC0_SC1A & ADC_SC1_COCO_MASK)!= 0){
     GPIOD_PTOR  = 0x400; //toggle 10
     GPIOD_PTOR  = 0x400; //toggle 10
     adc_sample0 = ADC0_RA;
     
  }
 
  if((ADC0_SC1B & ADC_SC1_COCO_MASK)!= 0){
    GPIOD_PTOR  = 0x800; //toggle 11
    GPIOD_PTOR  = 0x800; //toggle 11
    adc_sample1 = ADC0_RB;
  }
     
}

void adc1_isr(void){
  uint16 adc_sample0;
  uint16 adc_sample1;  
  int16  i;
  
  if((ADC1_SC1A&ADC_SC1_COCO_MASK)!= 0){
     GPIOD_PTOR  = 0x400; //toggle 10
     GPIOD_PTOR  = 0x400; //toggle 10
     adc_sample0 = ADC1_RA;
  }
  if((ADC1_SC1B&ADC_SC1_COCO_MASK)!= 0){
    GPIOD_PTOR  = 0x800; //toggle 11
    GPIOD_PTOR  = 0x800; //toggle 11
    adc_sample1 = ADC1_RB;

   
    wSamplesBuf[wSamplesCounter++] = adc_sample1;   
    if(wSamplesCounter == MAX_SAMPLES) {

      // Copy samples to offline backup buf
      for(i=0;i<MAX_SAMPLES;i++) {
        wSamplesBufOffline[i]=wSamplesBuf[i];
      }
   
      bSamplesReady = 1;
      wSamplesCounter = 0;
    }        
  }
}


void pdb_isr(void) 
{
   // Check if pdb sequence error
   if(PDB0_CH0S & PDB_S_ERR(1))
   {
      pdb_seq_error(0,0); // pretrigger 0 sequence error
   }
   if(PDB0_CH0S & PDB_S_ERR(2))
   {
      pdb_seq_error(0,1); // pretrigger 1 sequence error
   }
   if(PDB0_CH1S & PDB_S_ERR(1))
   {
      pdb_seq_error(1,0); // pretrigger 0 sequence error
   }
   if(PDB0_CH1S & PDB_S_ERR(2))
   {
      pdb_seq_error(1,1); // pretrigger 1 sequence error
   }   
   if(PDB0_SC & PDB_SC_PDBIF_MASK)
   {
      PDB0_SC &= ~PDB_SC_PDBIF_MASK;  // write 0 to PDBIF to clear which is different from EM256
   }
}

 
void pdb_seq_error(uint16 channel,uint16 pretrigger)
{
    bSeqErr++;
    if(channel==1)
    {
      PDB0_CH1S &= ~PDB_S_ERR(1<<pretrigger);//clear interrupt flag   
    }
    else if(channel==0)
    {
      PDB0_CH0S &= ~PDB_S_ERR(1<<pretrigger);//clear interrupt flag      
    }
}
/********************************************************************/

void deinit_pdb_adc(void)
{
    PDB0_SC = 0 ;
    
    // Disable the ADC and PDB interrupts in NVIC
    disable_irq(INT_ADC0-16) ;   // through with this interrupt. 
    disable_irq(INT_ADC1-16) ;   // through with this interrupt. 
    disable_irq(INT_PDB0) ;    // through with this interrupt.
}
