/*! * @file RigState.h */ #ifndef __RigState_h__ #define __RigState_h__ #include #define UBITX_VFOB_FLAG 0x00000001 #define UBITX_SPLIT_FLAG 0x00000002 #define UBITX_RIT_FLAG 0x00000004 #define UBITX_XIT_FLAG 0x00000008 #define UBITX_CW_FLAG 0x00000010 #define UBITX_USB_FLAG 0x00000020 #define UBITX_TX_FLAG 0x00000040 #define UBITX_SIDETONE_MASK 0x000007FF #define UBITX_KEYER_MODE_MASK 0x00003800 #ifdef TEENSYDUINO #define DISABLEINTS(CMD) do { noInterrupts(); CMD; interrupts(); } while (0) #else #define DISABLEINTS(CMD) do { CMD; } while (0) #endif enum RigStateWord { DIRTY_WORD = 0, VFOA_WORD, VFOB_WORD, OFFSETS_WORD, FLAGS_WORD, KEYER_WORD, NUM_WORDS }; inline RigStateWord& operator++(RigStateWord& orig) { orig = static_cast(orig + 1); // NOTE: Will overflow... return orig; } inline RigStateWord operator++(RigStateWord& orig, int) { RigStateWord rVal = orig; ++orig; return rVal; } struct UBitxRigState { volatile uint32_t data[NUM_WORDS] = {0}; void begin(); void send_RIGINF(); void receive_RIGINF(int numBytes = sizeof(data)); /*! * @brief Set the dirty bit for the specified word. * * @param w * The word to mark as dirty. */ inline void setDirty(RigStateWord w) { data[DIRTY_WORD] |= w < NUM_WORDS ? 1 << w : 0; } /*! * @brief Set the dirty bits for all words. */ inline void setDirty() { DISABLEINTS( data[DIRTY_WORD] = 0xFFFFFFFF ); } /*! * @brief Clear the dirty bit for the specified word. * * @param w * The word to mark as clean. */ inline void setClean(RigStateWord w) { data[DIRTY_WORD] &= ~(w < NUM_WORDS ? 1 << w : 0); } /*! * @brief Clear the dirty bits for all words. */ inline void setClean() { DISABLEINTS( data[DIRTY_WORD] = 0 ); } /*! * @brief Check whether the specified word is clean. * * @param w * The word to check for clean status. * * @return True if the word is clean. */ inline bool isClean(RigStateWord w) { bool clean; DISABLEINTS( clean = ((1 << w) & data[DIRTY_WORD]) > 0 ? false : true ); return clean; } /*! * @brief Check whether the data is clean (as a whole). * * @return True if the data is clean (no dirty fields). */ inline bool isClean() { bool clean; DISABLEINTS( clean = data[DIRTY_WORD] == 0 ); return clean; } /*! * @brief Check whether the specified word is dirty. * * @param w * The word to check for dirty status. * * @return True if the word is dirty. */ inline bool isDirty(RigStateWord w) { bool dirty; DISABLEINTS( dirty = ((1 << w) & data[DIRTY_WORD]) > 0 ? true : false ); return dirty; } /*! * @brief Check whether the data is dirty (as a whole). * * @return True if the data is dirty (at least one dirty field). */ inline bool isDirty() { bool dirty; DISABLEINTS( dirty = data[DIRTY_WORD] != 0 ); return dirty; } /*! * @brief Set the VFO A frequency. * * @param freq * The new frequency in Hz. */ inline void setFreqA(uint32_t freq, bool mark = true) { DISABLEINTS( data[VFOA_WORD] = freq; if (mark) setDirty(VFOA_WORD) ); } inline uint32_t getFreqA() const { uint32_t result; DISABLEINTS( result = data[VFOA_WORD] ); return result; } /*! * @brief Set the VFO B frequency. * * @param freq * The new frequency in Hz. */ inline void setFreqB(uint32_t freq, bool mark = true) { DISABLEINTS( data[VFOB_WORD] = freq ); } inline uint32_t getFreqB() const { uint32_t result; DISABLEINTS( result = data[VFOB_WORD] ); return result; } inline void setRIT(int16_t offset, bool mark = true) { DISABLEINTS( data[OFFSETS_WORD] = (int32_t(offset) << 16) | (0x0000FFFF & data[OFFSETS_WORD]); if (mark) setDirty(OFFSETS_WORD) ); } inline int16_t getRIT() const { int16_t result; DISABLEINTS( result = data[OFFSETS_WORD] >> 16 ); return result; } inline void setXIT(int16_t offset, bool mark = true) { DISABLEINTS( data[OFFSETS_WORD] = (0xFFFF0000 & data[OFFSETS_WORD]) | offset; if (mark) setDirty(OFFSETS_WORD) ); } inline int16_t getXIT() const { int16_t result; DISABLEINTS( result = 0x0000FFFF & data[OFFSETS_WORD] ); return result; } inline void setVFOA(bool mark = true) { DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_VFOB_FLAG; if (mark) setDirty(FLAGS_WORD) ); } inline void setVFOB(bool mark = true) { DISABLEINTS( data[FLAGS_WORD] |= UBITX_VFOB_FLAG; if (mark) setDirty(FLAGS_WORD) ); } inline bool isVFOA() const { bool result; DISABLEINTS( result = data[FLAGS_WORD] & UBITX_VFOB_FLAG ? false : true ); return result; } inline bool isVFOB() const { bool result; DISABLEINTS( result = data[FLAGS_WORD] & UBITX_VFOB_FLAG ? true : false ); return result; } inline void setSplitOn(bool mark = true) { DISABLEINTS( data[FLAGS_WORD] |= UBITX_SPLIT_FLAG; if (mark) setDirty(FLAGS_WORD) ); } inline void setSplitOff(bool mark = true) { DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_SPLIT_FLAG; if (mark) setDirty(FLAGS_WORD) ); } inline bool isSplit() const { bool result; DISABLEINTS( result = data[FLAGS_WORD] & UBITX_SPLIT_FLAG ? true : false ); return result; } inline void setRITOn(bool mark = true) { DISABLEINTS( data[FLAGS_WORD] |= UBITX_RIT_FLAG; if (mark) setDirty(FLAGS_WORD) ); } inline void setRITOff(bool mark = true) { DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_RIT_FLAG; if (mark) setDirty(FLAGS_WORD) ); } inline bool isRIT() const { bool result; DISABLEINTS( result = data[FLAGS_WORD] & UBITX_RIT_FLAG ? true : false ); return result; } inline void setXITOn(bool mark = true) { DISABLEINTS( data[FLAGS_WORD] |= UBITX_XIT_FLAG; if (mark) setDirty(FLAGS_WORD) ); } inline void setXITOff(bool mark = true) { DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_XIT_FLAG; if (mark) setDirty(FLAGS_WORD) ); } inline bool isXIT() const { bool result; DISABLEINTS( result = data[FLAGS_WORD] & UBITX_XIT_FLAG ? true : false ); return result; } inline void setModeUSB(bool mark = true) { DISABLEINTS( data[FLAGS_WORD] |= UBITX_USB_FLAG; data[FLAGS_WORD] &= ~UBITX_CW_FLAG; if (mark) setDirty(FLAGS_WORD) ); } inline void setModeLSB(bool mark = true) { DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_USB_FLAG; data[FLAGS_WORD] &= ~UBITX_CW_FLAG; if (mark) setDirty(FLAGS_WORD) ); } inline void setModeCW(bool mark = true) { DISABLEINTS( data[FLAGS_WORD] |= UBITX_USB_FLAG; data[FLAGS_WORD] |= UBITX_CW_FLAG; if (mark) setDirty(FLAGS_WORD) ); } inline void setModeCWR(bool mark = true) { DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_USB_FLAG; data[FLAGS_WORD] |= UBITX_CW_FLAG; if (mark) setDirty(FLAGS_WORD) ); } inline bool isModeUSB() const { bool result; DISABLEINTS( result = ((data[FLAGS_WORD] & UBITX_USB_FLAG) > 0) && ((data[FLAGS_WORD] & UBITX_CW_FLAG) == 0) ); return result; } inline bool isModeLSB() const { bool result; DISABLEINTS( result = ((data[FLAGS_WORD] & UBITX_USB_FLAG) == 0) && ((data[FLAGS_WORD] & UBITX_CW_FLAG) == 0) ); return result; } inline bool isModeCWAny() const { bool result; DISABLEINTS( result = (data[FLAGS_WORD] & UBITX_CW_FLAG) > 0 ); return result; } inline bool isModeCW() const { bool result; DISABLEINTS( result = ((data[FLAGS_WORD] & UBITX_USB_FLAG) > 0) && ((data[FLAGS_WORD] & UBITX_CW_FLAG) > 0) ); return result; } inline bool isModeCWR() const { bool result; DISABLEINTS( result = ((data[FLAGS_WORD] & UBITX_USB_FLAG) == 0) && ((data[FLAGS_WORD] & UBITX_CW_FLAG) > 0) ); return result; } inline void setSidetone(uint16_t f, bool mark = true) { DISABLEINTS( data[KEYER_WORD] &= ~UBITX_SIDETONE_MASK; data[KEYER_WORD] |= (uint32_t(f) & UBITX_SIDETONE_MASK); if (mark) setDirty(KEYER_WORD) ); } inline uint16_t getSidetone() { uint32_t result; DISABLEINTS( result = data[KEYER_WORD] & UBITX_SIDETONE_MASK ); return uint16_t(result); } #ifdef DEBUG void serialHexState(const char* label); void serialPrettyState(const char* label); #endif #ifndef TEENSYDUINO // These methods are only defined in the Raduino (Arduino) case of the // RigState, not in the TeensyDSP (Teensy) case. void writeDirty(); // write fields FROM RigState TO Raduino void readDirty(); // read variables FROM Raduino TO RigState #else // These methods are only defined (currently) in the TeensyDSP case. void processDirty(); #endif }; #ifndef TEENSYDUINO extern UBitxRigState& rigState; #endif /* NOTE: This is all currently OBE, leaving it here for reference/future cleanup. Protocol discussion: - I2C master: Raduino - I2C slave: TeensyDSP Raduino state: - Baseline uBITX variables - I2C buffer - On I2C transmit: make updates based on current variables - On I2C receive: - Update based on received I2C responses - Update associated variables TeensyDSP state: - CAT buffer - Used to receive command from CAT (when commands arrive via Serial) - Used to transmit state to Raduino (when requested via Wire1) - Raduino buffer - Used to receive state from Raduino (when received via Wire1) - Used to transmit responses to CAT (over Serial) - Questions - How can these be synchronized? - At the tail end of an I2C request handler. Before sending the response to the Raduino via I2C: - Copy updated CAT buffer items to the Raduino buffer. - Copy updated Raduino buffer items to the CAT buffer. - In the case of conflicts, CAT wins. - Transmit the CAT buffer state to the Raduino. - TeensyDSP updates 'outgoing' state based on CAT inputs. - Make change to data. - Mark data as dirty, if different than incoming state. - When requested, Teensy DSP sends 'outgoing' state to Raduino. - Send dirty data over I2C. - Mark data as clean. */ #endif /*********************************************************************** * EOF **********************************************************************/