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:
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;
 }