/*
 * File: 
 * DMA glue logic timer2-to-ADC-to-memory
 * 
 * ---------
 * Bruce Land
 * Target PIC:  PIC32MX250F128B
 */
// all peripheral library includes
#define _SUPPRESS_PLIB_WARNING 
#include <plib.h>
// serial interface
#include <stdio.h>
// sine function
#include <math.h>

/////////////////////////////////////////////////////////
#pragma config FNOSC = FRCPLL, POSCMOD = OFF
//#pragma config FNOSC = PRIPLL, POSCMOD = EC, OSCIOFNC = OFF
#pragma config FPLLIDIV = DIV_2, FPLLMUL = MUL_20, FPLLODIV = DIV_2  //40 MHz
#pragma config FPBDIV = DIV_1 // PB 40 MHz
// turn off alternative functions for port pins B.4 and B.5
#pragma config JTAGEN = OFF, DEBUG = OFF
#pragma config FSOSCEN = OFF

// ==============
// SPI DAC: A-channel, 1x, active
#define DAC_config_chan_A 0b0011000000000000

// ==============
//clearing bit 1 starts ADC (write to AD1CON1CLR)
char start_ADC = 0b00000010  ; //AD1CON1CLR mask
// array to hold DMA output from ADC
short v_in[256];
// SPI transport source
short DAC_data;


// === Main  ======================================================
void main(void) {
 
    SYSTEMConfig(40000000, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);
 
    /* Blocks:
     * block 0 -- wait for timer2 then trigger ADC, MANUALLY by DMA copy
     * block 1 -- wait for ADC done copy 256 conversions to memory 
      */
    
    // === DMA =====================
   
    // DMA0 writes ADC start bit on timer2-match flag 
    //-- writing a zero to SAMP bit ends sampling and starts conversion
    //-- writing to the AD1CON1CLR register clears the bit mask stored in start_ADC
    //-- AD1CON1CLR is one of three write-only shadow registers to AD1CON1 (SET, CLR, INV)
    //-- The ADC setup below sets AUTOSAMPLE ON, so SAMP bit is auto-set at end of conversion
    // DMA0 is in AUTO mode to keep repeating whenver there is a timer event
    DmaChnOpen(0, 0, DMA_OPEN_AUTO);
    DmaChnSetTxfer(0, &start_ADC, (void*)&AD1CON1CLR, 1, 1, 1);
    DmaChnSetEventControl(0, DMA_EV_START_IRQ(_TIMER_2_IRQ));
    DmaChnEnable(0);
    // DMA0 execute interval source
    // slow enough to test using potentiometer input to ADC
    OpenTimer2(T2_ON | T2_SOURCE_INT | T2_PS_1_8, 40000); //
    
    // DMA1 writes ADC to memory on ADC-complete flag
    // Transfers 256 shorts for every ENABLE from main
    // DMA1 is opened in DEFAULT mode so that MAIN can start and sync with DMA
    DmaChnOpen(1, 0, DMA_OPEN_DEFAULT);
    DmaChnSetTxfer(1, (void*)&ADC1BUF0, (void*)v_in, 2, 512, 2); //256 16-bit integers);
    DmaChnSetEventControl(1, DMA_EV_START_IRQ(_ADC_IRQ)); 
    
    // === set up i/o port pins  =============
    ANSELA =0; // turn off analog on A
    ANSELB =0; // turn off analog on B
    
    // === led =======
    mPORTASetBits(BIT_0);		
    mPORTASetPinsDigitalOut(BIT_0);    
    
    // === init the uart2 ===================
    // to show test results 
    // The RX pin must be one of the Group 2 input pins
    // RPA1, RPB1, RPB5, RPB8, RPB11
    PPSInput (2, U2RX, RPA1); //Assign U2RX to pin RPA1 -- 
    // The TX pin must be one of the Group 4 output pins
    // RPA3, RPB0, RPB9, RPB10, RPB14 
    PPSOutput(4, RPB10, U2TX); //Assign U2TX to pin RPB10 -- 
    #define BAUDRATE 115200
    UARTConfigure(UART2, UART_ENABLE_PINS_TX_RX_ONLY);
    UARTSetLineControl(UART2, UART_DATA_SIZE_8_BITS | UART_PARITY_NONE | UART_STOP_BITS_1);
    UARTSetDataRate(UART2, 40000000, BAUDRATE);
    UARTEnable(UART2, UART_ENABLE_FLAGS(UART_PERIPHERAL | UART_RX | UART_TX));

    // === DAC SPI ===============
    // open the SPI channel
    // clk divider set to 4 for 10 MHz
    SpiChnOpen(SPI_CHANNEL2, SPI_OPEN_ON | SPI_OPEN_MODE16 | SPI_OPEN_MSTEN | SPI_OPEN_CKE_REV , 4);
    // SDO2 (MOSI) is in PPS output group 2, could be connected to RB5 which is pin 14
    PPSOutput(2, RPB5, SDO2);
    mPORTBSetBits(BIT_4);		// Set SPI chip select
    mPORTBSetPinsDigitalOut(BIT_4);    //Set B port as output
    
    // 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 T3
        // ADC_CLK_MANUAL -- triggered by clearing SAMP bit in AD1CON1
        // ADC_AUTO_SAMPLING_ON -- Sampling begins immediately after last conversion completes; SAMP bit is automatically set
        // ADC_AUTO_SAMPLING_OFF -- Sampling begins with AcquireADC10();
        // ADC_FORMAT_FRACT16 -- 10 bits aligned so top bit is bit-15
        // ADC_FORMAT_INTG16 -- 10 bits allgned so top bit is bit-9
        #define PARAM1  ADC_AUTO_SAMPLING_ON | ADC_CLK_MANUAL | ADC_FORMAT_FRACT16

	// 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_AN11_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_AN11 ); // configure to sample AN0
	OpenADC10( PARAM1, PARAM2, PARAM3, PARAM4, PARAM5 ); // configure ADC using the parameters defined above

	EnableADC10(); // Enable the ADC
    
    // === MAIN loop ====================
    // mark start for debugging
    printf( "Start:\n\r");
   
     while(1){   
         
        // enable the DMA to memory after the SPI transfer (below) is complete
        DmaChnEnable(1);
        
        // heartbeat LED for debugging
        mPORTAToggleBits(BIT_0);

        // ADC transfer done
        // waits for DMA1 to become un-enabled at end of memory transfer
         #define Chn_busy 0x80
        while( (DCH1CON & Chn_busy) ){};
        
        // print for testing
        printf("%d \n\r", v_in[0]);
        
        // write 256 values to spi2
        int i;
        for(i=0; i<256; i++) {
            // CS low to start transaction
            mPORTBClearBits(BIT_4); // start transaction
            // initiate SPI transfer (assumes 16-bit fractional ADC format)
            DAC_data = v_in[i]>>4;
           WriteSPI2(DAC_config_chan_A | DAC_data);       
           // test for done
           while (SPI2STATbits.SPIBUSY){}; // wait for end of transaction
           // CS high
           mPORTBSetBits(BIT_4); // end transaction
        }
        
    } // while(1)
  } // main

// === end  ======================================================

