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 🙂

46 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

  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

      • Hi, Thanks, I am trying to repeat what you have done as a practice but I find several tiny issues with CodeWarrior that is slowing me down. I know this might not be directed to your post but your answer can be very valuable to me. I cannot find some of the components in my CodeWarrior. For example, I cannot find the WAIT or LED. I have read some of your pages about missing components. I refreshed the pages and also my software is updated. Do you have any suggestions?

        Thanks,
        Bee Zee

        Like

      • Thanks alot, it was useful. I managed to add the components. I repeated your work but with a different FRDM board (KL-46Z256). In this case the current consumption is 18mA at 3V before the LED blinks and it is 23mA at 3V when the LED blinks. I think it is not gone to the low power mode. I am not expert and probably I have done something wrong but the only thing I changed compare to your code is the LEDs pin numbers (Since it is different for KL-46Z256). I can see on the board the LED is blinking, that means interrupt has happened. Do you have any example for KL-46Z256 or how to debug with CodeWarrior to find if the system is really gone to low power sleep or not? I used this application.c code (changed your code slightly) :
        /*
        * Application.c
        *
        * Created on: Mar 16, 2014
        * Author: Erich Styger
        */

        #include “Application.h”
        #include “LED1.h”
        #include “LED2.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(); /* GREEN LED */
        }

        void APP_Run(void) {
        //LED2_On(); /* RED LED */
        //WAIT1_Waitms(1000);
        //LED2_Off();
        for(;;) {
        //LED2_Neg(); /* RED 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 */
        } // IS THIS FOR LOOP CORRECT?
        }

        Like

        • that current amount indicates that your *not* in sleep mode. Without seeing the full picture it is hard to say what is missing. But have you measured it with or without the debug cable attached?

          Like

        • Thanks, I looked at the problem in details. I am sure my current reading is correct as I have done it several times in the past, with other MCU as well and I read a correct value.
          I have saved 2 videos and photos here:

          https://www.dropbox.com/sh/usvqvddtsxadec5/AACh1l89c8MPB-CWMxpVX6Oxa?dl=0

          I share a video with you which shows that the current consumption changes slightly when an interrupt happen (when the yellow LED is on). This is shown in the “FirstVideo” and the code is in a png file as “CodeForFirstVideo.png”. Then secondly I put and LED2_Neg(); /* RED LED */ inside the for(;;) loop and run the code. In the “SecondVideo” you can see the RED LED is blinking. I also put the code “CodeForSecondVideo.png”.
          So I guess I am in the for loop and also the interrupt happens but something is wrong then.

          My question is: APP_OnLLSWakeUpInterrupt() function is responsible for interupt or APP_TimerInterrupt()? In my case APP_OnLLSWakeUpInterrupt() is not sending an interupt.

          Thanks,
          Bee Zee

          Like

        • Hi,
          APP_TimerInterrupt() is only used to toggle the RED LED to show the system is running. It gets called by the T1 (LPTMR_CMR) interrupt which wakes up from LLS mode.
          In case the CPU wakes up from LLS, it calls Cpu_OnLLSWakeUpINT() which then calls APP_OnLLSWakeUpInterrupt(). If APP_OnLLSWakeUpInterrupt() does not happen, your system is not waking up from LLS.

          I hope this helps,
          Erich

          Like

  10. Hi Erich,

    Do you know if there is anyway to look at the peripherals in CodeWarior and see that low power mode has happened? I am looking for a method that shows we are in low power without measuring the current every time. I have done your work but it seems Cpu_SetOperationMode(DOM_STOP, NULL, NULL); /* next interrupt will wake us up */
    does not do anything and I am not in low power stop mode, even if I go to the function I do not know what to check or look for to find the problem.

    Thanks,
    Markus

    Like

    • To my understanding, there is no such information on the device itself. What I did is measuring the current to check if it really enters low power mode. Keep in mind that you should disconnect the debugging cable (and not debugging it while in low power mode).
      I hope this helps,
      Erich

      Like

    • I’m not aware of such a problem, but I admit I have not used the KL05Z in such a configuration. Are you sure the wake-up settings for the interrupts are not changed somehow?
      Does it wake up with the other wake-up sources?

      Like

      • This is exactly the same example which you describe in this article, but with SynchroMaster component. I check fiew time and when I add this SM1 component, DOM_STOP doesn’t work – with DOM_SLLEP and DOM_WAIT work fine but with DOM_STOP doesn’t. I add Cpu_SetOperationMode(DOM_RUN, NULL, NULL) on wake event up but it does’t change anything 😦

        Like

        • UPDATE:
          My Gosh! I found stupid mistake! In TimerInt and Interrupt period I didn’t change clock source to LPO_1kHzSrc 😦
          Uff, now it works perfect

          Like

  11. Hi Erich,

    I try to use interrupt period with interval or list of values. When I change Runtime settings type from fixed to interval or list in Timing dialog I have message: “Inherited component does not support this feature: Runtime setting type interval”. I would like to use SetPeriodMS method in TimerInt but I can’t change it to enable in Component Inspector (the method is gray).
    I would be very grateful for your help.

    Like

What do you think?

This site uses Akismet to reduce spam. Learn how your comment data is processed.