From 87105e0694d06849af0b929476709ea1d3f4e93c Mon Sep 17 00:00:00 2001 From: Marven Gilhespie <45208741+marvensilabs@users.noreply.github.com> Date: Wed, 4 Sep 2019 20:02:58 +0100 Subject: [PATCH] [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. --- configure.ac | 3 + examples/platforms/efr32mg12/radio.c | 2 +- examples/platforms/efr32mg21/Makefile.am | 29 +- examples/platforms/efr32mg21/alarm.c | 136 ++++-- examples/platforms/efr32mg21/platform-band.h | 13 +- examples/platforms/efr32mg21/platform-efr32.h | 26 ++ examples/platforms/efr32mg21/radio.c | 393 +++++++++-------- examples/platforms/efr32mg21/rail_config.h | 4 +- .../efr32mg21/sleepy-demo/Makefile.am | 54 +++ .../platforms/efr32mg21/sleepy-demo/README.md | 87 ++++ .../sleepy-demo/sleepy-demo-ftd/Makefile.am | 156 +++++++ .../sleepy-demo/sleepy-demo-ftd/main.c | 323 ++++++++++++++ .../sleepy-demo/sleepy-demo-mtd/Makefile.am | 156 +++++++ .../sleepy-demo/sleepy-demo-mtd/main.c | 395 ++++++++++++++++++ examples/platforms/efr32mg21/system.c | 57 ++- 15 files changed, 1594 insertions(+), 240 deletions(-) create mode 100644 examples/platforms/efr32mg21/sleepy-demo/Makefile.am create mode 100644 examples/platforms/efr32mg21/sleepy-demo/README.md create mode 100644 examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-ftd/Makefile.am create mode 100644 examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-ftd/main.c create mode 100644 examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-mtd/Makefile.am create mode 100644 examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-mtd/main.c diff --git a/configure.ac b/configure.ac index a48b45b61..dcf30f4f8 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/examples/platforms/efr32mg12/radio.c b/examples/platforms/efr32mg12/radio.c index 6460068e1..836b349a4 100644 --- a/examples/platforms/efr32mg12/radio.c +++ b/examples/platforms/efr32mg12/radio.c @@ -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}; diff --git a/examples/platforms/efr32mg21/Makefile.am b/examples/platforms/efr32mg21/Makefile.am index c84ee6471..1edd73f6c 100644 --- a/examples/platforms/efr32mg21/Makefile.am +++ b/examples/platforms/efr32mg21/Makefile.am @@ -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 diff --git a/examples/platforms/efr32mg21/alarm.c b/examples/platforms/efr32mg21/alarm.c index 36b265fec..4c2c3d9e6 100644 --- a/examples/platforms/efr32mg21/alarm.c +++ b/examples/platforms/efr32mg21/alarm.c @@ -32,13 +32,17 @@ * */ +#include #include #include +#include "openthread-system.h" #include #include #include +#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) -{ -} diff --git a/examples/platforms/efr32mg21/platform-band.h b/examples/platforms/efr32mg21/platform-band.h index 9afdd02c2..f219c3638 100644 --- a/examples/platforms/efr32mg21/platform-band.h +++ b/examples/platforms/efr32mg21/platform-band.h @@ -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; diff --git a/examples/platforms/efr32mg21/platform-efr32.h b/examples/platforms/efr32mg21/platform-efr32.h index b3c0f180b..0f62c47aa 100644 --- a/examples/platforms/efr32mg21/platform-efr32.h +++ b/examples/platforms/efr32mg21/platform-efr32.h @@ -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_ diff --git a/examples/platforms/efr32mg21/radio.c b/examples/platforms/efr32mg21/radio.c index b2b34dc94..9b072974c 100644 --- a/examples/platforms/efr32mg21/radio.c +++ b/examples/platforms/efr32mg21/radio.c @@ -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; diff --git a/examples/platforms/efr32mg21/rail_config.h b/examples/platforms/efr32mg21/rail_config.h index c268b8882..72c7c0586 100644 --- a/examples/platforms/efr32mg21/rail_config.h +++ b/examples/platforms/efr32mg21/rail_config.h @@ -1,9 +1,9 @@ #ifndef __RAIL_CONFIG_H__ #define __RAIL_CONFIG_H__ -#include -#include "rail_types.h" #include "board_config.h" +#include "rail_types.h" +#include #define RADIO_CONFIG_XTAL_FREQUENCY 38400000UL diff --git a/examples/platforms/efr32mg21/sleepy-demo/Makefile.am b/examples/platforms/efr32mg21/sleepy-demo/Makefile.am new file mode 100644 index 000000000..e9ff42f40 --- /dev/null +++ b/examples/platforms/efr32mg21/sleepy-demo/Makefile.am @@ -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 diff --git a/examples/platforms/efr32mg21/sleepy-demo/README.md b/examples/platforms/efr32mg21/sleepy-demo/README.md new file mode 100644 index 000000000..2e91c77fa --- /dev/null +++ b/examples/platforms/efr32mg21/sleepy-demo/README.md @@ -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 +$ ./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. + diff --git a/examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-ftd/Makefile.am b/examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-ftd/Makefile.am new file mode 100644 index 000000000..df14cc4d2 --- /dev/null +++ b/examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-ftd/Makefile.am @@ -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 diff --git a/examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-ftd/main.c b/examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-ftd/main.c new file mode 100644 index 000000000..fb6c11940 --- /dev/null +++ b/examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-ftd/main.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 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); +} diff --git a/examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-mtd/Makefile.am b/examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-mtd/Makefile.am new file mode 100644 index 000000000..2e4aa1be9 --- /dev/null +++ b/examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-mtd/Makefile.am @@ -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 diff --git a/examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-mtd/main.c b/examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-mtd/main.c new file mode 100644 index 000000000..5b88c539f --- /dev/null +++ b/examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-mtd/main.c @@ -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 +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 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); + } + } +} diff --git a/examples/platforms/efr32mg21/system.c b/examples/platforms/efr32mg21/system.c index 0fefb0c7a..dbecf7f20 100644 --- a/examples/platforms/efr32mg21/system.c +++ b/examples/platforms/efr32mg21/system.c @@ -34,19 +34,24 @@ #include +#include #include #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;