EmbeddedRelated.com
Forums

MSP430F149 Interrupt Handling

Started by soon yan bo February 2, 2008
Hi Everyone,

I have programmed the MSP430F149 to toggle the p1.0 pin every 1000ns
using a 12.288 mhz crystal clock connect to XT2IN. The ISR will wake the
CPU up every 1000ns, after toggling, it will put it to sleep. However,
the code does not entered the interrupt routine and IAR cannot compile
when include the __interrupt [TimerA Vector] void Timer_A(void).

Attached is the code. Will really appreciate any help. =)

Regards,
Yb
#include
int i = 0;

void main(void)
{

WDTCTL = WDTPW + WDTHOLD; // Stop watchdog
P1DIR |= 0x01;
P1OUT = 0x00;

BCSCTL1 &= ~XT2OFF; // XT2= HF XTAL switch master
clock to XT2IN

do
{
IFG1 &= ~OFIFG; // Clear OSCFault flag
for ( x = 0xFF; x > 0; x--); // Time for flag to set
}

while ((IFG1 & OFIFG)); // OSCFault flag
still set?

BCSCTL2 |= SELM_2+SELS; // MCLK= XT2 (safe) &
SMCLK = XT2 as well

TACTL = TASSEL1+TACLR+MC1; // Timer init using
SMCLK, Clear counter and set timer to continous mode
CCR0 = 12; // 1s which is 12288000
ticks using a 12.288MHz crystal
CCTL0 = CCIE; // Enables CCR

for(;;)
{

_BIS_SR(LPM0_bits + GIE); // Enter LPM0 w/
interrupt so CPU sleep and wait till 1s
P1OUT ^= 0x01;
}
}
__interrupt void Timer_A(void);

//TIMERA1_ISR(Timer_A)
__interrupt void Timer_A(void)
{
LPM0_EXIT;
}

Beginning Microcontrollers with the MSP430

Timer A has 2 vectors, A0 & A1.
A0 is for CCR0 and A1 is for CCR1..CCRX.
Do you have the right vector ? I'm not familiar anymore with the IAR intrinsics, but
I think your handler should be TIMERA0_VECTOR or some such.

Best Regards,
Kris

-----Original Message-----
From: m... [mailto:m...] On Behalf Of soon yan bo
Sent: Saturday, 2 February 2008 8:32 PM
To: m...
Subject: [msp430] MSP430F149 Interrupt Handling

Hi Everyone,

I have programmed the MSP430F149 to toggle the p1.0 pin every 1000ns
using a 12.288 mhz crystal clock connect to XT2IN. The ISR will wake the
CPU up every 1000ns, after toggling, it will put it to sleep. However,
the code does not entered the interrupt routine and IAR cannot compile
when include the __interrupt [TimerA Vector] void Timer_A(void).

Attached is the code. Will really appreciate any help. =)

Regards,
Yb
#include
int i = 0;

void main(void)
{

WDTCTL = WDTPW + WDTHOLD; // Stop watchdog
P1DIR |= 0x01;
P1OUT = 0x00;

BCSCTL1 &= ~XT2OFF; // XT2= HF XTAL switch master
clock to XT2IN

do
{
IFG1 &= ~OFIFG; // Clear OSCFault flag
for ( x = 0xFF; x > 0; x--); // Time for flag to set
}

while ((IFG1 & OFIFG)); // OSCFault flag
still set?

BCSCTL2 |= SELM_2+SELS; // MCLK= XT2 (safe) &
SMCLK = XT2 as well

TACTL = TASSEL1+TACLR+MC1; // Timer init using
SMCLK, Clear counter and set timer to continous mode
CCR0 = 12; // 1s which is 12288000
ticks using a 12.288MHz crystal
CCTL0 = CCIE; // Enables CCR

for(;;)
{

_BIS_SR(LPM0_bits + GIE); // Enter LPM0 w/
interrupt so CPU sleep and wait till 1s
P1OUT ^= 0x01;
}
}
__interrupt void Timer_A(void);

//TIMERA1_ISR(Timer_A)
__interrupt void Timer_A(void)
{
LPM0_EXIT;
}
Good morning (or evening),

Here is what I use for timerA ISRs in IAR:

#pragma vector= TIMERA0_VECTOR
__interrupt void Timer_A0(void)
{
//your code here
}

#pragma vector= TIMERA1_VECTOR
__interrupt void Timer_A1(void)
{
// your code here
}

