I’m giving a presentation on Z-Wave where you can ask questions and get answers about the opening of the Z-Wave specification among other topics. There are more Tech Talks scheduled and several recent topics were recorded. Follow the links below.
Good news! We have added new Tech Talks. Join us on Tuesdays and Thursdays for live virtual, technical discussions hosted by a lead engineer with time allocated for your questions. We’ll cover topics like battery optimization with BG22, using Z-Wave for your Smart Home Solutions and more. You don’t want to miss these next talks.
Register by clicking here: Tech Talks: Z-Wave Smart Home Solutions by Doctor Z-Wave Tomorrow April 23 at 4 p.m ET
Battery Optimization with BG22 Tues, April 28 at 3 p.m. CT Max Performance on BLE – Simultaneous Connections, Beacons and Scanning Thurs, April 30 at 3 p.m. CT SubGHz Proprietary and Connect Software Stack Tues, May 5 at 3 p.m. CT How to Measure and Debug Network Performance – Using Silicon Labs Network Analyzer Thurs, May 7 at 3 p.m. CT
The Silicon Labs EFR32 family of IoT microcontrollers are very flexible and can do a ton of cool stuff. However, along with all that flexibility comes a lot of complexity. With that complexity are default settings that work fine for many applications but in some cases you want to dig into the details to come up with an optimal solution. In this post I’ll show how to speed up the wake up time for the Z-Wave ZGM130S chip from a GPIO.
But first – a caveat: This post applies to Z-Wave SDK 7.13.x. Future releases of the SDK may have different methods for sleep/wake and thus may require a different solution.
The Problem
Frequently Listening Routing Slaves (FLiRS) devices like door locks and many thermostats spend most of their time in Energy Mode 2 (EM2) to conserve battery power. Once per second they wake up briefly and listen for a Beam from an always-on device. If there is a beam, the FLiRS device will wakeup and receive the Z-Wave command. This allows battery powered devices to use very little power but still be able to respond to a Z-Wave command within one second. FLiRS devices use more battery power than fully sleeping devices like most sensors which use Hibernate Sleep mode (EM4). To wake every second the ZGM130 has to wake quickly and go right back to sleep to minimize power. The problem with EM4 is that it takes a few tens of milliseconds to wake up as the entire CPU and RAM have to be initialized as they were powered down to save power. For a FLiRS device, it’s more efficient to keep RAM powered but in a low-power state and resume quickly to go right back to sleep if there is no beam. Typically the ZGM130 can wake up in about 500 microseconds from EM2. But in many cases this is still too long of a time to stay awake if there are other interrupts such as UARTs or other sensors.
The scope shot above shows the processing that takes place by default on the ZGM130S. In this case I am using a WSTK to drive the SPI pins of another WSTK running the DoorLockKeyPad sample application. The chip is in EM2 at the start of the trace. When SPISEL signal goes low, the chip wakes up. But it is running on the HFRCO oscillator which is not accurate enough to run the radio but it is stable and usable in just a few microseconds. Thus, the SPI clock and data is captured in the USART using this clock. However, by default the Interrupt Service Routine is blocked waiting for the HFXO to stabilize. The 39MHz HFXO crystal oscillator has the accuracy required for the radio.
The question is what’s going on during this 500usec? The answer is the CPU is just waiting for the HFXO to stabilize. Can we use this time to do some other work? Fortunately, the answer is YES! The challenge is that it takes some understanding and some code which I’ll describe below.
The Solution
There are three functions that do the majority of the sleep processing. These are provided in source code so you can read the code but you should not change it. Instead you’ll provide a callback function to do your processing while the chip is waking up.
Simplified Sleep Processing Code:
SLEEP_Sleep in sleep.c: The main function called to enter sleep
CORE_ENTER_CRITICAL – PRIMASK=1 mask interrupts
DO-WHILE loop
Call enterEMx() – this is where the chip sleeps
Call restoreCallback (return 0 to wake, 1 to sleep)
Call EMU_Restore – waits for HFXO to be ready ~500us
CORE_EXIT_CRITICAL – ISRs will now run
enterEMx() in sleep.c:
sleepCallback called
Call EMU_EnterEM[1-4]
wakeupCallback after returning from EMU_EnterEMx
EMU_EnterEM2 in em_emu.c:
Scales voltage down
Call EMU_EM23PresleepHook()
__WFI – Wait-For-Interrupt instruction – ZGM130 sleeps here
Call EMU_EM23PostsleepHook() ~ 17usec after wakeup
Voltage Scale restored which takes ~20us
The code is in sleep.c in the SDK which has a lot more detail but at a high level this is what you need to know. The important part to understand here is where the “hooks” are and how to use them.
Use Sleep_initEx() to assign:
sleepCallback – called just before sleeping
restoreCallback – Return 0 to wake, 1 to sleep
wakeupCallback – called after waking
Sleep_initEx() input is a pointer to a structure with the three callbacks or NULL if not used
Define the function:
EMU_EM23PresleepHook()
EMU_EM23PostsleepHook()
These are both WEAK functions with nothing in them so if you define them then the compiler will install them
The two EMU_EM23* weak functions are run immediately before/after the Wait-For-Interrupt (WFI) instruction which is where the CPU sleeps. These are very low level functions and while you can use them I recommend using the callbacks from Sleep_initEx().
The SLEEP_initEx() function is the one we want to use and in particular the restoreCallback. The comments around the restoreCallback function talk about restoring the clocks but if the function returns a 0 the chip will wake up and if it returns a 1 then it will immediately go back to sleep which is what we want! You can use the other two hooks if you want but the restoreCallback is the key one since it will immediately put the chip back to sleep if everything is idle.
The key to using ANY of these function is that you CANNOT call ANY FreeRTOS functions! You cannot send any Z-Wave frames or call any Z-Wave function as they all require the RTOS. At this point in the wakeup processing the RTOS is not running! All you can do in these routines is to capture data and quickly decide if everything is idle and to go back to sleep. If there is more processing needed, then return 0 and wait for the event in the RTOS and process the data there. You also don’t want to spend too much time in these routines as it may interfere with the timing of the RTOS. A hundred microseconds is probably fine but longer you should wait for the HFXO.
In ApplicationInit() you will call Sleep_initEx() like this:
const SLEEP_Init_t sleepinit = {NULL, NULL, CheckSPI};
...
ZW_APPLICATION_STATUS ApplicationInit(EResetReason_t eResetReason) {
...
SLEEP_InitEx(&sleepinit); // call checkSPI() upon wakeup from EM2.
...
}
...
uint32_t CheckSPI(SLEEP_EnergyMode_t emode) {
uint32_t retval=0; // wake up by default
if (GPIO_IntGetEnabled() & 0x0000AAAA) { // Check SPI
GPIO_ODD_IRQHandler(); // service the GPIO interrupt
// wait for all the bytes to come in and compute checksum
NVIC->ICPR[0] = NVIC->ICPR[0]; //clear NVIC pending interrupts
if (!SPIDataError && !IsWakeupCausedByRtccTimeout()) {
retval=1; // go back to sleep!
}
}
return(retval); // 0=wakeup, 1=sleep
}
Recall that every second the FLiRS device has to check for a Z-Wave beam which is triggered by the RTCC timer. Thus the check for IsWakeupCausedByRtccTimer ensures that the beaming still works.
This scope shot shows the wake up processing of the ZGM130S:
SPISEL_N SPI chip select signal goes low triggering a GPIO_ODD interrupt
The chip wakes up, the HFRCO begins oscillating
HFRCO begins oscillating in a few microseconds
Once HFRCO is running, the peripherals are functional
SPI data can begin shifting once the HFRCO is running
The default HFRCO frequency is 19MHz but can be increased
Higher frequencies for HFRCO also may need more wait states for the CPU and will use more power
The WFI instruction that put the CPU to sleep is exited here
EMU_EM23PostSleepHook function is called if defined
After returning from PostSleepHook, the VSCALE is returned to full power which takes about 10usec
It is best to wait for the voltage to be powered up to ensure all logic is running at optimal speeds
EMU_EnterEM2 is exited and restoreCallback is called if initialized
This is the function where the ISR should be called to process data
If the data says things are idle and want to go back to sleep, return 1
If more analysis is needed, then return 0
Carefully clear the interrupt bits
First clear the peripheral Interrupt Flags
Then clear the NVIC Interrupt pending register
NVIC->ICPR[n]=NVIC->ICPR[n] where n is 0-1 depending on your interrupt
Make sure there aren’t other reasons to wake up fully
!IsWakeupCausedByRtccTimeout() is the 1s FLiRS interrupt
There may be other reasons to wake up which is application dependent
In this example the SPI data is being fetched from the USART at each toggle of the GPIO
The final toggle shows that the checksum was computed and the data is idle so go back to sleep
The chip returns back to sleep in a few more microseconds
Total processing time of this interrupt is less than 200usec which is a fraction of the time just waiting for the HFXO to stabilize
Much of that time is receiving and processing the SPI data
It is possible to sleep in under 50usec if the check for idle is quicker
If your peripheral processing will take significantly less than 500usec, then it may be more efficient to process the data using the HFRCO and not wait for the HFXO to power up. But if your application needs more processing, then you are probably better off waiting. Each application must make their own calculations to determine the most efficient path.
What About Sleeping Devices?
Fully sleeping devices (EM4 also known as RSS – Routing Sleeping Slaves) have entirely different wake/sleep processing. For sleeping slaves the processor and RAM have to be re-initialized and the chip essentially boots out of reset. All that initialization takes quite a bit of time – a few tens of milliseconds. If your device needs to do a lot of frequent checking of a sensor, then it might make more sense to force it to stay in EM2 by setting a Power Lock to PM_TYPE_PERIPHERAL. For more details on power locks see INS14259 section 7.6. Deciding which way to go is application specific so you have to make the calculations or measurements to find the right balance for your project.
This is a complex posting but I hope I’ve made it clear enough to enable you to optimize your application firmware. Let me know what you think by leaving a comment below.