/*
************************************************************************************
* This driver assumes SPI1
************************************************************************************
*/

#include "vs1003b.h"

#define SPIGet() SPI1BUF

static void SPIPut(BYTE data);
static void vs1003RegWrite(BYTE addr, WORD data);
static WORD vs1003RegRead(BYTE addr);

/*
******************************************************************************************
* Description: 	SPIPut(BYTE data)
*				This is the low level SPI write funtion, and this is hardware specific.
*				SPI1 module has been assumed
* Arguments:	(BYTE data)		data byte to send
* Remarks:	
******************************************************************************************
*/
static void SPIPut(BYTE data)
{
	    // Wait for free buffer
	    while(!SPI1STATbits.SPITBE);
	    SPI1BUF = data;
	
	    // Wait for data byte
	    while(!SPI1STATbits.SPIRBF);
}


/*
******************************************************************************************
* Description: 	vs1003RegWrite(BYTE addr, WORD data)
*				This is the basic register-write function. On VS1003 data sheet,
*				it refers to the Serial Command Interface (SCI).
*				Arguments consist of an instruction byte 0x02(VS1003_WRITE),
*				address byte (BYTE addr), and one 16-bit data word (WORD data).
* Arguments:	(BYTE addr)		address of the command
*				(WORD data)		data to send for the command register
* Remarks:		It is necessary to check for the DREQ pin high again since VS1003
*				sets DREQ low after each SCI operation. The duration depends on the 
*				operation. It is NOT allowed to start a new SCI/SDI operation before
*				DREQ is high again.
******************************************************************************************
*/
static void vs1003RegWrite(BYTE addr, WORD data)
{
	vs1003CSLow();

	SPIPut(VS1003_WRITE);
	SPIGet();

	SPIPut(addr);
	SPIGet();

	SPIPut(((WORD_VAL) data).v[1]);
	SPIGet();

	SPIPut(((WORD_VAL) data).v[0]);
	SPIGet();
	
	vs1003CSHigh();

	while(VS1003_DREQ==0);
}

/*
******************************************************************************************
* Description: 	vs1003RegRead(BYTE addr)
*				This is the basic register-read function. On VS1003 data sheet,
*				it refers to the Serial Command Interface (SCI).
*				Arguments consist of an instruction byte 0x03 (VS1003_READ),
*				and an address byte (BYTE addr).
* Arguments:	(BYTE addr)		address of the command
* Remarks:		For some unkown reason, the private temp variable 'data' should be
*				of the structure type 'WORD_VAL' instead of WORD.
******************************************************************************************
*/
static WORD vs1003RegRead(BYTE addr)
{
	WORD_VAL temp;

	vs1003CSLow();

	SPIPut(VS1003_READ);
	SPIGet();
	
	SPIPut(addr);
	SPIGet();

	SPIPut(0);
	temp.v[1] = SPIGet();
	SPIPut(0);
	temp.v[0] = SPIGet();

	vs1003CSHigh();

	while(VS1003_DREQ==0);	

	return (temp.Val);
}


/*
*****************************************************************************************************
* Description: 	vs1003LLInit(void)
*				Low level SPI initialization. This can be the spi configuration 
*				initialization if SPI bus is to be shared between various SPI slaves
* Dependency:	none
* Arguments:	none
*
* Remarks:		This function is hardware specific.
* The sampling freq is a function of Fpb, which can be as high as 80MHz for a PIC32MX device
* SPI clock rate for vs1003b max. at 2CLKI cycles. Although the timing is derived from internal
* clock CLKI, the system always starts up in 1.0x mode, therefore CLKI=XTALI=12.288MHz on
* PIC32_SSD1963_STK hardware prior to CLOCKF register updated. 
* This is equivalent to a high pulse timing of 163ns leading to 
* a max. SCK1 rate of merely 6MHz something. Thus, a "high" SPI1BRG divider has been chosen to 
* make sure SCK1 clock with a period of 440ns is there to match vs1003b prior to internal clock init
*****************************************************************************************************
*/
void vs1003LLInit(void)
{
	//Low level SPI initialization
	SPI1STAT = 0;
	SPI1CON = 0;
	SPI1BRG = 0;
	SPI1CONbits.MSTEN = 1;	//Master mode
	SPI1CONbits.CKP = 0;	//idle state for clock is a low level, active state is high
	SPI1CONbits.CKE = 1;	//SDO changes from active to idle (i.e. high to low)
	SPI1CONbits.SMP = 0;	//input data sampled at middle of data output line
	SPI1BRG = 15;			//SCK clock at period ~440ns with Fpb = 72MHz, i.e. SCK1 = 2.25MHz
	SPI1CONbits.ON = 1;		
							

    // Set IOs directions for VS1003B SPI
    VS1003_CS_LAT = 1;
    VS1003_CS_TRIS	= 0;

	VS1003_ADPCFG_xDCS = 1;	//digital operation for AN1/RB1
	VS1003_xDCS_LAT = 1;
	VS1003_xDCS_TRIS = 0;

	VS1003_ADPCFG_DREQ = 1;	//digital operation for AN0/RB0
	VS1003_DREQ_TRIS = 1;

    VS1003_SCK_TRIS = 0;
    VS1003_SDO_TRIS = 0;
    VS1003_SDI_TRIS = 1;
	VS1003_RST_LAT  = 1;
	VS1003_RST_TRIS = 0;

	//vs1003RegWrite(SCI_CLOCKF, 0x9800);	//11000 XTALI clock time for DREQ required
	//vs1003RegWrite(SCI_AUDATA, 0xBB81);	
	//vs1003RegWrite(SCI_BASS, 0x0055);
}

