This program gives an example of using LPM within an application, where there are timers, serial peripherals, and other things that need to be disabled and enabled around the low power transition.
Measurements using this application for different platforms and different configurations are below. The power values are in microamps; the boards were powered by USB at 3.6V. In the first column (+S, +U) the USCI serial and Timer_A-based uptime clock are left enabled. In the second (-S, +U), the USCI serial is placed in reset mode prior to entering LPM. In the third (-S, -U) the uptime clock is also placed in reset mode.
Some platforms support LPMx.5 modes, which are very low power and require something like the EEVblog uCurrent to measure. LPM3 and LPM4 for the MSP430G2553 is also sub-100nA and cannot be measured with a standard ammeter.
Use a revision 1.5 board; earlier ones do poorly. Place the ammeter across the Vcc pair on the header connecting the emulation and MSP430 sides of the board.
Current costs are slightly higher in LPM3 and LPM4 if the crystal is populated.
I don't know why it's lower power to keep USCI and Timer_A enabled in LPMx.5. That's just what the measurements show.
Note that LPM3.5 doesn't seem to be different from LPM3, and unlike the FR5739 LPM4.5 is not entered if either the serial or the timer are left enabled.
Place the ammeter between a 3.3V source and the external power header.
LPMX.5 modes could not be reliably measured.
#include <string.h>
#if ! (BSP430_PLATFORM_BUTTON0 - 0)
#error No button available on this platform
#endif
typedef struct sCommand {
char cmd;
const char * description;
} sCommand;
const sCommand commands[] = {
#define CMD_MODE_ACTIVE 'a'
{ CMD_MODE_ACTIVE, "Remain in active mode while idle" },
#define CMD_MODE_LPM0 '0'
{ CMD_MODE_LPM0, "Set mode to enter LPM0" },
#define CMD_MODE_LPM1 '1'
{ CMD_MODE_LPM1, "Set mode to enter LPM1" },
#define CMD_MODE_LPM2 '2'
{ CMD_MODE_LPM2, "Set mode to enter LPM2" },
#define CMD_MODE_LPM3 '3'
{ CMD_MODE_LPM3, "Set mode to enter LPM3" },
#define CMD_MODE_LPM4 '4'
{ CMD_MODE_LPM4, "Set mode to enter LPM4" },
#ifdef BSP430_PMM_ENTER_LPMXp5_NI
#define CMD_MODE_LPM3p5 '5'
{ CMD_MODE_LPM3p5, "Set mode to enter LPM3.5" },
#define CMD_MODE_LPM4p5 '6'
{ CMD_MODE_LPM4p5, "Set mode to enter LPM4.5" },
#endif
#define CMD_HOLD_SERIAL 's'
{ CMD_HOLD_SERIAL, "Toggle whether serial is placed on hold during LPM" },
#define CMD_HOLD_CLOCK 'u'
{ CMD_HOLD_CLOCK, "Toggle whether uptime clock is placed on hold during LPM" },
#define CMD_HELP '?'
{ CMD_HELP, "Display help" },
#define CMD_STATE '='
{ CMD_STATE, "Display system state (clock speeds, etc.)" },
#define CMD_SLEEP '$'
{ CMD_SLEEP, "Enter sleep mode" },
#if (BSP430_PMM_SUPPORTS_COREV - 0)
#define CMD_COREV_INCR '>'
{ CMD_COREV_INCR, "Increment core voltage" },
#define CMD_COREV_DECR '<'
{ CMD_COREV_DECR, "Decrement core voltage" },
#endif
};
typedef struct sState {
unsigned int lpm_bits;
const char * lpm_description;
int hold_serial;
int hold_clock;
} sState;
volatile int button;
static int
void * context,
int idx)
{
(void)cb;
(void)context;
(void)idx;
++button;
}
};
char rx_buffer[16];
char * volatile rx_head;
char * volatile rx_tail;
static int
consume_rx_ni ()
{
int rc;
if (rx_head == rx_tail) {
return -1;
}
rc = *rx_tail++;
if (rx_tail > (rx_buffer + sizeof(rx_buffer) / sizeof(*rx_buffer))) {
rx_tail = rx_buffer;
}
return rc;
}
static int
consume_rx ()
{
int rc;
rc = consume_rx_ni();
return rc;
}
static int
void * context)
{
if (rx_head > (rx_buffer + sizeof(rx_buffer) / sizeof(*rx_buffer))) {
rx_head = rx_buffer;
}
if (rx_head == rx_tail) {
(void)consume_rx_ni;
}
}
};
void main ()
{
int b0pin;
sState state;
#if (BSP430_MODULE_SYS - 0)
unsigned long reset_causes = 0;
unsigned int reset_flags = 0;
#endif
#if (BSP430_MODULE_SYS - 0)
{
unsigned int sysrstiv;
reset_causes |= 1UL << (sysrstiv / 2);
}
#ifdef BSP430_PMM_ENTER_LPMXp5_NI
PMMCTL0_H = PMMPW_H;
PM5CTL0 = 0;
PMMCTL0_H = 0;
}
#endif
}
#endif
#if APP_CONFIGURE_PORTS_FOR_LPM
#endif
cprintf(
"\napplpm " __DATE__
" " __TIME__
"\n");
#if (BSP430_MODULE_SYS - 0)
cprintf(
"System reset bitmask %lx; causes:\n", reset_causes);
{
int bit = 0;
while (bit < (8 * sizeof(reset_causes))) {
if (reset_causes & (1UL << bit)) {
}
++bit;
}
}
}
}
}
}
#endif
#if (BSP430_PORT_SUPPORTS_REN - 0)
#endif
rx_head = rx_tail = rx_buffer;
*rx_head++ = CMD_MODE_ACTIVE;
*rx_head++ = CMD_STATE;
memset(&state, 0, sizeof(state));
#if 1 && (BSP430_PMM_SUPPORTS_SVSM - 0)
#endif
while (1) {
int enter_sleep = 0;
unsigned long int sleep_utt;
unsigned long int wake_utt;
do {
int c;
while (0 <= ((c = consume_rx()))) {
const sCommand * cmdp = commands;
const sCommand * const commands_end = commands + sizeof(commands) / sizeof(*commands);
while ((cmdp < commands_end) && (cmdp->cmd != c)) {
++cmdp;
}
if (cmdp->cmd == c) {
cputs(cmdp->description);
switch (cmdp->cmd) {
case CMD_MODE_ACTIVE:
state.lpm_bits = 0;
state.lpm_description = "Active";
break;
case CMD_MODE_LPM0:
state.lpm_description = "LPM0";
break;
case CMD_MODE_LPM1:
state.lpm_description = "LPM1";
break;
case CMD_MODE_LPM2:
state.lpm_description = "LPM2";
break;
case CMD_MODE_LPM3:
state.lpm_description = "LPM3";
break;
case CMD_MODE_LPM4:
state.lpm_description = "LPM4";
break;
#ifdef BSP430_PMM_ENTER_LPMXp5_NI
case CMD_MODE_LPM3p5:
state.lpm_description = "LPM3.5";
break;
case CMD_MODE_LPM4p5:
state.lpm_description = "LPM4.5";
break;
#endif
case CMD_HELP: {
cmdp = commands;
while (cmdp < commands_end) {
cprintf(
"\t%c : %s\n", cmdp->cmd, cmdp->description);
++cmdp;
}
break;
}
case CMD_STATE:
cprintf(
"Selected idle state: %s\n", state.lpm_description);
cprintf(
"Clocks will %s\n", state.hold_clock ?
"freeze" :
"run");
cprintf(
"Serial will %s\n", state.hold_serial ?
"be held" :
"be active");
#if (BSP430_PMM_SUPPORTS_COREV - 0)
cprintf(
"Core voltage level %d, SVSMHCTL %04x SVSMLCTL %04x\n",
#endif
#ifdef BSP430_PMM_ENTER_LPMXp5_NI
#else
cprintf(
"LPM X.5 is NOT supported\n");
#endif
break;
case CMD_HOLD_SERIAL:
state.hold_serial = ! state.hold_serial;
break;
case CMD_HOLD_CLOCK:
state.hold_clock = ! state.hold_clock;
break;
case CMD_SLEEP:
enter_sleep = 1;
break;
#if (BSP430_PMM_SUPPORTS_COREV - 0)
case CMD_COREV_INCR:
case CMD_COREV_DECR: {
int delta = 0;
int rc = 0;
do {
if ((cmdp->cmd == CMD_COREV_INCR) && (level < PMMCOREV_3)) {
delta = 1;
}
if ((cmdp->cmd == CMD_COREV_DECR) && (level >
PMMCOREV_0)) {
delta = -1;
}
if (0 != delta) {
}
} while (0);
if (0 == delta) {
cprintf(
"Core voltage at limit\n");
} else {
cprintf(
"Core voltage adjusted %d to %d rv %d\n", delta, PMMCTL0 & PMMCOREV_3, rc);
}
cprintf(
"PMM: CTL0 %04x CTL1 %04x\n", PMMCTL0, PMMCTL1);
break;
}
#endif
}
} else {
cprintf(
"Unrecognized command: %c\n", c);
}
}
} while (! enter_sleep);
cprintf(
"Entering idle mode at %lu: %s\n", sleep_utt, state.lpm_description);
cprintf(
"NOTE: Will use LPMx.5: press button to exit\n");
}
if (state.hold_clock) {
}
if (state.hold_serial) {
cprintf(
"Disabling serial; press button to wake\n\n");
}
} else {
}
if (0 == state.lpm_bits) {
button = 0;
while ((rx_head == rx_tail) && (! button)) {
}
} else {
#ifdef BSP430_PMM_ENTER_LPMXp5_NI
}
#endif
}
if (state.hold_serial) {
}
if (state.hold_clock) {
}
cprintf(
"Left idle mode at %lu: %lu asleep\n", wake_utt, wake_utt - sleep_utt);
}
}