/** \file maximDeviceSpecificUtilities.c ****************************************
 *
 *             Project: Petaluma (MAXREFDES30#)
 *            Filename: maximDeviceSpecificUtilities.c
 *         Description: This module uses the AXI_MAX11046 custom ip core Ver 1.00a to
 *                      read ADC data from the Petaluma MAXREFDES30#.  These
 *                      low level functions could be cut/pasted into the user's
 *                      application as a starting point for development of an end
 *                      application.
 *
 *    Revision History:
 *\n                    03-26-13    Rev 01.00    GL    Initial Release
 *
 *  --------------------------------------------------------------------
 *
 *  This code follows the following naming conventions:
 *
 *\n    char                    chPmodValue
 *\n    char (array)            sPmodString[16]
 *\n    float                   fPmodValue
 *\n    int                     nPmodValue
 *\n    int (array)             anPmodValue[16]
 *\n    u16                     uPmodValue
 *\n    u16 (array)             auPmodValue[16]
 *\n    u8                      uchPmodValue
 *\n    u8 (array)              auchPmodBuffer[16]
 *\n    unsigned int            unPmodValue
 *\n    int *                   punPmodValue
 *
 *  ------------------------------------------------------------------------- */
/*
 * Copyright (C) 2012 Maxim Integrated Products, Inc., All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL MAXIM INTEGRATED PRODUCTS BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Maxim Integrated Products
 * shall not be used except as stated in the Maxim Integrated Products
 * Branding Policy.
 *
 * The mere transfer of this software does not imply any licenses
 * of trade secrets, proprietary technology, copyrights, patents,
 * trademarks, maskwork rights, or any other form of intellectual
 * property whatsoever. Maxim Integrated Products retains all ownership rights.
 *
 ***************************************************************************/

#include "xbasic_types.h"
#include "stdio.h"
#include "xscugic.h"
#include "axi_max11046.h"  //AXI_MAX11046 custom ip core driver

#include "utilities.h"

#define INTC_DEVICE_INT_ID 91	//interrupt ID for the AXI_MAX11046 IP core defined in the XPS

static void ReadADCHandler(void *CallBackRef);
int SetupInterruptSystem(XScuGic *IntcInstancePtr);

u32 g_unCount=0;
u16 *g_auSamplesCh0;	//a pointer that stores the sampled data
u16 *g_auSamplesCh1;	//a pointer that stores the sampled data
u16 *g_auSamplesCh2;	//a pointer that stores the sampled data
u16 *g_auSamplesCh3;	//a pointer that stores the sampled data
u16 *g_auSamplesCh4;	//a pointer that stores the sampled data
u16 *g_auSamplesCh5;	//a pointer that stores the sampled data
u16 *g_auSamplesCh6;	//a pointer that stores the sampled data
u16 *g_auSamplesCh7;	//a pointer that stores the sampled data
u32 g_unSampleSize;	//requested sample size
u8 g_uchReadADCHandlerStop=1;	//a status bit that shows whether the sampling is done

