Can anyone share sample code for a MQX-Lite ISR for LLWU?

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Can anyone share sample code for a MQX-Lite ISR for LLWU?

Jump to solution
1,165 Views
jvasil
Contributor III

I want to use the LLS mode of the Kinesis K24 processor we are using for sleep mode.

We are using Processor Expert 10.4 and I finally found the "Init_LLWU" component [HINT: it is NOT added automatically when you set STOP operation mode to LLS or VLLS3] and added that.  And I think I have correctly configured the pin I want to wake the device (LLWU pin 4).

However, this component generates an error if you do not enter a name in the "ISR Name" field.  [ASIDE: This is true whether or not the "Interrupt request" checkbox is checked so it's not clear what that checkbox is doing--if anything.  Is this a bug?]

Once I enter a name, I can successfully "Generate Processor Expert Code."  However, when I try to build the project, the linker (IAR in this case) complains about there being no definition for the ISR.

Can anyone provide an example of a simple ISR that can be linked in to handle this interrupt.  All it needs to do is to allow the processor to continue running after an edge is detected on the GPIO (I'm using PTA13 which I have also shared with an ExtInt_LDD component).  While I can find multiple examples of how to use the low-power modes when running on a bare-metal processor, I have been unable to find one that uses MQX-Lite.  This is surprising since I'd think that the majority of users of these processors would need to put the processor into one of the low-power modes sooner or later!!

Anyhow, any help will be appreciated!!

Thanks in advance,

James

Tags (2)
0 Kudos
1 Solution
713 Views
mjbcswitzerland
Specialist V

Hi James

>> But perhaps these are cleared automatically when entering a sleep mode that uses them?  Or, maybe you only need to clear them if you plan on looking at them when you next wake up??

Since the LLWU is in fact only active when the LLS mode is entered I could imagine that only the flags for the enabled sources need to be cleared before entering/re-entering the mode. However, I also think that it is good practice to clear the flags consistently and this is always done and 'may' solve issues when not doing so, but I never tested doing it in a different fashion.

I have posted some more code from the uTasker project in case it gives any ideas. The actual LLWU is very simple (the uTasker project allows multiple interrupt handlers to be managed as well as multiple LLWU_Px pins to be assigned to each, which makes it look a bit more complicated, Most of the potentially more difficult stuff is correctly managing moving in and out of the modes, which is why I wrote that it will depend on how well the MQX / PE frame-work is prepared as to whether the "overall" operation is suitable. There is usually some preparation to be performed before going to the mode (as well as delaying in case of critical system operation) if you want to recover later, plus some clean-up work after the wake up - before the actual LLWU interrupt is called.

Regards

Mark

This is the interrupt routine that handles LLWU pins and modules as three separate entities (since the pins are spead over two registers and the modules over a third).

static __interrupt void _wakeup_isr(void)

{

    fnHandleWakeupSources(LLWU_FLAG_ADDRESS, 0); // handle the LLWU_P0..P7 input source(s) that woke the processor

    fnHandleWakeupSources((LLWU_FLAG_ADDRESS + 1), 8); // handle the LLWU_P8..P5 input source(s) that woke the processor

    fnHandleWakeupSources((LLWU_FLAG_ADDRESS + 2), 16); // handle the LLWU_M0..7 peripheral source(s) that woke the processor

}

The generic handling routine (allowing a user interrupt callback on each possible source and also allowing multiple ones to be handled in case a wakeup was due to multiple sources at the same time) is below:

static void fnHandleWakeupSources(volatile unsigned char *prtFlagRegister, int iSouceStart)

{

    register unsigned char ucBit;

    register unsigned char ucFlags = *prtFlagRegister;// check whether a source woke the processor

    if (ucFlags == 0) {

        return;

    }

    ucBit = 0x01;

    while (ucFlags != 0) { // while sources are flagged

        if (ucFlags & ucBit) {

            ucFlags &= ~ucBit;

            *prtFlagRegister = ucBit; // reset the interrupt flag (write '1' to clear)

            if (wakeup_handlers[iSouceStart] != 0) { // if there is a user handler for the source

                uDisable_Interrupt(); // ensure interrupts remain blocked when user callback operates

                    wakeup_handlers[iSouceStart]();

                uEnable_Interrupt();

            }

        }

        iSouceStart++;

        ucBit <<= 1;

    }

}

The actual configuration of the pins (as noted, it looks complicated since it allow multiple pins to be configured at the same time and has a map to convert between port pins and LLWU pins available on the particular Kinetis part) does:

// The port inputs are now mapped to available LLWU pins (pins that do not have LLWU functionality will not be configured)

//

while (ulPortBits != 0) { // handle each bit on the port

    if (waveup_interrupt->int_port_bits & ulBit) { // if the port bit is to be enabled

        if (cWakeupPorts[waveup_interrupt->int_port][iBitRef] != NO_WAKEUP) {

            int iShift = ((cWakeupPorts[waveup_interrupt->int_port][iBitRef]%4) * LLWU_PE_WUPE_SHIFT);

            volatile unsigned char *ptrFlagRegister = LLWU_FLAG_ADDRESS + (cWakeupPorts[waveup_interrupt->int_port][iBitRef]/8);

            unsigned char *ptrWakeupEnable = (unsigned char *)LLWU_BLOCK + (cWakeupPorts[waveup_interrupt->int_port][iBitRef]/4); // set the enable register pointer

            unsigned char ucValueMask = (LLWU_PE_WUPE_MASK << iShift); // set the mask in the enable register

            *ptrWakeupEnable &= ~ucValueMask; // disable the wakeup functionality

            wakeup_handlers[cWakeupPorts[waveup_interrupt->int_port][iBitRef]] = waveup_interrupt->int_handler; // enter the user interrupt handler for this wakeup input

            fnEnterInterrupt(irq_LL_wakeup_ID, waveup_interrupt->int_priority, _wakeup_isr); // ensure that the handler is entered

            *ptrFlagRegister = (LLWU_F_WUF0 << (cWakeupPorts[waveup_interrupt->int_port][iBitRef]%8)); // reset pending flags

            *ptrWakeupEnable |= (ucInterruptType << iShift); // set/enable the type required

        }

        else {

            _EXCEPTION("Invalid wakeup port bit being selected!");

        }

        ulPortBits &= ~ulBit;

    }

    ulBit <<= 1;

    iBitRef++;

}

For each pin enabled, the sequence (in the middle) is however:

1. Disable the pin's wakeup functionality (although it isn't actually function in the processor's RUN state any way)

2. Enter its (new) interrupt handler

3. Reset pending flags on the source

4. Set the pin mode to enable its operation

In fact there are only about about 4 lines of code that actually do any LLWU register/interrupt configuration - the rest is more overall system design/operation on the bigger scale.

View solution in original post

0 Kudos
7 Replies
713 Views
adriancano
NXP Employee
NXP Employee

Hi James,

You can refer to the KL25 source code rev.10 it includes an example that demonstrates the use of the Low Power modes in the Kinetis KL25 device using Processor Expert, you will find it very useful.

In this example you can select to enter VLLS mode and wake up via LLWU.

The source code is available HERE follow the path <install_folder>\kl25_sc_rev10\klxx-sc-pex\projects\PEx_low_power_demo\cw


Hope this information can help you.

Best Regards,
Adrian Sanchez Cano
Technical Support Engineer
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

713 Views
jvasil
Contributor III

Thanks! Is it available as a zip or 7z archive instead of as an exe file? I assume this is a self-extracting archive but I'm a little hesitant to download it!

Regards,

James

0 Kudos
713 Views
adriancano
NXP Employee
NXP Employee

Hi,

Yes it is a self extracting archive

Regards,

Adrian

0 Kudos
713 Views
mjbcswitzerland
Specialist V

Hi James

I can't help with PE (or MQX lite) but the LLS mode/LLWU is fully integrated in the uTasker project as detailed here:

http://www.utasker.com/kinetis/LLWU.html

If you have a TWR-K24F120M you can also load reference binary to it showing LLS mode recovery via LLWU inputs (it will temporarily recover when SW2 [LLWUP10] is pressed before automatically moving back to the LLS mode after work is done, or it will wake and restore the original power mode (120MHz operation with full-functionality) when there is an event on LLWU_P4 [the one you want]):

µTasker Kinetis TWR-K24F120M support

If you have a specific question about the LLWU I can certainly help but I don't know whether MQX/PE allow a complete integration (without various other work) to achieve the same level of functionality.

Regards

Mark

http://www.utasker.com/kinetis.html

P.S. The LLWU is integrated in the uTasker Kinetis simulator and allows it to be fully verified without the need for HW.

P.P.S. In case you need boot loaders these are also available at the K24 link (KBOOT compatible UART, USB-HID in parallel with USB-MSD or SD-card loading). These can be used together with applications developed in any environment.

713 Views
jvasil
Contributor III

Thanks for the info, Mark.

In another discussion, I found a comment that said something about needing to call CPU_SetOperationMode from the LLWU ISR.  I suspect this is similar to the calls to fnSetLowPowerMode that exist in the uTasker code you shared.

Solely from reading the processor datasheet I thought that I'd also need to clear the flags that indicate the source of the wakeup.  Something like:

    /* Clear flags that may have caused wakeup.. */

    LLWU_F1 = LLWU_F1_VALUE;

    LLWU_F2 = LLWU_F2_VALUE;

    LLWU_FILT1 = LLWU_FILT1_VALUE;

    LLWU_FILT2 = LLWU_FILT2_VALUE;

But perhaps these are cleared automatically when entering a sleep mode that uses them?  Or, maybe you only need to clear them if you plan on looking at them when you next wake up??

In any event, thanks for the help!

James

0 Kudos
714 Views
mjbcswitzerland
Specialist V

Hi James

>> But perhaps these are cleared automatically when entering a sleep mode that uses them?  Or, maybe you only need to clear them if you plan on looking at them when you next wake up??

Since the LLWU is in fact only active when the LLS mode is entered I could imagine that only the flags for the enabled sources need to be cleared before entering/re-entering the mode. However, I also think that it is good practice to clear the flags consistently and this is always done and 'may' solve issues when not doing so, but I never tested doing it in a different fashion.

I have posted some more code from the uTasker project in case it gives any ideas. The actual LLWU is very simple (the uTasker project allows multiple interrupt handlers to be managed as well as multiple LLWU_Px pins to be assigned to each, which makes it look a bit more complicated, Most of the potentially more difficult stuff is correctly managing moving in and out of the modes, which is why I wrote that it will depend on how well the MQX / PE frame-work is prepared as to whether the "overall" operation is suitable. There is usually some preparation to be performed before going to the mode (as well as delaying in case of critical system operation) if you want to recover later, plus some clean-up work after the wake up - before the actual LLWU interrupt is called.

Regards

Mark

This is the interrupt routine that handles LLWU pins and modules as three separate entities (since the pins are spead over two registers and the modules over a third).

static __interrupt void _wakeup_isr(void)

{

    fnHandleWakeupSources(LLWU_FLAG_ADDRESS, 0); // handle the LLWU_P0..P7 input source(s) that woke the processor

    fnHandleWakeupSources((LLWU_FLAG_ADDRESS + 1), 8); // handle the LLWU_P8..P5 input source(s) that woke the processor

    fnHandleWakeupSources((LLWU_FLAG_ADDRESS + 2), 16); // handle the LLWU_M0..7 peripheral source(s) that woke the processor

}

The generic handling routine (allowing a user interrupt callback on each possible source and also allowing multiple ones to be handled in case a wakeup was due to multiple sources at the same time) is below:

static void fnHandleWakeupSources(volatile unsigned char *prtFlagRegister, int iSouceStart)

{

    register unsigned char ucBit;

    register unsigned char ucFlags = *prtFlagRegister;// check whether a source woke the processor

    if (ucFlags == 0) {

        return;

    }

    ucBit = 0x01;

    while (ucFlags != 0) { // while sources are flagged

        if (ucFlags & ucBit) {

            ucFlags &= ~ucBit;

            *prtFlagRegister = ucBit; // reset the interrupt flag (write '1' to clear)

            if (wakeup_handlers[iSouceStart] != 0) { // if there is a user handler for the source

                uDisable_Interrupt(); // ensure interrupts remain blocked when user callback operates

                    wakeup_handlers[iSouceStart]();

                uEnable_Interrupt();

            }

        }

        iSouceStart++;

        ucBit <<= 1;

    }

}

The actual configuration of the pins (as noted, it looks complicated since it allow multiple pins to be configured at the same time and has a map to convert between port pins and LLWU pins available on the particular Kinetis part) does:

// The port inputs are now mapped to available LLWU pins (pins that do not have LLWU functionality will not be configured)

//

while (ulPortBits != 0) { // handle each bit on the port

    if (waveup_interrupt->int_port_bits & ulBit) { // if the port bit is to be enabled

        if (cWakeupPorts[waveup_interrupt->int_port][iBitRef] != NO_WAKEUP) {

            int iShift = ((cWakeupPorts[waveup_interrupt->int_port][iBitRef]%4) * LLWU_PE_WUPE_SHIFT);

            volatile unsigned char *ptrFlagRegister = LLWU_FLAG_ADDRESS + (cWakeupPorts[waveup_interrupt->int_port][iBitRef]/8);

            unsigned char *ptrWakeupEnable = (unsigned char *)LLWU_BLOCK + (cWakeupPorts[waveup_interrupt->int_port][iBitRef]/4); // set the enable register pointer

            unsigned char ucValueMask = (LLWU_PE_WUPE_MASK << iShift); // set the mask in the enable register

            *ptrWakeupEnable &= ~ucValueMask; // disable the wakeup functionality

            wakeup_handlers[cWakeupPorts[waveup_interrupt->int_port][iBitRef]] = waveup_interrupt->int_handler; // enter the user interrupt handler for this wakeup input

            fnEnterInterrupt(irq_LL_wakeup_ID, waveup_interrupt->int_priority, _wakeup_isr); // ensure that the handler is entered

            *ptrFlagRegister = (LLWU_F_WUF0 << (cWakeupPorts[waveup_interrupt->int_port][iBitRef]%8)); // reset pending flags

            *ptrWakeupEnable |= (ucInterruptType << iShift); // set/enable the type required

        }

        else {

            _EXCEPTION("Invalid wakeup port bit being selected!");

        }

        ulPortBits &= ~ulBit;

    }

    ulBit <<= 1;

    iBitRef++;

}

For each pin enabled, the sequence (in the middle) is however:

1. Disable the pin's wakeup functionality (although it isn't actually function in the processor's RUN state any way)

2. Enter its (new) interrupt handler

3. Reset pending flags on the source

4. Set the pin mode to enable its operation

In fact there are only about about 4 lines of code that actually do any LLWU register/interrupt configuration - the rest is more overall system design/operation on the bigger scale.

0 Kudos
713 Views
jvasil
Contributor III

Ahh, thanks again.  The comments on the three functions listed on the µTasker LLWU Support page refer to them as "interrupt handlers" so I thought that was all that was being done!  The additional code you posted makes it much more clear.

Regards,

James

0 Kudos