/*********************************************************************
 *
 *                Microchip USB C18 Firmware Version 1.0
 *
 *********************************************************************
 * FileName:        user.c
 * Dependencies:    See INCLUDES section below
 * Processor:       PIC18
 * Compiler:        C18 2.30.01+
 * Company:         Microchip Technology, Inc.
 *
 * Software License Agreement
 *
 * The software supplied herewith by Microchip Technology Incorporated
 * (the “Company”) for its PICmicro® Microcontroller is intended and
 * supplied to you, the Company’s customer, for use solely and
 * exclusively on Microchip PICmicro Microcontroller products. The
 * software is owned by the Company and/or its supplier, and is
 * protected under applicable copyright laws. All rights are reserved.
 * Any use in violation of the foregoing restrictions may subject the
 * user to criminal sanctions under applicable laws, as well as to
 * civil liability for the breach of the terms and conditions of this
 * license.
 *
 * THIS SOFTWARE IS PROVIDED IN AN “AS IS” CONDITION. NO WARRANTIES,
 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
 * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
 * IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
 * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 *
 * Author               Date        Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Rawin Rojvanit       11/19/04    Original.
 * Brian Schmalz		07/09/06	FW C v 1.0 now sends back PORT A
 *									inputs on every USB packet reception
 * Tom Rowley, W2TER	12/05/06	Replaced User code with input from C#
 *									specifying a target DDS device and frequency
 *									control word which is then serially
 *									bit-banged SPI-like to the device
 *									Will run on either 20Mhz breadboard or
 *									Bit Wacker 
 *									- first device is DDS-60
 *									- added DDS-30
 *						2/17/07		Re orged code layout to accommodate more 
 *									DDS devices in the future
 *									- Added support for AD9854
 *									- added support for AD995x
 * Cash Olsen, KD5SSJ	2/16/07		Added an initialization routine for
 *									AD9850/AD9851 DDS in serial mode. This
 *									corrects a failure to initialize the DDS
 *									properly.
 *									Modified SPIleast() to use wait_10_inst()
 *									to substantially speed up clock and strobe
 *									routines. I did not modify the AD9854
 *									routines because I don't have a system
 *									to test it on but the same modifications
 *									made should work on the other AD9854.
 *	Tom Rowley, w2ter	5/17/07		Re organized code to provide for more 
 *									flexibility in initialization and a 
 *									framework for broader device support
 *  Terry Fox, WB4JFI  5/24/07		fixed 995x ioupdate pin (per Tom, w2ter)
 *  Terry Fox, WB4JFI  5/24/07		added first attempt to set attenuator	
 ********************************************************************/

/** I N C L U D E S **********************************************************/
#include <p18cxxx.h>
#include <usart.h>
#include "system\typedefs.h"
#include "stdio.h"
#include "delays.h"
#include "system\usb\usb.h"

#include "io_cfg.h"             // I/O pin mapping
#include "user\user.h"

/** V A R I A B L E S ********************************************************/
#pragma udata

char input_buffer[64];
char output_buffer[64];
char out_ptr;
char Buffer;


//	initializations for AD 9854 credit to AA0ZZ
char freq_write_cmd = 0x02;
char CR_write_cmd = 0x07;
char CR_byte_0 = 0x10; // Most signicant CR byte (0x1D) (COMP PD)
char CR_byte_1 = 0x20; // Second CR byte (0x1E) (Bypass PLL)
char CR_byte_2 = 0x00; // Third CR byte (0x1F) (No SRC QDAC + External UPDATE)
char CR_byte_3 = 0x40; // Fourth CR byte (0x20) (Bypass INV SINC)
BOOL first_time = TRUE;		// test for initialization stage
BOOL first_AD995x = TRUE;   // test for initialization of AD995x



/** P R I V A T E  P R O T O T Y P E S ***************************************/
void SPIleast(void);		// Serial LSB out
void DDSx0(void);			// DDS30 or DDS60
void SPImost(void);			// Serial MSB out
void AD9854Int(void);		// Startup IQPro
void wait_10_inst(void);	// timimg routine
void AD9850Int(void);		// AD9850/51 Init
void send_9854Freq(void); 	// IQPro update
void AD995xInt(void);		// Init for AD995x
void send_9951Freq(void);	// AD9951 /2 ferq update
void AD9958Int(void);		// Init for AD9958
void send_9958Freq(void);	// AD9958 freq update
void set_atten(void);		// HRF-AT4611 attenuator set

