diff --git a/latest/Firmware/Inc/RFIC.hpp b/latest/Firmware/Inc/RFIC.hpp index 3f421eb..f89b232 100644 --- a/latest/Firmware/Inc/RFIC.hpp +++ b/latest/Firmware/Inc/RFIC.hpp @@ -50,6 +50,7 @@ public: protected: virtual void configure(); bool sendCmd(uint8_t cmd, void* params, uint8_t paramLen, void* result, uint8_t resultLen); + bool sendCmdNoWait(uint8_t cmd, void* params, uint8_t paramLen); bool isInitialized(); void powerOnReset(); bool isReceiving(); @@ -71,8 +72,8 @@ protected: uint32_t mClockPin; uint8_t mLastNRZIBit; BitState mBitState; - bool mSPIBusy; uint32_t mChipID; + bool mCTSPending = false; }; #endif /* RFIC_HPP_ */ diff --git a/latest/Firmware/Inc/Receiver.hpp b/latest/Firmware/Inc/Receiver.hpp index d514259..6bf6ebb 100644 --- a/latest/Firmware/Inc/Receiver.hpp +++ b/latest/Firmware/Inc/Receiver.hpp @@ -50,12 +50,19 @@ public: virtual void timeSlotStarted(uint32_t slot); void switchToChannel(VHFChannel channel); protected: + typedef enum + { + NO_ACTION, + RESTART_RX, + RETRIEVE_RSSI + } Action; + void startListening(VHFChannel channel, bool reconfigGPIOs); bool addBit(uint8_t bit); void resetBitScanner(); uint8_t reportRSSI(); void pushPacket(); - void processNRZIBit(uint8_t level); + Action processNRZIBit(uint8_t level); virtual void configureGPIOsForRX(); protected: RXPacket *mRXPacket = nullptr; @@ -66,9 +73,8 @@ protected: BitState mBitState; uint8_t mRXByte; VHFChannel mChannel; - uint16_t mSlotBitNumber; - bool mSwitchAtNextSlot; - VHFChannel mSwitchToChannel; + int mSlotBitNumber; + VHFChannel mNextChannel; uint32_t mTimeSlot = 0xffffffff; }; diff --git a/latest/Firmware/Src/RFIC.cpp b/latest/Firmware/Src/RFIC.cpp index 5e5240e..a899098 100644 --- a/latest/Firmware/Src/RFIC.cpp +++ b/latest/Firmware/Src/RFIC.cpp @@ -15,15 +15,14 @@ You should have received a copy of the GNU General Public License along with this program. If not, see -*/ + */ #include "RFIC.hpp" #include "radio_config.h" #include "Utils.hpp" #include "EZRadioPRO.h" -#include -#include "printf_serial.h" +#include #include "bsp.hpp" RFIC::RFIC(GPIO_TypeDef *sdnPort, @@ -46,8 +45,6 @@ RFIC::RFIC(GPIO_TypeDef *sdnPort, mDataPin = dataPin; mClockPin = clockPin; - //mRSSIAdjustment = 0; - mSPIBusy = false; mChipID = chipID; if ( !isInitialized() ) @@ -71,7 +68,13 @@ inline void RFIC::spiOff() bool RFIC::sendCmd(uint8_t cmd, void* params, uint8_t paramLen, void* result, uint8_t resultLen) { - mSPIBusy = true; + if ( mCTSPending ) + { + while ( readSPIResponse(NULL, 0) == false) + ; + mCTSPending = false; + } + //bsp_signal_high(); spiOn(); @@ -90,11 +93,34 @@ bool RFIC::sendCmd(uint8_t cmd, void* params, uint8_t paramLen, void* result, ui ; //bsp_signal_low(); - - mSPIBusy = false; return true; } +bool RFIC::sendCmdNoWait(uint8_t cmd, void* params, uint8_t paramLen) +{ + if ( mCTSPending ) + { + while ( readSPIResponse(NULL, 0) == false) + ; + mCTSPending = false; + } + + spiOn(); + + bsp_tx_spi_byte(cmd); + + uint8_t *b = (uint8_t*) params; + for ( int i = 0; i < paramLen; ++i ) + { + bsp_tx_spi_byte(b[i]); + } + spiOff(); + mCTSPending = true; + + return true; +} + + // This is borrowed from the dAISy project. Thank you Adrian :) bool RFIC::readSPIResponse(void *data, uint8_t length) { @@ -106,12 +132,15 @@ bool RFIC::readSPIResponse(void *data, uint8_t length) return false; } - uint8_t* b = (uint8_t*) data; - uint8_t i = 0; - while (i < length) + if ( data ) { - b[i] = bsp_tx_spi_byte(0); - ++i; + uint8_t* b = (uint8_t*) data; + uint8_t i = 0; + while (i < length) + { + b[i] = bsp_tx_spi_byte(0); + ++i; + } } spiOff(); @@ -138,11 +167,9 @@ bool RFIC::isInitialized() HAL_GPIO_WritePin(mSDNP, mSDNPin, GPIO_PIN_RESET); HAL_Delay(100); - //DBG("Checking RF chip status\r\n"); CHIP_STATUS_REPLY chip_status; memset(&chip_status, 0, sizeof chip_status); sendCmd(GET_CHIP_STATUS, NULL, 0, &chip_status, sizeof chip_status); - //DBG("Chip status: 0x%.2x\r\n", chip_status.Current); if ( chip_status.Current & 0x08 ) { return false; @@ -150,13 +177,11 @@ bool RFIC::isInitialized() else { return true; - } + } } void RFIC::powerOnReset() { - //DBG("Performing Power On Reset\r\n"); - // Pull SDN high to shut down the IC HAL_GPIO_WritePin(mSDNP, mSDNPin, GPIO_PIN_SET); @@ -166,12 +191,8 @@ void RFIC::powerOnReset() // Pull SDN low and poll the status of GPIO1 HAL_GPIO_WritePin(mSDNP, mSDNPin, GPIO_PIN_RESET); - //DBG("Waiting for GPIO1\r\n"); while ( HAL_GPIO_ReadPin(mDataPort, mDataPin) == GPIO_PIN_RESET ) ; - - // We're done! - //DBG("Radio Ready!\r\n"); } uint8_t RFIC::readRSSI() diff --git a/latest/Firmware/Src/Receiver.cpp b/latest/Firmware/Src/Receiver.cpp index 76ec01a..fee8050 100644 --- a/latest/Firmware/Src/Receiver.cpp +++ b/latest/Firmware/Src/Receiver.cpp @@ -30,14 +30,13 @@ Receiver::Receiver(GPIO_TypeDef *sdnPort, uint32_t sdnPin, GPIO_TypeDef *csPort, GPIO_TypeDef *clockPort, uint32_t clockPin, int chipId) : RFIC(sdnPort, sdnPin, csPort, csPin, dataPort, dataPin, clockPort, clockPin, chipId) { - mSlotBitNumber = 0xffff; - mSwitchAtNextSlot = false; + mSlotBitNumber = -1; mOneBitCount = 0; mChannel = CH_88; mBitCount = 0; mBitState = BIT_STATE_PREAMBLE_SYNC; mLastNRZIBit=0x00; - mSwitchToChannel = mChannel; + mNextChannel = mChannel; mRXByte = 0; mBitWindow = 0; mRXPacket = EventPool::instance().newRXPacket(); @@ -55,25 +54,22 @@ VHFChannel Receiver::channel() bool Receiver::init() { - //DBG("Configuring IC\r\n"); configure(); resetBitScanner(); - //configureGPIOsForRX(); - return true; } void Receiver::startReceiving(VHFChannel channel, bool reconfigGPIOs) { mChannel = channel; + mNextChannel = channel; startListening(mChannel, reconfigGPIOs); resetBitScanner(); } void Receiver::switchToChannel(VHFChannel channel) { - mSwitchAtNextSlot = true; - mSwitchToChannel = channel; + mNextChannel = channel; } // TODO: This is a really, really long operation - over 320us !!! @@ -95,10 +91,10 @@ void Receiver::startListening(VHFChannel channel, bool reconfigGPIOs) options.next_state3 = 0; /** - * This can take up to 220us, that's 3 bit clocks!!! + * This never takes more than 65us now :D */ //bsp_signal_high(); - sendCmd (START_RX, &options, sizeof options, NULL, 0); + sendCmdNoWait(START_RX, &options, sizeof options);//, NULL, 0); //bsp_signal_low(); } @@ -110,8 +106,8 @@ void Receiver::resetBitScanner() mLastNRZIBit = 0xff; mRXByte = 0; mBitState = BIT_STATE_PREAMBLE_SYNC; - - mRXPacket->reset(); + if ( mRXPacket ) + mRXPacket->reset(); } /* @@ -123,49 +119,79 @@ void Receiver::resetBitScanner() void Receiver::onBitClock() { + ++mSlotBitNumber; + // Don't waste time processing bits when the transceiver is transmitting if ( gRadioState == RADIO_TRANSMITTING ) return; - //bsp_signal_high(); + bsp_signal_high(); + + if ( !mRXPacket ) + { + mRXPacket = EventPool::instance().newRXPacket(); + if ( !mRXPacket ) + { + return; + } + } + uint8_t bit = HAL_GPIO_ReadPin(mDataPort, mDataPin); - processNRZIBit(bit); - if ( mTimeSlot != 0xffffffff && mSlotBitNumber != 0xffff && - mTimeSlot % 17 == mChipID && mSlotBitNumber++ == CCA_SLOT_BIT - 1 ) + Receiver::Action action = processNRZIBit(bit); + if ( action == RESTART_RX ) + { + startReceiving(mChannel, false); + } + /** + * This trick ensures that we only sample RSSI every 17 time slots and never in the + * same time slot for both ICs, so we don't conduct long SPI operations on consecutive + * interrupt handlers that might exceed the bit clock period. There is no reason for RSSI + * collection to have a high duty cycle anyway, it just serves to establish the noise floor. + */ + else if ( mTimeSlot != 0xffffffff && mSlotBitNumber != 0xffff && + mTimeSlot % 17 == mChipID && mSlotBitNumber == CCA_SLOT_BIT - 1 ) { uint8_t rssi = reportRSSI(); mRXPacket->setRSSI(rssi); } - //bsp_signal_low(); + + bsp_signal_low(); } +/** + * This is called from the SOTDMA timer interrupt, which is at the same priority as the bit clock. + * So timeSlotStarted() and onBitClock() cannot preempt each other. + */ + void Receiver::timeSlotStarted(uint32_t slot) { // This should never be called while transmitting. Transmissions start after the slot boundary and end before the end of it. - //assert(gRadioState == RADIO_RECEIVING); - //if ( gRadioState != RADIO_RECEIVING ) - //DBG(" **** WTF??? Transmitting past slot boundary? **** \r\n"); + ASSERT(gRadioState == RADIO_RECEIVING); - mSlotBitNumber = 0; + mSlotBitNumber = -1; mTimeSlot = slot; if ( mBitState == BIT_STATE_IN_PACKET ) return; - mRXPacket->setSlot(slot); - if ( mSwitchAtNextSlot ) + if ( mRXPacket ) + mRXPacket->setSlot(slot); + + if ( mChannel != mNextChannel ) { - mSwitchAtNextSlot = false; - startReceiving(mSwitchToChannel, false); + startReceiving(mNextChannel, false); } } -void Receiver::processNRZIBit(uint8_t bit) +/** + * This method must complete in a few microseconds, worst case! + */ +Receiver::Action Receiver::processNRZIBit(uint8_t bit) { if ( mLastNRZIBit == 0xff ) { mLastNRZIBit = bit; - return; + return NO_ACTION; } uint8_t decodedBit = !(mLastNRZIBit ^ bit); @@ -178,7 +204,7 @@ void Receiver::processNRZIBit(uint8_t bit) mBitWindow |= decodedBit; /* - * By checking for the last few training bits plus the HDLC start flag, + * By checking for the last few preamble bits plus the HDLC start flag, * we gain enough confidence that this is not random noise. */ if ( mBitWindow == 0b1010101001111110 || mBitWindow == 0b0101010101111110 ) @@ -194,29 +220,29 @@ void Receiver::processNRZIBit(uint8_t bit) if ( mRXPacket->size() >= MAX_AIS_RX_PACKET_SIZE ) { // Start over - startReceiving(mChannel, false); - - return; + return RESTART_RX; } if ( mOneBitCount >= 7 ) { // Bad packet! - startReceiving(mChannel, false); - return; + return RESTART_RX; } mLastNRZIBit = bit; mBitWindow <<= 1; mBitWindow |= decodedBit; - - if ( (mBitWindow & 0x00ff) == 0x7E ) { + // We have a complete packet mBitState = BIT_STATE_PREAMBLE_SYNC; + /** + * This is the longest operation undertaken here. Now that we use object pools and pointers, + * it completes in about 14us + */ pushPacket(); - startReceiving(mChannel, false); + return RESTART_RX; } else { @@ -227,6 +253,7 @@ void Receiver::processNRZIBit(uint8_t bit) } } + return NO_ACTION; } @@ -267,29 +294,36 @@ bool Receiver::addBit(uint8_t bit) void Receiver::pushPacket() { Event *p = EventPool::instance().newEvent(AIS_PACKET_EVENT); - RXPacket *currPacket = mRXPacket; - mRXPacket = EventPool::instance().newRXPacket(); - ASSERT_VALID_PTR(mRXPacket); + ASSERT_VALID_PTR(p); if ( p ) { //bsp_signal_high(); - p->rxPacket = currPacket; + p->rxPacket = mRXPacket; EventQueue::instance().push(p); //bsp_signal_low(); + mRXPacket = EventPool::instance().newRXPacket(); + } + else + { + /** + * We're out of resources so just keep using the existing packet. + * If this happens, the most logical outcome is a watchdog reset + * because something has blocked the main task and the pool is not + * getting replenished + */ + mRXPacket->reset(); } - - mRXPacket->reset(); } +/** + * This operation typically takes under 85us + */ uint8_t Receiver::reportRSSI() { - //bsp_signal_high(); uint8_t rssi = readRSSI(); - //bsp_signal_low(); char channel = AIS_CHANNELS[mChannel].designation; NoiseFloorDetector::instance().report(channel, rssi); - return rssi; } @@ -303,7 +337,7 @@ void Receiver::configureGPIOsForRX() gpiocfg.NIRQ = 0x00; // Nothing gpiocfg.SDO = 0x00; // No change gpiocfg.GENCFG = 0x00; // No change - sendCmd(GPIO_PIN_CFG, &gpiocfg, sizeof gpiocfg, &gpiocfg, sizeof gpiocfg); + sendCmd(GPIO_PIN_CFG, &gpiocfg, sizeof gpiocfg, NULL, 0); } diff --git a/latest/Firmware/Src/Transceiver.cpp b/latest/Firmware/Src/Transceiver.cpp index 64325b1..203883a 100644 --- a/latest/Firmware/Src/Transceiver.cpp +++ b/latest/Firmware/Src/Transceiver.cpp @@ -150,7 +150,7 @@ void Transceiver::configureGPIOsForTX(tx_power_level powerLevel) gpiocfg.NIRQ = 0x1A; // Sync word detect gpiocfg.SDO = 0x00; // No change gpiocfg.GENCFG = 0x00; // No change - sendCmd(GPIO_PIN_CFG, &gpiocfg, sizeof gpiocfg, &gpiocfg, sizeof gpiocfg); + sendCmd(GPIO_PIN_CFG, &gpiocfg, sizeof gpiocfg, NULL, 0); setTXPower(powerLevel); } @@ -323,7 +323,7 @@ void Transceiver::configureGPIOsForRX() gpiocfg.NIRQ = 0x00; // Nothing gpiocfg.SDO = 0x00; // No change gpiocfg.GENCFG = 0x00; // No change - sendCmd(GPIO_PIN_CFG, &gpiocfg, sizeof gpiocfg, &gpiocfg, sizeof gpiocfg); + sendCmd(GPIO_PIN_CFG, &gpiocfg, sizeof gpiocfg, NULL, 0); } void Transceiver::reportTXEvent()