/*
******************************************************************************************
* Description: 	vs1003HWReset(void)
*				Hardware reset fot VS1003 by bringing xRESET pin low and then high for
*				normal operation
* Dependency:	Software delay DelayMs()
* Arguments:	none
* Remarks:		vs1003 runs at XTALI since no PLL enabled yet.
******************************************************************************************
*/
void vs1003HWReset(void)
{
	//hardware reset vs1003B
	VS1003_RST_LAT = 0;
	DelayMs(1);
	VS1003_RST_LAT = 1;
	//after a hardware reset, DREQ will stay down for at least 16600 clock cycles
	//At crystal freq 12.288MHz, this means 1.35ms
	while(VS1003_DREQ==0);	

}

void vs1003SWReset(void)
{
}

void vs1003PowerOn(BOOL state)
{

}

/*
******************************************************************************************
* Description: 	vs1003SineTest(BOOL sw, WORD freq)
*				
* Dependency:	Sine Test in compliance with section 9.8.1 of 
*				vs1003's datasheet(Version 1.04 2009-02-03).
*				
* Arguments:	BOOL sw		1 to enable sine test
*							0 to disable sine test. WORD freq doesn't matter here
*				WORD freq	frequency of the sine wave output.
*				The frequency does not span a continuous spectrum due to the fact that,
*				the output is obtained by the formula Fs x S/128 with Fs = 22050Hz
*				which has been hard-coded in this function with S being a 5-bit number.
*				Please refer to section 9.8.1 on datasheet for details 
*				Therefore the max freq to use in this function = 22050Hz * 31/128 = 5,340Hz;
*				wherease, the min. freq = 22050Hz * 1/128 = 172Hz
*
*				When BOOL sw = 0, the sine test exit.
*
* Remarks:		vs1003HWReset(void) executed in this function. Therefore all control
*				registers and internal states are set to the initial values.
*				vs1003 runs at XTALI since no PLL enabled yet.
******************************************************************************************
*/
void vs1003SineTest(BOOL sw, WORD freq)
{
	BYTE sine_skip_speed;

	vs1003HWReset();

	vs1003RegWrite(SCI_MODE, 0x0820);	//set SM_TESTS
	
	sine_skip_speed = freq/172;			//22050Hz/128 = 172!

	if(sine_skip_speed>31) 
		sine_skip_speed = 31;
	else if (sine_skip_speed==0)
		sine_skip_speed = 1;

	vs1003xDCSLow();

	if(sw==1){
		SPIPut(0x53); SPIGet();
		SPIPut(0xEF); SPIGet();
		SPIPut(0x6E); SPIGet();
		SPIPut(0x60|sine_skip_speed); SPIGet();	//A 22050Hz sampling rate hardcoded here as 0x60
		SPIPut(0); SPIGet();
		SPIPut(0); SPIGet();
		SPIPut(0); SPIGet();
		SPIPut(0); SPIGet();
	} else {
		SPIPut(0x45); SPIGet();
		SPIPut(0x78); SPIGet();
		SPIPut(0x69); SPIGet();
		SPIPut(0x74); SPIGet();
		SPIPut(0); SPIGet();
		SPIPut(0); SPIGet();
		SPIPut(0); SPIGet();
		SPIPut(0); SPIGet();
	}

	vs1003xDCSHigh();	
}

/*
******************************************************************************************
* Description: 	vs1003MemoryTest(void)
*				This test complies with the Memory Test procedure as stipulated in 9.8.3
*				on vs1003b datasheet (Version 1.04 2009-02-03).
* Arguments:	(none)
* Return:		If "all ok", the return value should be 0x807f with msb first	
*				Memory test overwrite the current contents of the RAM memories
*				vs1003HWReset(void) executed in this function. Therefore all control
*				registers and internal states are set to the initial values.
*				vs1003 runs at XTALI since no PLL enabled yet.
******************************************************************************************
*/
WORD	vs1003MemoryTest(void)
{
	WORD temp;

	vs1003HWReset();

	vs1003RegWrite(SCI_MODE, 0x0820);	//set SM_TESTS

	vs1003xDCSLow();
	SPIPut(0x4D); SPIGet();
	SPIPut(0xEA); SPIGet();
	SPIPut(0x6D); SPIGet();
	SPIPut(0x54); SPIGet();
	SPIPut(0); SPIGet();
	SPIPut(0); SPIGet();
	SPIPut(0); SPIGet();
	SPIPut(0); SPIGet();
	vs1003xDCSHigh();

	DelayMs(50);	//a long delay as long as 500000 clock cycles required!
					//assumed the min. clock ferq as CLKI = 1/12.288us
					// A 500,000 clock cycles eq. 40,690us
	temp = vs1003RegRead(SCI_HDAT0);	//should be 0x807f af all ok

	return (temp);
}




