Prototype RF driver for STM Sub-1 GHz RF expansion board based on the SPSGRF-868 module for STM32 Nucleo.
Prototype RF Driver for STM Sub-1 GHz RF Expansion Boards based on the SPSGRF-868 and SPSGRF-915 Modules for STM32 Nucleo
Currently supported boards:
Note, in order to use expansion board X-NUCLEO-IDS01A4
in mbed you need to perform the following HW modifications on the board:
- Unmount resistor
R4
- Mount resistor
R7
Furthermore, on some Nucleo development boards (e.g. the NUCLEO_F429ZI), in order to be able to use Ethernet together with these Sub-1 GHz RF expansion boards, you need to compile this driver with macro SPIRIT1_SPI_MOSI=PB_5
defined, while the development board typically requires some HW modification as e.g. described here!
This driver can be used together with the 6LoWPAN stack (a.k.a. Nanostack).
Revision 82:a18c22d2b83a, committed 2018-02-02
- Comitter:
- Wolfgang Betz
- Date:
- Fri Feb 02 14:13:24 2018 +0100
- Parent:
- 81:b16caa776548
- Commit message:
- Correct comments/documentation
Changed in this revision
--- a/source/SimpleSpirit1.cpp Fri Feb 02 09:57:17 2018 +0100 +++ b/source/SimpleSpirit1.cpp Fri Feb 02 14:13:24 2018 +0100 @@ -131,7 +131,6 @@ radio_afc_freeze_on_sync(S_ENABLE); calibration_rco(S_ENABLE); - spirit_on = OFF; CLEAR_TXBUF(); CLEAR_RXBUF(); _spirit_tx_started = false; @@ -378,6 +377,7 @@ } +/* betzw - TODO: CCA should be reviewed (completely)! */ int SimpleSpirit1::channel_clear(void) { float rssi_value; @@ -407,228 +407,232 @@ enable_spirit_irq(); /* Puts the SPIRIT1 in its previous state */ - if(spirit_state==OFF) { + if(spirit_state == OFF) { off(); + #ifndef NDEBUG #ifdef USE_STANDBY_STATE if(SPIRIT1_STATUS() != SPIRIT1_STATE_STANDBY) { -#else - if(SPIRIT1_STATUS() != SPIRIT1_STATE_READY) { -#endif - debug("\r\nAssert failed in: %s (%d): state=%x\r\n", __func__, __LINE__, last_state>>1); - } -#endif - } else { - disable_spirit_irq(); + debug("\r\nAssert failed in: %s (%d): state=%x\r\n", __func__, __LINE__, last_state>>1); + } +#else // !USE_STANDBY_STATE + if(SPIRIT1_STATUS() != SPIRIT1_STATE_READY) { + debug("\r\nAssert failed in: %s (%d): state=%x\r\n", __func__, __LINE__, last_state>>1); + } +#endif // !USE_STANDBY_STATE +#endif // NDEBUG + } else { // spirit_state != OFF + disable_spirit_irq(); - set_ready_state(); + set_ready_state(); - cmd_strobe(SPIRIT1_STROBE_RX); - BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_RX, STATE_TIMEOUT); - if((last_state & SPIRIT1_STATE_STATEBITS) != SPIRIT1_STATE_RX) { - error("\r\nSpirit1: (#2) failed to enter rx (%x) => retry\r\n", last_state>>1); - } + cmd_strobe(SPIRIT1_STROBE_RX); + BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_RX, STATE_TIMEOUT); + if((last_state & SPIRIT1_STATE_STATEBITS) != SPIRIT1_STATE_RX) { + error("\r\nSpirit1: (#2) failed to enter rx (%x) => retry\r\n", last_state>>1); + } - enable_spirit_irq(); + enable_spirit_irq(); #ifndef NDEBUG - if(SPIRIT1_STATUS() != SPIRIT1_STATE_RX) { - debug("\r\nAssert failed in: %s (%d): state=%x\r\n", __func__, __LINE__, last_state>>1); + if(SPIRIT1_STATUS() != SPIRIT1_STATE_RX) { + debug("\r\nAssert failed in: %s (%d): state=%x\r\n", __func__, __LINE__, last_state>>1); + } +#endif + } + + /* If the received signal strength is above a certain level the medium is considered busy! */ + /* Compare RSSI value with threshold */ + if(rssi_value < CCA_THRESHOLD) { + return 0; // idle + } else { + return 1; // busy + } +} + +int SimpleSpirit1::get_pending_packet(void) +{ + return !IS_RXBUF_EMPTY(); +} + +/** Spirit Irq Callback **/ +/* betzw - TODO: use threaded interrupt handling when `MBED_CONF_RTOS_PRESENT` is defined (see `atmel-rf-driver`) */ +void SimpleSpirit1::IrqHandler() { + st_lib_spirit_irqs x_irq_status; + + /* get interrupt source from radio */ + irq_get_status(&x_irq_status); + + /* The IRQ_TX_DATA_SENT notifies the packet has been sent. Puts the SPIRIT1 in RX */ + if(x_irq_status.IRQ_TX_DATA_SENT) { /* betzw - NOTE: MUST be handled before `IRQ_RX_DATA_READY` for Nanostack integration! + Logically, Nanostack only expects the "DONE" after "SUCCESS" (if it gets + DONE before SUCCESS, it assumes you're not going to bother to send SUCCESS). + */ +#ifdef DEBUG_IRQ + uint32_t *tmp = (uint32_t*)&x_irq_status; + debug_if(!((*tmp) & IRQ_TX_DATA_SENT_MASK), "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); + debug_if(tx_fifo_remaining != 0, "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); +#endif + + if(_spirit_tx_started) { + _spirit_tx_started = false; + + /* call user callback */ + if(_current_irq_callback) { + _current_irq_callback(TX_DONE); } -#endif } - /* Checks the RSSI value with the threshold */ - if(rssi_value<CCA_THRESHOLD) { - return 0; - } else { - return 1; - } - } - - int SimpleSpirit1::get_pending_packet(void) - { - return !IS_RXBUF_EMPTY(); + /* Disable handling of other TX flags */ + x_irq_status.IRQ_TX_FIFO_ALMOST_EMPTY = S_RESET; + tx_fifo_buffer = NULL; } - /** Spirit Irq Callback **/ - /* betzw - TODO: use threaded interrupt handling when `MBED_CONF_RTOS_PRESENT` is defined (see `atmel-rf-driver`) */ - void SimpleSpirit1::IrqHandler() { - st_lib_spirit_irqs x_irq_status; - - /* get interrupt source from radio */ - irq_get_status(&x_irq_status); - - /* The IRQ_TX_DATA_SENT notifies the packet has been sent. Puts the SPIRIT1 in RX */ - if(x_irq_status.IRQ_TX_DATA_SENT) { /* betzw - NOTE: MUST be handled before `IRQ_RX_DATA_READY` for Nanostack integration! - Logically, Nanostack only expects the "DONE" after "SUCCESS" (if it gets - DONE before SUCCESS, it assumes you're not going to bother to send SUCCESS). - */ +#ifndef RX_FIFO_THR_WA + /* The IRQ_TX_FIFO_ALMOST_EMPTY notifies an nearly empty TX fifo */ + if(x_irq_status.IRQ_TX_FIFO_ALMOST_EMPTY) { #ifdef DEBUG_IRQ - uint32_t *tmp = (uint32_t*)&x_irq_status; - debug_if(!((*tmp) & IRQ_TX_DATA_SENT_MASK), "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); - debug_if(tx_fifo_remaining != 0, "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); + uint32_t *tmp = (uint32_t*)&x_irq_status; + debug_if(!((*tmp) & IRQ_TX_FIFO_ALMOST_EMPTY_MASK), "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); + debug_if(!_spirit_tx_started, "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); + debug_if(tx_fifo_buffer == NULL, "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); #endif - if(_spirit_tx_started) { - _spirit_tx_started = false; + int8_t fifo_available = SPIRIT_MAX_FIFO_LEN/2; // fill-up half fifo + int8_t to_send = (tx_fifo_remaining > fifo_available) ? fifo_available : tx_fifo_remaining; + + tx_fifo_remaining -= to_send; + + /* Fill FIFO Buffer */ + if(to_send > 0) { + spi_write_linear_fifo(to_send, (uint8_t*)&tx_fifo_buffer[tx_buffer_pos]); + } + tx_buffer_pos += to_send; + } +#endif // !RX_FIFO_THR_WA + + /* TX FIFO underflow/overflow error */ + if(x_irq_status.IRQ_TX_FIFO_ERROR) { +#ifdef DEBUG_IRQ + uint32_t *tmp = (uint32_t*)&x_irq_status; + debug("\r\n%s (%d): irq=%x\r\n", __func__, __LINE__, *tmp); + debug_if(!((*tmp) & IRQ_TX_FIFO_ERROR_MASK), "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); +#endif + if(_spirit_tx_started) { + _spirit_tx_started = false; + /* call user callback */ + if(_current_irq_callback) { + _current_irq_callback(TX_ERR); + } + } - /* call user callback */ - if(_current_irq_callback) { - _current_irq_callback(TX_DONE); + /* reset data still to be sent */ + tx_fifo_remaining = 0; + } + + /* The IRQ_RX_DATA_READY notifies a new packet arrived */ + if(x_irq_status.IRQ_RX_DATA_READY) { +#ifdef DEBUG_IRQ + uint32_t *tmp = (uint32_t*)&x_irq_status; + debug_if(!((*tmp) & IRQ_RX_DATA_READY_MASK), "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); +#endif + + if(!_is_receiving) { // spurious irq?!? (betzw: see comments on macro 'RX_FIFO_THR_WA'!) +#ifdef HEAVY_DEBUG + debug("\r\n%s (%d): irq=%x\r\n", __func__, __LINE__, *tmp); +#endif + } else { + _is_receiving = false; // Finished receiving + stop_rx_timeout(); + + spirit_rx_len = pkt_basic_get_received_pkt_length(); + +#ifdef DEBUG_IRQ + debug_if(!(spirit_rx_len <= MAX_PACKET_LEN), "\r\n%s (%d): irq=%x\r\n", __func__, __LINE__, *tmp); +#endif + + if(spirit_rx_len <= MAX_PACKET_LEN) { + uint8_t to_receive = spirit_rx_len - _spirit_rx_pos; + if(to_receive > 0) { + spi_read_linear_fifo(to_receive, &spirit_rx_buf[_spirit_rx_pos]); + _spirit_rx_pos += to_receive; } } - /* Disable handling of other TX flags */ - x_irq_status.IRQ_TX_FIFO_ALMOST_EMPTY = S_RESET; - tx_fifo_buffer = NULL; - } - -#ifndef RX_FIFO_THR_WA - /* The IRQ_TX_FIFO_ALMOST_EMPTY notifies an nearly empty TX fifo */ - if(x_irq_status.IRQ_TX_FIFO_ALMOST_EMPTY) { -#ifdef DEBUG_IRQ - uint32_t *tmp = (uint32_t*)&x_irq_status; - debug_if(!((*tmp) & IRQ_TX_FIFO_ALMOST_EMPTY_MASK), "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); - debug_if(!_spirit_tx_started, "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); - debug_if(tx_fifo_buffer == NULL, "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); -#endif - - int8_t fifo_available = SPIRIT_MAX_FIFO_LEN/2; // fill-up half fifo - int8_t to_send = (tx_fifo_remaining > fifo_available) ? fifo_available : tx_fifo_remaining; - - tx_fifo_remaining -= to_send; + cmd_strobe(SPIRIT1_STROBE_FRX); - /* Fill FIFO Buffer */ - if(to_send > 0) { - spi_write_linear_fifo(to_send, (uint8_t*)&tx_fifo_buffer[tx_buffer_pos]); - } - tx_buffer_pos += to_send; - } -#endif // !RX_FIFO_THR_WA + last_rssi = qi_get_rssi(); //MGR + last_sqi = qi_get_sqi(); //MGR - /* TX FIFO underflow/overflow error */ - if(x_irq_status.IRQ_TX_FIFO_ERROR) { -#ifdef DEBUG_IRQ - uint32_t *tmp = (uint32_t*)&x_irq_status; - debug("\r\n%s (%d): irq=%x\r\n", __func__, __LINE__, *tmp); - debug_if(!((*tmp) & IRQ_TX_FIFO_ERROR_MASK), "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); -#endif - if(_spirit_tx_started) { - _spirit_tx_started = false; - /* call user callback */ - if(_current_irq_callback) { - _current_irq_callback(TX_ERR); - } + /* call user callback */ + if((_spirit_rx_pos == spirit_rx_len) && _current_irq_callback) { + _current_irq_callback(RX_DONE); } - /* reset data still to be sent */ - tx_fifo_remaining = 0; + /* Disable handling of other RX flags */ + x_irq_status.IRQ_RX_FIFO_ALMOST_FULL = S_RESET; } - - /* The IRQ_RX_DATA_READY notifies a new packet arrived */ - if(x_irq_status.IRQ_RX_DATA_READY) { -#ifdef DEBUG_IRQ - uint32_t *tmp = (uint32_t*)&x_irq_status; - debug_if(!((*tmp) & IRQ_RX_DATA_READY_MASK), "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); -#endif - - if(!_is_receiving) { // spurious irq?!? (betzw: see comments on macro 'RX_FIFO_THR_WA'!) -#ifdef HEAVY_DEBUG - debug("\r\n%s (%d): irq=%x\r\n", __func__, __LINE__, *tmp); -#endif - } else { - _is_receiving = false; // Finished receiving - stop_rx_timeout(); - - spirit_rx_len = pkt_basic_get_received_pkt_length(); - -#ifdef DEBUG_IRQ - debug_if(!(spirit_rx_len <= MAX_PACKET_LEN), "\r\n%s (%d): irq=%x\r\n", __func__, __LINE__, *tmp); -#endif - - if(spirit_rx_len <= MAX_PACKET_LEN) { - uint8_t to_receive = spirit_rx_len - _spirit_rx_pos; - if(to_receive > 0) { - spi_read_linear_fifo(to_receive, &spirit_rx_buf[_spirit_rx_pos]); - _spirit_rx_pos += to_receive; - } - } - - cmd_strobe(SPIRIT1_STROBE_FRX); - - last_rssi = qi_get_rssi(); //MGR - last_sqi = qi_get_sqi(); //MGR - - /* call user callback */ - if((_spirit_rx_pos == spirit_rx_len) && _current_irq_callback) { - _current_irq_callback(RX_DONE); - } - - /* Disable handling of other RX flags */ - x_irq_status.IRQ_RX_FIFO_ALMOST_FULL = S_RESET; - } - } + } #ifndef RX_FIFO_THR_WA - /* RX FIFO almost full */ - if(x_irq_status.IRQ_RX_FIFO_ALMOST_FULL) { + /* RX FIFO almost full */ + if(x_irq_status.IRQ_RX_FIFO_ALMOST_FULL) { +#ifdef DEBUG_IRQ + uint32_t *tmp = (uint32_t*)&x_irq_status; + debug_if(!((*tmp) & IRQ_RX_FIFO_ALMOST_FULL_MASK), "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); +#endif + if(!_is_receiving) { // spurious irq?!? #ifdef DEBUG_IRQ - uint32_t *tmp = (uint32_t*)&x_irq_status; - debug_if(!((*tmp) & IRQ_RX_FIFO_ALMOST_FULL_MASK), "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); + debug("\r\n%s (%d): irq=%x\r\n", __func__, __LINE__, *tmp); #endif - if(!_is_receiving) { // spurious irq?!? + } else { + uint8_t fifo_available = linear_fifo_read_num_elements_rx_fifo(); + if((fifo_available + _spirit_rx_pos) <= MAX_PACKET_LEN) { + spi_read_linear_fifo(fifo_available, &spirit_rx_buf[_spirit_rx_pos]); + _spirit_rx_pos += fifo_available; + } else { #ifdef DEBUG_IRQ debug("\r\n%s (%d): irq=%x\r\n", __func__, __LINE__, *tmp); #endif - } else { - uint8_t fifo_available = linear_fifo_read_num_elements_rx_fifo(); - if((fifo_available + _spirit_rx_pos) <= MAX_PACKET_LEN) { - spi_read_linear_fifo(fifo_available, &spirit_rx_buf[_spirit_rx_pos]); - _spirit_rx_pos += fifo_available; - } else { -#ifdef DEBUG_IRQ - debug("\r\n%s (%d): irq=%x\r\n", __func__, __LINE__, *tmp); -#endif - } } } + } #endif // !RX_FIFO_THR_WA - /* Reception errors */ - if((x_irq_status.IRQ_RX_FIFO_ERROR) || (x_irq_status.IRQ_RX_DATA_DISC)) { + /* Reception errors */ + if((x_irq_status.IRQ_RX_FIFO_ERROR) || (x_irq_status.IRQ_RX_DATA_DISC)) { #ifdef DEBUG_IRQ - uint32_t *tmp = (uint32_t*)&x_irq_status; - debug("\r\n%s (%d): irq=%x\r\n", __func__, __LINE__, *tmp); - debug_if(!((*tmp) & (IRQ_RX_FIFO_ERROR_MASK | IRQ_RX_DATA_DISC_MASK)), "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); + uint32_t *tmp = (uint32_t*)&x_irq_status; + debug("\r\n%s (%d): irq=%x\r\n", __func__, __LINE__, *tmp); + debug_if(!((*tmp) & (IRQ_RX_FIFO_ERROR_MASK | IRQ_RX_DATA_DISC_MASK)), "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); #endif - rx_timeout_handler(); - if(_spirit_tx_started) { - _spirit_tx_started = false; - /* call user callback */ - if(_current_irq_callback) { - _current_irq_callback(TX_ERR); - } - } - } - - /* The IRQ_VALID_SYNC is used to notify a new packet is coming */ - if(x_irq_status.IRQ_VALID_SYNC) { -#ifdef DEBUG_IRQ - uint32_t *tmp = (uint32_t*)&x_irq_status; - debug_if(!((*tmp) & IRQ_VALID_SYNC_MASK), "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); -#endif - /* betzw - NOTE: there is a race condition between Spirit1 receiving packets and - * the MCU trying to send a packet, which gets resolved in favor of - * sending. - */ - if(_spirit_tx_started) { -#ifdef DEBUG_IRQ - debug("\r\n%s (%d): irq=%x\r\n", __func__, __LINE__, *tmp); -#endif - } else { - _is_receiving = true; - start_rx_timeout(); + rx_timeout_handler(); + if(_spirit_tx_started) { + _spirit_tx_started = false; + /* call user callback */ + if(_current_irq_callback) { + _current_irq_callback(TX_ERR); } } } + + /* The IRQ_VALID_SYNC is used to notify a new packet is coming */ + if(x_irq_status.IRQ_VALID_SYNC) { +#ifdef DEBUG_IRQ + uint32_t *tmp = (uint32_t*)&x_irq_status; + debug_if(!((*tmp) & IRQ_VALID_SYNC_MASK), "\r\nAssert failed in: %s (%d)\r\n", __func__, __LINE__); +#endif + /* betzw - NOTE: there is a race condition between Spirit1 receiving packets and + * the MCU trying to send a packet, which gets resolved in favor of + * sending. + */ + if(_spirit_tx_started) { +#ifdef DEBUG_IRQ + debug("\r\n%s (%d): irq=%x\r\n", __func__, __LINE__, *tmp); +#endif + } else { + _is_receiving = true; + start_rx_timeout(); + } + } +}
--- a/stm-spirit1-rf-driver/SimpleSpirit1.h Fri Feb 02 09:57:17 2018 +0100 +++ b/stm-spirit1-rf-driver/SimpleSpirit1.h Fri Feb 02 14:13:24 2018 +0100 @@ -442,14 +442,7 @@ return *_singleton; } - /** Create singleton instance of 'SimpleSpirit1' - * - * @param mosi 'PinName' of mosi pin to use - * @param miso 'PinName' of miso pin to use - * @param sclk 'PinName' of clock pin to use - * @param irq 'PinName' of interrupt pin to use - * @param cs 'PinName' of chip-select pin pin to use - * @param sdn 'PinName' of pin to use for device shutdown + /** Get singleton instance of 'SimpleSpirit1' * * @returns reference to singleton instance */ @@ -476,6 +469,7 @@ /** Switch Radio On */ int on(void); + /** Switch Radio Off */ int off(void); @@ -509,9 +503,9 @@ */ 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. + /** Perform a Clear-Channel Assessment (CCA) to find out whether the medium is idle or not. * - * @returns 1 if packet has been seen. + * @returns 1 if the medium is busy. */ int channel_clear(void);