#include <drivers/adi_dev.h>			// device manager includes
#include "ad7477_test1.h"

////////////////////////////////////////////////////////////////////////////////
//
// Test program for AD7477 device driver.
//
// This program sets up the device driver to extract sampled data to a 
// circular DMA buffer which interrupts at the completion of each sub-buffer, at
// which point the sub-buffer is copied aside to a larger buffer. Once the 
// latter buffer is full, a FFT analysis is performed. The Deferred Callback
// Manager is used to defer the callbacks. This means (in theory) that 
// subsequent sub-buffers are filled before the current sub-buffer is copied
// to the analysis buffer.
// 
// The sample rate is set by adjusting the following macro:
//
#define SAMPLE_RATE 1000000  // 1 MSPS (Max)
//
// Note: this is only a requested value. The AD7477 driver will
//       modify this to give the true achievable value as dictated
//		 by the peripheral clock
static u32 true_sample_rate = SAMPLE_RATE;
//
// and the Peripheral clock (SCLK) frequency is defined by:
//
#define FSCLK_VALUE 120000000 // Hz
//
// The Circular DMA buffer is defined by the following macros, the first is the 
// overall size and the second is the number of sub-buffers to use
//
#define ADC_BUFFER_SIZE 512
#define NUM_SUB_BUFFERS 2
//
// Therefore the size of each sub-buffer is
#define SUB_BUFFER_SIZE (ADC_BUFFER_SIZE/NUM_SUB_BUFFERS)
//
// The size of the sample buffer (used for FFT analysis is typically bigger than
// the DMA buffer. The multiple is defined by the macro:
//
#define SAMPLE_SIZE_MULTIPLE 2
// giving the size of the sample as
#define SAMPLE_SIZE (SAMPLE_SIZE_MULTIPLE*ADC_BUFFER_SIZE)

////////////////////////////////////////////////////////////////////////////////



////////////////////////////////////////////////////////////////////////////////
// Data buffers
//
// The DMA buffer (make sure its in L1 data memory)
static short section("constdata") adc_buffer_data[ADC_BUFFER_SIZE];
// and its position marker 
static int bpos=0; 

// The Device driver buffer type 
static ADI_DEV_BUFFER adc_buffer;
// and data area for critical regions:
static ADI_INT_IMASK  imask_storage;

// The sampling buffer to be populated sample[i] = (fract16)adc_buffer_data[j]<<3;
static fract16 sample[SAMPLE_SIZE];
// and its position marker 
static int spos=0;  

////////////////////////////////////////////////////////////////////////////////
// System Services Memory requirements:
//
#pragma alignment_region (4)
// One DMA channel
static u8 dmamgr_storage[ADI_DMA_BASE_MEMORY + (ADI_DMA_CHANNEL_MEMORY * 1)];

// Two devices, (AD7477 and underlying SPI driver)
static u8 devmgr_storage[ADI_DEV_BASE_MEMORY + (ADI_DEV_DEVICE_MEMORY * 2)];

// Deferred Callback:
// if a break point is set in the callback routine it is best to use live callbacks
// otherwise the following macro can be commented out to invoke the deferred
// callback manager.
#define USE_LIVE_CALLBACKS

#if !defined(USE_LIVE_CALLBACKS)
//one queue with 5 entries
static u8 dcbmgr_storage[ADI_DCB_QUEUE_SIZE  + (ADI_DCB_ENTRY_SIZE * 5)];
#endif

// Interrupt Manager - 8 secondary handlers
static u8 intmgr_storage[(ADI_INT_SECONDARY_MEMORY * 8)];
#pragma alignment_region_end


////////////////////////////////////////////////////////////////
// Function: 	process_sub_buffer							   /
// Description: To adjust the contents of the src buffer and   /
//              copy the results to the dst buffer.			
////////////////////////////////////////////////////////////////
static void process_sub_buffer(void* dst, void *src, int nwords)
{
	// increase size of fracts by shifting 3 left 
	// this keeps them positive (AD7477 only deals in positive
	// values).
	fract16 *t = (fract16*)dst;
	u16 *s = (u16*)src;
	int i;
	for (i=0;i<nwords;i++)
		*t++ = (fract16)((*s++)<<3);
}

// //////////////////////////////////////////////////////////////////
// Function:	AD7477_callback_function							/	
// Description:	Callback function for AD7477 DMA buffer completion	/	
/////////////////////////////////////////////////////////////////////
static void ad7477_callback_function(void* pHandle, u32 u32Arg, void* pArg)
{
	// Arguments
	ADI_DEV_DEVICE_HANDLE pDevice = (ADI_DEV_DEVICE_HANDLE *)pHandle;
	u32 event = u32Arg;
	u16 *data = (u16*)pArg;

	// adjust and copy DMA buffer to larger array
	process_sub_buffer(
			&sample[spos], 
			&adc_buffer_data[bpos], 
			SUB_BUFFER_SIZE
	);
	
	// increment DMA buffer pointer to next sub-buffer
	bpos += SUB_BUFFER_SIZE;
	
	// and wrap round to start 
	if (bpos==ADC_BUFFER_SIZE)
  		bpos=0;
  		
	// increment sample buffer pointer  		
	spos += SUB_BUFFER_SIZE;

	// After the larger buffer is full, reset the sample position counter
	if (spos==SAMPLE_SIZE)
	{
		// ADD BREAK POINT here to see spectral analysis plot
  		spos=0;
	}
  		
}

