mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
297 lines
8.2 KiB
C
297 lines
8.2 KiB
C
/**
|
|
* cc2420.c - Implementation of cc2420 functions.
|
|
* Copyright (C) 2013 Milan Babel <babel@inf.fu-berlin.de>
|
|
* Copyright (C) 2014 Kévin Roussel <Kevin.Roussel@inria.fr>
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
#include "crash.h"
|
|
#include "cc2420.h"
|
|
#include "cc2420_spi.h"
|
|
#include "cc2420_settings.h"
|
|
#include "cc2420_arch.h"
|
|
#include "hwtimer.h"
|
|
#include "transceiver.h"
|
|
|
|
#define ENABLE_DEBUG (0)
|
|
#include "debug.h"
|
|
|
|
|
|
/* number of attempts to turn on transceiver at startup, before panicking */
|
|
#define CC2420_STARTUP_ATTEMPTS 3
|
|
|
|
/* startup timeout (2 ms) in 16MHz-clock cycles */
|
|
#define CC2420_STARTUP_TIMEOUT 32000U
|
|
|
|
|
|
/* implementation of driver's functions */
|
|
|
|
void cc2420_initialize(void)
|
|
{
|
|
cc2420_spi_init();
|
|
hwtimer_wait(CC2420_WAIT_TIME);
|
|
cc2420_reset();
|
|
|
|
bool ok = false;
|
|
for (int i = 0; i < CC2420_STARTUP_ATTEMPTS; i++) {
|
|
ok = cc2420_on();
|
|
if (ok) break;
|
|
}
|
|
if (!ok) {
|
|
core_panic(0x2420, "Could not start CC2420 radio transceiver");
|
|
}
|
|
|
|
hwtimer_wait(CC2420_WAIT_TIME);
|
|
|
|
uint16_t reg = cc2420_read_reg(CC2420_REG_MDMCTRL0);
|
|
reg |= CC2420_ADR_DECODE; /* enable auto adress decoding */
|
|
reg |= CC2420_AUTOACK; /* enable auto ACK */
|
|
reg |= CC2420_AUTOCRC; /* enable auto CRC */
|
|
reg &= ~(CC2420_RES_FRM_MODE); /* disable reserved frames */
|
|
cc2420_write_reg(CC2420_REG_MDMCTRL0, reg);
|
|
|
|
/* Change default values as recomended in the data sheet, */
|
|
/* RX bandpass filter = 1.3uA. */
|
|
reg = cc2420_read_reg(CC2420_REG_RXCTRL1);
|
|
reg |= CC2420_RXBPF_LOCUR;
|
|
cc2420_write_reg(CC2420_REG_RXCTRL1, reg);
|
|
|
|
/* Set the FIFOP threshold to maximum. */
|
|
cc2420_write_reg(CC2420_REG_IOCFG0, 127);
|
|
|
|
/* Turn off "Security enable" (page 32). */
|
|
reg = cc2420_read_reg(CC2420_REG_SECCTRL0);
|
|
reg &= ~CC2420_RXFIFO_PROTECTION;
|
|
cc2420_write_reg(CC2420_REG_SECCTRL0, reg);
|
|
|
|
cc2420_set_tx_power(0);
|
|
cc2420_set_channel(CC2420_DEFAULT_CHANNR);
|
|
cc2420_set_pan(0x1111);
|
|
DEBUG("CC2420 initialized and set to channel %i and pan 0x1111\n", CC2420_DEFAULT_CHANNR);
|
|
cc2420_init_interrupts();
|
|
cc2420_switch_to_rx();
|
|
}
|
|
|
|
void cc2420_init(kernel_pid_t tpid)
|
|
{
|
|
transceiver_pid = tpid;
|
|
cc2420_initialize();
|
|
}
|
|
|
|
bool cc2420_on(void)
|
|
{
|
|
/* enable transceiver's crystal oscillator */
|
|
cc2420_strobe(CC2420_STROBE_XOSCON);
|
|
/* wait for the oscillator to be stable */
|
|
unsigned int delay_on = 0;
|
|
do {
|
|
delay_on++;
|
|
if (delay_on >= CC2420_STARTUP_TIMEOUT) {
|
|
/* could not start up radio transceiver! */
|
|
return false;
|
|
}
|
|
} while ((cc2420_status_byte() & CC2420_STATUS_XOSC16M_STABLE) == 0);
|
|
hwtimer_wait(CC2420_WAIT_TIME);
|
|
/* discard any potential garbage in TX buffer */
|
|
cc2420_strobe(CC2420_STROBE_FLUSHTX);
|
|
/* switch to RX mode */
|
|
cc2420_switch_to_rx();
|
|
/* OK, radio is on */
|
|
return true;
|
|
}
|
|
|
|
void cc2420_off(void)
|
|
{
|
|
/* halt any pending communication... */
|
|
cc2420_strobe(CC2420_STROBE_RFOFF);
|
|
/* ... and put the transceiver in power-down mode */
|
|
cc2420_strobe(CC2420_STROBE_XOSCOFF);
|
|
}
|
|
|
|
bool cc2420_is_on(void)
|
|
{
|
|
return ((cc2420_status_byte() & CC2420_STATUS_XOSC16M_STABLE) != 0);
|
|
}
|
|
|
|
void cc2420_switch_to_rx(void)
|
|
{
|
|
cc2420_strobe(CC2420_STROBE_RFOFF);
|
|
cc2420_strobe(CC2420_STROBE_FLUSHRX);
|
|
cc2420_strobe(CC2420_STROBE_FLUSHRX);
|
|
cc2420_strobe(CC2420_STROBE_RXON);
|
|
}
|
|
|
|
void cc2420_rxoverflow_irq(void)
|
|
{
|
|
cc2420_strobe(CC2420_STROBE_FLUSHRX);
|
|
/* CC2420 datasheet says to do this twice... */
|
|
cc2420_strobe(CC2420_STROBE_FLUSHRX);
|
|
}
|
|
|
|
void cc2420_rx_irq(void)
|
|
{
|
|
cc2420_rx_handler();
|
|
}
|
|
|
|
void cc2420_set_monitor(bool mode)
|
|
{
|
|
uint16_t reg = cc2420_read_reg(CC2420_REG_MDMCTRL0);
|
|
if (mode) {
|
|
reg &= ~CC2420_ADR_DECODE;
|
|
} else {
|
|
reg |= CC2420_ADR_DECODE;
|
|
}
|
|
cc2420_write_reg(CC2420_REG_MDMCTRL0, reg);
|
|
}
|
|
|
|
bool cc2420_get_monitor(void)
|
|
{
|
|
uint16_t reg = cc2420_read_reg(CC2420_REG_MDMCTRL0);
|
|
return ((reg & CC2420_ADR_DECODE) == 0);
|
|
}
|
|
|
|
int cc2420_set_channel(unsigned int chan)
|
|
{
|
|
if (chan < 11 || chan > 26) {
|
|
DEBUG("Invalid channel %i set. Valid channels are 11 through 26\n", chan);
|
|
return -1;
|
|
}
|
|
/*
|
|
* calculation from http://www.ti.com/lit/ds/symlink/cc2420.pdf p.50
|
|
*/
|
|
uint16_t freq = 357 + (5 * (chan - 11));
|
|
cc2420_write_reg(CC2420_REG_FSCTRL, freq);
|
|
return ((unsigned int) chan);
|
|
}
|
|
|
|
unsigned int cc2420_get_channel(void)
|
|
{
|
|
/* undo calculation from cc2420_set_channel() */
|
|
return ((cc2420_read_reg(CC2420_REG_FSCTRL) - 357) / 5) + 11;
|
|
}
|
|
|
|
uint16_t cc2420_set_address(uint16_t addr)
|
|
{
|
|
uint8_t buf[2];
|
|
buf[0] = (uint8_t)(addr & 0xFF);
|
|
buf[1] = (uint8_t)(addr >> 8);
|
|
cc2420_write_ram(CC2420_RAM_SHORTADR, buf, 2);
|
|
cc2420_set_address_long(0xFFFF & addr);
|
|
return addr;
|
|
}
|
|
|
|
uint64_t cc2420_set_address_long(uint64_t addr)
|
|
{
|
|
uint8_t buf[8];
|
|
buf[0] = (uint8_t)(addr & 0xFF);
|
|
buf[1] = (uint8_t)(addr >> 8);
|
|
buf[2] = (uint8_t)(addr >> 16);
|
|
buf[3] = (uint8_t)(addr >> 24);
|
|
buf[4] = (uint8_t)(addr >> 32);
|
|
buf[5] = (uint8_t)(addr >> 40);
|
|
buf[6] = (uint8_t)(addr >> 48);
|
|
buf[7] = (uint8_t)(addr >> 56);
|
|
cc2420_write_ram(CC2420_RAM_IEEEADR, buf, 8);
|
|
return addr;
|
|
}
|
|
|
|
uint16_t cc2420_get_address(void)
|
|
{
|
|
uint16_t addr;
|
|
cc2420_read_ram(CC2420_RAM_SHORTADR, (uint8_t *)&addr, sizeof(addr));
|
|
return addr;
|
|
}
|
|
|
|
uint64_t cc2420_get_address_long(void)
|
|
{
|
|
uint64_t addr;
|
|
cc2420_read_ram(CC2420_RAM_IEEEADR, (uint8_t *)&addr, sizeof(addr));
|
|
return addr;
|
|
}
|
|
|
|
uint16_t cc2420_set_pan(uint16_t pan)
|
|
{
|
|
uint8_t buf[2];
|
|
buf[0] = (uint8_t)(pan & 0xFF);
|
|
buf[1] = (uint8_t)(pan >> 8);
|
|
cc2420_write_ram(CC2420_RAM_PANID, buf, 2);
|
|
return pan;
|
|
}
|
|
|
|
uint16_t cc2420_get_pan(void)
|
|
{
|
|
uint16_t pan;
|
|
cc2420_read_ram(CC2420_RAM_PANID, (uint8_t *)&pan, sizeof(pan));
|
|
return pan;
|
|
}
|
|
|
|
static const uint8_t DBM_TO_LEVEL[32] = {
|
|
31, 27, 25, 23, 21, 19, 17, 15, 13, 12, 11, 10, 9, 8, 7, 7,
|
|
6, 6, 5, 5, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3
|
|
};
|
|
|
|
int cc2420_set_tx_power(int pow)
|
|
{
|
|
uint16_t txctrl_reg = cc2420_read_reg(CC2420_REG_TXCTRL);
|
|
/* reset PA_LEVEL bits */
|
|
txctrl_reg &= 0xFFE0;
|
|
/* constrain power in transceiver's acceptable set of values */
|
|
if (pow > 0) pow = 0;
|
|
if (pow < -25) pow = -25;
|
|
/* determine TX level from power in dBm */
|
|
uint8_t level = DBM_TO_LEVEL[-pow];
|
|
/* put wanted value in PA_LEVEL bits, and write back register */
|
|
txctrl_reg |= level;
|
|
cc2420_write_reg(CC2420_REG_TXCTRL, txctrl_reg);
|
|
return pow;
|
|
}
|
|
|
|
static const int LEVEL_TO_DBM[32] = {
|
|
-25, -25, -25, -24, -21, -19, -17, -15, -13, -12, -11, -10, -9, -8, -7, -7,
|
|
-6, -6, -5, -5, -4, -4, -3, -3, -2, -2, -1, -1, 0, 0, 0, 0
|
|
};
|
|
|
|
int cc2420_get_tx_power(void)
|
|
{
|
|
uint16_t txctrl_reg = cc2420_read_reg(CC2420_REG_TXCTRL);
|
|
/* PA_LEVEL is in the 5 least-significant bits of TXCTRL register */
|
|
uint8_t level = txctrl_reg & 0x001F;
|
|
/* determine output power in dBm from TX level */
|
|
int pow = LEVEL_TO_DBM[level];
|
|
return pow;
|
|
}
|
|
|
|
inline bool cc2420_channel_clear(void)
|
|
{
|
|
return (cc2420_get_cca() != 0);
|
|
}
|
|
|
|
/* CC2420 low-level radio driver definition */
|
|
const ieee802154_radio_driver_t cc2420_radio_driver = {
|
|
.init = cc2420_initialize,
|
|
.on = cc2420_on,
|
|
.off = cc2420_off,
|
|
.is_on = cc2420_is_on,
|
|
.load_tx = cc2420_load_tx_buf,
|
|
.transmit = cc2420_transmit_tx_buf,
|
|
.send = cc2420_do_send,
|
|
.set_receive_callback = cc2420_set_recv_callback,
|
|
.switch_to_rx = cc2420_switch_to_rx,
|
|
.set_channel = do_set_channel,
|
|
.get_channel = cc2420_get_channel,
|
|
.set_address = do_set_address,
|
|
.get_address = cc2420_get_address,
|
|
.set_long_address = do_set_long_address,
|
|
.get_long_address = cc2420_get_address_long,
|
|
.set_pan_id = do_set_pan_id,
|
|
.get_pan_id = cc2420_get_pan,
|
|
.set_tx_power = do_set_tx_power,
|
|
.get_tx_power = cc2420_get_tx_power,
|
|
.channel_is_clear = cc2420_channel_clear,
|
|
.set_promiscuous_mode = cc2420_set_monitor,
|
|
.in_promiscuous_mode = cc2420_get_monitor
|
|
};
|