Contiki 3.x
Files | Macros | Functions

Driver for the cc2538 power modes. More...

Files

file  lpm.c
 Implementation of low power modes ofr the cc2538.
 
file  lpm.h
 Header file with register, macro and function declarations for the cc2538 low power module.
 

Macros

#define lpm_init(void)
 Initialise the LPM module.
 
#define lpm_enter(void)
 Drop to Deep Sleep. More...
 
#define lpm_exit(void)
 Perform an 'Exit Deep Sleep' sequence. More...
 

Functions

void lpm_set_max_pm (uint8_t pm)
 Prevent the SoC from dropping to a PM higher than max_pm. More...
 
void lpm_register_peripheral (lpm_periph_permit_pm1_func_t permit_pm1_func)
 Register a peripheral function which will get called by the LPM module to get 'permission' to drop to PM1+. More...
 

LPM stats

Maintains a record of how many rtimer ticks spent in each Power Mode.

Mainly used for debugging the module

#define LPM_STATS_GET(pm)
 

Constants to be used as arguments to lpm_set_max_pm()

#define LPM_PM0   0
 
#define LPM_PM1   1
 
#define LPM_PM2   2
 

Detailed Description

Driver for the cc2538 power modes.

Macro Definition Documentation

void lpm_enter (   void)

Drop to Deep Sleep.

This function triggers a sequence to enter Deep Sleep. The sequence involves determining the most suitable PM and switching the system clock source to the 16MHz if required. If the energest module is enabled, the sequence also performs some simple energest calculations.

Broadly speaking, this function will be called from the main loop when all events have been serviced. This functions aims to be clever enough in order to be able to choose between PMs 0/1/2 depending on chip status and anticipated sleep duration. This choice is made subject to configuration restrictions and subject to restrictions imposed by calls to lpm_set_max_pm().

This PM selection heuristic has the following primary criteria:

  • Is the RF off?
  • Are all registered peripherals permitting PM1+?
  • Is the Sleep Timer scheduled to fire an interrupt?

If the answer to any of those questions is no, we will drop to PM0 and will wake up to any interrupt. Best case scenario (if nothing else happens), we will idle until the next SysTick in no more than 1000/CLOCK_SECOND ms (7.8125ms).

If all can be answered with 'yes', we can drop to PM1/2 knowing that the Sleep Timer will wake us up. Depending on the estimated deep sleep duration and the max PM allowed by user configuration, we select the most efficient Power Mode to drop to. If the duration is too short, we simply IDLE in PM0.

Dropping to PM1/2 requires a switch to the 16MHz OSC. We have the option of letting the SoC do this for us automatically. However, if an interrupt fires during this automatic switch, we will need to re-assert WFI. To avoid this complexity, we perform the switch to the 16MHz OSC manually in software and we assert WFI after the transition has been completed. This gives us a chance to bail out if an interrupt fires or an event is raised during the transition. If nothing happens, dropping to PM1+ is un-interruptible and with a deterministic duration. When we wake up, we switch back to the 32MHz OSC manually before handing control back to main. This is implemented in lpm_exit(), which will always be called from within the Sleep Timer ISR context.

Note
Dropping to PM2 means that data in the SRAM non-retention area will be lost. It is recommended to disable PM2 if the total RAM footprint is larger than what will fit in the retention area. .nrdata* sections can be used to place uninitialized data in the SRAM non-retention area.
See Also
main(), rtimer_arch_next_trigger(), lpm_exit(), lpm_set_max_pm()

Definition at line 212 of file lpm.h.

Referenced by main().

void lpm_exit (   void)

Perform an 'Exit Deep Sleep' sequence.

This routine is called from within the context of the ISR that caused us to come out of PM1/2. It performs a wake up sequence to make sure the 32MHz OSC is back on and the system clock is sourced on it.

While in PMs 1 and 2, the system clock stops ticking. This functions adjusts it when we wake up.

We always exit PM1/2 as a result of a scheduled rtimer task or a GPIO interrupt. This may lead to other parts of the code trying to use the RF, so we need to switch the clock source before said code gets executed.

This function also makes sure that the sleep timer value is up-to-date following wake-up from PM1/2 so that RTIMER_NOW() works.

Note
This function should be called at the very beginning of ISRs waking up the SoC in order to restore all clocks and timers.
See Also
lpm_enter(), rtimer_isr()

Definition at line 213 of file lpm.h.

Referenced by gpio_port_a_isr(), gpio_port_b_isr(), gpio_port_c_isr(), gpio_port_d_isr(), and rtimer_isr().

Function Documentation

void lpm_register_peripheral ( lpm_periph_permit_pm1_func_t  permit_pm1_func)

Register a peripheral function which will get called by the LPM module to get 'permission' to drop to PM1+.

Parameters
permit_pm1_funcPointer to the function

Some peripherals are sensitive to PM changes. For instance, we don't want to drop to PM1+ if the USB PLL is active or if the UART TX FIFO is not clear.

When changing power modes, the LPM driver will call all FPs registered with this function. The peripheral's function will return true or false to permit / prohibit PM1+ respectively. If at least one peripheral returns false, the SoC will drop to PM0 Deep Sleep instead.

Registering several times the same function makes the LPM module behave as if the function had been registered once.

Referenced by uart_init().

void lpm_set_max_pm ( uint8_t  pm)

Prevent the SoC from dropping to a PM higher than max_pm.

Parameters
pmThe highest PM we are allowed to enter, specified as a number in [0, 2]

Defines for the pm argument are LPM_PMx.

This function can be used by software in situations where some power modes are undesirable. If, for example, an application needs to avoid PM2, it would call lpm_set_max_pm(LPM_PM1). If an application wants to avoid PM1 as well, it would call lpm_set_max_pm(LPM_PM0)

PM0 can not be disabled at runtime. Use LPM_CONF_ENABLE to disable LPM support altogether

Note
If the value of argument pm is greater than the value of the LPM_CONF_MAX_PM configuration directive, LPM_CONF_MAX_PM is used. Thus if LPM_CONF_MAX_PM==1, calling lpm_set_max_pm(LPM_PM2) would result in a maximum PM set to 1 and all subsequent Deep Sleeps would be limited to either PM0 or PM1.
See Also
lpm_enter()