contiki/cpu/stm32w108/dev/stm32w-radio.c

709 lines
20 KiB
C

/*
* Copyright (c) 2010, STMicroelectronics.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This file is part of the Contiki OS
*
* $Id: stm32w-radio.c,v 1.2 2010/10/27 14:05:23 salvopitru Exp $
*/
/*---------------------------------------------------------------------------*/
/**
* \file
* Machine dependent STM32W radio code.
* \author
* Salvatore Pitrulli
* Chi-Anh La la@imag.fr
* Simon Duquennoy <simonduq@sics.se>
*/
/*---------------------------------------------------------------------------*/
#include PLATFORM_HEADER
#include "hal/error.h"
#include "hal/hal.h"
#include "contiki.h"
#include "net/mac/frame802154.h"
#include "dev/stm32w-radio.h"
#include "net/netstack.h"
#include "net/packetbuf.h"
#include "net/rime/rimestats.h"
#include "sys/rtimer.h"
#define DEBUG 0
#include "dev/leds.h"
#define LED_ACTIVITY 0
#ifdef ST_CONF_RADIO_AUTOACK
#define ST_RADIO_AUTOACK ST_CONF_RADIO_AUTOACK
#else
#define ST_RADIO_AUTOACK 0
#endif /* ST_CONF_RADIO_AUTOACK */
#if RDC_CONF_DEBUG_LED
#define LED_RDC RDC_CONF_DEBUG_LED
#define LED_ACTIVITY 1
#else
#define LED_RDC 0
#endif
#if DEBUG > 0
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...) do {} while (0)
#endif
#if LED_ACTIVITY
#define LED_TX_ON() leds_on(LEDS_GREEN)
#define LED_TX_OFF() leds_off(LEDS_GREEN)
#define LED_RX_ON() { \
if(LED_RDC == 0){ \
leds_on(LEDS_RED); \
} \
}
#define LED_RX_OFF() { \
if(LED_RDC == 0){ \
leds_off(LEDS_RED); \
} \
}
#define LED_RDC_ON() { \
if(LED_RDC == 1){ \
leds_on(LEDS_RED); \
} \
}
#define LED_RDC_OFF() { \
if(LED_RDC == 1){ \
leds_off(LEDS_RED); \
} \
}
#else
#define LED_TX_ON()
#define LED_TX_OFF()
#define LED_RX_ON()
#define LED_RX_OFF()
#define LED_RDC_ON()
#define LED_RDC_OFF()
#endif
#if RDC_CONF_HARDWARE_CSMA
#define MAC_RETRIES 0
#endif
#ifndef MAC_RETRIES
#define MAC_RETRIES 1
#endif
#if MAC_RETRIES
int8_t mac_retries_left;
#define INIT_RETRY_CNT() (mac_retries_left = packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS))
#define DEC_RETRY_CNT() (mac_retries_left--)
#define RETRY_CNT_GTZ() (mac_retries_left > 0)
#else
#define INIT_RETRY_CNT()
#define DEC_RETRY_CNT()
#define RETRY_CNT_GTZ() 0
#endif
/* If set to 1, a send() returns only after the packet has been transmitted.
This is necessary if you use the x-mac module, for example. */
#ifndef RADIO_WAIT_FOR_PACKET_SENT
#define RADIO_WAIT_FOR_PACKET_SENT 1
#endif
#define TO_PREV_STATE() { \
if(onoroff == OFF){ \
ST_RadioSleep(); \
ENERGEST_OFF(ENERGEST_TYPE_LISTEN); \
} \
}
#if RDC_CONF_HARDWARE_CSMA
#define ST_RADIO_CHECK_CCA FALSE
#define ST_RADIO_CCA_ATTEMPT_MAX 0
#define ST_BACKOFF_EXP_MIN 0
#define ST_BACKOFF_EXP_MAX 0
#else
#define ST_RADIO_CHECK_CCA TRUE
#define ST_RADIO_CCA_ATTEMPT_MAX 4
#define ST_BACKOFF_EXP_MIN 2
#define ST_BACKOFF_EXP_MAX 6
#endif
const RadioTransmitConfig radioTransmitConfig = {
TRUE, // waitForAck;
ST_RADIO_CHECK_CCA, // checkCca; // Set to FALSE with low-power MACs.
ST_RADIO_CCA_ATTEMPT_MAX, // ccaAttemptMax;
ST_BACKOFF_EXP_MIN, // backoffExponentMin;
ST_BACKOFF_EXP_MAX, // backoffExponentMax;
TRUE // appendCrc;
};
#define MAC_RETRIES 0
/*
* The buffers which hold incoming data.
*/
#ifndef RADIO_RXBUFS
#define RADIO_RXBUFS 1
#endif
static uint8_t stm32w_rxbufs[RADIO_RXBUFS][STM32W_MAX_PACKET_LEN+1]; // +1 because of the first byte, which will contain the length of the packet.
#if RADIO_RXBUFS > 1
static volatile int8_t first = -1, last=0;
#else
static const int8_t first=0, last=0;
#endif
#if RADIO_RXBUFS > 1
#define CLEAN_RXBUFS() do{first = -1; last = 0;}while(0)
#define RXBUFS_EMPTY() (first == -1)
int RXBUFS_FULL(){
int8_t first_tmp = first;
return first_tmp == last;
}
#else /* RADIO_RXBUFS > 1 */
#define CLEAN_RXBUFS() (stm32w_rxbufs[0][0] = 0)
#define RXBUFS_EMPTY() (stm32w_rxbufs[0][0] == 0)
#define RXBUFS_FULL() (stm32w_rxbufs[0][0] != 0)
#endif /* RADIO_RXBUFS > 1 */
static uint8_t __attribute__(( aligned(2) )) stm32w_txbuf[STM32W_MAX_PACKET_LEN+1];
#define CLEAN_TXBUF() (stm32w_txbuf[0] = 0)
#define TXBUF_EMPTY() (stm32w_txbuf[0] == 0)
#define CHECKSUM_LEN 2
/*
* The transceiver state.
*/
#define ON 0
#define OFF 1
static volatile uint8_t onoroff = OFF;
static uint8_t receiving_packet = 0;
static s8 last_rssi;
static volatile StStatus last_tx_status;
#define BUSYWAIT_UNTIL(cond, max_time) \
do { \
rtimer_clock_t t0; \
t0 = RTIMER_NOW(); \
while(!(cond) && RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + (max_time))); \
} while(0)
static uint8_t locked;
#define GET_LOCK() locked++
static void RELEASE_LOCK(void) {
if(locked>0)
locked--;
}
static volatile uint8_t is_transmit_ack;
/*---------------------------------------------------------------------------*/
PROCESS(stm32w_radio_process, "STM32W radio driver");
/*---------------------------------------------------------------------------*/
static int stm32w_radio_init(void);
static int stm32w_radio_prepare(const void *payload, unsigned short payload_len);
static int stm32w_radio_transmit(unsigned short payload_len);
static int stm32w_radio_send(const void *data, unsigned short len);
static int stm32w_radio_read(void *buf, unsigned short bufsize);
static int stm32w_radio_channel_clear(void);
static int stm32w_radio_receiving_packet(void);
static int stm32w_radio_pending_packet(void);
static int stm32w_radio_on(void);
static int stm32w_radio_off(void);
static int add_to_rxbuf(uint8_t * src);
static int read_from_rxbuf(void * dest, unsigned short len);
const struct radio_driver stm32w_radio_driver =
{
stm32w_radio_init,
stm32w_radio_prepare,
stm32w_radio_transmit,
stm32w_radio_send,
stm32w_radio_read,
stm32w_radio_channel_clear,
stm32w_radio_receiving_packet,
stm32w_radio_pending_packet,
stm32w_radio_on,
stm32w_radio_off,
};
/*---------------------------------------------------------------------------*/
static int stm32w_radio_init(void)
{
// A channel needs also to be setted.
ST_RadioSetChannel(RF_CHANNEL);
// Initialize radio (analog section, digital baseband and MAC).
// Leave radio powered up in non-promiscuous rx mode.
ST_RadioInit(ST_RADIO_POWER_MODE_OFF);
onoroff = OFF;
ST_RadioSetPanId(IEEE802154_PANID);
CLEAN_RXBUFS();
CLEAN_TXBUF();
#if ST_RADIO_AUTOACK && !(UIP_CONF_LL_802154 && RIMEADDR_CONF_SIZE==8)
#error "Autoack and address filtering can only be used with EUI 64"
#endif
ST_RadioEnableAutoAck(ST_RADIO_AUTOACK);
ST_RadioEnableAddressFiltering(ST_RADIO_AUTOACK);
locked = 0;
process_start(&stm32w_radio_process, NULL);
return 0;
}
/*---------------------------------------------------------------------------*/
int stm32w_radio_set_channel(uint8_t channel)
{
if (ST_RadioSetChannel(channel) == ST_SUCCESS)
return 0;
else
return 1;
}
/*---------------------------------------------------------------------------*/
static int wait_for_tx(void){
struct timer t;
timer_set(&t, CLOCK_SECOND/10);
while(!TXBUF_EMPTY()){
if(timer_expired(&t)){
PRINTF("stm32w: tx buffer full.\r\n");
return 1;
}
/* Put CPU in sleep mode. */
halSleepWithOptions(SLEEPMODE_IDLE,0);
}
return 0;
}
/*---------------------------------------------------------------------------*/
static int stm32w_radio_prepare(const void *payload, unsigned short payload_len)
{
if(payload_len > STM32W_MAX_PACKET_LEN){
PRINTF("stm32w: payload length=%d is too long.\r\n", payload_len);
return RADIO_TX_ERR;
}
#if !RADIO_WAIT_FOR_PACKET_SENT
/* Check if the txbuf is empty.
* Wait for a finite time.
* This sould not occur if we wait for the end of transmission in stm32w_radio_transmit().
*/
if(wait_for_tx()){
PRINTF("stm32w: tx buffer full.\r\n");
return RADIO_TX_ERR;
}
#endif /* RADIO_WAIT_FOR_PACKET_SENT */
/* Copy to the txbuf.
* The first byte must be the packet length.
*/
CLEAN_TXBUF();
memcpy(stm32w_txbuf + 1, payload, payload_len);
return RADIO_TX_OK;
}
/*---------------------------------------------------------------------------*/
static int stm32w_radio_transmit(unsigned short payload_len)
{
stm32w_txbuf[0] = payload_len + CHECKSUM_LEN;
INIT_RETRY_CNT();
if(onoroff == OFF){
PRINTF("stm32w: Radio is off, turning it on.\r\n");
ST_RadioWake();
ENERGEST_ON(ENERGEST_TYPE_LISTEN);
}
#if RADIO_WAIT_FOR_PACKET_SENT
GET_LOCK();
#endif /* RADIO_WAIT_FOR_PACKET_SENT */
last_tx_status = -1;
LED_TX_ON();
if(ST_RadioTransmit(stm32w_txbuf)==ST_SUCCESS){
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
ENERGEST_ON(ENERGEST_TYPE_TRANSMIT);
PRINTF("stm32w: sending %d bytes\r\n", payload_len);
#if DEBUG > 1
for(uint8_t c=1; c <= stm32w_txbuf[0]-2; c++){
PRINTF("%x:",stm32w_txbuf[c]);
}
PRINTF("\r\n");
#endif
#if RADIO_WAIT_FOR_PACKET_SENT
if(wait_for_tx()){
PRINTF("stm32w: unknown tx error.\r\n");
TO_PREV_STATE();
LED_TX_OFF();
RELEASE_LOCK();
return RADIO_TX_ERR;
}
TO_PREV_STATE();
if(last_tx_status == ST_SUCCESS || last_tx_status == ST_PHY_ACK_RECEIVED || last_tx_status == ST_MAC_NO_ACK_RECEIVED){
RELEASE_LOCK();
if(last_tx_status == ST_PHY_ACK_RECEIVED){
return RADIO_TX_OK; /* ACK status */
}
else if (last_tx_status == ST_MAC_NO_ACK_RECEIVED || last_tx_status == ST_SUCCESS){
return RADIO_TX_NOACK;
}
}
LED_TX_OFF();
RELEASE_LOCK();
return RADIO_TX_ERR;
#else /* RADIO_WAIT_FOR_PACKET_SENT */
TO_PREV_STATE();
LED_TX_OFF();
return RADIO_TX_OK;
#endif /* RADIO_WAIT_FOR_PACKET_SENT */
}
#if RADIO_WAIT_FOR_PACKET_SENT
RELEASE_LOCK();
#endif /* RADIO_WAIT_FOR_PACKET_SENT */
TO_PREV_STATE();
PRINTF("stm32w: transmission never started.\r\n");
/* TODO: Do we have to retransmit? */
CLEAN_TXBUF();
LED_TX_OFF();
return RADIO_TX_ERR;
}
/*---------------------------------------------------------------------------*/
static int stm32w_radio_send(const void *payload, unsigned short payload_len)
{
if(stm32w_radio_prepare(payload, payload_len) == RADIO_TX_ERR)
return RADIO_TX_ERR;
return stm32w_radio_transmit(payload_len);
}
/*---------------------------------------------------------------------------*/
static int stm32w_radio_channel_clear(void)
{
return ST_RadioChannelIsClear();
}
/*---------------------------------------------------------------------------*/
static int stm32w_radio_receiving_packet(void)
{
return receiving_packet;
}
/*---------------------------------------------------------------------------*/
static int stm32w_radio_pending_packet(void)
{
return !RXBUFS_EMPTY();
}
/*---------------------------------------------------------------------------*/
static int stm32w_radio_off(void)
{
/* Any transmit or receive packets in progress are aborted.
* Waiting for end of transmission or reception have to be done.
*/
if(locked)
{
PRINTF("stm32w: try to off while sending/receiving (lock=%u).\r\n", locked);
return 0;
}
/* off only if there is no transmission or reception of packet. */
if(onoroff == ON && TXBUF_EMPTY() && !receiving_packet){
LED_RDC_OFF();
ST_RadioSleep();
onoroff = OFF;
CLEAN_TXBUF();
CLEAN_RXBUFS();
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
}
return 1;
}
/*---------------------------------------------------------------------------*/
static int stm32w_radio_on(void)
{
PRINTF("stm32w: turn radio on\n");
if(onoroff == OFF){
LED_RDC_ON();
ST_RadioWake();
onoroff = ON;
ENERGEST_ON(ENERGEST_TYPE_LISTEN);
}
return 1;
}
/*---------------------------------------------------------------------------*/
int stm32w_radio_is_on(void)
{
return onoroff == ON;
}
/*---------------------------------------------------------------------------*/
void ST_RadioReceiveIsrCallback(u8 *packet,
boolean ackFramePendingSet,
u32 time,
u16 errors,
s8 rssi)
{
LED_RX_ON();
PRINTF("stm32w: incomming packet received\n");
receiving_packet = 0;
/* Copy packet into the buffer. It is better to do this here. */
if(add_to_rxbuf(packet)){
process_poll(&stm32w_radio_process);
last_rssi = rssi;
}
LED_RX_OFF();
GET_LOCK();
is_transmit_ack = 1;
/* Wait for sending ACK */
BUSYWAIT_UNTIL(!is_transmit_ack, RTIMER_SECOND / 1500);
RELEASE_LOCK();
}
void ST_RadioTxAckIsrCallback (void)
{
/* This callback is for simplemac 1.1.0.
Till now we block (RTIMER_SECOND / 1500)
to prevent radio off during ACK transmission */
is_transmit_ack = 0;
//RELEASE_LOCK();
}
void ST_RadioTransmitCompleteIsrCallback(StStatus status,
u32 txSyncTime,
boolean framePending)
{
ENERGEST_OFF(ENERGEST_TYPE_TRANSMIT);
ENERGEST_ON(ENERGEST_TYPE_LISTEN);
LED_TX_OFF();
last_tx_status = status;
if(status == ST_SUCCESS || status == ST_PHY_ACK_RECEIVED){
CLEAN_TXBUF();
}
else {
if(RETRY_CNT_GTZ()){
// Retransmission
LED_TX_ON();
if(ST_RadioTransmit(stm32w_txbuf)==ST_SUCCESS){
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
ENERGEST_ON(ENERGEST_TYPE_TRANSMIT);
PRINTF("stm32w: retransmission.\r\n");
DEC_RETRY_CNT();
}
else {
CLEAN_TXBUF();
LED_TX_OFF();
PRINTF("stm32w: retransmission failed.\r\n");
}
}
else {
CLEAN_TXBUF();
}
}
/* Debug outputs. */
if(status == ST_SUCCESS || status == ST_PHY_ACK_RECEIVED){
PRINTF("stm32w: return status TX_END\r\n");
}
else if (status == ST_MAC_NO_ACK_RECEIVED){
PRINTF("stm32w: return status TX_END_NOACK\r\n");
}
else if (status == ST_PHY_TX_CCA_FAIL){
PRINTF("stm32w: return status TX_END_CCA_FAIL\r\n");
}
else if(status == ST_PHY_TX_UNDERFLOW){
PRINTF("stm32w: return status TX_END_UNDERFLOW\r\n");
}
else {
PRINTF("stm32w: return status TX_END_INCOMPLETE\r\n");
}
}
boolean ST_RadioDataPendingShortIdIsrCallback(int16u shortId) {
receiving_packet = 1;
return FALSE;
}
boolean ST_RadioDataPendingLongIdIsrCallback(int8u* longId) {
receiving_packet = 1;
return FALSE;
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(stm32w_radio_process, ev, data)
{
int len;
PROCESS_BEGIN();
PRINTF("stm32w_radio_process: started\r\n");
while(1) {
PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL);
PRINTF("stm32w_radio_process: calling receiver callback\r\n");
#if DEBUG > 1
for(uint8_t c=1; c <= RCVD_PACKET_LEN; c++){
PRINTF("%x",stm32w_rxbuf[c]);
}
PRINTF("\r\n");
#endif
packetbuf_clear();
len = stm32w_radio_read(packetbuf_dataptr(), PACKETBUF_SIZE);
if(len > 0) {
packetbuf_set_datalen(len);
NETSTACK_RDC.input();
}
if(!RXBUFS_EMPTY()){
// Some data packet still in rx buffer (this happens because process_poll doesn't queue requests),
// so stm32w_radio_process need to be called again.
process_poll(&stm32w_radio_process);
}
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
static int stm32w_radio_read(void *buf, unsigned short bufsize)
{
return read_from_rxbuf(buf,bufsize);
}
/*---------------------------------------------------------------------------*/
void ST_RadioOverflowIsrCallback(void)
{
PRINTF("stm32w: radio overflow\r\n");
}
/*---------------------------------------------------------------------------*/
void ST_RadioSfdSentIsrCallback(u32 sfdSentTime)
{
}
/*---------------------------------------------------------------------------*/
void ST_RadioMacTimerCompareIsrCallback(void)
{
}
/*---------------------------------------------------------------------------*/
static int add_to_rxbuf(uint8_t * src)
{
if(RXBUFS_FULL()){
return 0;
}
memcpy(stm32w_rxbufs[last], src, src[0] + 1);
#if RADIO_RXBUFS > 1
last = (last + 1) % RADIO_RXBUFS;
if(first == -1){
first = 0;
}
#endif
return 1;
}
/*---------------------------------------------------------------------------*/
static int read_from_rxbuf(void * dest, unsigned short len)
{
if(RXBUFS_EMPTY()){ // Buffers are all empty
return 0;
}
if(stm32w_rxbufs[first][0] > len){ // Too large packet for dest.
len = 0;
}
else {
len = stm32w_rxbufs[first][0];
memcpy(dest,stm32w_rxbufs[first]+1,len);
packetbuf_set_attr(PACKETBUF_ATTR_RSSI, last_rssi);
}
#if RADIO_RXBUFS > 1
ATOMIC(
first = (first + 1) % RADIO_RXBUFS;
int first_tmp = first;
if(first_tmp == last){
CLEAN_RXBUFS();
}
)
#else
CLEAN_RXBUFS();
#endif
return len;
}
/*---------------------------------------------------------------------------*/
short last_packet_rssi(){
return last_rssi;
}