Break out of a case after interrupt function?

Thread Starter

Mailliw102

Joined Nov 5, 2012
3
Hi!

I'm working on a school project in C, using a PIC16F877A. Here is the general idea of the code (This is pretty basic stuff, I'm not an advanced C programmer):

Rich (BB code):
int step=0; 		//This is global

void main (void){
     while(1){
       switch(step){
         case 0:do stuff
	        while(button1 not pressed);     //Wait for button1 push
                do more stuff
                step = 1;
                break;

         case 1:do stuff
	        while(button1 not pressed);     //Wait for button1 push
                do more stuff
                step = 2;
                break;

         case 2:do stuff
	        while(button1 not pressed);     //Wait for button1 push
                do more stuff
                step = 3;
                break;

         case 3:do stuff
	        while(button1 not pressed);     //Wait for button1 push
                do more stuff
                step = 0;
                break;

         default:step = 0;
                break;
       }
     }
}
Basically, this program performs different actions depending on the current step. Every step there has to be a button1 push to continue to the next step. This part of the code is 100% working as intended.

I want to add another button (button2), that whenever pushed, changes the "step" value to 3. In other words, if you press button2, I want it to skip right to case 3 in the main function. I have no clue as to how I can achieve this. I thought of using an interrupt on change on portB with button2 that would put 3 in the "step" variable (which is a global variable) but the problem is that after the interrupt function is completed, the program would go back to where it was before the interrupt i.e. in the middle of a case. I don't want it to finish the case it was in, I'd like it to jump straight to case 3. How would I go about doing that? I also thought of putting my whole while(1) in a function, then calling this function from within the interrupt function. While this would technically make the program work as I want it to, it kinda feels weird and I'm really not sure if this is a good idea. I mean, the interrupt function would never properly complete itself. Is this dangerous for my PIC ?

Is there a "cleaner" way to do this?

Thanks in advance!

Edit : I'm using MPlab with the CCS compiler in case that matters.
 
Last edited:

tshuck

Joined Oct 18, 2012
3,534
Add dangerous as it is, you could modify the stack so that the program would think it wad returning where it was when, in fact, it would be going to case 3. Other than that, I can't think of anything aside from testing a variable that is set when interrupted...
 

tshuck

Joined Oct 18, 2012
3,534
actually, I just thought of another possibility, you could pop the stack of the return address of the interupt and do a go to the location of case 3. You would probably need to write this in assembly, and you'd need to be sure how many items were added to the stack from the context switch...
 
Last edited:

tshuck

Joined Oct 18, 2012
3,534
To respond to your solution, no, your pic has a finite stack size it would overflow...I believe the 16f877A has a 8 level deep stack, but I'm not sure of this... also, you should not be able to interrupt within an interrupt routine, you need a retfie(return from interrupt...this cleans up the stack) first.
 

ErnieM

Joined Apr 24, 2011
8,377
Assuming the "while(button1 not pressed);" is pseudo code, I would write it like this:

Rich (BB code):
int step=0;         //This is global

void main (void){
     while(1){
       while(button1 and button2 not pressed);     //Wait for either button pushed
       if (button2 pressed) step = 3;              // this is your step correction for button 2

       switch(step){
         case 0:do stuff
                do more stuff
                step = 1;
                break;

         case 1:do stuff
                do more stuff
                step = 2;
                break;

         case 2:do stuff
                do more stuff
                step = 3;
                break;

         case 3:do stuff
                do more stuff
                step = 0;
                break;

         default:step = 0;
                break;
       }
       // note this essential addition !!!!
       while(button1 or button2 pressed);     //Wait for button release here
     }
}
Do check the release I added.
 

Thread Starter

Mailliw102

Joined Nov 5, 2012
3
Thank you for your answers. I have never played with the stack and have no experience with it, so I did a bit of research and found this :

PICs have a hardware call stack, which is used to save return addresses. The hardware stack is not software accessible on earlier devices, but this changed with the 18 series devices. Hardware support for a general purpose parameter stack was lacking in early series, but this greatly improved in the 18 series, making the 18 series architecture more friendly to high level language compilers.

As I'm using a PIC16 (earlier than 18) I don't know if this would even be possible.

As of your suggestion, ErnieM, after trying to apply your logic to my project, I find that I could very well make this work and it is so much simpler than messing around with interrupts and with the stack :)

Thanks again to both of you!
 

John P

Joined Oct 14, 2008
2,026
I'm sure there's a way to make ErnieM's suggestion work, but he has introduced a major change. In the original version, the order of events was "do stuff, Wait for button1 push, do more stuff". Ernie wants it to go "Wait for either button pushed (assuming it's not b2), do stuff, do more stuff". If this is workable, then fine.

I don't think there's any way a PIC processor can be interrupted during a task, and then based on something that happens during that interrupt, go somewhere else rather than returning to where it was. The best you can do IMHO is have a flag examined at various places in the code, which can be set in the interrupt and if found to be set, can cause a branch in program execution.
 

MrChips

Joined Oct 2, 2009
30,806
Just to repeat what others are saying,

there is no safe way to break out of a task on an interrupt.
Modifying the stack is a definite no-no.