Also, I'm curious to know how well the app you have there will really work. 12.288MHz is
well beyond the spec of the MSP430. Also, consider that you are loading the CCR0
register with a value of 12. Given that the processor takes 6 clock cycles to go into an ISR
and 5 to get out I can't see how the 'F149 will have time to complete the toggling of the
pin before the next interrupt wants to fire.

Robert

--- In m..., soon yan bo wrote:
>
> Hi Everyone,
>
> I have programmed the MSP430F149 to toggle the p1.0 pin every 1000ns
> using a 12.288 mhz crystal clock connect to XT2IN. The ISR will wake the
> CPU up every 1000ns, after toggling, it will put it to sleep. However,
> the code does not entered the interrupt routine and IAR cannot compile
> when include the __interrupt [TimerA Vector] void Timer_A(void).
>
> Attached is the code. Will really appreciate any help. =)
>
> Regards,
> Yb
> #include
> int i = 0;
>
> void main(void)
> {
>
> WDTCTL = WDTPW + WDTHOLD; // Stop watchdog
> P1DIR |= 0x01;
> P1OUT = 0x00;
>
> BCSCTL1 &= ~XT2OFF; // XT2= HF XTAL switch master
> clock to XT2IN
>
> do
> {
> IFG1 &= ~OFIFG; // Clear OSCFault flag
> for ( x = 0xFF; x > 0; x--); // Time for flag to set
> }
>
> while ((IFG1 & OFIFG)); // OSCFault flag
> still set?
>
> BCSCTL2 |= SELM_2+SELS; // MCLK= XT2 (safe) &
> SMCLK = XT2 as well
>
> TACTL = TASSEL1+TACLR+MC1; // Timer init using
> SMCLK, Clear counter and set timer to continous mode
> CCR0 = 12; // 1s which is 12288000
> ticks using a 12.288MHz crystal
> CCTL0 = CCIE; // Enables CCR
>
> for(;;)
> {
>
> _BIS_SR(LPM0_bits + GIE); // Enter LPM0 w/
> interrupt so CPU sleep and wait till 1s
> P1OUT ^= 0x01;
> }
> }
> __interrupt void Timer_A(void);
>
> //TIMERA1_ISR(Timer_A)
> __interrupt void Timer_A(void)
> {
> LPM0_EXIT;
> }
>
Robert is absolutely right. The F149 specification says it runs at
8MHz max. (And only when Vcc is 3.6V) Using 12.288MHz might work, but
you are pressing your luck.

He is also right about the cycles. You probably end up handling one
interrupt and missing the next; handling another one and missing the
next; etc. P1.0 thus toggles every ~2000ns instead of ~1000ns.

By the way, if you just want one of the port pins to toggle, there are
many other ways to do it. For example:
(a) Some of the port pins can be set up to output one of the clocks
directly.
(b) The CPU can flip any port pin in a loop.
(c) The Compare & Out circuit of Timer can be set up to flip some of
the port pins.
All the above are simpler than the method you try to do, and can do it
much faster. Method (c) is very flexible while (a) and (b) are very
restricted.

--- "Robert" wrote:
>
> Good morning (or evening),
>
> Here is what I use for timerA ISRs in IAR:
>
> #pragma vector= TIMERA0_VECTOR
> __interrupt void Timer_A0(void)
> {
> //your code here
> }
>
> #pragma vector= TIMERA1_VECTOR
> __interrupt void Timer_A1(void)
> {
> // your code here
> }
>
> Also, I'm curious to know how well the app you have there
> will really work. 12.288MHz is well beyond the spec of the
> MSP430. Also, consider that you are loading the CCR0
> register with a value of 12. Given that the processor
> takes 6 clock cycles to go into an ISR and 5 to get out I
> can't see how the 'F149 will have time to complete the
> toggling of the pin before the next interrupt wants to fire.
>
> Robert
>
> --- soon yan bo wrote:
> >
> > Hi Everyone,
> >
> > I have programmed the MSP430F149 to toggle the p1.0 pin
> > every 1000ns using a 12.288 mhz crystal clock connect to
> > XT2IN. The ISR will wake the CPU up every 1000ns, after
> > toggling, it will put it to sleep. However, the code does
> > not entered the interrupt routine and IAR cannot compile
> > when include the __interrupt [TimerA Vector] void
> > Timer_A(void).
> >
> > Attached is the code. Will really appreciate any help. =)
> >
> > Regards,
> > Yb
> >
> > #include
> >
> > int i = 0;
> >
> > void main(void)
> > {
> >
> > WDTCTL = WDTPW + WDTHOLD;
> > P1DIR |= 0x01;
> > P1OUT = 0x00;
> >
> > BCSCTL1 &= ~XT2OFF;
> >
> > do
> > {
> > IFG1 &= ~OFIFG;
> > for ( x = 0xFF; x > 0; x--);
> > }
> >
> > while ((IFG1 & OFIFG));
> >
> > BCSCTL2 |= SELM_2+SELS;
> >
> > TACTL = TASSEL1+TACLR+MC1;
> > CCR0 = 12;
> > CCTL0 = CCIE;
> > for(;;)
> > {
> > _BIS_SR(LPM0_bits + GIE);
> > P1OUT ^= 0x01;
> > }
> > }
> > __interrupt void Timer_A(void);
> >
> > //TIMERA1_ISR(Timer_A)
> > __interrupt void Timer_A(void)
> > {
> > LPM0_EXIT;
> > }
>
> I have programmed the MSP430F149 to toggle the p1.0 pin every 1000ns
> using a 12.288 mhz crystal clock connect to XT2IN. The ISR will wake the
> CPU up every 1000ns after toggling, it will put it to sleep. However,
> the code does not entered the interrupt routine and IAR cannot compile
> when include the __interrupt [TimerA Vector] void Timer_A(void).

