Starting Point for Kinetis Low Power LLS Mode

In “IoT: FreeRTOS Down to the Micro Amps” I’m using an application with FreeRTOS to get down in micro amps low power mode. Well, nearly all or my applications are using FreeRTOS because it makes the application scalable and extensible. Still, for anyone not used to an RTOS, that might be a hard start. So here we go: how to get into the Kinetis Low Power LLS Mode *without* an RTOS.

Power Measurement

Power Measurement

Outline

In this project I create a very simple bare-metal application for the Freescale FRDM-KL25Z board. All what it does is to blink an LED from the Low Power wake-up interrupt to show that the application is running. Right after the wake-up interrupt, it enters again the LLS low power mode. So I enter LLS low power mode, and the timer interrupt will wake me up every second. As in LLS mode only a few timer and wake-up sources are available, I’m going to use the 1 kHz Low Power clock source.

The project and all the settings is available on GitHub at the link provided at the end of this article.

Processor Expert Components

In this project, I’m using the following components:

  1. 3 LED components (RGB)
  2. TimerInt with TimerUnit_LDD for the low power wake-up timer
  3. WAIT component to flash different LED’s after power-up with a delay.
Processor Expert Components

Processor Expert Components

Timer

As timer I use the LPTMR with a period of one second:

Low Power 1 Second Timer

Low Power 1 Second Timer

Inside the period settings, I configure it to use the LPO 1 kHz timer as clock source, as this clock still will run in LLS mode:

LPO 1 kHz Source

LPO 1 kHz Source

CPU Component

First, we need to configure the CPU component to be ready for low power mode. I enable the methods SetOperationMode() and GetLLSWakupFlags() (so the black x is removed):

Enabled SetOperationMode and GetLLSWakeupFlags

Enabled SetOperationMode and GetLLSWakeupFlags

In the CPU properties, I enable all low power modes, configure LPTMR0 as wake-up source, and enable the INT_LLW wake-up interrupt:

Enabled Low Power Modes

Enabled Low Power Modes

Then I configure the WAIT, SLEEP and STOP modes (these are the three different low power modes in Processor Expert):

WAIT, SLEEP and STOP modes

WAIT, SLEEP and STOP modes

Source Files

Time to write the application code. I’m using Application.h and Application.c:

/*
* Application.h
*
*  Created on: Mar 16, 2014
*      Author: Erich Styger
*/

#ifndef APPLICATION_H_
#define APPLICATION_H_

/*! \brief callback called from the LLS wake-up interrupt */
void APP_OnLLSWakeUpInterrupt(void);

/*! \brief callback called from low power timer interrupt */
void APP_TimerInterrupt(void);

/*! \brief application main entry point */
void APP_Run(void);

#endif /* APPLICATION_H_ */

The the Application.c looks like this:

/*
* Application.c
*
*  Created on: Mar 16, 2014
*      Author: Erich Styger
*/

#include "Application.h"
#include "LED1.h"
#include "LED2.h"
#include "LED3.h"
#include "WAIT1.h"
#include "LPTMR_PDD.h"

void APP_OnLLSWakeUpInterrupt(void) {
  uint32_t tmp;

  tmp = Cpu_GetLLSWakeUpFlags();
  if (tmp&LLWU_INT_MODULE0) { /* LPTMR */
    LPTMR_PDD_ClearInterruptFlag(LPTMR0_BASE_PTR); /* Clear interrupt flag */
  }
}

void APP_TimerInterrupt(void) {
  LED1_Neg(); /* red LED */
}

void APP_Run(void) {
  LED2_On();
  WAIT1_Waitms(1000);
  LED2_Off();
  for(;;) {
    //LED3_Neg(); /* blue LED */
    //Cpu_SetOperationMode(DOM_WAIT, NULL, NULL); /* next interrupt will wake us up */
    //Cpu_SetOperationMode(DOM_SLEEP, NULL, NULL); /* next interrupt will wake us up */
    Cpu_SetOperationMode(DOM_STOP, NULL, NULL); /* next interrupt will wake us up */
  }
}

From main() in ProcessorExpert.c I call APP_Run():

#include "PE_Types.h"
#include "PE_Error.h"
#include "PE_Const.h"
#include "IO_Map.h"

/* User includes (#include below this line is not maintained by Processor Expert) */
#include "Application.h"

