This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

"resetting" Timer_A



the documentation for timer_a (in the MSP430x2xx family users guide) says, for the capture/compare unit Control register Output Mode bits:

001 Set The output is set when the timer counts
to the TACCRx value. It remains set until
a reset of the timer
, or until another
output mode is selected and affects the
output.

(emphasis added)  So... how does one accomplish a "reset of the timer" ?!  If the output bit is set as a result of having reached the CCR value, the only way I can find to reset the bit is to change the whole CCTLx register to OUTMOD_OUTPUT with a 0 in the OUT bit, which is not what I'd think would be called a reset.  various writes to TACTL don't seem to do anything...

Is there any other way to change the output bits?  I want them all set back to 0 in the overflow ISR, so preferably in as few instructions as possible.

 

  • westfw said:
    how does one accomplish a "reset of the timer"

    Every MSP comes with a pin called 'RST' or 'RESET'. It will reset the whole hardware including the timers.

    All outmodes are event triggered. So the output bit changes from its current state (which is 0 at startup because the default outmode is OUTBIT and the OUT bit value is 0) to the new state when the event happens.

    In case of the 'set' mode, the only event that will ever happen is 'set'. so the bit is set and set again and again but never reset. This mode is intended to provide a timeout-like function (a monoflop) which will set the output bit after a given time if the event is not prevented before it happens (e.g. by any other 'trigger' event). It's the job of the application to reset the bit again if necessary. By temporarily switching to the OUTBIT outmode.

    westfw said:
    Is there any other way to change the output bits?  I want them all set back to 0 in the overflow ISR, so preferably in as few instructions as possible.

    Then use the set/reset outmode. It will set the bit when the CCRx value is reached and clear it when TxR overflows. It will happen immediately when the overflow occurs, no matter when the overflow ISR is actually executed (if there is one at all).

    That's the way the hardware PWM is implemented. Set at trigger point, reset at overflow. (where the overflow point is not necessarily the 16 bit overflow, but alternatively the TxCCR0 value). And if you need inverted logic, there's the reset/set mode. Or the toggle modes for more specific problems.

  • Jens-Michael Gross said:
    Then use the set/reset outmode. It will set the bit when the CCRx value is reached and clear it when TxR overflows. It will happen immediately when the overflow occurs, no matter when the overflow ISR is actually executed (if there is one at all).

    That's the way the hardware PWM is implemented. Set at trigger point, reset at overflow. (where the overflow point is not necessarily the 16 bit overflow, but alternatively the TxCCR0 value).

    Huh.  The documentation doesn't say that SET/RESET resets on overflow, only on reaching CCR0, and I thought I had tried it and had it not work (but it does seem to be working now.)

    But I want to use the CCR0 unit separately (also resetting its associated output pin at overflow); is that possible?  I'm operating the timer in CONTINUOUS mode; does that prevent the reset of TxCC1 output when TxCCR1 == TxCCR0?  I'm trying to get two HW pwms with only the two CC units.

  • westfw said:
    The documentation doesn't say that SET/RESET resets on overflow, only on reaching CCR0

    No, the double outmodes (x/y) do the first action on reacing CCRx and the second on TAR->0. Which in up mode is one tick after reaching CCR0, in cont mode at overflow. and in up/down mode when counting from 1 down to 0. It does, however, not happen at setting TAR=0 or using the reset bit in TACNTL0.

    westfw said:
    But I want to use the CCR0 unit separately

    If the timer runs in cont mode, CCR0 is behaving the same way as the other CCRs. Except for its own interrupt vector.

    P.s.: if you use the timer in up mode and use the set/reset outmode for CCR0, then TA.0 is set when TAR reaches CCR0 and reset one tick later when it counts to 0. Or, if the timer is running in up/down mode, it is set when reaching CCR0 and reset when reaching 0 again. This allows CCR0 used as pulse generator or 50% duty cycle (half frequency 50% can be done with toggle mode) output even if using CCR0 for setting the PWM frequency /TimerA range.

  • So the code here should work (provide varying PWM on pin 6 of my ez430-f2013) regardless of how I set TA0CCR0 on the relevant line?  That doesn't seem to be the case.  I get the desired behavior if CCR0 is bigger than 65535-8000, but it looks like the output bit never resets if CCR0 is less than that (ie, it looks like the reset happens ONLY for CCR0 match and NOT overflow...)

    This is mcpgcc, but it should be conceptually generic...

     

    #include <signal.h;>
    #include <msp430x20x3.h;>

    volatile unsigned long timerA_tick_count = 0;
    interrupt(TIMERA1_VECTOR) timera_isr()
    {
        timerA_tick_count++;
        TA0IV = 0;  /* reset intrerupt */
        TA0R = 65535-8000; /* overflow 8000 ticks from now */
    }

    void delay(unsigned long ms)
    {
        unsigned long start = timerA_tick_count;

        while (timerA_tick_count - start <= ms) ; // spin.
    }

    void main() {

        int i;
        /*
         * Change clock to approx 8MHz (prescaled from internal 1.1MHz
         */
        BCSCTL1 = CALBC1_8MHZ;                    // Set range
        DCOCTL = CALDCO_8MHZ;                     // Set DCO step + modulation */

        /*
         * set up pin 6 as timer PWM output
         */
        P2SEL = 0; /* use port2 pins as outputs */
        P1SEL |= 1<<6; // TA0CCR1 output on pin 6
        P1DIR |= 1<<6;

        /*
         * Set up timer A
         */
        TA0CTL = TASSEL_SMCLK + MC_CONT + TAIE; /* SMCLK, continuous mode */
        TA0R = 65536-8000; /* initial value */
        TA0CCTL1 = OUTMOD_SET_RESET; // timer outputing PWM
        TA0CCTL0 = OUTMOD_SET_RESET; // timer outputing PWM
        eint(); /* global interrupts on */

    /*********************************************
     * the big question.  Does changing TA0CCR0 change the behavior of TA0CCTL1 ?
     *********************************************/
        TA0CCR0 = 100;

        while (1) {
    for (i=100; i < 8000; i += 100) {
        TA0CCR1= (65535-8000)+i;
        delay(100);
    }
        }
    }

  • First, I wouldn't do TA0R = 65535-8000. Since it executing your ISR has taken at least the ISR latency time, plus maybe LPM wakeup time plus maybe execution time of another ISR in charge when the interrupt came, You might miss some ticks and the timer interval extends to (possibl ymuch)more than 8000 ticks. So adding TA0R to this equation would be more precise. Yet it may be that you miss one of the CCRs then. That's why the use of CCR= as 'overflow' margin is much better. Anyway, if you need the second PWM and don't care for the PWM frequency skew, it should work your way.

    Based on your observations I did anothe rlook into the TA documentation of the 2xx family and discovered that you're right. The second event does not happen at TAR overflow but at reaching CCR0, which is (in up mode) 1 tick before and disqualifies CCR0 to use the X/Y modes. THis is what the comment to the OUTMODx register bits says: "Modes 2, 3, 6, and 7 are not useful for TACCR0 because EQUx = EQU0."

    With maybe introducing some additional jitter to TAR, you can emulate it anyway: Use CCR0 in set mode or reset mode, set an ISR up for CCR0 and switch between these two modes and the CCR0 value. So CCR0 is either 0 in reset mode or 65535-x in set mode. If you set CCIS properly, you can test the CCI bit for the next mode to set: If CCI is set, then set RESET mode and CCR=0, if CCI is clear, select SET mode and set CCR0 accordingly.

    Anyway, doing any software for the PWM control may quickly lead you into ressource problems. Remember, each timer tick is one MCLK cycle. So if your settings are causing an interrupt after 100 ticks, it is only 100 MCLK cycles away. This is almost the time needed for wakeup from deeper LPMs (5us@8MHz plus ISR latency is already 50 ticks)

  • Rats.  I was hoping I was missing something unobvious :-(

    Is timer_A behavior different on other MSP430 families?  The 5xx family users guide has slightly different explanatory text, but the bit descriptions are all the same, and I thought the fact that timerA was timerA regardless of other chip details was one of the advantages of the MSP430 series...

     

  • PS: I had thought about switching between set and reset modes in the overflow ISR, but I'm not sure I see how it helps me get two "almost hw" PWM outputs.  It's not only the fact that the bits don't reset at overflow, but also that CC1 WILL reset when it hits CCR0 (even in continuous mode, apparently.)  So CC1 behavior would always be dependent on CC0 behavior...  It pretty much looks like I'm stuck with only one full-range PWM without "lots" of SW involvement.

     

  • Yes, you can get two "almost hardware" PWM output. As a mater of fact, you can get two separate SET-RESET waveforms (not limited to PWM).

    Set TACTL to continues mode.

    Set TACR0 to when you want to change OUT0 in the near future.

    Set TACCTL0 to make that change (i.e., SET or RESET OUT0) and also generate an interrupt.

    In the TACCTL0 ISR, do the above two items for the next near future.

    Do the same independently for OUT1 with TACR1 and TACCTL1.

    This is "almost hardware" because you are not depending on the CPU cycles for precise  timing. The CPU is used to set up the hardware (TimerA) to SET-RESET OUT0 and OUT1 in the near future.

     

     

  • cow: I still don't see how that addresses the issue of the CC1 output changing states whenever TAR reaches TACR0. (it does NOT appear that operating in continuous mode prevents set/reset/interrupt operations when CC1 reaches CR0.)

    It doesn't look like I can change CC0 at all without affecting CC1 ?

    I guess using the CC unit to change pin states has a slight advantage over changing pin states in the ISR in terms of determinism, but maintaining and searching a sorted list of pin-change-times doesn't seem very "light" on the SW side.

     

  • When I said:

    "Set TACCTL0 to make that change (i.e., SET or RESET OUT0) and also generate an interrupt."

    I mean either use: OUTMOD_1 to set OUTn (when TAR counts to TACCRn in the near future)

    or use: OUTMOD_5 to reset OUTn (when TAR counts to TACCRn in the near future)

    You could also use OUTMOD_4 to toggle OUTn.

    But you should not use OUTMOD_2,3,6 or 7 unless you are not using CC0.

  • Ah.  Got it.  I was stuck in a rut, there...

    So for example, since my desire is that the pin reset at timer overflow, I can just set the CCRx to "reset at FFFF" in the CC ISR...

     

  • So here is what I ended up with:

    • At timer overflow:
    1.  Re-initialize TAR to (FFFF - <period>). (using add, to prevent latency-caused jitter)
    2.  Set both CCTL register to OUTMOD_RESET + CCIE
    3.  Set each CCRx register to appropriate (saved) "off" time.
    • At (either) CCRx interrupt:
    1. Set CCTLx to OUTMOD_SET (with no interrupt)
    2. set the matching CCRx register to 0xFFFF, so that the output bit will be set "just before" the next overflow.

    I'm not sure I'm happy with the amount of code and RAM this takes (does anyone have ideas for improvement?), but it seems to work.  The LED fades appropriately, and the output waveform looks nice on a scope.

    Here's the code:

     

    #include <signal.h>
    #include <msp430x20x3.h>

    volatile unsigned long timerA_tick_count = 0;
    volatile unsigned int pwm0, pwm1;

    interrupt(TIMERA0_VECTOR)
    wewtimer_isr0()
    {
        TA0CCR0 = 0xFFFF; /* set at overflow */
        TA0CCTL0 = OUTMOD_SET; /* note: don't interrupt */
    }

    interrupt(TIMERA1_VECTOR)
    wewtimer_isr()
    {
        timerA_tick_count++;
        switch (TA0IV) { /* reset intrerupt */
        
        case TAIV_OVERFLOW:
    TA0R += 65535-8000; /* count another ms */
    /*
     * Note that at this point, both CC units should have SET
     * their respective output bits when TAR hit 0xFFFF
     */
    TA0CCTL0 = OUTMOD_RESET + CCIE; /* reset and int at count */
    TA0CCR0 = pwm0; /* set new count */
    TA0CCTL1 = OUTMOD_RESET + CCIE;
    TA0CCR1 = pwm1;
    break;

        case TAIV_CCR1:
    TA0CCR1 = 0xFFFF; /* set at overflow */
    TA0CCTL1 = OUTMOD_SET; /* note: don't interrupt */
    break;
        }
    }

    void delay(unsigned long ms)
    {
        unsigned long start = timerA_tick_count;

        while (timerA_tick_count - start <= ms) ; // spin.
    }

    void main() {

        int i;
        /*
         * Change clock to approx 8MHz (prescaled from internal 1.1MHz
         */
        BCSCTL1 = CALBC1_8MHZ;                    // Set range
        DCOCTL = CALDCO_8MHZ;                     // Set DCO step + modulation */

        /*
         * set up pin 6 as timer PWM output
         */
        P2SEL = 0; /* use port2 pins as outputs */
        P1SEL |= 1<<6; // TA0CCR1 output on pin 6
        P1DIR |= 1<<6;

        /*
         * Set up timer A
         */
        TA0CTL = TASSEL_SMCLK + MC_CONT + TAIE; /* SMCLK, continuous mode */
        TA0R = 65536-8000; /* initial value */
        TA0CCTL1 = OUTMOD_RESET+CCIE; // timer outputing PWM
        TA0CCTL0 = OUTMOD_RESET+CCIE; // timer outputing PWM
        eint(); /* global interrupts on */

    /*********************************************
     * the big question.  Does changing TA0CCR0 change the behavior of TA0CCTL1 ?
     *********************************************/
        TA0CCR0 = pwm0 = 65536-4000; /* 50% while we test pwm1 */

        while (1) {
    for (i=100; i < 8000; i += 50) {
        TA0CCR1 = pwm1 = (65535-8000)+i;
        delay(100);
    }
        }
    }

     

  • (warning; this code has problems at the extremes of the PWM values.  Presumably where the latency causes TAR to increment past the value that the code is about to set, before it gets set to that value.  For "reasonable" resolutions of PWM, I'll probably hard-wire the "completely on" and "completely off" values.)

     

  • westfw said:
    warning; this code has problems at the extremes of the PWM values

    Yes, that's the problem with almost-hardware PWM :)
    westfw said:
    I'll probably hard-wire the "completely on" and "completely off"
    This is necessary even for plain hardware PWM. Since you have two trigger-points, you cannot generate 100% and 0%. One of them might be possible, but the docs don't state which 'event' has precedence when CCRx==CCR0. No matter whichone has, the other extreme isn't possible then. So the PWM works only with duty cylces from 0,00...1 to 0,99... (depending on the timer resolution). There's always a spike of at least one timer tick. So if you need 0% or 100%, better set the out bit in the control register and switch the outmode to 0.
    In case of youe almost-hardware PWM, this requires even more code, as you'll need to disable the mode switching in the ISR if you have 0% or 100% duty-cycle. (or whatever your effective margins are).

  • I am not a native c speaker. Pardon my c.

    Near the top of  “wewtimer_isr();” you have the statement: “timerA_tick_count++;”. Shouldn’t that statement be moved down inside “case TAIV_OVERFLOW:”?

  • old_cow_yellow said:
    Near the top of  “wewtimer_isr();” you have the statement: “timerA_tick_count++;”. Shouldn’t that statement be moved down inside “case TAIV_OVERFLOW:”?


    It definitely should, or else it will be incremented on every interrupt, being it from TAIV_OVERFLOW or TAIV_CCR1.
    Well since both happen once on each round, it just ticks twice as fast and not erratically :)

  • yes, the tick count increment needs to be inside the case, and yes it causes exactly double-speed counting, which isn't immediately noticeable :-(

     

**Attention** This is a public forum