/*********************************************************************
 *
 *                 NTSC TV interface
 *
 *********************************************************************
 * Bruce Land Cornell University
 * June 2014
 * This code uses many cool ideas from
 * Programming 32-bit Microcontrollers in C: Exploring the PIC32
 * by Lucio Di Jasio
 *
 * Uses two Compare units from one timer to do sync and video timing
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * SCK1 is pin 25
 * SDO1 is PPS group 2, map to RPA1 (pin 3)
 * SDI1 is PPS group 2, map to RPB8 (pin 17)
 * SYNC is PortB.0 (pin 4)
 */

#include <plib.h>
#include <xc.h> // need for pps
#include <stdio.h>
#include <stdlib.h>
#include <math.h>


// Configuration Bit settings
// SYSCLK = 60 MHz (8MHz Crystal/ FPLLIDIV * FPLLMUL / FPLLODIV)
// PBCLK = 30 MHz
// Primary Osc w/PLL (XT+,HS+,EC+PLL)
// WDT OFF
// Other options are don't care
//                       8MHZ                          4MHz               60MHz            30   <-----<---    60MHz
#pragma config FNOSC = FRCPLL, POSCMOD = HS, FPLLIDIV = DIV_2, FPLLMUL = MUL_15, FPBDIV = DIV_2, FPLLODIV = DIV_1
#pragma config FWDTEN = OFF
#pragma config FSOSCEN = OFF, JTAGEN = OFF
// core frequency we're running at // peripherals at 40 MHz
#define	SYS_FREQ 60000000

// The SPI channel
volatile SpiChannel spiChn = SPI_CHANNEL1 ;	// the SPI channel to use
volatile int spiClkDiv = 6 ;//5 MHz pixel rate => 256 points in 51.2 microSec

// main screen buffer array 256 wide x 200 high = 51200 pixels
// 51200/32 = 1600 integer words
int screen_buffer[1600] ;
volatile int *screen_buffer_addr = screen_buffer ;
volatile int *screen_ptr ;

// The DMA channels
#define DMAchn1 1
#define DMApri0 0

// video timing
#define line_cycles 1905 // 63.5 uSec at 30 MHz Fpb, prescaler=1
#define us_5_cycles 150 // 5 uSec at 30 MHz Fpb, prescaler=1
#define us_13_cycles 390 // beginning of the video DMA burst -- use for centering
#define us_11_cycles 330 // beginning of the video DMA burst -- use for centering
#define us_10_cycles 300 // beginning of the video DMA burst -- use for centering

// video active lines -- 200 total
#define image_start 20
#define image_end image_start+200

// Current line number which is modified
// by a state machine in the timer2 ISR
volatile int LineCount = 0 ;
// ISR driven 1/60 second time
volatile int time_tick_60_hz = 0 ;

// video formatiing variables
//One bit masks for each bit of an INT
unsigned int pos[32] = {0x80000000,0x40000000,0x20000000,0x10000000,0x08000000,0x04000000,0x02000000,0x01000000,
                0x800000,0x400000,0x200000,0x100000,0x080000,0x040000,0x020000,0x010000,
                0x8000,0x4000,0x2000,0x1000,0x0800,0x0400,0x0200,0x0100,
                0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};

#include "ascii_characters.h"
#define top 0
#define left 0
#define right 255
#define bottom 199


//=== fixed conversion macros =========================================
// for 16.16 fixed point
typedef signed int fix ;
#define int2fix(a)   (((fix)(a))<<16)            //Convert char to fix. a is a char
#define fix2int(a)   ((signed int)((a)>>16))    //Convert fix to int. a is an int
#define float2fix(a) ((fix)((a)*65536.0))         //Convert float to fix. a is a float
#define fix2float(a) (((float)(a))/65536.0)       //Convert fix to float. a is an int
//#define multfix(a,b) ((fix)(((( signed long long)(a))*(( signed long long)(b)))>>16)) //multiply two fixed 16.16
#define multfix(a,b) ((fix)(((( signed long)(a))*(( signed long)(b)))>>16)) //multiply two fixed 16.16