u32 start_sampling(u32 unSampleSize, int nSampleRate, u16 *auSamplesCh0, u16 *auSamplesCh1, u16 *auSamplesCh2, u16 *auSamplesCh3, u16 *auSamplesCh4, u16 *auSamplesCh5, u16 *auSamplesCh6, u16 *auSamplesCh7)
/**
* \brief       Receive a block of samples at a constant rate
* \par         Details
*              This function is used to receive a block of samples at a constant
* \n           sampling rate.  The size of the block is defined in u16 *auSampleSize.
* \n		   And the sampling rate is defined in int nSampleRate. The Sampled data
* \n		   will be stored in an array *auSamples
*
* \param[in]   unSampleSize      - Sample size
* \param[in]   nSampleRate       - Sample rate
* \param[out]  *auSamples        - An array
*
* \retval      Number of samples received
*/
{
	u32 unDelay;

	//sampling rate = 100000000/(unDelay+400)

	if(nSampleRate==5)  //sample rate = 250ksps
		unDelay=0;
	else if(nSampleRate==4)  //sample rate = 100ksps
		unDelay=600;
	else if(nSampleRate==3)  //sample rate = 50ksps
		unDelay=1600;
	else if(nSampleRate==2)  //sample rate = 20ksps
		unDelay=4600;
	else if(nSampleRate==1)  //sample rate = 10ksps
		unDelay=9600;
	else if(nSampleRate==0)  //sample rate = 1ksps
		unDelay=99600;

	g_unCount=0;
	g_auSamplesCh0=auSamplesCh0;
	g_auSamplesCh1=auSamplesCh1;
	g_auSamplesCh2=auSamplesCh2;
	g_auSamplesCh3=auSamplesCh3;
	g_auSamplesCh4=auSamplesCh4;
	g_auSamplesCh5=auSamplesCh5;
	g_auSamplesCh6=auSamplesCh6;
	g_auSamplesCh7=auSamplesCh7;
	g_unSampleSize=unSampleSize;

	XScuGic InterruptController;

	/*
	 * ADC configuration
	 * - use external reference
	 * - use offset binary format
	 * - use acquisition mode 1
	 */
	AXI_MAX11046_Config_ADC(XPAR_AXI_MAX11046_0_BASEADDR, 0x01);

	/*
	 * Set up interrupt handler for the AXI_MAX11046 IP Core
	 */
	SetupInterruptSystem(&InterruptController);

	/*
	 * Enable the interrupt for AXI_MAX11046 IP Core
	 */
	XScuGic_Enable(&InterruptController, INTC_DEVICE_INT_ID);

	g_uchReadADCHandlerStop=0;  //this signal goes high when the sampling is done


	/*
	 * Enable AXI_MAX11046 IP core interrupt
	 */
	AXI_MAX11046_Interrupt_Enable(XPAR_AXI_MAX11046_0_BASEADDR);

	/*
	 * Write to the delay register
	 * the sampling rate = 2*(SCLK rate)/(unDelay+48)
	 */
	AXI_MAX11046_Write_Delay_Reg(XPAR_AXI_MAX11046_0_BASEADDR, unDelay);

	/*
	 * Start the ADC conversion at the rate defined above
	 * When a sample is available, AXI_MAX11046 raises an interrupt
	 * the interrupt handler (ReadADCHandler) reads/stores the sampled data
	 *
	 * The AXI_MAX11046 stops sampling when AXI_MAX11046_Stop_Conversion() is called
	 */
	AXI_MAX11046_Start_Conversion(XPAR_AXI_MAX11046_0_BASEADDR);

	/*
	 * wait until the sampling is done
	 */
	while(g_uchReadADCHandlerStop==0);

	/*
	 * return the number of samples collected
	 */
	return g_unCount;
}

void continuous_sampling(int Channel)
/**
* \brief       Continuously reads the ADC and display the data via the HyperTerminal
* \par         Details
*              This function reads the ADC every half of a second and display the data
* \n           via the Hyperterminal.  Press the ESC key to return to the main menu.
*
* \param       None
*
* \retval      None
*/
{
	u16 uSample[8];
	u8 uchInput;

	printf("Press the ESC key to stop sampling\n\n");


	clearOLEDBuffer(g_structureOLED.writeBuffer);

	if(Channel < 8)
		printf("Channel %i\n",Channel);
	else
		printf("Ch 0, Ch 1, Ch 2, Ch 3, Ch 4, Ch 5, Ch 6, Ch 7\n");

	/*
	 * ADC configuration
	 * - use external reference
	 * - use offset binary format
	 * - use acquisition mode 1
	 */
	AXI_MAX11046_Config_ADC(XPAR_AXI_MAX11046_0_BASEADDR, 0x01);

	while(uchInput!=0x1B)
	{
		if(Channel<8)
		{
			AXI_MAX11046_Single_Convert(XPAR_AXI_MAX11046_0_BASEADDR, Channel, uSample);

			printf("%i\n", uSample[0]);

			delay(ABOUT_ONE_SECOND /2);

			if((Xil_In32(XPAR_PS7_UART_1_BASEADDR+0x2C) & 0x02) == 0)
				uchInput = Xil_In32(XPAR_PS7_UART_1_BASEADDR+0x30);	// read the FIFO (only the LSB is valid).
		}
		else
		{
			AXI_MAX11046_Single_Convert(XPAR_AXI_MAX11046_0_BASEADDR, 8, uSample);

			printf("%i, %i, %i, %i, %i, %i, %i, %i\n", uSample[0], uSample[1], uSample[2], uSample[3], uSample[4], uSample[5], uSample[6], uSample[7]);

			delay(ABOUT_ONE_SECOND /2);

			if((Xil_In32(XPAR_PS7_UART_1_BASEADDR+0x2C) & 0x02) == 0)
				uchInput = Xil_In32(XPAR_PS7_UART_1_BASEADDR+0x30);	// read the FIFO (only the LSB is valid).
		}
	}
}

