Jump to content

retnuH

Members
  • Content Count

    6
  • Joined

  • Last visited


Reputation Activity

  1. Like
    retnuH reacted to Rickta59 in Problem with interrupts on P1 and a timer   
    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
  2. Like
    retnuH reacted to Rickta59 in Problem with interrupts on P1 and a timer   
    unsigned char count = 0; unsigned char times = 0; void handleButtonPress(void); void setup() { pinMode(RED_LED,OUTPUT); pinMode(GREEN_LED,OUTPUT); pinMode(PUSH2, INPUT_PULLUP); digitalWrite(RED_LED, HIGH); digitalWrite(GREEN_LED, LOW); attachInterrupt(PUSH2, handleButtonPress, RISING); } void loop() { LPM3; } void handleButtonPress(void) { ++times; P1OUT |= digital_pin_to_bit_mask[RED_LED]; P1OUT &= ~digital_pin_to_bit_mask[GREEN_LED]; TACCR0=0x1FFF; TACCTL0 = CCIE; TACTL = MC_1 | ID_0 | TASSEL_1 | TACLR; } __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; count == 0; } else { P1OUT ^= digital_pin_to_bit_mask[RED_LED]|digital_pin_to_bit_mask[GREEN_LED]; } }
     
    In your original code you were overwriting the P1OUT values instead if just changing the pins that needed to be changed. The pullup resistor directon for the push button is controlled by the value of BIT3 in P1OUT. Overwriting wiped out that setting. In the code above I turned on and off the bits for the leds instead of overwriting. I also got rid of signal.h and used the more naked style of defining an ISR using __attribute__. You might also notice for the handling the PORT1 interrupt I used the more Energia friendly method of attachInterrupt() with a function handler. This makes it easier to write handlers for different pins on the same port.
     
     
    Yes interrupts are automatically enabled by the Energia framework code. The watchdog timer is automatically enabled to increment the millis() value. If you don't care about that you can disable it with a disableWatchDog() call. If you use Serial on a chip that doesn't have a real UART, it will steal the TIMER1 interrupt and will not be available for use. There are other cases. You might take a look at the core code and see what is going on down inside the framework for a deeper understanding.
     
    -rick
  3. Like
    retnuH reacted to gordon in Problem with interrupts on P1 and a timer   
    This was working a couple of days back... Load on a LaunchPad, poke button. Scratch that, I didn't notice the Energia part. You could still adapt, though.
     

    #include #include #include #define LED BIT6 #define BUTTON BIT3 #define DEBUG #undef DEBUG #ifdef DEBUG #define DEBUGLED BIT0 #endif void __attribute__((interrupt (PORT1_VECTOR))) port1_isr(void); void __attribute__((interrupt (WDT_VECTOR))) wdt_isr(void); #define PWM_PERIOD 128 static const uint16_t brightness_levels[] = { 0, 6, 23, 56, PWM_PERIOD }; #define n_brightness_levels ( sizeof( brightness_levels ) / sizeof( brightness_levels[0] ) ) static volatile uint8_t cur_brightness = 0; static volatile uint8_t debounce_ctr = 0; /* /3 -- WDT_ADLY_250 assumes LFXTCLK @ 32KiHz, VLO is ~12KHz */ #define POWEROFF_TIMER (2400 / 3) /* 10m @ 250ms */ #if 0 #undef POWEROFF_TIMER #define POWEROFF_TIMER (40 / 3) /* TEST: 10 seconds */ #endif static volatile uint32_t poweroff_timer = POWEROFF_TIMER; static volatile bool poweroff = false; void main(void) { WDTCTL = WDT_ADLY_250; /* 750ms if running on VLO */ BCSCTL2 |= SELM_3 | SELS; /* MCLK to LFXTCLK, SMCLK to LFXTCLK */ BCSCTL3 |= LFXT1S_2; /* LFXT1 to VLO */ IE1 |= WDTIE; IFG1 &= ~WDTIFG; P1DIR |= LED; CCR0 = PWM_PERIOD; CCTL1 = OUTMOD_7; TACTL = TASSEL_1; P1DIR &= ~BUTTON; P1IE |= BUTTON; P1REN |= BUTTON; P1IFG &= ~BUTTON; P1OUT |= BUTTON; #ifdef DEBUG P1DIR |= DEBUGLED; P1OUT &= ~DEBUGLED; #endif __eint(); while( 1 ) { if( poweroff || cur_brightness == 0 ) { TACTL |= MC_0; CCR1 = 0; P1SEL &= ~LED; P1OUT &= ~LED; __bis_status_register( debounce_ctr == 0 ? LPM4_bits : LPM3_bits ); } else { TACTL |= MC_0; if( cur_brightness == n_brightness_levels - 1 ) { P1SEL &= ~LED; P1OUT |= LED; } else { P1SEL |= LED; CCR1 = brightness_levels[ cur_brightness ]; TACTL |= MC_1; } __bis_status_register( LPM3_bits ); } } WDTCTL = 0xDEAD; } void __attribute__((interrupt (PORT1_VECTOR))) port1_isr(void) { P1IE &= ~BUTTON; debounce_ctr = 1; if( !poweroff && ( ++cur_brightness >= n_brightness_levels ) ) { cur_brightness = 0; } poweroff_timer = 0; poweroff = false; P1IFG &= ~BUTTON; __bic_status_register_on_exit( LPM4_bits ); } void __attribute__((interrupt (WDT_VECTOR))) wdt_isr(void) { #ifdef DEBUG P1OUT ^= DEBUGLED; #endif /* Decrement debounce_ctr iff > 0, else WDT timer will keep * it on for one more loop, causing a delay in poweroff */ if( debounce_ctr > 0 && --debounce_ctr == 0 ) { P1IFG &= ~BUTTON; P1IE |= BUTTON; } if( ++poweroff_timer == POWEROFF_TIMER ) { poweroff = true; } IFG1 &= ~WDTIFG; __bic_status_register_on_exit( LPM4_bits ); }
×
×
  • Create New...