EmbeddedRelated.com
Forums
Memfault Beyond the Launch

Combining polling and interrupts with timers.

Started by Andy Warner April 30, 2008
I have an application where I have tight assembler loops
polling Timer A and wiggling pins.

I would like to combine this with running Timer B in
interrupt mode to provide a "if it's not done by now
it never will be" exit path for the code. I'm not worried
about the interrupt changing the Timer A loop timing,
because it will be terminating it anyway (e.g. if I get
an interrupt at all, it's already game over.)

I can't find a way to run one timer in polled mode, and
another in interrupt mode. This is because the only bits I
can identify to use for polling (I use TAIV, so I don't have to
spend an instruction clearing it) will cause interrupts if
I enable them.

Anyone worked through polling one timer while using
interrupts on the other ? I can't see how it's possible.
--
Andy

Beginning Microcontrollers with the MSP430

TAIV stands for Timer_A Interrupt Vector. It has nothing to do with
Timer_B interrupts.

--- In m..., "Andy Warner" wrote:
>
> I have an application where I have tight assembler loops
> polling Timer A and wiggling pins.
>
> I would like to combine this with running Timer B in
> interrupt mode to provide a "if it's not done by now
> it never will be" exit path for the code. I'm not worried
> about the interrupt changing the Timer A loop timing,
> because it will be terminating it anyway (e.g. if I get
> an interrupt at all, it's already game over.)
>
> I can't find a way to run one timer in polled mode, and
> another in interrupt mode. This is because the only bits I
> can identify to use for polling (I use TAIV, so I don't have to
> spend an instruction clearing it) will cause interrupts if
> I enable them.
>
> Anyone worked through polling one timer while using
> interrupts on the other ? I can't see how it's possible.
> --
> Andy
>

On Wed, Apr 30, 2008 at 5:10 PM, old_cow_yellow
wrote:
> TAIV stands for Timer_A Interrupt Vector. It has nothing to do with
> Timer_B interrupts.

Yeah - I know that - I must not have been clear.

I want to poll Timer A for rollovers (very fast) while
simultaneously having Timer B running (very slowly)
in interrupt mode.

The problem arises (as far as I can figure out) from
the fact that the only flags I can poll on Timer A are
the interrupt flags, so I have to have interrupts disabled
globally, or Timer A would be generating them, and
I don't have time for that.
--
Andy

I see. Maybe you can use the WDT instead of Timer_B. Or, maybe you do
not need to use Timer_A for the fast loop. What are the MCLK, SMCLK,
and ACLK frequencies? What does your fast loop need to do? Can I help
you with your code?

--- In m..., "Andy Warner" wrote:
>
> On Wed, Apr 30, 2008 at 5:10 PM, old_cow_yellow
> wrote:
> > TAIV stands for Timer_A Interrupt Vector. It has nothing to do with
> > Timer_B interrupts.
>
> Yeah - I know that - I must not have been clear.
>
> I want to poll Timer A for rollovers (very fast) while
> simultaneously having Timer B running (very slowly)
> in interrupt mode.
>
> The problem arises (as far as I can figure out) from
> the fact that the only flags I can poll on Timer A are
> the interrupt flags, so I have to have interrupts disabled
> globally, or Timer A would be generating them, and
> I don't have time for that.
> --
> Andy
>

Hi Andy
You state that you're polling Timer A for rollovers. A slow event, I
assume you need to catch these faster than the interrupt latency allows.
Why not simply disable the relevant Timer A interrupts? The flags will
still get set. In your case you just clear TAIE in TACTL. TAIFG will
still become set everytime TAR rolls over, but no interrupt event will
occur, why poll TAIV? It makes no sense when you could do the same for
any individual interrupt. IN fact testing TAIE in speed sensitive
application makes more sense since it is bit 0x0002 which makes the src
use one of the CG registers, unless you are simply polling TAIV for non
zero of course, which does the same, except it will react, ie be non
zero for ANY timer event. If no other timer channels are used their IFG
bits will ultimately become set.

In the 2xx series micros the interrupt latency is given as 6 cycles for
the CPU core and 5 for the CPUX core. Compared to the older 1xx cores
this would seem to imply that some parallel processing takes place WHILE
the current execution is executing, whereas the 1xx cores processed
interrupts After the current instruction completed. If this is the case
then an interrupt on a 2xx series will generally be as fast as a tight
polling loop (the loop takes 6 clocks using CMP Rn,&regname, JZ
loopstart) but needs to clear the calling flag after processing, then
get back here, so another 6 cycles vs the 5 taken by RETI. Also you
can''t use low power in the polling loop, whereas you can waiting for an
interrupt.

You can still use the dual timer method of checking the Timer A event
with Timer B if you use interrupts. Since the implication of your post
is that all other interrupts (bar Timer B) are disabled then using
interrupts wouldn't cause slow reaction in the case that an interrupt
was already executing.

I'd be very interested to know what you are trying to achieve and why
you feel that polling is necessary, in what seems like a perfect use for
an isr.

Cheers

Al

Andy Warner wrote:

>On Wed, Apr 30, 2008 at 5:10 PM, old_cow_yellow
> wrote:
>
>
>>TAIV stands for Timer_A Interrupt Vector. It has nothing to do with
>> Timer_B interrupts.
>>
>>Yeah - I know that - I must not have been clear.
>
>I want to poll Timer A for rollovers (very fast) while
>simultaneously having Timer B running (very slowly)
>in interrupt mode.
>
>The problem arises (as far as I can figure out) from
>the fact that the only flags I can poll on Timer A are
>the interrupt flags, so I have to have interrupts disabled
>globally, or Timer A would be generating them, and
>I don't have time for that.
>
>

On Wed, Apr 30, 2008 at 8:35 PM, Onestone wrote:
> [...]
> You state that you're polling Timer A for rollovers. A slow event, I
> assume you need to catch these faster than the interrupt latency allows.

Correct. I'm clocking Timer A externally from a fast source, and have a
divisor of 16, so I can generate a secondary frequency locked to
TACLK on TA1. TA1 is toggling at 500-900KHz.

> Why not simply disable the relevant Timer A interrupts? The flags will
> still get set. In your case you just clear TAIE in TACTL. TAIFG will
> still become set everytime TAR rolls over, but no interrupt event will

That's not the behaviour I thought I observed. When I disabled the various
interrupts (TAIE, CCIE), I do not believe I saw the interrupt flags set (TAIFG
and CCIFG respectively) - which made sense, because no interrupt _was_
pending. Maybe I was wrong, I'll check again, but I could swear that that
was the exact behaviour I was looking for and did not get.

> occur, why poll TAIV? It makes no sense when you could do the same for
> any individual interrupt. IN fact testing TAIE in speed sensitive
> application makes more sense since it is bit 0x0002 which makes the src
> use one of the CG registers, unless you are simply polling TAIV for non
> zero of course, which does the same, except it will react, ie be non
> zero for ANY timer event. If no other timer channels are used their IFG
> bits will ultimately become set.

Exactly, I have 1 possible source in TAIV, so I check for non-zero, and it
self-clears so that saves me an instruction.

> In the 2xx series micros the interrupt latency is given as 6 cycles for
> the CPU core and 5 for the CPUX core. Compared to the older 1xx cores
> this would seem to imply that some parallel processing takes place WHILE
> the current execution is executing, whereas the 1xx cores processed
> interrupts After the current instruction completed. If this is the case
> then an interrupt on a 2xx series will generally be as fast as a tight
> polling loop (the loop takes 6 clocks using CMP Rn,&regname, JZ
> loopstart) but needs to clear the calling flag after processing, then
> get back here, so another 6 cycles vs the 5 taken by RETI. Also you
> can''t use low power in the polling loop, whereas you can waiting for an
> interrupt.

I don't wait long (~1-2uS) for these interrupts, they are continuous while I'm
in this mode, so I'm not concerned about low power. I am also checking
for periods when the TACLK source is absent, which means tracking persistent
values across individual timer ticks, doing that in an ISR involved reading
memory, which takes too long. I found that the ISR could react to the event
OK, but could not become aware of context easily. My first implementation
was interrupt-based, I re-wrote it in assembler to poll the timer registers
and I hold context in registers across loops.

I also manipulate TACCTL1 to set the OUTMODx bits for TA1 after
periods, according to a varying pattern, and I found that the ISR latency was
loose enough that sometimes I would get spikes on TA1 or +/- 1 extra transition.

> You can still use the dual timer method of checking the Timer A event
> with Timer B if you use interrupts. Since the implication of your post
> is that all other interrupts (bar Timer B) are disabled then using
> interrupts wouldn't cause slow reaction in the case that an interrupt
> was already executing.

That was indeed my first pass, but it just could not track adequate
state and still meet the timings.

> I'd be very interested to know what you are trying to achieve and why
> you feel that polling is necessary, in what seems like a perfect use for
> an isr.

I'm normally an ISR guy (spending many years inside multi-user OSes,
where polling is almost always a huge mistake), but having tried both,
I'm convinced that I need to poll to get the responsiveness I need from
Timer A - it just seems that I also have to give up getting timer B to interrupt
me in a protocol watchdog role because of that. I'll triple check for TAIFG
without TAIE as you suggest.
--
Andy

Hi again

Andy Warner wrote:

>On Wed, Apr 30, 2008 at 8:35 PM, Onestone wrote:
>
>
>> [...]
>> You state that you're polling Timer A for rollovers. A slow event, I
>> assume you need to catch these faster than the interrupt latency allows.
>>
>>Correct. I'm clocking Timer A externally from a fast source, and have a
>divisor of 16, so I can generate a secondary frequency locked to
>TACLK on TA1. TA1 is toggling at 500-900KHz.
>
>
>
>> Why not simply disable the relevant Timer A interrupts? The flags will
>> still get set. In your case you just clear TAIE in TACTL. TAIFG will
>> still become set everytime TAR rolls over, but no interrupt event will
>>
>>That's not the behaviour I thought I observed. When I disabled the various
>interrupts (TAIE, CCIE), I do not believe I saw the interrupt flags set (TAIFG
>and CCIFG respectively) - which made sense, because no interrupt _was_
>pending. Maybe I was wrong, I'll check again, but I could swear that that
>was the exact behaviour I was looking for and did not get.
>
>
Any interrupt flag will set, whether it is enabled or not, if the
relevant event occurs. In fact in some cases they can be set by simply
changing registers. In the case of the TA overflwo interrupt this is
certainly the case.

>
>
>> occur, why poll TAIV? It makes no sense when you could do the same for
>> any individual interrupt. IN fact testing TAIE in speed sensitive
>> application makes more sense since it is bit 0x0002 which makes the src
>> use one of the CG registers, unless you are simply polling TAIV for non
>> zero of course, which does the same, except it will react, ie be non
>> zero for ANY timer event. If no other timer channels are used their IFG
>> bits will ultimately become set.
>>
>>Exactly, I have 1 possible source in TAIV, so I check for non-zero, and it
>self-clears so that saves me an instruction.
>
>
No, you have potentially multiple sources in TAIV, however to actually
set TAIV the specific interrupt MUST be enabled. The ONLY way to make
this test single event sensitive is to disable all other Timer A
interrupts, but even this won't give the reslt I think you want. There
are 3 sources that set TAIV, TACCR1 IFG, TACCR2 IFG AND TAIFG. Check out
the description of the TAIV register operation in the user guide. So to
actually cause TAIV to be set you must have enabled the interrupt
anyway, which will cause it to be processed. This is pointless since you
are trying to avoid an ISR, yet the ONLY way to poll from TAIV is to
actually enable the ISR which will, by definition of how the ISRs are
handled, always see the ISR processed before you can detect it by
polling. You method is seriously flawed as there is ALWAYS a delay to
service the ISR before you poll it. Interrupts are servioced on
completion of the current instruction. So if the event occurs during
the TST &TAIV instruction it can't be recognised by the instruction
so the ISR is handled between that and the conditional that follows it,
but the flag aren't set, so the conditional doesn't actually detect the
ent until the NEXT time it is tested so the delay here is the current
instruction, the ISR the cnditional and then the test and conditional
again. two full loops plus at least 11 clocks for a NULL ISR. If the
instruction occurs while the JZ LOOP instruction is being processed the
ISR will occur after that, and before the test, so there will still be
an ISR delay plus the tst plus the conditional.

test the TAIFG flag, its what its for. You may have used the TAIV
register because it clears whe read, and seems to save you an
instruction, but the reality is that it actually costs you by
significantly slowing down you detect time.

>
>
>> In the 2xx series micros the interrupt latency is given as 6 cycles for
>> the CPU core and 5 for the CPUX core. Compared to the older 1xx cores
>> this would seem to imply that some parallel processing takes place WHILE
>> the current execution is executing, whereas the 1xx cores processed
>> interrupts After the current instruction completed. If this is the case
>> then an interrupt on a 2xx series will generally be as fast as a tight
>> polling loop (the loop takes 6 clocks using CMP Rn,&regname, JZ
>> loopstart) but needs to clear the calling flag after processing, then
>> get back here, so another 6 cycles vs the 5 taken by RETI. Also you
>> can''t use low power in the polling loop, whereas you can waiting for an
>> interrupt.
>>
>>I don't wait long (~1-2uS) for these interrupts, they are continuous while I'm
>in this mode, so I'm not concerned about low power. I am also checking
>for periods when the TACLK source is absent, which means tracking persistent
>values across individual timer ticks, doing that in an ISR involved reading
>memory, which takes too long. I found that the ISR could react to the event
>OK, but could not become aware of context easily. My first implementation
>was interrupt-based, I re-wrote it in assembler to poll the timer registers
>and I hold context in registers across loops.
>
>
So why not do that rather than read memory as you describe above. Hold
the values in reserved registers? You would have to at least rread TAR
or CCRxx to recover the value, but that doesn't change by method

>I also manipulate TACCTL1 to set the OUTMODx bits for TA1 after
>periods, according to a varying pattern, and I found that the ISR latency was
>loose enough that sometimes I would get spikes on TA1 or +/- 1 extra transition.
>
>
That makes no sense. The whole idea of the OUTMOD bit is to set them on
match, ie exactly as the event occurs, automatically, they are not
affected by interrupt latency in this respect, so the little jitter seen
should only be related to the point on the incoming signal where actual
detection occurs and the propagaion delays through the match hardware of
the MSP. but the implication here is that TA1 is also in use for
something, since match won't occur unless something is changing to make
that match.

>> You can still use the dual timer method of checking the Timer A event
>> with Timer B if you use interrupts. Since the implication of your post
>> is that all other interrupts (bar Timer B) are disabled then using
>> interrupts wouldn't cause slow reaction in the case that an interrupt
>> was already executing.
>>
>>That was indeed my first pass, but it just could not track adequate
>state and still meet the timings.
>
>
>
>> I'd be very interested to know what you are trying to achieve and why
>> you feel that polling is necessary, in what seems like a perfect use for
>> an isr.
>>
>>I'm normally an ISR guy (spending many years inside multi-user OSes,
>where polling is almost always a huge mistake), but having tried both,
>I'm convinced that I need to poll to get the responsiveness I need from
>Timer A - it just seems that I also have to give up getting timer B to interrupt
>me in a protocol watchdog role because of that. I'll triple check for TAIFG
>without TAIE as you suggest.
>
>
No, I think you've simply misinterpretted the function of the timers and
their interrupt structures.
If these pulses are occurring every 1-2 usecs, that is 16 or 32 clock
cycles on a 2xx series at full bore, so there isn't much you can do in
the way of processing, given at least 6 cycles delay from detection to
process, with possibly 11 cycles delay (if the event occurs on the first
clock of the TST instruction it will take those 3, plus the 2 for the
conditional plus the next 4 for the test plus the next conditional to
react. so your polling method has a latency of 6 - 11 clocks, that
leaves just 5 -21 clocks to do something useful. You imply that you are
not simply handling one event, but possible several consecutive ones,
hence I assume that you must use a branch to get nback to the loop
start, another 2 clocks, so at 1uS you have 3 clock cycles to do
smethin, at 2us you have 19 clock cycles, but either way you have
significant jitter in any output singla you create based on polling.

Cheers

Al

On Wed, Apr 30, 2008 at 11:20 PM, Onestone wrote:
> Andy Warner wrote:
> [...]
> >and CCIFG respectively) - which made sense, because no interrupt _was_
> >pending. Maybe I was wrong, I'll check again, but I could swear that that
> >was the exact behaviour I was looking for and did not get.