//
// == OC3 ISR ============================================
// VECTOR 14 is OC3 vector -- set up of ipl 3 //

void __ISR(14, ipl3)  OC3Handler(void) 
{
   // mPORTBSetBits(BIT_1);
    // Convert DMA to SPI control 
    DmaChnSetEventControl(DMAchn1, DMA_EV_START_IRQ(_SPI1_TX_IRQ)); //
    // clear the timer interrupt flag -- name from
    // http://people.ece.cornell.edu/land/courses/ece4760/PIC32/Microchip_stuff/32-bit-Peripheral-Library-Guide.pdf
    // Table 8.2
    mOC3ClearIntFlag();
   // mPORTBClearBits(BIT_1);  // for profiling the ISR execution time
}

// == Timer 2 ISR =========================================
void __ISR(_TIMER_2_VECTOR, ipl2) Timer2Handler(void)
{
    //mPORTBSetBits(BIT_1); // for profiling the ISR execution time
    // update the current scanline number
    LineCount++ ;

    // start the DMA byte blaster to the screen
    if (LineCount >= image_start && LineCount < image_end){
        // set the Chan1 DMA transfer parameters: source & destination address,
        // source & destination size, number of bytes per event
        // 32 bytes / line with 4 bytes per transfer (SPI in 32 bit mode)
        //screen_ptr = screen_buffer + ((LineCount - image_start)<<5) ;
        DmaChnSetTxfer(DMAchn1, (void*)screen_ptr, (void*)&SPI1BUF, 32, 4, 4); //32
        // IRO 17 is the output compare 3 interrupt (See datasheet table 7.1)
        ConfigIntOC3(OC_INT_PRIOR_1 | OC_INT_ON); //3 //
        DmaChnSetEventControl(DMAchn1, DMA_EV_START_IRQ(17)); //
        // turn it on for 32 bytes
        DmaChnEnable(DMAchn1);
        // increment the image memory pointer for the next ISR pass
        screen_ptr += 8; // 8 32-bit words per line
    }
    else ConfigIntOC3(OC_INT_PRIOR_1 | OC_INT_OFF); //turn it off when we dont need to write to video

    // == SYNC state machine ====
    // begin long (Vertical) synch after line 247
    if (LineCount==248) {OC2R = line_cycles - us_5_cycles ;}
    // back to regular sync after line 250
    // the first condition eliminates sync for one line (to avoid duplicate)
    if (LineCount==250) {OC2R = 0 ;}
    if (LineCount==251) {OC2R = us_5_cycles ;}
    // start new frame after line 262 and reset the image memory pointer and
    // update the frame time_tick
    if (LineCount==263) {
        LineCount = 1;
        // reset for the next frame
        screen_ptr = screen_buffer_addr;
        // a general propose time base
        time_tick_60_hz++;
    }
    
    // clear the timer interrupt flag
    mT2ClearIntFlag();
   // mPORTBClearBits(BIT_1);  // for profiling the ISR execution time
}

//== plot a point =========================================================
//plot one point
//at x,y with color 1=white 0=black 2=invert
#define word_per_line 8
void video_pt(int x, int y, char c) {
	//each line has 18 bytes
	//calculate i based upon this and x,y
	// the word with the pixel in it
	//int i = (x/32) + y*8
	int i = (x >> 5) + y * word_per_line ;
	if (c==1)
	  screen_buffer[i] = screen_buffer[i] | pos[x & 0x1f];
        else if (c==0)
	  screen_buffer[i] = screen_buffer[i] & ~pos[x & 0x1f];
        else
	  screen_buffer[i] = screen_buffer[i] ^ pos[x & 0x1f];
}
// macro version
#define video_pt_m(x,y,c) \
    do{int i = (x >> 5) + y * word_per_line ; \
       if (c==1) \
	  screen_buffer[i] = screen_buffer[i] | pos[x & 0x1f];\
        else if (c==0)\
	  screen_buffer[i] = screen_buffer[i] & ~pos[x & 0x1f];\
        else\
	  screen_buffer[i] = screen_buffer[i] ^ pos[x & 0x1f];\
    } while(0);

