You are here

PDL Examples - SysPM Deepsleep Callbacks | Cypress Semiconductor

PDL Examples - SysPM Deepsleep Callbacks

In addition to the usual peripheral drivers, like PWMs and pins, which I have covered in previous articles, the PDL has platform drivers like SysPM, SysLib and SysInt. These drivers are collections of functions that operate on the PSoC system as a whole. I already (rather sneakily) wrote about SysInt - it is the interrupt handling driver that I used to play around with pin inputs. I am going to use that driver again to show you some interesting features of SysPM, the Power Management driver.

SysPM gives you control over the device (and CPU core) power modes, voltage sources and regulators, and low power callbacks. Today I am going to talk about the callbacks. When you want an application to go into a low power mode you need to think a little bit about the impact of that on the peripherals. Do they stop working or continue? Do they retain state? Do they just pick up where they left off? The behavior is all documented in the device reference manuals, of course, and the PDL provides sensible default actions via callbacks. The callbacks are just functions that get called before the device goes into, and out of, a low power mode. But what if you want to change that default? Well, maybe I shall get into that next week, but first allow me to explain how the call back system works.

Let's use a simple but realistic example. Imagine you are logging data across a slow serial line and want to go into deepsleep without dropping messages. To do that I configure a UART component to run at 1200 baud. That is slow enough that you can see the characters get written to a terminal emulator (at just over 100 characters per second). It is also definitely slow enough that I can print a string of Shakspearean prose and get my program into its pajamas and off to bed-fordshire before the end of the first stanza. No, I don't know what a stanza is either, but I can remember that they were long enough for me to fall asleep in high school...

Before staring to write the application, though, I remembered that I am using a dual core device and that it will only enter deepsleep if BOTH the CM4 and CM0+ cores are stopped. To make that happen I added one line of code to the main_cm0p.c file, like this:

    for(;;)
    {
        Cy_SysPm_DeepSleep( CY_SYSPM_WAIT_FOR_INTERRUPT );
    }

In my PSoC schematic I added a UART component and configured it for 1200 baud. I set the pins to route through the kitprog debug chip so I can see the output in my terminal emulator.

PSoC Creator UART Component Customizer     PSoC creator Pin Editor (CYDWR)

In the C code I initialize the UART and steal some code from a previous blog to generate an interrupt when I press the SW2 button. Note that the ISR does nothing - it only serves to bring the device out of low power mode. Just before the main loop I send a nice cultured message (waiting 2s for the printing to complete) and then enter the loop. Then I start to print the message again, waits 100ms, and go into deepsleep.

#include "project.h"
#define SW2_PORT            GPIO_PRT0
#define SW2_NUM             4
#define SW2_IRQ_NUM         ioss_interrupts_gpio_0_IRQn
#define SW2_IRQ_PRIORITY    3

const cy_stc_sysint_t sw2_int_cfg = { SW2_IRQ_NUM, SW2_IRQ_PRIORITY };
char msg[] = "Once more unto the breach, dear friends, once more\r\n";

void sw2_int_handler( void )
{
    /* Clear the interrupt - the interrupt is only used to exit deepsleep */
    Cy_GPIO_ClearInterrupt( SW2_PORT, SW2_NUM );
}

int main(void)
{
    /* Initialize the UART */
    Cy_SCB_UART_Init( UART_HW, &UART_config, &UART_context );
    Cy_SCB_UART_Enable( UART_HW );
    
    /* Initialize input pin */
    Cy_GPIO_Pin_FastInit(     SW2_PORT, SW2_NUM, CY_GPIO_DM_PULLUP, 1, HSIOM_SEL_GPIO );
    Cy_GPIO_SetInterruptEdge( SW2_PORT, SW2_NUM, CY_GPIO_INTR_FALLING );
    Cy_GPIO_SetInterruptMask( SW2_PORT, SW2_NUM, 1 );
    Cy_SysInt_Init( &sw2_int_cfg, sw2_int_handler );
    NVIC_EnableIRQ( sw2_int_cfg.intrSrc );
    __enable_irq();
    /* Print the message and allow plenty of time for it to be sent */
    Cy_SCB_UART_PutString( UART_HW, msg );
    Cy_SysLib_Delay( 2000 );
       
    for(;;)
    {
        /* Start printing, wait a while, then go into deepsleep */
        Cy_SCB_UART_PutString( UART_HW, msg );
        Cy_SysLib_Delay( 100 );
        Cy_SysPm_DeepSleep( CY_SYSPM_WAIT_FOR_INTERRUPT );
    }
}

