contiki/cpu/cc253x/dev/cc2530-rf.c
George Oikonomou e95f94a9a8 Added support for reading MAC from the cc253x flash
We still use the primary location by default (Info Page) but
this is now configurable. This change is useful for users who
wish to specify their own MAC address. Since the Info Page is
read-only, they need to be able to use the secondary location
2012-04-20 16:03:38 +01:00

496 lines
13 KiB
C

/*
* Copyright (c) 2011, George Oikonomou - <oikonomou@users.sourceforge.net>
* 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. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``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 INSTITUTE OR CONTRIBUTORS 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 operating system.
*/
/**
* \file
* Implementation of the cc2530 RF driver
*
* \author
* George Oikonomou - <oikonomou@users.sourceforge.net>
*/
#include "contiki.h"
#include "dev/radio.h"
#include "dev/cc2530-rf.h"
#include "cc253x.h"
#include "sfr-bits.h"
#include "sys/clock.h"
#include "sys/rtimer.h"
#include "net/packetbuf.h"
#include "net/rime/rimestats.h"
#include "net/rime/rimeaddr.h"
#include "net/netstack.h"
#include <string.h>
/*---------------------------------------------------------------------------*/
#define CHECKSUM_LEN 2
/*---------------------------------------------------------------------------*/
#if CC2530_RF_CONF_LEDS
#define CC2530_RF_LEDS CC2530_RF_CONF_LEDS
#else
#define CC2530_RF_LEDS 0
#endif
#if CC2530_RF_LEDS
#include "dev/leds.h"
#define RF_RX_LED_ON() leds_on(LEDS_RED);
#define RF_RX_LED_OFF() leds_off(LEDS_RED);
#define RF_TX_LED_ON() leds_on(LEDS_GREEN);
#define RF_TX_LED_OFF() leds_off(LEDS_GREEN);
#else
#define RF_RX_LED_ON()
#define RF_RX_LED_OFF()
#define RF_TX_LED_ON()
#define RF_TX_LED_OFF()
#endif
/*---------------------------------------------------------------------------*/
#define DEBUG 0
#if DEBUG
#include "debug.h"
#define PUTSTRING(...) putstring(__VA_ARGS__)
#define PUTHEX(...) puthex(__VA_ARGS__)
#else
#define PUTSTRING(...)
#define PUTHEX(...)
#endif
/*---------------------------------------------------------------------------*/
/* Local RF Flags */
#define RX_ACTIVE 0x80
#define WAS_OFF 0x10
#define RF_ON 0x01
/* Bit Masks for the last byte in the RX FIFO */
#define CRC_BIT_MASK 0x80
#define LQI_BIT_MASK 0x7F
/* 192 ms, radio off -> on interval */
#define ONOFF_TIME RTIMER_ARCH_SECOND / 3125
/*---------------------------------------------------------------------------*/
#if CC2530_RF_CONF_HEXDUMP
#include "uart0.h"
static const uint8_t magic[] = { 0x53, 0x6E, 0x69, 0x66 }; /* Snif */
#endif
/*---------------------------------------------------------------------------*/
#ifdef CC2530_RF_CONF_AUTOACK
#define CC2530_RF_AUTOACK CC2530_RF_CONF_AUTOACK
#else
#define CC2530_RF_AUTOACK 1
#endif
/*---------------------------------------------------------------------------*/
static uint8_t __data rf_flags;
static int on(void); /* prepare() needs our prototype */
static int off(void); /* transmit() needs our prototype */
static int channel_clear(void); /* transmit() needs our prototype */
/*---------------------------------------------------------------------------*/
int8_t
cc2530_rf_channel_set(uint8_t channel)
{
PUTSTRING("RF: Set Chan\n");
if((channel < CC2530_RF_CHANNEL_MIN) || (channel > CC2530_RF_CHANNEL_MAX)) {
return -1;
}
/* Changes to FREQCTRL take effect after the next recalibration */
off();
FREQCTRL = (CC2530_RF_CHANNEL_MIN
+ (channel - CC2530_RF_CHANNEL_MIN) * CC2530_RF_CHANNEL_SPACING);
on();
return (int8_t) channel;
}
/*---------------------------------------------------------------------------*/
uint8_t
cc2530_rf_power_set(uint8_t new_power)
{
PUTSTRING("RF: Set Power\n");
/* off() */
TXPOWER = new_power;
/* on() */
return TXPOWER;
}
/*---------------------------------------------------------------------------*/
void
cc2530_rf_set_addr(uint16_t pan)
{
#if RIMEADDR_SIZE==8 /* EXT_ADDR[7:0] is ignored when using short addresses */
int i;
for(i = (RIMEADDR_SIZE - 1); i >= 0; --i) {
((uint8_t *)&EXT_ADDR0)[i] = rimeaddr_node_addr.u8[RIMEADDR_SIZE - 1 - i];
}
#endif
PAN_ID0 = pan & 0xFF;
PAN_ID1 = pan >> 8;
SHORT_ADDR0 = rimeaddr_node_addr.u8[RIMEADDR_SIZE - 1];
SHORT_ADDR1 = rimeaddr_node_addr.u8[RIMEADDR_SIZE - 2];
}
/*---------------------------------------------------------------------------*/
/* Netstack API radio driver functions */
/*---------------------------------------------------------------------------*/
static int
init(void)
{
PUTSTRING("RF: Init\n");
if(rf_flags & RF_ON) {
return 0;
}
#ifdef CC2530_RF_LOW_POWER_RX
/* Reduce RX power consumption current to 20mA at the cost of sensitivity */
RXCTRL = 0x00;
FSCTRL = 0x50;
#else
RXCTRL = 0x3F;
FSCTRL = 0x55;
#endif /* CC2530_RF_LOW_POWER_RX */
CCACTRL0 = CC2530_RF_CCA_THRES;
/*
* According to the user guide, these registers must be updated from their
* defaults for optimal performance
*
* Table 23-6, Sec. 23.15.1, p. 259
*/
TXFILTCFG = 0x09; /* TX anti-aliasing filter */
AGCCTRL1 = 0x15; /* AGC target value */
FSCAL1 = 0x00; /* Reduce the VCO leakage */
/* Auto ACKs and CRC calculation, default RX and TX modes with FIFOs */
FRMCTRL0 = FRMCTRL0_AUTOCRC;
#if CC2530_RF_AUTOACK
FRMCTRL0 |= FRMCTRL0_AUTOACK;
#endif
/* Disable source address matching and autopend */
SRCMATCH = 0; /* investigate */
/* MAX FIFOP threshold */
FIFOPCTRL = CC2530_RF_MAX_PACKET_LEN;
cc2530_rf_power_set(CC2530_RF_TX_POWER);
cc2530_rf_channel_set(CC2530_RF_CHANNEL);
RF_TX_LED_OFF();
RF_RX_LED_OFF();
rf_flags |= RF_ON;
return 1;
}
/*---------------------------------------------------------------------------*/
static int
prepare(const void *payload, unsigned short payload_len)
{
uint8_t i;
PUTSTRING("RF: Prepare 0x");
PUTHEX(payload_len + CHECKSUM_LEN);
PUTSTRING(" bytes\n");
/*
* When we transmit in very quick bursts, make sure previous transmission
* is not still in progress before re-writing to the TX FIFO
*/
while(FSMSTAT1 & FSMSTAT1_TX_ACTIVE);
if((rf_flags & RX_ACTIVE) == 0) {
on();
}
CC2530_CSP_ISFLUSHTX();
PUTSTRING("RF: data = ");
/* Send the phy length byte first */
RFD = payload_len + CHECKSUM_LEN; /* Payload plus FCS */
for(i = 0; i < payload_len; i++) {
RFD = ((unsigned char*) (payload))[i];
PUTHEX(((unsigned char*)(payload))[i]);
}
PUTSTRING("\n");
/* Leave space for the FCS */
RFD = 0;
RFD = 0;
return 0;
}
/*---------------------------------------------------------------------------*/
static int
transmit(unsigned short transmit_len)
{
uint8_t counter;
int ret = RADIO_TX_ERR;
rtimer_clock_t t0;
transmit_len; /* hush the warning */
if(!(rf_flags & RX_ACTIVE)) {
t0 = RTIMER_NOW();
on();
rf_flags |= WAS_OFF;
while (RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + ONOFF_TIME));
}
if(channel_clear() == CC2530_RF_CCA_BUSY) {
RIMESTATS_ADD(contentiondrop);
return RADIO_TX_COLLISION;
}
/*
* prepare() double checked that TX_ACTIVE is low. If SFD is high we are
* receiving. Abort transmission and bail out with RADIO_TX_COLLISION
*/
if(FSMSTAT1 & FSMSTAT1_SFD) {
RIMESTATS_ADD(contentiondrop);
return RADIO_TX_COLLISION;
}
/* Start the transmission */
RF_TX_LED_ON();
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
ENERGEST_ON(ENERGEST_TYPE_TRANSMIT);
CC2530_CSP_ISTXON();
counter = 0;
while(!(FSMSTAT1 & FSMSTAT1_TX_ACTIVE) && (counter++ < 3)) {
clock_delay(10);
}
if(!(FSMSTAT1 & FSMSTAT1_TX_ACTIVE)) {
PUTSTRING("RF: TX never active.\n");
CC2530_CSP_ISFLUSHTX();
ret = RADIO_TX_ERR;
} else {
/* Wait for the transmission to finish */
while(FSMSTAT1 & FSMSTAT1_TX_ACTIVE);
ret = RADIO_TX_OK;
}
ENERGEST_OFF(ENERGEST_TYPE_TRANSMIT);
ENERGEST_ON(ENERGEST_TYPE_LISTEN);
if(rf_flags & WAS_OFF){
off();
}
RIMESTATS_ADD(lltx);
RF_TX_LED_OFF();
/* OK, sent. We are now ready to send more */
return ret;
}
/*---------------------------------------------------------------------------*/
static int
send(void *payload, unsigned short payload_len)
{
prepare(payload, payload_len);
return transmit(payload_len);
}
/*---------------------------------------------------------------------------*/
static int
read(void *buf, unsigned short bufsize)
{
uint8_t i;
uint8_t len;
uint8_t crc_corr;
int8_t rssi;
PUTSTRING("RF: Read\n");
/* Check the length */
len = RFD;
/* Check for validity */
if(len > CC2530_RF_MAX_PACKET_LEN) {
/* Oops, we must be out of sync. */
PUTSTRING("RF: bad sync\n");
RIMESTATS_ADD(badsynch);
CC2530_CSP_ISFLUSHRX();
return 0;
}
if(len <= CC2530_RF_MIN_PACKET_LEN) {
PUTSTRING("RF: too short\n");
RIMESTATS_ADD(tooshort);
CC2530_CSP_ISFLUSHRX();
return 0;
}
if(len - CHECKSUM_LEN > bufsize) {
PUTSTRING("RF: too long\n");
RIMESTATS_ADD(toolong);
CC2530_CSP_ISFLUSHRX();
return 0;
}
#if CC2530_RF_CONF_HEXDUMP
/* If we reach here, chances are the FIFO is holding a valid frame */
uart0_writeb(magic[0]);
uart0_writeb(magic[1]);
uart0_writeb(magic[2]);
uart0_writeb(magic[3]);
uart0_writeb(len);
#endif
RF_RX_LED_ON();
PUTSTRING("RF: read (0x");
PUTHEX(len);
PUTSTRING(" bytes) = ");
len -= CHECKSUM_LEN;
for(i = 0; i < len; ++i) {
((unsigned char*)(buf))[i] = RFD;
#if CC2530_RF_CONF_HEXDUMP
uart0_writeb(((unsigned char*)(buf))[i]);
#endif
PUTHEX(((unsigned char*)(buf))[i]);
}
PUTSTRING("\n");
/* Read the RSSI and CRC/Corr bytes */
rssi = ((int8_t) RFD) - 45;
crc_corr = RFD;
#if CC2530_RF_CONF_HEXDUMP
uart0_writeb(rssi);
uart0_writeb(crc_corr);
#endif
/* MS bit CRC OK/Not OK, 7 LS Bits, Correlation value */
if(crc_corr & CRC_BIT_MASK) {
packetbuf_set_attr(PACKETBUF_ATTR_RSSI, rssi);
packetbuf_set_attr(PACKETBUF_ATTR_LINK_QUALITY, crc_corr & LQI_BIT_MASK);
RIMESTATS_ADD(llrx);
} else {
RIMESTATS_ADD(badcrc);
CC2530_CSP_ISFLUSHRX();
RF_RX_LED_OFF();
return 0;
}
/* If FIFOP==1 and FIFO==0 then we had a FIFO overflow at some point. */
if((FSMSTAT1 & (FSMSTAT1_FIFO | FSMSTAT1_FIFOP)) == FSMSTAT1_FIFOP) {
/*
* If we reach here means that there might be more intact packets in the
* FIFO despite the overflow. This can happen with bursts of small packets.
*
* Only flush if the FIFO is actually empty. If not, then next pass we will
* pick up one more packet or flush due to an error.
*/
if(!RXFIFOCNT) {
CC2530_CSP_ISFLUSHRX();
}
}
RF_RX_LED_OFF();
return (len);
}
/*---------------------------------------------------------------------------*/
static int
channel_clear(void)
{
if(FSMSTAT1 & FSMSTAT1_CCA) {
return CC2530_RF_CCA_CLEAR;
}
return CC2530_RF_CCA_BUSY;
}
/*---------------------------------------------------------------------------*/
static int
receiving_packet(void)
{
PUTSTRING("RF: Receiving\n");
/*
* SFD high while transmitting and receiving.
* TX_ACTIVE high only when transmitting
*
* FSMSTAT1 & (TX_ACTIVE | SFD) == SFD <=> receiving
*/
return (FSMSTAT1 & (FSMSTAT1_TX_ACTIVE | FSMSTAT1_SFD) == FSMSTAT1_SFD);
}
/*---------------------------------------------------------------------------*/
static int
pending_packet(void)
{
return (FSMSTAT1 & FSMSTAT1_FIFOP);
}
/*---------------------------------------------------------------------------*/
static int
on(void)
{
if(!(rf_flags & RX_ACTIVE)) {
CC2530_CSP_ISFLUSHRX();
CC2530_CSP_ISRXON();
rf_flags |= RX_ACTIVE;
}
ENERGEST_ON(ENERGEST_TYPE_LISTEN);
return 1;
}
/*---------------------------------------------------------------------------*/
static int
off(void)
{
CC2530_CSP_ISRFOFF();
CC2530_CSP_ISFLUSHRX();
rf_flags = 0;
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
return 1;
}
/*---------------------------------------------------------------------------*/
const struct radio_driver cc2530_rf_driver =
{
init,
prepare,
transmit,
send,
read,
channel_clear,
receiving_packet,
pending_packet,
on,
off,
};
/*---------------------------------------------------------------------------*/