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: mbed_driver_api.cpp
- Revision:
- 12:b8056eda4028
- Parent:
- 11:b769d6caad82
- Child:
- 13:739d89e71f31
--- a/mbed_driver_api.cpp Wed Oct 26 15:08:44 2016 +0200
+++ b/mbed_driver_api.cpp Fri Oct 28 14:14:03 2016 +0200
@@ -1,5 +1,9 @@
#include "SimpleSpirit1.h"
#include "nanostack/platform/arm_hal_phy.h"
+#include "platform/arm_hal_interrupt.h"
+
+#include "mbed_trace.h"
+#define TRACE_GROUP "SPIRIT"
/*Atmel RF Part Type*/
// betzw - TODO
@@ -32,30 +36,91 @@
{CHANNEL_PAGE_0, NULL}
};
+static uint8_t need_ack = 0;
static uint8_t tx_sequence = 0xff;
static uint8_t mac_tx_handle = 0;
static SimpleSpirit1 *rf_device = NULL;
static uint8_t rf_rx_buf[MAX_PACKET_LEN];
+static uint8_t stored_mac_address[8];
+static uint8_t stored_short_adr[2];
+static uint8_t stored_pan_id[2];
+
+#define RF_SIG_ACK_NEEDED (1<<0)
+static Thread rf_ack_sender(osPriorityRealtime);
+static uint8_t rf_rx_sequence;
+static uint8_t rf_src_pan_id[2];
+static bool rf_ack_sent = false;
+
+/* MAC frame helper macros */
+#define MAC_FCF_FRAME_TYPE_MASK 0x0007
+#define MAC_FCF_FRAME_TYPE_SHIFT 0
+#define MAC_FCF_SECURITY_BIT_MASK 0x0008
+#define MAC_FCF_SECURITY_BIT_SHIFT 3
+#define MAC_FCF_PENDING_BIT_MASK 0x0010
+#define MAC_FCF_PENDING_BIT_SHIFT 4
+#define MAC_FCF_ACK_REQ_BIT_MASK 0x0020
+#define MAC_FCF_ACK_REQ_BIT_SHIFT 5
+#define MAC_FCF_INTRA_PANID_MASK 0x0040
+#define MAC_FCF_INTRA_PANID_SHIFT 6
+#define MAC_FCF_DST_ADDR_MASK 0x0c00
+#define MAC_FCF_DST_ADDR_SHIFT 10
+#define MAC_FCF_VERSION_MASK 0x3000
+#define MAC_FCF_VERSION_SHIFT 12
+#define MAC_FCF_SRC_ADDR_MASK 0xc000
+#define MAC_FCF_SRC_ADDR_SHIFT 14
+
+/* MAC supported frame types */
+#define FC_BEACON_FRAME 0x00
+#define FC_DATA_FRAME 0x01
+#define FC_ACK_FRAME 0x02
+#define FC_CMD_FRAME 0x03
+
+static void rf_if_lock(void)
+{
+ platform_enter_critical();
+}
+
+static void rf_if_unlock(void)
+{
+ platform_exit_critical();
+}
+
static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol)
{
+ MBED_ASSERT(data_length >= 3);
+
/*Check if transmitter is busy*/
- if((rf_device->get_receiving_packet()) || (rf_device->channel_clear() == 0)) {
- /*Return busy*/
+ if((rf_device->get_pending_packet()) || (rf_device->channel_clear() == 0)) {
+ /*Return busy*/
return -1;
} else {
- /*Store the sequence number for ACK handling*/
- tx_sequence = *(data_ptr + 2);
+ /* Get Lock */
+ rf_if_lock();
- /*Store TX handle*/
- mac_tx_handle = tx_handle;
+ /*Check if transmitted data needs to be acked*/
+ if((data_ptr[0] & MAC_FCF_ACK_REQ_BIT_MASK) >> MAC_FCF_ACK_REQ_BIT_SHIFT)
+ need_ack = 1;
+ else
+ need_ack = 0;
+
+ /*Store the sequence number for ACK handling*/
+ tx_sequence = *(data_ptr + 2);
+
+ /*Store TX handle*/
+ mac_tx_handle = tx_handle;
/*Send the packet*/
rf_device->send(data_ptr, data_length);
+
+ /* Release Lock */
+ rf_if_unlock();
+
+ tr_debug("%s (%d), tx_handle=%x, tx_seq=%x", __func__, __LINE__, tx_handle, tx_sequence);
}
- /*Return success*/
+ /*Return success*/
return 0;
}
@@ -66,10 +131,12 @@
{
/*Reset PHY driver and set to idle*/
case PHY_INTERFACE_RESET:
+ tr_debug("%s (%d)", __func__, __LINE__);
rf_device->reset_board();
break;
/*Disable PHY Interface driver*/
case PHY_INTERFACE_DOWN:
+ tr_debug("%s (%d)", __func__, __LINE__);
ret_val = rf_device->off();
if(ret_val != 0) ret_val = -1;
break;
@@ -77,19 +144,26 @@
case PHY_INTERFACE_UP:
ret_val = rf_device->on();
if(ret_val != 0) {
+ tr_debug("%s (%d)", __func__, __LINE__);
ret_val = -1;
break;
}
+ tr_debug("%s (%d) - channel: %d", __func__, __LINE__, (int)rf_channel);
rf_device->set_channel(rf_channel);
break;
/*Enable wireless interface ED scan mode*/
case PHY_INTERFACE_RX_ENERGY_STATE:
+ tr_debug("%s (%d)", __func__, __LINE__);
break;
/*Enable Sniffer state*/
case PHY_INTERFACE_SNIFFER_STATE:
// TODO - if we really need this - WAS: rf_setup_sniffer(rf_channel);
+ tr_debug("%s (%d)", __func__, __LINE__);
ret_val = -1;
break;
+ default:
+ tr_debug("%s (%d)", __func__, __LINE__);
+ break;
}
return ret_val;
}
@@ -100,45 +174,82 @@
{
/*Control MAC pending bit for Indirect data transmission*/
case PHY_EXTENSION_CTRL_PENDING_BIT:
+ tr_debug("%s (%d)", __func__, __LINE__);
break;
/*Return frame pending status*/
case PHY_EXTENSION_READ_LAST_ACK_PENDING_STATUS:
- // TODO: *data_ptr = rf_if_last_acked_pending();
- *data_ptr = 0;
+ tr_debug("%s (%d), need_ack=%x", __func__, __LINE__, (unsigned int)need_ack);
+ *data_ptr = need_ack;
break;
/*Set channel, used for setting channel for energy scan*/
case PHY_EXTENSION_SET_CHANNEL:
+ tr_debug("%s (%d)", __func__, __LINE__);
break;
/*Read energy on the channel*/
case PHY_EXTENSION_READ_CHANNEL_ENERGY:
// TODO: *data_ptr = rf_get_channel_energy();
+ tr_debug("%s (%d)", __func__, __LINE__);
*data_ptr = rf_device->get_last_rssi_dbm();
break;
/*Read status of the link*/
case PHY_EXTENSION_READ_LINK_STATUS:
// TODO: *data_ptr = rf_get_link_status();
- *data_ptr = rf_device->get_last_lqi();
+ tr_debug("%s (%d)", __func__, __LINE__);
+ *data_ptr = rf_device->get_last_lqi()*17;
break;
default:
+ tr_debug("%s (%d)", __func__, __LINE__);
break;
}
return 0;
}
+#if 0 // Not used in this example
+static inline void rf_set_mac_48bit(uint8_t *ptr) {
+ tr_debug("%s (%d), adr0=%x, adr1=%x, adr2=%x, adr3=%x, adr4=%x, adr5=%x",
+ __func__, __LINE__,
+ ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]);
+}
+#endif // 0
+
+static inline void rf_set_mac_address(uint8_t *ptr) {
+ tr_debug("%s (%d), adr0=%x, adr1=%x, adr2=%x, adr3=%x, adr4=%x, adr5=%x, adr6=%x, adr7=%x",
+ __func__, __LINE__,
+ ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7]);
+ for(int i = 0; i < 8; i++) {
+ stored_mac_address[i] = ptr[i];
+ }
+}
+
+static inline void rf_set_short_adr(uint8_t *ptr) {
+ tr_debug("%s (%d), adr0=%x, adr1=%x",
+ __func__, __LINE__,
+ ptr[0], ptr[1]);
+ stored_short_adr[0] = ptr[0];
+ stored_short_adr[1] = ptr[1];
+}
+
+static inline void rf_set_pan_id(uint8_t *ptr) {
+ tr_debug("%s (%d), adr0=%x, adr1=%x",
+ __func__, __LINE__,
+ ptr[0], ptr[1]);
+ stored_pan_id[0] = ptr[0];
+}
+
static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr)
{
-#if 0 // TODO - if we really need this - WAS
switch (address_type)
{
/*Set 48-bit address*/
case PHY_MAC_48BIT:
/* Not used in this example */
+ // betzw - WAS: rf_set_mac_48bit(address_ptr);
break;
/*Set 64-bit address*/
case PHY_MAC_64BIT:
@@ -153,34 +264,117 @@
rf_set_pan_id(address_ptr);
break;
}
-#endif // 0
return 0;
}
/* Note: we are in IRQ context */
-static void rf_handle_ack(uint8_t seq_number, uint8_t data_pending)
+static void rf_handle_ack(uint8_t seq_number)
{
- phy_link_tx_status_e phy_status;
-
- // TODO - if we really need this - WAS: rf_if_lock();
-
/*Received ACK sequence must be equal with transmitted packet sequence*/
if(tx_sequence == seq_number)
{
- /*When data pending bit in ACK frame is set, inform NET library*/
- if(data_pending)
- phy_status = PHY_LINK_TX_DONE_PENDING;
- else
- phy_status = PHY_LINK_TX_DONE;
+ /* Reset 'need_ack' */
+ need_ack = 0;
- /*Call PHY TX Done API*/
+ /*Call PHY TX Done API*/
if(device_driver.phy_tx_done_cb){
- device_driver.phy_tx_done_cb(rf_radio_driver_id, mac_tx_handle, phy_status, 1, 1);
+ device_driver.phy_tx_done_cb(rf_radio_driver_id, mac_tx_handle, PHY_LINK_TX_DONE, 1, 1);
}
}
+}
- // TODO - if we really need this - WAS: rf_if_unlock();
+/* Note: we are in IRQ context */
+static inline bool rf_check_mac_address(uint8_t *dest) {
+ for(int i = 0; i < 8; i++) {
+ if(dest[i] != stored_mac_address[i]) return false;
+ }
+ return true;
+}
+
+/* Note: we are in IRQ context */
+/* Returns true if packet should be accepted */
+static bool rf_check_destination(int len, uint8_t *ack_requested) {
+ uint8_t dst_pan_id[2];
+ uint8_t dest_addr[8];
+ uint8_t dst_addr_mode = 0x0; /*0x00 = no address 0x01 = reserved 0x02 = 16-bit short address 0x03 = 64-bit extended address */
+ uint8_t src_addr_mode = 0x0; /*0x00 = no address 0x01 = reserved 0x02 = 16-bit short address 0x03 = 64-bit extended address */
+ uint8_t min_size = 3;
+ bool ret = false;
+
+ if(len < min_size) return false;
+
+ (*ack_requested) = ((rf_rx_buf[0] & MAC_FCF_ACK_REQ_BIT_MASK) >> MAC_FCF_ACK_REQ_BIT_SHIFT);
+ dst_addr_mode = ((rf_rx_buf[0] & MAC_FCF_DST_ADDR_MASK) >> MAC_FCF_DST_ADDR_SHIFT);
+ src_addr_mode = ((rf_rx_buf[0] & MAC_FCF_SRC_ADDR_MASK) >> MAC_FCF_SRC_ADDR_SHIFT);
+
+ rf_rx_sequence = rf_rx_buf[2];
+
+ switch(dst_addr_mode) {
+ case 0x00:
+ ret = true; // no check & ack possible;
+ (*ack_requested) = 0;
+ break;
+ case 0x02:
+ min_size = 7;
+ if(len < min_size) return false;
+ min_size = 9;
+ dst_pan_id[0] = rf_rx_buf[3];
+ dst_pan_id[1] = rf_rx_buf[4];
+ if((dst_pan_id[0] == 0xFF) && (dst_pan_id[1] == 0xFF)) {
+ ret = true;
+ break;
+ }
+
+ if((dst_pan_id[0] == stored_pan_id[0]) && (dst_pan_id[1] == stored_pan_id[1])) {
+ ret = true;
+ break;
+ }
+
+ dest_addr[0] = rf_rx_buf[5];
+ dest_addr[1] = rf_rx_buf[6];
+ if((dest_addr[0] == stored_short_adr[0]) && (dest_addr[1] == stored_short_adr[1])) {
+ ret = true;
+ break;
+ }
+ break;
+ case 0x03:
+ min_size = 7;
+ if(len < 7) return false;
+ min_size = 15;
+ dst_pan_id[0] = rf_rx_buf[3];
+ dst_pan_id[1] = rf_rx_buf[4];
+ if((dst_pan_id[0] == 0xFF) && (dst_pan_id[1] == 0xFF)) {
+ ret = true;
+ break;
+ }
+
+ ret = rf_check_mac_address(&rf_rx_buf[5]);
+ break;
+ default:
+ /* not supported */
+ return false;
+ }
+
+ if(*ack_requested) {
+ if(src_addr_mode == 0x00) {
+ *ack_requested = 0; // cannot send acknowledgment
+ } else {
+ if(len < min_size) {
+ *ack_requested = 0; // cannot send acknowledgment
+ } else {
+ rf_src_pan_id[0] = rf_rx_buf[min_size-2];
+ rf_src_pan_id[1] = rf_rx_buf[min_size-1];
+ }
+ }
+ }
+
+ return ret;
+}
+
+/* Note: we are in IRQ context */
+static inline void rf_send_ack() {
+ rf_ack_sender.signal_set(RF_SIG_ACK_NEEDED);
}
/* Note: we are in IRQ context */
@@ -189,31 +383,39 @@
uint8_t rf_lqi;
int8_t rf_rssi;
uint16_t rf_buffer_len;
+ uint8_t ack_requested = 0;
/* Get received data */
rf_buffer_len = rf_device->read(rf_rx_buf, MAX_PACKET_LEN);
if(!rf_buffer_len)
return;
- /* If waiting for ACK, check here if the packet is an ACK to a message previously sent */
- if((rf_buffer_len == 3) && ((rf_rx_buf[0] & 0x07) == 0x02)) {
- uint8_t pending = 0;
+ /* Check if packet should be accepted */
+ if(!rf_check_destination(rf_buffer_len, &ack_requested)) {
+ return;
+ }
- /*Check if data is pending*/
- if ((rf_rx_buf[0] & 0x10)) {
- pending=1;
- }
+ /* If waiting for ACK, check here if the packet is an ACK to a message previously sent */
+ if((rf_buffer_len == 5) && (((rf_rx_buf[0] & MAC_FCF_FRAME_TYPE_MASK) >> MAC_FCF_FRAME_TYPE_SHIFT) == FC_ACK_FRAME)) {
+ /*Send sequence number in ACK handler*/
+ tr_debug("%s (%d), len=%u", __func__, __LINE__, (unsigned int)rf_buffer_len);
+ rf_handle_ack(rf_rx_buf[2]);
+ return;
+ }
- /*Send sequence number in ACK handler*/
- rf_handle_ack(rf_rx_buf[2], pending);
- return;
+ /* Kick off ACK sending */
+ if(ack_requested) {
+ rf_send_ack();
}
/* Get link information */
rf_rssi = (int8_t)rf_device->get_last_rssi_dbm();
rf_lqi = (uint8_t)rf_device->get_last_lqi();
+ rf_lqi *= 17; // scale to 8-bit value
/* Note: Checksum of the packet must be checked and removed before entering here */
+ /* TODO - betzw: what to do? */
+ // rf_buffer_len -= 2;
/* Send received data and link information to the network stack */
if( device_driver.phy_rx_cb ){
@@ -226,6 +428,12 @@
{
phy_link_tx_status_e phy_status = PHY_LINK_TX_SUCCESS;
+ /* Check if this is an ACK sending which is still pending */
+ if(rf_ack_sent) {
+ rf_ack_sent = false;
+ return; // no need to inform stack
+ }
+
/*Call PHY TX Done API*/
if(device_driver.phy_tx_done_cb){
device_driver.phy_tx_done_cb(rf_radio_driver_id, mac_tx_handle, phy_status, 1, 1);
@@ -252,14 +460,52 @@
rf_handle_tx_end();
break;
case SimpleSpirit1::TX_ERR:
+ tr_debug("%s (%d)", __func__, __LINE__);
rf_handle_tx_err();
break;
}
}
+static void rf_ack_loop(void) {
+ static uint16_t buffer[4];
+
+ /* Pre-prepare payload */
+ buffer[0] = FC_ACK_FRAME | (0x2 << MAC_FCF_DST_ADDR_SHIFT); // FCF
+
+ do {
+ /* Wait for signal */
+ rf_ack_sender.signal_wait(RF_SIG_ACK_NEEDED);
+
+ /* Prepare payload */
+ uint8_t *ptr = (uint8_t*)&buffer[1];
+ ptr[0] = rf_rx_sequence; // Sequence number
+ ptr[1] = rf_src_pan_id[0]; // pan_id_0
+ ptr[2] = rf_src_pan_id[1]; // pan_id_1
+
+ /* Get Lock */
+ rf_if_lock();
+
+ /* Wait for device not receiving */
+ while(rf_device->is_receiving()) {
+ wait_us(10);
+ }
+
+ /* Set information that we have sent an ACK */
+ rf_ack_sent = true;
+
+ /*Send the packet*/
+ rf_device->send((uint8_t*)buffer, 5);
+
+ /* Release Lock */
+ rf_if_unlock();
+ } while(true);
+}
+
static void rf_init(void) {
rf_device = &SimpleSpirit1::CreateInstance(D11, D12, D13, D9, D10, D2);
rf_device->attach_irq_callback(rf_callback_func);
+
+ rf_ack_sender.start(rf_ack_loop);
}
extern "C" int8_t rf_device_register(void)
@@ -303,6 +549,7 @@
/*Register device driver*/
rf_radio_driver_id = arm_net_phy_register(&device_driver);
+ tr_debug("%s (%d)", __func__, __LINE__);
return rf_radio_driver_id;
}
@@ -315,6 +562,7 @@
*/
extern "C" void rf_read_mac_address(uint8_t *ptr)
{
+ tr_debug("%s (%d)", __func__, __LINE__);
memcpy(ptr, mac_address, 8);
}
@@ -327,6 +575,7 @@
*/
extern "C" int8_t rf_read_random(void)
{
+ tr_debug("%s (%d)", __func__, __LINE__);
return rf_rnd_rssi;
}
@@ -339,5 +588,6 @@
*/
extern "C" rf_trx_part_e rf_radio_type_read(void)
{
- return ATMEL_UNKNOW_DEV;
+ tr_debug("%s (%d)", __func__, __LINE__);
+ return ATMEL_UNKNOW_DEV;
}
X-NUCLEO-IDS01A4 Sub-1GHz RF Expansion Board