/** D E C L A R A T I O N S **************************************************/
//	Control Pins for AD9854 - IQPro
#define IQ_clk		LATAbits.LATA4		// Serial clk
#define	IQ_ioud		LATAbits.LATA3		// I/O update
#define	IQ_data		LATAbits.LATA2		// Serial data
#define	IQ_ioreset	LATAbits.LATA1		// I/O reset
#define	IQ_mreset	LATAbits.LATA0		// Master Reset

//	Control pins for AD9850/51 = DDS 30/60
#define DDS_clk		LATBbits.LATB6		// Serial clk
#define	DDS_ioud	LATBbits.LATB5		// I/O update
#define	DDS_data	LATBbits.LATB7		// Serial data
  
//	Control pin for AD9951 / 52  / 58
#define AD99_ioud	LATAbits.LATA3		// I/O reset


#define	TPin		LATAbits.LATA5		// Test Pin

//  Control pins for Honeywell Attenuator

#define	ATTEN_data	LATCbits.LATC6		//  HRF-AT4611 serial data pin
#define	ATTEN_clk	LATCbits.LATC0		//  HRF-AT4611 serial clock pin
#define	ATTEN_oe	LATCbits.LATC7		//  HRF-AT4611 OE (Latch actuallly)

#pragma code
void UserInit(void)
{
mInitAllLEDs();

    CMCON = 0x07;
	CVRCON = 0x00;
	ADCON0 = 0x00;
	ADCON1 = 0x0F;
    // Make all of PORTA digital output
    LATA = 0x00;
    TRISA = 0x00;
	// Make all of PORTB an output
	LATB = 0xFF;
	TRISB = 0x00;	

	AD99_ioud = 0;
mLED_2_Off();
	
}//end UserInit

/******************************************************************************
 * Function:        void DDS_Out(void)
 *
 * PreCondition:    None
 *
 * Input:           USB received data 
 *
 * Output:          Serial stream to DDS device
 *
 * Side Effects:    None
 *
 * Overview:        DDS_Out receives a frequency control "word" from the C#
 *					program on the PC and sends it out to the device in a serial
 *
 *
 * Note:            
 *
 *****************************************************************************/
void DDS_Out(void)
{   
	char bytesIn = 0;
	
    // User Application USB tasks
    if((usb_device_state < CONFIGURED_STATE)||(UCONbits.SUSPND==1)) return;

	// Pull in some new data if there is new data to pull in
	bytesIn = getsUSBUSART(input_buffer, 5);

	if (bytesIn > 0)
	{  // Have some USB data to process


      switch ( input_buffer[4] )
		//
		//SWITCH is the heart of the routine. The selection codes ranges are
		// 01 - 3F : Process Freq update for device
		//		1 - AD9850 / DDS 30
		//		2 - AD9851 / DDS 60
		//		3 - AD9854 / IQPro
		//		4 - AD995x
		//		5 - AD9958/9
		// 40 - 7F : Process special non- DDS devices, eg bandpass filters
		// 80 - BF : Initialization sequences 
		//		128 (80H) - Initialize 9850
		//		129 (81H) - Initialize 9850		
		//		130 (82H) - Initialize 9854
		//		131 (83H) - Initialize 995x
		//		132 (84H) - Initialize 9958/9	
      {
         case 1:	// send freq update control seq to DDS30
	    			input_buffer[4]= 0x00;  // set control word
					DDSx0();
            break;

         case 2:	// send freq update control seq to DDS60
					input_buffer[4]= 0x01;   /// set control word
	    			DDSx0();
            break;

         case 3:	// send freq update control seq to AD854

					send_9854Freq();

            break;

         case 4:	// send freq update control seq to AD995x

					send_9951Freq();

            break;
         case 5:	// send freq update control seq to AD9958

					send_9958Freq();

            break;
		case 127:	// set AT4611 attenuator
					set_atten();
			break;
		//
		//		Initialization Selections
		//
         case 128:	// Initialize 9850/51
	    		AD9850Int();
            break;

         case 129:	// Initialize 9851

	    		AD9850Int();
            break;

         case 130:	// Initialize 9854
	    		AD9854Int();
            break;

         case 131:	// Initialize 995x
	    		AD995xInt();
            break;

         case 132:	// Initialize 9858/9
	    		AD9958Int();
            break;

         default:	// send control word to DDS60
	    			DDSx0();
      }// End of witch statement

	}//bytesIN

}//end ProcessIO