When I run the program the UART prints the first message, because it has plenty of time, but only gets about a quarter of the way through the string after that before sleeping. This is because 100ms is not long enough to complete the printing. When I press SW2 the device exits deepsleep and tries to print again. Note, though, that it re-starts from the beginning of the string.

UART output interrupts by deepsleep transition

The UART "forgets" the contents of its buffer while in deepsleep. I am going to fix that by using the default UART callback. How does that work? The Cy_SysPm_DeepSleep() function looks for, and executes, callbacks that get registered with the Cy_SysPm_RegisterCallback(). When it runs them it passes in a pointer to a structure that controls what actions to perform and what instance of hardware to do them to. Here is my callback parameter struct for the UART. Note that it has three members; an ORed list of conditions, the hardware (SCB address for my UART), and the UART context. The ORed list is asking the callback function (which we do not have to write) to handle four things; check it is OK to go into low power, run code before going to sleep, run code after sleep, and handle any errors.

/* This struct is passed to Cy_SCB_UART_DeepSleepCallback */
cy_stc_syspm_callback_params_t deep_sleep_params = 
{
    CY_SYSPM_CHECK_READY | CY_SYSPM_BEFORE_TRANSITION | CY_SYSPM_AFTER_TRANSITION | CY_SYSPM_FAIL,
    UART_HW,
    &UART_context
};

In order to actually run the callback I create another struct. This contains a pointer to the callback function, the low power state to handle (you can create different callbacks for different states), the conditions to ignore in this particular callback, a pointer to the parameters (above), and a pair of prev and next pointers that I just set to NULL. These last two pointers are set by Cy_SysPm_RegisterCallback() so that it builds a linked list of all callbacks (I only have one).

/* This struct is part of a linked list of callbacks */
cy_stc_syspm_callback_t deep_sleep_checker =
{
    &Cy_SCB_UART_DeepSleepCallback,
    CY_SYSPM_DEEPSLEEP,
    0,
    &deep_sleep_params,
  NULL,
  NULL
};

Next I just register the callback for the UART.

    /* Initialize the UART */
    Cy_SCB_UART_Init( UART_HW, &UART_config, &UART_context );
    Cy_SCB_UART_Enable( UART_HW );
    
    /* Install the UART callback */
    Cy_SysPm_RegisterCallback( &deep_sleep_checker );

Lastly, I re-jig the main loop to check the return value from the call to Cy_SysPM_DeepSleep(), only going into the low power mode once the printing has completed.

        Cy_SCB_UART_PutString( UART_HW, msg );
        do
        {
            Cy_SysLib_Delay( 100 );
        } while( CY_SYSPM_SUCCESS != Cy_SysPm_DeepSleep( CY_SYSPM_WAIT_FOR_INTERRUPT ) );

Now, when I run the program, the whole string is printed every time.

Deepsleep transition delayed to allow the UART buffer to empty

The whole application is attached. I realize that I have glossed over a few areas in this article and so, next time, I'll dig into that a little more and create my own custom callbacks.

 

Blog: 

ALL CONTENT AND MATERIALS ON THIS SITE ARE PROVIDED "AS IS". CYPRESS SEMICONDUCTOR AND ITS RESPECTIVE SUPPLIERS MAKE NO REPRESENTATIONS ABOUT THE SUITABILITY OF THESE MATERIALS FOR ANY PURPOSE AND DISCLAIM ALL WARRANTIES AND CONDITIONS WITH REGARD TO THESE MATERIALS, INCLUDING BUT NOT LIMITED TO, ALL IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT OF ANY THIRD PARTY INTELLECTUAL PROPERTY RIGHT. NO LICENSE, EITHER EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, IS GRANTED BY CYPRESS SEMICONDUCTOR. USE OF THE INFORMATION ON THIS SITE MAY REQUIRE A LICENSE FROM A THIRD PARTY, OR A LICENSE FROM CYPRESS SEMICONDUCTOR.

Content on this site may contain or be subject to specific guidelines or limitations on use. All postings and use of the content on this site are subject to the Terms and Conditions of the site; third parties using this content agree to abide by any limitations or guidelines and to comply with the Terms and Conditions of this site. Cypress Semiconductor and its suppliers reserve the right to make corrections, deletions, modifications, enhancements, improvements and other changes to the content and materials, its products, programs and services at any time or to move or discontinue any content, products, programs, or services without notice.