/*lint -save  -e970 Disable MISRA rule (6.3) checking. */
int main(void)
/*lint -restore Enable MISRA rule (6.3) checking. */
{
  /* Write your local variable definition here */

  /*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/
  PE_low_level_init();
  /*** End of Processor Expert internal initialization.                    ***/

  APP_Run();

  /*** Don't write any code pass this line, or it will be deleted during code generation. ***/
  /*** RTOS startup code. Macro PEX_RTOS_START is defined by the RTOS component. DON'T MODIFY THIS CODE!!! ***/
  #ifdef PEX_RTOS_START
    PEX_RTOS_START();                  /* Startup of the selected RTOS. Macro is defined by the RTOS component. */
  #endif
  /*** End of RTOS startup code.  ***/
  /*** Processor Expert end of main routine. DON'T MODIFY THIS CODE!!! ***/
  for(;;){}
  /*** Processor Expert end of main routine. DON'T WRITE CODE BELOW!!! ***/
} /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/

And from Events.c I call my hooks:

/*
** ===================================================================
**     Event       :  TI1_OnInterrupt (module Events)
**
**     Component   :  TI1 [TimerInt]
**     Description :
**         When a timer interrupt occurs this event is called (only
**         when the component is enabled -  and the events are
**         enabled - ). This event is enabled only if a
**          is enabled.
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void TI1_OnInterrupt(void)
{
  APP_TimerInterrupt();
}

/*
** ===================================================================
**     Event       :  Cpu_OnLLSWakeUpINT (module Events)
**
**     Component   :  Cpu [MKL25Z128LK4]
*/
/*!
**     @brief
**         This event is called when Low Leakage WakeUp interrupt
**         occurs. LLWU flags indicating source of the wakeup can be
**         obtained by calling the [GetLLSWakeUpFlags] method. Flags
**         indicating the external pin wakeup source are automatically
**         cleared after this event is executed. It is responsibility
**         of user to clear flags corresponding to internal modules.
**         This event is automatically enabled when [LLWU interrupt
**         request] is enabled.
*/
/* ===================================================================*/
void Cpu_OnLLSWakeUpINT(void)
{
  APP_OnLLSWakeUpInterrupt();
}

Summary

This application shows how to set up an application for Low Power LLS mode, in a bare-metal mode. So it should be useful for you if you want to have a starting point for your own project without an RTOS.

The project and sources are available on GitHub.

PS: the KL25Z should use about 3 micro amps in LLS mode. I measure twice as much on the FRDM-KL25Z RevD board, because other components on the board are making the measurement inaccurate.

Happy LowPowering 🙂

Advertisements

