C program flow control - Very Basic Question.

I've got a very basic question guys, I'm teaching myself C using Microchip's C18 compiler.

I've got my main loop running, looking for certain conditions to occur, receiving a serial command & processing it.

I need to monitor a few things for a time out condition, to do this I'm pre-loading Timer1 with the required values and when the timer overflows it jumps to the relevant interrupt.

All the above works fine.

When the Timer1 interrupt fires what is the easiest/best/correct way to execute a block of "reset" code and then recommence execution at a _particular_ place in the main() loop?

Using assembly I would have used "goto" to steer execution where I wanted it, but how is it approached in C?

Thanks.

Reply to
Royston Vasey
Loading thread data ...

The interrupt will return to the place the interrupt triggered. No more, No less.

Do you want to return to another place and not the place the interrupt triggered ??

First I would ask why ?? or do you not understand interrupts ?

At the point of the interrupt, the (PC) Program Counter is pushed onto the stack. This address must have something done with it, not just "jump away". If your code is in the middle of a math routine when the interrupt is triggered, what do you want done with the that math routine ??

Second, Learn RTOS, this will take longer to understand, but it may be what your looking for.

There is no "goto" in C.

"goto" in C is bad (very bad) practice.

Your Welcome.

hamilton

Reply to
hamilton

(sigh) No, goto has a role in C. What is wrong is using goto as if it was GOTO (e.g., BASIC).

Usually, you can restructure your code with suitable blocks to eliminate the need for a "goto". But, there are times when "goto" is the *right* choice. Convincing yourself that it is "very bad practice" will blind you to when it is the *right* practice!

Reply to
D Yuniskis

As you have stated "you can restructure your code".

Why change your code just to use a goto ??

Design your code right the first time. ;-)

hamilton

Reply to
hamilton

There most certainly is.

I very much agree.

There are a few places where a goto in a C program is, IMO, the best choice. Most notably when you need to "signal" an exception that must cause an exit from inside several nested loops or other control structures. Adding a bunch of flags to the loops to make them all exit prematurely due to the exception/failure is convoluted, hard to maintain, and will cause bugs later in the codes life.

A simple "goto failure;" is much simpler and easier to read/maintain.

If you look at C code in things like kernels and network stacks (code that was written by some very skilled people), you'll see the occasional goto used to handle what would be an exception in a language like Python.

I've also seen goto used to implement a retry mechanisms where adding another layer of looping would have been too convoluted.

--
Grant Edwards               grant.b.edwards        Yow! The PILLSBURY DOUGHBOY
                                  at               is CRYING for an END to
 Click to see the full signature
Reply to
Grant Edwards

There are times when a goto is simpley the best choice and results in much simpler and eaiser to maintain code.

No idea what you're asking.

Sometimes a goto is the right way.

--
Grant Edwards               grant.b.edwards        Yow! Go on, EMOTE!
                                  at               I was RAISED on thought
 Click to see the full signature
Reply to
Grant Edwards

Great!

What are you timing? I.e., are you trying to prevent the code from waiting for a received character indefinitely? Or, trying to abort a system call after a certain time limit is exceeded? etc.

