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()