//----------------------------------------------------------------------------
//
//	SIMPLE SDR RECEIVER PSoC3 FIRMWARE for Hardware Rev -
//
//	Copyright 2011 Simple Circuits Inc.
//
//	06/03/2011	Original release.
//
//
//
//	ADC sample rate is 32068 sps total, or 16034 sps per channel
//
//----------------------------------------------------------------------------
#include <device.h>
#include <string.h>
#include "main.h"


//----------------------------------------------------------------------------
//
//	Constants
//
//----------------------------------------------------------------------------
#define	FREQ_START			"14070000"			// in ASCII BCD chars
#define	FREQ_PLL_TEST		"08000080"			// pll lock test frequency
#define	FREQ_LOW			 3500000
#define	FREQ_HIGH			18168000
const uint8 HEX2ASCII [] = {"0123456789ABCDEF"};
extern const uint8 CYCODE Filter_data_b_2500[];	// FIR coefficients
extern const uint8 CYCODE Filter_data_b_2000[];	// FIR coefficients
extern const uint8 CYCODE Filter_data_b_1500[];	// FIR coefficients
extern const uint8 CYCODE Filter_data_b_cw[];	// FIR coefficients
extern const int32 CYCODE Hilbert [];			// Hilbert tranformer coefficients

const uint8 FREQ_ERROR[8] _at_ CYDEV_EE_BASE+16*FQ_ROW;// freq offset is 1st row of eeprom
const uint8 SERIAL_NUM[8] _at_ CYDEV_EE_BASE+16*SN_ROW;// serial number is 2nd row of eeprom


//----------------------------------------------------------------------------
//
//	Global variables
//
//----------------------------------------------------------------------------
uint8	Freq [8];					// receive frequency in ASCII BCD
uint8	LastFreq [8];				// copy of last tuned rx frequency
int32	Frequency;					// receive frequency in binary
int32	FrequencyError;				// frequency error at 10 MHz

uint8	ComRxCmd [32];				// remote command rx buffer
uint8	ComRxPtr;					// pointer into com rx buffer
uint8	UsbTxBuffer [64];			// USB transmit buffer
uint8	UsbRxBuffer [256];			// USB receive buffer

uint8	AdcTick;					// ADC interrupt flag every 6.521 msec
int16	IDCOffset, QDCOffset;		// DC offset values for ADC input
int16	AdcPeak, AdcPeakAve;		// ADC input peak value, and its average
uint8	AgcDisable;					// Agc disable flag
uint8	AgcGain;					// AGC for ADC input
uint8	AgcTimer;					// AGC processing timer
uint8	AgcIntegrator;				// AGC integrator

uint8	LedTimer;					// LED on timer
uint8	DisplayIndex;				// index for status display

uint8	Filter;						// audio filter bandwidth selection
uint8	Mode;						// operating mode
uint8	UpperSideBand;				// upper side band mode selection
uint8	AttenuatorOn;				// rx attenuator on/off flag
uint8	Squelch;					// squelch level, 0 - 250.
uint8	Debug;						// debug mode flag, turns off boundary checks


//----------------------------------------------------------------------------
//
//	MAIN
//
//----------------------------------------------------------------------------
void main()
{
	Initialize ();

	while (1)
	{
		AdjustAgc ();				// AGC adjustment		

		ReadUSB ();					// read the USB port for command data
	}
}


