ST / stm-spirit1-rf-driver

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:
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();
+    }
+  }
+}