breaking out of the loop using interrupts

04 Feb 2011

Hi

I'm using a push-button to control when to enter a function.The function as shown below depends on the value of the index variable.I have set the attached interrupt function so that the index is set to 0 whenever the push-button is pressed.

Unless I am completely mistaken I thought that interrupts operated asynchronously in relation to the main program flow, so I was expecting that when pressed the function would return, but this is not what I'm seeing. The variable keeps incrementing and everything is blocked.

void update(){
  	
  refresh();
  printf("Loading");

	while(1){
	printf("%d",i++);
  
  	if(index==1) break; 
	}	    	 
}

What I want is to update the index variable asynchronously when I push the button. Is there a way to do this using interrupts? (I don't want to use a ticker in this case)

04 Feb 2011

You can use InterruptIn to do this. However, if using a push button you'll probably get a lot of bounce. In which case try using PinDect which does the debounce for you.

#include "mbed.h"
#include "PinDetect.h"

// Connect push-button between Pin 20 and Ground/0v
PinDetect  pin (p20);

DigitalOut led1(LED1);
DigitalOut led2(LED2);

void p21Pressed(void) {
  led2 = 0;
}

void p21Released(void) {
  led2 = 1;
}

int main() {
    pin.mode(PullUp);
    pin.attach_asserted(&p21Pressed);
    pin.attach_deasserted(&p21Released);
    pin.setSampleFrequency();

    while(1) {
        led1 = !led1;
        wait(0.2);
    }
}

Note, in the above example we set the input with the internal pullup resistor. So when nothing is connected to the pin (p20) it reads as 1. The push button connects it to ground changing it to 0.

Import libraryPinDetect

InterruptIn style DigitalIn debounced with callbacks for pin state change and pin state hold.

04 Feb 2011

I didn't have a problem with bounce actually. The thing that confuses me is that the index variable is not being updated when I press the button. This works fine when the function is simple (i.e without a loop inside) but once I use a loop inside the function to update a variable the associated interrupt function doesn't work. I find this really confusing.

void execute(){

  if (root == 0){
	root=1;
	  switch(index){
  
  case 2:       data();  break; 
  case 3:	calls(); break; 
  case 4:	time(); break;
  case 5:	update(); break; 
  default:  break;
	  	}
  }else {
	root = 0;		
   }	 
}

the first 3 functions just print a variable and work fine (meaning I can return from them). The last one has a loop inside which messes everything up.

04 Feb 2011

try declaring index as volatile

volatile int index;

Also, show all you code, snippets like that don't help alot to see what's going on. If your program is big then try reducing the problem to a small program that demonstrates the problem.

04 Feb 2011

hee it is, I ended up using the root variable insteda of index and still the same behaviour. the gist of it is that the root variable is not updated when i press the button.

#include "mbed.h"

volatile int index = 2;
volatile int root;

int i=0;
void update(){
  	
  refresh();
  printf("Loading");

	while(1){
	printf("%d",i++);
  
  	if(root==1) break; 
	}	    	 
}

void data(){
printf("Prints data packet");

}

void calls(){
printf("Print calls");
}


void time(){
printf("Current time");
}



void execute(){

  if (root == 0){
	root=1;
	  switch(index){
  
  case 2:       data();  break; 
  case 3:	calls(); break; 
  case 4:	time(); break;
  case 5:	update(); break; 
  default:  break;
	  	}
  }else {
	root = 0;
	Functions();		
   }	 
}



int main() {  
   pbone.rise(&execute);
   Functions();
	
   while(1){


   }

}	
04 Feb 2011

Is that all of it? volatile int index = 2; and then never changes. What changes index? what does Functions() do?

Also, it's better to try and put "all your stuff" in main()'s while(1) loop. Use interrupt callbacks to set flags that change what happens in the while(1) loop. printf() and other slow IO is notorious for breaking things when used inside callbacks.

04 Feb 2011

Functions just prints all the function names on the terminal. Another push-button is used just to increment index. As I said I switched to using the variable root as a flag. This program was just a proof of concept to see if I can navigate of a function while it is executing. The thing that confuses me is the inability of the interrupt routine to execute while the loop is running.

04 Feb 2011

Quote:

Another push-button is used just to increment index.

As I said, you need to publish all of it to determine whats going go or a cut down/reduced version that demos the problem that I or others can load onto an Mbed and try. It's hard to debug something when 100% isn't there to see.

04 Feb 2011

If I understand your program correctly, one pushbutton causes an interrupt that calls execute(), which in turn calls update(). So update() runs in an interrupt service context.

Then update() spins in a loop incrementing i and printing it. It cannot break out of the loop until root changes. But main can't change it until you relinquish control by exiting your chain of calls that the interrupt triggered.

And another interrupt can't interrupt your current interrupt service routine until, again, you relinquish control. So you are pretty much stuck...

As Andy rightly says

Quote:

it's better to try and put "all your stuff" in main()'s while(1) loop. Use interrupt callbacks to set flags that change what happens in the while(1) loop.

In other words, keeping the callback code to an absolute minimum avoids lots of headaches :-)

04 Feb 2011

#include "mbed.h"

volatile int index = 2;
volatile int root;

int i=0;
void update(){
  	
  refresh();
  printf("Loading");

	while(1){
	printf("%d",i++);
  
  	if(root== 0) break; //check if button's pressed
	}	    	 
}

void data(){
printf("Prints data packet");

}

void calls(){
printf("Print calls");
}


void time(){
printf("Current time");
}



