Thursday, February 07, 2013

Stopping Timer A on MSP430

Coding a microcontroller like MSP430 is a lot more difficult (at least for me) than Arduino but since I have a few around, I really want to learn. An important aspect of working with these are interrupts. I looked over the examples and they are all great but I wanted to do something a little bit more yet still simple. The idea: start a timer when the interrupt on a button fires, then stop it after a few seconds, if the button is not pushed again; if it is, restart the timer so it always stops the same time after the last button push. Pretty simple in theory; well, in practice as well after I figured it out. The most difficult task was to restart the timer with each button push. I tried to find info on the web and by reading the MSP430x2xx Family User's Guide but couldn't get anywhere: no matter what I tried the timer was stopping after what seemed like random times (after a lot of debugging I realized it wasn't random, the timer was always stopping after the right time measured since the first button push).

I tried several things but mainly:
  • set TACCR0=0 - I hoped this will reset the counter to 0 which is not true, and this was spelled out in the doc I mentioned

  • set TACTL = MC_0 - I hoped that stopping the timer means resetting the counter but it seems that the timer just stopped and when I re-enabled it it continued counting from where it left off.

  • In the end, re-reading the user's guide for the nth time, I noticed a paragraph in which it says that the register holding the current count (TAR) is writable (I was sure until then that is read only) and this was the key: when I want to reset the counter, I just write 0 in this register; just to make sure all is well, I also reset TACTL just to make sure the timer restarts - this may not be needed but it works so I left the code in there. Another thing is stopping the timer in the timer ISR by doing TACTL = MC_0 because I want the timer to shut down after the given time, and not keep counting - this makes the mcu go back to sleep mode.

    The final version I ended up with is here: I am sure it can be improved but again, it works now and exactly the way I wanted it so I'll keep it this way.

    Next thing: play with external interrupts.

    [Edit] Now that I figured out how to reset the timer, I changed the code to use a variable to handle the restart and stop of the timer: reset it to 0 when the button interrupt fires, increment it in the timer ISR and stop the timer after a few iterations. This is much more flexible because a) I can stop the timer at the exact time I want (not being dependent on the timer counter and the clock frequency and dividers) and b) I can reuse the timer for other things, changing the behavior based on the TAIV vector. Next step in developing my little app will use this approach.

    3 comments:

    PATRIK said...
    This comment has been removed by the author.
    Unknown said...

    WELL PLEASE HAVE A LOOK ON MY CODE ARE YOU SAYING THAT I SHOULD I DO.


    if((!(P2IN & BIT0))&& (value>0))
    {
    value --;
    TA1CCR0 = 105;
    TA1CCR2 = wave[value];
    TA1CCTL2 = OUTMOD_7;
    TA1CTL = TASSEL_2 + MC_1;
    P2IFG &=~BIT5;
    TACTL = MC_0;
    }

    Unknown said...

    I am using msp4630G2553.
    I want to find the time for which an adc value>511 and interpret this time to blink either red or green led.
    This is my code.

    #include
    unsigned int value=0;

    void ConfigureAdc(void);

    void main(void)
    {
    {
    int t;
    WDTCTL = WDTPW + WDTHOLD; // Stop WDT
    BCSCTL1 = CALBC1_1MHZ;
    BCSCTL2 &= ~(DIVS_3);
    P1DIR|=BIT0+BIT6;

    P1SEL|=BIT1;
    P1SEL |= BIT3;
    TACTL=TASSEL_2+ID_0+MC_2;
    ConfigureAdc(); // ADC set-up function call
    __enable_interrupt(); // Enable interrupts.

    while(1)
    {
    __delay_cycles(1000); ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
    __bis_SR_register(CPUOFF + GIE); // LPM0 with interrupts enabled// Low Power Mode 0 with interrupts enabled
    value = ADC10MEM;
    if(value>511)
    TA1R=0;
    else
    {
    t=TA1R;

    if(t<3000000)
    {P1OUT&=~(BIT0+BIT6);
    P1OUT|=BIT6;
    }
    else
    {P1OUT&=~(BIT0+BIT6);
    P1OUT|=BIT0;

    }

    }


    }

    }
    }

    // ADC10 interrupt service routine
    #pragma vector=ADC10_VECTOR

    __interrupt void ADC10_ISR (void)

    {

    __bic_SR_register_on_exit(CPUOFF); // Return to active mode

    }


    // Function containing ADC set-up
    void ConfigureAdc(void)
    {

    ADC10CTL1 = INCH_3 + ADC10DIV_3 ;
    ADC10CTL0 = SREF_0 + ADC10SHT_3 + ADC10ON + ADC10IE;
    ADC10AE0 |= BIT3; // ADC input enable P1.3
    }