//=============================================================================
//plot a line
//at x1,y1 to x2,y2 with color 1=white 0=black 2=invert
//NOTE: this function requires signed chars
//Code is from David Rodgers,
//"Procedural Elements of Computer Graphics",1985
void video_line(int x1, int y1, int x2, int y2, char c) {
	int e;
	signed int dx,dy,j, temp;
	signed char s1,s2, xchange;
        signed int x,y;

	x = x1;
	y = y1;

	//take absolute value
	if (x2 < x1) {
		dx = x1 - x2;
		s1 = -1;
	}

	else if (x2 == x1) {
		dx = 0;
		s1 = 0;
	}

	else {
		dx = x2 - x1;
		s1 = 1;
	}

	if (y2 < y1) {
		dy = y1 - y2;
		s2 = -1;
	}

	else if (y2 == y1) {
		dy = 0;
		s2 = 0;
	}

	else {
		dy = y2 - y1;
		s2 = 1;
	}

	xchange = 0;

	if (dy>dx) {
		temp = dx;
		dx = dy;
		dy = temp;
		xchange = 1;
	}

	e = ((int)dy<<1) - dx;

	for (j=0; j<=dx; j++) {
		video_pt_m(x,y,c);

		if (e>=0) {
			if (xchange==1) x = x + s1;
			else y = y + s2;
			e = e - ((int)dx<<1);
		}

		if (xchange==1) y = y + s2;
		else x = x + s1;

		e = e + ((int)dy<<1);
	}
}

//==============================================================
//return the value of one point
//at x,y with color 1=white 0=black 2=invert
char video_state(int x, int y) {
    //The following construction
    //detects exactly one bit at the x,y location
    // int i = (x>>3) + ((int)y<<4) + ((int)y<<3);
    int i = (x >> 5) + y * word_per_line ;
    return (screen_buffer[i] & pos[x & 0x1f]) != 0 ;
}

//===============================================================
// put a big character on the screen
// c is index into bitmap
void video_putchar(int x, int y, int c) {
    int i;
    int y_pos;
    int j;

    for (i=0;i<7;i++) {
        y_pos = y + i;

        j = ascii[c][i] ;

        video_pt_m(x,   y_pos, (j & 0x80)==0x80);
        video_pt_m(x+1, y_pos, (j & 0x40)==0x40);
        video_pt_m(x+2, y_pos, (j & 0x20)==0x20);
        video_pt_m(x+3, y_pos, (j & 0x10)==0x10);
        video_pt_m(x+4, y_pos, (j & 0x08)==0x08);
    }
}

//=============================================================
// put a string of big characters on the screen
void video_string(int x, int y, char *str) {
	char i;
	for (i=0; str[i]!=0; i++) {
		video_putchar(x,y,str[i]);
		x = x+6;
	}
}

//============================================================
// put a thick character on the screen
// c is index into bitmap
void video_big_putchar(int x, int y, int c) {
    int i;
    int y_pos;
    int j;

    for (i=0; i<7; i=i+1) {
        y_pos = y + i;

        j = ascii[c][i] ;

        video_pt_m(x,   y_pos, (j & 0x80)==0x80);
        video_pt_m(x+1, y_pos, (j & 0x80)==0x80);
        video_pt_m(x+2, y_pos, (j & 0x40)==0x40);
        video_pt_m(x+3, y_pos, (j & 0x40)==0x40);
        video_pt_m(x+4, y_pos, (j & 0x20)==0x20);
        video_pt_m(x+5, y_pos, (j & 0x40)==0x40);
        video_pt_m(x+6, y_pos, (j & 0x10)==0x10);
        video_pt_m(x+7, y_pos, (j & 0x10)==0x10);
        video_pt_m(x+8, y_pos, (j & 0x08)==0x08);
        video_pt_m(x+9, y_pos, (j & 0x08)==0x08);
    }
}