//
//	Adjust AGC by monitoring ADC peak signal level
//	Adjusts gain every 8 msec, rate of 32068 / 256 Hz
//
void AdjustAgc (void)
{
	uint16 atten;
	
	if (!AdcTick)					// 8 msec tick
		return;
	AdcTick = FALSE;
	
	if (LedTimer)
	{
		if (--LedTimer == 0)
			CyPins_SetPin (REDLED_0);	// red led off
		else
			CyPins_ClearPin (REDLED_0);	// red led on
	}	

	if (AgcDisable)
		AdcPeak = 0;				// disable AGC adjustments and go to max gain
	else if (AdcPeak > 0x3800)
	{
		AgcIntegrator = (AgcIntegrator >> 2) * 3;	// overloaded input, reduce gain to 75%
		AgcGain = 1 + (AgcIntegrator >> 2);	
		LedTimer = LED_SHORTBLINK;	// blink the LED for overload
	}
	AdcPeakAve = (AdcPeakAve + AdcPeak) / 2;		// compute average peak ADC value
	AdcPeak = 0;	
	
	if (--AgcTimer)
		return;
	AgcTimer = 4;					// every 32 msec
	
	atten = (AttenuatorOn) ? 0x1000 : 0x2800;
	if (AdcPeakAve < atten)			 // clips if higher than this number
	{
		if (AgcIntegrator != 0xFF)
			AgcIntegrator++;		// increase gain
	}
	else if (AgcIntegrator)
		AgcIntegrator--;			// decrease gain
		
	AgcGain = 1 + (AgcIntegrator >> 2);	
}

			
//
//	Read USB port for COM data
//
void ReadUSB (void)
{
	uint8 bCount, rx, i;
	
	bCount = USBUART_bGetRxCount();		// get the USB Rx data size
	if( bCount )
	{
		USBUART_ReadAll(UsbRxBuffer);	// read usb rx data

		for (i = 0; i < bCount; i++)
		{
			rx = UsbRxBuffer [i];
			ComRxCmd [ComRxPtr++] = rx;	// save in command buffer
			if (rx == ';')				// command string terminator
			{
				ProcessComRx ();
				ComRxPtr = 0;
			}
			else if ((rx == CR) && (ComRxCmd [0] == '$') && (ComRxCmd [1] == '*'))
			{
				FactorySetup ();
				ComRxPtr = 0;
			}
			else
				ComRxPtr &= sizeof(ComRxCmd) - 1;
		}
	}
}


//
//	Load DFB filter data into coefficient RAM
//
void LoadFilter (uint8 fltr)
{
	uint8 i, j;
	int32 x;
	uint8 xbuf [256];

	// Put DFB RAM on the bus
    Filter_RAM_DIR_REG = Filter_RAM_DIR_BUS;

	if (fltr == FILTER_FL1)
        cymemcpy(Filter_DB_RAM,Filter_data_b_2500, Filter_DB_RAM_SIZE);
	else if (fltr == FILTER_FL2)
        cymemcpy(Filter_DB_RAM,Filter_data_b_2000, Filter_DB_RAM_SIZE);
	else if (fltr == FILTER_FL3)
        cymemcpy(Filter_DB_RAM,Filter_data_b_1500, Filter_DB_RAM_SIZE);
	else if (fltr == FILTER_FL4)
        cymemcpy(Filter_DB_RAM,Filter_data_b_cw, Filter_DB_RAM_SIZE);
	else if (fltr == FILTER_RST)
        cymemcpy(Filter_DB_RAM,Filter_data_b, Filter_DB_RAM_SIZE);
		
	// copy Hilbert filter coefficients into filter channel B ram
	j = 0;
	for (i = 0; i < 64; i++) 
	{
		x = Hilbert [i];
		xbuf [j++] = x & 0xFF;
		xbuf [j++] = (x >> 8) & 0xFF;
		xbuf [j++] = (x >> 16) & 0xFF;
		xbuf [j++] = 0;
	}
	cymemcpy(Filter_DB_RAM + 256, xbuf, Filter_DB_RAM_SIZE / 2);
		
	// Take DFB RAM off the bus
	Filter_RAM_DIR_REG = Filter_RAM_DIR_DFB;
}