void execute(){

  if (root == 0){
	root=1;
	  switch(index){
  
  case 2:       data();  break; 
  case 3:	calls(); break; 
  case 4:	time(); break;
  case 5:	update(); break; 
  default:  break;
	  	}
  }else {
	root = 0;
	Functions();		
   }	 
}

void selectChoice(){
  d = 0;
  index++;
  led =1;
  wait(0.25);
  led =0;

if(index > 5){
  index=2;
}
}

void Functions(){
 printf("data\n\r");
 printf("calls\n\r");
 printf("time\n\r");
 printf("update\n\r");
}

int main() {  
   pbone.rise(&execute);
   pbtwo.rise(&selectChoice);

   Functions();
	
   while(1){


   }

}

This is the stripped down version. I was using a lcd to output the results so first I suspected that the print function was to blame , but now I see that the same behavior is displayed even on the serial terminal. The problem is that the interrupt does not update the flag variable root.

I think I'll use a ticker but in this case it's just out of curiosity since this seems such a simple case. Unless something is blocking the interrupt.

04 Feb 2011

I tried to cut'n'paste your code into a fresh main.cpp but it fails to compile. Can you just not publish your program and provide us with a link to it. I want to run it here on an Mbed not try to debug it from your written ideas on how it should work but doesn't :)

04 Feb 2011

Quote:

But main can't change it until you relinquish control by exiting your chain of calls that the interrupt triggered.

I don't understand this. Isn't the interrupt done when it calls the function. I was under the impression that once the function is called the interrupt finishes executing. (I'm still a newbie when it comes to these things so pardon me if this sounds stupid)

The way I envisioned it is that when i press the push button again the root variable will flip and the flag will terminate the function returning to the menu.

04 Feb 2011

No, the callback is made during the interrupt handler. So you are still "inside" teh interrupt. Unless you raise the priority of the other interrupt then it won't execute until this one completes. Deadlock ensues if you are relying on another interrupt to break a loop running inside another interrupt.

[Edit/add: that's why it's always best to try and run your program in main()'s while(1) loop. Use ISR callbacks to set flags that you main loop detects and actions as required. Keep callbacks short and sweet, don't have them doing "heavy lifting" work, esp don't printf() in them!]

04 Feb 2011

here's the program

http://mbed.org/users/DimiterK/programs/main/lm2xjo

it relies on a graphic lcd so unless you have the part I don't think you can test it.

I've never encountered this before so i don't really understand it well , but is there any way to circumvent this. i.e how do I raise the priority of the interrupt . Never so anything like this in the forum.

04 Feb 2011

Just looking now. Why are you decalring code in Functions.h ? headers are for defining not declaring. Anyway, let me have a prod about and I'll come back to you.

04 Feb 2011

ok, now we can see the program in full context. So this code has real meaning:-

int main() {  
   pbone.rise(&execute);
   pbtwo.rise(&selectChoice);

   menu();
    
   while(1){  
   }       
}       

Your entire program is driven by interrupts! This really isn't the way to go about doing this. As said before, while one InterruptIn callback is executing, any other InterruptIns that activate will NOT execute until the first one returns. Your stuck in interrupt deadlock. All InterruptIns use the same IRQ so they all get the same priority. You cannot raise one above another.

You really need to redesign this keeping your program running in "user context" and do as little as possible in "Interrupt context". For example:-

#include "mbed.h"

InterruptIn pbone(p7);
InterruptIn pbtwo(p8);

bool pbonePressed = false;
bool pbtwoPressed = false;

void pboneCallback(void) { pbonePressed = true; }
void pbtwoCallback(void) { pbtwoPressed = true; }

int main() {
   pbone.rise(&pboneCallback);
   pbtwo.rise(&pbtwoCallback);

    while(1) {
        if (pbonePressed) {
            pbonePressed = false;
            // Do actions required when button one is pressed.
        }
        if (pbtwoPressed) {
            pbtwoPressed = false;
            // Do actions required when button two is pressed.
        }
    }
}

As you can see from the above, the callbacks just set a flag. In the main() while(1) loop you detect these presses and act accordingly. If you are in a function that needs to break when button two is pressed for example, break the loop on pbtwoPressed going true. But so long as that function is called from main loop and NOT by another interrupt, it'll break the loop. But if you invoked a function that loops from a callback you will never see pbtwoPressed going true.

Try to stay in "user context" and use the interrupts sparingly. Don't execute your entire program from callbacks or you'll never get it to work (and if you did, slight minor changes or mods later will bring down the house of cards).

04 Feb 2011

Thanks for the explaining this. This was just a proof of concept as I said. The idea was to construct an interrupt driven menu. The reason that everything is driven by interrupts is that I was planning to run another state machine inside the while loop, so I wanted to delegate those functions to the interrupt routine.

I'll follow what you suggest but I sort of managed to make it work using a ticker inside that function. Anyway, thanks for clarifying this.

11 Oct 2013

FWIW the interrupt-driven thing is possible but it is HARD. To avoid interfering with the main loop each interrupt must return almost immediately. This means minimal or no delays and no blocking code. A lot of the more complex peripheral libs wait for the operation to complete, which is not what you want, you want to write to the peripheral, return, then receive a callback when the operation finishes.

To write to a GLCD using interrupts you would probably want a module with a "busy" pin so you could bind an interrupt to "not busy ". To write a string to the display under interrupts you could put the string in a buffer then write the first byte only and return. Later when the display completes and goes "not busy" this triggers an interrupt that pulls the next byte and sends it. When the buffer is empty the interrupt could set a flag to say the display is ready, or call a callback to refill the buffer.