Go Down

Topic: While loop not behaving as expected (Read 4679 times) previous topic - next topic

Teacup

Apr 11, 2012, 12:08 pm Last Edit: Apr 11, 2012, 12:12 pm by AWOL Reason: 1
Hello,

So I was just playing around with timer driven interupts and a basic error handler.  Everything behaved as expected untill I added a while loop.  So the program that behaved included a timer driven interupt calling a function for three seconds when the function would run the errorhandler that stopped it.  The code is bellow:
Code: [Select]
void errorHandler(char errorMsg[])
{
 Timer1.stop();
 pinMode(13, OUTPUT);
 digitalWrite(13,HIGH);
 Serial.println(errorMsg);
}

void interuptServoUpdate()
{
 Serial.print("Time: ");
 Serial.println(millis());
 if(millis()>3000) errorHandler("No error: just joshing");
}


void setup()
{
 leg.attachServo(1,2);
 leg.attachServo(2,3);
 
 Timer1.initialize(1000000);
 Timer1.attachInterrupt(interuptServoUpdate);
 
 Serial.begin(9600);
}


This worked correctly, and produced the following serial output:
Quote
Time: 996
Time: 1996
Time: 2996
Time: 3996
No error: just joshing


But when I added an infinate while loop into the error handler I wouldn't get the final message from the error handler.  Bellow is the code:
Code: [Select]
void errorHandler(char errorMsg[])
{
 Timer1.stop();
 digitalWrite(13,HIGH);
 while(1)
 {
   Serial.println(errorMsg);
   digitalWrite(13, HIGH);
   delay(500);
   digitalWrite(13, LOW);
   delay(500);
 }    
}

void interuptServoUpdate()
{
 Serial.print("Time: ");
 Serial.println(millis());
 if(millis()>3000) errorHandler("No error: just joshing");
}


void setup()
{
 leg.attachServo(1,2);
 leg.attachServo(2,3);
 
 Timer1.initialize(1000000);
 Timer1.attachInterrupt(interuptServoUpdate);
 
 Serial.begin(9600);
}


And the serial signal:
Quote
Time: 996
Time: 1996
Time: 2996


Moderator edit: Quote tags replaced with code tags

AWOL

You know it's a really bad idea to do serial output in an interrupt, don't you?

PaulS

Why not print the message first, then do the infinite loop? I don't think you want to be continuously printing the message.

Interrupt handlers are supposed to be fast. An infinite loop is not within anyone's definition of fast.

Teacup

Hey Guys,

thanks for the input.  I am still learning and self teaching so I think there are a lot of standard practices that I am missing out on having not learnt in uni.

AWOL:
This interupt was not intended to do serial output in the end.  It's intended to to servo position updating for path following.  I was just doing serial output to test out that the interrupt was working as I expected.  However, no, i didn't know that it's a bad idea.  What is the reason behind it?

PaulS
I was wanting to infinatly print the msg for when I eventually disable the auto reset due to serial connections.  I was wanting to just connect the Serial in the event of an error (see the light flashing) to find out the exacts.  Can you suggest a better way?

However, whether it is the better way or not, I was still curious as to why the while loop was unable to blink the LED or print the error msg as expected.  I kinda felt it was a gap in my knowledge, one which I was unable o figure out by googling.  Any ideas?


Thanks a heap for your input btw, i am trying to learn fast and these forums and all the people on them have been great.

Cheers,
David

PaulS

Quote
It's intended to to servo position updating for path following.

As opposed to using the Servo library? Why?

Teacup

PaulS

My project is to develope a walker.  This walker will eventually consist of four legs, each controlled by three servo's.  Currently I am just working on one leg in 2D (just two of three servo's).  I will make it more complex slowly as I get each step working properly as I expect.

I do use the servo library to do the controlling of the servo's.  Example bellow, this is a function I wrote to just throw a bit of sa:
Quote
void TwoDLeg::setS1(int inputS1)
{
  if (inputS1 > s1UpperLimit) inputS1 = s1UpperLimit;
  if (inputS1 < s1LowerLimit) inputS1 = s1LowerLimit;
  s1 = inputS1;
  servo1.write(180-s1);
}


I then progressed this and created a function that took intended coordinates,figured out the required servo positions and set them.

I am now working on a set of functions that will take a linear path from A to B over an aloted time and guide the foot along that path.  To do this I not only need the servo's signal repeating via the servo library but I also need the servo position to be repeatedly recalculated by a repeating function.

My thinking for doing this was to have a recalulating function called at roughly 20hz by  a software interupt.

Does this make sence?  Does it seem like the best way of doing this?

My end effector code is bellow:
Quote
void TwoDLeg::setEEPos(int y, int z)
{
   double cSQRE = square(y) + square(z);
   double cosC = (aSQRE + bSQRE - cSQRE)/(2*d2*d3);
   double C = acos(cosC);

   double theta = atan2(y,z);
   double c = sqrt(cSQRE);
   double cosB = (cSQRE + aSQRE - bSQRE)/(2*c*d2);
   double B = acos(cosB);

   theta = theta * (360/(2*M_PI));
   B = B * (360/(2*M_PI));
   C = C * (360/(2*3.14));

   int uncheckedS1 = round(theta + B);
   int uncheckedS2 = round(C-90);

   // Need a safety check in here to check position is obtainabl

   int s1 = uncheckedS1;
   int s2 = uncheckedS2;

   // Also deal with negative direction

   setS1(s1);
   setS2(s2);
}

