//---------------------------------------------------------------------------- // // SIMPLE SDR RECEIVER PSoC3 FIRMWARE for Hardware Rev - // // Copyright 2011 Simple Circuits Inc. // // 06/03/2011 Original release. // // // // Additional features by Dave J. Barnes 5/2012 // // // ADC sample rate is 32068 sps total, or 16034 sps per channel // //---------------------------------------------------------------------------- #include #include #include "main.h" //---------------------------------------------------------------------------- // // Constants // //---------------------------------------------------------------------------- #define VFOA_START "14076000" // in ASCII BCD chars #define VFOA_START_MODE MODE_RTTY #define VFOB_START "18102000" // in ASCII BCD chars #define VFOB_START_MODE MODE_RTTY #define MODE_RTTY_FILTER FILTER_FL3 // JT65 Filter #define MODE_SSB_FILTER FILTER_FL2 #define MODE_CW_FILTER FILTER_FL3 #define FREQ_PLL_TEST "08000080" // pll lock test frequency 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 uint8 CYCODE Filter_data_b_DJB_AM[]; // FIR coefficients extern const uint8 CYCODE Filter_data_b_DJB_SSBW[]; // FIR coefficients extern const uint8 CYCODE Filter_data_b_DJB_SSB[]; // FIR coefficients extern const uint8 CYCODE Filter_data_b_DJB_SSBN[]; // FIR coefficients extern const uint8 CYCODE Filter_data_b_DJB_JT65[]; // FIR coefficients extern const uint8 CYCODE Filter_data_b_DJB_CWN[]; // 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 LastFreq [8]; // copy of last tuned rx frequency 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 AgcTC; // AGC Time Constant, AGC is slow decay if AgcTC is non zero uint8 Mute; // 0 = unmuted, 1 = muted (mutes the rf input, sets AgcGain to 0) uint8 AudioMute; // 0 = unmuted, 1 = muted (mutes the input to the VDAC) uint8 LedTimer; // LED on timer uint8 DisplayIndex; // index for status display uint8 K2Legacy; // Legacy flag for older remote software uint8 Filter; // audio filter bandwidth selection uint8 UpperSideBand; // upper side band mode selection // uint8 AM_Mode; // AM flag uint8 ModeFilter[10]; // Save filter settings per mode uint8 FilterBW[2]; // Filter bandwidth string, 100's of Hz // New Filter tables uint8 CYCODE * SSB_Filters[] = { Filter_data_b_DJB_AM, Filter_data_b_DJB_SSBW, Filter_data_b_DJB_SSB, Filter_data_b_DJB_SSBN }; uint8 CYCODE * CW_Filters[] = { Filter_data_b_DJB_SSB, Filter_data_b_DJB_SSBN, Filter_data_b_1500, Filter_data_b_DJB_CWN }; uint8 CYCODE * RTTY_Filters[] = { Filter_data_b_DJB_AM, Filter_data_b_DJB_SSB, Filter_data_b_DJB_JT65, Filter_data_b_DJB_CWN }; // New dual VFO stuff uint8 Vfo[16]; // VFO A is 0-7, VFO B is 8-15 uint8 VfoMode[2]; uint8 VfoIndex; // Active VFO; 0 = VFO A, 1 = VFO B 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) { 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 (Mute) { AgcGain = 0; return; } 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; // atten useless :) Really should be called AGC threshold. // atten = (AttenuatorOn) ? 0x1000 : 0x2800; if (--AgcTimer) { if ((AdcPeakAve > 0x2800) && (AgcIntegrator)) // If input signal jumps XXXX can make gain on I and Q not equal { AgcIntegrator--; // Fast Attack based on peak level - decrease gain AgcGain = 1 + (AgcIntegrator >> 2); // Adjust Agc AdcPeak = 0; } return; } AgcTimer = (AgcTC ? 6 : 2); // (1)slow decay = 6 ticks, (0)fast decay = 2 ticks XXXX make constants XXXX AdcPeak = 0; if (AdcPeakAve < 0x2000) // clips if higher than this number XXXX 0x1000 or 0x2800? Which is best? { 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; // Using pointers to Filter tables if ((VfoMode[VfoIndex] <= MODE_USB) || (VfoMode[VfoIndex] == MODE_AM) || (VfoMode[VfoIndex] == MODE_FM)) { cymemcpy(Filter_DB_RAM, SSB_Filters[fltr], Filter_DB_RAM_SIZE/2); memcpy(FilterBW, (SSB_Filters[fltr] + 256), 2); // Bandwidth chars after filter data } else if ((VfoMode[VfoIndex] == MODE_CW) || (VfoMode[VfoIndex] == MODE_CWREV)) { cymemcpy(Filter_DB_RAM, CW_Filters[fltr], Filter_DB_RAM_SIZE/2); memcpy(FilterBW, (CW_Filters[fltr] + 256), 2); } else if ((VfoMode[VfoIndex] == MODE_RTTY) || (VfoMode[VfoIndex] == MODE_RTTYREV)) { cymemcpy(Filter_DB_RAM, RTTY_Filters[fltr], Filter_DB_RAM_SIZE/2); memcpy(FilterBW, (RTTY_Filters[fltr] + 256), 2); } // Save filter settings for this mode ModeFilter[VfoMode[VfoIndex]] = fltr; // 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 (&Vfo[VfoIndex << 3], LastFreq, 8)) // check if already tuned to that frequency return; // ignore set frequency command and return frq = BinaryFrequency (&Vfo[VfoIndex << 3]); // compute binary value from string // Range check now in Serial.c memcpy (LastFreq, &Vfo[VfoIndex << 3], 8); if (VfoMode[VfoIndex] == MODE_CWREV) frq = frq - 800; // It's customary for CW modes freq to be offset by cw pitch. else if (VfoMode[VfoIndex] == MODE_CW) frq = frq + 800; // We use nominal 800 Hz pitch and offset rx freq accordingly. // Frequency = frq; // save new receive frequency (binary form) **UNUSED** 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) { if (VfoMode[VfoIndex] != md) memcpy (LastFreq, "12345678", 8); // force frequency change if changing modes UpperSideBand = ((md == MODE_USB) || (md == MODE_CWREV) || (md == MODE_RTTY)); // K3 uses LSB for CW mode // AM_Mode = (md == MODE_AM); // Save new Mode VfoMode[VfoIndex] = md; // Restore filter settings for this mode Filter = ModeFilter[md]; LoadFilter (Filter); SetFrequency(); } // // 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 (&Vfo[0], "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 (&Vfo[0], "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 AudioMute = 1; // Don't want to hear all of the noise during setup K2Legacy = 1; // startup in legacy mode 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 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 (&Vfo[0], 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; if (FrequencyError == 0) FrequencyError = 55; // Current batch of 24 MHz xtals 5.5 PPM low memcpy (&Vfo[0], VFOA_START, 8); // initialize the clock generator output frequency memcpy (&Vfo[8], VFOB_START, 8); VfoMode[0] = VFOA_START_MODE; VfoMode[1] = VFOB_START_MODE; ModeFilter[MODE_LSB] = MODE_SSB_FILTER; // selects SSB Wide Filter ModeFilter[MODE_USB] = MODE_SSB_FILTER; // selects SSB Wide Filter ModeFilter[MODE_CW] = MODE_CW_FILTER; // selects CW Wide Filter ModeFilter[MODE_RTTY] = MODE_RTTY_FILTER; // Selects JT65A Filter SetMode (MODE_RTTY); AudioMute = 0; // Let's hear the receiver:) 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 */