The proper solution is to set a flag from the interrupt handler.
Test this flag within your main program in order to control program flow as Ernie and others have said. Remember to clear this flag once acknowledged.
 

kubeek

Joined Sep 20, 2005
5,795
I would do it like MrChips says:

Rich (BB code):
int step=0;         //This is global

void main (void){
     while(1){
       switch(step){
         case 0:do stuff
            while(button1 not pressed || button2 not pressed);     //Wait for button push
            //these should be just flags and should be set by the interrupt routine
            //remember to unset the flags in the interrupt as well, or in some other predictable way

            if(button2 not pressed == pressed)
            {
                    step=3;
                    break;
            }
            do more stuff
            step = 1;
            break;

        case 1:
        etc...

         default:step = 0;
                break;
       }
     }
}
 

ErnieM

Joined Apr 24, 2011
8,377
I'm sure there's a way to make ErnieM's suggestion work, but he has introduced a major change. In the original version, the order of events was "do stuff, Wait for button1 push, do more stuff". Ernie wants it to go "Wait for either button pushed (assuming it's not b2), do stuff, do more stuff". If this is workable, then fine.
A good point, I did make that change. As far as how workable it is, it is basically how I test my buttons when driving around a menu. Here's a snipplet of some code (simplified to remove my app's details) I'm currently using:

Rich (BB code):
void Routine(void)
{
  // routine initial set-up here
  while (Keys);       // wait for keys to be released from previous task
  while (1)           // loop to watch for key pressings
  {
    while (!Keys);   // wait for keys to be pressed
    switch (Keys)
    {
      case BUTTON_UP:
        // do the BUTTON_UP task
        break;

      case BUTTON_LEFT: 
        // do the BUTTON_LEFT task
        break;
        
      case BUTTON_DOWN:
        // do the BUTTON_DOWN task
        break;

      case BUTTON_RIGHT:
        // do the BUTTON_RIGHT task
        break;

      case BUTTON_ENTER:
        // ********************************
        // THIS task completes this routine
        //  then returns control to the calling routine
        // ********************************
        // do the BUTTON_ENTER task
        return;        // EXIT this routine
    }
    while (Keys);   // wait for key to be released
  }
}
In this code Keys is a global variable who's value is set inside an ISR to co-inside with the current debounced state of 5 external buttons who's names and functions can be inferred from the case statements.

When a button is pressed the function is activated and a case statement executes. Then the trailing while(Keys); blocks the code till the button is released. There is also an auto-repeat built into the ISR which comes in handy when incrementing or decrementing some parameter.

I don't think there's any way a PIC processor can be interrupted during a task, and then based on something that happens during that interrupt, go somewhere else rather than returning to where it was. The best you can do IMHO is have a flag examined at various places in the code, which can be set in the interrupt and if found to be set, can cause a branch in program execution.
Exactly the correct point. You loop and test a global variable set in the ISR that the looped code monitors. Here I am using the Keys variable in just such a fashion.
 

Thread Starter

Mailliw102

Joined Nov 5, 2012
3
After a bit of thinking I figured that the "do stuff" and the "do more stuff" parts are actually happening really quick in my project. Therefore button2 would almost certainly be pressed during the "while(button1 not pressed)" part in any case of the switch. This brought me to this :

Rich (BB code):
int step=0; 		//This is global

void main (void){
  while(1){
    switch(step){
      case 0:do stuff
	     while((button1 not pressed)&&(button2 not pressed));     //Wait for any button push
             if(button 2 is pressed)
               step = 3;
             else{
               do more stuff
               step = 1;
             }
             break;

      case 1:do stuff
	     while((button1 not pressed)&&(button2 not pressed));     //Wait for any button push
             if(button 2 is pressed)
               step = 3;
             else{
               do more stuff
               step = 2;
             }
             break;

      case 2:do stuff
	     while((button1 not pressed)&&(button2 not pressed));     //Wait for any button push
             if(button 2 is pressed)
               step = 3;
             else{
               do more stuff
               step = 3;
             }
             break;

      case 3:do stuff        
             while(button1 not pressed);     //Wait for button1 push
             do more stuff
             step = 0;
             break;

      default:step = 0;
             break;
    }
  }
}
This way, I wouldn't even need to use interrupts. I would of course add some more "whiles" to wait for buttons to be released to make sure the cases won't all happen in one button push.
 
Last edited:

ErnieM

Joined Apr 24, 2011
8,377
You are beginning to loose me. (OK, I think I really dropped out a while ago.) This is due to using code to design the code: all you have is code and no design!

A state diagram would be useful to describe the steps needed to perform the task without writing code, be it pseudo code or real C.
 

THE_RB

Joined Feb 11, 2008
5,438
I don't use interrupts for button presses, and try to save ints for very time critical stuff like timers.

Buttons are very very slow in comparison and are best handled manually by your code as some part of a well designed main operation loop, which normally does the decision making stuff (ie "if this button is pressed, do X, or go to mode Y").

I agree with ErnieM you should start fromt he beginning with a hand written flow chart etc and decide exactly WHAT the program will do and HOW and WHEN it is done. The adding the code part later should be much easier.
 
Top