CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Prog. technique: interrupt loops in the main with an isr

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
foxOnTheRun



Joined: 17 Apr 2010
Posts: 43

View user's profile Send private message

Prog. technique: interrupt loops in the main with an isr
PostPosted: Mon Jun 20, 2011 4:43 pm     Reply with quote

This is a technique question, no actual coding involved Smile - I found myself in this situation:

I have a small system were the user interacts with a button and reads from a 16x2 LCD; the application is heavy menu based and is logically divided in macro blocks:
Code:
main
while (true){
Menu block:
init:
opt1 -> goto label1
opt2 -> goto label2
opt3 -> goto label3
..
wait for getch.

label1:
Block 1:
..
..
goto end

label2:
Block 2
..
goto end

label3:
Block 3
..
goto end

end:
stuff
..
goto init:
}

Once the user has made his decision, the code jumps into one of this blocks and the code is executed. The codes inside this block can be easily divided in action which last an amount of time, so delays_ms are the norm and then loops over depending on some parameters. At some point the loop ends because some conditions are verified and the control return to the "menu" which sits down and wait for user input.

I've stumped into this: how do I implement a watchdog style behaviour, where the user is able to interrupt the execution (once it is inside one of the macro blocks) and returns to the main? how can I do it in a beautiful and coordinated way?

Because until now I've been able to attach a rising edge external interrupt where I can detect user push AND detect the length of the push. The isr is super fast, it is just setting a flag, more, with just one button, I have a super reliable menu' navigation (debouncing has been taken care of with a mix of rising edge, hold for back to low, rc button, permanent flag status set and a 100ms delay).

But, I can do that because the program stops and wait for the input at some point. The problem arise when the code is executing, like:
Code:
ist1
delay_xx
ist2
delay_xx
...

.. and I would like to stop it (quite) anytime, exit the code and jump back at a predefined insertion point; well, just like a goto would do.

I tried configuring int timer_1 to overflow so that, every independent timestep, I could process something, check values, see what happened.. but then? How could an ISR take the main status, and rewire it so, at the end of interrupt the execution goes back to a label, for example?

I implemented the menu system with labels and gotos, just like the old time with DOS batch files ;)

Actually.. and I don't like it, I have, after every delay_ms, a condition check (IF statement) to see if the button has been pressed (the interrupt set a flag and then exit); so that if flag was set-->goto label_end. But I have to wait for the end of delay_ms instruction :\

I see this as a monstrosity! My cycles are artificially prolonged, the code is all jumpy, repetitive.. kind I need to rewrite the delay_ms instruction just to hide all that overhead.

So, to make a long story short, Is there a better/cleaner method? How could an isr modify the return pointer of the main (to a label)?

Tnx ;)
_________________
Listen, why don't you relax? Take a pill, bake a cake or go and read the encyclopedia.
asmallri



Joined: 12 Aug 2004
Posts: 1634
Location: Perth, Australia

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Mon Jun 20, 2011 6:22 pm     Reply with quote

Decouple your mainline code from the delay_ms() function by implementing your own version of the delay routine with an interrupt driven counter that decrements to zero. For example, use a counter that interrupts every ms. The interrupt handler could interrupt every millisecond but will only decrement the counter if it is non zero. Your mainline checks to see if the counter == 0 determine if the delay period has expired. This approach means your code can be looking for other exist conditions rather than waiting in delay_ms to return before checking for other exit conditions.

In this case, rather than call delay_ms(200), I would do something like, disable time interrupt, counter = 200, enable timer interrupt.
_________________
Regards, Andrew

http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!!
foxOnTheRun



Joined: 17 Apr 2010
Posts: 43

View user's profile Send private message

PostPosted: Tue Jun 21, 2011 12:58 am     Reply with quote

Looks interesting!

So in my code there will be this pseudo code in place of every standard delay_ms (I'll pack it in a function):

Code:
count = 10;                  // set my delay ms
enable_interrupts(timer1);   // enable decrement
while (count>0);             // hang here for 10ms
disable_interrupts(timer1);  // stop the int
if (flag == 1) goto END;     // Button pushed?


And, a timer will overflow every ms, so that:

Code:
TMR1_isr
{
if (flag == 1) count=0;       //  button has been pushed? exit immediately
else if (count>0) count--;    // or just decrement
}


So, when someone pushes a button the usual interrupt will set the flag (button pushed) and whenever I'm counting time, every ms, I'm checking it too so I artificially put to zero the count andback give control to the main.

Is this what you suggested?
_________________
Listen, why don't you relax? Take a pill, bake a cake or go and read the encyclopedia.
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Tue Jun 21, 2011 6:44 am     Reply with quote

Your approach will work, but I would make it into a more general solution because you are likely to have a wish for more timers. What I often do is to create a timer interrupt that is always running and triggered every 100ms, then inside this interrupt I create as many 'Software Timers' as I want. These timers work by the main routine setting a value, and then in the interrupt the value is decremented every 100ms until it reaches zero where it stops. In main I will test for the timer to have reached zero and then perform the timer based action and/or set a new value.

Example:
Code:
//-----------------------------------------------------------------------------
// Timer0 interrupt, triggers every 100 miliseconds.
// Used for multiple software based timers.
//-----------------------------------------------------------------------------
#int_timer0
void timer0_isr()
{
  // Many software count down timers...
  if (GprsStateTimer)     GprsStateTimer--;
  if (LocalTimer)         LocalTimer--;       // General purpose timer for temporary local use
  if (SignOfLiveTimer)    SignOfLiveTimer--;
  if (GprsTxDelayTimer)   GprsTxDelayTimer--;
 
  if (LedOnTimer)
  {
    LedOnTimer--;           // Count down till 0
    if (!LedOnTimer)        // Switch LED off on time out.
      output_low(LED);
  }
Now, if in the Main routine I need to do some processing where a maximum timeout is defined I can do it like this:
Code:
#define TIMER_EXPIRED   0     // Value of the software timers when expired.

  // Initialize MMC memory card
  LocalTimer = 5;     // According to the MMC specs a max. of 500ms.
  do
  {
    Result = MMC_SendCmd(SEND_OP_COND);
  }
  while ((Result != R1_OK) && (LocalTimer != TIMER_EXPIRED));

The LED timer was a piece of code where I needed a LED to be on for a defined moment of time. So in the main routine I would set:
Code:
    output_high(LED);
    LedOnTimer = ONE_SECOND;   // Switch LED off after 1000ms
And then the main program would continue with the interrupt routine taking care of the LED to switch of after 1 second.


The main difference with your approach is that I never enable/disable the timer interrupt allowing for many software timers into this single hardware timer. You only have to disable/enable the timer interrupt when the counter variable is larger than an int8, then the action of setting the value will no longer be an atomic operation (a single assembly instruction).
foxOnTheRun



Joined: 17 Apr 2010
Posts: 43

View user's profile Send private message

PostPosted: Wed Jun 22, 2011 11:31 am     Reply with quote

Wow! thanks ckielstra!

That is a powerfull approach! Bookmarked and saved locally :)

I'll start implement it in my code.

Now, switching to the next Holy Grail, parallel task programming on the PIC.

... just kidding! ;)
_________________
Listen, why don't you relax? Take a pill, bake a cake or go and read the encyclopedia.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group