Merge pull request #1709 from atiselsts/cc26xx_tsch_port

TSCH port for CC26xx
This commit is contained in:
George Oikonomou 2016-06-15 10:28:58 +01:00 committed by GitHub
commit 5fe95fc425
11 changed files with 641 additions and 216 deletions

View file

@ -38,6 +38,7 @@ It has been tested on the following platforms:
* Zolertia Z1 (`z1`, tested in cooja only)
* CC2538DK (`cc2538dk`, tested on hardware)
* Zolertia Zoul (`zoul`, tested on hardware)
* CC2650 (`srf06-cc26xx`, tested on hardware)
This implementation was present at the ETSI Plugtest
event in Prague in July 2015, and did successfully inter-operate with all
@ -78,7 +79,7 @@ Orchestra is implemented in:
A simple TSCH+RPL example is included under `examples/ipv6/rpl-tsch`.
To use TSCH, first make sure your platform supports it.
Currently, `jn516x`, `sky`, `z1`, `cc2538dk` and `zoul` are the supported platforms.
Currently, `jn516x`, `sky`, `z1`, `cc2538dk`, `zoul` and `srf06-cc26xx` are the supported platforms.
To add your own, we refer the reader to the next section.
To add TSCH to your application, first include the TSCH module from your makefile with:
@ -164,7 +165,7 @@ Finally, one can also implement his own scheduler, centralized or distributed, b
## Porting TSCH to a new platform
Porting TSCH to a new platform requires a few new features in the radio driver, a number of timing-related configuration paramters.
The easiest is probably to start from one of the existing port: `jn516x`, `sky`, `z1`, `cc2538dk`, `zoul`.
The easiest is probably to start from one of the existing port: `jn516x`, `sky`, `z1`, `cc2538dk`, `zoul`, `srf06-cc26xx`.
### Radio features required for TSCH

View file

@ -118,6 +118,9 @@ clock_init(void)
/* GPT0 / Timer B: One shot, PWM interrupt enable */
HWREG(GPT0_BASE + GPT_O_TBMR) =
((TIMER_CFG_B_ONE_SHOT >> 8) & 0xFF) | GPT_TBMR_TBPWMIE;
/* enable sync with radio timer */
HWREGBITW(AON_RTC_BASE + AON_RTC_O_CTL, AON_RTC_CTL_RTC_UPD_EN_BITN) = 1;
}
/*---------------------------------------------------------------------------*/
static void

View file

@ -81,11 +81,13 @@ soc_rtc_init(void)
ti_lib_aon_rtc_event_clear(AON_RTC_CH0);
ti_lib_aon_rtc_event_clear(AON_RTC_CH1);
ti_lib_aon_rtc_event_clear(AON_RTC_CH2);
/* Setup the wakeup event */
ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU0, AON_EVENT_RTC_CH0);
ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU1, AON_EVENT_RTC_CH1);
ti_lib_aon_rtc_combined_event_config(AON_RTC_CH0 | AON_RTC_CH1);
ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU2, AON_EVENT_RTC_CH2);
ti_lib_aon_rtc_combined_event_config(AON_RTC_CH0 | AON_RTC_CH1 | AON_RTC_CH2);
HWREG(AON_RTC_BASE + AON_RTC_O_SEC) = SOC_RTC_START_TICK_COUNT;
@ -123,7 +125,7 @@ soc_rtc_get_next_trigger()
void
soc_rtc_schedule_one_shot(uint32_t channel, uint32_t ticks)
{
if((channel != AON_RTC_CH0) && (channel != AON_RTC_CH1)) {
if((channel != AON_RTC_CH0) && (channel != AON_RTC_CH1) && (channel != AON_RTC_CH2)) {
return;
}
@ -170,6 +172,12 @@ soc_rtc_isr(void)
rtimer_run_next();
}
if(ti_lib_aon_rtc_event_get(AON_RTC_CH2)) {
/* after sleep; since a rtimer is already scheduled, do nothing */
ti_lib_aon_rtc_channel_disable(AON_RTC_CH2);
HWREG(AON_RTC_BASE + AON_RTC_O_EVFLAGS) = AON_RTC_EVFLAGS_CH2;
}
ENERGEST_OFF(ENERGEST_TYPE_IRQ);
}
/*---------------------------------------------------------------------------*/

View file

