From ccae79925b1ba6a3783afe58e069a5fb42e179c5 Mon Sep 17 00:00:00 2001 From: Rob French Date: Sat, 16 May 2020 23:48:39 -0500 Subject: [PATCH] Updated to include iopcomm.h/iopcomm.cpp, common to both the IOP and the Raduino. It was intended to be more extensive, but I had to roll some of it back. Mostly just sharing of enums/types currently. Currently in "factory calibration" mode. Comms (CAT) in normal mode needs to be re-checked. Added two tone test mode, with two new modes available via Raduino menu: TTL and TTU. Using test mode, I'm thinking that the proper TX output calibration, assuming a full-scale 700/1900 test tone, is either: 0.061 (which I believe gives a 25 mV RMS output), or ~0.100 (which is just before the audio starts to distort, in terms of visible harmonics in the spectrum at the 45 MHz IF. --- iopcomm/iopcomm.cpp | 351 ++++++++++++++++++++++++++++++++++++++++ iopcomm/iopcomm.h | 211 ++++++++++++++++++++++++ ubitx_iop/audio.h | 1 + ubitx_iop/audio.ino | 134 ++++++++++----- ubitx_iop/cat.h | 128 +-------------- ubitx_iop/cat.ino | 160 ++++++++++-------- ubitx_iop/config.h | 20 ++- ubitx_iop/ubitx_iop.h | 16 +- ubitx_iop/ubitx_iop.ino | 20 +++ 9 files changed, 803 insertions(+), 238 deletions(-) create mode 100644 iopcomm/iopcomm.cpp create mode 100644 iopcomm/iopcomm.h diff --git a/iopcomm/iopcomm.cpp b/iopcomm/iopcomm.cpp new file mode 100644 index 0000000..df528a0 --- /dev/null +++ b/iopcomm/iopcomm.cpp @@ -0,0 +1,351 @@ +//====================================================================== +// iopcomm.cpp +//====================================================================== + +#include + +#include "iopcomm.h" + +#if defined(TEENSYDUINO) + // Compiling for the Teensy, so assuming this is IOP code. + #define MYSERIAL Serial1 +#else + // Not compiling for the Teensy, so assuming this is Raduino code. + #define MYSERIAL Serial +#endif + +//====================================================================== + +void sendIOPMessage(IOPMessage const& msg) +{ + MYSERIAL.write(prefixAndLengthToByte(IOP_PREFIX, msg.len + 1)); + MYSERIAL.write(msg.id); + for (int i = 0; i < msg.len; i++) { + MYSERIAL.write(msg.data[i]); + } +} + +//====================================================================== + +void recvIOPMessage(IOPMessage& msg, const uint8_t* buf, int len) +{ + msg.id = buf[0]; + msg.len = len - 1; + for (int i = 1; i < len; i++) { + msg.data[i-1] = buf[i]; + } +} + +//====================================================================== + +void sendIOPModeCommand(RigMode mode) +{ + IOPMessage m; + m.id = IOP_MODE_COMMAND; + m.len = 1; + m.data[0] = uint8_t(mode); + sendIOPMessage(m); +} + +//====================================================================== + +void sendIOPStartTxCommand() +{ + IOPMessage m; + m.id = IOP_START_TX_COMMAND; + m.len = 0; + sendIOPMessage(m); +} + +//====================================================================== + +void sendIOPStopTxCommand() +{ + IOPMessage m; + m.id = IOP_STOP_TX_COMMAND; + m.len = 0; + sendIOPMessage(m); +} + +//====================================================================== + +void sendIOPModeRequest() +{ + IOPMessage m; + m.id = IOP_MODE_REQUEST; + m.len = 0; + sendIOPMessage(m); +} + +//====================================================================== + +/* +enum SerialState { + NORMAL = 0, + CAT_MODE, + IOP_MODE, + EEPROM_READ, + EEPROM_WRITE, +}; + +Translator::Translator(RigMode& m, CWConfig& c, Stream& s = SERIAL): +mode(m), cwConfig(c), serial(s) +{ + for (int i = 0; i < NUM_PREFIX_IDS; i++) { + prefixCb[i] = NULL; + } + + for (int i = 0; i < NUM_MESSAGE_IDS; i++) { + messageCb[i] = NULL; + } +} + +void Translator::sendACK() +{ + serial.write(prefixAndLengthToByte(ACK, 0)); +} + +void Translator::sendCATMessage(byte b, bool continued=false) +{ + if (!continued) { // we're starting a CAT transmission sequence + + } else { // we're continuing a CAT transmission sequence + + } +} + +void Translator::sendIOPMessage(IOPMessage const& m) +{ + serial.write(prefixAndLengthToByte(IOP_PREFIX, m.len+1)); + serial.write(m.id); + for (int i = 0; i < m.len; i++) { + serial.write(m.data[i]); + } +} + +static void Translator::receive() +{ + static SerialState state; + + static PrefixID readPrefix = 0; + static uint8_t readLength = 0; + + static IOPMessage iopMsg; + + static uint8_t cmdLength = 0; + static byte cmdBuffer[16]; + static char cmdString[17]; // added a char for null termination when required + + static uint16_t eepromStartIndex; + static uint16_t eepromReadLength; + static int eepromMagicFlag = 0; + + int incomingByte; + + for (int i = 0; i < serial.available(); i++) { + incomingByte = serial.read(); + + switch(state) { + case NORMAL: + if (incomingByte == ACK) { + prefixCb[ACK](NULL); + } else { + readPrefix = byteToPrefix(incomingbyte); + readLength = byteToLength(incomingbyte); + if (readLength > 0) { + switch(readPrefix) { + case CAT_PREFIX: + case RAD_EEPROM_WRITE_PREFIX: + state = CAT_MODE; + break; + + case IOP_PREFIX: + state = IOP_MODE; + iopMsg.len = readLength - 1; + cmdLength = 0; + break; + + case RAD_EEPROM_READ_PREFIX: + state = EEPROM_READ; + readLength = 5; + eepromMagicFlag = 0; + break; + + default: + // should never happen + break; + } + } + } + break; + + case CAT_MODE: + // In CAT mode, we just pass thru the remaining bytes to the PC. + if + USBSERIAL.write(incomingByte); + readLength--; + if (readLength == 0) { + state = NORMAL; + } + break; + + case IOP_MODE: + cmdBuffer[cmdLength] = incomingByte; + cmdLength++; + readLength--; + if (readLength == 0) { + processIOPCommand(cmdBuffer, cmdLength); + state = NORMAL; + } + break; + + case EEPROM_READ: + readLength--; + switch(readLength) { + case 4: + eepromStartIndex = incomingByte; + if (incomingByte == 0x16) { + magicFlag++; + } + break; + + case 3: + eepromStartIndex += (256 * incomingByte); + if (incomingByte == 0xe8) { + magicFlag++; + } + break; + + case 2: + eepromReadLength = incomingByte; + break; + + case 1: + eepromReadLength += (256 * incomingByte); + break; + + case 0: + USBSERIAL.write(incomingByte); + if (magicFlag == 2) { + readLength = 126 + 2; + } else { + readLength = eepromReadLength + 2; + } + state = CAT_MODE; + break; + + default: + // should never happen + break; + } + break; + + case EEPROM_WRITE: + // TODO + break; + + default: + // should never happen... + break; + } + } +} + +void Translator::registerPrefixCb(PrefixID id, void (*func)(void*)) +{ + if (id >= 0 && id < NUM_PREFIX_IDS) { + prefixCb[id] = func; + } + +void Translator::registerMessageCb(MessageID id, void (*func)(void*)) +{ + if (id >= 0 && id < NUM_MESSAGE_IDS) { + messageCb[id] = func; + } +} + +void Translator::pack_ModeCommand(IOPMessage& m, RigMode r) +{ + m.id = IOP_MODE_COMMAND; + m.len = 1; + m.data[0] = r; +} + +void Translator::unpack_ModeCommand(IOPMessage const& m) +{ + *mode = RigMode(m.data[0]); +} + +void Translator::pack_CWConfig(IOPMessage &m, CWConfig const& c) +{ + m.id = IOP_CW_CONFIG_MSG; + m.len = 6; + // mode + m.data[0] = byte(c.mode); + // flags + m.data[1] = NO_FLAGS; + m.data[1] |= (c.reversed ? REVERSED : 0); + // parameters + m.data[2] = byte(c.wpm); + m.data[3] = byte(c.weight * 10.0); + m.data[4] = byte(c.sidetone >> 8); + m.data[5] = byte(c.sidetone | 0x0F); +} + +void Translator::unpack_CWConfig(IOPMessage const &m) +{ + //if (m.id != IOP_CW_CONFIG_MSG || m.len != 6) { + // // do some error thing... + //} + //mode + cwConfig->mode = KeyMode(m.data[0]); + // flags + cwConfig->reversed = bool(m.data[1] & REVERSED); + // parameters + cwConfig->wpm = uint8_t(m.data[2]); + cwConfig->weight = float(m.data[3]) / 10.0; + cwConfig->sidetone = (m.data[4] << 8) | m.data[5]; +} + +void Translator::pack_ModeRequest(IOPMessage& m) +{ + m.id = IOP_MODE_REQUEST; + m.len = 0; +} + +void Translator::unpack_ModeRequest(IOPMessage const& m) +{ +} + +void Translator::unpack(IOPMessage const& m) +{ + switch (m.id) { + case IOP_MODE_COMMAND: + unpack_ModeCommand(m); + messageCb[m.id](mode); + break; + + case IOP_START_TX_COMMAND: + break; + + case IOP_STOP_TX_COMMAND: + break; + + case IOP_CW_CONFIG_MSG: + unpack_CWConfig(m); + messageCb[m.id](cwConfig); + break; + + case IOP_MODE_REQUEST: + unpack_ModeRequest(m); + messageCb[m.id](NULL); + break; + + case IOP_CW_STATUS_MSG: + break; + } +} +*/ +//====================================================================== +// EOF +//====================================================================== diff --git a/iopcomm/iopcomm.h b/iopcomm/iopcomm.h new file mode 100644 index 0000000..9922320 --- /dev/null +++ b/iopcomm/iopcomm.h @@ -0,0 +1,211 @@ +//====================================================================== +// iopcomm.h +//====================================================================== + +#ifndef __iopcomm_h__ +#define __iopcomm_h__ + +#include + +/* Message prefixes, starting with the ACK message (0). Message format + * consists of 1 or more bytes, where: + * b[0] - leftmost 3 bits == prefix + * - rightmost 5 bits == length of data (0-31) + * b[1]...b[31] (if present) - data bits + */ + +enum PrefixID { + ACK = 0, + CAT_PREFIX, + IOP_PREFIX, + RAD_EEPROM_READ_PREFIX, + RAD_EEPROM_WRITE_PREFIX, + IOP_EEPROM_READ_PREFIX, + IOP_EEPROM_WRITE_PREFIX, + NUM_PREFIX_IDS +}; + +/* Masks for the leftmost 3 bits (prefix) and rightmost 5 bits (length) + * of a message. + */ + +#define PREFIX_MASK 0xE0 +#define LENGTH_MASK 0x1F + +/* Convert a prefix and length to a byte, by shifting the prefix left + * 5 bits and OR-ing with the 5 bits of length. + */ + +inline uint8_t prefixAndLengthToByte(PrefixID id, uint8_t len) +{ + return (uint8_t(id) << 5) | (len & LENGTH_MASK); +} + +/* Extract the prefix (leftmost 3 bits) from a byte. + */ + +inline PrefixID byteToPrefix(uint8_t b) +{ + return PrefixID(b >> 5); +} + +/* Extract the length (rightmost 5 bits) from a byte. + */ + +inline uint8_t byteToLength(byte b) +{ + return uint8_t(b & LENGTH_MASK); +} + +/* Message IDs/types, for IOP messages (messages between the Raduino and + * the IOP, not including CAT messages to be passed through). + */ + +enum MessageID { + // Commands + IOP_MODE_COMMAND = 0, + IOP_START_TX_COMMAND, + IOP_STOP_TX_COMMAND, + IOP_CW_CONFIG_MSG, + + // Requests + IOP_MODE_REQUEST, + IOP_CW_STATUS_MSG, + + // add any new elements here + NUM_MESSAGE_IDS +}; + +/* Max length of an IOP message payload (data), based on the following + * message format: + * + * byte 0: prefix (3 bits) + length (5 bits) + * byte 1: message ID (type) + * bytes 2-31: data bytes + */ + +#define IOP_MESSAGE_MAX_LEN 30 + +/* Rig modes. Currently just defined as what the IOP cares about, but + * maybe these need to be expanded to encompass all of the rig modes. + * (e.g. USB, LSB, etc.) + */ + +enum RigMode { + MODE_SSB = 0, + MODE_DIGI, + MODE_CW, + MODE_TEST, + // add any new elements here + NUM_RIG_MODES +}; + +/* Keyer modes. + */ + +enum KeyMode { + STRAIGHT = 0, + IAMBIC_A, + IAMBIC_B, + //ULTIMATIC, + //BUG, + // add any new elements here + NUM_KEY_MODES +}; + +const unsigned char MODE_LETTER[3] = {'S', 'A', 'B'}; + +const uint8_t NO_FLAGS = 0; +const uint8_t REVERSED = 1; + +struct IOPMessage { + uint8_t id; + uint8_t len; + uint8_t data[IOP_MESSAGE_MAX_LEN]; +}; + + +void sendIOPMessage(IOPMessage const&); +void recvIOPMessage(IOPMessage&, const uint8_t*, int); + +void sendIOPModeCommand(RigMode); +void sendIOPStartTxCommand(); +void sendIOPStopTxCommand(); +void sendIOPModeRequest(); + +//====================================================================== +// CW CONFIGURATION +//====================================================================== + +struct CWConfig { + // mode + KeyMode mode = IAMBIC_A; + // flags + bool reversed = false; + // parameters + uint8_t wpm = 15; + float weight = 3.0; + uint16_t sidetone = 700; +}; + +//====================================================================== +// TRANSLATOR +//====================================================================== + +/* +class Translator +{ + public: + Translator(RigMode&, CWConfig&, Stream&); + + void registerPrefixCb(PrefixID id, void (*func)(void*)); + void registerMessageCb(MessageID id, void (*func)(void*)); + + void sendACK(); + void sendIOPMessage(IOPMessage const&); + + void receive(); + + void pack_ModeCommand(IOPMessage&, RigMode); + void pack_CWConfig(IOPMessage&, CWConfig const&); + void pack_ModeRequest(IOPMessage&); + + void unpack(IOPMessage const&); + + protected: + void unpack_ModeCommand(IOPMessage const&); + void unpack_CWConfig(IOPMessage const&); + void unpack_ModeRequest(IOPMessage const&); + + private: + RigMode& mode; + CWConfig& cwConfig; + Stream& serial; + + void (*prefixCb[NUM_PREFIX_IDS])(void*); + void (*messageCb[NUM_MESSAGE_IDS])(void*); +}; +*/ + +//====================================================================== +// CW STATUS MESSAGE +//====================================================================== + +//void packT_DisplayText(TMessage &m, CWConfig const &c) +//{ +// m.id = IOP_CW_STATUS_MSG; +// m.len = 3; +// m.data[0] = ' '; // growth +// m.data[1] = MODE_LETTER[c.mode]; +// m.data[2] = ' '; // TODO: RX filter width +//} + +// No unpack required: this is a string to put on the display. + +//====================================================================== + +#endif + +//====================================================================== +// EOF +//====================================================================== diff --git a/ubitx_iop/audio.h b/ubitx_iop/audio.h index 382949c..7b6441e 100644 --- a/ubitx_iop/audio.h +++ b/ubitx_iop/audio.h @@ -22,6 +22,7 @@ enum TxInput { TX_MIC_IN = -1, TX_LINE_IN = 0, TX_USB_IN = 1, + TX_TEST_IN = 2, }; enum TxOutput { diff --git a/ubitx_iop/audio.ino b/ubitx_iop/audio.ino index 1f452a6..3805dbd 100644 --- a/ubitx_iop/audio.ino +++ b/ubitx_iop/audio.ino @@ -16,43 +16,49 @@ extern IOPConfig iopConfig; #include #include #include - + // GUItool: begin automatically generated code -AudioInputI2S inLine; //xy=224,194 -AudioInputUSB inUSB; //xy=224,303 -AudioAnalyzeRMS rmsRX; //xy=399,71 -AudioAnalyzePeak peakRX; //xy=403,122 -AudioMixer4 mixRX; //xy=460,187 -AudioMixer4 mixTX; //xy=460,303 -AudioAmplifier calRxUSB; //xy=650,189 -AudioAmplifier calRxSpkr; //xy=654,79 -AudioAmplifier calRxLine; //xy=657,136 -AudioAmplifier calTxLine; //xy=669,272 -AudioAmplifier calTxUSB; //xy=670,329 -AudioAnalyzePeak peakTX; //xy=685,402 -AudioAnalyzeRMS rmsTX; //xy=699,457 -AudioOutputAnalog outSpkr; //xy=830,79 -AudioOutputUSB outUSB; //xy=839,279 -AudioOutputI2S outLine; //xy=842,228 -AudioConnection patchCord1(inLine, 0, rmsRX, 0); -AudioConnection patchCord2(inLine, 0, peakRX, 0); -AudioConnection patchCord3(inLine, 0, mixRX, 0); -AudioConnection patchCord4(inLine, 1, mixTX, 0); -AudioConnection patchCord5(inUSB, 0, mixRX, 1); -AudioConnection patchCord6(inUSB, 1, mixTX, 1); -AudioConnection patchCord7(mixRX, calRxSpkr); -AudioConnection patchCord8(mixRX, calRxLine); -AudioConnection patchCord9(mixRX, calRxUSB); -AudioConnection patchCord10(mixTX, calTxLine); -AudioConnection patchCord11(mixTX, calTxUSB); -AudioConnection patchCord12(mixTX, peakTX); -AudioConnection patchCord13(mixTX, rmsTX); -AudioConnection patchCord14(calRxUSB, 0, outUSB, 0); -AudioConnection patchCord15(calRxSpkr, outSpkr); -AudioConnection patchCord16(calRxLine, 0, outLine, 0); -AudioConnection patchCord17(calTxLine, 0, outLine, 1); -AudioConnection patchCord18(calTxUSB, 0, outUSB, 1); -AudioControlSGTL5000 audioCtrl; //xy=407,467 +AudioSynthWaveformSine sideTone; //xy=204,221 +AudioInputI2S inLine; //xy=208,170 +AudioSynthWaveformSine sine2; //xy=207,423 +AudioInputUSB inUSB; //xy=208,279 +AudioSynthWaveformSine sine1; //xy=208,382 +AudioAnalyzeRMS rmsRX; //xy=383,47 +AudioAnalyzePeak peakRX; //xy=387,98 +AudioMixer4 mixRX; //xy=444,163 +AudioMixer4 mixTX; //xy=444,279 +AudioAmplifier calRxUSB; //xy=634,165 +AudioAmplifier calRxSpkr; //xy=638,55 +AudioAmplifier calRxLine; //xy=641,112 +AudioAmplifier calTxLine; //xy=653,248 +AudioAmplifier calTxUSB; //xy=654,305 +AudioAnalyzePeak peakTX; //xy=669,378 +AudioAnalyzeRMS rmsTX; //xy=683,433 +AudioOutputAnalog outSpkr; //xy=814,55 +AudioOutputUSB outUSB; //xy=823,255 +AudioOutputI2S outLine; //xy=826,204 +AudioConnection patchCord1(sideTone, 0, mixRX, 2); +AudioConnection patchCord2(inLine, 0, rmsRX, 0); +AudioConnection patchCord3(inLine, 0, peakRX, 0); +AudioConnection patchCord4(inLine, 0, mixRX, 0); +AudioConnection patchCord5(inLine, 1, mixTX, 0); +AudioConnection patchCord6(sine2, 0, mixTX, 3); +AudioConnection patchCord7(inUSB, 0, mixRX, 1); +AudioConnection patchCord8(inUSB, 1, mixTX, 1); +AudioConnection patchCord9(sine1, 0, mixTX, 2); +AudioConnection patchCord10(mixRX, calRxSpkr); +AudioConnection patchCord11(mixRX, calRxLine); +AudioConnection patchCord12(mixRX, calRxUSB); +AudioConnection patchCord13(mixTX, calTxLine); +AudioConnection patchCord14(mixTX, calTxUSB); +AudioConnection patchCord15(mixTX, peakTX); +AudioConnection patchCord16(mixTX, rmsTX); +AudioConnection patchCord17(calRxUSB, 0, outUSB, 0); +AudioConnection patchCord18(calRxSpkr, outSpkr); +AudioConnection patchCord19(calRxLine, 0, outLine, 0); +AudioConnection patchCord20(calTxLine, 0, outLine, 1); +AudioConnection patchCord21(calTxUSB, 0, outUSB, 1); +AudioControlSGTL5000 audioCtrl; //xy=391,443 // GUItool: end automatically generated code RxInput audioRxInput; @@ -75,11 +81,30 @@ void audioInit() //audioCtrl.dacVolumeRamp(); // if this seems too slow, might try dacVolumeRampLinear(). //audioCtrl.dacVolume(1.0, 0.0); // we're going to mute TX audio via the DAC unless we're transmitting + updateRxRigIn(); + updateRxSpkrOut(); + updateRxLineOut(); + updateRxUSBOut(); + + // Note - these really just need to be init functions; keep things muted while setting them up. + updateTxMicIn(); + updateTxLineIn(); + updateTxUSBIn(); + updateTxTwoToneIn(); + + updateTxRigOut(); + audioSelectRxInput(RX_RIG_IN); audioSelectTxInput(TX_MIC_IN); // superfluous I think //audioCtrl.adcHighPassFilterDisable(); audioCtrl.audioPreProcessorEnable(); + + // setup the two-tone generator + sine1.frequency(700); + sine2.frequency(1900); + sine1.amplitude(0); + sine2.amplitude(0); } inline void updateRxRigIn() @@ -180,6 +205,30 @@ inline void restoreTxUSBIn() mixTX.gain(TX_USB_IN, iopConfig.txUSBInVol * iopConfig.txUSBInCal); } +inline void updateTxTwoToneIn() +{ + sine1.amplitude(0.5); + sine2.amplitude(0.5); + mixTX.gain(TX_TEST_IN, iopConfig.txSine1Vol); + mixTX.gain(TX_TEST_IN + 1, iopConfig.txSine2Vol); +} + +inline void muteTxTwoToneIn() +{ + mixTX.gain(TX_TEST_IN, 0.0); + mixTX.gain(TX_TEST_IN + 1, 0.0); + sine1.amplitude(0); + sine2.amplitude(0); +} + +inline void restoreTxTwoToneIn() +{ + sine1.amplitude(0.5); + sine2.amplitude(0.5); + mixTX.gain(TX_TEST_IN, iopConfig.txSine1Vol); + mixTX.gain(TX_TEST_IN + 1, iopConfig.txSine2Vol); +} + inline void updateTxRigOut() { calTxLine.gain(iopConfig.txRigOutCal); @@ -224,8 +273,7 @@ void audioSelectTxInput(TxInput input) //muteTxMicIn(); // redundant w/ Line-In muteTxLineIn(); muteTxUSBIn(); - mixTX.gain(2, 0); - mixTX.gain(3, 0); + muteTxTwoToneIn(); /* switch(input) { case TX_MIC_IN: muteTxUSBIn(); @@ -293,6 +341,11 @@ void audioTransmit() muteRxRigIn(); updateTxUSBIn(); break; + + case MODE_TEST: + muteRxRigIn(); + updateTxTwoToneIn(); + break; } } @@ -323,6 +376,11 @@ void audioReceive() muteTxUSBIn(); restoreRxRigIn(); break; + + case MODE_TEST: + muteTxTwoToneIn(); + restoreRxRigIn(); + break; } } diff --git a/ubitx_iop/cat.h b/ubitx_iop/cat.h index 1283eb4..4167778 100644 --- a/ubitx_iop/cat.h +++ b/ubitx_iop/cat.h @@ -5,134 +5,18 @@ #ifndef __iop_cat_h__ #define __iop_cat_h__ -#define ACK 0 -#define CAT_PREFIX 0xC0 -#define IOP_PREFIX 0xD0 -#define EEPROM_READ_PREFIX 0xE0 -#define EEPROM_WRITE_PREFIX 0xF0 - -#define IOP_MODE_COMMAND 0x00 -#define IOP_START_TX_COMMAND 0x01 -#define IOP_STOP_TX_COMMAND 0x02 -#define IOP_CW_CONFIG_MSG 0x03 - -#define IOP_MODE_REQUEST 0x00 -#define IOP_CW_STATUS_MSG 0x03 - -#define IOP_MODE_SSB 0x00 -#define IOP_MODE_DIGI 0x01 -#define IOP_MODE_CW 0x02 +//#define ACK 0 +//#define CAT_PREFIX 0xC0 +//#define IOP_PREFIX 0xD0 +//#define EEPROM_READ_PREFIX 0xE0 +//#define EEPROM_WRITE_PREFIX 0xF0 +#include #include "config.h" #define USBSERIAL Serial #define HWSERIAL Serial1 -/*struct IOPTMsg { -}; -*/ - -enum KeyMode { - STRAIGHT = 0, - IAMBIC_A, - IAMBIC_B, - //ULTIMATIC, - //BUG, -}; - -const byte MODE_LETTER[3] = {'S', 'A', 'B'}; - -const byte NO_FLAGS = 0; -const byte REVERSED = 1; - -#define R_MESSAGE_MAX_LEN 32 -#define T_MESSAGE_MAX_LEN 32 - -struct RMessage { - uint8_t id; - uint8_t len; - byte data[R_MESSAGE_MAX_LEN]; -}; - -struct TMessage { - uint8_t id; - uint8_t len; - byte data[T_MESSAGE_MAX_LEN]; -}; - -//====================================================================== -// CW CONFIGURATION MESSAGE -//====================================================================== - -struct CWConfig { - // mode - KeyMode mode = IAMBIC_A; - // flags - bool reversed = false; - // parameters - uint8_t wpm = 15; - float weight = 3.0; - uint16_t sidetone = 700; -}; - -void packR_CWConfig(RMessage &m, CWConfig const &c) -{ - m.id = IOP_CW_CONFIG_MSG; - m.len = 6; - // mode - m.data[0] = byte(c.mode); - // flags - m.data[1] = NO_FLAGS; - m.data[1] |= (c.reversed ? REVERSED : 0); - // parameters - m.data[2] = byte(c.wpm); - m.data[3] = byte(c.weight * 10.0); - m.data[4] = byte(c.sidetone >> 8); - m.data[5] = byte(c.sidetone | 0x0F); -} - -void unpackR_CWConfig(CWConfig &c, RMessage const &m) -{ - //if (m.id != IOP_CW_CONFIG_MSG || m.len != 6) { - // // do some error thing... - //} - //mode - c.mode = KeyMode(m.data[0]); - // flags - c.reversed = bool(m.data[1] & REVERSED); - // parameters - c.wpm = uint8_t(m.data[2]); - c.weight = float(m.data[3]) / 10.0; - c.sidetone = (m.data[4] << 8) | m.data[5]; -} - -//====================================================================== -// MODE REQUEST MESSAGE -//====================================================================== - -void packT_ModeRequest(TMessage &m) -{ - m.id = IOP_MODE_REQUEST; - m.len = 0; -} - -//====================================================================== -// CW STATUS MESSAGE -//====================================================================== - -//void packT_DisplayText(TMessage &m, CWConfig const &c) -//{ -// m.id = IOP_CW_STATUS_MSG; -// m.len = 3; -// m.data[0] = ' '; // growth -// m.data[1] = MODE_LETTER[c.mode]; -// m.data[2] = ' '; // TODO: RX filter width -//} - -// No unpack required: this is a string to put on the display. - -//====================================================================== - void initCAT(long, int); void serviceCAT(); diff --git a/ubitx_iop/cat.ino b/ubitx_iop/cat.ino index abe1ea6..7ee5d99 100644 --- a/ubitx_iop/cat.ino +++ b/ubitx_iop/cat.ino @@ -4,18 +4,9 @@ #include "cat.h" -//#define ACK 0 -//#define CAT_PREFIX 0xC0 -//#define IOP_PREFIX 0xD0 -//#define EEPROM_READ_PREFIX 0xE0 -//#define EEPROM_WRITE_PREFIX 0xF0 -// -//#define IOP_MODE_COMMAND 0x00 -//#define IOP_START_TX_COMMAND 0x01 -//#define IOP_STOP_TX_COMMAND 0x02 -//#define IOP_MODE_SSB 0x00 -//#define IOP_MODE_DIGI 0x01 -//#define IOP_MODE_CW 0x02 +// make these messages static inside a function +IOPMessage inBuf; // input message buffer +IOPMessage outBuf; // output message buffer //====================================================================== // CAT from PC-to-IOP @@ -39,50 +30,60 @@ void initCAT(long baud, int portConfig) // CAT with PC via USB USBSERIAL.begin(baud); USBSERIAL.flush(); - -#if not defined(FACTORY_CALIBRATION) - // CAT with Raduino via UART - HWSERIAL.begin(baud, portConfig); - USBSERIAL.flush(); +#if defined(DEBUG) + USBSERIAL.println("IOP: opened USB serial port (to PC)"); #endif + + // Comm (including CAT passthru) with Raduino via UART + HWSERIAL.begin(baud, portConfig); + HWSERIAL.flush(); +#if defined(DEBUG) + USBSERIAL.println("IOP: opened H/W serial port (to Raduino)"); +#endif + + //sendIOPModeRequest(); // Raduino doesn't support this yet } //====================================================================== -void processIOPCommand(const byte* buf, int len) +void processIOPCommand(IOPMessage const& m) { - if (len > 0) { - switch(buf[0]) { - case IOP_MODE_COMMAND: - if (len < 2) { - return; - } else { - setRigMode(RigMode(buf[1])); - #if defined(DEBUG) - USBSERIAL.print("DEBUG: mode "); - switch(rigMode) { - case MODE_CW: - USBSERIAL.println("CW"); - break; - case MODE_SSB: - USBSERIAL.println("SSB"); - break; - case MODE_DIGI: - USBSERIAL.println("DIGI"); - break; - } - #endif - } - break; - - case IOP_START_TX_COMMAND: - catPTTOn(); - break; - - case IOP_STOP_TX_COMMAND: - catPTTOff(); - break; + switch(inBuf.id) { + case IOP_MODE_COMMAND: + USBDEBUG("IOP_MODE_COMMAND received (from Raduino)"); + if (m.len < 1) { + return; + } else { + setRigMode(m.data[0]); +#if defined(DEBUG) + switch(rigMode) { + case MODE_CW: + USBDEBUG("new mode - CW"); + break; + case MODE_SSB: + USBDEBUG("new mode - SSB"); + break; + case MODE_DIGI: + USBDEBUG("new mode - DIGI"); + break; + case MODE_TEST: + USBDEBUG("new mode - TEST"); + break; + } +#endif } + break; + + case IOP_START_TX_COMMAND: + catPTTOn(); + break; + + case IOP_STOP_TX_COMMAND: + catPTTOff(); + break; + + case IOP_CW_CONFIG_MSG: + break; } } @@ -117,15 +118,16 @@ void processCalCommand(const char* buf) //====================================================================== -enum serial_mode_t { +enum SerialState { NORMAL = 0, CAT_MODE, IOP_MODE, EEPROM_READ, EEPROM_WRITE, -} serialMode = NORMAL; +} serialState = NORMAL; -int readLength = 0; +uint8_t readPrefix = 0; +uint8_t readLength = 0; int cmdLength = 0; byte cmdBuffer[16]; @@ -139,7 +141,7 @@ int magicFlag = 0; void serviceCAT() { - int incomingByte; + uint8_t incomingByte; // read from the USB serial, pass through to UART serial for (int i = 0; i < USBSERIAL.available(); i++) { @@ -172,33 +174,56 @@ void serviceCAT() } } #else + // Don't pass CAT commands through if in DEBUG mode. +#if not defined(DEBUG) HWSERIAL.write(incomingByte); +#endif } // read from the UART serial, see what we need to do with it for (int i = 0; i < HWSERIAL.available(); i++) { incomingByte = HWSERIAL.read(); +#if defined(DEBUG) + char ibBuff[20]; + itoa(serialState, ibBuff, 10); + USBSERIAL.print("IOP: serialState = "); + USBSERIAL.print(ibBuff); + itoa(incomingByte, ibBuff, 10); + USBSERIAL.print("; incomingByte = "); + USBSERIAL.println(ibBuff); +#endif - switch(serialMode) { + switch(serialState) { case NORMAL: if (incomingByte == ACK) { USBSERIAL.write(incomingByte); - } else { - readLength = incomingByte & 0x0F; + } else { + readPrefix = byteToPrefix(incomingByte); + readLength = byteToLength(incomingByte); +#if defined(DEBUG) + itoa(readPrefix, ibBuff, 10); + USBSERIAL.print("IOP: readPrefix = "); + USBSERIAL.print(ibBuff); + itoa(readLength, ibBuff, 10); + USBSERIAL.print("; readLength = "); + USBSERIAL.println(ibBuff); +#endif if (readLength > 0) { - switch(incomingByte & 0xF0) { + USBDEBUG("readLength > 0"); + switch(readPrefix) { case CAT_PREFIX: - case EEPROM_WRITE_PREFIX: - serialMode = CAT_MODE; + case RAD_EEPROM_WRITE_PREFIX: + serialState = CAT_MODE; break; case IOP_PREFIX: - serialMode = IOP_MODE; - cmdLength = 0; + USBDEBUG("entering IOP_MODE"); + serialState = IOP_MODE; + cmdLength = 0; break; - case EEPROM_READ_PREFIX: - serialMode = EEPROM_READ; + case RAD_EEPROM_READ_PREFIX: + serialState = EEPROM_READ; readLength = 5; magicFlag = 0; break; @@ -216,7 +241,7 @@ void serviceCAT() USBSERIAL.write(incomingByte); readLength--; if (readLength == 0) { - serialMode = NORMAL; + serialState = NORMAL; } break; @@ -225,8 +250,9 @@ void serviceCAT() cmdLength++; readLength--; if (readLength == 0) { - processIOPCommand(cmdBuffer, cmdLength); - serialMode = NORMAL; + recvIOPMessage(inBuf, cmdBuffer, cmdLength); + processIOPCommand(inBuf); + serialState = NORMAL; } break; @@ -262,7 +288,7 @@ void serviceCAT() } else { readLength = eepromReadLength + 2; } - serialMode = CAT_MODE; + serialState = CAT_MODE; break; default: diff --git a/ubitx_iop/config.h b/ubitx_iop/config.h index 008a51b..530a743 100644 --- a/ubitx_iop/config.h +++ b/ubitx_iop/config.h @@ -5,13 +5,15 @@ #ifndef __iop_config_h__ #define __iop_config_h__ +#include + #define KEYER_LEFT_PADDLE_PIN 16 #define KEYER_RIGHT_PADDLE_PIN 17 // Uncomment to use the "factory" calibration mode. This is intended to // allow calibration of IOP settings, separately from the uBITX/Raduino. // There will be no pass-thru of any CAT. -//#define FACTORY_CALIBRATION +#define FACTORY_CALIBRATION // IOPConfig // Used to store configuration parameters during runtime, as well as to @@ -22,7 +24,7 @@ struct IOPConfig { // rig-in parameters (RX) uint8_t rxRigInLevel = 5; - float rxRigInVol = 0.7; + float rxRigInVol = 1.0; //0.7; float rxRigInCal = 1.0; // USB-in parameters (RX) - debug/monitor use only bool rxUSBInEnable = false; @@ -68,21 +70,27 @@ struct IOPConfig { // microphone-in parameters (TX) uint8_t txMicInGain = 12; - float txMicInVol = 0.7; + float txMicInVol = 1.0; //0.7; float txMicInCal = 1.0; // line-in parameters (TX) uint8_t txLineInLevel = 5; - float txLineInVol = 0.7; + float txLineInVol = 1.0; //0.7; float txLineInCal = 1.30; // USB-in parameters (TX) - float txUSBInVol = 0.7; + float txUSBInVol = 1.0; //0.7; float txUSBInCal = 1.0; + // two-tone parameters (TX) + float txSine1Vol = 1.0; + float txSine2Vol = 1.0; // rig-out parameters (TX) - default settings are based on hitting 70% of 25mVrms w/ mic uint8_t txRigOutLevel = 31; - float txRigOutCal = 0.043; + float txRigOutCal = 0.043; // USB-out parameters (TX)- debug/monitor use only bool txUSBOutEnable = true; float txUSBOutCal = 1.0; + + // CW configuration + CWConfig cw; }; extern IOPConfig iopConfig; diff --git a/ubitx_iop/ubitx_iop.h b/ubitx_iop/ubitx_iop.h index 7adc9e3..6769ad6 100644 --- a/ubitx_iop/ubitx_iop.h +++ b/ubitx_iop/ubitx_iop.h @@ -14,11 +14,17 @@ // comment this out to disable debugging code //#define DEBUG -enum RigMode { - MODE_SSB = 0, - MODE_DIGI = 1, - MODE_CW = 2, -}; +#if defined(DEBUG) +#define USBDEBUG(x) USBSERIAL.print("IOP: "); USBSERIAL.println(x); +#else +#define USBDEBUG(x) +#endif + +//enum RigMode { +// MODE_SSB = 0, +// MODE_DIGI = 1, +// MODE_CW = 2, +//}; enum TxState { TX_OFF = 0, diff --git a/ubitx_iop/ubitx_iop.ino b/ubitx_iop/ubitx_iop.ino index bd29de2..4f4eb79 100644 --- a/ubitx_iop/ubitx_iop.ino +++ b/ubitx_iop/ubitx_iop.ino @@ -2,6 +2,7 @@ // ubitx_iop.ino //====================================================================== +#include #include "ubitx_iop.h" #include @@ -145,6 +146,13 @@ void checkMicPTT() case MODE_DIGI: // Mic PTT actuation during Digital does nothing. break; + + case MODE_TEST: + txState = TX_MIC; + audioSelectTxInput(TX_TEST_IN); + audioTransmit(); + digitalWrite(PTT_KEY_OUT_PIN, LOW); + break; } } } @@ -186,6 +194,13 @@ void checkLinePTT() case MODE_DIGI: // Line PTT actuation during Digital does nothing. break; + + case MODE_TEST: + txState = TX_LINE; + audioSelectTxInput(TX_TEST_IN); + audioTransmit(); + digitalWrite(PTT_KEY_OUT_PIN, LOW); + break; } } } @@ -198,6 +213,7 @@ void setRigMode(RigMode m) switch(rigMode) { case MODE_SSB: + case MODE_TEST: // SSB sets the TX audio input to line-in. Note that this will be // automatically overridden by mic-in, if the mic PTT is pressed. audioSelectTxInput(TX_LINE_IN); @@ -244,7 +260,11 @@ void setup() { digitalWrite(PTT_KEY_OUT_PIN, HIGH); audioInit(); +#if defined(FACTORY_CALIBRATION) + setRigMode(MODE_TEST); +#else setRigMode(MODE_SSB); +#endif } //======================================================================