Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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).
Diff: SimpleSpirit1.cpp
- Revision:
- 4:07537ca85c66
- Parent:
- 3:0df38cfb1e53
- Child:
- 5:c9c5bc673c64
--- a/SimpleSpirit1.cpp Fri Oct 14 16:47:22 2016 +0200
+++ b/SimpleSpirit1.cpp Tue Oct 18 07:06:12 2016 +0200
@@ -4,11 +4,9 @@
/*** Macros from Cube Implementation ***/
-#define CLEAR_TXBUF() (spirit_txbuf[0] = 0)
-#define CLEAR_RXBUF() (spirit_rxbuf[0] = 0)
-/* transceiver state. */
-#define ON 0
-#define OFF 1
+#define CLEAR_TXBUF() (spirit_tx_len = 0)
+#define CLEAR_RXBUF() (spirit_rx_len = 0)
+#define IS_RXBUF_EMPTY() (spirit_rx_len == 0)
#ifndef NDEBUG
#include <stdio.h>
@@ -24,18 +22,23 @@
#define ACKPRINTF(...)
#endif /* NULLRDC_CONF_802154_AUTOACK */
-#define SPIRIT_GPIO_IRQ (SPIRIT_GPIO_3)
+#define SPIRIT_GPIO_IRQ (SPIRIT_GPIO_3)
-#define SPIRIT1_STATUS() (arch_refresh_status() & SPIRIT1_STATE_STATEBITS)
+#define SPIRIT1_STATUS() (arch_refresh_status() & SPIRIT1_STATE_STATEBITS)
-#define SABORT_WAIT_US (400)
+#define SABORT_WAIT_US (400)
#define BUSYWAIT_UNTIL(cond, millisecs) \
do { \
- uint32_t start = _busywait_timer.read_ms(); \
- while (!(cond) && ((((uint32_t)(_busywait_timer.read_ms())) - start) < (uint32_t)millisecs)); \
+ uint32_t start = _busywait_timer.read_us(); \
+ while (!(cond) && ((((uint32_t)(_busywait_timer.read_us())) - start) < ((uint32_t)millisecs)*1000U)); \
} while(0)
+extern volatile SpiritStatus g_xStatus;
+#define st_lib_g_x_status (g_xStatus)
+
+#define st_lib_spirit_irqs SpiritIrqs
+
/*** Class Implementation ***/
/** Static Class Variables **/
@@ -50,12 +53,12 @@
_irq(irq),
_chip_select(cs),
_shut_down(sdn),
- _led(led)
+ _led(led),
+ _current_irq_callback()
{
/* reset irq disable counter and irq callback & disable irq */
_nr_of_irq_disables = 0;
- _current_irq_callback = NULL;
- disable_irq();
+ disable_spirit_irq();
/* unselect chip */
chip_unselect();
@@ -72,6 +75,8 @@
packet_is_prepared = 0;
receiving_packet = 0;
just_got_an_ack = 0;
+ last_rssi = 0 ; //MGR
+ last_lqi = 0 ; //MGR
}
/** Init Function **/
@@ -85,7 +90,7 @@
exit_shutdown();
/* soft core reset */
- cmd_strobe_command(SPIRIT1_STROBE_SRES);
+ cmd_strobe(SPIRIT1_STROBE_SRES);
/* Configures the SPIRIT1 radio part */
SRadioInit x_radio_init = {
@@ -137,7 +142,7 @@
radio_afc_freeze_on_sync(S_ENABLE);
/* Puts the SPIRIT1 in STANDBY mode (125us -> rx/tx) */
- cmd_strobe_command(SPIRIT1_STROBE_STANDBY);
+ cmd_strobe(SPIRIT1_STROBE_STANDBY);
spirit_on = OFF;
CLEAR_RXBUF();
CLEAR_TXBUF();
@@ -175,11 +180,11 @@
#endif /* NULLRDC_CONF_802154_AUTOACK */
/* Sets the length of the packet to send */
- disable_irq();
- cmd_strobe_command(SPIRIT1_STROBE_FTX);
+ disable_spirit_irq();
+ cmd_strobe(SPIRIT1_STROBE_FTX);
pkt_basic_set_payload_length(payload_len);
spi_write_linear_fifo(payload_len, (uint8_t *)payload);
- enable_irq();
+ enable_spirit_irq();
PRINTF("PREPARE OUT\n");
@@ -199,12 +204,12 @@
/* Stores the length of the packet to send */
/* Others spirit_radio_prepare will be in hold */
- spirit_txbuf[0] = payload_len;
+ spirit_tx_len = payload_len;
/* Puts the SPIRIT1 in TX state */
receiving_packet = 0;
set_ready_state();
- cmd_strobe_command(SPIRIT1_STROBE_TX);
+ cmd_strobe(SPIRIT1_STROBE_TX);
just_got_an_ack = 0;
BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_TX, 1);
//BUSYWAIT_UNTIL(SPIRIT1_STATUS() != SPIRIT1_STATE_TX, 4); //For GFSK with high data rate
@@ -213,15 +218,15 @@
/* Reset radio - needed for immediate RX of ack */
CLEAR_TXBUF();
CLEAR_RXBUF();
- disable_irq();
+ disable_spirit_irq();
irq_clear_status();
- cmd_strobe_command(SPIRIT1_STROBE_SABORT);
+ cmd_strobe(SPIRIT1_STROBE_SABORT);
wait_us(SABORT_WAIT_US);
- cmd_strobe_command(SPIRIT1_STROBE_READY);
+ cmd_strobe(SPIRIT1_STROBE_READY);
BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_READY, 1);
- cmd_strobe_command(SPIRIT1_STROBE_RX);
+ cmd_strobe(SPIRIT1_STROBE_RX);
BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_RX, 1);
- enable_irq();
+ enable_spirit_irq();
#if XXX_ACK_WORKAROUND
just_got_an_ack = 1;
@@ -229,12 +234,12 @@
#if NULLRDC_CONF_802154_AUTOACK
if (wants_an_ack) {
- rtimer_txdone = _busywait_timer.read_ms();
+ rtimer_txdone = _busywait_timer.read_us();
BUSYWAIT_UNTIL(just_got_an_ack, 2);
- rtimer_rxack = _busywait_timer.read_ms();
+ rtimer_rxack = _busywait_timer.read_us();
if(just_got_an_ack) {
- ACKPRINTF("debug_ack: ack received after %u ms\n",
+ ACKPRINTF("debug_ack: ack received after %u us\n",
(uint32_t)(rtimer_rxack - rtimer_txdone));
} else {
ACKPRINTF("debug_ack: no ack received\n");
@@ -258,28 +263,28 @@
PRINTF("READY IN\n");
irq_clear_status();
- disable_irq();
+ disable_spirit_irq();
if(SPIRIT1_STATUS() == SPIRIT1_STATE_STANDBY) {
- cmd_strobe_command(SPIRIT1_STROBE_READY);
+ cmd_strobe(SPIRIT1_STROBE_READY);
} else if(SPIRIT1_STATUS() == SPIRIT1_STATE_RX) {
- cmd_strobe_command(SPIRIT1_STROBE_SABORT);
+ cmd_strobe(SPIRIT1_STROBE_SABORT);
irq_clear_status();
}
- enable_irq();
+ enable_spirit_irq();
PRINTF("READY OUT\n");
}
-int SimpleSpirit1::radio_off(void) {
+int SimpleSpirit1::off(void) {
PRINTF("Spirit1: ->off\n");
if(spirit_on == ON) {
/* Disables the mcu to get IRQ from the SPIRIT1 */
- disable_irq();
+ disable_spirit_irq();
/* first stop rx/tx */
- cmd_strobe_command(SPIRIT1_STROBE_SABORT);
+ cmd_strobe(SPIRIT1_STROBE_SABORT);
/* Clear any pending irqs */
irq_clear_status();
@@ -291,7 +296,7 @@
}
/* Puts the SPIRIT1 in STANDBY */
- cmd_strobe_command(SPIRIT1_STROBE_STANDBY);
+ cmd_strobe(SPIRIT1_STROBE_STANDBY);
BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_STANDBY, 5);
if(SPIRIT1_STATUS() != SPIRIT1_STATE_STANDBY) {
PRINTF("Spirit1: failed off->standby\n");
@@ -299,6 +304,7 @@
}
spirit_on = OFF;
+ _nr_of_irq_disables = 1;
CLEAR_TXBUF();
CLEAR_RXBUF();
}
@@ -306,13 +312,13 @@
return 0;
}
-int SimpleSpirit1::radio_on(void) {
+int SimpleSpirit1::on(void) {
PRINTF("Spirit1: on\n");
- cmd_strobe_command(SPIRIT1_STROBE_SABORT);
+ cmd_strobe(SPIRIT1_STROBE_SABORT);
wait_us(SABORT_WAIT_US);
if(spirit_on == OFF) {
/* ensure we are in READY state as we go from there to Rx */
- cmd_strobe_command(SPIRIT1_STROBE_READY);
+ cmd_strobe(SPIRIT1_STROBE_READY);
BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_READY, 5);
if(SPIRIT1_STATUS() != SPIRIT1_STATE_READY) {
PRINTF("Spirit1: failed to turn on\n");
@@ -321,7 +327,7 @@
}
/* now we go to Rx */
- cmd_strobe_command(SPIRIT1_STROBE_RX);
+ cmd_strobe(SPIRIT1_STROBE_RX);
BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_RX, 5);
if(SPIRIT1_STATUS() != SPIRIT1_STATE_RX) {
PRINTF("Spirit1: failed to enter rx\n");
@@ -331,8 +337,9 @@
/* Enables the mcu to get IRQ from the SPIRIT1 */
spirit_on = ON;
- if((_current_irq_callback != NULL) && (*_current_irq_callback)) {
- enable_irq();
+ if(_current_irq_callback) {
+ MBED_ASSERT(_nr_of_irq_disables == 1);
+ enable_spirit_irq();
}
}
@@ -361,3 +368,172 @@
return mcstate;
}
+int SimpleSpirit1::read(void *buf, unsigned short bufsize)
+{
+ PRINTF("READ IN\n");
+
+ /* Checks if the RX buffer is empty */
+ if(IS_RXBUF_EMPTY()) {
+ disable_spirit_irq();
+ CLEAR_RXBUF();
+ cmd_strobe(SPIRIT1_STROBE_SABORT);
+ wait_us(SABORT_WAIT_US);
+ cmd_strobe(SPIRIT1_STROBE_READY);
+ BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_READY, 1);
+ cmd_strobe(SPIRIT1_STROBE_RX);
+ BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_RX, 1);
+ PRINTF("READ OUT RX BUF EMPTY\n");
+ enable_spirit_irq();
+ return 0;
+ }
+
+ if(bufsize < spirit_rx_len) {
+ /* If buf has the correct size */
+ PRINTF("TOO SMALL BUF\n");
+ return 0;
+ } else {
+ /* Copies the packet received */
+ memcpy(buf, spirit_rx_buf, spirit_rx_len);
+
+#ifdef CONTIKI // betzw - TODO
+ packetbuf_set_attr(PACKETBUF_ATTR_RSSI, last_rssi); //MGR
+ packetbuf_set_attr(PACKETBUF_ATTR_LINK_QUALITY, last_lqi); //MGR
+#endif
+
+ bufsize = spirit_rx_len;
+ CLEAR_RXBUF();
+
+ PRINTF("READ OUT\n");
+
+ return bufsize;
+ }
+
+}
+
+/*---------------------------------------------------------------------------*/
+int SimpleSpirit1::channel_clear(void)
+{
+ float rssi_value;
+ /* Local variable used to memorize the SPIRIT1 state */
+ uint8_t spirit_state = ON;
+
+ PRINTF("CHANNEL CLEAR IN\n");
+
+ if(spirit_on == OFF) {
+ /* Wakes up the SPIRIT1 */
+ on();
+ spirit_state = OFF;
+ }
+
+ /* */
+ disable_spirit_irq();
+ cmd_strobe(SPIRIT1_STROBE_SABORT);
+ /* SpiritCmdStrobeSabort();*/
+ irq_clear_status();
+ enable_spirit_irq();
+ {
+ uint32_t timeout = _busywait_timer.read_us() + 5000;
+ do {
+ mgmt_refresh_status();
+ } while((st_lib_g_x_status.MC_STATE != MC_STATE_READY) && (((uint32_t)_busywait_timer.read_us()) < timeout));
+ if(st_lib_g_x_status.MC_STATE != MC_STATE_READY) {
+ return 1;
+ }
+ }
+
+ /* Stores the RSSI value */
+ rssi_value = qi_get_rssi_dbm();
+
+ /* Puts the SPIRIT1 in its previous state */
+ if(spirit_state==OFF) {
+ off();
+ } else {
+ cmd_strobe(SPIRIT1_STROBE_RX);
+ /* SpiritCmdStrobeRx();*/
+ BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_RX, 5);
+ }
+
+ PRINTF("CHANNEL CLEAR OUT\n");
+
+ /* Checks the RSSI value with the threshold */
+ if(rssi_value<CCA_THRESHOLD) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+int SimpleSpirit1::incoming_packet(void)
+{
+ return receiving_packet;
+}
+
+int SimpleSpirit1::pending_packet(void)
+{
+ PRINTF("PENDING PACKET\n");
+ return !IS_RXBUF_EMPTY();
+}
+
+/** Contiki Irq Callback **/
+void SimpleSpirit1::ContikiIrqHandler() {
+ st_lib_spirit_irqs x_irq_status;
+
+ /* get interrupt source from radio */
+ irq_get_status(&x_irq_status);
+ irq_clear_status();
+
+ if(x_irq_status.IRQ_RX_FIFO_ERROR) {
+ receiving_packet = 0;
+ cmd_strobe(SPIRIT1_STROBE_FRX);
+ return;
+ }
+
+ if(x_irq_status.IRQ_TX_FIFO_ERROR) {
+ receiving_packet = 0;
+ cmd_strobe(SPIRIT1_STROBE_FTX);
+ return;
+ }
+
+ /* The IRQ_VALID_SYNC is used to notify a new packet is coming */
+ if(x_irq_status.IRQ_VALID_SYNC) {
+ receiving_packet = 1;
+ }
+
+ /* The IRQ_TX_DATA_SENT notifies the packet received. Puts the SPIRIT1 in RX */
+ if(x_irq_status.IRQ_TX_DATA_SENT) {
+ cmd_strobe(SPIRIT1_STROBE_RX);
+ /* SpiritCmdStrobeRx();*/
+ CLEAR_TXBUF();
+ return;
+ }
+
+ /* The IRQ_RX_DATA_READY notifies a new packet arrived */
+ if(x_irq_status.IRQ_RX_DATA_READY) {
+ spi_read_linear_fifo(linear_fifo_read_num_elements_rx_fifo(), spirit_rx_buf);
+ spirit_rx_len = pkt_basic_get_received_pkt_length();
+ cmd_strobe(SPIRIT1_STROBE_FRX);
+
+ last_rssi = qi_get_rssi(); //MGR
+ last_lqi = qi_get_lqi(); //MGR
+
+ receiving_packet = 0;
+
+#if NULLRDC_CONF_802154_AUTOACK
+ if (spirit_rxbuf[0] == ACK_LEN) {
+ /* For debugging purposes we assume this is an ack for us */
+ just_got_an_ack = 1;
+ }
+#endif /* NULLRDC_CONF_802154_AUTOACK */
+
+ /* betzw - TODO: call user callback */
+ return;
+ }
+
+ if(x_irq_status.IRQ_RX_DATA_DISC)
+ {
+ /* RX command - to ensure the device will be ready for the next reception */
+ if(x_irq_status.IRQ_RX_TIMEOUT) {
+ cmd_strobe_flush_rx_fifo();
+ }
+ }
+}
X-NUCLEO-IDS01A4 Sub-1GHz RF Expansion Board