Fork of my original MQTTGateway
easy-connect/stm-spirit1-rf-driver/stm-spirit1-rf-driver/SimpleSpirit1.h
- Committer:
- vpcola
- Date:
- 2017-04-08
- Revision:
- 0:a1734fe1ec4b
File content as of revision 0:a1734fe1ec4b:
/*** Mbed Includes ***/
#include "mbed.h"
#include "mbed_debug.h"
/*** Cube Includes ***/
#include "SPIRIT_Radio.h"
#include "SPIRIT_Management.h"
#include "SPIRIT_Commands.h"
#include "MCU_Interface.h"
/*** Contiki Lib Includes ***/
#include "spirit1.h"
#include "spirit1-config.h"
#include "spirit1-const.h"
// betzw: enable beyond macro if you want debug messages also from IRQ handler
// #define DEBUG_IRQ
/*** Macros from Cube Implementation ***/
#define CLEAR_TXBUF() (spirit_tx_len = 0)
#define IS_RXBUF_EMPTY() (spirit_rx_len == 0)
#define CLEAR_RXBUF() do { \
spirit_rx_len = 0; \
_spirit_rx_pos = 0; \
} while(0)
/*** Macros from Cube Implementation ***/
/* transceiver state. */
#define ON 0
#define OFF 1
/*** Missing Cube External Declarations ***/
extern "C" void SpiritManagementSetFrequencyBase(uint32_t);
/*** UnlockedSPI for Performance (due to singleton) ***/
class UnlockedSPI : public SPI {
public:
UnlockedSPI(PinName mosi, PinName miso, PinName sclk) :
SPI(mosi, miso, sclk) { }
virtual ~UnlockedSPI() {}
virtual void lock() { }
virtual void unlock() { }
};
/*** A Simple Spirit1 Class ***/
// NOTE: must be a singleton (due to mix of MBED/CUBE code)!!!
// NOTE: implementation is IRQ-save but (intentionally) NOT thread-safe!!!
class SimpleSpirit1 {
protected:
static SimpleSpirit1 *_singleton;
/** Communication Interface Instance Variables **/
UnlockedSPI _spi; // betzw - NOTE: Morpho/Zio pins are valid only for NUCLEO-F401RE
// mosi: PA_7 (D11)
// miso: PA_6 (D12)
// sclk: PB_3 (D3) or
// PA_5 (D13) (only in case you unmount R4 & mount R7,
// (note: in this case you may not use LED1 on some platforms)
// bits: 8-bit
// mode: 0
// ordr: MSB
// freq: max 10MHz
InterruptIn _irq; // PC_7 (D9) (falling)
DigitalOut _chip_select; // PB_6 (D10) ('1' == chip unselected)
DigitalOut _shut_down; // PA_10 (D2) ('1' == shut_down)
DigitalOut _led; // PB_4 (D5) (optional)
Callback<void(int)> _current_irq_callback;
Timeout _rx_receiving_timeout;
void rx_timeout_handler(void) {
set_ready_state();
cmd_strobe(SPIRIT1_STROBE_RX);
#ifdef DEBUG_IRQ
debug("\n\r%s (%d)\n\r", __func__, __LINE__);
#endif
}
void start_rx_timeout(void) {
_rx_receiving_timeout.attach_us(Callback<void()>(this, &SimpleSpirit1::rx_timeout_handler), 100 * 1000); // 100ms
}
void stop_rx_timeout(void) {
_rx_receiving_timeout.detach();
}
/** Static Variables from Cube Implementation **/
/*
* The buffers which hold incoming data.
* The +1 because of the first byte,
* which will contain the length of the packet.
*/
volatile uint16_t spirit_tx_len;
volatile bool _spirit_tx_started;
volatile uint16_t spirit_rx_len;
volatile uint16_t _spirit_rx_pos;
volatile bool _spirit_rx_err;
uint8_t spirit_rx_buf[MAX_PACKET_LEN];
volatile bool _is_receiving;
/** Status Variables from Cube Implementation **/
unsigned int spirit_on;
uint8_t last_rssi; //MGR
uint8_t last_sqi; //MGR
/** Low Level Instance Variables **/
unsigned int _nr_of_irq_disables;
/** Low Level Instance Methods **/
void disable_spirit_irq(void) {
_irq.disable_irq();
_nr_of_irq_disables++;
#ifndef NDEBUG
debug_if(_nr_of_irq_disables == 0, "\n\rassert failed in: %s (%d)\n\r", __func__, __LINE__);
#endif
}
void enable_spirit_irq(void) {
#ifndef NDEBUG
debug_if(_nr_of_irq_disables == 0, "\n\rassert failed in: %s (%d)\n\r", __func__, __LINE__);
#endif
if(--_nr_of_irq_disables == 0)
_irq.enable_irq();
}
void chip_select() { _chip_select = 0; }
void chip_unselect() { _chip_select = 1; }
void enter_shutdown() {
_shut_down = 1;
wait_ms(5); // wait 5 milliseconds (to allow Spirit1 to shut down)
}
void exit_shutdown() {
_shut_down = 0;
wait_ms(10); // wait 10 milliseconds (to allow Spirit1 a proper boot-up sequence)
}
void cs_to_sclk_delay(void) {
wait_us(1); // heuristic value
}
/**
* @brief Write and read a buffer to/from the SPI peripheral device at the same time
* in 8-bit data mode using synchronous SPI communication.
* @param[in] pBufferToWrite pointer to the buffer of data to send.
* @param[out] pBufferToRead pointer to the buffer to read data into.
* @param[in] NumBytes number of bytes to read and write.
* @retval 0 if ok.
* @retval -1 if data format error.
* @note When using the SPI in Interrupt-mode, remember to disable interrupts
* before calling this function and to enable them again after.
*/
void spi_write_read(uint8_t* pBufferToWrite, uint8_t* pBufferToRead, uint16_t NumBytes)
{
/* Read and write data at the same time. */
for (int i = 0; i < NumBytes; i++) {
pBufferToRead[i] = _spi.write(pBufferToWrite[i]);
}
}
/** Radio Instance Methods **/
void radio_set_xtal_freq(uint32_t freq) {
SpiritRadioSetXtalFrequency(freq);
}
void radio_set_pa_level_dbm(uint8_t cIndex, float fPowerdBm) {
SpiritRadioSetPALeveldBm(cIndex, fPowerdBm);
}
void radio_set_pa_level_max_index(uint8_t cIndex) {
SpiritRadioSetPALevelMaxIndex(cIndex);
}
uint8_t radio_init(SRadioInit *init_struct) {
return SpiritRadioInit(init_struct);
}
void radio_persistent_rx(SpiritFunctionalState xNewState) {
SpiritRadioPersistenRx(xNewState);
}
void radio_afc_freeze_on_sync(SpiritFunctionalState xNewState) {
SpiritRadioAFCFreezeOnSync(xNewState);
}
/** Packet System Instance Methods **/
void pkt_basic_init(PktBasicInit* pxPktBasicInit) {
SpiritPktBasicInit(pxPktBasicInit);
}
void pkt_basic_set_payload_length(uint16_t nPayloadLength) {
SpiritPktBasicSetPayloadLength(nPayloadLength);
}
uint16_t pkt_basic_get_received_pkt_length(void) {
return SpiritPktBasicGetReceivedPktLength();
}
/** IRQ Instance Methods **/
void irq_de_init(SpiritIrqs* pxIrqInit) {
SpiritIrqDeInit(pxIrqInit);
}
void irq_clear_status(void) {
SpiritIrqClearStatus();
}
void irq_set_status(IrqList xIrq, SpiritFunctionalState xNewState) {
SpiritIrq(xIrq, xNewState);
}
void irq_get_status(SpiritIrqs* pxIrqStatus) {
SpiritIrqGetStatus(pxIrqStatus);
}
/** Management Instance Methods **/
void mgmt_set_freq_base(uint32_t freq) {
SpiritManagementSetFrequencyBase(freq);
}
void mgmt_refresh_status(void) {
SpiritRefreshStatus();
}
/** Spirit GPIO Instance Methods **/
void spirit_gpio_init(SGpioInit* pxGpioInitStruct) {
SpiritGpioInit(pxGpioInitStruct);
}
/** Qi Instance Methods **/
void qi_set_sqi_threshold(SqiThreshold xSqiThr) {
SpiritQiSetSqiThreshold(xSqiThr);
}
void qi_sqi_check(SpiritFunctionalState xNewState) {
SpiritQiSqiCheck(xNewState);
}
void qi_set_rssi_threshold_dbm(int nDbmValue) {
SpiritQiSetRssiThresholddBm(nDbmValue);
}
float qi_get_rssi_dbm() {
last_rssi = qi_get_rssi();
return get_last_rssi_dbm();
}
uint8_t qi_get_rssi() {
return SpiritQiGetRssi();
}
uint8_t qi_get_sqi() {
return SpiritQiGetSqi();
}
/** Timer Instance Methods **/
void timer_set_rx_timeout_stop_condition(RxTimeoutStopCondition xStopCondition) {
SpiritTimerSetRxTimeoutStopCondition(xStopCondition);
}
void timer_set_rx_timeout_counter(uint8_t cCounter) {
SpiritTimerSetRxTimeoutCounter(cCounter);
}
void timer_set_infinite_rx_timeout(void) {
timer_set_rx_timeout_counter(0);
}
/** CSMA/CA Instance Methods **/
void csma_ca_state(SpiritFunctionalState xNewState) {
SpiritCsma(xNewState);
}
void csma_ca_init(CsmaInit* pxCsmaInit) {
csma_ca_state(S_DISABLE); // Disabled at init
SpiritCsmaInit(pxCsmaInit);
SpiritCsmaSeedReloadMode(S_DISABLE); // always disable seed reload
}
/** Command Instance Methods**/
void cmd_strobe(uint8_t cmd) {
SpiritCmdStrobeCommand((SpiritCmd)cmd);
}
void cmd_strobe_flush_rx_fifo() {
SpiritCmdStrobeCommand(CMD_FLUSHRXFIFO);
}
/** SPI Instance Methods **/
StatusBytes spi_write_linear_fifo(uint8_t cNbBytes, uint8_t* pcBuffer) {
return SdkEvalSpiWriteFifo(cNbBytes, pcBuffer);
}
StatusBytes spi_read_linear_fifo(uint8_t cNbBytes, uint8_t* pcBuffer) {
return SdkEvalSpiReadFifo(cNbBytes, pcBuffer);
}
/** Linear FIFO Instance Methods **/
uint8_t linear_fifo_read_num_elements_rx_fifo(void) {
return SpiritLinearFifoReadNumElementsRxFifo();
}
uint8_t linear_fifo_read_num_elements_tx_fifo(void) {
return SpiritLinearFifoReadNumElementsTxFifo();
}
void linear_fifo_set_almost_full_thr_rx(uint8_t cThrRxFifo) {
SpiritLinearFifoSetAlmostFullThresholdRx(cThrRxFifo);
}
/** Calibration Instance Methods **/
void calibration_rco(SpiritFunctionalState xNewState) {
SpiritCalibrationRco(xNewState);
}
/** Internal Spirit Methods */
void set_ready_state(void);
uint8_t refresh_state(void);
/** Friend Functions **/
friend StatusBytes SdkEvalSpiWriteRegisters(uint8_t cRegAddress, uint8_t cNbBytes, uint8_t* pcBuffer);
friend StatusBytes SdkEvalSpiReadRegisters(uint8_t cRegAddress, uint8_t cNbBytes, uint8_t* pcBuffer);
friend StatusBytes SdkEvalSpiCommandStrobes(uint8_t cCommandCode);
friend StatusBytes SdkEvalSpiWriteFifo(uint8_t cNbBytes, uint8_t* pcBuffer);
friend StatusBytes SdkEvalSpiReadFifo(uint8_t cNbBytes, uint8_t* pcBuffer);
/** Sdk Instance Methods **/
StatusBytes SdkEvalSpiWriteRegisters(uint8_t cRegAddress, uint8_t cNbBytes, uint8_t* pcBuffer);
StatusBytes SdkEvalSpiReadRegisters(uint8_t cRegAddress, uint8_t cNbBytes, uint8_t* pcBuffer);
StatusBytes SdkEvalSpiCommandStrobes(uint8_t cCommandCode);
StatusBytes SdkEvalSpiWriteFifo(uint8_t cNbBytes, uint8_t* pcBuffer);
StatusBytes SdkEvalSpiReadFifo(uint8_t cNbBytes, uint8_t* pcBuffer);
/** Helper Instance Methods **/
void chip_sync_select() {
disable_spirit_irq();
chip_select();
cs_to_sclk_delay();
}
void chip_sync_unselect() {
chip_unselect();
enable_spirit_irq();
}
/** Init Instance Method **/
void init();
/** Spirit Irq Callback */
void IrqHandler();
/** Constructor **/
SimpleSpirit1(PinName mosi, PinName miso, PinName sclk,
PinName irq, PinName cs, PinName sdn,
PinName led);
/** Destructor **/
~SimpleSpirit1(void); // should never be called!
public:
enum {
RX_DONE,
TX_DONE,
TX_ERR
};
static SimpleSpirit1& CreateInstance(PinName mosi, PinName miso, PinName sclk,
PinName irq, PinName cs, PinName sdn,
PinName led = NC) {
if(_singleton == NULL) {
_singleton = new SimpleSpirit1(mosi, miso, sclk,
irq, cs, sdn, led);
_singleton->init();
} else {
error("SimpleSpirit1 singleton already created!\n");
}
return *_singleton;
}
static SimpleSpirit1& Instance() {
if(_singleton == NULL) {
error("SimpleSpirit1 must be created before used!\n");
}
return *_singleton;
}
/** Attach a function to be called by the Spirit Irq handler when packet has arrived
*
* @param func A void() callback, or 0 to set as none
*
* @note Function 'func' will be executed in interrupt context!
*/
void attach_irq_callback(Callback<void(int)> func) {
_current_irq_callback = func;
}
/** Switch Radio On/Off **/
int on(void);
int off(void);
/** Set Channel **/
void set_channel(uint8_t channel) {
SpiritRadioSetChannel(channel);
}
/** Send a Buffer **/
int send(const void *payload, unsigned int payload_len);
/** Read into Buffer **/
int read(void *buf, unsigned int bufsize);
/** Perform a Clear-Channel Assessment (CCA) to find out if there is
a packet in the air or not.
Returns 1 if packet has been seen.
*/
int channel_clear(void);
/** Check if the radio driver has just received a packet **/
int get_pending_packet(void);
/** Is radio currently receiving **/
bool is_receiving(void) {
return _is_receiving;
}
/** Get latest value of RSSI (in dBm) **/
float get_last_rssi_dbm(void) {
get_last_rssi_raw();
return (-120.0+((float)(last_rssi-20))/2);
}
/** Get latest value of RSSI (as Spirit1 raw value) **/
uint8_t get_last_rssi_raw(void) {
if(last_rssi == 0) {
last_rssi = qi_get_rssi();
}
return last_rssi;
}
/** Get latest value of LQI (scaled to 8-bit) **/
uint8_t get_last_sqi(void) {
const uint8_t max_sqi = 8 * ((SYNC_LENGTH>>1)+1);
if(last_sqi == 0) {
last_sqi = qi_get_sqi();
}
if(last_sqi > max_sqi) last_sqi = max_sqi;
return (last_sqi * 255 / max_sqi);
}
/** Reset Board **/
void reset_board() {
init();
}
};