I don't see a definition for 'x' in the code. But I'm sure you must have
merely missed including it, because I'm pretty sure it wouldn't compile
without one and you'd know it.

Have you considered this scenario?

(1) You enter LPM0 in your main for loop, just before the LED XOR.
(2) CPU is now sitting on, but not executing yet, the XOR.
(3) Interrupt takes place, saving the current LPM0 status on the
stack, changing the mode, and entering the interrupt.
(4) Your interrupt routine modifies the saved stack status to
LPM0, but of course it already is that way so there is no
real change caused by that.
(5) Interrupt routine returns, causing the CPU to enter LPM0 and
thus sit once again waiting on, but not yet executing that
XOR.
(6) Another interrupt takes place later on, saving again the LPM0
mode, etc., and getting to the interrupt code, and we go back
to step (4).

The result is that you never ever get to the XOR.

Jon
Jon,

I think you may have been misled by LPM0_EXIT when you said: "(4) Your
interrupt routine modifies the saved stack status to LPM0, but of
course it already is that way so there is no real change caused by that."

What do you think is the resulting x after the statement:
x = ADD_ONE_TO(2);

where:
int ADD_ONE_TO(int i) {return (i-1);}
> I think you may have been misled by LPM0_EXIT when you said: "(4) Your
> interrupt routine modifies the saved stack status to LPM0, but of
> course it already is that way so there is no real change caused by that."

That could be. Thanks for bringing my attention here. I don't use
LPM0_EXIT, at all, so I just looked it up on the web and noted that it
appears to modify the saved stack status and went with that.

