1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/cc110x/cc1100.c

906 lines
24 KiB
C

/******************************************************************************
Copyright 2008, Freie Universitaet Berlin (FUB). All rights reserved.
These sources were developed at the Freie Universitaet Berlin, Computer Systems
and Telematics group (http://cst.mi.fu-berlin.de).
-------------------------------------------------------------------------------
This file is part of FeuerWare.
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
FeuerWare is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see http://www.gnu.org/licenses/ .
--------------------------------------------------------------------------------
For further information and questions please use the web site
http://scatterweb.mi.fu-berlin.de
and the mailinglist (subscription via web site)
scatterweb@lists.spline.inf.fu-berlin.de
*******************************************************************************/
/**
* @ingroup dev_cc110x
* @{
*/
/**
* @file
* @internal
* @brief TI Chipcon CC110x Radio driver
*
* @author Freie Universität Berlin, Computer Systems & Telematics, FeuerWhere project
* @author Thomas Hillebrandt <hillebra@inf.fu-berlin.de>
* @author Heiko Will <hwill@inf.fu-berlin.de>
* @version $Revision: 2283 $
*
* @note $Id: cc1100.c 2283 2010-06-15 14:02:27Z hillebra $
*/
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "irq.h"
#include "arch_cc1100.h"
#include "cc1100.h"
#include "cc1100_phy.h"
#include "cc1100_spi.h"
#include "cc1100-internal.h"
#include "cc1100-defaultSettings.h"
#include "hwtimer.h"
#include "core/include/bitarithm.h"
// TODO: cc1100 port timer
#ifdef FEUERWARE_CPU_LPC2387
//#include "cpu/lpc2387/lpc2387-timer2.h"
#endif
#if defined(FEUERWARE_CPU_MSP430)
#include <msp430x16x.h>
#elif defined(FEUERWARE_CPU_LPC2387)
#include "lpc2387.h"
#endif
#define PACKET_LENGTH (0x3E) ///< Packet length = 62 Bytes.
#define CC1100_SYNC_WORD_TX_TIME (90000) // loop count (max. timeout ~ 15 ms) to wait for
// sync word to be transmitted (GDO2 from low to high)
/**
* @name Virtual Radio Device methods (see vdevice_radio_methods)
* @{
*/
static int rd_set_mode(int mode);
/** @} */
static void switch_to_wor(void);
/*---------------------------------------------------------------------------*/
// Power control data structures
/*---------------------------------------------------------------------------*/
static uint8_t pa_table_index = PATABLE; ///< Current PATABLE Index
static uint8_t pa_table[] = { ///< PATABLE with available output powers
0x00, ///< -52 dBm
0x03, ///< -30 dBm
0x0D, ///< -20 dBm
0x1C, ///< -15 dBm
0x34, ///< -10 dBm
0x57, ///< - 5 dBm
0x3F, ///< - 1 dBm
0x8E, ///< 0 dBm
0x85, ///< + 5 dBm
0xCC, ///< + 7 dBm
0xC6, ///< + 9 dBm
0xC3 ///< +10 dBm
}; // If PATABLE is changed in size, adjust MAX_OUTPUT_POWER definition in CC1100 interface!
static int8_t pa_table_dBm[] = { ///< Values of the PATABLE in dBm
-52,
-30,
-20,
-15,
-10,
-5,
-1,
0,
5,
7,
9,
10
};
/*---------------------------------------------------------------------------*/
// Main radio data structures
/*---------------------------------------------------------------------------*/
volatile cc1100_flags rflags; ///< Radio control flags
static uint8_t radio_address; ///< Radio address
static uint8_t radio_channel; ///< Radio channel number
const radio_t radio_cc1100 = { ///< Radio driver API
"CC1100",
CC1100_BROADCAST_ADDRESS,
MAX_OUTPUT_POWER,
cc1100_get_avg_transmission_duration,
cc1100_get_address,
cc1100_set_address,
cc1100_set_output_power,
cc1100_set_packet_monitor,
cc1100_set_packet_handler,
cc1100_send_csmaca,
cc1100_print_statistic,
cc1100_print_config
};
/*---------------------------------------------------------------------------*/
// Data structures for mode control
/*---------------------------------------------------------------------------*/
volatile uint8_t radio_mode; ///< Radio mode
volatile uint8_t radio_state = RADIO_UNKNOWN; ///< Radio state
volatile cc1100_mode_callback_t cc1100_go_idle; ///< Function for going IDLE
volatile cc1100_mode_callback_t cc1100_go_receive; ///< Function for going RX
volatile cc1100_mode_callback_t cc1100_go_after_tx; ///< Function to call after TX (burst send)
volatile cc1100_mode_callback_t cc1100_setup_mode; ///< Function to set up selected mode (RX or WOR)
volatile int wor_hwtimer_id = -1;
/*---------------------------------------------------------------------------*/
// Low-level hardware access
/*---------------------------------------------------------------------------*/
void cc1100_disable_interrupts(void)
{
cc1100_gdo2_disable();
cc1100_gdo0_disable();
}
void cc1100_gdo0_irq(void)
{
// Air was not free -> Clear CCA flag
rflags.CAA = false;
// Disable carrier sense detection (GDO0 interrupt)
cc1100_gdo0_disable();
}
void cc1100_gdo2_irq(void)
{
cc1100_phy_rx_handler();
}
/*---------------------------------------------------------------------------*/
// High level CC1100 SPI functions for transferring packet out
// of RX FIFO (don't call when in WOR mode)
/*---------------------------------------------------------------------------*/
static bool spi_receive_packet_variable(uint8_t *rxBuffer, uint8_t length)
{
// Needed here for statistics
extern cc1100_statistic_t cc1100_statistic;
uint8_t status[2];
uint8_t packetLength = 0;
// Any bytes available in RX FIFO?
if ((cc1100_spi_read_status(CC1100_RXBYTES) & BYTES_IN_RXFIFO))
{
// Read length byte (first byte in RX FIFO)
packetLength = cc1100_spi_read_reg(CC1100_RXFIFO);
// Read data from RX FIFO and store in rxBuffer
if (packetLength <= length)
{
// Put length byte at first position in RX Buffer
rxBuffer[0] = packetLength;
// Read the rest of the packet
cc1100_spi_readburst_reg(CC1100_RXFIFO, (char*)rxBuffer+1, packetLength);
// Read the 2 appended status bytes (status[0] = RSSI, status[1] = LQI)
cc1100_spi_readburst_reg(CC1100_RXFIFO, (char*)status, 2);
// Store RSSI value of packet
rflags.RSSI = status[I_RSSI];
// MSB of LQI is the CRC_OK bit
rflags.CRC = (status[I_LQI] & CRC_OK) >> 7;
if (!rflags.CRC) cc1100_statistic.packets_in_crc_fail++;
// Bit 0-6 of LQI indicates the link quality (LQI)
rflags.LQI = status[I_LQI] & LQI_EST;
return rflags.CRC;
}
else
{
// RX FIFO get automatically flushed if return value is false
return false;
}
}
else
{
// RX FIFO get automatically flushed if return value is false
return false;
}
}
bool cc1100_spi_receive_packet(uint8_t *rxBuffer, uint8_t length)
{
uint8_t pkt_len_cfg = cc1100_spi_read_reg(CC1100_PKTCTRL0) & PKT_LENGTH_CONFIG;
if (pkt_len_cfg == VARIABLE_PKTLEN)
{
return spi_receive_packet_variable(rxBuffer, length);
}
// Fixed packet length not supported.
// RX FIFO get automatically flushed if return value is false
return false;
}
/*---------------------------------------------------------------------------*/
// CC1100 mode functionality
/*---------------------------------------------------------------------------*/
void cc1100_set_idle(void) {
if (radio_state == RADIO_WOR) {
// Wake up the chip from WOR/sleep
cc1100_spi_select();
hwtimer_wait(RTIMER_TICKS(122));
cc1100_spi_unselect();
radio_state = RADIO_IDLE;
// XOSC startup + FS calibration (300 + 809 us ~ 1.38 ms)
hwtimer_wait(FS_CAL_TIME);
return;
}
cc1100_spi_strobe(CC1100_SIDLE);
radio_state = RADIO_IDLE;
}
static void wakeup_from_rx(void)
{
if (radio_state != RADIO_RX) return;
cc1100_spi_strobe(CC1100_SIDLE);
radio_state = RADIO_IDLE;
}
static void switch_to_rx(void)
{
radio_state = RADIO_RX;
cc1100_spi_strobe(CC1100_SRX);
}
static void setup_rx_mode(void)
{
// Stay in RX mode until end of packet
cc1100_spi_write_reg(CC1100_MCSM2, 0x07);
switch_to_rx();
}
/**
* Note: It is possible that this code is executed in an ISR!
*/
static void wakeup_from_wor(void)
{
if (radio_state != RADIO_WOR) {
return;
}
// Wake up the chip from WOR/sleep
cc1100_spi_select();
hwtimer_wait(RTIMER_TICKS(122));
cc1100_spi_unselect();
radio_state = RADIO_IDLE;
// XOSC startup + FS calibration (300 + 809 us ~ 1.38 ms)
hwtimer_wait(FS_CAL_TIME);
}
/**
* Note: This code is executed in the hwtimer ISR!
*/
void switch_to_wor2(void)
{
if (cc1100_get_gdo2()) return; // If incoming packet, then don't go to WOR now
cc1100_spi_strobe(CC1100_SIDLE); // Put CC1100 to IDLE
radio_state = RADIO_IDLE; // Radio state now IDLE
cc1100_spi_write_reg(CC1100_MCSM2,
cc1100_wor_config.rx_time_reg); // Configure RX_TIME (for use in WOR)
cc1100_spi_write_reg(CC1100_MCSM0, 0x18); // Turn on FS-Autocal
if (rflags.WOR_RST) {
cc1100_spi_strobe(CC1100_SWORRST); // Resets the real time clock
rflags.WOR_RST = false;
}
cc1100_spi_strobe(CC1100_SWOR); // Put radio back to sleep/WOR (must be in IDLE when this is done)
radio_state = RADIO_WOR; // Radio state now WOR
}
/**
* Note: This code is executed in the hwtimer ISR!
*/
static void hwtimer_switch_to_wor2_wrapper(void* ptr)
{
wor_hwtimer_id = -1; // kernel timer handler function called, clear timer id
if (rflags.TX) return; // Stability: don't allow WOR timers at this point
rflags.WOR_RST = true;
switch_to_wor2();
}
/**
* Note: This code is executed in the hwtimer ISR!
*/
static void switch_to_wor(void)
{
// Any incoming packet?
if (cc1100_get_gdo2())
{
// Then don't go to WOR now
return;
}
// Step 1: Set chip for random interval (1..RX_INTERVAL) to power down mode
if (!rflags.MAN_WOR)
{
rflags.MAN_WOR = true;
radio_state = RADIO_WOR;
// Go to power down mode
cc1100_spi_strobe(CC1100_SIDLE);
cc1100_spi_strobe(CC1100_SPWD);
// Set timer to do second step of manual WOR
int r = (rand() / (double)(RAND_MAX + 1.0)) * (cc1100_wor_config.rx_interval * 100.0) + 20;
wor_hwtimer_id = hwtimer_set(r, cc1100_hwtimer_go_receive_wrapper, NULL);
if (wor_hwtimer_id == -1)
{
rflags.KT_RES_ERR = true;
// No hwtimer available, go immediately to WOR mode.
// Else never receiving packets again...
rflags.MAN_WOR = false;
switch_to_wor2();
}
}
// Step 2: Go to RX and then to WOR mode again
else
{
rflags.MAN_WOR = false;
wakeup_from_wor();
cc1100_spi_strobe(CC1100_SRX);
hwtimer_wait(IDLE_TO_RX_TIME);
radio_state = RADIO_RX;
// Register timer to go to WOR after RX timeout
wor_hwtimer_id = hwtimer_set((cc1100_wor_config.rx_time_ms * 100 + 150),
hwtimer_switch_to_wor2_wrapper, NULL); // add 1,5 ms secure time
if (wor_hwtimer_id == -1)
{
rflags.KT_RES_ERR = true;
}
}
}
static void setup_wor_mode(void)
{
// Wake up from WOR (if in WOR, else no effect)
cc1100_go_idle();
// Make sure CC1100 is in IDLE state
cc1100_spi_strobe(CC1100_SIDLE);
// Enable automatic initial calibration of RCosc.
// Set T_event1 ~ 1.4 ms, enough for XOSC stabilize and FS calibration before RX.
// Enable RC oscillator before starting with WOR (or else it will not wake up).
// Not using AUTO_SYNC function.
cc1100_spi_write_reg(CC1100_WORCTRL, cc1100_wor_config.wor_ctrl);
// Set Event0 timeout (RX polling interval)
cc1100_spi_write_reg(CC1100_WOREVT1, cc1100_wor_config.wor_evt_1);
cc1100_spi_write_reg(CC1100_WOREVT0, cc1100_wor_config.wor_evt_0);
// Set RX time in WOR mode
cc1100_spi_write_reg(CC1100_MCSM2, cc1100_wor_config.rx_time_reg);
// Enable automatic FS calibration when going from IDLE to RX/TX/FSTXON (in between EVENT0 and EVENT1)
cc1100_spi_write_reg(CC1100_MCSM0, 0x18);
// Put the radio to SLEEP by starting Wake-on-Radio.
cc1100_spi_strobe(CC1100_SWORRST); // Resets the real time clock
cc1100_spi_strobe(CC1100_SWOR); // Starts Wake-on-Radio
radio_state = RADIO_WOR;
}
static void switch_to_pwd(void)
{
cc1100_go_idle();
cc1100_spi_strobe(CC1100_SPWD);
radio_state = RADIO_PWD;
}
uint8_t cc1100_get_mode(void)
{
return radio_mode;
}
static bool cc1100_set_mode0(uint8_t mode, uint16_t opt_mode_data)
{
int result;
switch (mode)
{
case CC1100_MODE_WOR:
// Calculate WOR settings, store result (new burst count)
result = cc1100_phy_calc_wor_settings(opt_mode_data);
// If settings can be applied, set new mode and burst count
if (result != -1)
{
radio_mode = mode;
cc1100_go_idle = wakeup_from_wor;
cc1100_go_receive = switch_to_wor;
cc1100_go_after_tx = switch_to_wor2;
cc1100_setup_mode = setup_wor_mode;
cc1100_burst_count = result;
cc1100_retransmission_count_uc = TRANSMISSION_RETRIES_WOR_UC;
cc1100_retransmission_count_bc = TRANSMISSION_RETRIES_WOR_BC;
return true;
}
break;
case CC1100_MODE_CONSTANT_RX:
radio_mode = mode;
cc1100_go_idle = wakeup_from_rx;
cc1100_go_receive = switch_to_rx;
cc1100_go_after_tx = switch_to_rx;
cc1100_setup_mode = setup_rx_mode;
cc1100_burst_count = 1;
cc1100_retransmission_count_uc = TRANSMISSION_RETRIES_CRX_UC;
cc1100_retransmission_count_bc = TRANSMISSION_RETRIES_CRX_BC;
return true;
}
return false;
}
bool cc1100_set_mode(uint8_t mode, uint16_t opt_mode_data)
{
// Wake up from WOR/RX (if in WOR/RX, else no effect)
cc1100_go_idle();
// Make sure CC1100 is in IDLE state
cc1100_spi_strobe(CC1100_SIDLE);
// Set the new mode
bool result = cc1100_set_mode0(mode, opt_mode_data);
// If mode change was successful (mode is valid)
if (result)
{
// Setup new mode configuration
cc1100_setup_mode();
// Reset statistics
cc1100_reset_statistic();
return true;
}
else
{
// Still in old mode, go to receive mode again
cc1100_go_receive();
return false;
}
}
char* cc1100_mode_to_text(uint8_t mode)
{
switch (mode)
{
case CC1100_MODE_WOR:
return "Wake-On-Radio";
case CC1100_MODE_CONSTANT_RX:
return "Constant RX";
default:
return "unknown";
}
}
char* cc1100_state_to_text(uint8_t state)
{
switch (state)
{
case RADIO_UNKNOWN:
return "Unknown";
case RADIO_AIR_FREE_WAITING:
return "CS";
case RADIO_WOR:
return "WOR";
case RADIO_IDLE:
return "IDLE";
case RADIO_SEND_BURST:
return "TX BURST";
case RADIO_RX:
return "RX";
case RADIO_SEND_ACK:
return "TX ACK";
case RADIO_PWD:
return "PWD";
default:
return "unknown";
}
}
void cc1100_hwtimer_go_receive_wrapper(void *ptr)
{
// kernel timer handler function called, clear timer id
wor_hwtimer_id = -1;
// Stability: don't allow WOR timers at this point
if (rflags.TX) return;
if (radio_state == RADIO_PWD) {
// Go to RX state, listen for packets as long as WOR_TIMEOUT_2
cc1100_spi_strobe(CC1100_SRX);
hwtimer_wait(IDLE_TO_RX_TIME);
radio_state = RADIO_RX;
// Set hwtimer to put CC1100 back to WOR after WOR_TIMEOUT_2
wor_hwtimer_id = hwtimer_set(WOR_TIMEOUT_2, cc1100_hwtimer_go_receive_wrapper, NULL);
if (wor_hwtimer_id == -1)
{
rflags.KT_RES_ERR = true;
// No hwtimer available, go immediately to WOR mode.
// Else never receiving packets again...
rflags.MAN_WOR = false;
switch_to_wor2();
}
} else {
cc1100_go_receive();
}
}
/*---------------------------------------------------------------------------*/
// CC1100 reset functionality
/*---------------------------------------------------------------------------*/
static void reset(void)
{
cc1100_go_idle();
cc1100_spi_select();
cc1100_spi_strobe(CC1100_SRES);
hwtimer_wait(RTIMER_TICKS(10));
}
static void power_up_reset(void)
{
cc1100_spi_unselect();
cc1100_spi_cs();
cc1100_spi_unselect();
hwtimer_wait(RESET_WAIT_TIME);
reset();
radio_state = RADIO_IDLE;
}
/*---------------------------------------------------------------------------*/
// CC1100 low level send function
/*---------------------------------------------------------------------------*/
void cc1100_send_raw(uint8_t *tx_buffer, uint8_t size)
{
volatile uint32_t abort_count;
// The number of bytes to be transmitted must be smaller
// or equal to PACKET_LENGTH (62 bytes). So the receiver
// can put the whole packet in its RX-FIFO (with appended
// packet status bytes).
if (size > PACKET_LENGTH) return;
// Disables RX interrupt etc.
cc1100_before_send();
// But CC1100 in IDLE mode to flush the FIFO
cc1100_spi_strobe(CC1100_SIDLE);
// Flush TX FIFO to be sure it is empty
cc1100_spi_strobe(CC1100_SFTX);
// Write packet into TX FIFO
cc1100_spi_writeburst_reg(CC1100_TXFIFO, (char*) tx_buffer, size);
// Switch to TX mode
abort_count = 0;
unsigned int cpsr = disableIRQ();
cc1100_spi_strobe(CC1100_STX);
// Wait for GDO2 to be set -> sync word transmitted
while (cc1100_get_gdo2() == 0) {
abort_count++;
if (abort_count > CC1100_SYNC_WORD_TX_TIME) {
// Abort waiting. CC1100 maybe in wrong mode
// e.g. sending preambles for always
puts("[CC1100 TX] fatal error\n");
break;
}
}
restoreIRQ(cpsr);
// Wait for GDO2 to be cleared -> end of packet
while (cc1100_get_gdo2() != 0);
// Experimental - TOF Measurement
cc1100_after_send();
}
/*---------------------------------------------------------------------------*/
// Various functions (mode safe - they can be called in any radio mode)
/*---------------------------------------------------------------------------*/
uint8_t
read_register(uint8_t r)
{
uint8_t result;
// Save old radio state
uint8_t old_state = radio_state;
// Wake up from WOR/RX (if in WOR/RX, else no effect)
cc1100_go_idle();
result = cc1100_spi_read_reg(r);
// Have to put radio back to WOR/RX if old radio state
// was WOR/RX, otherwise no action is necessary
if (old_state == RADIO_WOR || old_state == RADIO_RX) {
cc1100_go_receive();
}
return result;
}
static void
write_register(uint8_t r, uint8_t value)
{
// Save old radio state
uint8_t old_state = radio_state;
// Wake up from WOR/RX (if in WOR/RX, else no effect)
cc1100_go_idle();
cc1100_spi_write_reg(r, value);
// Have to put radio back to WOR/RX if old radio state
// was WOR/RX, otherwise no action is necessary
if (old_state == RADIO_WOR || old_state == RADIO_RX) {
cc1100_go_receive();
}
}
char* cc1100_get_output_power(char* buf)
{
sprintf(buf, "%+i dBm", pa_table_dBm[pa_table_index]);
return buf;
}
uint8_t cc1100_get_channel(void)
{
return radio_channel;
}
bool
cc1100_set_channel(uint8_t channr)
{
if (channr > MAX_CHANNR) return false;
write_register(CC1100_CHANNR, channr*10);
radio_channel = channr;
return true;
}
bool
cc1100_set_output_power(uint8_t pa_idx)
{
if (pa_idx >= sizeof(pa_table)) return false;
write_register(CC1100_PATABLE, pa_table[pa_idx]);
pa_table_index = pa_idx;
return true;
}
char* cc1100_get_marc_state(void)
{
uint8_t state;
// Save old radio state
uint8_t old_state = radio_state;
// Read content of status register
state = cc1100_spi_read_status(CC1100_MARCSTATE) & MARC_STATE;
// Make sure in IDLE state.
// Only goes to IDLE if state was RX/WOR
cc1100_go_idle();
// Have to put radio back to WOR/RX if old radio state
// was WOR/RX, otherwise no action is necessary
if (old_state == RADIO_WOR || old_state == RADIO_RX) {
cc1100_go_receive();
}
switch (state)
{
// Note: it is not possible to read back the SLEEP or XOFF state numbers
// because setting CSn low will make the chip enter the IDLE mode from the
// SLEEP (0) or XOFF (2) states.
case 1: return "IDLE";
case 3: case 4: case 5: return "MANCAL";
case 6: case 7: return "FS_WAKEUP";
case 8: case 12: return "CALIBRATE";
case 9: case 10: case 11: return "SETTLING";
case 13: case 14: case 15: return "RX";
case 16: return "TXRX_SETTLING";
case 17: return "RXFIFO_OVERFLOW";
case 18: return "FSTXON";
case 19: case 20: return "TX";
case 21: return "RXTX_SETTLING";
case 22: return "TXFIFO_UNDERFLOW";
default: return "UNKNOWN";
}
}
/*
static int8_t
rssi_2_dbm(uint8_t rssi)
{
if (rssi >= 128) rssi -= 256;
rssi /= 2;
rssi -= 78;
return rssi;
}*/
/*---------------------------------------------------------------------------*/
// Radio Driver API
/*---------------------------------------------------------------------------*/
void cc1100_init(void)
{
// Initialize SPI
cc1100_spi_init();
// Set default mode (with default (energy optimized) RX interval)
cc1100_set_mode0(CC1100_RADIO_MODE, T_RX_INTERVAL);
// Load driver & reset
power_up_reset();
// Write configuration to configuration registers
extern char cc1100_conf[];
cc1100_spi_writeburst_reg(0x00, cc1100_conf, CC1100_CONF_SIZE);
// Write PATABLE (power settings)
cc1100_spi_write_reg(CC1100_PATABLE, pa_table[pa_table_index]);
// Initialize Radio Flags
rflags.RSSI = 0x00;
rflags.LL_ACK = false;
rflags.CAA = false;
rflags.CRC = false;
rflags.SEQ = false;
rflags.MAN_WOR = false;
rflags.KT_RES_ERR = false;
rflags.TX = false;
rflags.WOR_RST = false;
// Initialize physical layer
cc1100_phy_init();
// Set radio address of CC1100
cc1100_set_address(radio_address);
// Set default channel number
radio_channel = CC1100_DEFAULT_CHANNR;
// Switch to desired mode (WOR or RX)
rd_set_mode(RADIO_MODE_ON);
}
int cc1100_get_avg_transmission_duration(void)
{
if (radio_mode == CC1100_MODE_WOR) {
// Transmission duration ~ RX interval
// Double value because of MAC delay.
return 2 * cc1100_wor_config.rx_interval;
} else {
// Transmission duration ~ 32 ms
// Double value because of MAC delay.
return 2 * 32;
}
}
radio_address_t cc1100_get_address(void)
{
return radio_address;
}
bool cc1100_set_address(radio_address_t address)
{
if (address < MIN_UID || address > MAX_UID)
{
return false;
}
uint8_t id = (uint8_t) address;
if (radio_state != RADIO_UNKNOWN)
{
write_register(CC1100_ADDR, id);
}
radio_address = id;
return true;
}
static int
rd_set_mode(int mode)
{
int result;
// Get current radio mode
if (radio_state == RADIO_UNKNOWN || radio_state == RADIO_PWD)
{
result = RADIO_MODE_OFF;
}
else
{
result = RADIO_MODE_ON;
}
switch (mode)
{
case RADIO_MODE_ON:
cc1100_init_interrupts(); // Enable interrupts
cc1100_setup_mode(); // Set chip to desired mode
break;
case RADIO_MODE_OFF:
cc1100_disable_interrupts(); // Disable interrupts
switch_to_pwd(); // Set chip to power down mode
break;
case RADIO_MODE_GET:
// do nothing, just return current mode
default:
// do nothing
break;
}
// Return previous mode
return result;
}
/*---------------------------------------------------------------------------*/
// Carrier sense interface functions
/*---------------------------------------------------------------------------*/
void cc1100_cs_init(void)
{
cc1100_go_idle(); // Wake CC1100 up from Wake-On-Radio mode
if (radio_state == RADIO_RX) // If radio in RX mode
{
cc1100_spi_strobe(CC1100_SIDLE); // Go back to IDLE for calibration
}
cc1100_spi_write_reg(CC1100_MCSM0, 0x08); // Turn off FS-Autocal
cc1100_spi_strobe(CC1100_SCAL); // Calibrate manually (721 us)
hwtimer_wait(MANUAL_FS_CAL_TIME); // Wait for calibration to finish before packet burst can start
radio_state = RADIO_AIR_FREE_WAITING; // Set status "waiting for air free"
cc1100_spi_write_reg(CC1100_MCSM2, 0x07); // Configure RX_TIME = Until end of packet (no timeout)
cc1100_spi_strobe(CC1100_SRX); // Switch to RX (88.4 us) (Carrier Sense)
hwtimer_wait(CS_READY_TIME); // Wait until CC1100 is in RX + carrier sense ready (GDO0 ready for readout -> data rate dependent!!!)
}
void cc1100_cs_set_enabled(bool enabled)
{
if (enabled)
{
// Enable carrier sense detection (GDO0 interrupt)
cc1100_gdo0_enable();
}
else
{
// Disable carrier sense detection (GDO0 interrupt)
cc1100_gdo0_disable();
}
}
int cc1100_cs_read(void)
{
/* GDO0 reflects CS (high: air not free, low: air free) */
return cc1100_get_gdo0();
}
int cc1100_cs_read_cca(void)
{
return rflags.CAA;
}
void cc1100_cs_write_cca(const int cca)
{
rflags.CAA = cca;
}
/*---------------------------------------------------------------------------*/
/** @} */