//
//	Frequency synthesizer set frequency
//	Freq (MHz)  = (24/9) * (N + Frac/16384) / (4 * IQDividerRatio)
//				= 2 * (N + Frac/16384) / (3 * IQDividerRatio)
//
void SetFrequency (void)
{
	uint8 pllN, iqDivider, fracLo, fracHi;
	uint32 frq;
	
	if (!memcmp (Freq, LastFreq, 8))	// check if already tuned to that frequency
		return;							// ignore set frequency command and return

	frq = BinaryFrequency (Freq);		// compute binary value from string

	if ((!Debug) && ((frq < FREQ_LOW) || (frq > FREQ_HIGH))) // ignore frequencies outside tuning range
	{
		memcpy (Freq, LastFreq, 8);	// restore last frequency allowed
		return;
	}
	memcpy (LastFreq, Freq, 8);
	Frequency = frq;				// save new receive frequency (binary form)
	
	iqDivider = 2;					// find best divide ratio that keeps vco near 68 MHz
	while (( (uint32) (iqDivider + 2) * frq)  < 68000000)
		iqDivider += 2;

	if (iqDivider < 4)				// bound the divisor
		iqDivider = 4;
	else if (iqDivider > 32)
		iqDivider = 32;

	frq += (( (int32) frq / 100) * FrequencyError) / 100000;	// offset by frequency error (factory adjusted)
	
	frq = (frq * 71) / 107;			// pll value = 0000 0000 0000 0nnn nn.<14 bits frac>
	frq = (frq * iqDivider) / 108;
	fracLo = (uint8) frq;
	frq >>= 8;
	fracHi = (uint8) frq & 0x3F;
	frq >>= 6;
	pllN = (uint8) frq;				// shifted by 14 bits, integer part of pll divisor

	AgcGain = 0;					// mute the input signal during frequency changes

	CY_SET_REG8(IQ_Generator_IQDividerMax__CONTROL_REG, iqDivider/2 - 1 );
	CY_SET_REG8(FracN_PLL_N__CONTROL_REG, pllN);		// PLL N value (integer part)
	CY_SET_REG8(FracN_FracHi__CONTROL_REG, 0x80);		// msb resets FracN accumulators
	CyDelay (1);
	CY_SET_REG8(FracN_FracLo__CONTROL_REG, fracLo);
	CY_SET_REG8(FracN_FracHi__CONTROL_REG, fracHi);
}


//
//	Set operating mode
//
void SetMode (uint8 md)
{
	Mode = md;					// save operating mode
	UpperSideBand = ((Mode == MODE_USB) || (Mode == MODE_CW) || (Mode == MODE_RTTY));
		
	if ((Mode == MODE_CW) || (Mode == MODE_CWREV))
		Filter = FILTER_FL4;
	else if ((Mode <= MODE_USB) && (Filter == FILTER_FL4))
		Filter = FILTER_FL2;
	LoadFilter (Filter);
}


//
//	Compute binary frequency value from ASCII character string
//
uint32 BinaryFrequency (uint8 *ptr)
{
	uint8 i;
	uint32 frq, dec;
	
	frq = 0;
	dec = 10000000;					// 10's of MHz
	for (i = 0; i < 8; i++)
	{
		frq += (*ptr++ - '0') * dec;
		dec /= 10;
	}
	return (frq);
}


//
//	Factory setup commands
//
void FactorySetup (void)
{
	uint8 sendUsb = FALSE;
	
	switch (ComRxCmd [2])
	{
		case 'b':						// bootloader mode
			CyBtldr_Load();				// enter USB boot loader mode
			break;
			
		case 'c':						// calibrate frequency mode
			memcpy (LastFreq, "12345678", 8);	// force frequency change
			memcpy (Freq, "10000000", 8);		// set frequency to 10MHz
			FrequencyError = 0;
			SetFrequency ();
			memcpy (UsbTxBuffer, "Calibrate\r", 10);
			sendUsb = 10;
			break;
			
		case 'd':						// debug mode
			Debug = TRUE;
			memcpy (UsbTxBuffer, "Debug Mode\r", 11);
			sendUsb = 11;
			break;
			
		case 'n':						// store serial number in eeprom
			CySetTemp ();				// acquire die temperature before writing eeprom
			if (EEPROM_Write (&ComRxCmd [3], SN_ROW) == CYRET_SUCCESS)
				LedTimer = LED_BLINK;	// if successful write, blink the LED
			break;
			
		case 'f':						// store frequency offset in eeprom
			CySetTemp ();				// acquire die temperature before writing eeprom
			if (EEPROM_Write (&ComRxCmd [3], FQ_ROW) == CYRET_SUCCESS)
			{
				LedTimer = LED_BLINK;	// if successful write, blink the LED
				memcpy (LastFreq, "12345678", 8);	// force frequency change
				memcpy (Freq, "10000000", 8);		// set frequency to 10MHz
				FrequencyError = 10000000 - BinaryFrequency (FREQ_ERROR);	// compute binary value from string
				SetFrequency ();
			}
//			break;

		case '?':						// status command, reply with eeprom data
			memcpy (UsbTxBuffer, SERIAL_NUM, 8);		// serial number
			UsbTxBuffer [8] = ' ';
			memcpy (&UsbTxBuffer [9], FREQ_ERROR, 8);	// frequency offset at 10MHz
			UsbTxBuffer [17] = CR;
			sendUsb = 18;
			break;
	}

	if (sendUsb)
	{
		USBUART_Write(UsbTxBuffer, sendUsb);	// send response
		while(!USBUART_bTxIsReady()) ;
	}
}



