osd-contiki/cpu/cc2430/dev/cc2430_rf.c
zdshelby d98026ae03 - Added RF address setup function (byte order may not be correct)
- Fixed the interrupt and read functions, now receiving one frame correctly
- Improved the send and read functions to work with sicslowmac.c
- TODO: Still a MAC interaction problem. Although the exact same payload is received and sent up to the MAC as is sent, the MAC doesn't do anything with it. Turn on DEBUG in sicslowmac.c to see the problem.
- Removed mtarch files, not used currently
2009-09-11 08:07:12 +00:00

679 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 0
#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);
/* Send */
cc2430_rf_command(ISFLUSHTX);
RFD = payload_len;
PRINTF("cc2430_rf: sent = ");
for(i = 0 ; i < payload_len; i++) {
RFD = ((unsigned char*)(payload))[i];
PRINTF("%02X", ((unsigned char*)(payload))[i]);
}
PRINTF("\n");
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, type;
#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;
len &= 0x7f;
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 = ");
for(i = 1; i < (len + 1 - CHECKSUM_LEN); i++) {
((unsigned char*)(buf))[i] = RFD;
PRINTF("%02X", ((unsigned char*)(buf))[i]);
}
PRINTF("\n");
#if CC2430_CONF_CHECKSUM
/* Deal with the checksum */
#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;
}
/*---------------------------------------------------------------------------*/