2010-09-22 15:10:42 +02:00
/******************************************************************************
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 physical 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 : 2130 $
*
* @ note $ Id : cc1100_phy . c 2130 2010 - 05 - 12 13 : 19 : 07 Z hillebra $
*/
# include <stdio.h>
# include <string.h>
# include "hwtimer.h"
# include "swtimer.h"
# include "cc1100.h"
# include "cc1100_spi.h"
# include "cc1100_phy.h"
# include "cc1100-defaultSettings.h"
# include "protocol-multiplex.h"
# include "kernel.h"
# include "thread.h"
# include "mutex.h"
# include "msg.h"
# include "debug.h"
# define MSG_POLL 12346
# define FLAGS_IDENTIFICATION (0x01) ///< Bit mask for reading the identification out of the flags field
# define R_FLAGS_PROTOCOL(x) ((x & 0x0E)>>1) ///< Macro for reading the protocol out of the flags field
# define W_FLAGS_PROTOCOL(x) ((x<<1) & 0x0E) ///< Macro for writing the protocol in the flags field
/*---------------------------------------------------------------------------*/
// RX/TX buffer data structures
/*---------------------------------------------------------------------------*/
typedef struct rx_buffer_t
{
cc1100_packet_layer0_t packet ;
packet_info_t info ;
} rx_buffer_t ;
# define RX_BUFF_SIZE (10) ///< Size of RX queue
static volatile uint8_t rx_buffer_head ; ///< RX queue head
static volatile uint8_t rx_buffer_tail ; ///< RX queue tail
static volatile uint8_t rx_buffer_size ; ///< RX queue size
static rx_buffer_t rx_buffer [ RX_BUFF_SIZE ] ; ///< RX buffer
static cc1100_packet_layer0_t tx_buffer ; ///< TX buffer (for one packet)
/*---------------------------------------------------------------------------*/
// Process/Event management data structures
/*---------------------------------------------------------------------------*/
# define MAX_PACKET_HANDLERS (5)
static packet_monitor_t packet_monitor ;
static handler_entry_t handlers [ MAX_PACKET_HANDLERS ] ;
static const pm_table_t handler_table ;
static const char * cc1100_event_handler_name = " cc1100_event_handler " ;
static mutex_t cc1100_mutex ;
volatile int cc1100_mutex_pid ;
static swtimer_t cc1100_watch_dog ;
static uint64_t cc1100_watch_dog_period = 0 ;
2010-10-25 15:40:01 +02:00
static uint16_t cc1100_event_handler_pid ;
static void cc1100_event_handler_function ( void ) ;
static tcb event_handler_tcb ;
static char event_handler_stack [ KERNEL_CONF_STACKSIZE_MAIN ] ;
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
// Sequence number buffer management data structures
/*---------------------------------------------------------------------------*/
/**
* @ name Sequence Buffer
* @ {
*/
# define MAX_SEQ_BUFFER_SIZE (20) ///< Maximum size of the sequence number buffer
typedef struct
{
uint64_t m_ticks ; ///< 64-bit timestamp
uint8_t source ; ///< Source address
uint8_t identification ; ///< Identification (1-bit)
} seq_buffer_entry ;
/// Sequence number buffer for this layer
static seq_buffer_entry seq_buffer [ MAX_SEQ_BUFFER_SIZE ] ;
/// Next position to enter a new value into ::seqBuffer
static uint8_t seq_buffer_pos = 0 ;
/**
* @ brief Last sequence number this node has seen
*
* @ note ( phySrc + flags . identification ) - for speedup in ISR .
*/
static volatile uint16_t last_seq_num = 0 ;
/** @} */
/*---------------------------------------------------------------------------*/
// WOR configuration data structures
/*---------------------------------------------------------------------------*/
# define EVENT0_MAX (60493) ///< Maximum RX polling interval in milliseconds
# define WOR_RES_SWITCH (1891) ///< Switching point value in milliseconds between
///< WOR_RES = 0 and WOR_RES = 1
# define DUTY_CYCLE_SIZE (7) ///< Length of duty cycle array
cc1100_wor_config_t cc1100_wor_config ; ///< CC1100 WOR configuration
uint16_t cc1100_burst_count ; ///< Burst count, number of packets in a burst transfer
uint8_t cc1100_retransmission_count_uc ; ///< Number of retransmissions for unicast
uint8_t cc1100_retransmission_count_bc ; ///< Number of retransmissions for broadcast
const static double duty_cycle [ 2 ] [ DUTY_CYCLE_SIZE ] = ///< Duty cycle values from AN047
{
{ 12.5 , 6.25 , 3.125 , 1.563 , 0.781 , 0.391 , 0.195 } ,
{ 1.95 , 0.9765 , 0.4883 , 0.2441 , 0.1221 , 0.061035 , 0.030518 }
} ;
/*---------------------------------------------------------------------------*/
// Data structures for statistic
/*---------------------------------------------------------------------------*/
cc1100_statistic_t cc1100_statistic ;
/*---------------------------------------------------------------------------*/
// Initialization of physical layer
/*---------------------------------------------------------------------------*/
void cc1100_phy_init ( )
{
int i ;
rx_buffer_head = 0 ;
rx_buffer_tail = 0 ;
rx_buffer_size = 0 ;
// Initialize RX-Buffer (clear content)
for ( i = 0 ; i < RX_BUFF_SIZE ; i + + )
{
rx_buffer - > packet . length = 0 ;
}
// Initialize handler table & packet monitor
packet_monitor = NULL ;
pm_init_table ( ( pm_table_t * ) & handler_table , MAX_PACKET_HANDLERS , handlers ) ;
// Clear sequence number buffer
memset ( seq_buffer , 0 , sizeof ( seq_buffer_entry ) * MAX_SEQ_BUFFER_SIZE ) ;
// Initialize mutex
cc1100_mutex_pid = - 1 ;
mutex_init ( & cc1100_mutex ) ;
// Allocate event numbers and start cc1100 event process
2010-10-25 15:40:01 +02:00
cc1100_event_handler_pid = thread_create ( & event_handler_tcb , event_handler_stack , sizeof ( event_handler_stack ) , PRIORITY_CC1100 , CREATE_STACKTEST ,
2010-09-22 15:10:42 +02:00
cc1100_event_handler_function , cc1100_event_handler_name ) ;
// Active watchdog for the first time
if ( radio_mode = = CC1100_MODE_CONSTANT_RX ) {
cc1100_watch_dog_period = CC1100_WATCHDOG_PERIOD ;
if ( cc1100_watch_dog_period ! = 0 ) {
swtimer_set_msg ( & cc1100_watch_dog , 5000000L , cc1100_event_handler_pid , NULL ) ;
}
}
}
/*---------------------------------------------------------------------------*/
// CC1100 mutual exclusion
/*---------------------------------------------------------------------------*/
void cc1100_phy_mutex_lock ( void )
{
if ( fk_thread - > pid ! = cc1100_mutex_pid ) {
mutex_lock ( & cc1100_mutex ) ;
cc1100_mutex_pid = fk_thread - > pid ;
}
}
void cc1100_phy_mutex_unlock ( void )
{
cc1100_mutex_pid = - 1 ;
mutex_unlock ( & cc1100_mutex , 0 ) ;
}
/*---------------------------------------------------------------------------*/
// Statistical functions
/*---------------------------------------------------------------------------*/
void cc1100_reset_statistic ( void )
{
cc1100_statistic . packets_in_up = 0 ;
cc1100_statistic . acks_send = 0 ;
cc1100_statistic . packets_out_acked = 0 ;
cc1100_statistic . packets_in = 0 ;
cc1100_statistic . packets_out = 0 ;
cc1100_statistic . packets_out_broadcast = 0 ;
cc1100_statistic . raw_packets_out_acked = 0 ;
cc1100_statistic . raw_packets_out = 0 ;
cc1100_statistic . packets_in_dups = 0 ;
cc1100_statistic . packets_in_crc_fail = 0 ;
cc1100_statistic . packets_in_while_tx = 0 ;
cc1100_statistic . rx_buffer_max = 0 ;
cc1100_statistic . watch_dog_resets = 0 ;
}
void cc1100_print_statistic ( void )
{
printf ( " \n Statistic on CC1100 interface \n \n " ) ;
printf ( " Total packets send on layer 0.5 (broadcast): %lu \n " , cc1100_statistic . packets_out_broadcast ) ;
printf ( " Total packets send on layer 0.5 (unicast): %lu \n " , cc1100_statistic . packets_out ) ;
printf ( " Total packets Acked on layer 0.5: %lu (%.2f%%) \n " , cc1100_statistic . packets_out_acked , cc1100_statistic . packets_out_acked * ( 100.0f / ( float ) cc1100_statistic . packets_out ) ) ;
printf ( " Total packets send on layer 0: %lu \n " , cc1100_statistic . raw_packets_out ) ;
printf ( " Total packets send on layer 0 w. Ack on Layer 0.5: %lu (Avg. Ack after: %lu packets) \n " , cc1100_statistic . raw_packets_out_acked , cc1100_statistic . raw_packets_out_acked / cc1100_statistic . packets_out_acked ) ;
printf ( " Burst count on this node: %i (%.2f%%) \n " , cc1100_burst_count , ( 100 / ( float ) cc1100_burst_count ) * ( cc1100_statistic . raw_packets_out_acked / ( float ) cc1100_statistic . packets_out_acked ) ) ;
printf ( " Total packets In on layer 0: %lu \n " , cc1100_statistic . packets_in ) ;
printf ( " Duped packets In on layer 0: %lu \n " , cc1100_statistic . packets_in_dups ) ;
printf ( " Corrupted packets In on layer 0: %lu \n " , cc1100_statistic . packets_in_crc_fail ) ;
printf ( " Packets In on layer 0 while in TX: %lu \n " , cc1100_statistic . packets_in_while_tx ) ;
printf ( " Total packets In and up to layer 1: %lu (%.2f%%) \n " , cc1100_statistic . packets_in_up , cc1100_statistic . packets_in_up * ( 100.0f / ( float ) cc1100_statistic . packets_in ) ) ;
printf ( " Total Acks send on layer 0.5: %lu \n " , cc1100_statistic . acks_send ) ;
printf ( " RX Buffer max: %lu (now: %u) \n " , cc1100_statistic . rx_buffer_max , rx_buffer_size ) ;
printf ( " State machine resets by cc1100 watchdog: %lu \n " , cc1100_statistic . watch_dog_resets ) ;
}
void cc1100_print_config ( void )
{
char buf [ 8 ] ;
printf ( " Current radio mode: %s \r \n " , cc1100_mode_to_text ( radio_mode ) ) ;
printf ( " Current radio state: %s \r \n " , cc1100_state_to_text ( radio_state ) ) ;
printf ( " Current MARC state: %s \r \n " , cc1100_get_marc_state ( ) ) ;
printf ( " Current channel number: %u \r \n " , cc1100_get_channel ( ) ) ;
printf ( " Burst count: %u packet(s) \r \n " , cc1100_burst_count ) ;
printf ( " Retransmissions (unicast): %u - if no ACK \r \n " , cc1100_retransmission_count_uc ) ;
printf ( " Retransmissions (broadcast): %u - always \r \n " , cc1100_retransmission_count_bc ) ;
printf ( " Output power setting: %s \r \n " , cc1100_get_output_power ( buf ) ) ;
if ( radio_mode = = CC1100_MODE_WOR )
{
printf ( " RX polling interval: %u ms \r \n " , cc1100_wor_config . rx_interval ) ;
printf ( " WOR receive time: 0x%.2X (%f ms) \r \n " , cc1100_wor_config . rx_time_reg ,
cc1100_wor_config . rx_time_ms ) ;
printf ( " CC1100 WOREVT0 register: 0x%.2X \r \n " , cc1100_wor_config . wor_evt_0 ) ;
printf ( " CC1100 WOREVT1 register: 0x%.2X \r \n " , cc1100_wor_config . wor_evt_1 ) ;
printf ( " CC1100 WOR_CTRL register: 0x%.2X \r \n " , cc1100_wor_config . wor_ctrl ) ;
printf ( " CC1100 MAN_WOR flag: %u \r \n " , rflags . MAN_WOR ) ;
}
}
/*---------------------------------------------------------------------------*/
// Change of RX polling interval (T_EVENT0)
/*---------------------------------------------------------------------------*/
inline uint16_t iround ( double d )
{
return ( uint16_t ) d < 0 ? d - .5 : d + .5 ;
}
int cc1100_phy_calc_wor_settings ( uint16_t millis )
{
// Get packet interval as milliseconds
double t_packet_interval = ( double ) ( ( T_PACKET_INTERVAL ) / 1000.0 ) ;
// Calculate minimal T_EVENT0:
//
// (1) t_rx_time > t_packet_interval
// (2) t_rx_time = T_EVENT0 / 2 ^ (RX_TIME + 3 + WOR_RES)
// ------------------------------------------------------
// with RX_TIME = 0 && WOR_RES = 0 => event0_min > t_packet_interval * 8
//
// t_packet_interval = 3.8 ms (@400kbit/s)
//
// => event0_min = Math.ceil(3.8 * 8) + 10
uint16_t event0_min = ( uint16_t ) ( t_packet_interval * 8 ) + 1 + 10 ;
// Check if given value is in allowed range
if ( millis < event0_min | | millis > EVENT0_MAX )
{
return - 1 ;
}
// Time resolution for EVENT0 and other WOR parameters,
// possible values are 0 and 1 if WOR is used
uint8_t wor_res = millis < WOR_RES_SWITCH ? 0 : 1 ;
// Calculate new value for EVENT0
double tmp = ( millis * 26 ) / ( double ) 750 ;
if ( wor_res = = 1 ) tmp / = 32 ;
tmp * = 1000 ;
uint16_t event0 = ( uint16_t ) iround ( tmp ) ;
// Calculate all possible RX timeouts
int i ;
double rx_timeouts [ DUTY_CYCLE_SIZE ] ;
for ( i = 0 ; i < DUTY_CYCLE_SIZE ; i + + )
{
rx_timeouts [ i ] = ( millis * duty_cycle [ wor_res ] [ i ] ) / 100 ;
}
// Calculate index for optimal rx_timeout (MCSM2.RX_TIME) (if possible)
int idx = - 1 ;
for ( i = DUTY_CYCLE_SIZE - 1 ; i > = 0 ; i - - )
{
if ( rx_timeouts [ i ] > t_packet_interval )
{
idx = i ;
break ;
}
}
// If no index found, exit here (configuration with given value is not possible)
if ( idx = = - 1 ) return - 1 ;
// Calculate burst count (secure burst calculation with 8 extra packets)
int burst_count = ( int ) iround ( millis / t_packet_interval ) + 8 ;
// All calculations successful, now its safe to store
// final configuration values in global WOR configuration
cc1100_wor_config . rx_interval = millis ;
cc1100_wor_config . wor_ctrl = ( wor_res = = 0 ) ? 0x78 : 0x79 ;
cc1100_wor_config . wor_evt_0 = ( uint8_t ) event0 ;
cc1100_wor_config . wor_evt_1 = ( uint8_t ) ( event0 > > 8 ) ;
cc1100_wor_config . rx_time_reg = idx ;
cc1100_wor_config . rx_time_ms = rx_timeouts [ idx ] ;
// If successful, return number of packets in a burst transfer
return burst_count ;
}
/*---------------------------------------------------------------------------*/
// Sequence number buffer management
/*---------------------------------------------------------------------------*/
static bool contains_seq_entry ( uint8_t src , uint8_t id )
{
int i ;
uint32_t cmp ;
uint64_t now = swtimer_now ( ) ;
for ( i = 0 ; i < MAX_SEQ_BUFFER_SIZE ; i + + )
{
if ( ( seq_buffer [ i ] . source = = src ) & & ( seq_buffer [ i ] . identification = = id ) )
{
// Check if time stamp is OK
cmp = ( radio_mode = = CC1100_MODE_WOR ) ? cc1100_wor_config . rx_interval : 16000 ; // constant RX ~16ms
if ( ( now - seq_buffer [ i ] . m_ticks ) < = cmp )
{
return true ;
}
else
{
seq_buffer [ i ] . source = 0 ; // Reset
}
}
}
return false ;
}
static void add_seq_entry ( uint8_t src , uint8_t id )
{
// Remove all entries with given source to avoid short time overflow
// of one bit counter (of the source node). So a valid packet would get
// lost (especially important in constant RX mode).
int i ;
for ( i = 0 ; i < MAX_SEQ_BUFFER_SIZE ; i + + )
{
if ( seq_buffer [ i ] . source = = src )
{
seq_buffer [ i ] . source = 0 ; // Reset
}
}
// Add new entry
seq_buffer [ seq_buffer_pos ] . source = src ;
seq_buffer [ seq_buffer_pos ] . identification = id ;
seq_buffer [ seq_buffer_pos ] . m_ticks = swtimer_now ( ) ;
// Store 16 bit sequence number of layer 0 for speedup
last_seq_num = src ;
last_seq_num < < = 8 ;
last_seq_num + = id ;
seq_buffer_pos + + ;
if ( seq_buffer_pos = = MAX_SEQ_BUFFER_SIZE ) seq_buffer_pos = 0 ;
}
/*---------------------------------------------------------------------------*/
// CC1100 physical layer send functions
/*---------------------------------------------------------------------------*/
static void send_link_level_ack ( uint8_t dest )
{
uint8_t oldState = radio_state ; // Save old state
cc1100_packet_layer0_t ack ; // Local packet, don't overwrite
radio_state = RADIO_SEND_ACK ; // Set state to "Sending ACK"
cc1100_spi_write_reg ( CC1100_MCSM0 , 0x08 ) ; // Turn off FS-Autocal
cc1100_spi_write_reg ( CC1100_MCSM1 , 0x00 ) ; // TX_OFFMODE = IDLE
ack . length = 3 ; // possible packet in txBuffer!
ack . address = dest ;
ack . phy_src = rflags . RSSI ;
ack . flags = ( LAYER_1_PROTOCOL_LL_ACK < < 1 ) ;
cc1100_send_raw ( ( uint8_t * ) & ack , // IDLE -> TX (88.4 us)
ack . length + 1 ) ;
cc1100_spi_write_reg ( CC1100_MCSM0 , 0x18 ) ; // Turn on FS-Autocal
cc1100_spi_write_reg ( CC1100_MCSM1 , 0x03 ) ; // TX_OFFMODE = RX
radio_state = oldState ; // Restore state
cc1100_statistic . acks_send + + ;
}
static bool send_burst ( cc1100_packet_layer0_t * packet , uint8_t retries , uint8_t rtc )
{
int i ;
radio_state = RADIO_SEND_BURST ;
rflags . LL_ACK = false ;
for ( i = 1 ; i < = cc1100_burst_count ; i + + )
{
/*
* Number of bytes to send is :
* length of phy payload ( packet - > length )
* + size of length field ( 1 byte )
*/
extern unsigned long hwtimer_now ( void ) ;
timer_tick_t t = hwtimer_now ( ) + RTIMER_TICKS ( T_PACKET_INTERVAL ) ;
cc1100_send_raw ( ( uint8_t * ) packet , packet - > length + 1 ) ; // RX -> TX (9.6 us)
cc1100_statistic . raw_packets_out + + ;
// Delay until predefined "send" interval has passed
timer_tick_t now = hwtimer_now ( ) ;
if ( t > now )
{
hwtimer_wait ( t - now ) ;
}
/**
* After sending the packet the CC1100 goes automatically
* into RX mode ( 21.5 us ) ( listening for an ACK ) .
* Do not interrupt burst if send to broadcast address ( a node may
* have the broadcast address at startup and would stop the burst
* by sending an ACK ) .
*/
if ( rflags . LL_ACK & & packet - > address ! = CC1100_BROADCAST_ADDRESS )
{
cc1100_statistic . raw_packets_out_acked + = i ;
break ;
}
}
// No link level ACK -> do retry if retry counter greater zero
// Note: Event broadcast packets can be sent repeatedly if in
// constant RX mode. In WOR mode it is not necessary, so
// set retry count to zero.
if ( ! rflags . LL_ACK & & retries > 0 )
{
return send_burst ( packet , retries - 1 , rtc + 1 ) ;
}
// Store number of transmission retries
rflags . RTC = rtc ;
rflags . RPS = rtc * cc1100_burst_count + i ;
if ( i > cc1100_burst_count ) rflags . RPS - - ;
rflags . TX = false ;
// Go to mode after TX (CONST_RX -> RX, WOR -> WOR)
cc1100_go_after_tx ( ) ;
// Burst from any other node is definitely over
last_seq_num = 0 ;
if ( packet - > address ! = CC1100_BROADCAST_ADDRESS & & ! rflags . LL_ACK )
{
return false ;
}
return true ;
}
int cc1100_send ( radio_address_t addr , protocol_t protocol , int priority , char * payload , int payload_len )
{
bool result ;
int return_code ;
uint8_t address ;
uint8_t retries ;
// Lock mutex, nobody else should send now
cc1100_phy_mutex_lock ( ) ;
// TX state machine lock -> no timers (WOR), no packets (only ACKs)
rflags . TX = true ;
// Set chip to idle state
cc1100_set_idle ( ) ;
// CC1100 radio layer only supports 8-bit addresses
address = addr ;
// Loopback not supported
if ( address = = cc1100_get_address ( ) )
{
return_code = RADIO_ADDR_OUT_OF_RANGE ;
goto mode_before_final ;
}
// Check address
if ( address > MAX_UID )
{
return_code = RADIO_ADDR_OUT_OF_RANGE ;
goto mode_before_final ;
}
// Packet too long
if ( payload_len > MAX_DATA_LENGTH )
{
return_code = RADIO_PAYLOAD_TOO_LONG ;
goto mode_before_final ;
}
if ( radio_state = = RADIO_PWD )
{
return_code = RADIO_WRONG_MODE ;
goto mode_before_final ;
}
// Set number of transmission retries
retries = ( address = = CC1100_BROADCAST_ADDRESS ) ?
cc1100_retransmission_count_bc : cc1100_retransmission_count_uc ;
memset ( tx_buffer . data , 0 , MAX_DATA_LENGTH ) ; // Clean data
tx_buffer . length = 3 + MAX_DATA_LENGTH ; // 3 bytes (A&PS&F) + data length
tx_buffer . address = address ; // Copy destination address
tx_buffer . flags = 0x00 ; // Set clean state
tx_buffer . flags = W_FLAGS_PROTOCOL ( protocol ) ; // Copy protocol identifier
tx_buffer . phy_src = ( uint8_t ) cc1100_get_address ( ) ; // Copy sender address
// Set identification number of packet
tx_buffer . flags | = rflags . SEQ ; // Set flags.identification (bit 0)
rflags . SEQ = ! rflags . SEQ ; // Toggle value of layer 0 sequence number bit
memcpy ( tx_buffer . data , payload , payload_len ) ; // Copy data
// Send the packet
cc1100_spi_write_reg ( CC1100_MCSM0 , 0x08 ) ; // Turn off FS-Autocal
result = send_burst ( & tx_buffer , retries , 0 ) ; // Send raw burst
return_code = result ? payload_len : RADIO_OP_FAILED ;
// Collect statistics
if ( address ! = CC1100_BROADCAST_ADDRESS )
{
cc1100_statistic . packets_out + + ;
if ( result ) cc1100_statistic . packets_out_acked + + ;
}
else cc1100_statistic . packets_out_broadcast + + ;
goto final ;
mode_before_final :
rflags . TX = false ;
// Definitely set secure mode (CONST_RX -> RX, WOR -> WOR)
cc1100_go_after_tx ( ) ;
final :
// Release mutex and return
cc1100_phy_mutex_unlock ( ) ;
return return_code ;
}
/*---------------------------------------------------------------------------*/
// RX Event Handler
/*---------------------------------------------------------------------------*/
bool cc1100_set_packet_monitor ( packet_monitor_t monitor )
{
packet_monitor = monitor ;
return true ;
}
int cc1100_set_packet_handler ( protocol_t protocol , packet_handler_t handler )
{
if ( protocol > 7 ) return - 1 ; // Only 3-bit value allowed
return pm_set_handler ( & handler_table , protocol , handler ) ;
}
static void cc1100_event_handler_function ( void )
{
msg m ;
while ( 1 )
{
if ( cc1100_watch_dog_period ! = 0 ) {
swtimer_remove ( & cc1100_watch_dog ) ;
}
// Test if any resource error has occurred
if ( rflags . KT_RES_ERR )
{
rflags . KT_RES_ERR = false ;
// possibly do something, e.g. log error condition
}
if ( m . type = = MSG_TIMER )
{
uint8_t state ;
if ( radio_mode = = CC1100_MODE_CONSTANT_RX ) {
state = cc1100_spi_read_status ( CC1100_MARCSTATE ) & MARC_STATE ;
if ( ( state < 13 | | state > 15 ) & & radio_state = = RADIO_RX & & ! rflags . TX ) {
cc1100_statistic . watch_dog_resets + + ;
if ( state ! = 1 ) {
cc1100_spi_strobe ( CC1100_SIDLE ) ;
}
cc1100_spi_strobe ( CC1100_SFRX ) ;
cc1100_go_receive ( ) ;
}
} else {
// Radio mode is WOR, cannot read current MARC state, will
// always be IDLE. So do nothing here, e.g. disable watchdog.
}
}
while ( rx_buffer_size > 0 )
{
rx_buffer_t * packet = & rx_buffer [ rx_buffer_head ] ;
protocol_t p = R_FLAGS_PROTOCOL ( packet - > packet . flags ) ;
if ( packet_monitor ! = NULL ) packet_monitor ( ( void * ) & packet - > packet . data , MAX_DATA_LENGTH , p , & packet - > info ) ;
pm_invoke ( & handler_table , p , ( void * ) & packet - > packet . data , MAX_DATA_LENGTH , & packet - > info ) ;
dINT ( ) ;
rx_buffer_size - - ;
rx_buffer_head + + ;
if ( rx_buffer_head = = RX_BUFF_SIZE ) rx_buffer_head = 0 ;
eINT ( ) ;
}
dINT ( ) ;
if ( rx_buffer_size = = 0 )
{
if ( cc1100_watch_dog_period ! = 0 ) {
swtimer_set_msg ( & cc1100_watch_dog , cc1100_watch_dog_period * 1000000L ,
cc1100_event_handler_pid , NULL ) ;
}
msg_receive ( & m ) ;
}
eINT ( ) ;
}
}
/*---------------------------------------------------------------------------*/
// CC1100 packet (RX) ISR
/*---------------------------------------------------------------------------*/
void cc1100_phy_rx_handler ( void )
{
msg m ;
m . type = MSG_POLL ;
bool dup = false ;
bool res = false ;
// Possible packet received, RX -> IDLE (0.1 us)
rflags . CAA = false ;
rflags . MAN_WOR = false ;
cc1100_statistic . packets_in + + ;
// If WOR timer set, delete it now (new one will be set at end of ISR)
if ( wor_hwtimer_id ! = - 1 )
{
hwtimer_remove ( wor_hwtimer_id ) ;
wor_hwtimer_id = - 1 ;
}
// Transfer packet into temporary buffer position
res = cc1100_spi_receive_packet ( ( uint8_t * ) & ( rx_buffer [ rx_buffer_tail ] . packet ) , sizeof ( cc1100_packet_layer0_t ) ) ;
if ( res )
{
// Get packet pointer and store additional data in packet info structure
cc1100_packet_layer0_t * p = & ( rx_buffer [ rx_buffer_tail ] . packet ) ;
rx_buffer [ rx_buffer_tail ] . info . phy_src = p - > phy_src ;
rx_buffer [ rx_buffer_tail ] . info . rssi = rflags . RSSI ;
rx_buffer [ rx_buffer_tail ] . info . lqi = rflags . LQI ;
rx_buffer [ rx_buffer_tail ] . info . promiscuous = false ;
// Get protocol and id field out of flags field
uint8_t protocol = R_FLAGS_PROTOCOL ( p - > flags ) ;
uint8_t identification = ( p - > flags & FLAGS_IDENTIFICATION ) ;
// If received packet was an ACK (here we must be in
// TX lock state, otherwise we don't expect an ACK)
if ( protocol = = LAYER_1_PROTOCOL_LL_ACK & & rflags . TX )
{
// And packet was for us
if ( p - > address = = cc1100_get_address ( ) )
{
// Stop the burst
rflags . LL_ACK = true ;
rflags . RSSI_SEND = p - > phy_src ;
rflags . TCP = ( uint32_t ) ( ( uint16_t * ) p - > data ) [ 0 ] ;
}
return ;
}
else
{
// No ACK received so TOF is unpredictable
rflags . TOF = 0 ;
}
// If we are sending a burst, don't accept packets.
// Only ACKs are processed (for stopping the burst).
// Same if state machine is in TX lock.
if ( radio_state = = RADIO_SEND_BURST | | rflags . TX )
{
cc1100_statistic . packets_in_while_tx + + ;
return ;
}
// If buffer is currently full -> don't check sequence numbers, send
// ACK and restore state (keep always one position free for temporary packets)
if ( rx_buffer_size > = RX_BUFF_SIZE - 1 ) goto send_ack ;
// Build 16 bit sequence number of layer 0 for fast check
uint16_t new_seq_num = p - > phy_src ;
new_seq_num < < = 8 ;
new_seq_num + = identification ;
// Duplicate packet detection
dup = true ;
// If new and last sequence number are the same, then discard packet
if ( last_seq_num ! = new_seq_num )
{
// Do a more precise check (takes more time) with larger buffer
if ( ! contains_seq_entry ( p - > phy_src , identification ) )
{
// Sequence number is new, no duplicate packet
dup = false ;
// Store sequence number
add_seq_entry ( p - > phy_src , identification ) ;
// Make temporary packet in RX buffer to a "real" packet which is processed
rx_buffer_size + + ;
if ( rx_buffer_size > cc1100_statistic . rx_buffer_max ) cc1100_statistic . rx_buffer_max = rx_buffer_size ;
rx_buffer_tail + + ;
if ( rx_buffer_tail = = RX_BUFF_SIZE ) rx_buffer_tail = 0 ;
// Send empty message to wake up receiver process.
// Receiver process could already be running (triggered by previous message),
// so function would return 0 and assume the receiver is not waiting but indeed
// all is working fine.
msg_send_int ( & m , cc1100_event_handler_pid ) ;
cc1100_statistic . packets_in_up + + ;
}
}
send_ack :
// If packet was send directly to us, send an ACK packet back to sender.
// But only not if the packet itself was a LL-ACK!
if ( p - > address = = cc1100_get_address ( ) & & protocol ! = LAYER_1_PROTOCOL_LL_ACK )
{
send_link_level_ack ( p - > phy_src ) ;
// After LL-ACK burst is over, reset number
last_seq_num = 0 ;
}
// If duplicate packet detected, clear rxBuffer position
if ( dup )
{
cc1100_statistic . packets_in_dups + + ;
}
// If packet interrupted this nodes send call,
// don't change anything after this point.
if ( radio_state = = RADIO_AIR_FREE_WAITING )
{
cc1100_spi_strobe ( CC1100_SRX ) ;
hwtimer_wait ( IDLE_TO_RX_TIME ) ;
return ;
}
// Valid packet. After a wake-up, the radio should be in IDLE.
// So put CC1100 to RX for WOR_TIMEOUT (have to manually put
// the radio back to sleep/WOR).
cc1100_spi_write_reg ( CC1100_MCSM0 , 0x08 ) ; // Turn off FS-Autocal
cc1100_spi_write_reg ( CC1100_MCSM2 , 0x07 ) ; // Configure RX_TIME (until end of packet)
if ( radio_mode = = CC1100_MODE_CONSTANT_RX ) {
cc1100_spi_strobe ( CC1100_SRX ) ;
hwtimer_wait ( IDLE_TO_RX_TIME ) ;
radio_state = RADIO_RX ;
// Return here if mode is CONSTANT_RX_MODE
return ;
} else {
cc1100_spi_strobe ( CC1100_SPWD ) ;
radio_state = RADIO_PWD ;
}
// Set hwtimer to put CC1100 back to RX after WOR_TIMEOUT_1
wor_hwtimer_id = hwtimer_set ( WOR_TIMEOUT_1 , cc1100_hwtimer_go_receive_wrapper , NULL ) ;
if ( wor_hwtimer_id = = - 1 )
{
// Signal hwtimer resource error, radio stays in RX,
// so no big problem, only energy is wasted.
rflags . KT_RES_ERR = true ;
}
}
else
{
// No ACK received so TOF is unpredictable
rflags . TOF = 0 ;
// CRC false or RX buffer full -> clear RX FIFO in both cases
last_seq_num = 0 ; // Reset for correct burst detection
cc1100_spi_strobe ( CC1100_SIDLE ) ; // Switch to IDLE (should already be)...
cc1100_spi_strobe ( CC1100_SFRX ) ; // ...for flushing the RX FIFO
// If packet interrupted this nodes send call,
// don't change anything after this point.
if ( radio_state = = RADIO_AIR_FREE_WAITING )
{
cc1100_spi_strobe ( CC1100_SRX ) ;
hwtimer_wait ( IDLE_TO_RX_TIME ) ;
return ;
}
// If currently sending, exit here (don't go to RX/WOR)
if ( radio_state = = RADIO_SEND_BURST )
{
cc1100_statistic . packets_in_while_tx + + ;
return ;
}
// No valid packet, so go back to RX/WOR as soon as possible
cc1100_go_receive ( ) ;
}
}