Regardless (because it doesn't matter to what I was pointing out), I think
the rest of the logic holds, though. Or?

> What do you think is the resulting x after the statement:
> x = ADD_ONE_TO(2);
>
> where:
> int ADD_ONE_TO(int i) {return (i-1);}

Since I'm on shaky ground regarding my knowledge of LPM0_EXIT, not using
it myself, I'll hold short of trying to interpret your question in that
context. I'm more likely than not of getting myself into trouble doing
that.

The main point I had wanted to get across, and I don't think LPM0_EXIT
affects the point, is that the design appears flawed at the outset. The
fact that there is no toggling going on is more an issue of approach, I've
think.

In the past, I've done the toggle (xor) in the interrupt function itself.
The main code (assuming the simple case here of just toggling an LED) can
just go into LPM0 mode and sit a "return 0;" statement, if it wanted to.
The return would never actually get to execute, the interrupt would
operate just fine and toggle the LED and no modification of the stack
would be required since the LPM0 was properly saved there when the
interrupt took place. I've done it that way, in fact, with some
educational programs for a 1st year class in programming at a local
college here, using the MSP430. Worked very easy and was easy to explain,
too. (Can provide source, if you want to see the 10 project series I
developed for them.)

Jon
I do not use LPM0_EXIT either. For that matter, I do not use c period.
I dislike layers and layers of syntax and semantics wrapped around
lousy ideas or algorithms. For example:

for(;;)
{
_BIS_SR(LPM0_bits + GIE);
P1OUT ^= 0x01;
}

#pragma vector= TIMERA0_VECTOR
__interrupt void Timer_A0(void)
{
LPM0_EXIT;
}


I totally agree with you that this is a lousy way to toggle P1.0
output. But I think it does toggle P1.0 output periodically (if
Timer_A0 is set up correctly).

I use (IAR) KickStart to compile the above fragment of c code and
examined the object code generated. I found that LPM0_EXIT actually
***clears*** the LPM0 bits of the saved SR in the stack.

--- In m..., jonk@... wrote:
>
> > I think you may have been misled by LPM0_EXIT when you said:
> > "(4) Your interrupt routine modifies the saved stack status
> > to LPM0, but of course it already is that way so there is no
> > real change caused by that."
>
> That could be. Thanks for bringing my attention here. I don't
> use LPM0_EXIT, at all, so I just looked it up on the web and
> noted that it appears to modify the saved stack status and went
> with that.
>
> Regardless (because it doesn't matter to what I was pointing out),
> I think the rest of the logic holds, though. Or?
>
> > What do you think is the resulting x after the statement:
> > x = ADD_ONE_TO(2);
> >
> > where:
> > int ADD_ONE_TO(int i) {return (i-1);}
>
> Since I'm on shaky ground regarding my knowledge of LPM0_EXIT,
> not using it myself, I'll hold short of trying to interpret
> your question in that context. I'm more likely than not of
> getting myself into trouble doing that.
>
> The main point I had wanted to get across, and I don't think
> LPM0_EXIT affects the point, is that the design appears flawed
> at the outset. The fact that there is no toggling going on is
> more an issue of approach, I've think.
>
> In the past, I've done the toggle (xor) in the interrupt
> function itself. The main code (assuming the simple case here
> of just toggling an LED) can just go into LPM0 mode and sit a
> "return 0;" statement, if it wanted to. The return would never
> actually get to execute, the interrupt would operate just fine
> and toggle the LED and no modification of the stack would be
> required since the LPM0 was properly saved there when the
> interrupt took place. I've done it that way, in fact, with
> some educational programs for a 1st year class in programming
> at a local college here, using the MSP430. Worked very easy
> and was easy to explain, too. (Can provide source, if you want
> to see the 10 project series I developed for them.)
>
> Jon
>



> I do not use LPM0_EXIT either. For that matter, I do not use c period.

Just to make my position clear, I try to do what is in the client's better
interest, generally. Often, that will include c for many cpus, but
usually also with some assembly included. In some cases, assembly only is
called for. In the MSP430 case, I've only used the cpu for educational
and small scale personal purposes so I haven't paid for a commercial
license to use a c compiler for the chip, as yet. If that changes and a
customer's requirements included c as a better way to go, I'd go buy a
license, as appropriate for them. However, just so you know my use so
far, it has entirely been with IAR's free version for educational use and
for personal use has included both IAR's Kickstart and Imagecraft's ICC
(with permission.) I very much, as a personal matter, enjoy working with
either assembly or c or both. Just depends on what I'm doing.

> I dislike layers and layers of syntax and semantics wrapped around
> lousy ideas or algorithms. For example:
>
> for(;;)
> {
> _BIS_SR(LPM0_bits + GIE);
> P1OUT ^= 0x01;
> }
>
> #pragma vector= TIMERA0_VECTOR
> __interrupt void Timer_A0(void)
> {
> LPM0_EXIT;
> }
>

Yeah. That is basically trivial assembly, which is clear to read, cast
into obscure and specialized coding that only makes complete sense if you
go and dig things up, which is actually more work to me. In fact, as I
already admitted, I had to google LPM0_EXIT to even get a clue about it
(the IAR help didn't find it for me.) With assembly, everything would
have been clearer and not more wordy.

> I totally agree with you that this is a lousy way to toggle P1.0
> output. But I think it does toggle P1.0 output periodically (if
> Timer_A0 is set up correctly).

I didn't think so, though, given my perhaps wrong assumption about what
that macro did. And I'll point out the reason, again, just after your
next comment.

> I use (IAR) KickStart to compile the above segment of c code and
> examined the object code generated. I found that LPM0_EXIT actually
> ***clears*** the LPM0 bits of the saved SR in the stack.

I take it that you mean that SCG1 and SCG0 and OSCOFF are 0? But isn't
that exactly what it takes to get INTO LPM0 mode?? If so, the return from
interrupt will do what I feared and pointed out, earlier.

Not so?

Jon
Let me add a comment to what I just wrote, namely:

> I take it that you mean that SCG1 and SCG0 and OSCOFF are 0? But isn't
> that exactly what it takes to get INTO LPM0 mode?? If so, the return
> from interrupt will do what I feared and pointed out, earlier.

Also, CPUOFF is '1', I should have added. It's been my experience with
LPM0 that it disables the CPU. Isn't that right?

Jon