void DDSx0(void)
{
// 	Freq word arrived - Send freq word to device serially. LSB
//	Control word is 0 for DDS30 / AD9850
//	Control word is 1 for DDS60 / AD9851

//  DDS_clk		LATBbits.LATB6		// Serial clk
//  DDS_ioud	LATBbits.LATB5		// I/O update
//  DDS_data	LATBbits.LATB7		// Serial data
mLED_2_Toggle();

		DDS_ioud = 0;		//Clear Latch
	 	Buffer = input_buffer[3];
		output_buffer[0] = Buffer;
		//Buffer = 0xFE;
		SPIleast(); 	// Sends data LSB out PortB.7; clocked by PortB.6

	 	Buffer = input_buffer[2];
		output_buffer[1] = Buffer;
		//Buffer = 0x08;	
		SPIleast(); 	// Sends data LSB out PortB.7; clocked by PortB.6

	 	Buffer = input_buffer[1];
		output_buffer[2] = Buffer;
		//Buffer = 0xCD;			 	
		SPIleast(); 	// Sends data LSB out PortB.7; clocked by PortB.6

	 	Buffer = input_buffer[0];	
		output_buffer[3] = Buffer;
		//Buffer = 0x0A;	
		SPIleast(); 	// Sends data LSB out PortB.7; clocked by PortB.6

	
		Buffer = input_buffer[4];             //Control word
		output_buffer[4] = Buffer;	
		//Buffer = 0x01;		
		SPIleast(); 	// Sends data LSB out PortB.7; clocked by PortB.6


		//LATBbits.LATB5 = 1;		//Latch Freq word into DDS
		DDS_ioud = 1;		//Latch Freq word into DDS
 		Delay100TCYx(200);

		//LATBbits.LATB5 = 0;		//Clear Latch
		DDS_ioud = 0;		     	//Clear Latch
	    
		// Send result back to PC
	//	if(mUSBUSARTIsTxTrfReady())
	//       {

	//			mUSBUSARTTxRam((byte*)output_buffer,5); 
	//		}

//mLED_2_Toggle();

}// end of DDSx0

void SPIleast(void)
{
// Send byte of serial control word, LSB first

int i;

  DDS_clk = 0; // CLear clock  
  for(i=0;i<8;i++){ 

  DDS_data =(int)Buffer & 0x01; 
  DDS_clk = 1;  //Clock bit into DDS

	wait_10_inst();
  //Delay100TCYx(10); 
  DDS_clk = 0;

  Buffer >>= 1;
  wait_10_inst();
  //Delay100TCYx(10); 

  } //end of for loop
 
}// end of SPIlike

void SPImost(void)
{
// Send byte of serial control word, MSB first
int i,j;
char mask;

  mask = 0x80;
  IQ_clk = 0; // CLear clock 
  j=1; 			// waste a little time

  for(i=0;i<8;i++){ 
  IQ_data =0;
  if (Buffer & mask) IQ_data = 1; //set output to MSB
  
  j=1;
  IQ_clk = 1;  //Clock bit into DDS

  wait_10_inst();

  IQ_clk = 0;
  
  mask >>= 1;    // left shift byte

  //Delay100TCYx(10); 

  } //end of for loop
 
}// end of SPImost

void AD9850Int(void)
{
//  Send the sequence to set up the 9850/51 for serial updates
mLED_2_Toggle();	
	DDS_clk = 1;	//Clock serial control into DDS
	wait_10_inst();
	DDS_clk = 0;

	wait_10_inst();

	DDS_ioud = 1;
	wait_10_inst();
	DDS_ioud = 0;

 	Buffer = 0;
	SPIleast();		// Sends data LSB out PortB.7; clocked by PortB.6
 	Buffer = 0;
	SPIleast();		// Sends data LSB out PortB.7; clocked by PortB.6
 	Buffer = 0;
	SPIleast();		// Sends data LSB out PortB.7; clocked by PortB.6
 	Buffer = 0;	
	SPIleast();		// Sends data LSB out PortB.7; clocked by PortB.6
 	Buffer = 0;	
	SPIleast();		// Sends data LSB out PortB.7; clocked by PortB.6

	wait_10_inst();

	DDS_ioud = 1;
	wait_10_inst();
	DDS_ioud = 0;

}// end of 9850/51Int