srinathdevelopment

#6
Apr 11, 2012, 04:02 pm Last Edit: Apr 11, 2012, 04:06 pm by srinathdevelopment Reason: 1
Never have more than a couple of lines of code in any interrupt handler because another interrupt can always interrupt your interrupt handler and while loops here hijack the processor

eg.micros() depends on a timer0 interrupt with a resolution of 4us - so it is running  250000 times a second . Your serial communication function is much slower...  and you are adding another loop  you get the idea  what is happening .

the right way to handle an interrupt is to set some global flag like variable in the interrupt handler and exit immediately

in your main loop, examine the value of the variable every cycle and do your serial.whatever()  from here. In this case , the processor will keep track of  your code even when timer0 occurs and  your code will execute properly

AWOL

Quote
eg.micros() depends on a timer0 interrupt with a resolution of 4us - so it is running  250000 times a second

Not sure what you're saying there, but there is no regular interrupt that runs at anywhere near that rate.

srinathdevelopment

#8
Apr 11, 2012, 04:34 pm Last Edit: Apr 11, 2012, 04:45 pm by srinathdevelopment Reason: 1
Ah... you're right... my bad...the millis() and micros() depends on an interrupt handler that catches timer0 overflow and it IS much slower... only about 976 times a second with 64  prescalar.

Most of the other observations are still valid though :-)

Teacup

srinathdevelopment
Ok, so I was going to use an interrupt to change the variables every 50 ms.  However, what your saying is that every interrupt I add increases the chance that another interrupt will fail to execute.  That is correct?

So, you were saying that I should update the variables in the loop function and just set a global flag variable that the seperate interrupt should then update them for the Servo function to use?

If that is so, would it be a better option yet to just run the position update every loop and update the servo positions directly from there?  Reasons being:
1) it is one less interrupt running
2) I will get a more accurate update of the Servo positions as there will always be a lag between when the positions are recalculated and when they are updated by the timer interupt for use by the Servo library.

Does this sound right?

Cheers,
David

P.S. I am still not sure why my original while loop did not work (see first post in this topic)?

cmiyc


P.S. I am still not sure why my original while loop did not work (see first post in this topic)?


Printing must not actually occur until you exit the interrupt routine.  That can't happen if the ISR is stuck in an infinite loop.
Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

WizenedEE



P.S. I am still not sure why my original while loop did not work (see first post in this topic)?


Printing must not actually occur until you exit the interrupt routine.  That can't happen if the ISR is stuck in an infinite loop.


The Serial.print commands just queue up the messages, and they're printed via ISR. The easy not-actually-fixing-the-problem solution is to call the interrupts() (or sei()) function right before going into the infinite loop. Having an infinite loop in an ISR doesn't hurt the processor or anything if it's going to be reset anyway.

srinathdevelopment

Regarding your bigger question on do you need to use interrupts at all, i've found that it's usually necessary to use interrupts only in these circumstances -

1. You have a highly time critical response need (within microseconds)
In this case, you mentioned somewhere you're looking at 50ms or so timing - so this may not be critical

2. Your loop function spends much time doing something else & cannot monitor whatever you want with adequate frequency
Recall that the Arduino Uno processor is running at 16 Million processor instructions per second - so even if 10 lines of C code translate into 500 processor instructions on compilation (loops add more of course  :)), this has still taken only 32 microseconds

3. You are depending on some micro-processor peripheral which works on interrupts
In my experience on ATMEGA peripherals like TWI & Timers, there are usually options to avoid interrupts which product blocking but simpler code.

If you don't have these needs, it's likely that simply doing what you want in the loop function is sufficient and will simplify your code.

PS - would be great if others can provide more areas in which using interrupts has benefits, since i have relatively limited experience on interrupt programming, it's likely i'm missing some circumstances :)

Teacup

Heya Guys,

JamesC4S & WizenedEE

I understand that the while loop prevents the continuation of the usual programming loop.  However I would have thought that interupts would still be enabled during the while loop and hence they would be able to run.  Why does a while loop disable the trigering of ISR's?  Sorry, so many questions.

Srinathdevelopment

Thank you, that is very helpful.  I am fairly satisfied that an interupt is not the best way to go about my program.  It is good to hear your criteria.

Thanks for the help guys,
David

nickgammon


Never have more than a couple of lines of code in any interrupt handler because another interrupt can always interrupt your interrupt handler ...


No it can't. Interrupt handlers turn interrupts off. Otherwise that would happen. For example, the timer interrupts that increments millis() might happen to be interrupted by incoming serial data.

However you are right that interrupt handlers should be short, because the next interrupt is now pending until the first one is finished. If it is pending too long subsequent ones may be missed (eg. incoming serial bytes).

Quote
Why does a while loop disable the trigering of ISR's?


It doesn't. However you are calling a function from an ISR, and as I said above, interrupts are disabled inside ISRs.

Serial comms on version 1.0 of the IDE is (are?) done by an interrupt handler, and these are disabled during your while loop (purely because it is called from an interrupt handler).
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

Go Up