//----------------------------------------------------------------------------
//
//	Initialize and configure hardware, initialize variables
//
//----------------------------------------------------------------------------
void Initialize (void)
{
	uint8 dmaChan, td;				// DMA channel and transaction descriptor

	CyDelay (100);					// delay in msec

	CyPins_SetPin (TP2_OUT_0);
	CyPins_SetPin (REDLED_0);		// red led off
	
	Filter_Start ();				// start FIR filter
	VDAC_Start ();					// start DAC output
	
	ADC_Start ();					// power up and start 16 bit ADC
	ADC_IRQ_Enable ();
	ADC_StartConvert ();			// start conversions

	CyDelay (100);					// delay in msec
	
	// initialize vars
	Filter = FILTER_FL2;			// selects 2000 Hz LPF
	SetMode (MODE_USB);
	IDCOffset = QDCOffset = 0x500;	// preset offset for better settling time

	EEPROM_Start ();				// power up eeprom
	CyDelay (10);					// delay in msec
	if (SERIAL_NUM [0] != '0')
	{
		CySetTemp ();				// acquire die temperature before writing eeprom
		EEPROM_Write ((uint8 *) "00000000", SN_ROW);	// write and wait for completion
		EEPROM_Write ((uint8 *) "10000000", FQ_ROW);	// write and wait for completion
	}

	FrequencyError = 0;
	memcpy (Freq, FREQ_PLL_TEST, 8);// initialize the clock generator output frequency
	SetFrequency ();
	CyDelay (200);					// delay in msec
	
	// Configure DMA for single byte transfer
	dmaChan = DMA_DmaInitialize(1, 1, 0, 0); 
	td = CyDmaTdAllocate();			// Get the allocated TD no
	// Setup TD to transfer 1 byte and loop backed to the TD
	CyDmaTdSetConfiguration(td, 1, td, 0);
	CyDmaTdSetAddress(td, LO16(FracN_PLL_P_Reg__STATUS_REG), LO16(&FASTCLK_PLL_P));
	CyDmaChSetInitialTd(dmaChan, td);	// Map the DMA channel to the TD
	CyDmaChEnable(dmaChan, 1);			// Enable the DMA Channel

	CYGlobalIntEnable;
	
	// PLL lock test to prevent out of phase lock
	AgcGain = 0x0C;
	CyDelay (2000);							// let PLL lock
	for (td = 10; td; td--)
	{
		AdcPeak = 0;
		CyDelay (100);						// delay in msec
		AdcPeakAve += AdcPeak;
		AdcPeakAve /= 2;
	}

	if (AdcPeakAve > 8000)					// big signal if PLL not locked properly
	{
		Clock_FracN_SetDividerValue (10);	// unlock PLL by changing divisor
		CyDelay (500);						// delay in msec
		Clock_FracN_SetDividerValue (9);	// reset to correct divisor
		CyDelay (500);						// delay in msec
	}
	FrequencyError = 10000000 - BinaryFrequency (FREQ_ERROR);	// compute binary value from string
	if ((FrequencyError > 1000) || (FrequencyError < -1000))	// bounds checking
		FrequencyError = 0;
	memcpy (Freq, FREQ_START, 8);			// initialize the clock generator output frequency
	SetFrequency ();

	USBUART_Start(0, USBUART_3V_OPERATION);	// start USBFS Operation, device 0, 3V
	while(!USBUART_bGetConfiguration());	// wait for device to enumerate
	USBUART_Init();							// initialize the USBUART
}

/* [] END OF FILE */