@ -77,12 +77,13 @@ LIST(modules_list);
* Don't consider standby mode if the next AON RTC event is scheduled to fire
* in less than STANDBY_MIN_DURATION rtimer ticks
*/
#define STANDBY_MIN_DURATION (RTIMER_SECOND >> 11)
#define MINIMAL_SAFE_SCHEDUAL 8u
#define STANDBY_MIN_DURATION (RTIMER_SECOND / 100) /* 10.0 ms */
/* Wake up this much time earlier before the next rtimer */
#define SLEEP_GUARD_TIME (RTIMER_SECOND / 1000) /* 1.0 ms */
#define MAX_SLEEP_TIME RTIMER_SECOND
#define DEFAULT_SLEEP_TIME RTIMER_SECOND
/*---------------------------------------------------------------------------*/
#define CLK_TO_RT(c) ((c) * (RTIMER_SECOND / CLOCK_SECOND))
#define MINIMAL_SAFE_SCHEDULE 8u
/*---------------------------------------------------------------------------*/
/* Prototype of a function in clock.c. Called every time we come out of DS */
void clock_update(void);
@ -241,14 +242,124 @@ wake_up(void)
module->wakeup();
}
}
#if CC2650_FAST_RADIO_STARTUP
/*
* Trigger a switch to the XOSC, so that we can subsequently use the RF FS
*/
oscillators_request_hf_xosc();
#endif
}
/*---------------------------------------------------------------------------*/
static int
setup_sleep_mode(rtimer_clock_t *next_timer)
{
lpm_registered_module_t *module;
uint8_t max_pm = LPM_MODE_MAX_SUPPORTED;
rtimer_clock_t now = RTIMER_NOW();
const rtimer_clock_t max_sleep = now + MAX_SLEEP_TIME;
/* next_timer will hold the time of the next system wakeup due to a timer*/
*next_timer = max_sleep;
/* Check if any events fired before we turned interrupts off. If so, abort */
if(LPM_MODE_MAX_SUPPORTED == LPM_MODE_AWAKE || process_nevents()) {
return LPM_MODE_AWAKE;
}
if(ti_lib_aon_rtc_channel_active(AON_RTC_CH0)) {
rtimer_clock_t next_rtimer;
/* find out the timer of the next rtimer interrupt */
next_rtimer = ti_lib_aon_rtc_compare_value_get(AON_RTC_CH0);
if(RTIMER_CLOCK_LT(next_rtimer, now + 2)) {
return LPM_MODE_AWAKE;
}
if(RTIMER_CLOCK_LT(next_rtimer, now + STANDBY_MIN_DURATION)) {
return LPM_MODE_SLEEP;
}
*next_timer = next_rtimer;
}
/* also find out the timer of the next etimer */
if(etimer_pending()) {
int32_t until_next_etimer;
rtimer_clock_t next_etimer;
until_next_etimer = (int32_t)etimer_next_expiration_time() - (int32_t)clock_time();
if(until_next_etimer < 1) {
return LPM_MODE_AWAKE;
}
next_etimer = soc_rtc_last_isr_time() + (until_next_etimer * (RTIMER_SECOND / CLOCK_SECOND));
if(RTIMER_CLOCK_LT(next_etimer, now + STANDBY_MIN_DURATION)) {
/* ensure that we schedule sleep a minimal number of ticks into the
future */
soc_rtc_schedule_one_shot(AON_RTC_CH1, now + MINIMAL_SAFE_SCHEDULE);
return LPM_MODE_SLEEP;
}
if(RTIMER_CLOCK_LT(max_sleep, next_etimer)) {
/* if max_pm is LPM_MODE_SLEEP, we could trigger the watchdog if we slept
for too long. */
if(RTIMER_CLOCK_LT(max_sleep, *next_timer)) {
soc_rtc_schedule_one_shot(AON_RTC_CH1, max_sleep);
}
} else {
/* Reschedule AON RTC CH1 to fire just in time for the next etimer event */
soc_rtc_schedule_one_shot(AON_RTC_CH1, next_etimer);
}
if(RTIMER_CLOCK_LT(next_etimer, *next_timer)) {
/* set `next_timer` to the time the first etimer fires */
*next_timer = next_etimer;
}
}
/* Collect max allowed PM permission from interested modules */
for(module = list_head(modules_list); module != NULL;
module = module->next) {
if(module->request_max_pm) {
uint8_t module_pm = module->request_max_pm();
if(module_pm < max_pm) {
max_pm = module_pm;
}
}
}
return max_pm;
}
/*---------------------------------------------------------------------------*/
void
lpm_sleep(void)
{
ENERGEST_SWITCH(ENERGEST_TYPE_CPU, ENERGEST_TYPE_LPM);
/* We are only interested in IRQ energest while idle or in LPM */
ENERGEST_IRQ_RESTORE(irq_energest);
/* Just to be on the safe side, explicitly disable Deep Sleep */
HWREG(NVIC_SYS_CTRL) &= ~(NVIC_SYS_CTRL_SLEEPDEEP);
ti_lib_prcm_sleep();
/* Remember IRQ energest for next pass */
ENERGEST_IRQ_SAVE(irq_energest);
ENERGEST_SWITCH(ENERGEST_TYPE_LPM, ENERGEST_TYPE_CPU);
}
/*---------------------------------------------------------------------------*/
static void
deep_sleep(void)
deep_sleep(rtimer_clock_t next_timer)
{
uint32_t domains = LOCKABLE_DOMAINS;
lpm_registered_module_t *module;
#if CC2650_FAST_RADIO_STARTUP
/* schedule a wakeup briefly before the next etimer/rtimer to wake up the system */
soc_rtc_schedule_one_shot(AON_RTC_CH2, next_timer - SLEEP_GUARD_TIME);
#endif
/*
* Notify all registered modules that we are dropping to mode X. We do not
* need to do this for simple sleep.
@ -304,7 +415,8 @@ deep_sleep(void)
* turn back off.
*
* If the radio is on, we won't even reach here, and if it's off the HF
* clock source should already be the HF RC.
* clock source should already be the HF RC, unless CC2650_FAST_RADIO_STARTUP
* is defined.
*
* Nevertheless, request the switch to the HF RC explicitly here.
*/
@ -370,131 +482,32 @@ deep_sleep(void)
* unpending events so the handlers can fire
*/
wake_up();
}
/*---------------------------------------------------------------------------*/
static void
safe_schedule_rtimer(rtimer_clock_t time, rtimer_clock_t now, int pm)
{
rtimer_clock_t min_sleep;
rtimer_clock_t max_sleep;
min_sleep = now + MINIMAL_SAFE_SCHEDUAL;
max_sleep = now + MAX_SLEEP_TIME;
if(RTIMER_CLOCK_LT(time, min_sleep)) {
/* ensure that we schedule sleep a minimal number of ticks into the
future */
soc_rtc_schedule_one_shot(AON_RTC_CH1, min_sleep);
} else if((pm == LPM_MODE_SLEEP) && RTIMER_CLOCK_LT(max_sleep, time)) {
/* if max_pm is LPM_MODE_SLEEP, we could trigger the watchdog if we slept
for too long. */
soc_rtc_schedule_one_shot(AON_RTC_CH1, max_sleep);
} else {
soc_rtc_schedule_one_shot(AON_RTC_CH1, time);
}
}
/*---------------------------------------------------------------------------*/
static int
setup_sleep_mode(void)
{
rtimer_clock_t et_distance = 0;
lpm_registered_module_t *module;
int max_pm;
int module_pm;
int etimer_is_pending;
rtimer_clock_t now;
rtimer_clock_t et_time;
rtimer_clock_t next_trig;
max_pm = LPM_MODE_MAX_SUPPORTED;
now = RTIMER_NOW();
if((LPM_MODE_MAX_SUPPORTED == LPM_MODE_AWAKE) || process_nevents()) {
return LPM_MODE_AWAKE;
}
etimer_is_pending = etimer_pending();
if(etimer_is_pending) {
et_distance = CLK_TO_RT(etimer_next_expiration_time() - clock_time());
if(RTIMER_CLOCK_LT(et_distance, 1)) {
/* there is an etimer which is already expired; we shouldn't go to
sleep at all */
return LPM_MODE_AWAKE;
}
}
next_trig = soc_rtc_get_next_trigger();
if(RTIMER_CLOCK_LT(next_trig, now + STANDBY_MIN_DURATION)) {
return LPM_MODE_SLEEP;
}
/* Collect max allowed PM permission from interested modules */
for(module = list_head(modules_list); module != NULL;
module = module->next) {
if(module->request_max_pm) {
module_pm = module->request_max_pm();
if(module_pm < max_pm) {
max_pm = module_pm;
}
}
}
/* Reschedule AON RTC CH1 to fire just in time for the next etimer event */
if(etimer_is_pending) {
et_time = soc_rtc_last_isr_time() + et_distance;
safe_schedule_rtimer(et_time, now, max_pm);
} else {
/* set a maximal sleep period if no etimers are queued */
soc_rtc_schedule_one_shot(AON_RTC_CH1, now + DEFAULT_SLEEP_TIME);
}
return max_pm;
ti_lib_int_master_enable();
}
/*---------------------------------------------------------------------------*/
void
lpm_drop()
{
uint8_t max_pm;
rtimer_clock_t next_timer;
/* Critical. Don't get interrupted! */
ti_lib_int_master_disable();
max_pm = setup_sleep_mode();
max_pm = setup_sleep_mode(&next_timer);
/* Drop */
if(max_pm == LPM_MODE_SLEEP) {
lpm_sleep();
} else if(max_pm == LPM_MODE_DEEP_SLEEP) {
deep_sleep();
deep_sleep(next_timer);
}
ti_lib_int_master_enable();
}
/*---------------------------------------------------------------------------*/
void
lpm_sleep(void)
{
ENERGEST_SWITCH(ENERGEST_TYPE_CPU, ENERGEST_TYPE_LPM);
/* We are only interested in IRQ energest while idle or in LPM */
ENERGEST_IRQ_RESTORE(irq_energest);
/* Just to be on the safe side, explicitly disable Deep Sleep */
HWREG(NVIC_SYS_CTRL) &= ~(NVIC_SYS_CTRL_SLEEPDEEP);
ti_lib_prcm_sleep();
/* Remember IRQ energest for next pass */
ENERGEST_IRQ_SAVE(irq_energest);
ENERGEST_SWITCH(ENERGEST_TYPE_LPM, ENERGEST_TYPE_CPU);
}
/*---------------------------------------------------------------------------*/
void
lpm_register_module(lpm_registered_module_t *module)
{
list_add(modules_list, module);
@ -512,7 +525,7 @@ lpm_init()
list_init(modules_list);
/* Always wake up on any DIO edge detection */
ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU2, AON_EVENT_IO);
ti_lib_aon_event_mcu_wake_up_set(AON_EVENT_MCU_WU3, AON_EVENT_IO);
}
/*---------------------------------------------------------------------------*/
void

