osd-contiki/cpu/cc2430/dev/cc2430_rf.c

685 lines
15 KiB
C

/**
* \file
* CC2430 RF driver
* \author
* Zach Shelby <zach@sensinode.com>
*/
#include <stdio.h>
#include "contiki.h"
#include "dev/radio.h"
#include "dev/cc2430_rf.h"
#include "cc2430_sfr.h"
#include "dev/leds.h"
#include "sys/clock.h"
#include "net/rime/packetbuf.h"
#include "net/rime/rimestats.h"
static void (* receiver_callback)(const struct radio_driver *);
void cc2430_rf_set_receiver(void (* recv)(const struct radio_driver *));
int cc2430_rf_on(void);
int cc2430_rf_off(void);
int cc2430_rf_read(void *buf, unsigned short bufsize);
int cc2430_rf_send(const void *data, unsigned short len);
void cc2430_rf_init(void);
#ifndef RF_DEFAULT_POWER
#define RF_DEFAULT_POWER 100
#endif
#ifndef RF_DEFAULT_CHANNEL
#define RF_DEFAULT_CHANNEL 18
#endif
#ifndef CC2430_CONF_CHECKSUM
#define CC2430_CONF_CHECKSUM 0
#endif /* CC2420_CONF_CHECKSUM */
#if CC2430_CONF_CHECKSUM
#include "lib/crc16.h"
#define CHECKSUM_LEN 2
#else
#define CHECKSUM_LEN 2
#endif /* CC2430_CONF_CHECKSUM */
#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);
#define DEBUG 1
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...) do {} while (0)
#endif
#define RX_ACTIVE 0x80
#define TX_ACK 0x40
#define TX_ON_AIR 0x20
#define RX_NO_DMA
#ifdef HAVE_RF_ERROR
uint8_t rf_error = 0;
#endif
uint8_t rf_initialized = 0;
uint8_t rf_tx_power;
uint8_t rf_flags;
uint8_t rf_channel = 0;
rf_address_mode_t rf_addr_mode;
uint16_t rf_manfid;
uint8_t rf_softack;
uint16_t rf_panid;
/*---------------------------------------------------------------------------*/
PROCESS(cc2430_rf_process, "CC2430 RF driver");
/*---------------------------------------------------------------------------*/
const struct radio_driver cc2430_rf_driver =
{
cc2430_rf_send,
cc2430_rf_read,
cc2430_rf_set_receiver,
cc2430_rf_on,
cc2430_rf_off,
};
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(cc2430_rf_process, ev, data)
{
PROCESS_BEGIN();
while(1) {
PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL);
if(receiver_callback != NULL) {
PRINTF("cc2430_rf_process: calling receiver callback\n");
receiver_callback(&cc2430_rf_driver);
} else {
PRINTF("cc2430_rf_process: no receiver callback\n");
cc2430_rf_command(ISFLUSHRX);
}
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
void
cc2430_rf_init(void)
{
if(rf_initialized) {
return;
}
PRINTF("cc2430_rf_init called\n");
RFPWR &= ~RREG_RADIO_PD; /*make sure it's powered*/
while((RFPWR & ADI_RADIO_PD) == 1);
while((RFIF & IRQ_RREG_ON) == 0); /*wait for power up*/
SLEEP &= ~OSC_PD; /*Osc on*/
while((SLEEP & XOSC_STB) == 0); /*wait for power up*/
rf_flags = 0;
rf_softack = 0;
FSMTC1 = 1; /*don't abort reception, if enable called, accept ack, auto rx after tx*/
MDMCTRL0H = 0x02; /* Generic client, standard hysteresis, decoder on 0x0a */
MDMCTRL0L = 0xE2; /* automatic ACK and CRC, standard CCA and preamble 0xf2 */
MDMCTRL1H = 0x30; /* Defaults */
MDMCTRL1L = 0x0;
RXCTRL0H = 0x32; /* RX tuning optimized */
RXCTRL0L = 0xf5;
/* get ID for MAC */
rf_manfid = CHVER;
rf_manfid <<= 8;
rf_manfid += CHIPID;
cc2430_rf_channel_set(RF_DEFAULT_CHANNEL);
cc2430_rf_command(ISFLUSHTX);
cc2430_rf_command(ISFLUSHRX);
cc2430_rf_set_addr(0xffff, 0x0000, NULL);
cc2430_rf_address_decoder_mode(RF_DECODER_NONE);
RFIM = IRQ_FIFOP;
RFIF &= ~(IRQ_FIFOP);
S1CON &= ~(RFIF_0 | RFIF_1);
IEN2 |= RFIE;
RF_TX_LED_OFF();
RF_RX_LED_OFF();
rf_initialized = 1;
process_start(&cc2430_rf_process, NULL);
}
/*---------------------------------------------------------------------------*/
void
cc2430_rf_set_receiver(void (* recv)(const struct radio_driver *))
{
receiver_callback = recv;
}
/*---------------------------------------------------------------------------*/
int
cc2430_rf_send(const void *payload, unsigned short payload_len)
{
uint8_t i, counter;
if(rf_flags & TX_ACK) {
return -1;
}
if((rf_flags & RX_ACTIVE) == 0) {
cc2430_rf_rx_enable();
}
/* Check packet attributes */
/*printf("packetbuf_attr: txpower = %d\n", packetbuf_attr(PACKETBUF_ATTR_RADIO_TXPOWER));*/
/* Should set TX power according to this if > 0 */
PRINTF("cc2430_rf: sending %d byte payload\n", payload_len);
RIMESTATS_ADD(lltx);
cc2430_rf_command(ISFLUSHTX);
PRINTF("cc2430_rf: sent = ");
/* Send the phy length byte first */
RFD = payload_len+CHECKSUM_LEN; /* Payload plus FCS */
PRINTF("(%d)", payload_len+CHECKSUM_LEN);
for(i = 0 ; i < payload_len; i++) {
RFD = ((unsigned char*)(payload))[i];
PRINTF("%02X", ((unsigned char*)(payload))[i]);
}
PRINTF("\n");
/* Leave space for the FCS */
RFD = 0;
RFD = 0;
if(cc2430_rf_cca_check(0,0) == -1) {
return -1;
}
/* Start the transmission */
RFIF &= ~IRQ_TXDONE;
cc2430_rf_command(ISTXON);
counter = 0;
while(!(RFSTATUS & TX_ACTIVE) && (counter++ < 3)) {
clock_delay(10);
}
if(!(RFSTATUS & TX_ACTIVE)) {
PRINTF("cc2430_rf: TX never active.\n");
cc2430_rf_command(ISFLUSHTX);
return -1;
} else {
RF_RX_LED_OFF();
RF_TX_LED_ON();
// rf_flags |= TX_ON_AIR;
}
return 1;
}
/*---------------------------------------------------------------------------*/
int
cc2430_rf_read(void *buf, unsigned short bufsize)
{
uint8_t i, len;
#if CC2420_CONF_CHECKSUM
uint16_t checksum;
#endif /* CC2420_CONF_CHECKSUM */
/* RX interrupt polled the cc2430_rf_process, now read the RX FIFO */
/* Check the length */
len = RFD;
PRINTF("cc2430_rf: received %d bytes\n", len);
/* Check for validity */
if(len > CC2430_MAX_PACKET_LEN) {
/* Oops, we must be out of sync. */
PRINTF("error: bad sync\n");
cc2430_rf_command(ISFLUSHRX);
RIMESTATS_ADD(badsynch);
return 0;
}
if(len <= CC2430_MIN_PACKET_LEN) {
PRINTF("error: too short\n");
cc2430_rf_command(ISFLUSHRX);
RIMESTATS_ADD(tooshort);
return 0;
}
if(len - CHECKSUM_LEN > bufsize) {
PRINTF("error: too long\n");
cc2430_rf_command(ISFLUSHRX);
RIMESTATS_ADD(toolong);
return 0;
}
/* Read the buffer */
PRINTF("cc2430_rf: read = ");
PRINTF("(%d)", len);
for(i = 0; i < (len - CHECKSUM_LEN); i++) {
((unsigned char*)(buf))[i] = RFD;
PRINTF("%02X", ((unsigned char*)(buf))[i]);
}
PRINTF("\n");
#if CC2430_CONF_CHECKSUM
/* Deal with the checksum */
checksum = RFD * 256;
checksum += RFD;
#endif /* CC2430_CONF_CHECKSUM */
packetbuf_set_attr(PACKETBUF_ATTR_RSSI, ((int8_t) RFD) - 45);
packetbuf_set_attr(PACKETBUF_ATTR_LINK_QUALITY, RFD);
RFIF &= ~IRQ_FIFOP;
RFSTATUS &= ~FIFO;
cc2430_rf_command(ISFLUSHRX);
cc2430_rf_command(ISFLUSHRX);
RF_RX_LED_OFF();
RIMESTATS_ADD(llrx);
return (len - CHECKSUM_LEN);
}
/*---------------------------------------------------------------------------*/
int
cc2430_rf_off(void)
{
return cc2430_rf_rx_disable();
}
/*---------------------------------------------------------------------------*/
int
cc2430_rf_on(void)
{
return cc2430_rf_rx_enable();
}
/*---------------------------------------------------------------------------*/
/**
* Execute a single CSP command.
*
* \param command command to execute
*
*/
void cc2430_rf_command(uint8_t command)
{
if(command >= 0xE0) { /*immediate strobe*/
uint8_t fifo_count;
switch(command) { /*hardware bug workaround*/
case ISRFOFF:
case ISRXON:
case ISTXON:
fifo_count = RXFIFOCNT;
RFST = command;
clock_delay(2);
if(fifo_count != RXFIFOCNT) {
RFST = ISFLUSHRX;
RFST = ISFLUSHRX;
}
break;
default:
RFST = command;
}
} else if(command == SSTART) {
RFIF &= ~IRQ_CSP_STOP; /*clear IRQ flag*/
RFST = SSTOP; /*make sure there is a stop in the end*/
RFST = ISSTART; /*start execution*/
while((RFIF & IRQ_CSP_STOP) == 0);
} else {
RFST = command; /*write command*/
}
}
/*---------------------------------------------------------------------------*/
/**
* Select RF channel.
*
* \param channel channel number to select
*
* \return channel value or negative (invalid channel number)
*/
/* channel freqdiv = (2048 + FSCTRL(9:0)) / 4
freq = (2048 + FSCTRL(9:0)) MHz */
int8_t
cc2430_rf_channel_set(uint8_t channel)
{
uint16_t freq;
if((channel < 11) || (channel > 26)) {
return -1;
}
cc2430_rf_command(ISSTOP); /*make sure CSP is not running*/
cc2430_rf_command(ISRFOFF);
/* Channel values: 11-26 */
freq = (uint16_t) channel - 11;
freq *= 5; /*channel spacing*/
freq += 357; /*correct channel range*/
freq |= 0x4000; /*LOCK_THR = 1*/
FSCTRLH = (freq >> 8);
FSCTRLL = (uint8_t)freq;
cc2430_rf_command(ISRXON);
rf_channel = channel;
return (int8_t) channel;
}
/*---------------------------------------------------------------------------*/
/*PA_LEVEL TXCTRL register Output Power [dBm] Current Consumption [mA]
31 0xA0FF 0 17.4
27 0xA0FB -1 16.5
23 0xA0F7 -3 15.2
19 0xA0F3 -5 13.9
15 0xA0EF -7 12.5
11 0xA0EB -10 11.2
7 0xA0E7 -15 9.9
3 0xA0E3 -25 8.5*/
/**
* Select RF transmit power.
*
* \param new_power new power level (in per cent)
*
* \return new level or negative (value out of range)
*/
int8_t
cc2430_rf_power_set(uint8_t new_power)
{
uint16_t power;
if(new_power > 100) {
return -1;
}
power = 31 * new_power;
power /= 100;
power += 0xA160;
/* Set transmitter power */
TXCTRLH = (power >> 8);
TXCTRLL = (uint8_t)power;
rf_tx_power = (int8_t) new_power;
return rf_tx_power;
}
/*---------------------------------------------------------------------------*/
/**
* Enable RF receiver.
*
*
* \return pdTRUE
* \return pdFALSE bus not free
*/
int8_t
cc2430_rf_rx_enable(void)
{
PRINTF("cc2430_rf_rx_enable called\n");
if(!(rf_flags & RX_ACTIVE)) {
IOCFG0 = 0x7f; // Set the FIFOP threshold 127
RSSIH = 0xd2; /* -84dbm = 0xd2 default, 0xe0 -70 dbm */
rf_flags |= RX_ACTIVE;
RFPWR &= ~RREG_RADIO_PD; /*make sure it's powered*/
while((RFIF & IRQ_RREG_ON) == 0); /*wait for power up*/
SLEEP &= ~OSC_PD; /*Osc on*/
while((SLEEP & XOSC_STB) == 0); /*wait for power up*/
cc2430_rf_command(ISRXON);
cc2430_rf_command(ISFLUSHRX);
}
return 1;
}
/*---------------------------------------------------------------------------*/
/**
* Disable RF receiver.
*
*
* \return pdTRUE
* \return pdFALSE bus not free
*/
int8_t cc2430_rf_rx_disable(void)
{
cc2430_rf_command(ISSTOP); /*make sure CSP is not running*/
cc2430_rf_command(ISRFOFF);
RFPWR |= RREG_RADIO_PD; /*RF powerdown*/
rf_flags = 0;
return 1;
}
/*---------------------------------------------------------------------------*/
/**
* Enable RF transmitter.
*
*
* \return pdTRUE
* \return pdFALSE bus not free
*/
int8_t
cc2430_rf_tx_enable(void)
{
DMAARM = 0x80 + (1 << 0); /*ABORT + channel bit*/
return 1;
}
/**
* Set MAC addresses
*
* \param pan The PAN address to set
* \param adde The short address to set
* \param ieee_addr The 64-bit IEEE address to set
*/
void
cc2430_rf_set_addr(unsigned pan, unsigned addr, const uint8_t *ieee_addr)
{
uint8_t f;
__xdata unsigned char *ptr;
rf_panid = pan;
PANIDH = pan >> 8;
PANIDL = pan & 0xff;
SHORTADDRH = addr >> 8;
SHORTADDRL = addr & 0xff;
if(ieee_addr != NULL) {
ptr = &IEEE_ADDR0;
/* LSB first, MSB last for 802.15.4 addresses in CC2420 */
for (f = 0; f < 8; f++) {
*ptr++ = ieee_addr[f];
}
}
}
/*---------------------------------------------------------------------------*/
/**
* Set address decoder on/off.
*
* \param param 1=on 0=off.
* \return pdTRUE operation successful
*/
int8_t
cc2430_rf_address_decoder_mode(rf_address_mode_t mode)
{
int8_t retval = -1;
rf_softack = 0;
/* set oscillator on*/
switch(mode) {
case RF_SOFTACK_MONITOR:
rf_softack = 1;
case RF_MONITOR:
MDMCTRL0H |= 0x10; /*Address-decode off , coordinator*/
MDMCTRL0L &= ~0x10; /*no automatic ACK */
break;
case RF_DECODER_COORDINATOR:
MDMCTRL0H |= 0x18; /*Address-decode on , coordinator*/
MDMCTRL0L |= 0x10; /*automatic ACK */
break;
case RF_DECODER_ON:
MDMCTRL0H |= 0x08; /*Address-decode on */
MDMCTRL0L &= ~0x10; /* no automatic ACK */
break;
default:
MDMCTRL0H &= ~0x18; /* Generic client */
MDMCTRL0L &= ~0x10; /* no automatic ACK */
break;
}
rf_addr_mode = mode;
retval = 1;
return retval;
}
/*---------------------------------------------------------------------------*/
/**
* Channel energy detect.
*
* Coordinator use this function detect best channel for PAN-network.
* \return RSSI-energy level dBm.
* \return 0 operation failed.
*/
int8_t
cc2430_rf_analyze_rssi(void)
{
int8_t retval = -128;
/*pause_us(128);*/
retval = (int8_t)RSSIL;
retval -= 45;
return retval;
}
/*---------------------------------------------------------------------------*/
/**
* Clear channel assesment check.
*
* \return pdTRUE CCA clear
* \return pdFALSE CCA reserved
*/
int8_t
cc2430_rf_cca_check(uint8_t backoff_count, uint8_t slotted)
{
uint8_t counter, cca = 1;
int8_t retval = 1;
backoff_count;
cc2430_rf_command(ISRXON);
clock_delay(64);
switch(slotted) {
case 1:
if(RFSTATUS & CCA) {
counter = 0;
cca = 1;
while(cca != 0) {
if(counter > 1) {
cca = 0;
}
clock_delay(256);
if(!(RFSTATUS & CCA)) {
cca = 0;
retval = -1;
}
counter++;
}
} else {
retval = -1;
}
break;
case 0:
if(!(RFSTATUS & CCA)) {
retval = -1;
} else {
}
break;
}
return retval;
}
/*---------------------------------------------------------------------------*/
/**
* Send ACK.
*
*\param pending set up pending flag if pending > 0.
*/
void
cc2430_rf_send_ack(uint8_t pending)
{
if(pending) {
cc2430_rf_command(ISACKPEND);
} else {
cc2430_rf_command(ISACK);
}
}
/*---------------------------------------------------------------------------*/
/**
* RF interrupt service routine.
*
*/
void
cc2430_rf_ISR( void ) __interrupt (RF_VECTOR)
{
EA = 0;
if(RFIF & IRQ_TXDONE) {
RF_TX_LED_OFF();
RFIF &= ~IRQ_TXDONE;
cc2430_rf_command(ISFLUSHTX);
}
if(RFIF & IRQ_FIFOP) {
if(RFSTATUS & FIFO) {
RF_RX_LED_ON();
/* Poll the RF process which calls cc2430_rf_read() */
process_poll(&cc2430_rf_process);
} else {
cc2430_rf_command(ISFLUSHRX);
cc2430_rf_command(ISFLUSHRX);
RFIF &= ~IRQ_FIFOP;
}
}
S1CON &= ~(RFIF_0 | RFIF_1);
EA = 1;
}
/*---------------------------------------------------------------------------*/
/**
* RF error interrupt service routine.
*
*/
void
cc2430_rf_error_ISR( void ) __interrupt (RFERR_VECTOR)
{
EA = 0;
TCON_RFERRIF = 0;
#ifdef HAVE_RF_ERROR
rf_error = 254;
#endif
cc2430_rf_command(ISRFOFF);
cc2430_rf_command(ISFLUSHRX);
cc2430_rf_command(ISFLUSHRX);
cc2430_rf_command(ISRXON);
RF_RX_LED_OFF();
RF_TX_LED_OFF();
EA = 1;
}
/*---------------------------------------------------------------------------*/