Version of easy-connect with the u-blox cellular platforms C027 and C030 added.

Dependents:   HelloMQTT

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers SimpleSpirit1.h Source File

SimpleSpirit1.h

00001 /*** Mbed Includes ***/
00002 #include "mbed.h"
00003 #include "mbed_debug.h"
00004 
00005 
00006 /*** Cube Includes ***/
00007 #include "SPIRIT_Radio.h"
00008 #include "SPIRIT_Management.h"
00009 #include "SPIRIT_Commands.h"
00010 #include "MCU_Interface.h"
00011 
00012 
00013 /*** Contiki Lib Includes ***/
00014 #include "spirit1.h"
00015 #include "spirit1-config.h"
00016 #include "spirit1-const.h"
00017 
00018 
00019 // betzw: enable beyond macro if you want debug messages also from IRQ handler
00020 // #define DEBUG_IRQ
00021 
00022 
00023 /*** Macros from Cube Implementation ***/
00024 #define CLEAR_TXBUF()           (spirit_tx_len = 0)
00025 #define IS_RXBUF_EMPTY()        (spirit_rx_len == 0)
00026 #define CLEAR_RXBUF()           do {                    \
00027                                     spirit_rx_len = 0;  \
00028                                     _spirit_rx_pos = 0; \
00029                                 } while(0)
00030 
00031 
00032 /*** Macros from Cube Implementation ***/
00033 /* transceiver state. */
00034 #define ON     0
00035 #define OFF    1
00036 
00037 
00038 /*** Macros for Spirit1 API ***/
00039 /* max payload */
00040 #define SPIRIT1_MAX_PAYLOAD     (MAX_PACKET_LEN)
00041 
00042 
00043 /*** Missing Cube External Declarations ***/
00044 extern "C" void SpiritManagementSetFrequencyBase(uint32_t);
00045 
00046 
00047 /*** UnlockedSPI for Usage in IRQ context ***/
00048 class UnlockedSPI : public SPI {
00049 public:
00050     UnlockedSPI(PinName mosi, PinName miso, PinName sclk) :
00051         SPI(mosi, miso, sclk) { }
00052     virtual ~UnlockedSPI() {}
00053     virtual void lock() { }
00054     virtual void unlock() { }
00055 };
00056 
00057 
00058 /*** A Simple Spirit1 Class ***/
00059 // NOTE: must be a singleton (due to mix of MBED/CUBE code)!!!
00060 // NOTE: implementation is IRQ-save but (intentionally) NOT thread-safe!!!
00061 /** Simple Spirit1 Class
00062  *
00063  * @Note Synchronization level: implementation is IRQ-save but (intentionally) NOT thread-safe!!!
00064  *
00065  * Example:
00066  * @code
00067  * #include "mbed.h"
00068  * #include "SimpleSpirit1.h"
00069  *
00070  * static char *send_buf = "Hello World!";
00071  *
00072  * static SimpleSpirit1 &myspirit = SimpleSpirit1::CreateInstance(D11, D12, D3, D9, D10, D2);
00073  *
00074  * static volatile bool tx_done_flag = false;
00075  *
00076  * static void callback_func(int event)
00077  * {
00078  *   if (event == SimpleSpirit1::TX_DONE) {
00079  *     tx_done_flag = true;
00080  *   }
00081  * }
00082  *
00083  * int main()
00084  * {
00085  *   myspirit.on();
00086  *
00087  *   while(1)
00088  *   {
00089  *     size_t curr_len = strlen((const char*)send_buf);
00090  *     myspirit.send(send_buf, curr_len);
00091  *
00092  *     while(!tx_done_flag);
00093  *     tx_done_flag = false;
00094  *   }
00095  * }
00096  * @endcode
00097  */
00098 class SimpleSpirit1 {
00099  protected:
00100     static SimpleSpirit1 *_singleton;
00101 
00102     /** Communication Interface Instance Variables **/
00103     UnlockedSPI _spi; // betzw - NOTE: Morpho/Zio pins are valid only for NUCLEO-F401RE
00104                       // mosi: PA_7 (D11)
00105                       // miso: PA_6 (D12)
00106                       // sclk: PB_3 (D3) or
00107                       //       PA_5 (D13) (only in case you unmount R4 & mount R7,
00108                       //                  (note: in this case you may not use LED1 on some platforms)
00109                       // bits: 8-bit
00110                       // mode: 0
00111                       // ordr: MSB
00112                       // freq: max 10MHz
00113     InterruptIn _irq; // PC_7 (D9) (falling)
00114     DigitalOut _chip_select; // PB_6 (D10) ('1' == chip unselected)
00115     DigitalOut _shut_down; // PA_10 (D2) ('1' == shut_down)
00116     DigitalOut _led; // PB_4 (D5) (optional)
00117 
00118     Callback<void(int)> _current_irq_callback;
00119     Timeout _rx_receiving_timeout;
00120 
00121     void rx_timeout_handler(void) {
00122         set_ready_state();
00123         cmd_strobe(SPIRIT1_STROBE_RX);
00124 #ifdef DEBUG_IRQ
00125         debug("\r\n%s (%d)\r\n", __func__, __LINE__);
00126 #endif
00127     }
00128 
00129     void start_rx_timeout(void) {
00130         _rx_receiving_timeout.attach_us(Callback<void()>(this, &SimpleSpirit1::rx_timeout_handler), 100 * 1000); // 100ms
00131     }
00132 
00133     void stop_rx_timeout(void) {
00134         _rx_receiving_timeout.detach();
00135     }
00136 
00137     /** Static Variables from Cube Implementation **/
00138     /*
00139      * The buffers which hold incoming data.
00140      * The +1 because of the first byte,
00141      * which will contain the length of the packet.
00142      */
00143     volatile uint16_t spirit_tx_len;
00144     volatile bool _spirit_tx_started;
00145     volatile uint16_t spirit_rx_len;
00146     volatile uint16_t _spirit_rx_pos;
00147     volatile bool _spirit_rx_err;
00148     uint8_t spirit_rx_buf[MAX_PACKET_LEN];
00149     volatile bool _is_receiving;
00150 
00151     /** Status Variables from Cube Implementation **/
00152     unsigned int spirit_on;
00153     uint8_t last_rssi; //MGR
00154     uint8_t last_sqi;  //MGR
00155 
00156     /** Low Level Instance Variables **/
00157     unsigned int _nr_of_irq_disables;
00158 
00159     /** Low Level Instance Methods **/
00160     void disable_spirit_irq(void) {
00161         _irq.disable_irq();
00162         _nr_of_irq_disables++;
00163 #ifndef NDEBUG
00164         debug_if(_nr_of_irq_disables == 0, "\r\nassert failed in: %s (%d)\r\n", __func__, __LINE__);
00165 #endif
00166     }
00167 
00168     void enable_spirit_irq(void) {
00169 #ifndef NDEBUG
00170         debug_if(_nr_of_irq_disables == 0, "\r\nassert failed in: %s (%d)\r\n", __func__, __LINE__);
00171 #endif
00172         if(--_nr_of_irq_disables == 0)
00173             _irq.enable_irq();
00174     }
00175 
00176     void chip_select() { _chip_select = 0; }
00177     void chip_unselect() { _chip_select = 1; }
00178 
00179     void enter_shutdown() {
00180         _shut_down = 1;
00181         wait_ms(5); // wait 5 milliseconds (to allow Spirit1 to shut down)
00182     }
00183 
00184     void exit_shutdown() {
00185         _shut_down = 0;
00186         wait_ms(10); // wait 10 milliseconds (to allow Spirit1 a proper boot-up sequence)
00187     }
00188 
00189     void cs_to_sclk_delay(void) {
00190         wait_us(1); // heuristic value
00191     }
00192 
00193     /**
00194      * @brief      Write and read a buffer to/from the SPI peripheral device at the same time
00195      *             in 8-bit data mode using synchronous SPI communication.
00196      * @param[in]  pBufferToWrite pointer to the buffer of data to send.
00197      * @param[out] pBufferToRead pointer to the buffer to read data into.
00198      * @param[in]  NumBytes number of bytes to read and write.
00199      * @retval     0 if ok.
00200      * @retval     -1 if data format error.
00201      * @note       When using the SPI in Interrupt-mode, remember to disable interrupts
00202      *             before calling this function and to enable them again after.
00203      */
00204     void spi_write_read(uint8_t* pBufferToWrite, uint8_t* pBufferToRead, uint16_t NumBytes)
00205     {
00206         /* Read and write data at the same time. */
00207         for (int i = 0; i < NumBytes; i++) {
00208             pBufferToRead[i] = _spi.write(pBufferToWrite[i]);
00209         }
00210     }
00211 
00212     /** Radio Instance Methods **/
00213     void radio_set_xtal_freq(uint32_t freq) {
00214         SpiritRadioSetXtalFrequency(freq);
00215     }
00216 
00217     void radio_set_pa_level_dbm(uint8_t cIndex, float fPowerdBm) {
00218         SpiritRadioSetPALeveldBm(cIndex, fPowerdBm);
00219     }
00220 
00221     void radio_set_pa_level_max_index(uint8_t cIndex) {
00222         SpiritRadioSetPALevelMaxIndex(cIndex);
00223     }
00224 
00225     uint8_t radio_init(SRadioInit *init_struct) {
00226         return SpiritRadioInit(init_struct);
00227     }
00228 
00229     void radio_persistent_rx(SpiritFunctionalState xNewState) {
00230         SpiritRadioPersistenRx(xNewState);
00231     }
00232 
00233     void radio_afc_freeze_on_sync(SpiritFunctionalState xNewState) {
00234         SpiritRadioAFCFreezeOnSync(xNewState);
00235     }
00236 
00237     /** Packet System Instance Methods **/
00238     void pkt_basic_init(PktBasicInit* pxPktBasicInit) {
00239         SpiritPktBasicInit(pxPktBasicInit);
00240     }
00241 
00242     void pkt_basic_set_payload_length(uint16_t nPayloadLength) {
00243         SpiritPktBasicSetPayloadLength(nPayloadLength);
00244     }
00245 
00246     uint16_t pkt_basic_get_received_pkt_length(void) {
00247         return SpiritPktBasicGetReceivedPktLength();
00248     }
00249 
00250     /** IRQ Instance Methods **/
00251     void irq_de_init(SpiritIrqs* pxIrqInit) {
00252         SpiritIrqDeInit(pxIrqInit);
00253     }
00254 
00255     void irq_clear_status(void) {
00256         SpiritIrqClearStatus();
00257     }
00258 
00259     void irq_set_status(IrqList xIrq, SpiritFunctionalState xNewState) {
00260         SpiritIrq(xIrq, xNewState);
00261     }
00262 
00263     void irq_get_status(SpiritIrqs* pxIrqStatus) {
00264         SpiritIrqGetStatus(pxIrqStatus);
00265     }
00266 
00267     /** Management Instance Methods **/
00268     void mgmt_set_freq_base(uint32_t freq) {
00269         SpiritManagementSetFrequencyBase(freq);
00270     }
00271 
00272     void mgmt_refresh_status(void) {
00273         SpiritRefreshStatus();
00274     }
00275 
00276     /** Spirit GPIO Instance Methods **/
00277     void spirit_gpio_init(SGpioInit* pxGpioInitStruct) {
00278         SpiritGpioInit(pxGpioInitStruct);
00279     }
00280 
00281     /** Qi Instance Methods **/
00282     void qi_set_sqi_threshold(SqiThreshold xSqiThr) {
00283         SpiritQiSetSqiThreshold(xSqiThr);
00284     }
00285 
00286     void qi_sqi_check(SpiritFunctionalState xNewState) {
00287         SpiritQiSqiCheck(xNewState);
00288     }
00289 
00290     void qi_set_rssi_threshold_dbm(int nDbmValue) {
00291         SpiritQiSetRssiThresholddBm(nDbmValue);
00292     }
00293 
00294     float qi_get_rssi_dbm() {
00295         last_rssi = qi_get_rssi();
00296         return get_last_rssi_dbm();
00297     }
00298 
00299     uint8_t qi_get_rssi() {
00300         return SpiritQiGetRssi();
00301     }
00302 
00303     uint8_t qi_get_sqi() {
00304         return SpiritQiGetSqi();
00305     }
00306 
00307     /** Timer Instance Methods **/
00308     void timer_set_rx_timeout_stop_condition(RxTimeoutStopCondition xStopCondition) {
00309         SpiritTimerSetRxTimeoutStopCondition(xStopCondition);
00310     }
00311 
00312     void timer_set_rx_timeout_counter(uint8_t cCounter) {
00313         SpiritTimerSetRxTimeoutCounter(cCounter);
00314     }
00315 
00316     void timer_set_infinite_rx_timeout(void) {
00317         timer_set_rx_timeout_counter(0);
00318     }
00319 
00320     /** CSMA/CA Instance Methods **/
00321     void csma_ca_state(SpiritFunctionalState xNewState) {
00322         SpiritCsma(xNewState);
00323     }
00324 
00325     void csma_ca_init(CsmaInit* pxCsmaInit) {
00326         csma_ca_state(S_DISABLE); // Disabled at init
00327         SpiritCsmaInit(pxCsmaInit);
00328         SpiritCsmaSeedReloadMode(S_DISABLE); // always disable seed reload
00329     }
00330 
00331     /** Command Instance Methods**/
00332     void cmd_strobe(uint8_t cmd) {
00333         SpiritCmdStrobeCommand((SpiritCmd)cmd);
00334     }
00335 
00336     void cmd_strobe_flush_rx_fifo() {
00337         SpiritCmdStrobeCommand(CMD_FLUSHRXFIFO);
00338     }
00339 
00340     /** SPI Instance Methods **/
00341     StatusBytes spi_write_linear_fifo(uint8_t cNbBytes, uint8_t* pcBuffer) {
00342         return SdkEvalSpiWriteFifo(cNbBytes, pcBuffer);
00343     }
00344 
00345     StatusBytes spi_read_linear_fifo(uint8_t cNbBytes, uint8_t* pcBuffer) {
00346         return SdkEvalSpiReadFifo(cNbBytes, pcBuffer);
00347     }
00348 
00349     /** Linear FIFO Instance Methods **/
00350     uint8_t linear_fifo_read_num_elements_rx_fifo(void) {
00351         return SpiritLinearFifoReadNumElementsRxFifo();
00352     }
00353 
00354     uint8_t linear_fifo_read_num_elements_tx_fifo(void) {
00355         return SpiritLinearFifoReadNumElementsTxFifo();
00356     }
00357 
00358     void linear_fifo_set_almost_full_thr_rx(uint8_t cThrRxFifo) {
00359         SpiritLinearFifoSetAlmostFullThresholdRx(cThrRxFifo);
00360     }
00361 
00362     /** Calibration Instance Methods **/
00363     void calibration_rco(SpiritFunctionalState xNewState) {
00364         SpiritCalibrationRco(xNewState);
00365     }
00366 
00367     /** Internal Spirit Methods */
00368     void set_ready_state(void);
00369     uint8_t refresh_state(void);
00370 
00371     /** Friend Functions **/
00372     friend StatusBytes SdkEvalSpiWriteRegisters(uint8_t cRegAddress, uint8_t cNbBytes, uint8_t* pcBuffer);
00373     friend StatusBytes SdkEvalSpiReadRegisters(uint8_t cRegAddress, uint8_t cNbBytes, uint8_t* pcBuffer);
00374     friend StatusBytes SdkEvalSpiCommandStrobes(uint8_t cCommandCode);
00375     friend StatusBytes SdkEvalSpiWriteFifo(uint8_t cNbBytes, uint8_t* pcBuffer);
00376     friend StatusBytes SdkEvalSpiReadFifo(uint8_t cNbBytes, uint8_t* pcBuffer);
00377 
00378     /** Sdk Instance Methods **/
00379     StatusBytes SdkEvalSpiWriteRegisters(uint8_t cRegAddress, uint8_t cNbBytes, uint8_t* pcBuffer);
00380     StatusBytes SdkEvalSpiReadRegisters(uint8_t cRegAddress, uint8_t cNbBytes, uint8_t* pcBuffer);
00381     StatusBytes SdkEvalSpiCommandStrobes(uint8_t cCommandCode);
00382     StatusBytes SdkEvalSpiWriteFifo(uint8_t cNbBytes, uint8_t* pcBuffer);
00383     StatusBytes SdkEvalSpiReadFifo(uint8_t cNbBytes, uint8_t* pcBuffer);
00384 
00385     /** Helper Instance Methods **/
00386     void chip_sync_select() {
00387         disable_spirit_irq();
00388         chip_select();
00389         cs_to_sclk_delay();
00390     }
00391 
00392     void chip_sync_unselect() {
00393         chip_unselect();
00394         enable_spirit_irq();
00395     }
00396 
00397     /** Init Instance Method **/
00398     void init();
00399 
00400     /** Spirit Irq Callback */
00401     void IrqHandler();
00402 
00403     /** Constructor **/
00404     SimpleSpirit1(PinName mosi, PinName miso, PinName sclk,
00405           PinName irq, PinName cs, PinName sdn,
00406           PinName led);
00407 
00408     /** Destructor **/
00409     ~SimpleSpirit1(void); // should never be called!
00410 
00411 public:
00412     enum {
00413         RX_DONE,
00414         TX_DONE,
00415         TX_ERR
00416     };
00417 
00418     /** Create singleton instance of 'SimpleSpirit1'
00419      *
00420      * @param mosi 'PinName' of mosi pin to use
00421      * @param miso 'PinName' of miso pin to use
00422      * @param sclk 'PinName' of clock pin to use
00423      * @param irq  'PinName' of interrupt pin to use
00424      * @param cs   'PinName' of chip-select pin pin to use
00425      * @param sdn  'PinName' of pin to use for device shutdown
00426      *
00427      * @returns     reference to singleton instance
00428      */
00429     static SimpleSpirit1& CreateInstance(PinName mosi, PinName miso, PinName sclk,
00430             PinName irq, PinName cs, PinName sdn,
00431             PinName led = NC) {
00432 
00433         if(_singleton == NULL) {
00434             _singleton = new SimpleSpirit1(mosi, miso, sclk,
00435                     irq, cs, sdn, led);
00436             _singleton->init();
00437         } else {
00438             error("SimpleSpirit1 singleton already created!\n");
00439         }
00440 
00441         return *_singleton;
00442     }
00443 
00444     /** Create singleton instance of 'SimpleSpirit1'
00445      *
00446      * @param mosi 'PinName' of mosi pin to use
00447      * @param miso 'PinName' of miso pin to use
00448      * @param sclk 'PinName' of clock pin to use
00449      * @param irq  'PinName' of interrupt pin to use
00450      * @param cs   'PinName' of chip-select pin pin to use
00451      * @param sdn  'PinName' of pin to use for device shutdown
00452      *
00453      * @returns     reference to singleton instance
00454      */
00455     static SimpleSpirit1& Instance() {
00456         if(_singleton == NULL) {
00457             error("SimpleSpirit1 must be created before used!\n");
00458         }
00459 
00460         return *_singleton;
00461     }
00462 
00463     /** Attach a function to be called by the Spirit Irq handler when an event has occurred
00464      *
00465      *  @param func A void(int) callback, or 0 to set as none
00466      *
00467      *  @note  Function 'func' will be executed in interrupt context!
00468      *  @note  Function 'func' will be call with either 'RX_DONE', 'TX_DONE', or 'TX_ERR' as parameter
00469      *         to indicate which event has occurred.
00470      */
00471     void attach_irq_callback(Callback<void(int)> func) {
00472         _current_irq_callback = func;
00473     }
00474 
00475     /** Switch Radio On
00476      */
00477     int on(void);
00478     /** Switch Radio Off
00479      */
00480     int off(void);
00481 
00482     /** Set Channel
00483      */
00484     void set_channel(uint8_t channel) {
00485         SpiritRadioSetChannel(channel);
00486     }
00487 
00488     /** Send a Buffer
00489      *
00490      * @param payload       pointer to buffer to be send
00491      * @param payload_len   length of payload buffer in bytes
00492      * @param use_csma_ca   should CSMA/CA be enabled for transmission
00493      *
00494      * @returns             zero in case of success, non-zero error code otherwise
00495      *
00496      * @note                the maximum payload size in bytes allowed is defined by macro 'SPIRIT1_MAX_PAYLOAD'
00497      */
00498     int send(const void *payload, unsigned int payload_len, bool use_csma_ca = true);
00499 
00500     /** Copy received data into buffer
00501      *
00502      * @param buf       pointer to buffer to be filled
00503      * @param bufsize   size of buffer
00504      *
00505      * @returns         number of bytes copied into the buffer
00506      *
00507      * @note            the buffer should be (at least) of size 'SPIRIT1_MAX_PAYLOAD' (in bytes).
00508      */
00509     int read(void *buf, unsigned int bufsize);
00510 
00511     /** Perform a Clear-Channel Assessment (CCA) to find out if there is a packet in the air or not.
00512      *
00513      * @returns  1 if packet has been seen.
00514      */
00515     int channel_clear(void);
00516 
00517     /** Check if the radio driver has just received a packet
00518      */
00519     int get_pending_packet(void);
00520 
00521     /** Is radio currently receiving
00522      */
00523     bool is_receiving(void) {
00524         return _is_receiving;
00525     }
00526 
00527     /** Get latest value of RSSI (in dBm)
00528      */
00529     float get_last_rssi_dbm(void) {
00530         get_last_rssi_raw();
00531         return (-120.0+((float)(last_rssi-20))/2);
00532     }
00533 
00534     /** Get latest value of RSSI (as Spirit1 raw value)
00535      */
00536     uint8_t get_last_rssi_raw(void) {
00537         if(last_rssi == 0) {
00538             last_rssi = qi_get_rssi();
00539         }
00540         return last_rssi;
00541     }
00542 
00543     /** Get latest value of LQI (scaled to 8-bit)
00544      */
00545     uint8_t get_last_sqi(void) {
00546         const uint8_t max_sqi = 8 * ((SYNC_LENGTH>>1)+1);
00547         if(last_sqi == 0) {
00548             last_sqi = qi_get_sqi();
00549         }
00550         if(last_sqi > max_sqi) last_sqi = max_sqi;
00551 
00552         return (last_sqi * 255 / max_sqi);
00553     }
00554 
00555     /** Reset Board
00556      */
00557     void reset_board() {
00558         init();
00559     }
00560 };