// /////////////////////////////////////////////////////////////
// Function:	Init
// Description:	Initialise the AD7477 Device Driver and requiried
//              system services.
//
void Init()
{
	u32 response_count, result, fcclk,fsclk,fvco;
	ADI_DCB_HANDLE DCB_queue_handle;			// DCB manager handle
	ADI_DMA_MANAGER_HANDLE DMA_Manager_handle;  // DMA manager handle
	ADI_DEV_MANAGER_HANDLE devmgr_handle;  		// Device manager handle
	ADI_DEV_DEVICE_HANDLE adc_handle;			// AD7477 device driver handle
	
	// Initialize ezkit utils (includes power management)
	ezInit(1);
	
	// and turn off all LEDs. They will only come on if there is an error in
	// configuring the driver.
	ezTurnOffAllLEDs();
	
	////////////////////////////////////////////////////////////////////////////
	// initialize System Services: 
	
	// DMA Manager
	
	ezErrorCheck( adi_dma_Init(
					dmamgr_storage,
					sizeof(dmamgr_storage),
					&response_count,
					&DMA_Manager_handle,
					NULL)
			  );
	
	// Interrupt Manager 
	
	ezErrorCheck( adi_int_Init(
					intmgr_storage,
					sizeof(intmgr_storage),
					&response_count,
					NULL)
    		 );
        
	// Deferred Callback manager
#if !defined(USE_LIVE_CALLBACKS)
	ezErrorCheck( adi_dcb_Init(
					dcbmgr_storage,
					ADI_DCB_QUEUE_SIZE,
					&response_count,
					NULL)
    		 );
	
	// open a queue of 5 entries
    ezErrorCheck( adi_dcb_Open(
					14,
					(void*)&dcbmgr_storage[ADI_DCB_QUEUE_SIZE],
					(ADI_DCB_ENTRY_SIZE * 5),
					&response_count,
					&DCB_queue_handle)
			);
	
#endif

	// initialize the PLL so that the peripheral clock is as requested
	adi_pwr_SetFreq(0,FSCLK_VALUE,ADI_PWR_DF_NONE);
	
	////////////////////////////////////////////////////////////////////////////
	// 	Initialize the Device Manager
	//
	ezErrorCheck( adi_dev_Init(
                    devmgr_storage,
                    sizeof(devmgr_storage),
                    &response_count,
                    &devmgr_handle,
                    &imask_storage)
             );
	
	////////////////////////////////////////////////////////////////////////////
	// Initialize the AD7477 device driver
	//
	ezErrorCheck( adi_dev_Open(
					devmgr_handle,				// device manager handle
					&ADI_AD7477_EntryPoint,	// AD7477 entry point
					0,							// device number
					NULL,						// client handle
					&adc_handle,				// pointer to location where the device handle will be stored
					ADI_DEV_DIRECTION_INBOUND,	// data direction
					DMA_Manager_handle,			// handle to the DMA manager
#if defined(USE_LIVE_CALLBACKS)
					NULL,
#else
					DCB_queue_handle,			// handle to the callback manager
#endif
					ad7477_callback_function)	// client callback function
			 );
	
	// Configure the driver for 
	// 		SAMPLE_RATE samples per second (returns achievable value)
	//		circular buffer mode
	//		Using PF10 as the Slave Select line (SPISS)
 	ADI_DEV_CMD_VALUE_PAIR config_ad7477[] = {
		{ ADI_AD7477_CMD_SET_SAMPLE_RATE, 		(void*)&true_sample_rate	 },	
		{ ADI_AD7477_CMD_DECLARE_SLAVE_SELECT, (void*)1		     		 },	
		{ ADI_DEV_CMD_SET_DATAFLOW_METHOD,  	(void*)ADI_DEV_MODE_CIRCULAR },
		{ ADI_DEV_CMD_END, 0 }
	};
	ezErrorCheck( adi_dev_Control( adc_handle, ADI_DEV_CMD_TABLE, (void*)config_ad7477 ) );

	// Use a Circular Buffer with a number of sub-buffers
	adc_buffer.Circular.Data 				 	= (void*)adc_buffer_data;
	adc_buffer.Circular.SubBufferCount 		 	= NUM_SUB_BUFFERS;
	adc_buffer.Circular.SubBufferElementCount 	= ADC_BUFFER_SIZE/NUM_SUB_BUFFERS;
	adc_buffer.Circular.ElementWidth 		 	= sizeof(short);
	adc_buffer.Circular.CallbackType			= ADI_DEV_CIRC_SUB_BUFFER;
	
	ezErrorCheck( adi_dev_Read( adc_handle, ADI_DEV_CIRC, &adc_buffer ) );

	// set things in motion
	ezErrorCheck( adi_dev_Control( adc_handle, ADI_DEV_CMD_SET_DATAFLOW, (void*)TRUE ) );
	
	// All done, SPI DMA will be up and running and callback fn invoked.	
}

//function prototypes
void Init(void);

// /////////////////////////////////////////////////////////////
// Function:	main
// Description:	Main entry point.
//
void main()
{
	// Initialize the device driver and required system services
	Init();
	
	// event loop
	while(1);
}


