//---------------------------------------------------------------------------- // // 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 // //---------------------------------------------------------------------------- #include #include #include "main.h" //---------------------------------------------------------------------------- // // Global variables // //---------------------------------------------------------------------------- extern uint8 ComRxCmd [32]; // remote command rx buffer extern uint8 UsbTxBuffer [64]; extern uint8 ComRxPtr; // pointer into com rx buffer extern uint8 LedTimer; // LED on timer extern uint8 DisplayIndex; // index for status display extern uint8 AgcDisable; // Agc disable flag extern uint8 AgcGain; // AGC for ADC input, power of 2 scaling factor extern uint8 AgcIntegrator; // AGC integrator extern uint8 AgcTC; // AGC Time Constant, AGC is slow decay if AgcTC is non zero extern int16 AdcPeakAve; // ADC average extern uint8 Filter; // audio filter bandwidth extern uint8 Squelch; // squelch level, 0 - 250. extern uint8 K2Legacy; // legacy flag for older remote software extern uint8 Mute; // Mute the receiver extern uint8 FilterBW[2]; // New dual VFO stuff extern uint8 Vfo[16]; // VFO A is 0-7, VFO B is 8-15 extern uint8 VfoMode[2]; extern uint8 VfoIndex; // Active VFO 0 = VFO A, 8 = VFO B extern const uint8 FREQ_ERROR[8]; // freq offset is 1st row of eeprom extern const uint8 SERIAL_NUM[8]; // serial number is 2nd row of eeprom //---------------------------------------------------------------------------- // // SERIAL ROUTINES // //---------------------------------------------------------------------------- uint8 FreqRangeCheck (uint8 FreqReq) { if (FreqReq < FREQ_CHECK_LOW) return 0; // Requesting freq below 1.8 MHz if (FreqReq < FREQ_CHECK_HIGH) return 1; // Frequency between 1.8 and 18.2 MHz return 0; } // // Process com rx commands // void ProcessComRx (void) { #define GET_CMD (ComRxCmd [2] == ';') uint8 i; uint8 sendUsb = FALSE; switch ((ComRxCmd [0] * 256) + ComRxCmd [1]) { // The longest path through the Switch/Case code is for commands that are not implemented. // So every command must be handled. The most often sent commands must use the shortest path. // // Most often sent commands at the top case CMD_IF: // radio info sendUsb = 38; // 1 2 3 // 01234567890123456789012345678901234567 // fffffffffff*****+yyyyrx*00tmvspb01*; memcpy (UsbTxBuffer, "IF00000000000 +000000 0000000001 ;", sendUsb); memcpy (&UsbTxBuffer [5], &Vfo[VfoIndex << 3], 8); UsbTxBuffer [28] = Mute ? '1' : '0'; // t UsbTxBuffer [29] = VfoMode[VfoIndex] + '1'; // m UsbTxBuffer [30] = VfoIndex ? '1' : '0'; // v break; case CMD_BG: // bar graph, return BG12 thru BG22 // XXXXXX Should be in dB, ie log based, probably use a lookup table sendUsb = 5; i = AgcGain / 6; // 64 >= AgcGain >= 0, 10 >= i >= 0 i = (10 - i) + 12; // between 22 and 12. memcpy (UsbTxBuffer, "BG12;", sendUsb); UsbTxBuffer [2] = (i / 10) + '0'; // either '1' or '2' UsbTxBuffer [3] = (i % 10) + '0'; // '0' thru '9' break; case CMD_SM: // Smeter, 0000 - 0015, S9 = 6, +20 = 9, +40 = 12, +60 = 15 // XXXXXX Should be in dB, ie log based, probably use lookup table sendUsb = 7; // i = AgcGain / 4; // 64 >= AgcGain >= 0, 15 >= i >= 0 i = AgcGain >> 2; // divide by 4 i = (16 - i); memcpy (UsbTxBuffer, "SM0000;", sendUsb); UsbTxBuffer [4] = (i / 10) + '0'; // either '0' or '1' UsbTxBuffer [5] = (i % 10) + '0'; // '0' thru '9' break; // Others in alphabetical order case CMD_AG: // AF Gain if (GET_CMD) { sendUsb = 6; memcpy (UsbTxBuffer, "AG000;", sendUsb); } break; case CMD_AI: // auto-information, make always mode 0 if (GET_CMD) { sendUsb = 4; // flag to send response memcpy (UsbTxBuffer, "AI0;", sendUsb); } break; case CMD_AN: // antenna select, always 1 if (GET_CMD) { sendUsb = 4; memcpy (UsbTxBuffer, "AN1;", sendUsb); } break; case CMD_DS: // display read // DSttttttttaf; a = 0x80 for VFOA, 0x84 for VFOB sendUsb = 13; DisplayIndex++; DisplayIndex %= 12; if (DisplayIndex < 4) // display version info memcpy (UsbTxBuffer, VERSION, 10); else if (DisplayIndex < 8) { memcpy (UsbTxBuffer, "DSSN ", 5); memcpy (&UsbTxBuffer [5], &SERIAL_NUM [3], 5); } else { memcpy (UsbTxBuffer, "DSFR ", 5); memcpy (&UsbTxBuffer [5], &FREQ_ERROR [3], 5); } UsbTxBuffer [10] = VfoIndex ? 0x84 : 0x80; // VFOA VFOB display UsbTxBuffer [11] = 0x80; UsbTxBuffer [12] = ';'; break; case CMD_FA: // set/get frequency if (GET_CMD) { sendUsb = 14; memcpy (UsbTxBuffer, "FA00000000000;", sendUsb); memcpy (&UsbTxBuffer [5], &Vfo[0], 8); } else { // Need to range check the entered frequency here if (((ComRxCmd[5] & 0x0f) < 2) && (FreqRangeCheck(((ComRxCmd[5] & 0x0f) * 100) + ((ComRxCmd[6] & 0x0f) * 10) + (ComRxCmd[7] & 0x0f)))) { memcpy (&Vfo[0], &ComRxCmd [5], 8); if (VfoIndex == 0) SetMode(VfoMode[0]); } } break; case CMD_FB: if (GET_CMD) { sendUsb = 14; memcpy (UsbTxBuffer, "FB00000000000;", sendUsb); memcpy (&UsbTxBuffer [5], &Vfo[8], 8); } else { // range check the entered frequency here if (((ComRxCmd[5] & 0x0f) < 2) && (FreqRangeCheck(((ComRxCmd[5] & 0x0f) * 100) + ((ComRxCmd[6] & 0x0f) * 10) + (ComRxCmd[7] & 0x0f)))) { memcpy (&Vfo[8], &ComRxCmd [5], 8); if (VfoIndex == 1) SetMode(VfoMode[1]); } } break; case CMD_FR: // RX A and B Vfos if (GET_CMD) { sendUsb = 4; memcpy (UsbTxBuffer, "FR0;", sendUsb); UsbTxBuffer[2] = (VfoIndex ? '1' : '0'); } else { VfoIndex = (ComRxCmd[2] == '1'); SetMode(VfoMode[VfoIndex]); } break; case CMD_FT: // no transmit split mode, always 0 if (GET_CMD) { sendUsb = 4; memcpy (UsbTxBuffer, "FT0;", sendUsb); UsbTxBuffer[2] = (VfoIndex ? '1' : '0'); } else { VfoIndex = (ComRxCmd[2] == '1'); SetMode(VfoMode[VfoIndex]); } break; case CMD_FW: // filter bandwidth if (GET_CMD) { sendUsb = K2Legacy ? 7 : 9; memcpy (UsbTxBuffer, "FW000000;", sendUsb); memcpy (&UsbTxBuffer [2], FilterBW, 2); UsbTxBuffer [6] = K2Legacy ? ';' : Filter + '1'; } else { // XXXX Need something here to handle filter selectiion in legacy mode XXXX Filter = ComRxCmd [6] - '1'; if (Filter > 3) Filter = 0; LoadFilter (Filter); } break; case CMD_GT: // AGC time constant, slow, on/off XXXX Need legacy support here XXXX if (GET_CMD) { sendUsb = K2Legacy ? 6 : 7; memcpy (UsbTxBuffer, "GT0040;", sendUsb); UsbTxBuffer [4] = (AgcTC ? '4' : '2'); // AGC Slow if AgcTC is non-zero UsbTxBuffer [5] = K2Legacy ? ';' : (AgcDisable ? '0' : '1'); } else if ((ComRxCmd[2] == '0') && (ComRxCmd[3] == '0')) { // HRD spews out messages that can turn AGC off. Must check carefully. Yuck! if ((ComRxCmd[4] == '4') || (ComRxCmd[4] == '2')) AgcTC = (ComRxCmd [4] == '4'); // 4 = slow, AgcTC = 1 if ((ComRxCmd[5] == '1') || (ComRxCmd[5] == '0')) AgcDisable = (ComRxCmd [5] == '0'); } break; case CMD_ID: // K2 ID number sendUsb = 6; memcpy (UsbTxBuffer, "ID017;", sendUsb); break; case CMD_K2: // meta-command if (GET_CMD) { sendUsb = 4; memcpy (UsbTxBuffer, "K22;", sendUsb); K2Legacy = 0; // Extended K2 command set } else K2Legacy = (ComRxCmd [2] == '0'); // Legacy mode if 0 break; case CMD_K3: // meta-command if (GET_CMD) { sendUsb = 4; memcpy (UsbTxBuffer, "K31;", sendUsb); K2Legacy = 0; // Extended K2 command set } else K2Legacy = (ComRxCmd [2] == '0'); // Legacy mode if 0 break; case CMD_KS: // keyer speed, not used if (GET_CMD) { sendUsb = 6; memcpy (UsbTxBuffer, "KS009;", sendUsb); } break; case CMD_LK: // VFO lock, always unlocked XXXX GRIG seems to send lock once in a while. ARGH! if (GET_CMD) { sendUsb = 4; memcpy (UsbTxBuffer, "LK0;", sendUsb); } else { if (ComRxCmd [2] == '1') // Trying to set lock { if (!memcmp (&Vfo[VfoIndex << 3], "10101010", 8)) // special frequency test CyBtldr_Load(); // enter USB boot loader mode else { Clock_FracN_SetDividerValue (10); // unlock and relock PLL CyDelay (100); // delay in msec Clock_FracN_SetDividerValue (9); } } sendUsb = 4; memcpy (UsbTxBuffer, "LK0;", sendUsb); } break; case CMD_MD: // operating mode if (GET_CMD) { sendUsb = 4; memcpy (UsbTxBuffer, "MD1;", sendUsb); UsbTxBuffer [2] = VfoMode[VfoIndex] + '1'; // Mode = 0 - 9 } else { i = (ComRxCmd [2] - '1'); // grig sends bad mode occasionaly :( if (i < 9) SetMode (i); // map Mode to 0 - 9 } break; case CMD_MG: // mic gain, always off K3 if (GET_CMD) { sendUsb = 6; memcpy (UsbTxBuffer, "MG000;", sendUsb); } break; case CMD_NB: // noise blanker mode, extended if (GET_CMD) { sendUsb = K2Legacy ? 4 : 5; memcpy (UsbTxBuffer, "NB00;", sendUsb); UsbTxBuffer [3] = K2Legacy ? ';' : '0'; } break; case CMD_PA: // rx preamp, always off if (GET_CMD) { sendUsb = 5; memcpy (UsbTxBuffer, "PA0;", sendUsb); } break; case CMD_PC: // power output level XXXX Need legacy support here XXXX if (GET_CMD) { sendUsb = K2Legacy ? 6 : 7; memcpy (UsbTxBuffer, "PC0000;", sendUsb); UsbTxBuffer [5] = K2Legacy ? ';' : '0'; } break; case CMD_PS: // radio power status, always on sendUsb = 4; memcpy (UsbTxBuffer, "PS1;", sendUsb); break; case CMD_RA: // rx attenuator, always off if (GET_CMD) { sendUsb = 5; memcpy (UsbTxBuffer, "RA00;", sendUsb); } break; case CMD_RG: // RF Gain if (GET_CMD) { sendUsb = 6; memcpy (UsbTxBuffer, "RG000;", sendUsb); } break; case CMD_RX: Mute = 0; break; // Squelch is unused case CMD_SQ: // squelch, 0 2 250. if (GET_CMD) { sendUsb = 6; memcpy (UsbTxBuffer, "SQ000;", sendUsb); // i = Squelch; // UsbTxBuffer [2] = (i / 100) + '0'; // hundreds // i = i % 100; // UsbTxBuffer [3] = (i / 10) + '0'; // tens // UsbTxBuffer [4] = (i % 10) + '0'; // units } else { // Squelch = 100 * (ComRxCmd [2] - '0'); // Squelch += 10 * (ComRxCmd [3] - '0'); // Squelch += ComRxCmd [4] - '0'; } break; case CMD_SW: // Switch Emulation (needed for dual VFOs) switch ((ComRxCmd [2] * 256) + ComRxCmd [3]) { case SW_A1B: VfoIndex ^= 0x01; // A/B break; case SW_A2B: memcpy(&Vfo[0], &Vfo[8], 8); // A=B break; } SetMode(VfoMode[VfoIndex]); break; case CMD_TQ: if (GET_CMD) { sendUsb = 4; memcpy (UsbTxBuffer, "TQ0;", sendUsb); UsbTxBuffer [2] = Mute ? '1' : '0'; } break; case CMD_TX: Mute = 1; break; } if (sendUsb) { USBUART_Write(UsbTxBuffer, sendUsb); // send response // XXXX Do we really need to wait here until the message has been sent? XXXX // while(!USBUART_bTxIsReady()) ; } }