void AD9854Int(void)
{
//  Send the sequence to set up the 9854 for serial updates
//  Protocol courtesy of Craig Johnson, AA0ZZ

	
	//	Pulse Master reset
	Delay100TCYx(20); 	//wait 8ms
	IQ_mreset = 1;
	Delay100TCYx(20); 	//wait 8ms
	IQ_mreset = 0;
	Delay100TCYx(20); 	//wait 8ms

	// Pulse IO reset
	IQ_ioreset = 1;
	Delay100TCYx(20); 	//wait 8ms
	IQ_ioreset = 0;

	//Send command to write CR register
	Buffer = CR_write_cmd;
	SPImost();

	// send 4 CR bytes
	Buffer = CR_byte_0;
	SPImost();
	Buffer = CR_byte_1;
	SPImost();
	Buffer = CR_byte_2;
	SPImost();
	Buffer = CR_byte_3;
	SPImost();

//	External Update now set
//	Now send new IOreset and new command

	// Pulse IO reset
	IQ_ioreset = 1;
	Delay100TCYx(30); 	//wait 8ms
	IQ_ioreset = 0;

	//Send command to write CR register
	Buffer = CR_write_cmd;
	SPImost();

	Buffer = CR_byte_0;
	SPImost();
	Buffer = CR_byte_1;
	SPImost();
	Buffer = CR_byte_2;
	SPImost();
	Buffer = CR_byte_3;
	SPImost();

//	Send external update to trigger initialization
	// raise IO update
	IQ_ioud = 1;
	wait_10_inst();
	IQ_ioud = 0;

		// Send result back to PC
		//if(mUSBUSARTIsTxTrfReady())
	     //   {

		//		mUSBUSARTTxRam((byte*)output_buffer,5); 
		//	}
	
}// end of 9854Int

void wait_10_inst(void)
{
// actually about 15 instruction
int i;
	i=1;
	i=2;

}// end of wait_10_inst routine

void send_9854Freq(void)
{

	// Pulse IOreset
	IQ_ioreset = 1;
	wait_10_inst();
	IQ_ioreset = 0;

	//Send command to write CR register
	Buffer = freq_write_cmd;
	SPImost();
	Buffer = input_buffer[0];
    //Buffer = 0x0E;
	SPImost();
	Buffer = input_buffer[1];
	//Buffer = 0x6A;
	SPImost();
	Buffer = input_buffer[2];
    //Buffer = 0xFC;
	SPImost();
	Buffer = input_buffer[3];
    //Buffer = 0xCE;
	SPImost();
	Buffer = 0x00;
	SPImost();
	Buffer = 0x00;
	SPImost();

	// raise IO update
	IQ_ioud = 1;
	wait_10_inst();
	IQ_ioud = 0;
	
}// end of send_9854Freq

void AD995xInt(void)
{ //DRAFT CODE _ UNTESTED

	// Send reset - might not be needed
	IQ_mreset = 1;
	IQ_mreset = 0;


	//Send word for register selection 

	// Select CFR 1 to set up devices
	Buffer = 0x01;
	SPImost();

	// send initialization bytes (24 bits)
	Buffer = 0x00;
	SPImost();
	Buffer = 0x00;
	SPImost();
					//Note: This byte must be customized to the specific device
					//      configuration of the 995x -  test confg set for 
					//      20Mhz crystal multiplied 20 times
					//		10100-1-10 = 20x PLL- VCO>250MHZ -Default charge pump
	//Buffer = 0xA6;	// Pll Mult=20; 
	Buffer = input_buffer[0];	// Register byte set up in PC
	SPImost();

	// toggle IO update
	AD99_ioud = 1;
	wait_10_inst();
	AD99_ioud = 0;

}// end of  AD995xInt