//==================================================================
// put a string of thick characters on the screen
void video_bold_string(int x, int y, char *str) {
	char i;
	for (i=0; str[i]!=0; i++) {
		video_big_putchar(x,y,str[i]);
		x = x+10;
	}
}

//====================================================================
// FFT

#define N_WAVE          512    /* size of FFT 512 */
#define LOG2_N_WAVE     9     /* log2(N_WAVE) 0 */
#define begin {
#define end }

int Sinewave[N_WAVE]; // a table of sines for the FFT
int window[N_WAVE]; // a table of window values for the FFT
int fr[N_WAVE], fi[N_WAVE];

void FFTfix(int fr[], int fi[], int m)
//Adapted from code by:
//Tom Roberts 11/8/89 and Malcolm Slaney 12/15/94 malcolm@interval.com
//fr[n],fi[n] are real,imaginary arrays, INPUT AND RESULT.
//size of data = 2**m
// This routine does foward transform only
begin
    int mr,nn,i,j,L,k,istep, n;
    int qr,qi,tr,ti,wr,wi;

    mr = 0;
    n = 1<<m;   //number of points
    nn = n - 1;

    /* decimation in time - re-order data */
    for(m=1; m<=nn; ++m)
    begin
        L = n;
        do L >>= 1; while(mr+L > nn);
        mr = (mr & (L-1)) + L;
        if(mr <= m) continue;
        tr = fr[m];
        fr[m] = fr[mr];
        fr[mr] = tr;
        //ti = fi[m];   //for real inputs, don't need this
        //fi[m] = fi[mr];
        //fi[mr] = ti;
    end

    L = 1;
    k = LOG2_N_WAVE-1;
    while(L < n)
    begin
        istep = L << 1;
        for(m=0; m<L; ++m)
        begin
            j = m << k;
            wr =  Sinewave[j+N_WAVE/4];
            wi = -Sinewave[j];
            wr >>= 1;
            wi >>= 1;

            for(i=m; i<n; i+=istep)
            begin
                j = i + L;
                tr = multfix(wr,fr[j]) - multfix(wi,fi[j]);
                ti = multfix(wr,fi[j]) + multfix(wi,fr[j]);
                qr = fr[i] >> 1;
                qi = fi[i] >> 1;
                fr[j] = qr - tr;
                fi[j] = qi - ti;
                fr[i] = qr + tr;
                fi[i] = qi + ti;
            end
        end
        --k;
        L = istep;
    end
end

