[efr32] add sleepy demo for efr32mg21 (#4138)

Sleepy Demo added to the efr32mg21 directory.

Updated efr32mg21 alarm.c to use changes for efr32mg12 in commit 89dca58.
This commit is contained in:
Marven Gilhespie
2019-09-04 20:02:58 +01:00
committed by Jonathan Hui
parent ef4adde7be
commit 87105e0694
15 changed files with 1594 additions and 240 deletions
+3
View File
@@ -1075,6 +1075,9 @@ examples/platforms/efr32mg12/sleepy-demo/Makefile
examples/platforms/efr32mg12/sleepy-demo/sleepy-demo-ftd/Makefile
examples/platforms/efr32mg12/sleepy-demo/sleepy-demo-mtd/Makefile
examples/platforms/efr32mg21/Makefile
examples/platforms/efr32mg21/sleepy-demo/Makefile
examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-ftd/Makefile
examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-mtd/Makefile
examples/platforms/gp712/Makefile
examples/platforms/kw41z/Makefile
examples/platforms/nrf52811/Makefile
+1 -1
View File
@@ -222,7 +222,7 @@ static void efr32RailConfigLoad(efr32BandConfig *aBandConfig)
assert(status == RAIL_STATUS_NO_ERROR);
}
static void efr32RadioSetTxPower(uint8_t aPowerDbm)
static void efr32RadioSetTxPower(int8_t aPowerDbm)
{
RAIL_Status_t status;
RAIL_TxPowerCurvesConfig_t txPowerCurvesConfig = {curves24Hp, curvesSg, curves24Lp, piecewiseSegments};
+27 -2
View File
@@ -45,6 +45,13 @@ EFR32MG_SDK_SRCDIR = $(top_srcdir)/third_party/silabs
libopenthread_efr32mg21_a_CPPFLAGS = \
-D__START=main \
-D__STARTUP_CLEAR_BSS \
-DPLATFORM_HEADER=\"@top_builddir@/third_party/silabs/gecko_sdk_suite/v2.6/platform/base/hal/micro/cortexm3/compiler/gcc.h\" \
-Wno-sign-compare \
-DCORTEXM3 \
-DPHY=EMBER_PHY_RAIL \
-DMICRO=EMBER_MICRO_CORTEXM3_EFR32 \
-DCORTEXM3_EFM32_MICRO \
-DPLAT=EMBER_PLATFORM_CORTEXM3 \
-I$(top_srcdir)/include \
-I$(top_srcdir)/examples/platforms \
-I$(top_srcdir)/examples/platforms/efr32mg21/$(EFR32_BOARD_DIR) \
@@ -60,6 +67,11 @@ libopenthread_efr32mg21_a_CPPFLAGS =
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/plugin/pa-conversions \
-I$(EFR32MG_SDK_SRCDIR)/hardware/kit/common/bsp \
-I$(EFR32MG_SDK_SRCDIR)/hardware/kit/EFR32MG21_$(BOARD)/config \
-I$(EFR32MG_SDK_SRCDIR)/platform/base/ \
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal \
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/micro/cortexm3/efm32 \
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/micro/cortexm3/efm32/config \
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/plugin \
-I$(EFR32MG_SDK_SRCDIR)/platform/CMSIS/Include \
-I$(EFR32MG_SDK_SRCDIR)/platform/Device/SiliconLabs/EFR32MG21/Include \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/common/inc \
@@ -86,8 +98,10 @@ PLATFORM_SOURCES =
logging.c \
misc.c \
openthread-core-efr32-config.h \
openthread-core-efr32-config-check.h \
platform-efr32.h \
platform-band.h \
radio.c \
rail_config.h \
startup-gcc.c \
system.c \
uart.c \
@@ -104,7 +118,6 @@ libopenthread_efr32mg21_a_SOURCES =
PRETTY_FILES = \
$(PLATFORM_SOURCES) \
$(noinst_HEADERS) \
$(NULL)
Dash = -
@@ -112,4 +125,16 @@ libopenthread_efr32mg21_a_LIBADD
$(shell find $(top_builddir)/examples/platforms/utils $(Dash)type f $(Dash)name "*.o") \
$(shell find $(top_builddir)/third_party/jlink/SEGGER_RTT_V640/RTT $(Dash)type f $(Dash)name "*.o")
DIST_SUBDIRS = \
sleepy-demo \
$(NULL)
SUBDIRS = \
sleepy-demo \
$(NULL)
PRETTY_SUBDIRS = \
sleepy-demo \
$(NULL)
include $(abs_top_nlbuild_autotools_dir)/automake/post.am
+109 -27
View File
@@ -32,13 +32,17 @@
*
*/
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include "openthread-system.h"
#include <openthread/config.h>
#include <openthread/platform/alarm-milli.h>
#include <openthread/platform/diag.h>
#include "common/logging.hpp"
#include "platform-efr32.h"
#include "utils/code_utils.h"
#include "em_core.h"
@@ -47,12 +51,25 @@
#define XTAL_ACCURACY 200
#define US_IN_MS 1000
// Minimum duration of an alarm in milliseconds. Used to avoid setting the absolute
// expiry time of an alarm to the current time or slightly in the past.
#define TIMER_EPSILON_MS 1
// The longest Rail can set a timer is 53 minutes. Timers of a longer duration
// must wake up before this and set another timer for the remainder. We currently
// split long delays in 30 minute intervals using a value of 1800000.
#define RAIL_TIMER_MAX_DELTA_MS 1800000
static uint32_t sTimerHi = 0;
static uint32_t sTimerLo = 0;
static uint32_t sAlarmT0 = 0;
static uint32_t sAlarmDt = 0;
static bool sIsRunning = false;
static void RAILCb_TimerExpired(RAIL_Handle_t aHandle)
{
}
void efr32AlarmInit(void)
{
}
@@ -94,9 +111,47 @@ uint32_t otPlatTimeGetXtalAccuracy(void)
void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t t0, uint32_t dt)
{
OT_UNUSED_VARIABLE(aInstance);
uint32_t expires_microsec;
RAIL_Status_t status;
assert(gRailHandle != NULL);
if (sIsRunning)
{
RAIL_CancelTimer(gRailHandle);
}
sAlarmT0 = t0;
sAlarmDt = dt;
if (dt > RAIL_TIMER_MAX_DELTA_MS)
{
dt = RAIL_TIMER_MAX_DELTA_MS;
}
else if (dt < TIMER_EPSILON_MS)
{
dt = TIMER_EPSILON_MS;
}
expires_microsec = (t0 + dt) * US_IN_MS;
status = RAIL_SetTimer(gRailHandle, expires_microsec, RAIL_TIME_ABSOLUTE, RAILCb_TimerExpired);
if (status != RAIL_STATUS_NO_ERROR)
{
// The RAIL timer could not be set due to expiration time being in the past with respect to RAIL's current
// time which is in microseconds. We fallback to using a relative timer from the current time.
expires_microsec = dt * US_IN_MS;
status = RAIL_SetTimer(gRailHandle, expires_microsec, RAIL_TIME_DELAY, RAILCb_TimerExpired);
if (status != RAIL_STATUS_NO_ERROR)
{
otLogCritPlat("Alarm start timer failed, status: %d, dt: %u, t0: %u, now: %u", status, dt, t0,
otPlatAlarmMilliGetNow());
assert(false);
}
}
sAlarmT0 = t0;
sAlarmDt = dt;
sIsRunning = true;
}
@@ -105,48 +160,75 @@ void otPlatAlarmMilliStop(otInstance *aInstance)
OT_UNUSED_VARIABLE(aInstance);
sIsRunning = false;
assert(gRailHandle != NULL);
RAIL_CancelTimer(gRailHandle);
}
void efr32AlarmProcess(otInstance *aInstance)
{
uint32_t now = otPlatAlarmMilliGetNow();
uint32_t expires;
bool fire = false;
uint32_t now;
uint32_t new_expires_microsec;
uint32_t dt;
RAIL_Status_t status;
otEXPECT(sIsRunning);
expires = sAlarmT0 + sAlarmDt;
assert(gRailHandle != NULL);
if (sAlarmT0 <= now)
{
fire = (expires >= sAlarmT0 && expires <= now);
}
else
{
fire = (expires >= sAlarmT0 || expires <= now);
}
if (fire)
if (RAIL_IsTimerExpired(gRailHandle))
{
sIsRunning = false;
#if OPENTHREAD_CONFIG_DIAG_ENABLE
if (otPlatDiagModeGet())
if (sAlarmDt > RAIL_TIMER_MAX_DELTA_MS)
{
otPlatDiagAlarmFired(aInstance);
// We split longer delays in two due to the maximum allowed timer in RAIL. Here we
// re-arm the RAIL timer with the remaining part of the alarm.
now = otPlatAlarmMilliGetNow();
dt = (sAlarmT0 + sAlarmDt) - now;
if (dt > RAIL_TIMER_MAX_DELTA_MS)
{
dt = RAIL_TIMER_MAX_DELTA_MS;
}
else if (dt < TIMER_EPSILON_MS)
{
dt = TIMER_EPSILON_MS;
}
new_expires_microsec = (now + dt) * US_IN_MS;
status = RAIL_SetTimer(gRailHandle, new_expires_microsec, RAIL_TIME_ABSOLUTE, RAILCb_TimerExpired);
if (status != RAIL_STATUS_NO_ERROR)
{
new_expires_microsec = dt * US_IN_MS;
status = RAIL_SetTimer(gRailHandle, new_expires_microsec, RAIL_TIME_DELAY, RAILCb_TimerExpired);
if (status != RAIL_STATUS_NO_ERROR)
{
otLogCritPlat("Alarm extend timer failed, status: %d, dt: %u, now: %u", status, dt,
otPlatAlarmMilliGetNow());
assert(false);
}
}
sIsRunning = true;
}
else
#endif
{
otPlatAlarmMilliFired(aInstance);
#if OPENTHREAD_CONFIG_DIAG_ENABLE
if (otPlatDiagModeGet())
{
otPlatDiagAlarmFired(aInstance);
}
else
#endif
{
otPlatAlarmMilliFired(aInstance);
}
}
}
exit:
return;
}
void RAILCb_TimerExpired(void)
{
}
+9 -4
View File
@@ -41,13 +41,18 @@
#include "rail_config.h"
#include "rail_ieee802154.h"
#define RAIL_TX_FIFO_SIZE (OT_RADIO_FRAME_MAX_SIZE + 1)
typedef struct efr32CommonConfig
{
RAIL_Config_t mRailConfig;
uint8_t
mRailTxFifo[RAIL_TX_FIFO_SIZE]; // must be 2 power between 64 and 4096, and bigger than OT_RADIO_FRAME_MAX_SIZE
} efr32CommonConfig;
typedef struct efr32BandConfig
{
RAIL_Handle_t mRailHandle;
RAIL_Config_t mRailConfig;
RAILSched_Config_t mRailSchedState;
const RAIL_ChannelConfig_t *mChannelConfig;
uint8_t mRailTxFifo[OT_RADIO_FRAME_MAX_SIZE + 1];
uint8_t mChannelMin;
uint8_t mChannelMax;
} efr32BandConfig;
@@ -41,10 +41,14 @@
#include "em_system.h"
#include "core_cm33.h"
#include "rail.h"
// Global OpenThread instance structure
extern otInstance *sInstance;
// Global reference to rail handle
extern RAIL_Handle_t gRailHandle;
/**
* This function initializes the alarm service used by OpenThread.
*
@@ -109,4 +113,26 @@ void efr32LogInit(void);
*/
void efr32LogDeinit(void);
/**
* Registers the sleep callback handler. The callback is used to check that
* the application has no work pending and that it is safe to put the EFR32
* into a low energy sleep mode.
*
* The callback should return true if it is ok to enter sleep mode. Note
* that the callback itself is run with interrupts disabled and so should
* be kept as short as possible. Anny interrupt including those from timers
* will wake the EFR32 out of sleep mode.
*
* @param[in] aCallback Callback function.
*
*/
void efr32SetSleepCallback(bool (*aCallback)(void), void (*aCallbackWake)(void));
/**
* Put the EFR32 into a low power mode. Before sleeping it will call a callback
* in the application registered with efr32SetSleepCallback to ensure that there
* is no outstanding work in the application to do.
*/
void efr32Sleep(void);
#endif // PLATFORM_EFR32_H_
+191 -202
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, The OpenThread Authors.
* Copyright (c) 2019, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -45,8 +45,10 @@
#include "utils/soft_source_match_table.h"
#include "board_config.h"
#include "em_cmu.h"
#include "em_core.h"
#include "em_system.h"
#include "hal-config.h"
#include "openthread-core-efr32-config.h"
#include "pa_conversions_efr32.h"
#include "platform-band.h"
@@ -69,7 +71,6 @@ enum
enum
{
EFR32_RECEIVE_SENSITIVITY = -100, // dBm
EFR32_RSSI_AVERAGING_TIME = 16, // us
EFR32_RSSI_AVERAGING_TIMEOUT = 300, // us
};
@@ -98,6 +99,8 @@ typedef enum
ENERGY_SCAN_MODE_ASYNC
} energyScanMode;
RAIL_Handle_t gRailHandle;
static volatile bool sTransmitBusy = false;
static bool sPromiscuous = false;
static bool sIsSrcMatchEnabled = false;
@@ -111,7 +114,8 @@ static otRadioFrame sTransmitFrame;
static uint8_t sTransmitPsdu[IEEE802154_MAX_LENGTH];
static volatile otError sTransmitError;
static efr32BandConfig sBandConfigs[EFR32_NUM_BAND_CONFIGS];
static efr32CommonConfig sCommonConfig;
static efr32BandConfig sBandConfigs[EFR32_NUM_BAND_CONFIGS];
static volatile energyScanStatus sEnergyScanStatus;
static volatile int8_t sEnergyScanResultDbm;
@@ -123,113 +127,99 @@ static energyScanMode sEnergyScanMode;
static void RAILCb_Generic(RAIL_Handle_t aRailHandle, RAIL_Events_t aEvents);
static const RAIL_IEEE802154_Config_t sRailIeee802154Config = {
NULL, // addresses
{
// ackConfig
true, // ackConfig.enable
894, // ackConfig.ackTimeout
.addresses = NULL,
.ackConfig =
{
// ackConfig.rxTransitions
RAIL_RF_STATE_RX, // ackConfig.rxTransitions.success
RAIL_RF_STATE_RX, // ackConfig.rxTransitions.error
.enable = true,
.ackTimeout = 894,
.rxTransitions =
{
.success = RAIL_RF_STATE_RX,
.error = RAIL_RF_STATE_RX,
},
.txTransitions =
{
.success = RAIL_RF_STATE_RX,
.error = RAIL_RF_STATE_RX,
},
},
.timings =
{
// ackConfig.txTransitions
RAIL_RF_STATE_RX, // ackConfig.txTransitions.success
RAIL_RF_STATE_RX, // ackConfig.txTransitions.error
.idleToRx = 100,
.txToRx = 192 - 10,
.idleToTx = 100,
.rxToTx = 192,
.rxSearchTimeout = 0,
.txToRxSearchTimeout = 0,
},
},
{
// timings
100, // timings.idleToRx
192 - 10, // timings.txToRx
100, // timings.idleToTx
192, // timings.rxToTx
0, // timings.rxSearchTimeout
0, // timings.txToRxSearchTimeout
},
RAIL_IEEE802154_ACCEPT_STANDARD_FRAMES, // framesMask
false, // promiscuousMode
false, // isPanCoordinator
.framesMask = RAIL_IEEE802154_ACCEPT_STANDARD_FRAMES,
.promiscuousMode = false,
.isPanCoordinator = false,
};
RAIL_DECLARE_TX_POWER_VBAT_CURVES_ALT;
static int8_t sTxPowerDbm = OPENTHREAD_CONFIG_DEFAULT_TRANSMIT_POWER;
static efr32BandConfig *sTxBandConfig = NULL;
static efr32BandConfig *sRxBandConfig = NULL;
static efr32BandConfig *sCurrentBandConfig = NULL;
static RAIL_Handle_t efr32RailConfigInit(efr32BandConfig *aBandConfig)
static RAIL_Handle_t efr32RailInit(efr32CommonConfig *aCommonConfig)
{
RAIL_Status_t status;
RAIL_Handle_t handle;
RAIL_DataConfig_t railDataConfig = {
TX_PACKET_DATA,
RX_PACKET_DATA,
PACKET_MODE,
PACKET_MODE,
};
RAIL_Status_t status;
RAIL_Handle_t handle;
handle = RAIL_Init(&aBandConfig->mRailConfig, NULL);
handle = RAIL_Init(&aCommonConfig->mRailConfig, NULL);
assert(handle != NULL);
status = RAIL_ConfigData(handle, &railDataConfig);
assert(status == RAIL_STATUS_NO_ERROR);
RAIL_Idle(handle, RAIL_IDLE, true);
status = RAIL_ConfigCal(handle, RAIL_CAL_ALL);
assert(status == RAIL_STATUS_NO_ERROR);
if (aBandConfig->mChannelConfig != NULL)
{
RAIL_ConfigChannels(handle, aBandConfig->mChannelConfig, NULL);
}
else
{
status = RAIL_IEEE802154_Config2p4GHzRadio(handle);
assert(status == RAIL_STATUS_NO_ERROR);
}
status = RAIL_IEEE802154_Init(handle, &sRailIeee802154Config);
assert(status == RAIL_STATUS_NO_ERROR);
status = RAIL_ConfigEvents(handle, RAIL_EVENTS_ALL,
RAIL_EVENT_RX_ACK_TIMEOUT | //
RAIL_EVENT_TX_PACKET_SENT | //
RAIL_EVENTS_TX_COMPLETION | //
RAIL_EVENT_RX_PACKET_RECEIVED | //
RAIL_EVENT_RSSI_AVERAGE_DONE | //
RAIL_EVENT_SCHEDULER_STATUS | //
RAIL_EVENT_TX_CHANNEL_BUSY | //
RAIL_EVENT_TX_ABORTED | //
RAIL_EVENT_TX_BLOCKED | //
RAIL_EVENT_TX_UNDERFLOW | //
RAIL_EVENT_IEEE802154_DATA_REQUEST_COMMAND | //
RAIL_EVENT_CAL_NEEDED //
);
assert(status == RAIL_STATUS_NO_ERROR);
RAIL_SetTxFifo(handle, aBandConfig->mRailTxFifo, 0, sizeof(aBandConfig->mRailTxFifo));
uint16_t actualLenth = RAIL_SetTxFifo(handle, aCommonConfig->mRailTxFifo, 0, sizeof(aCommonConfig->mRailTxFifo));
assert(actualLenth == sizeof(aCommonConfig->mRailTxFifo));
return handle;
}
static void efr32RadioSetTxPower(RAIL_Handle_t aRailHandle,
const RAIL_ChannelConfig_t *aChannelConfig,
uint8_t aPowerDbm)
static void efr32RailConfigLoad(efr32BandConfig *aBandConfig)
{
RAIL_Status_t status;
#if HAL_PA_2P4_LOWPOWER == 1
RAIL_TxPowerConfig_t txPowerConfig = {RAIL_TX_POWER_MODE_2P4_LP, HAL_PA_VOLTAGE, 10};
#else
RAIL_TxPowerConfig_t txPowerConfig = {RAIL_TX_POWER_MODE_2P4_HP, HAL_PA_VOLTAGE, 10};
#endif
(void)aBandConfig;
status = RAIL_IEEE802154_Config2p4GHzRadio(gRailHandle);
assert(status == RAIL_STATUS_NO_ERROR);
status = RAIL_ConfigTxPower(gRailHandle, &txPowerConfig);
assert(status == RAIL_STATUS_NO_ERROR);
}
static void efr32RadioSetTxPower(int8_t aPowerDbm)
{
RAIL_Status_t status;
const RAIL_TxPowerCurvesConfigAlt_t txPowerCurvesConfig = RAIL_DECLARE_TX_POWER_CURVES_CONFIG_ALT;
RAIL_TxPowerConfig_t txPowerConfig = {RAIL_TX_POWER_MODE_2P4_HP, 3300, 10};
status = RAIL_InitTxPowerCurvesAlt(&txPowerCurvesConfig);
assert(status == RAIL_STATUS_NO_ERROR);
status = RAIL_ConfigTxPower(aRailHandle, &txPowerConfig);
assert(status == RAIL_STATUS_NO_ERROR);
status = RAIL_SetTxPowerDbm(aRailHandle, ((RAIL_TxPower_t)aPowerDbm) * 10);
status = RAIL_SetTxPowerDbm(gRailHandle, ((RAIL_TxPower_t)aPowerDbm) * 10);
assert(status == RAIL_STATUS_NO_ERROR);
}
@@ -249,39 +239,55 @@ static efr32BandConfig *efr32RadioGetBandConfig(uint8_t aChannel)
return config;
}
static void efr32BandConfigInit(void (*aEventCallback)(RAIL_Handle_t railHandle, RAIL_Events_t events))
static void efr32ConfigInit(void (*aEventCallback)(RAIL_Handle_t railHandle, RAIL_Events_t events))
{
sCommonConfig.mRailConfig.eventsCallback = aEventCallback;
sCommonConfig.mRailConfig.protocol = NULL; // only used by Bluetooth stack
sCommonConfig.mRailConfig.scheduler = NULL; // only needed for DMP
uint8_t index = 0;
#if RADIO_CONFIG_2P4GHZ_OQPSK_SUPPORT
sBandConfigs[index].mRailConfig.eventsCallback = aEventCallback;
sBandConfigs[index].mRailConfig.protocol = NULL;
sBandConfigs[index].mRailConfig.scheduler = &sBandConfigs[index].mRailSchedState;
sBandConfigs[index].mChannelConfig = NULL;
sBandConfigs[index].mChannelMin = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN;
sBandConfigs[index].mChannelMax = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX;
sBandConfigs[index].mChannelConfig = NULL;
sBandConfigs[index].mChannelMin = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN;
sBandConfigs[index].mChannelMax = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX;
assert((sBandConfigs[index].mRailHandle = efr32RailConfigInit(&sBandConfigs[index])) != NULL);
index++;
#endif
gRailHandle = efr32RailInit(&sCommonConfig);
assert(gRailHandle != NULL);
efr32RailConfigLoad(&(sBandConfigs[0]));
}
void efr32RadioInit(void)
{
efr32BandConfigInit(RAILCb_Generic);
RAIL_Status_t status;
// check if RAIL_TX_FIFO_SIZE is power of two..
assert((RAIL_TX_FIFO_SIZE & (RAIL_TX_FIFO_SIZE - 1)) == 0);
// check the limits of the RAIL_TX_FIFO_SIZE.
assert((RAIL_TX_FIFO_SIZE >= 64) || (RAIL_TX_FIFO_SIZE <= 4096));
efr32ConfigInit(RAILCb_Generic);
CMU_ClockEnable(cmuClock_PRS, true);
status = RAIL_ConfigSleep(gRailHandle, RAIL_SLEEP_CONFIG_TIMERSYNC_ENABLED);
assert(status == RAIL_STATUS_NO_ERROR);
sReceiveFrame.mLength = 0;
sReceiveFrame.mPsdu = sReceivePsdu;
sTransmitFrame.mLength = 0;
sTransmitFrame.mPsdu = sTransmitPsdu;
sRxBandConfig = efr32RadioGetBandConfig(OPENTHREAD_CONFIG_DEFAULT_CHANNEL);
assert(sRxBandConfig != NULL);
sCurrentBandConfig = efr32RadioGetBandConfig(OPENTHREAD_CONFIG_DEFAULT_CHANNEL);
assert(sCurrentBandConfig != NULL);
sTxBandConfig = sRxBandConfig;
efr32RadioSetTxPower(sTxBandConfig->mRailHandle, sTxBandConfig->mChannelConfig, sTxPowerDbm);
efr32RadioSetTxPower(sTxPowerDbm);
sEnergyScanStatus = ENERGY_SCAN_STATUS_IDLE;
sTransmitError = OT_ERROR_NONE;
sTransmitBusy = false;
otLogInfoPlat("Initialized", NULL);
}
@@ -290,34 +296,36 @@ void efr32RadioDeinit(void)
{
RAIL_Status_t status;
for (uint8_t i = 0; i < EFR32_NUM_BAND_CONFIGS; i++)
{
RAIL_Idle(sBandConfigs[i].mRailHandle, RAIL_IDLE_FORCE_SHUTDOWN_CLEAR_FLAGS, true);
RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true);
status = RAIL_ConfigEvents(gRailHandle, RAIL_EVENTS_ALL, 0);
assert(status == RAIL_STATUS_NO_ERROR);
status = RAIL_IEEE802154_Deinit(sBandConfigs[i].mRailHandle);
assert(status == RAIL_STATUS_NO_ERROR);
sBandConfigs[i].mRailHandle = NULL;
}
sTxBandConfig = NULL;
sRxBandConfig = NULL;
sCurrentBandConfig = NULL;
}
static otError efr32StartEnergyScan(energyScanMode aMode, uint16_t aChannel, RAIL_Time_t aAveragingTimeUs)
{
RAIL_Status_t status;
RAIL_SchedulerInfo_t schedulerInfo = {.priority = EFR32_SCHEDULER_SAMPLE_RSSI_PRIORITY};
otError error = OT_ERROR_NONE;
RAIL_Status_t status;
otError error = OT_ERROR_NONE;
efr32BandConfig *config = NULL;
otEXPECT_ACTION(sEnergyScanStatus == ENERGY_SCAN_STATUS_IDLE, error = OT_ERROR_BUSY);
sEnergyScanStatus = ENERGY_SCAN_STATUS_IN_PROGRESS;
sEnergyScanMode = aMode;
RAIL_Idle(sRxBandConfig->mRailHandle, RAIL_IDLE, true);
RAIL_Idle(gRailHandle, RAIL_IDLE, true);
status = RAIL_StartAverageRssi(sRxBandConfig->mRailHandle, aChannel, aAveragingTimeUs, &schedulerInfo);
config = efr32RadioGetBandConfig(aChannel);
otEXPECT_ACTION(config != NULL, error = OT_ERROR_INVALID_ARGS);
if (sCurrentBandConfig != config)
{
efr32RailConfigLoad(config);
sCurrentBandConfig = config;
}
status = RAIL_StartAverageRssi(gRailHandle, aChannel, aAveragingTimeUs, NULL);
otEXPECT_ACTION(status == RAIL_STATUS_NO_ERROR, error = OT_ERROR_FAILED);
exit:
@@ -352,7 +360,7 @@ void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanId)
for (uint8_t i = 0; i < EFR32_NUM_BAND_CONFIGS; i++)
{
status = RAIL_IEEE802154_SetPanId(sBandConfigs[i].mRailHandle, aPanId, 0);
status = RAIL_IEEE802154_SetPanId(gRailHandle, aPanId, 0);
assert(status == RAIL_STATUS_NO_ERROR);
}
}
@@ -368,7 +376,7 @@ void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aA
for (uint8_t i = 0; i < EFR32_NUM_BAND_CONFIGS; i++)
{
status = RAIL_IEEE802154_SetLongAddress(sBandConfigs[i].mRailHandle, (uint8_t *)aAddress->m8, 0);
status = RAIL_IEEE802154_SetLongAddress(gRailHandle, (uint8_t *)aAddress->m8, 0);
assert(status == RAIL_STATUS_NO_ERROR);
}
}
@@ -383,7 +391,7 @@ void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress)
for (uint8_t i = 0; i < EFR32_NUM_BAND_CONFIGS; i++)
{
status = RAIL_IEEE802154_SetShortAddress(sBandConfigs[i].mRailHandle, aAddress, 0);
status = RAIL_IEEE802154_SetShortAddress(gRailHandle, aAddress, 0);
assert(status == RAIL_STATUS_NO_ERROR);
}
}
@@ -427,12 +435,9 @@ otError otPlatRadioSleep(otInstance *aInstance)
error = OT_ERROR_INVALID_STATE);
otLogInfoPlat("State=OT_RADIO_STATE_SLEEP", NULL);
sState = OT_RADIO_STATE_SLEEP;
for (uint8_t i = 0; i < EFR32_NUM_BAND_CONFIGS; i++)
{
RAIL_Idle(sBandConfigs[i].mRailHandle, RAIL_IDLE, true);
}
RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true); // abort packages under reception
sState = OT_RADIO_STATE_SLEEP;
exit:
return error;
@@ -440,10 +445,9 @@ exit:
otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
{
otError error = OT_ERROR_NONE;
RAIL_SchedulerInfo_t schedulerInfo = {.priority = EFR32_SCHEDULER_RX_PRIORITY};
RAIL_Status_t status;
efr32BandConfig * config;
otError error = OT_ERROR_NONE;
RAIL_Status_t status;
efr32BandConfig *config;
OT_UNUSED_VARIABLE(aInstance);
otEXPECT_ACTION(sState != OT_RADIO_STATE_DISABLED, error = OT_ERROR_INVALID_STATE);
@@ -451,13 +455,14 @@ otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
config = efr32RadioGetBandConfig(aChannel);
otEXPECT_ACTION(config != NULL, error = OT_ERROR_INVALID_ARGS);
if (sRxBandConfig != config)
if (sCurrentBandConfig != config)
{
RAIL_Idle(sRxBandConfig->mRailHandle, RAIL_IDLE, false);
sRxBandConfig = config;
RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true);
efr32RailConfigLoad(config);
sCurrentBandConfig = config;
}
status = RAIL_StartRx(sRxBandConfig->mRailHandle, aChannel, &schedulerInfo);
status = RAIL_StartRx(gRailHandle, aChannel, NULL);
otEXPECT_ACTION(status == RAIL_STATUS_NO_ERROR, error = OT_ERROR_FAILED);
otLogInfoPlat("State=OT_RADIO_STATE_RECEIVE", NULL);
@@ -470,13 +475,14 @@ exit:
otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
{
otError error = OT_ERROR_NONE;
RAIL_CsmaConfig_t csmaConfig = RAIL_CSMA_CONFIG_802_15_4_2003_2p4_GHz_OQPSK_CSMA;
RAIL_TxOptions_t txOptions = RAIL_TX_OPTIONS_NONE;
RAIL_SchedulerInfo_t schedulerInfo = {.priority = EFR32_SCHEDULER_TX_PRIORITY};
efr32BandConfig * config;
RAIL_Status_t status;
uint8_t frameLength;
otError error = OT_ERROR_NONE;
RAIL_CsmaConfig_t csmaConfig = RAIL_CSMA_CONFIG_802_15_4_2003_2p4_GHz_OQPSK_CSMA;
RAIL_TxOptions_t txOptions = RAIL_TX_OPTIONS_DEFAULT;
efr32BandConfig * config;
RAIL_Status_t status;
uint8_t frameLength;
assert(sTransmitBusy == false);
otEXPECT_ACTION((sState != OT_RADIO_STATE_DISABLED) && (sState != OT_RADIO_STATE_TRANSMIT),
error = OT_ERROR_INVALID_STATE);
@@ -488,16 +494,17 @@ otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
sTransmitError = OT_ERROR_NONE;
sTransmitBusy = true;
if (sTxBandConfig != config)
if (sCurrentBandConfig != config)
{
efr32RadioSetTxPower(config->mRailHandle, config->mChannelConfig, sTxPowerDbm);
sTxBandConfig = config;
RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true);
efr32RailConfigLoad(config);
sCurrentBandConfig = config;
}
otEXPECT(aFrame->mLength >= IEEE802154_MIN_LENGTH && aFrame->mLength <= IEEE802154_MAX_LENGTH);
frameLength = (uint8_t)aFrame->mLength;
RAIL_WriteTxFifo(sTxBandConfig->mRailHandle, &frameLength, sizeof frameLength, true);
RAIL_WriteTxFifo(sTxBandConfig->mRailHandle, aFrame->mPsdu, frameLength - 2, false);
RAIL_WriteTxFifo(gRailHandle, &frameLength, sizeof frameLength, true);
RAIL_WriteTxFifo(gRailHandle, aFrame->mPsdu, frameLength - 2, false);
if (aFrame->mPsdu[0] & IEEE802154_ACK_REQUEST)
{
@@ -506,12 +513,11 @@ otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
if (aFrame->mInfo.mTxInfo.mCsmaCaEnabled)
{
status =
RAIL_StartCcaCsmaTx(sTxBandConfig->mRailHandle, aFrame->mChannel, txOptions, &csmaConfig, &schedulerInfo);
status = RAIL_StartCcaCsmaTx(gRailHandle, aFrame->mChannel, txOptions, &csmaConfig, NULL);
}
else
{
status = RAIL_StartTx(sTxBandConfig->mRailHandle, aFrame->mChannel, txOptions, &schedulerInfo);
status = RAIL_StartTx(gRailHandle, aFrame->mChannel, txOptions, NULL);
}
if (status == RAIL_STATUS_NO_ERROR)
@@ -537,30 +543,19 @@ otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
int8_t otPlatRadioGetRssi(otInstance *aInstance)
{
otError error;
uint32_t start;
int8_t rssi = OT_RADIO_RSSI_INVALID;
int8_t rssi = OT_RADIO_RSSI_INVALID;
OT_UNUSED_VARIABLE(aInstance);
error = efr32StartEnergyScan(ENERGY_SCAN_MODE_SYNC, sReceiveFrame.mChannel, EFR32_RSSI_AVERAGING_TIME);
otEXPECT(error == OT_ERROR_NONE);
start = RAIL_GetTime();
// waiting for the event RAIL_EVENT_RSSI_AVERAGE_DONE
while (sEnergyScanStatus == ENERGY_SCAN_STATUS_IN_PROGRESS &&
((RAIL_GetTime() - start) < EFR32_RSSI_AVERAGING_TIMEOUT))
;
if (sEnergyScanStatus == ENERGY_SCAN_STATUS_COMPLETED)
if ((RAIL_GetRadioState(gRailHandle) & RAIL_RF_STATE_RX))
{
rssi = sEnergyScanResultDbm;
int16_t railRssi = RAIL_RSSI_INVALID;
railRssi = RAIL_GetRssi(gRailHandle, true);
if (railRssi != RAIL_RSSI_INVALID)
{
rssi = railRssi / QUARTER_DBM_IN_DBM;
}
}
sEnergyScanStatus = ENERGY_SCAN_STATUS_IDLE;
exit:
return rssi;
}
@@ -588,7 +583,7 @@ void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
for (uint8_t i = 0; i < EFR32_NUM_BAND_CONFIGS; i++)
{
status = RAIL_IEEE802154_SetPromiscuousMode(sBandConfigs[i].mRailHandle, aEnable);
status = RAIL_IEEE802154_SetPromiscuousMode(gRailHandle, aEnable);
assert(status == RAIL_STATUS_NO_ERROR);
}
}
@@ -601,7 +596,7 @@ void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
sIsSrcMatchEnabled = aEnable;
}
static void processNextRxPacket(otInstance *aInstance, RAIL_Handle_t aRailHandle)
static void processNextRxPacket(otInstance *aInstance)
{
RAIL_RxPacketHandle_t packetHandle = RAIL_RX_PACKET_HANDLE_INVALID;
RAIL_RxPacketInfo_t packetInfo;
@@ -609,13 +604,11 @@ static void processNextRxPacket(otInstance *aInstance, RAIL_Handle_t aRailHandle
RAIL_Status_t status;
uint16_t length;
packetHandle = RAIL_GetRxPacketInfo(aRailHandle, RAIL_RX_PACKET_HANDLE_OLDEST, &packetInfo);
packetHandle = RAIL_GetRxPacketInfo(gRailHandle, RAIL_RX_PACKET_HANDLE_OLDEST, &packetInfo);
otEXPECT_ACTION(packetInfo.packetStatus == RAIL_RX_PACKET_READY_SUCCESS,
packetHandle = RAIL_RX_PACKET_HANDLE_INVALID);
packetDetails.timeReceived.timePosition = RAIL_PACKET_TIME_INVALID;
packetDetails.timeReceived.totalPacketBytes = 0;
status = RAIL_GetRxPacketDetails(aRailHandle, packetHandle, &packetDetails);
status = RAIL_GetRxPacketDetailsAlt(gRailHandle, packetHandle, &packetDetails);
otEXPECT(status == RAIL_STATUS_NO_ERROR);
length = packetInfo.packetBytes + 1;
@@ -639,13 +632,13 @@ static void processNextRxPacket(otInstance *aInstance, RAIL_Handle_t aRailHandle
memcpy(sReceiveFrame.mPsdu + packetInfo.firstPortionBytes, packetInfo.lastPortionData,
packetInfo.packetBytes - packetInfo.firstPortionBytes);
sReceiveFrame.mLength = length;
sReceiveFrame.mInfo.mRxInfo.mRssi = packetDetails.rssi;
sReceiveFrame.mInfo.mRxInfo.mLqi = packetDetails.lqi;
status = RAIL_ReleaseRxPacket(gRailHandle, packetHandle);
if (status == RAIL_STATUS_NO_ERROR)
{
packetHandle = RAIL_RX_PACKET_HANDLE_INVALID;
}
// TODO: grab timestamp and handle conversion to msec/usec
// sReceiveFrame.mInfo.mRxInfo.mMsec = packetDetails.packetTime;
// sReceiveFrame.mInfo.mRxInfo.mUsec = packetDetails.packetTime;
sReceiveFrame.mLength = length;
if (packetDetails.isAck)
{
@@ -669,6 +662,17 @@ static void processNextRxPacket(otInstance *aInstance, RAIL_Handle_t aRailHandle
sReceiveError = OT_ERROR_NONE;
sReceiveFrame.mInfo.mRxInfo.mRssi = packetDetails.rssi;
sReceiveFrame.mInfo.mRxInfo.mLqi = packetDetails.lqi;
// TODO: grab timestamp and handle conversion to msec/usec and RAIL_GetRxTimeSyncWordEndAlt
// sReceiveFrame.mInfo.mRxInfo.mMsec = packetDetails.packetTime;
// sReceiveFrame.mInfo.mRxInfo.mUsec = packetDetails.packetTime;
// TODO Set this flag only when the packet is really acknowledged with frame pending set.
// See https://github.com/openthread/openthread/pull/3785
sReceiveFrame.mInfo.mRxInfo.mAckedWithFramePending = true;
#if OPENTHREAD_CONFIG_DIAG_ENABLE
if (otPlatDiagModeGet())
@@ -692,7 +696,7 @@ exit:
if (packetHandle != RAIL_RX_PACKET_HANDLE_INVALID)
{
RAIL_ReleaseRxPacket(aRailHandle, packetHandle);
RAIL_ReleaseRxPacket(gRailHandle, packetHandle);
}
}
@@ -725,21 +729,36 @@ static void ieee802154DataRequestCommand(RAIL_Handle_t aRailHandle)
static void RAILCb_Generic(RAIL_Handle_t aRailHandle, RAIL_Events_t aEvents)
{
if (aEvents &
(RAIL_EVENT_TX_ABORTED | RAIL_EVENT_TX_BLOCKED | RAIL_EVENT_TX_UNDERFLOW | RAIL_EVENT_SCHEDULER_STATUS))
if (aEvents & RAIL_EVENT_IEEE802154_DATA_REQUEST_COMMAND)
{
sTransmitError = OT_ERROR_ABORT;
sTransmitBusy = false;
RAIL_YieldRadio(aRailHandle);
ieee802154DataRequestCommand(aRailHandle);
}
if (aEvents & RAIL_EVENTS_TX_COMPLETION)
{
if (aEvents & RAIL_EVENT_TX_PACKET_SENT)
{
if ((sTransmitFrame.mPsdu[0] & IEEE802154_ACK_REQUEST) == 0)
{
sTransmitError = OT_ERROR_NONE;
sTransmitBusy = false;
}
}
else if (aEvents & RAIL_EVENT_TX_CHANNEL_BUSY)
{
sTransmitError = OT_ERROR_CHANNEL_ACCESS_FAILURE;
sTransmitBusy = false;
}
else
{
sTransmitError = OT_ERROR_ABORT;
sTransmitBusy = false;
}
}
if (aEvents & RAIL_EVENT_RX_ACK_TIMEOUT)
{
sTransmitError = OT_ERROR_NO_ACK;
sTransmitBusy = false;
RAIL_YieldRadio(aRailHandle);
}
if (aEvents & RAIL_EVENT_RX_PACKET_RECEIVED)
@@ -747,30 +766,6 @@ static void RAILCb_Generic(RAIL_Handle_t aRailHandle, RAIL_Events_t aEvents)
RAIL_HoldRxPacket(aRailHandle);
}
if (aEvents & RAIL_EVENT_IEEE802154_DATA_REQUEST_COMMAND)
{
ieee802154DataRequestCommand(aRailHandle);
}
if (aEvents & RAIL_EVENT_TX_PACKET_SENT)
{
if ((sTransmitFrame.mPsdu[0] & IEEE802154_ACK_REQUEST) == 0)
{
sTransmitError = OT_ERROR_NONE;
sTransmitBusy = false;
RAIL_YieldRadio(aRailHandle);
}
}
if (aEvents & RAIL_EVENT_TX_CHANNEL_BUSY)
{
sTransmitError = OT_ERROR_CHANNEL_ACCESS_FAILURE;
sTransmitBusy = false;
RAIL_YieldRadio(aRailHandle);
}
if (aEvents & RAIL_EVENT_CAL_NEEDED)
{
RAIL_Status_t status;
@@ -793,8 +788,6 @@ static void RAILCb_Generic(RAIL_Handle_t aRailHandle, RAIL_Events_t aEvents)
{
sEnergyScanResultDbm = energyScanResultQuarterDbm / QUARTER_DBM_IN_DBM;
}
RAIL_YieldRadio(aRailHandle);
}
}
@@ -815,7 +808,6 @@ void efr32RadioProcess(otInstance *aInstance)
}
sState = OT_RADIO_STATE_RECEIVE;
#if OPENTHREAD_CONFIG_DIAG_ENABLE
if (otPlatDiagModeGet())
{
@@ -838,7 +830,7 @@ void efr32RadioProcess(otInstance *aInstance)
otPlatRadioEnergyScanDone(aInstance, sEnergyScanResultDbm);
}
processNextRxPacket(aInstance, sRxBandConfig->mRailHandle);
processNextRxPacket(aInstance);
}
otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower)
@@ -860,11 +852,8 @@ otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower)
RAIL_Status_t status;
for (uint8_t i = 0; i < EFR32_NUM_BAND_CONFIGS; i++)
{
status = RAIL_SetTxPowerDbm(sBandConfigs[i].mRailHandle, ((RAIL_TxPower_t)aPower) * 10);
assert(status == RAIL_STATUS_NO_ERROR);
}
status = RAIL_SetTxPowerDbm(gRailHandle, ((RAIL_TxPower_t)aPower) * 10);
assert(status == RAIL_STATUS_NO_ERROR);
sTxPowerDbm = aPower;
+2 -2
View File
@@ -1,9 +1,9 @@
#ifndef __RAIL_CONFIG_H__
#define __RAIL_CONFIG_H__
#include <stdint.h>
#include "rail_types.h"
#include "board_config.h"
#include "rail_types.h"
#include <stdint.h>
#define RADIO_CONFIG_XTAL_FREQUENCY 38400000UL
@@ -0,0 +1,54 @@
#
# Copyright (c) 2019, The OpenThread Authors.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
# Always package (e.g. for 'make dist') these subdirectories.
DIST_SUBDIRS = \
sleepy-demo-mtd \
sleepy-demo-ftd \
$(NULL)
# Always build (e.g. for 'make all') these subdirectories.
SUBDIRS = \
$(NULL)
if OPENTHREAD_ENABLE_EXECUTABLE
SUBDIRS += sleepy-demo-mtd sleepy-demo-ftd
endif
# Always pretty (e.g. for 'make pretty') these subdirectories.
PRETTY_SUBDIRS = \
sleepy-demo-mtd \
sleepy-demo-ftd \
$(NULL)
include $(abs_top_nlbuild_autotools_dir)/automake/post.am
@@ -0,0 +1,87 @@
# EFR32MG21 Sleepy Demo Example
The EFR32 Sleepy applications demonstrates Sleepy End Device behaviour using
the EFR32's low power EM2 mode. The steps below will take you through the
process of building and running the demo
For setting up the build environment refer to [examples/platforms/efr32mg21/README.md](../README.md).
## 1. Build
```bash
$ cd <path-to-openthread>
$ ./bootstrap
$ make -f examples/Makefile-efr32mg21 COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 BOARD=BRD4180A
```
Convert the resulting executables into S-Record format and append a s37 suffix.
```bash
$ cd output/efr32mg21/bin
$ arm-none-eabi-objcopy -O srec sleepy-demo-mtd sleepy-demo-mtd.s37
$ arm-none-eabi-objcopy -O srec sleepy-demo-ftd sleepy-demo-ftd.s37
```
In Silicon Labs Simplicity Studio flash one device with the sleepy-demo-mtd.s37
image and the other device with the sleepy-demo-ftd.s37 image.
For instructions on flashing firmware see [examples/platforms/efr32mg21/README.md](../README.md#flash-binaries)
## 2. Starting nodes
For demonstration purposes the network settings are hardcoded within the source files.
The devices start Thread and form a network within a few seconds of powering on. In a real-life
application the devices should implement and go through a commissioning process to create
a network and add devices.
When the sleepy-demo-ftd device is started in the CLI the user shall see:
```
sleepy-demo-ftd started
sleepy-demo-ftd changed to leader
```
When the sleepy-demo-mtd device starts it joins the preconfigured Thread network
before disabling Rx-On-Idle to become a Sleepy-End-Device.
Use the command "child table" in the FTD console and observe the R flag of the child is 0.
```
> child table
| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|S|D|N| Extended MAC |
+-----+--------+------------+------------+-------+------+-+-+-+-+------------------+
| 1 | 0x8401 | 240 | 3 | 3 | 3 |0|1|0|0| 8e8582dbd78c243c |
Done
```
## 3. MTD behaviour on wakeup
MTD wakes up every 5 seconds and sends a multicast UDP message containing the
string "mtd is awake". The FTD listens on the multicast address and will toggle
LED 0 and display a message in the CLI showing "Message Received: mtd is awake".
## 4. Monitoring power consumption of the MTD
Open the Energy Profiler within Silicon Labs Simplicity Studio. Within the Quick Access menu
select Start Energy Capture... and select the MTD device. When operating as a Sleepy End Device
with no LEDs on the current should be under 20-80 microamps with occassional spikes during waking
and polling the parent. With the LED on the MTD has a current consumption of approximately 1mA.
When operating as a Minial End Device with the Rx on Idle observe that the current is in the order
of 10ma.
With further configuration of GPIOs and peripherals it is possible to reduce the sleepy current
consumption further.
## 5. Notes on sleeping, sleepy callback and interrupts
To allow the EFR32 to enter sleepy mode the application must register a callback with efr32SetSleepCallback.
The return value of callback is used to indicate that the application has no further work to do and that
it is safe to go into a low power mode. The callback is called with interrupts disabled so should do
the minimum required to check if it can sleep.
@@ -0,0 +1,156 @@
#
# Copyright (c) 2019, The OpenThread Authors.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
include $(top_srcdir)/examples/platforms/Makefile.platform.am
override CFLAGS := $(filter-out -Wconversion,$(CFLAGS))
override CXXFLAGS := $(filter-out -Wconversion,$(CXXFLAGS))
EFR32_BOARD_DIR = $(shell echo $(BOARD) | tr A-Z a-z)
EFR32MG_SDK_SRCDIR = $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v2.6
$(top_builddir)/examples/platforms/efr32mg21/libopenthread-efr32mg21.a:
(cd $(top_builddir)/examples/platforms/efr32mg21/ && $(MAKE) $(AM_MAKEFLAGS) libopenthread-efr32mg21.a )
bin_PROGRAMS = \
$(NULL)
CPPFLAGS_COMMON += \
-I$(top_srcdir)/include \
-I$(top_srcdir)/src/core \
-I$(top_srcdir)/examples/platforms \
-DPLATFORM_HEADER=\"@top_builddir@/third_party/silabs/gecko_sdk_suite/v2.6/platform/base/hal/micro/cortexm3/compiler/gcc.h\" \
-Wno-sign-compare \
-DCORTEXM3 \
-D__START=main \
-DPHY=EMBER_PHY_RAIL \
-DMICRO=EMBER_MICRO_CORTEXM3_EFR32 \
-DCORTEXM3_EFM32_MICRO \
-DPLAT=EMBER_PLATFORM_CORTEXM3 \
-D__STARTUP_CLEAR_BSS \
-I$(top_srcdir)/include \
-I$(top_srcdir)/examples/platforms \
-I$(top_srcdir)/examples/platforms/efr32/$(EFR32_BOARD_DIR) \
-I$(top_srcdir)/src/core \
-I$(top_srcdir)/third_party/silabs/rail_config \
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/common \
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/chip/efr32 \
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/protocol/ieee802154 \
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/chip/efr32/rf/common/cortex \
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/hal \
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/hal/efr32 \
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/plugin/pa-conversions \
-I$(EFR32MG_SDK_SRCDIR)/hardware/kit/common/bsp \
-I$(EFR32MG_SDK_SRCDIR)/hardware/kit/EFR32MG21_$(BOARD)/config \
-I$(EFR32MG_SDK_SRCDIR)/platform/CMSIS/Include \
-I$(EFR32MG_SDK_SRCDIR)/platform/base/ \
-I$(EFR32MG_SDK_SRCDIR)/platform/Device/SiliconLabs/EFR32MG21/Include \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/common/inc \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/gpiointerrupt/inc \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/uartdrv/inc \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/uartdrv/config \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/ustimer/inc \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/dmadrv/inc \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/dmadrv/config \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/rtcdrv/inc \
-I$(EFR32MG_SDK_SRCDIR)/platform/emlib/inc \
-I$(EFR32MG_SDK_SRCDIR)/platform/halconfig/inc/hal-config \
-I$(EFR32MG_SDK_SRCDIR)/util/plugin/plugin-common/fem-control \
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/micro/cortexm3/efm32/config \
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/plugin \
-I$(EFR32MG_SDK_SRCDIR)/protocol/thread \
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/micro/cortexm3/efm32 \
-I$(EFR32MG_SDK_SRCDIR)/protocol/thread/stack \
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal \
-Wno-unused-parameter \
-Wno-missing-field-initializers \
$(NULL)
LDADD_COMMON += \
$(NULL)
LDFLAGS_COMMON += \
$(NULL)
LIBTOOLFLAGS_COMMON += \
$(NULL)
SOURCES_COMMON += \
main.c \
$(NULL)
if OPENTHREAD_ENABLE_BUILTIN_MBEDTLS
LDADD_COMMON += \
$(top_builddir)/third_party/mbedtls/libmbedcrypto.a \
$(NULL)
endif # OPENTHREAD_ENABLE_BUILTIN_MBEDTLS
if OPENTHREAD_ENABLE_EXECUTABLE
bin_PROGRAMS += \
sleepy-demo-ftd \
$(NULL)
endif
sleepy_demo_ftd_CPPFLAGS = \
$(CPPFLAGS_COMMON) \
$(NULL)
sleepy_demo_ftd_LDADD = \
$(top_builddir)/src/cli/libopenthread-cli-ftd.a \
$(top_builddir)/src/core/libopenthread-ftd.a \
$(LDADD_COMMON) \
$(top_builddir)/src/core/libopenthread-ftd.a \
$(LDADD_COMMON) \
$(NULL)
sleepy_demo_ftd_LDFLAGS = \
$(LDFLAGS_COMMON) \
$(NULL)
sleepy_demo_ftd_LIBTOOLFLAGS = \
$(LIBTOOLFLAGS_COMMON) \
$(NULL)
sleepy_demo_ftd_SOURCES = \
$(SOURCES_COMMON) \
$(NULL)
if OPENTHREAD_ENABLE_LINKER_MAP
sleepy_demo_ftd_LDFLAGS += -Wl,-Map=sleepy-demo-ftd.map
endif
if OPENTHREAD_BUILD_COVERAGE
CPPFLAGS_COMMON += \
-DOPENTHREAD_ENABLE_COVERAGE \
$(NULL)
CLEANFILES = $(wildcard *.gcda *.gcno)
endif # OPENTHREAD_BUILD_COVERAGE
include $(abs_top_nlbuild_autotools_dir)/automake/post.am
@@ -0,0 +1,323 @@
/*
* Copyright (c) 2019, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "bsp.h"
#include "em_cmu.h"
#include "em_emu.h"
#include "gpiointerrupt.h"
#include "hal-config-board.h"
#include "hal_common.h"
#include "openthread-system.h"
#include "rtcdriver.h"
#include <assert.h>
#include <common/logging.hpp>
#include <openthread-core-config.h>
#include <string.h>
#include <openthread/cli.h>
#include <openthread/config.h>
#include <openthread/dataset_ftd.h>
#include <openthread/diag.h>
#include <openthread/instance.h>
#include <openthread/message.h>
#include <openthread/tasklet.h>
#include <openthread/thread.h>
#include <openthread/udp.h>
#include <openthread/platform/logging.h>
// Constants
#define MULTICAST_ADDR "ff03::1"
#define MULTICAST_PORT 123
#define RECV_PORT 234
#define MTD_MESSAGE "mtd button"
#define FTD_MESSAGE "ftd button"
// Types
typedef struct ButtonArray
{
GPIO_Port_TypeDef port;
unsigned int pin;
} ButtonArray_t;
// Prototypes
void setNetworkConfiguration(otInstance *aInstance);
void handleNetifStateChanged(uint32_t aFlags, void *aContext);
void gpioInit(void (*gpioCallback)(uint8_t pin));
void buttonCallback(uint8_t pin);
void initUdp(void);
void applicationTick(void);
void sFtdReceiveCallback(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
// Variables
static otInstance * instance;
static otUdpSocket sFtdSocket;
static bool sLedOn = false;
static bool sHaveSwitchAddress = false;
static otIp6Address sSwitchAddress;
static bool sFtdButtonPressed = false;
static const ButtonArray_t sButtonArray[BSP_BUTTON_COUNT] = BSP_BUTTON_INIT;
void otTaskletsSignalPending(otInstance *aInstance)
{
(void)aInstance;
}
int main(int argc, char *argv[])
{
otSysInit(argc, argv);
gpioInit(buttonCallback);
instance = otInstanceInitSingle();
assert(instance);
otCliUartInit(instance);
otCliOutputFormat("sleepy-demo-ftd started\r\n");
setNetworkConfiguration(instance);
otSetStateChangedCallback(instance, handleNetifStateChanged, instance);
initUdp();
otIp6SetEnabled(instance, true);
otThreadSetEnabled(instance, true);
while (!otSysPseudoResetWasRequested())
{
otTaskletsProcess(instance);
otSysProcessDrivers(instance);
applicationTick();
}
otInstanceFinalize(instance);
return 0;
}
/*
* Provide, if required an "otPlatLog()" function
*/
#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_APP
void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
{
OT_UNUSED_VARIABLE(aLogLevel);
OT_UNUSED_VARIABLE(aLogRegion);
OT_UNUSED_VARIABLE(aFormat);
va_list ap;
va_start(ap, aFormat);
otCliPlatLogv(aLogLevel, aLogRegion, aFormat, ap);
va_end(ap);
}
#endif
/**
* Override default network settings, such as panid, so the devices can join a network
*/
void setNetworkConfiguration(otInstance *aInstance)
{
static char aNetworkName[] = "SleepyEFR32";
otOperationalDataset aDataset;
memset(&aDataset, 0, sizeof(otOperationalDataset));
/*
* Fields that can be configured in otOperationDataset to override defaults:
* Network Name, Mesh Local Prefix, Extended PAN ID, PAN ID, Delay Timer,
* Channel, Channel Mask Page 0, Network Master Key, PSKc, Security Policy
*/
aDataset.mActiveTimestamp = 1;
aDataset.mComponents.mIsActiveTimestampPresent = true;
/* Set Channel to 15 */
aDataset.mChannel = 15;
aDataset.mComponents.mIsChannelPresent = true;
/* Set Pan ID to 2222 */
aDataset.mPanId = (otPanId)0x2222;
aDataset.mComponents.mIsPanIdPresent = true;
/* Set Extended Pan ID to C0DE1AB5C0DE1AB5 */
uint8_t extPanId[OT_EXT_PAN_ID_SIZE] = {0xC0, 0xDE, 0x1A, 0xB5, 0xC0, 0xDE, 0x1A, 0xB5};
memcpy(aDataset.mExtendedPanId.m8, extPanId, sizeof(aDataset.mExtendedPanId));
aDataset.mComponents.mIsExtendedPanIdPresent = true;
/* Set master key to 1234C0DE1AB51234C0DE1AB51234C0DE */
uint8_t key[OT_MASTER_KEY_SIZE] = {0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5, 0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5};
memcpy(aDataset.mMasterKey.m8, key, sizeof(aDataset.mMasterKey));
aDataset.mComponents.mIsMasterKeyPresent = true;
/* Set Network Name to SleepyEFR32 */
size_t length = strlen(aNetworkName);
assert(length <= OT_NETWORK_NAME_MAX_SIZE);
memcpy(aDataset.mNetworkName.m8, aNetworkName, length);
aDataset.mComponents.mIsNetworkNamePresent = true;
otDatasetSetActive(aInstance, &aDataset);
}
void handleNetifStateChanged(uint32_t aFlags, void *aContext)
{
if ((aFlags & OT_CHANGED_THREAD_ROLE) != 0)
{
otDeviceRole changedRole = otThreadGetDeviceRole(aContext);
switch (changedRole)
{
case OT_DEVICE_ROLE_LEADER:
otCliOutputFormat("sleepy-demo-ftd changed to leader\r\n");
break;
case OT_DEVICE_ROLE_ROUTER:
otCliOutputFormat("sleepy-demo-ftd changed to router\r\n");
break;
case OT_DEVICE_ROLE_CHILD:
break;
case OT_DEVICE_ROLE_DETACHED:
case OT_DEVICE_ROLE_DISABLED:
break;
}
}
}
void gpioInit(void (*callback)(uint8_t pin))
{
// set up button GPIOs to input with pullups
for (int i = 0; i < BSP_BUTTON_COUNT; i++)
{
GPIO_PinModeSet(sButtonArray[i].port, sButtonArray[i].pin, gpioModeInputPull, 1);
}
// set up interrupt based callback function on falling edge
GPIOINT_Init();
GPIOINT_CallbackRegister(sButtonArray[0].pin, callback);
GPIOINT_CallbackRegister(sButtonArray[1].pin, callback);
GPIO_IntConfig(sButtonArray[0].port, sButtonArray[0].pin, false, true, true);
GPIO_IntConfig(sButtonArray[1].port, sButtonArray[1].pin, false, true, true);
BSP_LedsInit();
BSP_LedClear(0);
BSP_LedClear(1);
}
void initUdp(void)
{
otError error;
otSockAddr sockaddr;
memset(&sockaddr, 0, sizeof(sockaddr));
otIp6AddressFromString(MULTICAST_ADDR, &sockaddr.mAddress);
sockaddr.mPort = MULTICAST_PORT;
error = otUdpOpen(instance, &sFtdSocket, sFtdReceiveCallback, NULL);
if (error != OT_ERROR_NONE)
{
otCliOutputFormat("FTD failed to open udp multicast\r\n");
return;
}
error = otUdpBind(&sFtdSocket, &sockaddr);
if (error != OT_ERROR_NONE)
{
otUdpClose(&sFtdSocket);
otCliOutputFormat("FTD failed to bind udp multicast\r\n");
return;
}
}
void buttonCallback(uint8_t pin)
{
OT_UNUSED_VARIABLE(pin);
sFtdButtonPressed = true;
}
void applicationTick(void)
{
otError error = 0;
otMessageInfo messageInfo;
otMessage * message = NULL;
char * payload = FTD_MESSAGE;
if (sFtdButtonPressed == true)
{
sFtdButtonPressed = false;
if (sHaveSwitchAddress)
{
memset(&messageInfo, 0, sizeof(messageInfo));
memcpy(&messageInfo.mPeerAddr, &sSwitchAddress, sizeof messageInfo.mPeerAddr);
messageInfo.mPeerPort = RECV_PORT;
message = otUdpNewMessage(instance, NULL);
if (message != NULL)
{
error = otMessageAppend(message, payload, (uint16_t)strlen(payload));
if (error == OT_ERROR_NONE)
{
error = otUdpSend(&sFtdSocket, message, &messageInfo);
if (error == OT_ERROR_NONE)
{
return;
}
}
}
if (message != NULL)
{
otMessageFree(message);
}
}
}
}
void sFtdReceiveCallback(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
OT_UNUSED_VARIABLE(aContext);
OT_UNUSED_VARIABLE(aMessage);
OT_UNUSED_VARIABLE(aMessageInfo);
uint8_t buf[1500];
int length;
sLedOn = !sLedOn;
if (sLedOn)
{
BSP_LedSet(0);
}
else
{
BSP_LedClear(0);
}
length = otMessageRead(aMessage, otMessageGetOffset(aMessage), buf, sizeof(buf) - 1);
buf[length] = '\0';
otCliOutputFormat("Message Received: %s\r\n", buf);
sHaveSwitchAddress = true;
memcpy(&sSwitchAddress, &aMessageInfo->mPeerAddr, sizeof sSwitchAddress);
}
@@ -0,0 +1,156 @@
#
# Copyright (c) 2019, The OpenThread Authors.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
include $(top_srcdir)/examples/platforms/Makefile.platform.am
override CFLAGS := $(filter-out -Wconversion,$(CFLAGS))
override CXXFLAGS := $(filter-out -Wconversion,$(CXXFLAGS))
EFR32_BOARD_DIR = $(shell echo $(BOARD) | tr A-Z a-z)
EFR32MG_SDK_SRCDIR = $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v2.6
$(top_builddir)/examples/platforms/efr32mg21/libopenthread-efr32mg21.a:
(cd $(top_builddir)/examples/platforms/efr32mg21/ && $(MAKE) $(AM_MAKEFLAGS) libopenthread-efr32mg21.a )
bin_PROGRAMS = \
$(NULL)
CPPFLAGS_COMMON += \
-I$(top_srcdir)/include \
-I$(top_srcdir)/src/core \
-I$(top_srcdir)/examples/platforms \
-DPLATFORM_HEADER=\"@top_builddir@/third_party/silabs/gecko_sdk_suite/v2.6/platform/base/hal/micro/cortexm3/compiler/gcc.h\" \
-Wno-sign-compare \
-DCORTEXM3 \
-D__START=main \
-DPHY=EMBER_PHY_RAIL \
-DMICRO=EMBER_MICRO_CORTEXM3_EFR32 \
-DCORTEXM3_EFM32_MICRO \
-DPLAT=EMBER_PLATFORM_CORTEXM3 \
-D__STARTUP_CLEAR_BSS \
-I$(top_srcdir)/include \
-I$(top_srcdir)/examples/platforms \
-I$(top_srcdir)/examples/platforms/efr32/$(EFR32_BOARD_DIR) \
-I$(top_srcdir)/src/core \
-I$(top_srcdir)/third_party/silabs/rail_config \
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/common \
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/chip/efr32 \
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/protocol/ieee802154 \
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/chip/efr32/rf/common/cortex \
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/hal \
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/hal/efr32 \
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/plugin/pa-conversions \
-I$(EFR32MG_SDK_SRCDIR)/hardware/kit/common/bsp \
-I$(EFR32MG_SDK_SRCDIR)/hardware/kit/EFR32MG21_$(BOARD)/config \
-I$(EFR32MG_SDK_SRCDIR)/platform/CMSIS/Include \
-I$(EFR32MG_SDK_SRCDIR)/platform/base/ \
-I$(EFR32MG_SDK_SRCDIR)/platform/Device/SiliconLabs/EFR32MG21/Include \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/common/inc \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/gpiointerrupt/inc \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/uartdrv/inc \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/uartdrv/config \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/ustimer/inc \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/dmadrv/inc \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/dmadrv/config \
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/rtcdrv/inc \
-I$(EFR32MG_SDK_SRCDIR)/platform/emlib/inc \
-I$(EFR32MG_SDK_SRCDIR)/platform/halconfig/inc/hal-config \
-I$(EFR32MG_SDK_SRCDIR)/util/plugin/plugin-common/fem-control \
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/micro/cortexm3/efm32/config \
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/plugin \
-I$(EFR32MG_SDK_SRCDIR)/protocol/thread \
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/micro/cortexm3/efm32 \
-I$(EFR32MG_SDK_SRCDIR)/protocol/thread/stack \
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal \
-Wno-unused-parameter \
-Wno-missing-field-initializers \
$(NULL)
LDADD_COMMON += \
$(NULL)
LDFLAGS_COMMON += \
$(NULL)
LIBTOOLFLAGS_COMMON += \
$(NULL)
SOURCES_COMMON += \
main.c \
$(NULL)
if OPENTHREAD_ENABLE_BUILTIN_MBEDTLS
LDADD_COMMON += \
$(top_builddir)/third_party/mbedtls/libmbedcrypto.a \
$(NULL)
endif # OPENTHREAD_ENABLE_BUILTIN_MBEDTLS
if OPENTHREAD_ENABLE_EXECUTABLE
bin_PROGRAMS += \
sleepy-demo-mtd \
$(NULL)
endif
sleepy_demo_mtd_CPPFLAGS = \
$(CPPFLAGS_COMMON) \
$(NULL)
sleepy_demo_mtd_LDADD = \
$(top_builddir)/src/cli/libopenthread-cli-mtd.a \
$(top_builddir)/src/core/libopenthread-mtd.a \
$(LDADD_COMMON) \
$(top_builddir)/src/core/libopenthread-mtd.a \
$(LDADD_COMMON) \
$(NULL)
sleepy_demo_mtd_LDFLAGS = \
$(LDFLAGS_COMMON) \
$(NULL)
sleepy_demo_mtd_LIBTOOLFLAGS = \
$(LIBTOOLFLAGS_COMMON) \
$(NULL)
sleepy_demo_mtd_SOURCES = \
$(SOURCES_COMMON) \
$(NULL)
if OPENTHREAD_ENABLE_LINKER_MAP
sleepy_demo_mtd_LDFLAGS += -Wl,-Map=sleepy-demo-mtd.map
endif
if OPENTHREAD_BUILD_COVERAGE
CPPFLAGS_COMMON += \
-DOPENTHREAD_ENABLE_COVERAGE \
$(NULL)
CLEANFILES = $(wildcard *.gcda *.gcno)
endif # OPENTHREAD_BUILD_COVERAGE
include $(abs_top_nlbuild_autotools_dir)/automake/post.am
@@ -0,0 +1,395 @@
/*
* Copyright (c) 2019, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <assert.h>
#include <string.h>
#include "bsp.h"
#include "em_cmu.h"
#include "em_emu.h"
#include "gpiointerrupt.h"
#include "hal-config-board.h"
#include "hal_common.h"
#include "openthread-system.h"
#include "platform-efr32.h"
#include "rtcdriver.h"
#include <common/logging.hpp>
#include <openthread-core-config.h>
#include <openthread/cli.h>
#include <openthread/config.h>
#include <openthread/dataset_ftd.h>
#include <openthread/diag.h>
#include <openthread/instance.h>
#include <openthread/link.h>
#include <openthread/message.h>
#include <openthread/tasklet.h>
#include <openthread/thread.h>
#include <openthread/udp.h>
#include <openthread/platform/alarm-milli.h>
#include <openthread/platform/logging.h>
// Constants
#define MULTICAST_ADDR "ff03::1"
#define MULTICAST_PORT 123
#define RECV_PORT 234
#define SLEEPY_POLL_PERIOD_MS 5000
#define MTD_MESSAGE "mtd is awake"
#define FTD_MESSAGE "ftd button"
// Types
typedef struct ButtonArray
{
GPIO_Port_TypeDef port;
unsigned int pin;
} ButtonArray_t;
// Prototypes
void deviceOutOfSleepCb(void);
bool sleepCb(void);
void setNetworkConfiguration(otInstance *aInstance);
void handleNetifStateChanged(uint32_t aFlags, void *aContext);
void gpioInit(void (*gpioCallback)(uint8_t pin));
void buttonCallback(uint8_t pin);
void initUdp(void);
void applicationTick(void);
void mtdReceiveCallback(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
// Variables
static otInstance * instance;
static otUdpSocket sMtdSocket;
static otSockAddr sMulticastSockAddr;
static const ButtonArray_t sButtonArray[BSP_BUTTON_COUNT] = BSP_BUTTON_INIT;
static bool sButtonPressed = false;
static bool sRxOnIdleButtonPressed = false;
static bool sLedOn = false;
static bool sAllowDeepSleep = false;
static bool sTaskletsPendingSem = true;
int main(int argc, char *argv[])
{
otLinkModeConfig config;
otSysInit(argc, argv);
gpioInit(buttonCallback);
instance = otInstanceInitSingle();
assert(instance);
otCliUartInit(instance);
otLinkSetPollPeriod(instance, SLEEPY_POLL_PERIOD_MS);
setNetworkConfiguration(instance);
otSetStateChangedCallback(instance, handleNetifStateChanged, instance);
config.mRxOnWhenIdle = true;
config.mSecureDataRequests = true;
config.mDeviceType = 0;
config.mNetworkData = 0;
otThreadSetLinkMode(instance, config);
initUdp();
otIp6SetEnabled(instance, true);
otThreadSetEnabled(instance, true);
efr32SetSleepCallback(sleepCb, deviceOutOfSleepCb);
while (!otSysPseudoResetWasRequested())
{
otTaskletsProcess(instance);
otSysProcessDrivers(instance);
applicationTick();
// Put the EFR32 into deep sleep if callback sleepCb permits.
efr32Sleep();
}
otInstanceFinalize(instance);
return 0;
}
void deviceOutOfSleepCb(void)
{
static uint32_t udpPacketSendTimer = 0;
if (udpPacketSendTimer == 0)
{
udpPacketSendTimer = otPlatAlarmMilliGetNow();
}
if ((otPlatAlarmMilliGetNow() - udpPacketSendTimer) >= 5000)
{
sButtonPressed = true;
udpPacketSendTimer = 0;
}
}
/*
* Callback from efr32Sleep to indicate if it is ok to go into sleep mode.
* This runs with interrupts disabled.
*/
bool sleepCb(void)
{
bool allow;
allow = (sAllowDeepSleep && !sTaskletsPendingSem);
sTaskletsPendingSem = false;
return allow;
}
void otTaskletsSignalPending(otInstance *aInstance)
{
(void)aInstance;
sTaskletsPendingSem = true;
}
/*
* Override default network settings, such as panid, so the devices can join a network
*/
void setNetworkConfiguration(otInstance *aInstance)
{
static char aNetworkName[] = "SleepyEFR32";
otOperationalDataset aDataset;
memset(&aDataset, 0, sizeof(otOperationalDataset));
/*
* Fields that can be configured in otOperationDataset to override defaults:
* Network Name, Mesh Local Prefix, Extended PAN ID, PAN ID, Delay Timer,
* Channel, Channel Mask Page 0, Network Master Key, PSKc, Security Policy
*/
aDataset.mActiveTimestamp = 1;
aDataset.mComponents.mIsActiveTimestampPresent = true;
/* Set Channel to 15 */
aDataset.mChannel = 15;
aDataset.mComponents.mIsChannelPresent = true;
/* Set Pan ID to 2222 */
aDataset.mPanId = (otPanId)0x2222;
aDataset.mComponents.mIsPanIdPresent = true;
/* Set Extended Pan ID to C0DE1AB5C0DE1AB5 */
uint8_t extPanId[OT_EXT_PAN_ID_SIZE] = {0xC0, 0xDE, 0x1A, 0xB5, 0xC0, 0xDE, 0x1A, 0xB5};
memcpy(aDataset.mExtendedPanId.m8, extPanId, sizeof(aDataset.mExtendedPanId));
aDataset.mComponents.mIsExtendedPanIdPresent = true;
/* Set master key to 1234C0DE1AB51234C0DE1AB51234C0DE */
uint8_t key[OT_MASTER_KEY_SIZE] = {0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5, 0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5};
memcpy(aDataset.mMasterKey.m8, key, sizeof(aDataset.mMasterKey));
aDataset.mComponents.mIsMasterKeyPresent = true;
/* Set Network Name to SleepyEFR32 */
size_t length = strlen(aNetworkName);
assert(length <= OT_NETWORK_NAME_MAX_SIZE);
memcpy(aDataset.mNetworkName.m8, aNetworkName, length);
aDataset.mComponents.mIsNetworkNamePresent = true;
otDatasetSetActive(aInstance, &aDataset);
}
void handleNetifStateChanged(uint32_t aFlags, void *aContext)
{
otLinkModeConfig config;
if ((aFlags & OT_CHANGED_THREAD_ROLE) != 0)
{
otDeviceRole changedRole = otThreadGetDeviceRole(aContext);
switch (changedRole)
{
case OT_DEVICE_ROLE_LEADER:
case OT_DEVICE_ROLE_ROUTER:
break;
case OT_DEVICE_ROLE_CHILD:
config.mRxOnWhenIdle = 0;
config.mSecureDataRequests = true;
config.mDeviceType = 0;
config.mNetworkData = 0;
otThreadSetLinkMode(instance, config);
sAllowDeepSleep = true;
break;
case OT_DEVICE_ROLE_DETACHED:
case OT_DEVICE_ROLE_DISABLED:
break;
}
}
}
/*
* Provide, if required an "otPlatLog()" function
*/
#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_APP
void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
{
OT_UNUSED_VARIABLE(aLogLevel);
OT_UNUSED_VARIABLE(aLogRegion);
OT_UNUSED_VARIABLE(aFormat);
va_list ap;
va_start(ap, aFormat);
otCliPlatLogv(aLogLevel, aLogRegion, aFormat, ap);
va_end(ap);
}
#endif
void gpioInit(void (*callback)(uint8_t pin))
{
// set up button GPIOs to input with pullups
for (int i = 0; i < BSP_BUTTON_COUNT; i++)
{
GPIO_PinModeSet(sButtonArray[i].port, sButtonArray[i].pin, gpioModeInputPull, 1);
}
// set up interrupt based callback function on falling edge
GPIOINT_Init();
GPIOINT_CallbackRegister(sButtonArray[0].pin, callback);
GPIOINT_CallbackRegister(sButtonArray[1].pin, callback);
GPIO_IntConfig(sButtonArray[0].port, sButtonArray[0].pin, false, true, true);
GPIO_IntConfig(sButtonArray[1].port, sButtonArray[1].pin, false, true, true);
BSP_LedsInit();
BSP_LedClear(0);
BSP_LedClear(1);
}
void initUdp(void)
{
otError error;
otSockAddr sockaddr;
memset(&sMulticastSockAddr, 0, sizeof sMulticastSockAddr);
otIp6AddressFromString(MULTICAST_ADDR, &sMulticastSockAddr.mAddress);
sMulticastSockAddr.mPort = MULTICAST_PORT;
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.mPort = RECV_PORT;
error = otUdpOpen(instance, &sMtdSocket, mtdReceiveCallback, NULL);
if (error != OT_ERROR_NONE)
{
return;
}
error = otUdpBind(&sMtdSocket, &sockaddr);
if (error != OT_ERROR_NONE)
{
otUdpClose(&sMtdSocket);
return;
}
}
void buttonCallback(uint8_t pin)
{
if ((pin & 0x01) == 0x01)
{
sButtonPressed = true;
}
else if ((pin & 0x01) == 0x00)
{
sRxOnIdleButtonPressed = true;
}
}
void applicationTick(void)
{
otError error = 0;
otMessageInfo messageInfo;
otMessage * message = NULL;
char * payload = MTD_MESSAGE;
otLinkModeConfig config;
if (sRxOnIdleButtonPressed == true)
{
sRxOnIdleButtonPressed = false;
sAllowDeepSleep = !sAllowDeepSleep;
config.mRxOnWhenIdle = !sAllowDeepSleep;
config.mSecureDataRequests = true;
config.mDeviceType = 0;
config.mNetworkData = 0;
otThreadSetLinkMode(instance, config);
}
if (sButtonPressed == true)
{
sButtonPressed = false;
memset(&messageInfo, 0, sizeof(messageInfo));
memcpy(&messageInfo.mPeerAddr, &sMulticastSockAddr.mAddress, sizeof messageInfo.mPeerAddr);
messageInfo.mPeerPort = sMulticastSockAddr.mPort;
message = otUdpNewMessage(instance, NULL);
if (message != NULL)
{
error = otMessageAppend(message, payload, (uint16_t)strlen(payload));
if (error == OT_ERROR_NONE)
{
error = otUdpSend(&sMtdSocket, message, &messageInfo);
if (error == OT_ERROR_NONE)
{
return;
}
}
}
if (message != NULL)
{
otMessageFree(message);
}
}
}
void mtdReceiveCallback(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
OT_UNUSED_VARIABLE(aContext);
OT_UNUSED_VARIABLE(aMessage);
OT_UNUSED_VARIABLE(aMessageInfo);
uint8_t buf[1500];
int length;
length = otMessageRead(aMessage, otMessageGetOffset(aMessage), buf, sizeof(buf) - 1);
buf[length] = '\0';
if (strcmp((char *)buf, FTD_MESSAGE) == 0)
{
sLedOn = !sLedOn;
if (sLedOn)
{
BSP_LedSet(0);
}
else
{
BSP_LedClear(0);
}
}
}
+55 -2
View File
@@ -34,19 +34,24 @@
#include <string.h>
#include <openthread/tasklet.h>
#include <openthread/platform/uart.h>
#include "common/logging.hpp"
#include "bsp.h"
#include "em_chip.h"
#include "em_core.h"
#include "em_emu.h"
#include "em_system.h"
#include "hal-config.h"
#include "hal_common.h"
#include "rail.h"
#include "rtcdriver.h"
#include "openthread-core-efr32-config.h"
#include "platform-efr32.h"
#include "hal-config.h"
#if (HAL_FEM_ENABLE)
#include "fem-control.h"
#endif
@@ -58,6 +63,8 @@
void halInitChipSpecific(void);
otInstance *sInstance;
static bool (*sCanSleepCallback)(void);
static void (*sDeviceOutOfSleepCb)(void);
void otSysInit(int argc, char *argv[])
{
@@ -69,6 +76,7 @@ void otSysInit(int argc, char *argv[])
halInitChipSpecific();
BSP_Init(BSP_INIT_BCC);
RTCDRV_Init();
#if (HAL_FEM_ENABLE)
initFem();
@@ -97,6 +105,51 @@ void otSysDeinit(void)
#endif
}
void efr32SetSleepCallback(bool (*aCallback)(void), void (*aCallbackWake)(void))
{
sCanSleepCallback = aCallback;
sDeviceOutOfSleepCb = aCallbackWake;
}
void efr32Sleep(void)
{
bool canDeepSleep = false;
int wakeupProcessTime = 1000;
CORE_DECLARE_IRQ_STATE;
if (RAIL_Sleep(wakeupProcessTime, &canDeepSleep) == RAIL_STATUS_NO_ERROR)
{
if (canDeepSleep)
{
CORE_ENTER_ATOMIC();
if (sCanSleepCallback != NULL && sCanSleepCallback())
{
EMU_EnterEM2(true);
}
CORE_EXIT_ATOMIC();
// TODO OT will handle an interrupt here and it mustn't call any RAIL APIs
while (RAIL_Wake(0) != RAIL_STATUS_NO_ERROR)
{
}
if (sDeviceOutOfSleepCb != NULL)
{
sDeviceOutOfSleepCb();
}
}
else
{
CORE_ENTER_ATOMIC();
if (sCanSleepCallback != NULL && sCanSleepCallback())
{
EMU_EnterEM1();
}
CORE_EXIT_ATOMIC();
}
}
}
void otSysProcessDrivers(otInstance *aInstance)
{
sInstance = aInstance;