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:
- 3:0df38cfb1e53
- Parent:
- 2:45642c5198a2
- Child:
- 4:07537ca85c66
--- a/SimpleSpirit1.cpp Fri Oct 14 10:42:56 2016 +0200
+++ b/SimpleSpirit1.cpp Fri Oct 14 16:47:22 2016 +0200
@@ -1,18 +1,48 @@
/*** Mbed Includes ***/
#include "SimpleSpirit1.h"
+#include "radio_spi.h"
/*** Macros from Cube Implementation ***/
-#define CLEAR_TXBUF() (spirit_txbuf[0] = 0)
-#define CLEAR_RXBUF() (spirit_rxbuf[0] = 0)
+#define CLEAR_TXBUF() (spirit_txbuf[0] = 0)
+#define CLEAR_RXBUF() (spirit_rxbuf[0] = 0)
/* transceiver state. */
#define ON 0
#define OFF 1
+#ifndef NDEBUG
+#include <stdio.h>
+#define PRINTF(...) printf(__VA_ARGS__)
+#else
+#define PRINTF(...)
+#endif
-SimpleSpirit1 *SimpleSpirit1::_singleton = NULL;
+#if NULLRDC_CONF_802154_AUTOACK
+#define ACK_LEN 3
+static int wants_an_ack = 0; /* The packet sent expects an ack */
+//#define ACKPRINTF printf
+#define ACKPRINTF(...)
+#endif /* NULLRDC_CONF_802154_AUTOACK */
+
+#define SPIRIT_GPIO_IRQ (SPIRIT_GPIO_3)
+
+#define SPIRIT1_STATUS() (arch_refresh_status() & SPIRIT1_STATE_STATEBITS)
+
+#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)); \
+ } while(0)
+
/*** Class Implementation ***/
+/** Static Class Variables **/
+SimpleSpirit1 *SimpleSpirit1::_singleton = NULL;
+Timer SimpleSpirit1::_busywait_timer;
+
+/** Constructor **/
SimpleSpirit1::SimpleSpirit1(PinName mosi, PinName miso, PinName sclk,
PinName irq, PinName cs, PinName sdn,
PinName led) :
@@ -20,10 +50,11 @@
_irq(irq),
_chip_select(cs),
_shut_down(sdn),
- _led(led),
- _nr_of_irq_disables(0)
+ _led(led)
{
- /* reset irq disable counter & disable irq */
+ /* reset irq disable counter and irq callback & disable irq */
+ _nr_of_irq_disables = 0;
+ _current_irq_callback = NULL;
disable_irq();
/* unselect chip */
@@ -32,8 +63,18 @@
/* configure spi */
_spi.format(8, 0); /* 8-bit, mode = 0, [order = SPI_MSB] only available in mbed3 */
_spi.frequency(5000000); // 5MHz
+
+ /* start timer */
+ _busywait_timer.start();
+
+ /* init cube vars */
+ spirit_on = OFF;
+ packet_is_prepared = 0;
+ receiving_packet = 0;
+ just_got_an_ack = 0;
}
+/** Init Function **/
void SimpleSpirit1::init() {
/* set frequencies */
radio_set_xtal_freq(XTAL_FREQUENCY);
@@ -109,3 +150,214 @@
};
spirit_gpio_init(&x_gpio_init);
}
+
+/** Prepare the radio with a packet to be sent. **/
+int SimpleSpirit1::prepare(const void *payload, unsigned short payload_len) {
+ PRINTF("Spirit1: prep %u\n", payload_len);
+ packet_is_prepared = 0;
+
+ /* Checks if the payload length is supported */
+ if(payload_len > MAX_PACKET_LEN) {
+ return RADIO_TX_ERR;
+ }
+
+ /* Should we delay for an ack? */
+#if NULLRDC_CONF_802154_AUTOACK
+ frame802154_t info154;
+ wants_an_ack = 0;
+ if(payload_len > ACK_LEN
+ && frame802154_parse((char*)payload, payload_len, &info154) != 0) {
+ if(info154.fcf.frame_type == FRAME802154_DATAFRAME
+ && info154.fcf.ack_required != 0) {
+ wants_an_ack = 1;
+ }
+ }
+#endif /* NULLRDC_CONF_802154_AUTOACK */
+
+ /* Sets the length of the packet to send */
+ disable_irq();
+ cmd_strobe_command(SPIRIT1_STROBE_FTX);
+ pkt_basic_set_payload_length(payload_len);
+ spi_write_linear_fifo(payload_len, (uint8_t *)payload);
+ enable_irq();
+
+ PRINTF("PREPARE OUT\n");
+
+ packet_is_prepared = 1;
+ return RADIO_TX_OK;
+}
+
+/** Send the packet that has previously been prepared. **/
+int SimpleSpirit1::transmit(unsigned short payload_len) {
+ /* This function blocks until the packet has been transmitted */
+ //rtimer_clock_t rtimer_txdone, rtimer_rxack;
+
+ PRINTF("TRANSMIT IN\n");
+ if(!packet_is_prepared) {
+ return RADIO_TX_ERR;
+ }
+
+ /* Stores the length of the packet to send */
+ /* Others spirit_radio_prepare will be in hold */
+ spirit_txbuf[0] = payload_len;
+
+ /* Puts the SPIRIT1 in TX state */
+ receiving_packet = 0;
+ set_ready_state();
+ cmd_strobe_command(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
+ BUSYWAIT_UNTIL(SPIRIT1_STATUS() != SPIRIT1_STATE_TX, 50); //For FSK with low data rate
+
+ /* Reset radio - needed for immediate RX of ack */
+ CLEAR_TXBUF();
+ CLEAR_RXBUF();
+ disable_irq();
+ irq_clear_status();
+ cmd_strobe_command(SPIRIT1_STROBE_SABORT);
+ wait_us(SABORT_WAIT_US);
+ cmd_strobe_command(SPIRIT1_STROBE_READY);
+ BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_READY, 1);
+ cmd_strobe_command(SPIRIT1_STROBE_RX);
+ BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_RX, 1);
+ enable_irq();
+
+#if XXX_ACK_WORKAROUND
+ just_got_an_ack = 1;
+#endif /* XXX_ACK_WORKAROUND */
+
+#if NULLRDC_CONF_802154_AUTOACK
+ if (wants_an_ack) {
+ rtimer_txdone = _busywait_timer.read_ms();
+ BUSYWAIT_UNTIL(just_got_an_ack, 2);
+ rtimer_rxack = _busywait_timer.read_ms();
+
+ if(just_got_an_ack) {
+ ACKPRINTF("debug_ack: ack received after %u ms\n",
+ (uint32_t)(rtimer_rxack - rtimer_txdone));
+ } else {
+ ACKPRINTF("debug_ack: no ack received\n");
+ }
+ }
+#endif /* NULLRDC_CONF_802154_AUTOACK */
+
+ PRINTF("TRANSMIT OUT\n");
+
+ CLEAR_TXBUF();
+
+ packet_is_prepared = 0;
+
+ wait_us(1);
+
+ return RADIO_TX_OK;
+}
+
+/** Set Ready State **/
+void SimpleSpirit1::set_ready_state(void) {
+ PRINTF("READY IN\n");
+
+ irq_clear_status();
+ disable_irq();
+
+ if(SPIRIT1_STATUS() == SPIRIT1_STATE_STANDBY) {
+ cmd_strobe_command(SPIRIT1_STROBE_READY);
+ } else if(SPIRIT1_STATUS() == SPIRIT1_STATE_RX) {
+ cmd_strobe_command(SPIRIT1_STROBE_SABORT);
+ irq_clear_status();
+ }
+
+ enable_irq();
+
+ PRINTF("READY OUT\n");
+}
+
+int SimpleSpirit1::radio_off(void) {
+ PRINTF("Spirit1: ->off\n");
+ if(spirit_on == ON) {
+ /* Disables the mcu to get IRQ from the SPIRIT1 */
+ disable_irq();
+
+ /* first stop rx/tx */
+ cmd_strobe_command(SPIRIT1_STROBE_SABORT);
+
+ /* Clear any pending irqs */
+ irq_clear_status();
+
+ BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_READY, 5);
+ if(SPIRIT1_STATUS() != SPIRIT1_STATE_READY) {
+ PRINTF("Spirit1: failed off->ready\n");
+ return 1;
+ }
+
+ /* Puts the SPIRIT1 in STANDBY */
+ cmd_strobe_command(SPIRIT1_STROBE_STANDBY);
+ BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_STANDBY, 5);
+ if(SPIRIT1_STATUS() != SPIRIT1_STATE_STANDBY) {
+ PRINTF("Spirit1: failed off->standby\n");
+ return 1;
+ }
+
+ spirit_on = OFF;
+ CLEAR_TXBUF();
+ CLEAR_RXBUF();
+ }
+ PRINTF("Spirit1: off.\n");
+ return 0;
+}
+
+int SimpleSpirit1::radio_on(void) {
+ PRINTF("Spirit1: on\n");
+ cmd_strobe_command(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);
+ BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_READY, 5);
+ if(SPIRIT1_STATUS() != SPIRIT1_STATE_READY) {
+ PRINTF("Spirit1: failed to turn on\n");
+ while(1);
+ //return 1;
+ }
+
+ /* now we go to Rx */
+ cmd_strobe_command(SPIRIT1_STROBE_RX);
+ BUSYWAIT_UNTIL(SPIRIT1_STATUS() == SPIRIT1_STATE_RX, 5);
+ if(SPIRIT1_STATUS() != SPIRIT1_STATE_RX) {
+ PRINTF("Spirit1: failed to enter rx\n");
+ while(1);
+ //return 1;
+ }
+
+ /* Enables the mcu to get IRQ from the SPIRIT1 */
+ spirit_on = ON;
+ if((_current_irq_callback != NULL) && (*_current_irq_callback)) {
+ enable_irq();
+ }
+ }
+
+ return 0;
+}
+
+uint16_t SimpleSpirit1::arch_refresh_status(void) {
+ uint16_t mcstate;
+ uint8_t header[2];
+ header[0]=READ_HEADER;
+ header[1]=MC_STATE1_BASE;
+
+ /* Puts the SPI chip select low to start the transaction */
+ chip_sync_select();
+
+ /* Write the aHeader bytes and read the SPIRIT1 status bytes */
+ mcstate = _spi.write(header[0]);
+ mcstate = mcstate<<8;
+
+ /* Write the aHeader bytes and read the SPIRIT1 status bytes */
+ mcstate |= _spi.write(header[1]);
+
+ /* Puts the SPI chip select high to end the transaction */
+ chip_sync_unselect();
+
+ return mcstate;
+}
+
X-NUCLEO-IDS01A4 Sub-1GHz RF Expansion Board