I was wrong - one simple test case later, I can state that TAIFG and CCIFG
get set regardless of the state of TAIE and CCIE respectively. Not sure what
was wrong with the previous code, but the 15 line test case doesn't
lie :)

I appreciate the sanity check that prompted me to go back and check
again.

I now believe that I can poll Timer A as fast as I want (no matter how
fruitless people may think this is) and use Timer B as a timeout in interrupt
mode.

You are correct, that to operate in this mode, I _will_ have to stop using
TAIV, and go back to spinning on TAIFG and manually clearing it.
--
Andy

Hi Andy I know you were wrong :@. Either that or I have to go back and
re-write a whole load of MSP 430 programs!

Andy Warner wrote:

>On Wed, Apr 30, 2008 at 11:20 PM, Onestone wrote:
>
>
>> Andy Warner wrote:
>> [...]
>> >and CCIFG respectively) - which made sense, because no interrupt _was_
>> >pending. Maybe I was wrong, I'll check again, but I could swear that that
>> >was the exact behaviour I was looking for and did not get.
>>
>>I was wrong - one simple test case later, I can state that TAIFG and CCIFG
>get set regardless of the state of TAIE and CCIE respectively. Not sure what
>was wrong with the previous code, but the 15 line test case doesn't
>lie :)
>
>I appreciate the sanity check that prompted me to go back and check
>again.
>
>
Always welcome.

