Here is a follow up to the original post that is focused on trying to use the framework and stay in LPM4 as much as possible
-rick
File: LPM_Button.ino
/**
* LPM_Button - simple button handler with sleep features
*
* Demonstrates how to sleep in LPM4 and have port interrupt wake up on button push,
* switch to LPM3 run for a bit and then go back to LPM4 from interrupts.
*/
typedef unsigned int (*uintFuncPtr)(void);
/* forward declarations */
void attachInterrupt2(uint8_t, uintFuncPtr, int);
unsigned int handleButton(void);
/*
* -- G L O B A L V A R I A B L E S --
*/
unsigned char count = 0;
volatile unsigned char times = 0;
/*
* setup() - Configure pins, disable WDT, assign Pushbutton (p1.3) interrupt handler
*/
void setup()
{
disableWatchDog(); // disable timer keeper to use less power
pinMode(RED_LED,OUTPUT);
pinMode(GREEN_LED,OUTPUT);
pinMode(PUSH2, INPUT_PULLUP);
digitalWrite(RED_LED, HIGH);
digitalWrite(GREEN_LED, LOW);
attachInterrupt2(PUSH2, handleButton, RISING);
}
void loop()
{
LPM4; // start in ultra low power mode, all work is done in the ISR routines
}
/*
* handleButton() - interrupt handler for button up (low -> high) event
*/
unsigned int handleButton(void)
{
++times;
P1OUT |= digital_pin_to_bit_mask[RED_LED];
P1OUT &= ~digital_pin_to_bit_mask[GREEN_LED];
// Set up Timer A "Up to CCR0" mode, divide by 2^n, clock from SMCLK (2)/ACLK (1), clear timer
TACCR0 = 0x1fff;
TACCTL0 = CCIE;
TACTL = MC_1 | ID_0 | TASSEL_1 | TACLR;
return OSCOFF; // switch from LPM4 to LPM3 so the ACLK can drive the Timer clock
}
/*
* TA0_ISR() - Handle CCR0 interrupt
* in LPM3 mode, blink the lights and then go back to deep sleep
*/
__attribute__((interrupt(TIMER0_A0_VECTOR)))
static void TA0_ISR(void)
{
if (++count > 2 * times) {
P1OUT |= digital_pin_to_bit_mask[RED_LED];
P1OUT &= ~digital_pin_to_bit_mask[GREEN_LED];
TACTL = TACLR;
__bis_status_register_on_exit(OSCOFF); // switch back back into LPM4 sleep
}
else {
P1OUT ^= digital_pin_to_bit_mask[RED_LED]|digital_pin_to_bit_mask[GREEN_LED];
}
}
File: WInterrupt2.ino
/*
* WInterrupt2.ino - interrupt handlers with LPM modification
*
* Functions that provide a way for user written PORTN interrupt handlers to clear bits
* in the SR register of an ISR so the MCU can switch sleep modes. The user written
* handlers should return 0 or an unsigned LPM modebit value(s) to clear on ISR exit.
*
* Based on WInterrupts.cpp
*
* Author: Rick Kimball <rick@kimballsoftware.com>
*/
#define bit_pos(A) ((A) == 1u << 0 ? 0 \
: (A) == 1u << 1 ? 1 \
: (A) == 1u << 2 ? 2 \
: (A) == 1u << 3 ? 3 \
: (A) == 1u << 4 ? 4 \
: (A) == 1u << 5 ? 5 \
: (A) == 1u << 6 ? 6 \
: (A) == 1u << 7 ? 7 \
: 0)
typedef unsigned int (*uintFuncPtr)(void);
#define NUM_INTS_PER_PORT 8
static volatile uintFuncPtr intFuncP1[NUM_INTS_PER_PORT];
#if defined(__MSP430_HAS_PORT2_R__)
static volatile uintFuncPtr intFuncP2[NUM_INTS_PER_PORT];
#endif
/**
* attachInterrupt2 - assign a user defined function to a port interrupt pin
*/
void attachInterrupt2(uint8_t interruptNum, unsigned int (*userFunc)(void), int mode)
{
uint8_t bit = digitalPinToBitMask(interruptNum);
uint8_t port = digitalPinToPort(interruptNum);
if ((port == NOT_A_PIN) || !((mode == FALLING) || (mode == RISING))) return;
__dint();
switch(port) {
case P1:
P1IE |= bit;
P1IFG &= ~bit;
P1IES = mode ? P1IES | bit : P1IES & ~bit;
intFuncP1[bit_pos(bit)] = userFunc;
break;
#if defined(__MSP430_HAS_PORT2_R__)
case P2:
P2IE |= bit;
P2IFG &= bit;
P2IES = mode ? P2IES | bit : P2IES & ~bit;
intFuncP2[bit_pos(bit)] = userFunc;
break;
#endif
default:
break;
}
__eint();
}
void detachInterrupt2(uint8_t interruptNum)
{
uint8_t bit = digitalPinToBitMask(interruptNum);
uint8_t port = digitalPinToPort(interruptNum);
if (port == NOT_A_PIN) return;
switch(port) {
case P1:
P1IE &= ~bit;
intFuncP1[bit_pos(bit)] = 0;
break;
#if defined(__MSP430_HAS_PORT2_R__)
case P2:
P2IE &= ~bit;
intFuncP2[bit_pos(bit)] = 0;
break;
#endif
default:
break;
}
}
__attribute__((interrupt(PORT1_VECTOR)))
void Port_1(void)
{
uint8_t i;
for(i = 0; i < 8; i++) {
if((P1IFG & BV(i)) && intFuncP1[i]) {
unsigned bits2clear;
if ( bits2clear=intFuncP1[i]() ) {
__bic_status_register_on_exit(bits2clear);
}
P1IFG &= ~BV(i);
}
}
}
#if defined(__MSP430_HAS_PORT2_R__)
__attribute__((interrupt(PORT2_VECTOR)))
void Port_2(void)
{
uint8_t i;
for(i = 0; i < 8; i++) {
if((P2IFG & BV(i)) && intFuncP2[i]) {
unsigned bits2clear;
if ( bits2clear=intFuncP2[i]() ) {
__bic_status_register_on_exit(bits2clear);
}
P2IFG &= ~BV(i);
}
}
}
#endif