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.

Timer_A vector

Other Parts Discussed in Thread: MSP430F2012

I'm using the MSP430F2012 and am still sorting out the details of clocks, timers and interrupts. I went over the Timer_A code examples and couldn't help noticing there are no examples that give the vector ID for CCR0. There is only one example in which CCR0 and CCR1 are used together, and in it separate interrupt handling routines are used. Is that by necessity? Since TAIV for CCR1=2, and CCR2=4, I'd guess CCR0=0 ...

Thanks.

  • Hi Duane,

    Timer A actually has two interrupt vectors, and TAIV is used in connection with only one of them:

    So yes, by necessity you must register two interrupt handlers to handle the CCR0 interrupt and any other Timer A interrupt.  The CCR0 interrupt condition does not "appear" in TAIV.  In fact, if you read TAIV from the CCR0 interrupt handler, you might inadvertently clear another Timer A interrupt.

    When TAIV == 0, no associated interrupt is pending.  But remember, CCR0 is not associated with TAIV.  When TAIV == 0, it says absolutely nothing about the interrupt status of CCR0.

    When the CPU processes the interrupt request for CCR0, the Timer automatically clears the CCIFG for CCR0, even with no code in the ISR.  However, when the CPU processes the "other" Timer_A interrupt request, the Timer doesn't automatically clear anything.  Code in the ISR must clear the request, typically by reading TAIV.

    Jeff

  • Jeff,

    Thanks for the detailed response. I see now that if I had looked up the TAIV register in the Users Guide, I would have seen that 0=No interrupt pending. If I understand it correctly, then, reading TAIV automatically clears the flag for overflow, CCR1 or CCR2. A little like Schroedinger's cat, no?

    :)

    If i'm understanding your explanation, reading TAIV at any time -- and more to the point, outside of TIMERA1_VECTOR, clears a flag (if pending) for overflow/CCR1/CCR2. Thus, doing so outside of TIMERA1_VECTOR would be ill-advised, to say the least.

    CCR0, on the other hand, is handled in the TIMERA0_VECTOR ISR, which, if present, clears its interrupt flag.

  • Duane Schrag said:

    ...reading TAIV automatically clears the flag for overflow, CCR1 or CCR2. A little like Schroedinger's cat, no?

    Luckily, no.  ;-)  The value returned during the TAIV read operation tells you exactly which flag was cleared inside the timer.  It tells you deterministically which was the highest priority interrupt (aside from CCR0) that was pending in the timer when you read TAIV.  And that's also the flag that gets cleared.  It's really a very nice design, undoubtedly with some special care in the silicon to get the synchronization right.

    Jeff

  • Jeff Tenney said:
    It's really a very nice design

    It's even nicer:
    The values increment by 2, starting with 0 (no interrupt).
    So you can simply add the value read from TAIV to the program counter, to make the program jump into a jump table that contains jumps to the different handlers.
    This is how the _even_in_range() intrinsic works for switch statements.
    No individual comparisons and branches for every case, just an ADD followed by an implicit JMP and you're where you want.

    An example in assembly language from the 1611:

    timera1_handler:
    add      TAIV         ,r0
    reti                         ; NO INT
    jmp      timera_cc1_handler     ; CC1
    jmp      timera_cc2_handler     ; CC2
    reti                         ; RESERVED
    reti                         ; RESERVED
    jmp      timera_ifg_handler     ; TAIFG

    timera_cc1_handler:
    reti
    [...]

    only 6 MCLK cycles and you're at the start of any fo the various (here only 3) handlers. In case of the last one, the jmp can be omitted, saving two more cycles. Small and fast.

  • Is test for 0 necessary? Since this is an ISR, wouldn't there have to be an interrupt pending?

  • Duane Schrag said:
    Is test for 0 necessary? Since this is an ISR, wouldn't there have to be an interrupt pending?

    '0' is used for 'no interrupt' for all IV registers. If you do something that clears teh IFG bit before you read teh IV register, what value shall it return? There is no more interrupt pending. For some modules, this may happen due to hardware resons too (e.g. a pending NACKIFG interrupt can be cleared by a START or STOP condition, but the new event is not marked for triggering an interrupt)

    You can use it even to your advantage: put your reading of the IV register into a while(1) loop and exit the loop if the reading is zero. This way you can handle mutiple events for this vector without the overhead of leaving and re-entering the ISR.

  • I'll try this again. Since this an ISR, won't it always be true that an interrupt has occurred? If that is the case, would there be any need to check for TAIV=0, since the answer would always be no? I believe that when you brought this up earlier you noted the small number of CPU cycles in play; isn't checking for TAIV=0 a waste of CPU cycles?

  • That an interrupt occurred when the ISR callign was initiated, doe snot mean that an interrupt is still pending when the ISR is actually executed and reads the IV register.
    Normally, you're right, but some hardware modules have some conditions where an intrrupt event can be cleared by a different event, and if this secondary event is not configured to cause an interrupt, it does not appear in teh IV register, nor does the cleared one.

    Also, as I wrote, a suggestion to handle multiple interrupts without leavign and re-entering the ISR is to loop around the IV register read. SO you read and handle the interrupts, until the register reads 0 and no more interrupts are to handle. This is true for all modules. (e.g. different capture interrupts happening at once). Wihtout a 'no interrupt' result, you could only read the IV register once and then had to exit the ISR so it can be called again if another interrupt is pending.

  • So a CCR1, CCR2 or overflow interrupt could transfer the code pointer to the ISR, but before it had a chance to read/evaluate TAIV, the value in TAIV could have been changed by something else, so when control returns to this ISR, TAIV = 0?

    If there are, say, a CCR1 and an overflow interrupt pending, I assume TAIV will show the one with the higher priority. If I use a "while(TAIV)" loop in the ISR, it will return the first interrupt condition on the first pass; will TAIV be updated with the second interrupt condition, rather than 0, before the second pass?

  • Jens-Michael Gross said:

    timera1_handler:
    add      TAIV         ,r0
    reti                         ; NO INT
    jmp      timera_cc1_handler     ; CC1
    jmp      timera_cc2_handler     ; CC2
    reti                         ; RESERVED
    reti                         ; RESERVED
    jmp      timera_ifg_handler     ; TAIFG

    All the timera_???_handler used above could logically end with a "jmp timera1_handler" instead of a "reti". This could shorten consecutive TimerA1 interrupt latency by a few (~11) cycles, at the expense of 2 more cycles in the isr.

  • OCY: yes, that's how I do it in some but not all cases. So after solving all pending interupts without ever leaving the ISR, the ISR exits through the TAIV==0 RETI.

    However, this requires saving all processor registers ever used in any of the sub-ISRs before reading TAIV, and restoring them before looping. Or writing an exit code that is jumped-to for TAIV==0, and the looping happends no to the beginning of the ISR but just to the TAIV read.

    Duane Schrag said:
    So a CCR1, CCR2 or overflow interrupt could transfer the code pointer to the ISR, but before it had a chance to read/evaluate TAIV, the value in TAIV could have been changed by something else, so when control returns to this ISR, TAIV = 0?

    No, this is not possible. Not for the timer interrupts. Jus tbecause the only thig that can clear a tiem rinterrupt is manually clearing the '0' bit. And if the PCU is still capable of cklearing the bit, the ISR entry sequence hasn't been stated, and won't start anymore as the bit is clear.

    But there are other modules where the interrupt trigger can be cleared by hardware while the CPU is already entering the ISR or waking up from LPM. One example have been the start/stop/NACK condition interrupts in the USCI I2C. Also, a DMA, triggered by something else, might clear the trigger, while the CPU is already entering the ISR.

    However, I think it is possible to construct such a situation even with the timers:
    If you're doing a loop that just write '0' to TA0CCR1, it might happen that teh interrupt is triggered while the processor has just started executing this command. So the command is executed to its end, but the ISR entry sequency has Already been started. But when the ISR vector is fetched from the vector table, the IFG bit has been already cleared, and TAIV will return 0. The chance that this will happen is relatively high (>50%, if it is a tight assembly loop of only two instructions).
    I don't say that this handling of interrupt-rlevant registers makes any sense in a normal application, but I did see people trying similar things - mostly because they didn't really know about interrupt programming.

    Duane Schrag said:
    If there are, say, a CCR1 and an overflow interrupt pending, I assume TAIV will show the one with the higher priority. If I use a "while(TAIV)" loop in the ISR, it will return the first interrupt condition on the first pass; will TAIV be updated with the second interrupt condition, rather than 0, before the second pass?

    Yes. It's a virtual queue.
    And if the same interrupt you already handled happens again while you're still in the ISR, it will jump on top position again. (well, if this happens, it's not a realyl healthy condition, as it indicates that your code is probably much too slow)

**Attention** This is a public forum