// ========================================================================
int	main(void)
{
    // scope animation variables
    #define nSamp 512
    int sample_number=0 ; // index for balls and for next launch
    int v_in[nSamp], v_disp[nSamp] ;
    fix v_fix[nSamp] ;

    // varialbes for magnitude
    int abs_fr, abs_fi, max_fr, max_fr_index ;
    // variables for log2(magnitude)
    #define fixed_one (65536)
    #define fixed_043 (2818)
    #define fixed_16 (16*fixed_one)
    #define fixed_cutoff (-14*fixed_one)
    int sx, y, ly;

    // FFT timing
    int start_FFT;

    // global time
    int time ;
    // some strings
    char cu1[]="Cornell ECE 4760 -- PIC32 FFT";
    char time_string[10] ;

	// Configure the device for maximum performance but do not change the PBDIV
	// Given the options, this function will change the flash wait states, RAM
	// wait state and enable prefetch cache but will not change the PBDIV.
	// The PBDIV value is already set via the pragma FPBDIV option above..
	SYSTEMConfig(SYS_FREQ, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);

        //make sure analog is cleared
        //ANSELA =0;
        //ANSELB =0;

        // timer 2 setup for one video line ===========================================
        // Set up timer2 on,  interrupts, internal clock, prescalar 1, compare-value
        // This timer generates the time base for each horizontal video line
        OpenTimer2(T2_ON | T2_SOURCE_INT | T2_PS_1_1, line_cycles);
        // set up the timer interrupt with a priority of 2
        ConfigIntTimer2(T2_INT_ON | T2_INT_PRIOR_2);
        mT2ClearIntFlag(); // and clear the interrupt flag
        // zero the system time tick which is updated in the ISR
        time_tick_60_hz = 0;

        // timer 3 setup for adc trigger  ==============================================
        // Set up timer3 on,  no interrupts, internal clock, prescalar 1, compare-value
        // This timer generates the time base for each ADC sample. fails at 950000 samples/sec
        // works at 900000
        #define sample_rate 900000
        #define timer_match 30000000/sample_rate
        OpenTimer3(T3_ON | T3_SOURCE_INT | T3_PS_1_1, timer_match); //60 is 500 kHz (works at 33 -- is 900 kHz)
        // set up the timer interrupt with a priority of 2
        //ConfigIntTimer3(T3_INT_ON | T3_INT_PRIOR_2);
        //mT3ClearIntFlag(); // and clear the interrupt flag

        // ADC setup ===================================================================
        // trigger on timer3 match
        CloseADC10();	// ensure the ADC is off before setting the configuration

	// define  setup parameters for OpenADC10
	// Turn module on | ouput in integer | trigger mode auto | enable autosample
        // ADC_CLK_AUTO -- Internal counter ends sampling and starts conversion (Auto convert)
        // ADC_CLK_TMR -- triggered off timer3 match
        // ADC_AUTO_SAMPLING_ON -- Sampling begins immediately after last conversion completes; SAMP bit is automatically set
        // ADC_AUTO_SAMPLING_OFF -- Sampling begins with AcquireADC10();
        #define PARAM1  ADC_FORMAT_INTG32 | ADC_CLK_TMR | ADC_AUTO_SAMPLING_ON //ADC_CLK_TMR ADC_CLK_AUTO

	// define setup parameters for OpenADC10
	// ADC ref external  | disable offset test | disable scan mode | do 1 sample | use single buf | alternate mode off
	#define PARAM2  ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | ADC_SCAN_OFF | ADC_SAMPLES_PER_INT_1 | ADC_ALT_BUF_OFF | ADC_ALT_INPUT_OFF
        //
	// Define setup parameters for OpenADC10
        // use peripherial bus clock | set sample time | set ADC clock divider
        // ADC_CONV_CLK_Tcy2 means divide CLK_PB by 2 (max speed)
        // ADC_SAMPLE_TIME_5 seems to work with a source resistance < 1kohm
        // At PB clock 30 MHz, divide by two for ADC_CONV_CLK gives 66 nSec
        #define PARAM3 ADC_CONV_CLK_PB | ADC_SAMPLE_TIME_5 | ADC_CONV_CLK_Tcy2 //ADC_SAMPLE_TIME_15| ADC_CONV_CLK_Tcy2

	// define setup parameters for OpenADC10
	// set AN0 and  as analog inputs
	#define PARAM4	ENABLE_AN0_ANA

	// define setup parameters for OpenADC10
	// do not assign channels to scan
	#define PARAM5	SKIP_SCAN_ALL

	// use ground as neg ref for A | use AN0 for input A
	// configure to sample AN4
	SetChanADC10( ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN0 ); // configure to sample AN0
	OpenADC10( PARAM1, PARAM2, PARAM3, PARAM4, PARAM5 ); // configure ADC using the parameters defined above

	EnableADC10(); // Enable the ADC

        //=== DMA Channel 0 transfer ADC data to array v_in ================================
        // Open DMA Chan1 for later use sending video to TV
        #define DMAchan0 0
	DmaChnOpen(DMAchan0, DMApri0, DMA_OPEN_DEFAULT);
        DmaChnSetTxfer(DMAchan0, (void*)&ADC1BUF0, (void*)v_in, 4, nSamp*4, 4); //512 32-bit integers
        DmaChnSetEventControl(DMAchan0, DMA_EV_START_IRQ(28)); // 28 is ADC done


        // Compuare match setup for SYNC ===============================================
        //Set up compare match unit to produce sync pulses
        // 5 uSec low
        // or 63.5-5 = 58.5 microSec (2340 ticks) low
        // pulse duration will be controlled in Timer2 ISR
        // #define OpenOC2( config, value1, value2) ( OC2RS = (value1), OC2R = (value2), OC2CON = (config) )
        OpenOC2(OC_ON | OC_TIMER2_SRC | OC_CONTINUE_PULSE, line_cycles-1, us_5_cycles);
        // OC2 is PPS group 2, map to RPB5 (pin 14)
        PPSOutput(2, RPB5, OC2);

        // Compare match OC3 setup for video delay =====================================
        // Compare unit for video timing, to delay DMA until end of backporch
        // using the interrupt flag to trigger the first DMA,
        // then use the ISR to change the DMA control to SPI
        // #define OpenOC2( config, value1, value2) ( OC2RS = (value1), OC2R = (value2), OC2CON = (config) )
        // Pulse needs to be TWO cycles long
        OpenOC3(OC_ON | OC_TIMER2_SRC | OC_CONTINUE_PULSE , us_10_cycles+2, us_10_cycles); //
        // turn on ISR so that DMA can covert to SPI control
        ConfigIntOC3(OC_INT_PRIOR_1 | OC_INT_ON); //3 //  
        mOC3ClearIntFlag(); // and clear the interrupt flag

        // SPI configure ==============================================================
        // SCK1 is pin 25 RB14 -- not used here
        // SDO1 is PPS group 2, map to RPA1 (pin 3)
        // SDI1 is PPS group 2, map to RPB8 (pin 17) -- not used here
        // SS1 input is PPS group 1, map to RPB7 (pin 16) for framing -- not used here
        // specify PPS group, signal, logical pin name
        // PPSInput (1, SS1, RPB7);
        PPSOutput(2, RPA1, SDO1);
        // set up i/o
        mPORTBSetPinsDigitalOut(BIT_0 | BIT_1 | BIT_5);
        mPORTBSetBits(BIT_0);

        // divide Fpb by spiClkDiv, configure the I/O ports.
        // 32 bit transfer
         SpiChnOpen(spiChn, SPI_OPEN_ON | SPI_OPEN_MODE32 | SPI_OPEN_MSTEN , spiClkDiv ) ;

        //=== DMA Channel 1 for TV signal ==========================================
        // Open DMA Chan1 for later use sending video to TV
	DmaChnOpen(DMAchn1, DMApri0, DMA_OPEN_DEFAULT);

        // timer 4 setup for profiling code ===========================================
        // Set up timer2 on,  interrupts, internal clock, prescalar 1, compare-value
        // This timer generates the time base for each horizontal video line
        OpenTimer45(T45_ON | T45_SOURCE_INT | T45_PS_1_1, 0xffffffff);

        // === turn it all on ====================================================
        // setup system wide interrupts  ///
        INTEnableSystemMultiVectoredInt();

        //=== now the application ===============================================
        // Draw the screen boundaries
        video_line(left,top+10, right,top+10, 1); // Title line
        video_line(left,top, right,top, 1); // top
        video_line(right,top, right,bottom, 1); // right
        video_line(right,bottom, left,bottom, 1); // bottom
        video_line(left,top, left,bottom ,1); // left

        // Draw a title
        //Print "CORNELL" message
        video_string(30,top+2,cu1);

        // init voltage display
        for (sample_number=0; sample_number<nSamp; sample_number++){
            v_in[sample_number] = 0 ; //+ (rand()>>10) ; //int2fix(ball%3);ADC1BUF0
            v_disp[sample_number] = 180-v_in[sample_number] ;
            //video_pt(sample_number,  v_disp[sample_number],1);
        }

        // === init FFT data =====================================
        // one cycle sine table
        //  required for FFT
        int ii;
        for ( ii=0; ii<N_WAVE; ii++){
             Sinewave[ii] = float2fix(sin(6.283*((float)ii)/N_WAVE));
             window[ii] = float2fix(0.5*(1.0-cos(6.283*((float)ii)/(N_WAVE-1)))) ;
             //window[ii] = float2fix(1.0) ;
        }
        // === ============= =====================================
        time = time_tick_60_hz ;
        // wait to start
        //while(time+600 > time_tick_60_hz){};
        //time = time_tick_60_hz ;
        int trig_1, trig_2, max_ADC, min_ADC, mid_ADC, trig_time;
        // init the trigger state machine
         min_ADC = 500; max_ADC = 500; trig_1 = 50;  trig_2 = 1000;
         // frequency calc
         int i, st, ed;
         char freq_str[16] ;

        #define wait10 asm("nop"); asm("nop");asm("nop"); asm("nop"); asm("nop"); asm("nop");asm("nop"); asm("nop");asm("nop"); asm("nop");

	while(1)
	{
            //mPORTBToggleBits(BIT_1); // for profiling execution time
            mid_ADC = (min_ADC + max_ADC)/2 ;
            trig_2 = 0; trig_1 = 100000; // Always traverse the while
            trig_time = time_tick_60_hz ;
            while(!(trig_2>mid_ADC && trig_1<=mid_ADC)) {
                
                // wait for ADC done
                 while(!(IFS1 & 0x10)) {};
                // first conversion
                // Read the value
                trig_1 = ADC1BUF0 ; //IFS1<1>

                // wait for next one
                while(!(IFS1 & 0x10)) {};
                // second conversion
                // Read the value
                trig_2 = ADC1BUF0 ;

                // do not wait longer than 1 second
                if (time_tick_60_hz > trig_time+60) break;
            }

            // start the ADC burst after the trigger is good
            DmaChnEnable(DMAchan0);
            // do not update the screen more than 60/sec
             // wait until the next 1/60 second
            //while(time == time_tick_60_hz){};
            #define Chn_busy 0x80
            while((time_tick_60_hz <= time+1) | (DCH0CON & Chn_busy) ){};
            // use ISR time-tick to update time stamp
            time = time_tick_60_hz ;

            // draw the trace
            /*
             for (sample_number=1; sample_number<nSamp-1; sample_number++){
                //video_pt(sample_number,  100-v_in[sample_number],1);
                video_pt_m(sample_number,  v_disp[sample_number], 0);  // erase
                //video_line(sample_number,  v_disp[sample_number], sample_number+1,  v_disp[sample_number+1],0);
                // v_disp[sample_number] =  100-(ADC1BUF0>>5) ;
                 v_disp[sample_number] =  170-(v_in[sample_number]>>3) ; //3
                video_pt_m(sample_number,   v_disp[sample_number],1);
                //video_line(sample_number,  v_disp[sample_number], sample_number+1,  v_disp[sample_number+1],1);
                if (v_in[sample_number]<min_ADC) min_ADC=v_in[sample_number] ;
                if (v_in[sample_number]>max_ADC) max_ADC=v_in[sample_number] ;
            }
*/
            // update the trigger condition
            st=0; ed=0;
            for (sample_number=0; sample_number<nSamp; sample_number++){
                if (v_in[sample_number]<mid_ADC && v_in[sample_number+1]>=mid_ADC && st==0 ) st = sample_number;
                if (v_in[sample_number]<mid_ADC && v_in[sample_number+1]>=mid_ADC && st>0 && sample_number>st+2 && ed==0) ed = sample_number;
            }
            
            start_FFT = ReadTimer45();
            // compute and display fft
             for (sample_number=0; sample_number<nSamp-1; sample_number++){
                 fr[sample_number] = multfix(v_in[sample_number]<<4, window[sample_number]); // 10 bit to fixed pt fraction <<5 for signal gen
                 fi[sample_number] = 0 ;
            }
                     
            FFTfix(fr, fi, LOG2_N_WAVE) ;

             // get magnitude
             max_fr = 0 ; // peak magnitude for freq calc
             max_fr_index = 0 ;
             fr[1] = 0 ; // dc scatter
             // comput aprox magnutude, then find the peak
             for (sample_number=2; sample_number<nSamp/2; sample_number++){
                 abs_fr = abs(fr[sample_number]) ; //>>9
                 abs_fi = abs(fi[sample_number]) ;
                 // reuse fr to hold magnitude
                 fr[sample_number] = max(abs_fr, abs_fi) + multfix(min(abs_fr,abs_fi), 26214 ); //0.4 is 26214 in 16:16 fixed
                // find the peak
                 if (fr[sample_number]>max_fr) {
                     max_fr = fr[sample_number] ;
                     max_fr_index = sample_number;
                 }
                 // reuse fr to hold log magnitude
                 // form 16:16 fixed point approx_log
                 // shifting finds most significant bit
                 // then make approxlog  = ly + (fr-y)./(y) + 0.043;
                 // BUT need to convert to fixed by shifting left, then subtracting 16 at the
                 // end  (see also matlab pgm comment at the end of this program)
                 sx = fr[sample_number];
                 y=1; ly=0;
                 while(sx>1) {
                    y=y*2; ly=ly+1; sx=sx>>1;
                 }
                 fr[sample_number] = ((ly)<<16) + fixed_043 + (int)(((unsigned long long)(fr[sample_number]-y)<<16)>>ly)-fixed_16 ;
                 // bound the noise at low amp
                 if(fr[sample_number]<fixed_cutoff) fr[sample_number] = fixed_cutoff;
            }

             sprintf(freq_str, "cycles FFT = %d      ",ReadTimer45() - start_FFT);
            video_string(10,12, freq_str);

             // erase and plot
            for (sample_number=2; sample_number<nSamp/2-1; sample_number++){
                video_pt_m(sample_number,  v_disp[sample_number], 0);  // erase
                 v_disp[sample_number] =  -(fr[sample_number]>>13) ; //>>3 for signal gen and 180
                video_pt_m(sample_number,   v_disp[sample_number],1);

            }
            // once per second, print the frequency
            if ((time_tick_60_hz % 3) == 0){
                sprintf(freq_str, "PEAK FREQ = %d      ", max_fr_index * sample_rate/(nSamp));
                video_string(10,bottom-10, freq_str);
            }

            mPORTBToggleBits(BIT_1);
 	} // while(1)

} // main