View file

@ -51,6 +51,7 @@
#include "sys/energest.h"
#include "sys/clock.h"
#include "sys/rtimer.h"
#include "sys/ctimer.h"
#include "sys/cc.h"
#include "lpm.h"
#include "ti-lib.h"
@ -102,6 +103,9 @@
#define IEEE_MODE_RSSI_THRESHOLD 0xA6
#endif /* IEEE_MODE_CONF_RSSI_THRESHOLD */
/*---------------------------------------------------------------------------*/
#define STATUS_CRC_OK 0x80
#define STATUS_CORRELATION 0x7f
/*---------------------------------------------------------------------------*/
/* Data entry status field constants */
#define DATA_ENTRY_STATUS_PENDING 0x00 /* Not in use by the Radio CPU */
#define DATA_ENTRY_STATUS_ACTIVE 0x01 /* Open for r/w by the radio CPU */
@ -142,6 +146,13 @@ static uint8_t rf_stats[16] = { 0 };
#define RF_CMD_CCA_REQ_CCA_STATE_IDLE 0 /* 00 */
#define RF_CMD_CCA_REQ_CCA_STATE_BUSY 1 /* 01 */
#define RF_CMD_CCA_REQ_CCA_STATE_INVALID 2 /* 10 */
#define RF_CMD_CCA_REQ_CCA_CORR_IDLE (0 << 4)
#define RF_CMD_CCA_REQ_CCA_CORR_BUSY (1 << 4)
#define RF_CMD_CCA_REQ_CCA_CORR_INVALID (3 << 4)
#define RF_CMD_CCA_REQ_CCA_CORR_MASK (3 << 4)
#define RF_CMD_CCA_REQ_CCA_SYNC_BUSY (1 << 6)
/*---------------------------------------------------------------------------*/
#define IEEE_MODE_CHANNEL_MIN 11
#define IEEE_MODE_CHANNEL_MAX 26
@ -186,6 +197,35 @@ static const output_config_t output_power[] = {
/* Default TX Power - position in output_power[] */
const output_config_t *tx_power_current = &output_power[0];
/*---------------------------------------------------------------------------*/
static volatile int8_t last_rssi = 0;
static volatile uint8_t last_corr_lqi = 0;
extern int32_t rat_offset;
/*---------------------------------------------------------------------------*/
/* SFD timestamp in RTIMER ticks */
static volatile uint32_t last_packet_timestamp = 0;
/* SFD timestamp in RAT ticks (but 64 bits) */
static uint64_t last_rat_timestamp64 = 0;
/* For RAT overflow handling */
static struct ctimer rat_overflow_timer;
static uint32_t rat_overflow_counter = 0;
static rtimer_clock_t last_rat_overflow = 0;
/* RAT has 32-bit register, overflows once 18 minutes */
#define RAT_RANGE 4294967296ull
/* approximate value */
#define RAT_OVERFLOW_PERIOD_SECONDS (60 * 18)
/* XXX: don't know what exactly is this, looks like the time to Tx 3 octets */
#define TIMESTAMP_OFFSET -(USEC_TO_RADIO(32 * 3) - 1) /* -95.75 usec */
/*---------------------------------------------------------------------------*/
/* Are we currently in poll mode? */
static uint8_t poll_mode = 0;
static rfc_CMD_IEEE_MOD_FILT_t filter_cmd;
/*---------------------------------------------------------------------------*/
/*
* Buffers used to send commands to the RF core (generic and IEEE commands).
* Some of those buffers are re-usable, some are not.
@ -202,7 +242,7 @@ static uint8_t cmd_ieee_rx_buf[RF_CMD_BUFFER_SIZE] CC_ALIGN(4);
#define DATA_ENTRY_LENSZ_BYTE 1
#define DATA_ENTRY_LENSZ_WORD 2 /* 2 bytes */
#define RX_BUF_SIZE 140
#define RX_BUF_SIZE 144
/* Four receive buffers entries with room for 1 IEEE802.15.4 frame in each */
static uint8_t rx_buf_0[RX_BUF_SIZE] CC_ALIGN(4);
static uint8_t rx_buf_1[RX_BUF_SIZE] CC_ALIGN(4);
@ -544,7 +584,7 @@ init_rf_params(void)
cmd->rxConfig.bAppendRssi = 1;
cmd->rxConfig.bAppendCorrCrc = 1;
cmd->rxConfig.bAppendSrcInd = 0;
cmd->rxConfig.bAppendTimestamp = 0;
cmd->rxConfig.bAppendTimestamp = 1;
cmd->pRxQ = &rx_data_queue;
cmd->pOutput = (rfc_ieeeRxOutput_t *)rf_stats;
@ -568,7 +608,7 @@ init_rf_params(void)
cmd->frameFiltOpt.defaultPend = 0;
cmd->frameFiltOpt.bPendDataReqOnly = 0;
cmd->frameFiltOpt.bPanCoord = 0;
cmd->frameFiltOpt.maxFrameVersion = 1;
cmd->frameFiltOpt.maxFrameVersion = 2;
cmd->frameFiltOpt.bStrictLenFilter = 0;
/* Receive all frame types */
@ -584,9 +624,9 @@ init_rf_params(void)
/* Configure CCA settings */
cmd->ccaOpt.ccaEnEnergy = 1;
cmd->ccaOpt.ccaEnCorr = 1;
cmd->ccaOpt.ccaEnSync = 0;
cmd->ccaOpt.ccaEnSync = 1;
cmd->ccaOpt.ccaCorrOp = 1;
cmd->ccaOpt.ccaSyncOp = 1;
cmd->ccaOpt.ccaSyncOp = 0;
cmd->ccaOpt.ccaCorrThr = 3;
cmd->ccaRssiThr = IEEE_MODE_RSSI_THRESHOLD;
@ -598,6 +638,11 @@ init_rf_params(void)
cmd->endTrigger.triggerType = TRIG_NEVER;
cmd->endTime = 0x00000000;
/* set address filter command */
filter_cmd.commandNo = CMD_IEEE_MOD_FILT;
memcpy(&filter_cmd.newFrameFiltOpt, &cmd->frameFiltOpt, sizeof(cmd->frameFiltOpt));
memcpy(&filter_cmd.newFrameTypes, &cmd->frameTypes, sizeof(cmd->frameTypes));
}
/*---------------------------------------------------------------------------*/
static int
@ -647,13 +692,14 @@ rx_off(void)
if(RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf) == IEEE_DONE_STOPPED ||
RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf) == IEEE_DONE_ABORT) {
/* Stopped gracefully */
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
ret = RF_CORE_CMD_OK;
} else {
PRINTF("RX off: BG status=0x%04x\n", RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf));
ret = RF_CORE_CMD_ERROR;
}
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
return ret;
}
/*---------------------------------------------------------------------------*/
@ -712,6 +758,55 @@ static const rf_core_primary_mode_t mode_ieee = {
soft_on,
};
/*---------------------------------------------------------------------------*/
static void
check_rat_overflow(bool first_time)
{
static uint32_t last_value;
uint32_t current_value;
uint8_t interrupts_enabled;
interrupts_enabled = ti_lib_int_master_disable();
if(first_time) {
last_value = HWREG(RFC_RAT_BASE + RATCNT);
} else {
current_value = HWREG(RFC_RAT_BASE + RATCNT);
if(current_value + RAT_RANGE / 4 < last_value) {
/* overflow detected */
last_rat_overflow = RTIMER_NOW();
rat_overflow_counter++;
}
last_value = current_value;
}
if(interrupts_enabled) {
ti_lib_int_master_enable();
}
}
/*---------------------------------------------------------------------------*/
static void
handle_rat_overflow(void *unused)
{
uint8_t was_off = 0;
if(!rf_is_on()) {
was_off = 1;
if(on() != RF_CORE_CMD_OK) {
PRINTF("overflow: on() failed\n");
ctimer_set(&rat_overflow_timer, RAT_OVERFLOW_PERIOD_SECONDS * CLOCK_SECOND / 2,
handle_rat_overflow, NULL);
return;
}
}
check_rat_overflow(false);
if(was_off) {
off();
}
ctimer_set(&rat_overflow_timer, RAT_OVERFLOW_PERIOD_SECONDS * CLOCK_SECOND / 2,
handle_rat_overflow, NULL);
}
/*---------------------------------------------------------------------------*/
static int
init(void)
{
@ -745,6 +840,10 @@ init(void)
rf_core_primary_mode_register(&mode_ieee);
check_rat_overflow(true);
ctimer_set(&rat_overflow_timer, RAT_OVERFLOW_PERIOD_SECONDS * CLOCK_SECOND / 2,
handle_rat_overflow, NULL);
process_start(&rf_core_process, NULL);
return 1;
}
@ -755,7 +854,7 @@ prepare(const void *payload, unsigned short payload_len)
int len = MIN(payload_len, TX_BUF_PAYLOAD_LEN);
memcpy(&tx_buf[TX_BUF_HDR_LEN], payload, len);
return RF_CORE_CMD_OK;
return 0;
}
/*---------------------------------------------------------------------------*/
static int
@ -768,6 +867,7 @@ transmit(unsigned short transmit_len)
uint8_t tx_active = 0;
rtimer_clock_t t0;
volatile rfc_CMD_IEEE_TX_t cmd;
uint8_t interrupts_enabled;
if(!rf_is_on()) {
was_off = 1;
@ -805,8 +905,19 @@ transmit(unsigned short transmit_len)
cmd.payloadLen = transmit_len;
cmd.pPayload = &tx_buf[TX_BUF_HDR_LEN];
cmd.startTime = 0;
cmd.startTrigger.triggerType = TRIG_NOW;
/* XXX: there seems to be no function that gets interrupt state w/o changing it */
interrupts_enabled = ti_lib_int_master_disable();
if(interrupts_enabled) {
ti_lib_int_master_enable();
}
/* Enable the LAST_FG_COMMAND_DONE interrupt, which will wake us up */
rf_core_cmd_done_en(true);
if(interrupts_enabled) {
rf_core_cmd_done_en(true, poll_mode);
}
ret = rf_core_send_cmd((uint32_t)&cmd, &cmd_status);
@ -818,7 +929,14 @@ transmit(unsigned short transmit_len)
/* Idle away while the command is running */
while((cmd.status & RF_CORE_RADIO_OP_MASKED_STATUS)
== RF_CORE_RADIO_OP_MASKED_STATUS_RUNNING) {
lpm_sleep();
/* Note: for now sleeping while Tx'ing in polling mode is disabled.
* To enable it:
* 1) make the `lpm_sleep()` call here unconditional;
* 2) change the radio ISR priority to allow radio ISR to interrupt rtimer ISR.
*/
if(interrupts_enabled) {
lpm_sleep();
}
}
stat = cmd.status;
@ -848,12 +966,13 @@ transmit(unsigned short transmit_len)
ENERGEST_OFF(ENERGEST_TYPE_TRANSMIT);
ENERGEST_ON(ENERGEST_TYPE_LISTEN);
/*
* Disable LAST_FG_COMMAND_DONE interrupt. We don't really care about it
* except when we are transmitting
*/
rf_core_cmd_done_dis();
if(interrupts_enabled) {
/*
* Disable LAST_FG_COMMAND_DONE interrupt. We don't really care about it
* except when we are transmitting
*/
rf_core_cmd_done_dis(poll_mode);
}
if(was_off) {
off();
@ -880,13 +999,48 @@ release_data_entry(void)
/* Set status to 0 "Pending" in element */
entry->status = DATA_ENTRY_STATUS_PENDING;
rx_read_entry = entry->pNextEntry;
}/*---------------------------------------------------------------------------*/
}
/*---------------------------------------------------------------------------*/
static uint32_t
calc_last_packet_timestamp(uint32_t rat_timestamp)
{
uint64_t rat_timestamp64;
uint32_t adjusted_overflow_counter = rat_overflow_counter;
/* if the timestamp is large and the last oveflow was recently,
assume that the timestamp refers to the time before the overflow */
if(rat_timestamp > (uint32_t)(RAT_RANGE * 3 / 4)) {
if(RTIMER_CLOCK_LT(RTIMER_NOW(),
last_rat_overflow + RAT_OVERFLOW_PERIOD_SECONDS * RTIMER_SECOND / 4)) {
adjusted_overflow_counter--;
}
}
/* add the overflowed time to the timestamp */
rat_timestamp64 = rat_timestamp + RAT_RANGE * adjusted_overflow_counter;
/* correct timestamp so that it refers to the end of the SFD */
rat_timestamp64 += TIMESTAMP_OFFSET;
last_rat_timestamp64 = rat_timestamp64 - rat_offset;
return RADIO_TO_RTIMER(rat_timestamp64 - rat_offset);
}
/*---------------------------------------------------------------------------*/
static int
read_frame(void *buf, unsigned short buf_len)
{
int8_t rssi;
int len = 0;
rfc_dataEntryGeneral_t *entry = (rfc_dataEntryGeneral_t *)rx_read_entry;
uint32_t rat_timestamp;
if(rf_is_on()) {
check_rat_overflow(false);
}
/* wait for entry to become finished */
rtimer_clock_t t0 = RTIMER_NOW();
while(entry->status == DATA_ENTRY_STATUS_BUSY
&& RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + (RTIMER_SECOND / 250)));
if(entry->status != DATA_ENTRY_STATUS_FINISHED) {
/* No available data */
@ -901,7 +1055,7 @@ read_frame(void *buf, unsigned short buf_len)
return 0;
}
len = rx_read_entry[8] - 4;
len = rx_read_entry[8] - 8;
if(len > buf_len) {
PRINTF("RF: too long\n");
@ -913,9 +1067,21 @@ read_frame(void *buf, unsigned short buf_len)
memcpy(buf, (char *)&rx_read_entry[9], len);
rssi = (int8_t)rx_read_entry[9 + len + 2];
last_rssi = (int8_t)rx_read_entry[9 + len + 2];
last_corr_lqi = (uint8_t)rx_read_entry[9 + len + 2] & STATUS_CORRELATION;
packetbuf_set_attr(PACKETBUF_ATTR_RSSI, rssi);
/* get the timestamp */
memcpy(&rat_timestamp, (char *)rx_read_entry + 9 + len + 4, 4);
last_packet_timestamp = calc_last_packet_timestamp(rat_timestamp);
if(!poll_mode) {
/* Not in poll mode: packetbuf should not be accessed in interrupt context.
* In poll mode, the last packet RSSI and link quality can be obtained through
* RADIO_PARAM_LAST_RSSI and RADIO_PARAM_LAST_LINK_QUALITY */
packetbuf_set_attr(PACKETBUF_ATTR_RSSI, last_rssi);
packetbuf_set_attr(PACKETBUF_ATTR_LINK_QUALITY, last_corr_lqi);
}
RIMESTATS_ADD(llrx);
release_data_entry();
@ -983,9 +1149,7 @@ channel_clear(void)
static int
receiving_packet(void)
{
int ret = 0;
uint8_t cca_info;
uint8_t was_off = 0;
/*
* If we are in the middle of a BLE operation, we got called by ContikiMAC
@ -1010,19 +1174,17 @@ receiving_packet(void)
cca_info = get_cca_info();
/* If we can't read CCA info, return "not receiving" */
if(cca_info == RF_GET_CCA_INFO_ERROR) {
/* If we can't read CCA info, return "not receiving" */
ret = 0;
} else {
/* Return 1 (receiving) if ccaState is busy */
ret = (cca_info & 0x03) == RF_CMD_CCA_REQ_CCA_STATE_BUSY;
return 0;
}
if(was_off) {
off();
/* If sync has been seen, return 1 (receiving) */
if(cca_info & RF_CMD_CCA_REQ_CCA_SYNC_BUSY) {
return 1;
}
return ret;
return 0;
}
/*---------------------------------------------------------------------------*/
static int
@ -1033,9 +1195,12 @@ pending_packet(void)
/* Go through all RX buffers and check their status */
do {
if(entry->status == DATA_ENTRY_STATUS_FINISHED) {
if(entry->status == DATA_ENTRY_STATUS_FINISHED
|| entry->status == DATA_ENTRY_STATUS_BUSY) {
rv = 1;
process_poll(&rf_core_process);
if(!poll_mode) {
process_poll(&rf_core_process);
}
}
entry = (rfc_dataEntry_t *)entry->pNextEntry;
@ -1057,11 +1222,13 @@ on(void)
return RF_CORE_CMD_OK;
}
#if !CC2650_FAST_RADIO_STARTUP
/*
* Request the HF XOSC as the source for the HF clock. Needed before we can
* use the FS. This will only request, it will _not_ perform the switch.
*/
oscillators_request_hf_xosc();
#endif
if(rf_is_on()) {
PRINTF("on: We were on. PD=%u, RX=0x%04x \n", rf_core_is_accessible(),
@ -1069,21 +1236,22 @@ on(void)
return RF_CORE_CMD_OK;
}
init_rx_buffers();
/*
* Trigger a switch to the XOSC, so that we can subsequently use the RF FS
* This will block until the XOSC is actually ready, but give how we
* requested it early on, this won't be too long a wait.
* This should be done before starting the RAT.
*/
oscillators_switch_to_hf_xosc();
if(rf_core_boot() != RF_CORE_CMD_OK) {
PRINTF("on: rf_core_boot() failed\n");
return RF_CORE_CMD_ERROR;
}
init_rx_buffers();
rf_core_setup_interrupts();
/*
* Trigger a switch to the XOSC, so that we can subsequently use the RF FS
* This will block until the XOSC is actually ready, but give how we
* requested it early on, this won't be too long a wait/
*/
oscillators_switch_to_hf_xosc();
rf_core_setup_interrupts(poll_mode);
if(rf_radio_setup() != RF_CORE_CMD_OK) {
PRINTF("on: radio_setup() failed\n");
@ -1113,8 +1281,12 @@ off(void)
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
/* Switch HF clock source to the RCOSC to preserve power */
#if !CC2650_FAST_RADIO_STARTUP
/* Switch HF clock source to the RCOSC to preserve power.
* This must be done after stopping RAT.
*/
oscillators_switch_to_hf_rc();
#endif
/* We pulled the plug, so we need to restore the status manually */
((rfc_CMD_IEEE_RX_t *)cmd_ieee_rx_buf)->status = RF_CORE_RADIO_OP_STATUS_IDLE;
@ -1123,14 +1295,33 @@ off(void)
* Just in case there was an ongoing RX (which started after we begun the
* shutdown sequence), we don't want to leave the buffer in state == ongoing
*/
((rfc_dataEntry_t *)rx_buf_0)->status = DATA_ENTRY_STATUS_PENDING;
((rfc_dataEntry_t *)rx_buf_1)->status = DATA_ENTRY_STATUS_PENDING;
((rfc_dataEntry_t *)rx_buf_2)->status = DATA_ENTRY_STATUS_PENDING;
((rfc_dataEntry_t *)rx_buf_3)->status = DATA_ENTRY_STATUS_PENDING;
if(((rfc_dataEntry_t *)rx_buf_0)->status == DATA_ENTRY_STATUS_BUSY) {
((rfc_dataEntry_t *)rx_buf_0)->status = DATA_ENTRY_STATUS_PENDING;
}
if(((rfc_dataEntry_t *)rx_buf_1)->status == DATA_ENTRY_STATUS_BUSY) {
((rfc_dataEntry_t *)rx_buf_1)->status = DATA_ENTRY_STATUS_PENDING;
}
if(((rfc_dataEntry_t *)rx_buf_2)->status == DATA_ENTRY_STATUS_BUSY) {
((rfc_dataEntry_t *)rx_buf_2)->status = DATA_ENTRY_STATUS_PENDING;
}
if(((rfc_dataEntry_t *)rx_buf_3)->status == DATA_ENTRY_STATUS_BUSY) {
((rfc_dataEntry_t *)rx_buf_3)->status = DATA_ENTRY_STATUS_PENDING;
}
return RF_CORE_CMD_OK;
}
/*---------------------------------------------------------------------------*/
/* Enable or disable CCA before sending */
static radio_result_t
set_send_on_cca(uint8_t enable)
{
if(enable) {
/* this driver does not have support for CCA on Tx */
return RADIO_RESULT_NOT_SUPPORTED;
}
return RADIO_RESULT_OK;
}
/*---------------------------------------------------------------------------*/
static radio_result_t
get_value(radio_param_t param, radio_value_t *value)
{
@ -1162,7 +1353,13 @@ get_value(radio_param_t param, radio_value_t *value)
if(cmd->frameFiltOpt.autoAckEn) {
*value |= RADIO_RX_MODE_AUTOACK;
}
if(poll_mode) {
*value |= RADIO_RX_MODE_POLL_MODE;
}
return RADIO_RESULT_OK;
case RADIO_PARAM_TX_MODE:
*value = 0;
return RADIO_RESULT_OK;
case RADIO_PARAM_TXPOWER:
*value = get_tx_power();
@ -1190,6 +1387,12 @@ get_value(radio_param_t param, radio_value_t *value)
case RADIO_CONST_TXPOWER_MAX:
*value = OUTPUT_POWER_MAX;
return RADIO_RESULT_OK;
case RADIO_PARAM_LAST_RSSI:
*value = last_rssi;
return RADIO_RESULT_OK;
case RADIO_PARAM_LAST_LINK_QUALITY:
*value = last_corr_lqi;
return RADIO_RESULT_OK;
default:
return RADIO_RESULT_NOT_SUPPORTED;
}
@ -1198,9 +1401,9 @@ get_value(radio_param_t param, radio_value_t *value)
static radio_result_t
set_value(radio_param_t param, radio_value_t value)
{
uint8_t was_off = 0;
radio_result_t rv = RADIO_RESULT_OK;
rfc_CMD_IEEE_RX_t *cmd = (rfc_CMD_IEEE_RX_t *)cmd_ieee_rx_buf;
uint8_t old_poll_mode;
switch(param) {
case RADIO_PARAM_POWER_MODE:
@ -1222,6 +1425,7 @@ set_value(radio_param_t param, radio_value_t value)
return RADIO_RESULT_INVALID_VALUE;
}
/* Note: this return may lead to long periods when RAT and RTC are not resynchronized */
if(cmd->channel == (uint8_t)value) {
/* We already have that very same channel configured.
* Nothing to do here. */
@ -1239,7 +1443,7 @@ set_value(radio_param_t param, radio_value_t value)
case RADIO_PARAM_RX_MODE:
{
if(value & ~(RADIO_RX_MODE_ADDRESS_FILTER |
RADIO_RX_MODE_AUTOACK)) {
RADIO_RX_MODE_AUTOACK | RADIO_RX_MODE_POLL_MODE)) {
return RADIO_RESULT_INVALID_VALUE;
}
@ -1252,8 +1456,30 @@ set_value(radio_param_t param, radio_value_t value)
cmd->frameFiltOpt.bPendDataReqOnly = 0;
cmd->frameFiltOpt.bPanCoord = 0;
cmd->frameFiltOpt.bStrictLenFilter = 0;
old_poll_mode = poll_mode;
poll_mode = (value & RADIO_RX_MODE_POLL_MODE) != 0;
if(poll_mode == old_poll_mode) {
uint32_t cmd_status;
/* do not turn the radio on and off, just send an update command */
memcpy(&filter_cmd.newFrameFiltOpt, &cmd->frameFiltOpt, sizeof(cmd->frameFiltOpt));
if(rf_core_send_cmd((uint32_t)&filter_cmd, &cmd_status) == RF_CORE_CMD_ERROR) {
PRINTF("setting address filter failed: CMDSTA=0x%08lx\n", cmd_status);
return RADIO_RESULT_ERROR;
}
return RADIO_RESULT_OK;
}
break;
}
case RADIO_PARAM_TX_MODE:
if(value & ~(RADIO_TX_MODE_SEND_ON_CCA)) {
return RADIO_RESULT_INVALID_VALUE;
}
return set_send_on_cca((value & RADIO_TX_MODE_SEND_ON_CCA) != 0);
case RADIO_PARAM_TXPOWER:
if(value < OUTPUT_POWER_MIN || value > OUTPUT_POWER_MAX) {
return RADIO_RESULT_INVALID_VALUE;
@ -1262,37 +1488,37 @@ set_value(radio_param_t param, radio_value_t value)
set_tx_power(value);
return RADIO_RESULT_OK;
case RADIO_PARAM_CCA_THRESHOLD:
cmd->ccaRssiThr = (int8_t)value;
break;
default:
return RADIO_RESULT_NOT_SUPPORTED;
}
/* If we reach here we had no errors. Apply new settings */
/* If off, the new configuration will be applied the next time radio is started */
if(!rf_is_on()) {
was_off = 1;
if(on() != RF_CORE_CMD_OK) {
PRINTF("set_value: on() failed (2)\n");
return RADIO_RESULT_ERROR;
}
return RADIO_RESULT_OK;
}
/* If we reach here we had no errors. Apply new settings */
if(rx_off() != RF_CORE_CMD_OK) {
PRINTF("set_value: rx_off() failed\n");
rv = RADIO_RESULT_ERROR;
}
/* Restart the radio timer (RAT).
This causes resynchronization between RAT and RTC: useful for TSCH. */
rf_core_restart_rat();
check_rat_overflow(false);
if(rx_on() != RF_CORE_CMD_OK) {
PRINTF("set_value: rx_on() failed\n");
rv = RADIO_RESULT_ERROR;
}
/* If we were off, turn back off */
if(was_off) {
off();
}
return rv;
}
/*---------------------------------------------------------------------------*/
@ -1318,13 +1544,22 @@ get_object(radio_param_t param, void *dest, size_t size)
return RADIO_RESULT_OK;
}
if(param == RADIO_PARAM_LAST_PACKET_TIMESTAMP) {
if(size != sizeof(rtimer_clock_t) || !dest) {
return RADIO_RESULT_INVALID_VALUE;
}
*(rtimer_clock_t *)dest = last_packet_timestamp;
return RADIO_RESULT_OK;
}
return RADIO_RESULT_NOT_SUPPORTED;
}
/*---------------------------------------------------------------------------*/
static radio_result_t
set_object(radio_param_t param, const void *src, size_t size)
{
uint8_t was_off = 0;
radio_result_t rv;
int i;
uint8_t *dst;
@ -1341,12 +1576,9 @@ set_object(radio_param_t param, const void *src, size_t size)
dst[i] = ((uint8_t *)src)[7 - i];
}
/* If off, the new configuration will be applied the next time radio is started */
if(!rf_is_on()) {
was_off = 1;
if(on() != RF_CORE_CMD_OK) {
PRINTF("set_object: on() failed\n");
return RADIO_RESULT_ERROR;
}
return RADIO_RESULT_OK;
}
if(rx_off() != RF_CORE_CMD_OK) {
@ -1359,11 +1591,6 @@ set_object(radio_param_t param, const void *src, size_t size)
rv = RADIO_RESULT_ERROR;
}
/* If we were off, turn back off */
if(was_off) {
off();
}
return rv;
}
return RADIO_RESULT_NOT_SUPPORTED;

View file

@ -634,7 +634,7 @@ prepare(const void *payload, unsigned short payload_len)
int len = MIN(payload_len, TX_BUF_PAYLOAD_LEN);
memcpy(&tx_buf[TX_BUF_HDR_LEN], payload, len);
return RF_CORE_CMD_OK;
return 0;
}
/*---------------------------------------------------------------------------*/
static int
@ -683,7 +683,7 @@ transmit(unsigned short transmit_len)
rx_off_prop();
/* Enable the LAST_COMMAND_DONE interrupt to wake us up */
rf_core_cmd_done_en(false);
rf_core_cmd_done_en(false, false);
ret = rf_core_send_cmd((uint32_t)cmd_tx_adv, &cmd_status);
@ -728,7 +728,7 @@ transmit(unsigned short transmit_len)
* Disable LAST_FG_COMMAND_DONE interrupt. We don't really care about it
* except when we are transmitting
*/
rf_core_cmd_done_dis();
rf_core_cmd_done_dis(false);
/* Workaround. Set status to IDLE */
cmd_tx_adv->status = RF_CORE_RADIO_OP_STATUS_IDLE;
@ -933,7 +933,7 @@ on(void)
}
}
rf_core_setup_interrupts();
rf_core_setup_interrupts(false);
/*
* Trigger a switch to the XOSC, so that we can subsequently use the RF FS

View file

@ -92,6 +92,8 @@
#define ENABLED_IRQS (RX_FRAME_IRQ | ERROR_IRQ)
#endif
#define ENABLED_IRQS_POLL_MODE (ENABLED_IRQS & ~(RX_FRAME_IRQ | ERROR_IRQ))
#define cc26xx_rf_cpe0_isr RFCCPE0IntHandler
#define cc26xx_rf_cpe1_isr RFCCPE1IntHandler
/*---------------------------------------------------------------------------*/
@ -101,6 +103,10 @@ static rfc_radioOp_t *last_radio_op = NULL;
/* A struct holding pointers to the primary mode's abort() and restore() */
static const rf_core_primary_mode_t *primary_mode = NULL;
/*---------------------------------------------------------------------------*/
/* Radio timer (RAT) offset as compared to the rtimer counter (RTC) */
int32_t rat_offset = 0;
static bool rat_offset_known = false;
/*---------------------------------------------------------------------------*/
PROCESS(rf_core_process, "CC13xx / CC26xx RF driver");
/*---------------------------------------------------------------------------*/
#define RF_CORE_CLOCKS_MASK (RFC_PWR_PWMCLKEN_RFC_M | RFC_PWR_PWMCLKEN_CPE_M \
@ -265,6 +271,66 @@ rf_core_power_up()
return RF_CORE_CMD_OK;
}
/*---------------------------------------------------------------------------*/
uint8_t
rf_core_start_rat(void)
{
uint32_t cmd_status;
rfc_CMD_SYNC_START_RAT_t cmd_start;
/* Start radio timer (RAT) */
rf_core_init_radio_op((rfc_radioOp_t *)&cmd_start, sizeof(cmd_start), CMD_SYNC_START_RAT);
/* copy the value and send back */
cmd_start.rat0 = rat_offset;
if(rf_core_send_cmd((uint32_t)&cmd_start, &cmd_status) != RF_CORE_CMD_OK) {
PRINTF("rf_core_get_rat_rtc_offset: SYNC_START_RAT fail, CMDSTA=0x%08lx\n",
cmd_status);
return RF_CORE_CMD_ERROR;
}
/* Wait until done (?) */
if(rf_core_wait_cmd_done(&cmd_start) != RF_CORE_CMD_OK) {
PRINTF("rf_core_cmd_ok: SYNC_START_RAT wait, CMDSTA=0x%08lx, status=0x%04x\n",
cmd_status, cmd_start.status);
return RF_CORE_CMD_ERROR;
}
return RF_CORE_CMD_OK;
}
/*---------------------------------------------------------------------------*/
uint8_t
rf_core_stop_rat(void)
{
rfc_CMD_SYNC_STOP_RAT_t cmd_stop;
uint32_t cmd_status;
rf_core_init_radio_op((rfc_radioOp_t *)&cmd_stop, sizeof(cmd_stop), CMD_SYNC_STOP_RAT);
int ret = rf_core_send_cmd((uint32_t)&cmd_stop, &cmd_status);
if(ret != RF_CORE_CMD_OK) {
PRINTF("rf_core_get_rat_rtc_offset: SYNC_STOP_RAT fail, ret %d CMDSTA=0x%08lx\n",
ret, cmd_status);
return ret;
}
/* Wait until done */
ret = rf_core_wait_cmd_done(&cmd_stop);
if(ret != RF_CORE_CMD_OK) {
PRINTF("rf_core_cmd_ok: SYNC_STOP_RAT wait, CMDSTA=0x%08lx, status=0x%04x\n",
cmd_status, cmd_stop.status);
return ret;
}
if(!rat_offset_known) {
/* save the offset, but only if this is the first time */
rat_offset_known = true;
rat_offset = cmd_stop.rat0;
}
return RF_CORE_CMD_OK;
}
/*---------------------------------------------------------------------------*/
void
rf_core_power_down()
{
@ -280,6 +346,8 @@ rf_core_power_down()
fs_powerdown();
}
rf_core_stop_rat();
/* Shut down the RFCORE clock domain in the MCU VD */
ti_lib_prcm_domain_disable(PRCM_DOMAIN_RFCORE);
ti_lib_prcm_load_set();
@ -329,22 +397,6 @@ rf_core_set_modesel()
}
/*---------------------------------------------------------------------------*/
uint8_t
rf_core_start_rat()
{
uint32_t cmd_status;
/* Start radio timer (RAT) */
if(rf_core_send_cmd(CMDR_DIR_CMD(CMD_START_RAT), &cmd_status)
!= RF_CORE_CMD_OK) {
PRINTF("rf_core_apply_patches: START_RAT fail, CMDSTA=0x%08lx\n",
cmd_status);
return RF_CORE_CMD_ERROR;
}
return RF_CORE_CMD_OK;
}
/*---------------------------------------------------------------------------*/
uint8_t
rf_core_boot()
{
if(rf_core_power_up() != RF_CORE_CMD_OK) {
@ -366,10 +418,31 @@ rf_core_boot()
return RF_CORE_CMD_OK;
}
/*---------------------------------------------------------------------------*/
uint8_t
rf_core_restart_rat(void)
{
if(rf_core_stop_rat() != RF_CORE_CMD_OK) {
PRINTF("rf_core_restart_rat: rf_core_stop_rat() failed\n");
return RF_CORE_CMD_ERROR;
}
if(rf_core_start_rat() != RF_CORE_CMD_OK) {
PRINTF("rf_core_restart_rat: rf_core_start_rat() failed\n");
rf_core_power_down();
return RF_CORE_CMD_ERROR;
}
return RF_CORE_CMD_OK;
}
/*---------------------------------------------------------------------------*/
void
rf_core_setup_interrupts()
rf_core_setup_interrupts(bool poll_mode)
{
bool interrupts_disabled;
const uint32_t enabled_irqs = poll_mode ? ENABLED_IRQS_POLL_MODE : ENABLED_IRQS;
/* We are already turned on by the caller, so this should not happen */
if(!rf_core_is_accessible()) {
@ -384,7 +457,7 @@ rf_core_setup_interrupts()
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEISL) = ERROR_IRQ;
/* Acknowledge configured interrupts */
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = ENABLED_IRQS;
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = enabled_irqs;
/* Clear interrupt flags, active low clear(?) */
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x0;
@ -400,18 +473,20 @@ rf_core_setup_interrupts()
}
/*---------------------------------------------------------------------------*/
void
rf_core_cmd_done_en(bool fg)
rf_core_cmd_done_en(bool fg, bool poll_mode)
{
uint32_t irq = fg ? IRQ_LAST_FG_COMMAND_DONE : IRQ_LAST_COMMAND_DONE;
const uint32_t enabled_irqs = poll_mode ? ENABLED_IRQS_POLL_MODE : ENABLED_IRQS;
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = ENABLED_IRQS;
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = ENABLED_IRQS | irq;
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = enabled_irqs;
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = enabled_irqs | irq;
}
/*---------------------------------------------------------------------------*/
void
rf_core_cmd_done_dis()
rf_core_cmd_done_dis(bool poll_mode)
{
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = ENABLED_IRQS;
const uint32_t enabled_irqs = poll_mode ? ENABLED_IRQS_POLL_MODE : ENABLED_IRQS;
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = enabled_irqs;
}
/*---------------------------------------------------------------------------*/
rfc_radioOp_t *

View file

@ -228,6 +228,9 @@ typedef struct rf_core_primary_mode_s {
#define RF_CORE_COMMAND_PROTOCOL_IEEE 0x2000
#define RF_CORE_COMMAND_PROTOCOL_PROP 0x3000
/*---------------------------------------------------------------------------*/
/* Radio timer register */
#define RATCNT 0x00000004
/*---------------------------------------------------------------------------*/
/* Make the main driver process visible to mode drivers */
PROCESS_NAME(rf_core_process);
/*---------------------------------------------------------------------------*/
@ -310,6 +313,24 @@ uint8_t rf_core_set_modesel(void);
*/
uint8_t rf_core_start_rat(void);
/**
* \brief Stop the CM0 RAT synchronously
* \return RF_CORE_CMD_OK or RF_CORE_CMD_ERROR
*
* This function is not strictly necessary, but through calling it it's possibly
* to learn the RAT / RTC offset, which useful to get accurate radio timestamps.
*/
uint8_t rf_core_stop_rat(void);
/**
* \brief Restart the CM0 RAT
* \return RF_CORE_CMD_OK or RF_CORE_CMD_ERROR
*
* This function restarts the CM0 RAT and therefore resynchornizes it with RTC.
* To achieve good timing accuracy, it should be called periodically.
*/
uint8_t rf_core_restart_rat(void);
/**
* \brief Boot the RF Core
* \return RF_CORE_CMD_OK or RF_CORE_CMD_ERROR
@ -327,19 +348,20 @@ uint8_t rf_core_boot(void);
/**
* \brief Setup RF core interrupts
*/
void rf_core_setup_interrupts(void);
void rf_core_setup_interrupts(bool poll_mode);
/**
* \brief Enable interrupt on command done.
* \param fg set true to enable irq on foreground command done and false for
* background commands or if not in ieee mode.
* \param poll_mode true if the driver is in poll mode
*
* This is used within TX routines in order to be able to sleep the CM3 and
* wake up after TX has finished
*
* \sa rf_core_cmd_done_dis()
*/
void rf_core_cmd_done_en(bool fg);
void rf_core_cmd_done_en(bool fg, bool poll_mode);
/**
* \brief Disable the LAST_CMD_DONE and LAST_FG_CMD_DONE interrupts.
@ -348,7 +370,7 @@ void rf_core_cmd_done_en(bool fg);
*
* \sa rf_core_cmd_done_en()
*/
void rf_core_cmd_done_dis(void);
void rf_core_cmd_done_dis(bool poll_mode);
/**
* \brief Returns a pointer to the most recent proto-dependent Radio Op

View file

@ -50,6 +50,21 @@
#define RTIMER_ARCH_SECOND 65536
/*---------------------------------------------------------------------------*/
rtimer_clock_t rtimer_arch_now(void);
/* HW oscillator frequency is 32 kHz, not 64 kHz and RTIMER_NOW() never returns
* an odd value; so US_TO_RTIMERTICKS always rounds to the nearest even number.
*/
#define US_TO_RTIMERTICKS(US) (2 * ((US) >= 0 ? \
(((int32_t)(US) * (RTIMER_ARCH_SECOND / 2) + 500000) / 1000000L) : \
((int32_t)(US) * (RTIMER_ARCH_SECOND / 2) - 500000) / 1000000L))
#define RTIMERTICKS_TO_US(T) ((T) >= 0 ? \
(((int32_t)(T) * 1000000L + ((RTIMER_ARCH_SECOND) / 2)) / (RTIMER_ARCH_SECOND)) : \
((int32_t)(T) * 1000000L - ((RTIMER_ARCH_SECOND) / 2)) / (RTIMER_ARCH_SECOND))
/* A 64-bit version because the 32-bit one cannot handle T >= 4295 ticks.
Intended only for positive values of T. */
#define RTIMERTICKS_TO_US_64(T) ((uint32_t)(((uint64_t)(T) * 1000000 + ((RTIMER_ARCH_SECOND) / 2)) / (RTIMER_ARCH_SECOND)))
/*---------------------------------------------------------------------------*/
#endif /* RTIMER_ARCH_H_ */
/*---------------------------------------------------------------------------*/