int SetupInterruptSystem(XScuGic *IntcInstancePtr)
/**
* \brief       Setup the interrupt handler
* \par         Details
*              This function connects the interrupt handler to the processor
*
* \param[in]	*IntcInstancePtr	- IntcInstancePtr is the instance of the interrupt controller
*
* \retval		XST_SUCCESS to indicate success, otherwise XST_FAILURE
*/
{
	int nStatus;
	XScuGic_Config *GicConfig;

	GicConfig = XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID);
	if (NULL == GicConfig) {
		return XST_FAILURE;
	}

	nStatus = XScuGic_CfgInitialize(IntcInstancePtr, GicConfig,
						GicConfig->CpuBaseAddress);
	if (nStatus != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Perform a self-test to ensure that the hardware was built
	 * correctly
	 */
	nStatus = XScuGic_SelfTest(IntcInstancePtr);
	if (nStatus != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Connect the interrupt controller interrupt handler to the hardware
	 * interrupt handling logic in the ARM processor.
	 */
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
			(Xil_ExceptionHandler) XScuGic_InterruptHandler,
			IntcInstancePtr);

	/*
	 * Enable interrupts in the ARM
	 */
	Xil_ExceptionEnable();

	/*
	 * Connect a device driver handler that will be called when an
	 * interrupt for the device occurs, the device driver handler performs
	 * the specific interrupt processing for the device
	 */
	nStatus = XScuGic_Connect(IntcInstancePtr, INTC_DEVICE_INT_ID,
			   (Xil_ExceptionHandler)ReadADCHandler,
			   (void *)IntcInstancePtr);

	return nStatus;
}

void ReadADCHandler(void *CallBackRef)
/**
* \brief       Interrupt handler
* \par         Details
*              When the AXI_MAX11046 ip core raises an interrupt, the interrupt handler
*              clears the interrupt and reads the ADC data from the AXI_MAX11046 data register.
*              When enough samples are collected or the Esc key is pressed, the interrupt
*              handler will stop the ADC conversion.
*
* \param[in]	*CallBackRef	- CallbackRef is passed back to the device driver's interrupt
* \n			handler by the XScuGic driver.  It was given to the XScuGic
* \n			driver in the XScuGic_Connect() function call.  It is typically
* \n			a pointer to the device driver instance variable.
*
* \retval		None
*/
{
	u32 nStatus;

	/*
	 * Read the interrupt status register to clear the interrupt
	 */
	nStatus=AXI_MAX11046_Read_Interrupt_Status(XPAR_AXI_MAX11046_0_BASEADDR);

	/*
	 * Read the sampled data and store it in the *g_suSamples array
	 */
	//*(g_auSamples+g_unCount)=AXI_MAX11046_Read_Data(XPAR_AXI_MAX11046_0_BASEADDR);
	g_auSamplesCh0[g_unCount]=AXI_MAX11046_Read_Data(XPAR_AXI_MAX11046_0_BASEADDR,0);
	g_auSamplesCh1[g_unCount]=AXI_MAX11046_Read_Data(XPAR_AXI_MAX11046_0_BASEADDR,1);
	g_auSamplesCh2[g_unCount]=AXI_MAX11046_Read_Data(XPAR_AXI_MAX11046_0_BASEADDR,2);
	g_auSamplesCh3[g_unCount]=AXI_MAX11046_Read_Data(XPAR_AXI_MAX11046_0_BASEADDR,3);
	g_auSamplesCh4[g_unCount]=AXI_MAX11046_Read_Data(XPAR_AXI_MAX11046_0_BASEADDR,4);
	g_auSamplesCh5[g_unCount]=AXI_MAX11046_Read_Data(XPAR_AXI_MAX11046_0_BASEADDR,5);
	g_auSamplesCh6[g_unCount]=AXI_MAX11046_Read_Data(XPAR_AXI_MAX11046_0_BASEADDR,6);
	g_auSamplesCh7[g_unCount]=AXI_MAX11046_Read_Data(XPAR_AXI_MAX11046_0_BASEADDR,7);
	if(g_unCount==5)
		g_unCount=g_unCount;
	g_unCount++;

	/*
	 * Stop sampling when enough samples are collected
	 */
	if(g_unCount==g_unSampleSize)
	{
		/*
		 * stops sampling
		 */
		AXI_MAX11046_Stop_Conversion(XPAR_AXI_MAX11046_0_BASEADDR);
		g_uchReadADCHandlerStop=1;
	}

	/*
	 * End sampling if the ESC key is pressed
	 */
	if((Xil_In32(XPAR_PS7_UART_1_BASEADDR+0x2C) & 0x02) == 0) //if UART Rx buffer is not empty
	{
		u8 uchInput = Xil_In32(XPAR_PS7_UART_1_BASEADDR+0x30);	// read the FIFO (only the LSB is valid).
		if(uchInput==0x1B)
		{
			AXI_MAX11046_Stop_Conversion(XPAR_AXI_MAX11046_0_BASEADDR);
			g_uchReadADCHandlerStop=1;
		}
	}
}