//////////////////////////////////////////////
///////// matlab program to gen approx log
//////////////////////////////////////////////

/*% log approx
clear all; clc;

one = 2^16; % in 16:16 fixed pt
offset043 = (0.043*one);
offset086 = (0.086*one);
scale34 = (0.1*one);

x = 2000:1000:500000 ; % int equvalent of fixed
xx = x/one; % the float equivalent of the fixed

for i=1:length(x)
    %find number of non zero bits, as if an 32-bit integer
    sx = x(i);
    y=1; ly=0;
    while(sx>1)
        y=y*2; ly=ly+1; sx=bitshift(sx, -1);
    end

    truelog(i) = log2(xx(i));

    % following approx +/- 0.04
    %approxlog(i) = bitsll(ly-16,16) + bitsra(bitsll(int64(x(i)-y),16), ly) + offset043;
    approxlog(i) = bitsll(ly,16) + bitsra(bitsll(int64(x(i)-y),16), ly) + offset043 - bitsll(16,16);

    % following approx +/- 0.007
    %approxlog(i) = ly + (x(i)-y)./(y) - 0.34*((x(i)-(y+y/2))./(y)).^2 + 0.086;
%     approxlog(i) = bitsll(ly-16,16) + bitsra(bitsll(int64(x(i)-y),16), ly) + ...
%         bitsra( scale34*bitsra(bitsra(bitsll(int64(x(i)-y-y/2),16), ly) .^2, 16),16) + ...
%         offset086;

    %approxlog(i) = (ly-16) + bitsra((x(i)-y),ly) + offset; %bitsra((x(i)-y),(ly)) + offset;
    float_approxlog(i) =  double(approxlog(i))/one ;
end
figure(1); clf
plot(x, float_approxlog-truelog)

%figure(2); clf
%plot(x, approxlog./truelog)
%plot(xx, 2*float_approxlog./(approxlog+truelog))

figure(3); clf
plot(x,truelog)
hold on
plot(x,float_approxlog,'r')

% in C -- input MUST be positive
% find high order bit and save position=pn and nn=number
% form pn + (input-nn)>>pn + 0.045
% form pn + (input-nn)>>pn + 0.045*/