View file

@ -329,6 +329,55 @@ typedef uint32_t uip_stats_t;
*/
typedef uint32_t rtimer_clock_t;
#define RTIMER_CLOCK_DIFF(a, b) ((int32_t)((a) - (b)))
/* --------------------------------------------------------------------- */
/* TSCH related defines */
/* Delay between GO signal and SFD */
#define RADIO_DELAY_BEFORE_TX ((unsigned)US_TO_RTIMERTICKS(81))
/* Delay between GO signal and start listening.
* This value is so small because the radio is constantly on within each timeslot. */
#define RADIO_DELAY_BEFORE_RX ((unsigned)US_TO_RTIMERTICKS(15))
/* Delay between the SFD finishes arriving and it is detected in software.
* Not important on this platform as it uses hardware timestamps for SFD */
#define RADIO_DELAY_BEFORE_DETECT ((unsigned)US_TO_RTIMERTICKS(0))
/* Timer conversion; radio is running at 4 MHz */
#define RADIO_TIMER_SECOND 4000000u
#if (RTIMER_SECOND % 256) || (RADIO_TIMER_SECOND % 256)
#error RADIO_TO_RTIMER macro must be fixed!
#endif
#define RADIO_TO_RTIMER(X) ((uint32_t)(((uint64_t)(X) * (RTIMER_SECOND / 256)) / (RADIO_TIMER_SECOND / 256)))
#define USEC_TO_RADIO(X) ((X) * 4)
/* The PHY header (preamble + SFD, 4+1 bytes) duration is equivalent to 10 symbols */
#define RADIO_IEEE_802154_PHY_HEADER_DURATION_USEC 160
/* Do not turn off TSCH within a timeslot: not enough time */
#define TSCH_CONF_RADIO_ON_DURING_TIMESLOT 1
/* Disable TSCH frame filtering */
#define TSCH_CONF_HW_FRAME_FILTERING 0
/* Disable turning off HF oscillator when radio is off;
enable this for TSCH, disable to save more energy. */
#ifndef CC2650_FAST_RADIO_STARTUP
#define CC2650_FAST_RADIO_STARTUP 1
#endif
/* Use hardware timestamps */
#ifndef TSCH_CONF_RESYNC_WITH_SFD_TIMESTAMPS
#define TSCH_CONF_RESYNC_WITH_SFD_TIMESTAMPS 1
#define TSCH_CONF_TIMESYNC_REMOVE_JITTER 0
#endif
/* The drift compared to "true" 10ms slots.
Enable adaptive sync to enable compensation for this. */
#define TSCH_CONF_BASE_DRIFT_PPM -977
/* 10 times per second */
#define TSCH_CONF_ASSOCIATION_CHANNEL_SWITCH_FREQUENCY 10
/** @} */
/*---------------------------------------------------------------------------*/
/* board.h assumes that basic configuration is done */

View file

@ -59,6 +59,7 @@
#include "uart.h"
#include "sys/clock.h"
#include "sys/rtimer.h"
#include "sys/node-id.h"
#include "lib/sensors.h"
#include "button-sensor.h"
#include "dev/serial-line.h"
@ -68,6 +69,8 @@
#include <stdio.h>
/*---------------------------------------------------------------------------*/
unsigned short node_id = 0;
/*---------------------------------------------------------------------------*/
/** \brief Board specific iniatialisation */
void board_init(void);
/*---------------------------------------------------------------------------*/
@ -123,6 +126,10 @@ set_rf_params(void)
printf("%02x\n", linkaddr_node_addr.u8[i]);
}
#endif
/* also set the global node id */
node_id = short_addr;
printf(" Node ID: %d\n", node_id);
}
/*---------------------------------------------------------------------------*/
/**
@ -142,6 +149,11 @@ main(void)
/* Set the LF XOSC as the LF system clock source */
oscillators_select_lf_xosc();
#if CC2650_FAST_RADIO_STARTUP
/* Also request HF XOSC to start up */
oscillators_request_hf_xosc();
#endif
lpm_init();
board_init();