void send_9951Freq(void)
{ //DRAFT CODE _ UNTESTED
	//Send bytes for fequency selection

	// Select Freq reg CFR 4
	Buffer = 0x04;	// select register cf4 - Freq
	SPImost();


	// Now send 32 bit freq word

	Buffer = input_buffer[0];
	SPImost();

	Buffer = input_buffer[1];
	SPImost();

	Buffer = input_buffer[2];
	SPImost();

	Buffer = input_buffer[3];
	SPImost();

	// toggle IO update
	AD99_ioud = 1;
	wait_10_inst();
	AD99_ioud = 0;

}// end of send_9951Freq

void AD9958Int(void)
{
	//Draft Code - untested

	// First set the PLL from the initialization word
	Buffer = 0x01;	//Select Reg 1
	SPImost();

	// set PLL multiplier from MSB byte of initialization word
	//Buffer = input_buffer[0];
	//Buffer <<= 2;    // right shift byte twice
	//Buffer = Buffer << 2;
	Buffer = 0x90;		// Set mult to 4 ( left shifted 2 bits)
	SPImost();
	Buffer = 0x00;		// byte 2
	SPImost();
	Buffer = 0x00;		// byte 3 of reg 1
	SPImost();

	// Wait 1ms for the clock to settle
	Delay10TCYx(50); 	//wait 1ms

	// Now set the channel 1 phase to 180 degrees from channel 0
	Buffer = 0x00;	//Select Reg 0
	SPImost();
	Buffer = 0x80;	//Enable only channel 1
	SPImost();

	Buffer = 0x05;	//Select Reg 5 = phase set
	SPImost();	
	Buffer = 0x20;		// Set phase to 180 degrees
	SPImost();
	Buffer = 0x00;		// byte 2
	SPImost();

	// Reset the channel bits so they are both being updated

	Buffer = 0x00;	//Select Reg 0
	SPImost();
	Buffer = 0xF0;	//Enable only channel 1
	SPImost();

	// Send initial frequency 7.040 at 500Mhz

	Buffer = 0x04;	//Select Reg 4 = freq set
	SPImost();	
	Buffer = 0x20;		// Freq MSB
	SPImost();
	Buffer = 0x00;		// byte 2
	SPImost();
	Buffer = 0x00;		// byte 3
	SPImost();
	Buffer = 0x00;		// Freq LSB
	SPImost();

	// Send IO_UPDATA to load all the parameters
	// toggle IO update

	AD99_ioud = 1;
	wait_10_inst();
	AD99_ioud = 0;

}
void send_9958Freq(void)
{ //DRAFT CODE _ UNTESTED
	//Send bytes for fequency selection
	// Send initial frequency 7.040 at 500Mhz

	Buffer = 0x04;	//Select Reg 4 = freq set
	SPImost();	

	// Send freq from USB buffer
	Buffer = input_buffer[0];
    //Buffer = 0x0E;
	SPImost();
	Buffer = input_buffer[1];
	//Buffer = 0x6A;
	SPImost();
	Buffer = input_buffer[2];
    //Buffer = 0xFC;
	SPImost();
	Buffer = input_buffer[3];

	// Send IO_UPDATA to load all the parameters
	// toggle IO update

	AD99_ioud = 1;
	wait_10_inst();
	AD99_ioud = 0;

}// end of send_9958Freq


void set_atten(void)
{
	int i,j;
	
	ATTEN_data = 0;				// initialize the attenuator data pin
	ATTEN_clk = 0;				// initialize the attenuator clock pin
	ATTEN_oe = 0;				// and init the attenuator out enable pin
	
	Buffer = input_buffer[0] & 0x3f;	// get and store the atten command
	for(i=0; i<6; i++)					// send the six atten control bits
	{
		j = Buffer & 0x20;				// strip all but the bit of interest
		if(j)
			ATTEN_data = 1;				//  if it was a one, set the data bit
		else
			ATTEN_data = 0;				// otherwise clear the data bit to zero
		wait_10_inst();					// delay a little
		ATTEN_clk = 1;					// toggle the clock pin (leading edge)
		wait_10_inst();					// delay a little
		ATTEN_clk = 0;					// and reset the clock pin
		Buffer <<= 1;					// shift to test the next bit
	} // send-a-bit for loop	
	wait_10_inst();						// delay a little
	ATTEN_oe = 1;						// Now, toggle the output enable bit - up
	wait_10_inst();						// delay a little
	ATTEN_oe = 0;						// and set the oe pin down
	ATTEN_data = 0;						// make sure data pin is down

} // end of set_atten



/** EOF user.c ***************************************************************/
