From 1b9c50112e31c6cd243363f1807d55dd9d39ccae Mon Sep 17 00:00:00 2001 From: Marven Gilhespie <45208741+marvensilabs@users.noreply.github.com> Date: Fri, 3 May 2019 17:10:39 +0100 Subject: [PATCH] [efr32] add support for entering EM2 low-power mode for sleepy devices (#3758) Changes to EFR32 platform to allow entering EM2 low-power mode for sleepy devices sleepy-demo applications added to examples/platforms/efr32. --- configure.ac | 3 + examples/platforms/efr32/Makefile.am | 24 ++ examples/platforms/efr32/README.md | 3 + examples/platforms/efr32/alarm.c | 110 +++-- examples/platforms/efr32/platform-efr32.h | 26 ++ examples/platforms/efr32/radio.c | 16 + .../platforms/efr32/sleepy-demo/Makefile.am | 54 +++ .../platforms/efr32/sleepy-demo/README.md | 100 +++++ .../efr32-sleepy-demo-ftd/Makefile.am | 162 ++++++++ .../sleepy-demo/efr32-sleepy-demo-ftd/main.c | 329 +++++++++++++++ .../efr32-sleepy-demo-mtd/Makefile.am | 162 ++++++++ .../sleepy-demo/efr32-sleepy-demo-mtd/main.c | 384 ++++++++++++++++++ examples/platforms/efr32/system.c | 48 ++- 13 files changed, 1396 insertions(+), 25 deletions(-) create mode 100644 examples/platforms/efr32/sleepy-demo/Makefile.am create mode 100644 examples/platforms/efr32/sleepy-demo/README.md create mode 100644 examples/platforms/efr32/sleepy-demo/efr32-sleepy-demo-ftd/Makefile.am create mode 100644 examples/platforms/efr32/sleepy-demo/efr32-sleepy-demo-ftd/main.c create mode 100644 examples/platforms/efr32/sleepy-demo/efr32-sleepy-demo-mtd/Makefile.am create mode 100644 examples/platforms/efr32/sleepy-demo/efr32-sleepy-demo-mtd/main.c diff --git a/configure.ac b/configure.ac index 0a5bb05f2..5ce636e85 100644 --- a/configure.ac +++ b/configure.ac @@ -1984,6 +1984,9 @@ examples/platforms/cc2650/Makefile examples/platforms/cc2652/Makefile examples/platforms/da15000/Makefile examples/platforms/efr32/Makefile +examples/platforms/efr32/sleepy-demo/Makefile +examples/platforms/efr32/sleepy-demo/efr32-sleepy-demo-ftd/Makefile +examples/platforms/efr32/sleepy-demo/efr32-sleepy-demo-mtd/Makefile examples/platforms/emsk/Makefile examples/platforms/gp712/Makefile examples/platforms/kw41z/Makefile diff --git a/examples/platforms/efr32/Makefile.am b/examples/platforms/efr32/Makefile.am index a4d768a9b..0eb07749f 100644 --- a/examples/platforms/efr32/Makefile.am +++ b/examples/platforms/efr32/Makefile.am @@ -45,6 +45,13 @@ EFR32MG_SDK_SRCDIR = $(top_srcdir)/third_party/silabs libopenthread_efr32_a_CPPFLAGS = \ -D__START=main \ -D__STARTUP_CLEAR_BSS \ + -DPLATFORM_HEADER=\"@top_builddir@/third_party/silabs/gecko_sdk_suite/v2.5/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/efr32/$(EFR32_BOARD_DIR) \ @@ -60,6 +67,11 @@ libopenthread_efr32_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/EFR32MG12_$(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/EFR32MG12P/Include \ -I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/common/inc \ @@ -145,4 +157,16 @@ libopenthread_efr32_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/efr32/README.md b/examples/platforms/efr32/README.md index 62492e31b..26a946f67 100644 --- a/examples/platforms/efr32/README.md +++ b/examples/platforms/efr32/README.md @@ -13,6 +13,9 @@ has rich memory and peripheral resources which can support all OpenThread capabilities. See the "Run the example with EFR32 boards" section below for an example using basic OpenThread capabilities. +See [sleepy-demo/README.md](sleepy-demo/README.md) for instructions for an example that uses the low-energy +modes of the EFR32 when running as a Sleepy End Device. + [efr32mg12p]: http://www.silabs.com/products/wireless/mesh-networking/efr32mg-mighty-gecko-zigbee-thread-soc/device.EFR32MG12P432F1024GL125 ## Toolchain diff --git a/examples/platforms/efr32/alarm.c b/examples/platforms/efr32/alarm.c index 9f5717321..9ae66e57a 100644 --- a/examples/platforms/efr32/alarm.c +++ b/examples/platforms/efr32/alarm.c @@ -32,13 +32,16 @@ * */ +#include #include #include #include #include #include +#include "common/logging.hpp" +#include "platform-efr32.h" #include "utils/code_utils.h" #include "em_core.h" @@ -47,12 +50,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,10 +110,40 @@ 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; - sAlarmT0 = t0; - sAlarmDt = dt; - sIsRunning = true; + 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) + { + sIsRunning = true; + } + else + { + sIsRunning = false; + otLogCritPlat("Alarm set timer, status: %d, dt: %u, t0: %u", status, dt, t0); + } } void otPlatAlarmMilliStop(otInstance *aInstance) @@ -105,33 +151,56 @@ 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_ENABLE_DIAG + if (sAlarmDt > RAIL_TIMER_MAX_DELTA_MS) + { + 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) + { + sIsRunning = true; + } + else + { + sIsRunning = false; + otLogCritPlat("Alarm set timer, status: %d, expires: %u, dt: %u, now: %u", status, sAlarmT0 + sAlarmDt, + dt, now); + } + } + +#if OPENTHREAD_ENABLE_DIAG if (otPlatDiagModeGet()) { otPlatDiagAlarmFired(aInstance); @@ -142,11 +211,6 @@ void efr32AlarmProcess(otInstance *aInstance) otPlatAlarmMilliFired(aInstance); } } - exit: return; } - -void RAILCb_TimerExpired(void) -{ -} diff --git a/examples/platforms/efr32/platform-efr32.h b/examples/platforms/efr32/platform-efr32.h index a82e5b5d4..c956fb7ca 100644 --- a/examples/platforms/efr32/platform-efr32.h +++ b/examples/platforms/efr32/platform-efr32.h @@ -41,10 +41,14 @@ #include "em_system.h" #include "core_cm4.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)); + +/** + * 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/efr32/radio.c b/examples/platforms/efr32/radio.c index 6c34bff40..3723bb3eb 100644 --- a/examples/platforms/efr32/radio.c +++ b/examples/platforms/efr32/radio.c @@ -45,6 +45,7 @@ #include "utils/soft_source_match_table.h" #include "board_config.h" +#include "em_cmu.h" #include "em_core.h" #include "em_system.h" #include "openthread-core-efr32-config.h" @@ -53,6 +54,7 @@ #include "rail.h" #include "rail_config.h" #include "rail_ieee802154.h" +#include "rtcdriver.h" enum { @@ -102,6 +104,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; @@ -178,6 +182,11 @@ static RAIL_Handle_t efr32RailConfigInit(efr32BandConfig *aBandConfig) handle = RAIL_Init(&aBandConfig->mRailConfig, NULL); assert(handle != NULL); + if (gRailHandle == NULL) + { + gRailHandle = handle; + } + status = RAIL_ConfigData(handle, &railDataConfig); assert(status == RAIL_STATUS_NO_ERROR); @@ -288,8 +297,15 @@ static void efr32BandConfigInit(void (*aEventCallback)(RAIL_Handle_t railHandle, void efr32RadioInit(void) { + RAIL_Status_t status; + efr32BandConfigInit(RAILCb_Generic); + CMU_ClockEnable(cmuClock_PRS, true); + RTCDRV_Init(); + status = RAIL_ConfigSleep(gRailHandle, RAIL_SLEEP_CONFIG_TIMERSYNC_ENABLED); + assert(status == RAIL_STATUS_NO_ERROR); + sReceiveFrame.mLength = 0; sReceiveFrame.mPsdu = sReceivePsdu; sTransmitFrame.mLength = 0; diff --git a/examples/platforms/efr32/sleepy-demo/Makefile.am b/examples/platforms/efr32/sleepy-demo/Makefile.am new file mode 100644 index 000000000..139baecad --- /dev/null +++ b/examples/platforms/efr32/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 = \ + efr32-sleepy-demo-mtd \ + efr32-sleepy-demo-ftd \ + $(NULL) + +# Always build (e.g. for 'make all') these subdirectories. + +SUBDIRS = \ + $(NULL) + +if OPENTHREAD_ENABLE_EXECUTABLE +SUBDIRS += efr32-sleepy-demo-mtd efr32-sleepy-demo-ftd +endif + +# Always pretty (e.g. for 'make pretty') these subdirectories. + +PRETTY_SUBDIRS = \ + efr32-sleepy-demo-mtd \ + efr32-sleepy-demo-ftd \ + $(NULL) + +include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/examples/platforms/efr32/sleepy-demo/README.md b/examples/platforms/efr32/sleepy-demo/README.md new file mode 100644 index 000000000..343ceaac9 --- /dev/null +++ b/examples/platforms/efr32/sleepy-demo/README.md @@ -0,0 +1,100 @@ + +# EFR32 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/efr32/README.md](../README.md). + + +## 1. Build + +```bash +$ cd +$ ./bootstrap +$ make -f examples/Makefile-efr32 COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 BOARD=BRD4161A +``` + +Convert the resulting executables into S-Record format and append a s37 suffix. + +```bash +$ cd output/efr32/bin +$ arm-none-eabi-objcopy -O srec efr32-sleepy-demo-mtd efr32-sleepy-demo-mtd.s37 +$ arm-none-eabi-objcopy -O srec efr32-sleepy-demo-ftd efr32-sleepy-demo-ftd.s37 +``` + +In Silicon Labs Simplicity Studio flash one device with the efr32-sleepy-demo-mtd.s37 +image and the other device with the efr32-sleepy-demo-ftd.s37 image. + +For instructions on flashing firmware see [examples/platforms/efr32/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 efr32-sleepy-demo-ftd device is started in the CLI the user shall see: + +``` +efr32-sleepy-demo-ftd started +efr32-sleepy-demo-ftd changed to leader +``` + +When the efr32-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. Buttons on the MTD + +Pressing button 0 on the MTD toggles between operating as a Minimal End Device (MED) and +a Sleepy End Device (SED) with the RX off when idle. + +Pressing button 1 on the MTD sends a multicast UDP message containing the +string "mtd button". The FTD listens on the multicast address and will toggle +LED 0 and display a message in the CLI showing "Message Received: mtd button". + +## 4. Buttons on the FTD + +Pressing either button 0 or 1 on the FTD will send a UDP message to the FTD containing the string +"ftd button". The MTD must first send a multicast message by pressing the MTD's button 1 so that +the FTD knows the address of the MTD to send messages to. + +This will toggle the state of LED0 on the MTD. If the MTD is operating as a sleepy end device then +the MTD polls the parent every 5 seconds for messages and will update the LED state on the +next poll. + +## 5. 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 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. + +## 6. 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/efr32/sleepy-demo/efr32-sleepy-demo-ftd/Makefile.am b/examples/platforms/efr32/sleepy-demo/efr32-sleepy-demo-ftd/Makefile.am new file mode 100644 index 000000000..924f824a2 --- /dev/null +++ b/examples/platforms/efr32/sleepy-demo/efr32-sleepy-demo-ftd/Makefile.am @@ -0,0 +1,162 @@ +# +# 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.5 + +$(top_builddir)/examples/platforms/efr32/libopenthread-efr32.a: + (cd $(top_builddir)/examples/platforms/efr32/ && $(MAKE) $(AM_MAKEFLAGS) libopenthread-efr32.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.5/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/EFR32MG12_$(BOARD)/config \ + -I$(EFR32MG_SDK_SRCDIR)/platform/CMSIS/Include \ + -I$(EFR32MG_SDK_SRCDIR)/platform/base/ \ + -I$(EFR32MG_SDK_SRCDIR)/platform/Device/SiliconLabs/EFR32MG12P/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_DIAG +LDADD_COMMON += \ + $(top_builddir)/src/diag/libopenthread-diag.a \ + $(NULL) +endif # OPENTHREAD_ENABLE_DIAG + +if OPENTHREAD_ENABLE_EXECUTABLE +bin_PROGRAMS += \ + efr32-sleepy-demo-ftd \ + $(NULL) +endif + +efr32_sleepy_demo_ftd_CPPFLAGS = \ + $(CPPFLAGS_COMMON) \ + $(NULL) + +efr32_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) + +efr32_sleepy_demo_ftd_LDFLAGS = \ + $(LDFLAGS_COMMON) \ + $(NULL) + +efr32_sleepy_demo_ftd_LIBTOOLFLAGS = \ + $(LIBTOOLFLAGS_COMMON) \ + $(NULL) + +efr32_sleepy_demo_ftd_SOURCES = \ + $(SOURCES_COMMON) \ + $(NULL) + +if OPENTHREAD_ENABLE_LINKER_MAP +efr32_sleepy_demo_ftd_LDFLAGS += -Wl,-Map=efr32-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/efr32/sleepy-demo/efr32-sleepy-demo-ftd/main.c b/examples/platforms/efr32/sleepy-demo/efr32-sleepy-demo-ftd/main.c new file mode 100644 index 000000000..c20cf2fb9 --- /dev/null +++ b/examples/platforms/efr32/sleepy-demo/efr32-sleepy-demo-ftd/main.c @@ -0,0 +1,329 @@ +/* + * 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 OTCALL 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("efr32-sleepy-demo-ftd started\r\n"); + + setNetworkConfiguration(instance); + otSetStateChangedCallback(instance, handleNetifStateChanged, instance); + initUdp(); + + otIp6SetEnabled(instance, true); + otThreadSetEnabled(instance, true); + +#if OPENTHREAD_ENABLE_DIAG + otDiagInit(instance); +#endif + + 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 OTCALL 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("efr32-sleepy-demo-ftd changed to leader\r\n"); + break; + case OT_DEVICE_ROLE_ROUTER: + otCliOutputFormat("efr32-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; + sockaddr.mScopeId = OT_NETIF_INTERFACE_ID_THREAD; + + 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; + messageInfo.mInterfaceId = OT_NETIF_INTERFACE_ID_THREAD; + + 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/efr32/sleepy-demo/efr32-sleepy-demo-mtd/Makefile.am b/examples/platforms/efr32/sleepy-demo/efr32-sleepy-demo-mtd/Makefile.am new file mode 100644 index 000000000..5fb314c06 --- /dev/null +++ b/examples/platforms/efr32/sleepy-demo/efr32-sleepy-demo-mtd/Makefile.am @@ -0,0 +1,162 @@ +# +# 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.5 + +$(top_builddir)/examples/platforms/efr32/libopenthread-efr32.a: + (cd $(top_builddir)/examples/platforms/efr32/ && $(MAKE) $(AM_MAKEFLAGS) libopenthread-efr32.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.5/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/EFR32MG12_$(BOARD)/config \ + -I$(EFR32MG_SDK_SRCDIR)/platform/CMSIS/Include \ + -I$(EFR32MG_SDK_SRCDIR)/platform/base/ \ + -I$(EFR32MG_SDK_SRCDIR)/platform/Device/SiliconLabs/EFR32MG12P/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_DIAG +LDADD_COMMON += \ + $(top_builddir)/src/diag/libopenthread-diag.a \ + $(NULL) +endif # OPENTHREAD_ENABLE_DIAG + +if OPENTHREAD_ENABLE_EXECUTABLE +bin_PROGRAMS += \ + efr32-sleepy-demo-mtd \ + $(NULL) +endif + +efr32_sleepy_demo_mtd_CPPFLAGS = \ + $(CPPFLAGS_COMMON) \ + $(NULL) + +efr32_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) + +efr32_sleepy_demo_mtd_LDFLAGS = \ + $(LDFLAGS_COMMON) \ + $(NULL) + +efr32_sleepy_demo_mtd_LIBTOOLFLAGS = \ + $(LIBTOOLFLAGS_COMMON) \ + $(NULL) + +efr32_sleepy_demo_mtd_SOURCES = \ + $(SOURCES_COMMON) \ + $(NULL) + +if OPENTHREAD_ENABLE_LINKER_MAP +efr32_sleepy_demo_mtd_LDFLAGS += -Wl,-Map=efr32_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/efr32/sleepy-demo/efr32-sleepy-demo-mtd/main.c b/examples/platforms/efr32/sleepy-demo/efr32-sleepy-demo-mtd/main.c new file mode 100644 index 000000000..6d3c6c158 --- /dev/null +++ b/examples/platforms/efr32/sleepy-demo/efr32-sleepy-demo-mtd/main.c @@ -0,0 +1,384 @@ +/* + * 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 + +// Constants +#define MULTICAST_ADDR "ff03::1" +#define MULTICAST_PORT 123 +#define RECV_PORT 234 +#define SLEEPY_POLL_PERIOD_MS 5000 +#define MTD_MESSAGE "mtd button" +#define FTD_MESSAGE "ftd button" + +// Types +typedef struct ButtonArray +{ + GPIO_Port_TypeDef port; + unsigned int pin; +} ButtonArray_t; + +// Prototypes +bool sleepCb(void); +void setNetworkConfiguration(otInstance *aInstance); +void OTCALL 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); + +#if OPENTHREAD_ENABLE_DIAG + otDiagInit(instance); +#endif + + while (!otSysPseudoResetWasRequested()) + { + otTaskletsProcess(instance); + otSysProcessDrivers(instance); + + applicationTick(); + + // Put the EFR32 into deep sleep if callback sleepCb permits. + efr32Sleep(); + } + + otInstanceFinalize(instance); + return 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 OTCALL 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; + sMulticastSockAddr.mScopeId = OT_NETIF_INTERFACE_ID_THREAD; + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.mPort = RECV_PORT; + sockaddr.mScopeId = OT_NETIF_INTERFACE_ID_THREAD; + + 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; + messageInfo.mInterfaceId = sMulticastSockAddr.mScopeId; + + 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/efr32/system.c b/examples/platforms/efr32/system.c index 81e384566..a4c94ca45 100644 --- a/examples/platforms/efr32/system.c +++ b/examples/platforms/efr32/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,7 @@ void halInitChipSpecific(void); otInstance *sInstance; +static bool (*sCanSleepCallback)(void); void otSysInit(int argc, char *argv[]) { @@ -98,6 +104,44 @@ void otSysDeinit(void) #endif } +void efr32SetSleepCallback(bool (*aCallback)(void)) +{ + sCanSleepCallback = aCallback; +} + +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(); + + while (RAIL_Wake(0) != RAIL_STATUS_NO_ERROR) + { + } + } + else + { + CORE_ENTER_ATOMIC(); + if (sCanSleepCallback != NULL && sCanSleepCallback()) + { + EMU_EnterEM1(); + } + CORE_EXIT_ATOMIC(); + } + } +} + void otSysProcessDrivers(otInstance *aInstance) { sInstance = aInstance;