>I now believe that I can poll Timer A as fast as I want (no matter how
>fruitless people may think this is) and use Timer B as a timeout in interrupt
>mode.
>
>
>
I didn't say it was fruitless. I don't know what you want to do with the
event once trapped, or what you think you want to do with it! ( A subtle
but important difference. Often a new perspective on a problem might
lead to a previously unthought of solution). therefore it is impossible
for me to assess the best method to use. My comment was simply that with
a 2xx processor your interrupt service latency is not really very
different to your polling overhead, and any signals set on the event
automatically will be more jitter free than settng them manually...

>You are correct, that to operate in this mode, I _will_ have to stop using
>TAIV, and go back to spinning on TAIFG and manually clearing it.
>
>
...Especially in light of this. If you need to act on every event the
act of clearing the interrupt flag will consume another 4 cycles. hence
your detect processing loop now requires from 12 to 17 clock cycles. Too
slow to guarantee detecting a 1usec pulse.

If, instead of using the rollover of the counter and the global overflow
interrupt you instead used Timer A0 then you get the same loop
control/re-entry for just 11 clock cycles, plus any automated setting of
TA0 output pins on match. This is faster than using TAIFG because TAIFG
uses a shared interupt vector and doesn't clear automatically when the
interrupt is serviced, so you would have to explicitly clear it, as you
do in the polling process. But by setting TA0 to interrupt on a compare
match of 0000, or any other value for that matter, but keeping the value
the same, you get the same exact interval between the compares as you
would roll overs with the added advantage that TA0 interrupt is a single
source interrupt, therefore the IFG flag is cleared on entry to the ISR.
thus your detection overhead is 6 clocks for the ISR and 5 for the RETI.
The rest remains the same as it would for the polling method, saving 1
to 6 clocks every time.

Cheers

Al


Memfault Beyond the Launch