28 thoughts on “Starting Point for Kinetis Low Power LLS Mode

  1. Hi Erich

    Thanks for this timely project. I am also working on getting low power operation on a FRDM-KL25Z so it is interesting to compare my results with yours.

    My own code does pretty much the same thing as yours, and I can get about 1.2uA current on the FRSM-KL25Z (Rev D) in LLS mode, and 1.9uA on a modified version where I change the crystal to a 32kHz crystal.

    I have run your LowPower_LLS project on my FRDM-KL25Z and get a current of about 1.7uA (measured on two different boards). There is a spike every second when the timer wakes up.

    So I am interested to know why you seem to measure higher currents than I do. Is it the PCB revision – are you using a Rev E? Do you have the debugger interface active? I am measuring the current by cutting the JP4 link and bridging it with a 10 ohm resistor and measuring the voltage with a volt meter.

    The KL25Z is not perfectly isolated from the rest of the circuit: it is not impossible that the debug interface and the accelerometer interface (powered on the USB side of JP4) could be influencing our readings. I am normally meticulous about ensuring that GPIO pins are in the right state – e.g. not floating. Mis-configured pins can have a big effect on sleep current. The Kinetis family seems to have a novel feature where if the PORTx_PCRn register MUX bits are set to disabled, you can overlook how the pins are terminated. (I think…)

    FYI: I changed the toggling LED in your app with code that clears PTB8 in APP_OnLLSWakeUpInterrupt() and sets it in APP_TimerInterrupt(). This makes no different to the power consumption (since the LEDs draw their current on the USB side of JP4) but allows me (monitoring PTB8 on a scope) to see that there is about 10us between these two events.

    I will now return to FreeRTOS – so far I have not managed to get it into a low current state. For me your Freedom_LowPower project draws 610uA. (Would you care to revisit this?)

    Like

    • I used in that post an older RevD board. I know these boards make it hard to measure things as there are other components drawinig current. I have re-run that LLS mode application on a KL25Z RevE board, with R73. R81 and R74 removed (with jumper headers installed). Now I measure the current flow thorugh J4 with a multimeter, and I get 2.05 uA (with the spike you report during timer wake-up).

      Like

  2. Hi Erich,

    How do I use two timers at the same time?
    One is counter in wake status and the interrupt period is 1ms
    The other is for LLWU source and the interrupt period is 300ms.

    Thanks.
    BR,
    Sean

    Like

      • Dear Sir,

        Sorry for reply too late.
        I used this example. And I used interrupt period 1ms in run status. But I want to change the interrupt period to 300ms in sleep status to wake up the device, and then change back to 1ms after wake up. How could I do?

        Thanks.
        BR,
        Sean

        Like

      • If you are using my latest FreeRTOS component, then it has two additional hooks: vOnPreSleepProcessing() and vOnPostSleepProcessing(): you can use the hooks to do whatever you want, including changing the clock frequency.

        Like

  3. Great info here, Erich!

    I have successfully used LLS mode on the KL25Z. Now I was trying out the same on the FRDM-K64F Board, but ran into the following problem:
    In Processor Expert –> CPU component Properties –> Low Power mode settings
    …there are only two categories: “Allowed Power modes” and “Operation mode settings”. Where is the “LLWU settings” category?

    Without that category it seems difficult to select a “wake-up source”, etc…

    Any suggestions? Have you tried using the LLS mode on the K64F Board?

    Regards,
    Erik

    Like

    • Hi Erik,
      thanks! But I have not used low power modes for the K64F yet. Simply because the boards I have used with it did not need in low power mode (always connected to power). But looking at the CPU component, it looks it now only supports the Kinetis SDK :-(. Which means a completely different way and method has to be used. I would need to wrap my head around this first (speaking in pictures)….

      Erich

      Like

  4. Hi Mr Styger
    I am currently trying to use the CMP module in LLS mode. The CMP should wake up the MCU on rising and falling edge. As soon as the LLS is entered, the CMP module throws numerous interrupts (with a delay of 49us between the interrupts) through NVIC, altought there is no edge in the analog signal. The LLWU interrupt is only sporadically called by CMP, not on every edge as it should.
    What i have already checked:
    – voltage reference of CMP
    – LLS works in combination with low power timer. If I add the CMP module, the LTPM does not work reliable any more.
    – Resetting the interrupt flags of CMP (similar to LPTM) in LLWU interrupt routine.
    – I considered the errata sheet, which contains two entrys of CMP and LLS => Recommended solutions do not solve the problem.

    Project setup:
    – Kinetis K22P64M120
    – Eclipse with Processor Expert plugin
    – IAR 6.6 (debug)

    Have you ever used the CMP with LLS? Any suggestions?

    Regards,
    Mathias

    Like

    • Hi Mathais
      I have not used the comparator, but here are a few extra things you could check:
      – As you leave and enter low power mode the processor current will change and this may lead to changes in supply voltage. Could this be causing the comparator to trigger?
      – Do you have plenty of power decoupling capacitance, and decoupling on your analog inputs?
      – Have you enabled the comparator output function, so you can monitor the comparator’s output on a pin? Then you could be certain whether the interrupts you see coincide with real comparator transitions or some other reason.
      – Explore settings for filtering and hysteresis. The KL15 data sheet says “The window and filter functions are not available in LLS”. Are you relying on some feature that vanishes when you sleep?
      – Are you comparing two external voltages or one voltage and the internal DAC? What voltages do you expect these to be?

      Like

      • Hi
        – Supply voltage looks smooth
        – Decoupling capacitors are similar to TWRK21-Evalboard
        – CMP works correctly => The problem must on NVIC or LLWU
        – No filter or window functions are enabled. Hysteresis is set to 30mV.
        – I compare an external votlage with the internal reference. I already changed it to comparing two external voltages, internal bandgap,.. with no effect.

        I tested some more cases:
        – If the CMP event is disabled in Processor Expert, it no longer throws interrupts through NVIC (makes sense). But the LLWU does not work too, respectively only throws ISR’s randomly.
        – If the CMP event is disabled and it triggers only on rising edges, everything is functioning.
        – IF the CMP event is enabled and it triggers only on rising edges, the LLWU functions and the NVIC only throws interrupts if the voltage at the CMP has a high level.
        – If all events of Processor Expert are disabled and two CMP’s should trigger independently at either falling or rising edge, the LLWU stops working again. I’m really confused.

        It seems that there are two problems:
        1. NVIC interrupt throwing during LLS. Maybe a problem of priority..
        2. LLWU fails, if two wakeup sources are active.

        Have you ever used two wakeup sources of LLWU?

        Regards,
        Mathias

        Like

  5. Hi Erich,
    I am working with Freescale KL-15 processor and PE version (Version: 10.4.0
    Build: b140319). Initially I am trying to use your FRDM KL-25Z LLS example, when I import the project in PE 10.4.0, I do not see the following three Methods under the CPU (SetClockConfiguration, GetClockConfiguration and SetOPerationMode). Any suggestions or ideas why these methods are not appearing under the CPU?

    regards,
    Mo

    Like

  6. Hi Erich,

    I am using FRDM-KL25Z and trying to use the RTC with KSDK 1.3 and PE. Mi idea is to wake up the processor from LLS mode with an alarm.

    My problem appears when trying to set a date, no matter the fields in the structure rtc_datetime_t, the date always sets to:

    {year = 2106, month = 2, day = 6, hour = 6, minute = 28, second = 15 ‘\017’}

    I have connected PTC3 to PTC1 to clock the RTC with the 32kHz source. I am able to blink an LED in trtcTimer1_SecIRQHandler,so the RTC is working but problem is saving the correct date.

    When I call RTC_DRV_SetDatetime, and step into it, I notice that in function CLOCK_SYS_GetExternalRefClock32kFreq it goes through this case:

    case kClockEr32kSrcRtc: /* RTC 32k clock */
    freq = g_xtalRtcClkFreq;
    break;

    And it returns a frequency of 0 as the variable g_xtalRtcClkFreq is global and never initialized. So the variable seconds in RTC_DRV_SetDatetime is set to 0 too. Do you know how can I solve this?

    Thanks in advance.

    Valentín

    Like

    • I could solve it by assigning 32768 to g_xtalRtcClkFreq in fsl_mcg_hal.c. But is it ok that I have to do it manually?

      Thanks

      Like

      • Hi Valentin,
        I have not used the SDK RTC component (yet?). But the advantage of the SDK is that you can do such things manually. So I think setting that manually is ok.

        Like

  7. Hello Erich,

    I would like to use the sleep mode (STOP) in KL25Z128 but to wake up use the different interrupts (ExtInt) that I have, Is this possible? Do you have some example? I have to use another sleep mode? I was checking the manual an it says “Normal stop mode (STOP): Asynchronous Wakeup Interrupt Controller (AWIC) is used to wake from interruptions.”

    Thanks for all the help,
    Iñaki

    Like

  8. Hi Erich, very good post as always!

    I’m having an issue here. I used a delay to blink a LED when the MCU wakes up. Firts time (before the first sleep), its blinks normal. But when the MCU sleeps, and wakes up after I send something on my UART port, the blink function takes a long time. I’m using the WAIT component with a 250ms delay. Seems like there’s an issue with my clock (after the sleep). It’s taking much more than 250ms to blink the LED.

    Like

    • Hi Henrique,
      are you using a Cortex-M4(F) or the M0(+)? If you are using the M4(F) and have enabled the ‘Use Cycle Counter’ option, can you try turnig it off (I just fixed an issue with this today).

      Like

      • Thanks for the reply Erich! I’m using Cortex-M4 (MK20). I fixed the issue half an hour after I posted here. Inside APP_OnLLSWakeUpInterrupt(), I called Cpu_SetOperationMode() with DOM_RUN, and seems to work normal now.
        Cpu_SetOperationMode(DOM_RUN,NULL,NULL);

        Like

  9. I know this thread is very old but I am having issues finding a solution to my problem.
    I am using the K10 series and would like to use the 4MHz internal clock while awake and the 32.768kHz internal clock while asleep.

    Looking in the user manual doesn’t seem to be helping.

    Am I missing something?

    Like

    • I cannot comment on the K10 as I have not used it in my designs, but I would think that if you use Processor Expert, things should be pretty much possible. You would need to go through the clock gear shifting. Processor Expert has the option to allow multiple different clock settings, and it generates the correct code to shift between them. Otherwise you might look a the MCUXpresso SDK (if it does support your device) if there are examples for this?

      Like

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s