You (typically) can't just jump around your code "willy nilly" -- unless you really know what's going on under the hood (doubtful if you're just learning).

E.g., when the IRQ is signalled, *something* was being done at that time. That "something" has to be considered when you decide to *abandon* it (by jumping to a *particular* place in the main() loop).

If, for example, the code was busy *deep* in some nested function call -- lots of cruft on the stack -- and you just "jumped" to someplace else to "resume execution", then you have all that cruft still sitting on the stack with nothing to unroll it. (what happens if the *next* interrupt does the exact same thing? and the one after that? i.e., you eventually "blow the stack" because you've never cleaned its previous contents off of it)

Even if you were lucky enough to be able to perform the jump without consequence, you can't jump to any arbitrary point in "main()" -- because you don't know what the prerequisites for that part of main happen to be. Consider, what would you expect the code to do if you jumped into the middle of a "for" loop?

If you are building an application that relies on a simple while loop for it's structure, then you have to poll every "event" of interest in that loop and "dispatch" accordingly

*from* that loop. Your interrupt has to be "invisible" to the application (in terms of how it *immediately* affects program flow).

If you want to implement a simple timeout, try something like:

- set a (volatile) flag (*bit*!) someplace "common"

- have the ISR *clear* that flag when it occurs

- have whatever "service" you are trying to "time" poll that flag along with whatever else it is supposed to do; if it sees the flag get cleared "magically", the service returns an error (TIMEOUT) instead of success

So, if you wanted to put a timeout on how long you would wait for a character to be received:

result receive_character(char *character, boolean *flag) { while (*flag == SET) { if (check_UART_for_character() == TRUE) { *character = get_UART_character(); return SUCCESS; } return FAILURE; }

I.e., as long as the flag is still *set*, keep checking the UART to see if a character is available. If so, return the character *and* indicate "SUCCESS".

However, once the flag is NOT "SET", stop checking the UART and just return a FAILURE (TIMEOUT) indication.

Your main() then does something like:

// setup hardware boolean waiting;

... while (FOREVER) { char character; ... start_timer(&waiting, time_limit); if (receive_character(&character, &waiting) == SUCCESS) // do something with "character" else // character didn't show up in the prescribed time limit ... }

void start_timer(boolean *flag, timeout_t timeout) { *flag = SET; set_hardware_timer(timeout); }

and your ISR does:

void ISR(void) { // diddle with the hardware waiting = ~SET; }

(I've not proofed this for typos :< )

Reply to
D Yuniskis

Yes. Who said anything about *changing* it to *exploit* a goto! Rather, *avoiding* the goto can be a colossal waste of time and just make the code more brittle.

The goal of a HLL (if you want to call C such :> ) is to make it easier to *express* algorithms. Let the compiler sort out how to get what you want out of the code. (and the compiler will often use *lots* of "goto's" -- jumps!)

Exactly! -------------^^^^^^^^^^^^^^

I've seen horribly mangled code that went out of its way to avoid using a goto. As a result, it's a nightmare to maintain and very brittle.

Not all algorithms lend themselves to simple blocks.

Reply to
D Yuniskis

Exactly. The "prima donna" syndrome may make you *feel* like you've been extremely clever -- until your code breaks the first time someone tries to modify it (this ignores the costs to *you* to get it working correctly in the first place!)

In C, I often bracket "troublesome" regions in a superfluous "do-while" and "break" from that in lieu of a "goto". But, that assumes you don't *normally* exit that do-while "out the bottom" (i.e., you either stay in it indefinitely *until* one of those "breaks" occurs *or* you "return" out of it)

Reply to
D Yuniskis

-- snip --

There is a "goto" in C.

"goto" in C can be about the worst thing that you can do to a program.

But used carefully and sparingly, "goto" in C can provide a very good mechanism for exception handling. I use it occasionally, and it does well for what it is. Were I to see a "goto" in a code review I would pin the author of the code against the wall to make sure he was using it correctly; were I to enter some of my code with a "goto" in a code review I would schedule extra time to be pinned to the wall and justify myself (I'd probably have careful comments in the code, too).

Exception handling in object-oriented programming is specifically designed to address the problems in C++ that C's "goto" and long branch (long goto? I can't remember what it's called) solve in C.

--
Tim Wescott
Control system and signal processing consulting
 Click to see the full signature
Reply to
Tim Wescott

Just jump to wherever the thing starts up from out of reset?

I think the PIC18 has a watch-dog timer that will do what you want in hardware -- if you don't kick the dog periodically it'll time out and reset the processor. If your timeout is to make sure that code is executing correctly, this may be what you want.

Assuming normal program operation, however, the way that this sort of thing is done is to implement a state machine in main, that loops, tests states to see where it should go next, and tests flags to see if it should do anything exceptional. In your case you'd have a flag that would get set in the ISR, then tested in main.

--
Tim Wescott
Control system and signal processing consulting
 Click to see the full signature
Reply to
Tim Wescott

setjmp()/longjmp()?

Reply to
D Yuniskis

It sounds like you want to avoid executing some code in the main loop if a timeout occurs.

The reset code is probably best handled in the interrupt routine. Also, when in that routine set a volatile global variable timeout_occured and test it in the main loop just before entering the stretch of code you don't want executed.

ex:

volatile bool timeout_occured; : : /* I don't use this compiler, so use whatever interrupt pragma it calls for */ void timeout_interrupt(void) { : /* reset code */ timeout_occured = true; } : : while (true) do { timeout_occured = false; : : if (timeout_occured) next; /* skip remaining code in the loop */

/* stuff to exexcute if no timeout has occured */ }

Yes, this is a bit of a kludge, but it is easy to understand (extremely important when you come back to modify the code months later) and it should preserve the good structure of the main loop.

If the reset code is going to take too long (always remember to keep your interrupt routines as fast as possible so as not to miss other interrupts), change the if... code to something like this:

if (!timeout_occured) { /* stuff to exexcute if no timeout has occured */ } else { /* reset code, ripped out of the ISR */ }

hope that helps

Joe Power Pain is inevitable; suffering is optional.

Reply to
Joseph Power

People who are allergic to "goto" in C should run screaming at the sight of these functions!

There are times when "goto" is the best way to structure your code - I've used it a few times. You have to be careful with it, as it's easy to write spaghetti code and lose track of things like variable initialisations (C++ is fussier about when you are allowed to use it). But setjmp() and longjmp() are far worse - if you think you need them, you are almost certainly wrong and should re-structure your code. (If you /know/ you need them, it's a different matter - few rules in C programming are absolute.)

Reply to
David Brown

Back before C++ exceptions, I wrapped setjump/longjump in a struct w/ heap cleanup func pointers to handler errors that just wouldn't reasonably work any other way (floating point and floppy disk errors). I treat C++ exceptions just as warily. If the problem can be handled with error returns, then you don't need exceptions/longjump.

-- Bob

Reply to
Bob

I've only used setjmp/longjmp to implement simulators and coroutines. Other than that, I think your "error returns" comment applies best! (i.e., make the problem *visible*)

Reply to
D Yuniskis

In our code reviews a goto had to be either a usage that the team had agreed on (very few - usually related to exception cases) or you had to defend the usage.

When we went to C++ most of the error case gotos went away with well designed destructors (to release resources) and throw() of an exception.

Reply to
Dennis

One of the incentives to move to C++ was the nested setjmp() and longjmp() in the existing C code (yes setjmp() and longjmp() was the cleanest solution in C). With matching constructors and destructors in C++ it was much easier to manage resources automatically on a throw() in the exception case. Of course C++ takes some careful design up front.

Reply to
Dennis

That's it. The more you start doing "C++ish" things the more dangerous it gets -- but it's kewl for (very, very carefully constructed) library code-like things.

--
Tim Wescott
Control system and signal processing consulting
 Click to see the full signature
Reply to
Tim Wescott

Yup. And if you're smart you take your code to one or two of the folks you know are going to _really_ care and you run it by them _first_.

And yup.

--
Tim Wescott
Control system and signal processing consulting
 Click to see the full signature
Reply to
Tim Wescott

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.