This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

returning to different address form interrupt with c routine

i used INTX0 for intering to the MENU of my program
and need to back to a address, labled in my main program. so i need to change the contain of the memory that SP is pointed at.
how can i do it in C . i chaned the SP to the address that point to 0x0000 and expected to be reset but it didnot.

  • If I understood, you are using an external interrupt to directly 'jump' to a function that process the user interface, and want to 'return to' a label in your main program body, is that what you want?

    A few comments on this:

    1) It is a really bad, bad idea to do this, in any language, but especially in C.

    While from a purely academic thought experience it would be possible to manipulate the return stack to do that, it requires much more than 'change the contents of [SP]'. The C runtime would be interrupted at any point, and would never return. Any non-pure reentrant code would probably cause a runtime corruption of non-atomic data accesses.

    Do you have any knowledge of the compiler entry/leave code for interrupt handlers? This is not standardized, and would require assembly code to proper hack the return stack. Furthermore, you would have to disable interrupts while processing the 'menu option', and only enable them at a while(1); loop or something like that.

    Another twisted-mind approach would be to use a call to an element of an array of function pointers that point to the menu option functions. That could be done entirely in C. The menu options would then be processed in the interrupt thread context, and the menu interrupt function would then return to the 'main' thread context, that might just be a while(1); loop. That is a silly way of using the interrupt, and is a total inversion of good programming techniques, but would ultimately work.

    2) A label in C has local scope, i.e., it is only visible at the current code block level. Labels are very suspicious in any C program, and are in the language only to save a indent level on an infinite loop, when you can place a goto label instead of a do{}while(1);. This is very, very ugly.

    3) As I said, this is simply the WRONG APPROACH.

    Interrupts are NOT MEANT to be used that way. If you want your external INT0 pin to cause a main menu entrance, you can just set a flag at the INT0 interrupt handler, and then test for this flag at the main thread of your program, which will invoke the menu function.

  • 2) A label in C has local scope, i.e., it is only visible at the current code block level. Labels are very suspicious in any C program, and are in the language only to save a indent level on an infinite loop, when you can place a goto label instead of a do{}while(1);. This is very, very ugly.<p>

    I'd never use a label this way. However, they can be quite handy for exiting triple-nested loops, or do certain kinds of error handling.

    100% agreed on the answer to the original question, though. Doing something like that would amount to digital violence and will result in a horrible, messy, unmaintainable, bug-infested nightmare at some point. If it ever works.

  • "I'd never use a label this way"

    Actually, that very example is in K&R first manuals...
    I agree with you, have never used it to substitute for a long loop. It is much better to use good indent policy and document the end of long loops.

    "[...] they can be quite handy for exiting triple-nested loops, or do certain kinds of error handling.
    "

    Good point. Using labels for exiting multiple nested loops is a quite good way of implementing some algorithms, particularly if extracted from Pascal-style implementations.

    Thank you, I stand corrected. :-)

    "[...] Doing something like that would amount to digital violence[...]"

    Surely. That would be SMD (Sado-Masochistic Development).

  • returning to different address form interrupt

    This comes up on a regular basis and, as always. the OP has a 'reason' for it. Well I have a 'reason' for going 100 MPH down the road, and I am sure the tree I hit has a 'reason' for being there as well.

    That something seems the solution is not 'proof' that it is right.

    YES, I can see places where doing it this way might be handy, but the experience I have tell me that however handy it might be, using flags etc will save me money, time and headaches.

    Erik

  • Returning to a different address on return from an interrupt, is a very specific feature of real-time operating systems, where the OS can reschedule the running task on the fly when an interrupt detects an event that should wake up a higher-priority task.

    It isn't trivial to do, and to do it correctly, it requires taking care of processor registers, switching stack etc. Leave such coding for the best embedded programmers you can hire - or buy the finished work from.

    To do it just for menu processing is _bad_.

    Letting the interrupt perform the menu choice before returning is also _bad_. It either requires that the processor supports (or are helped with supporting) recursive interrupts, or that other interrupts will not get serviced on time.

    In my view, interrupts are for real-time action and for detecting events.

    Unless the event is trivial to process, the actual processing should be performed somewhere else. Maybe a super-loop scanning through a set of action flags. Maybe by using an RTOS to prioritize between pending tasks.

    However, I would recommend that you see interrupts as an "unstable" state that you want to keep the processor in for as short a time as possible. Having a processor with nested and prioritized interrupts means that you may get away with a solution with long-running, low-prioritized interrupts. However, that does not mean that it is a good solution.

    Avoid ugly or bad solutions unless you realize that you have your back against the wall and that there really are no good solutions available. Even then, write down the list of reasons _why_ the good solutions are not available to you. Check them one by one. Have someone else check them.

    Building your software around a bad idea will cost you (or someone else) dearly for the rest of the life of that software.

  • "Building your software around a bad idea will cost you (or someone else) dearly for the rest of the life of that software."

    And, as we can see from so many posts here and elsewhere, that can be a very long time indeed...! :-0

    Remember the so-called "Year-2000 bug" - even now, its fallout is still lurking: http://www.keil.com/product/y2k.asp

  • "that can be a very long time indeed...! :-0 "

    Absolutely! One thing is right about firmware lifetime: it is always longer than the programmer's ... Especially when it is full of unfathomable black magic.

    You get really indoctrinated on good software practices only after you inherit some gargantuan spaghettiware from a previous programmer (your predecessor), and is forced to 'catch the bugs' on that nasty piece of junkware.

  • Per Westermark wrote: "It isn't trivial to do, and to do it correctly, it requires taking care of processor registers, switching stack etc."

    That and much more. What escapes people quite often is that the full static context of the running thread must be preserved. That includes ALL static variables that the C runtime and C libraries eventually use, in addition to processor 'bare machine' context. That is only feasible if the particular compiler is designed for preemption. This is NOT the case of C51. That is one of the reasons why preemption on any '51 kernel sucks so badly.

    "Letting the interrupt perform the menu choice before returning is also _bad_. It either requires that the processor supports (or are helped with supporting) recursive interrupts, or that other interrupts will not get serviced on time."

    Not necessarily. If the systems designer wants to perform lengthy operations on the context of the interrupt service routines, and the system is designed accordingly, i.e., with full context preservation and protection against non-atomic data operations, it is perfectly viable to do so. The system may not require realtime, for example. Actually, that is one model of multitasking, where there are only 2 tasks that operate, one higher priority preempting the other. This is not realtime oriented, and I doubt that there are many embedded systems that would benefit from this architecture.

    As you say, that doesn't mean it's a good architecture.

    Building your software around a bad idea will cost you (or someone else) dearly for the rest of the life of that software.

    And what is worse: will guarantee you bad karma. Generations of programmers after you are fired will send you bad vibrations for that.

  • All that being said, here are a few comments on simple interrupt/main code interworking:

    1) Plan your system following a 'event-driven' architecture, i.e., where actions are taken depending on events that may be triggered on the software. It is a intuitive, clean, simple to understand and simple to document architecture for many control-oriented designs.

    2) The simplest design is a main loop in which all main events are checked and the actions are dispatched. This is simple yet very powerful. Surprisingly fast systems can be realized if all the 'actions' are fast. For example, using distribution and state machines to perform the tasks in granular form, you can achieve a high degree of availability and low latency.

    3) Use 'atomic operations' for the events. In the 8051 you have the bit datatype, that is guaranteed to be set/clear/tested in a single uninterruptible operation. Use the C51 intrinsic _testbit_() to execute-if-set-and-clear in a single operation.

    4) Arrange small, localized actions to be done on well-defined events, and use meaningful names. Having 53 events called flag_1 to flag_53 is sure to atract the wrath of the gods.

    5) If you have to service events triggered in interrupts, set the bit in the ISRs, and execute the actions in the main loop. This is simple and makes the ISR extremelly fast. Avoid any function calls in the ISRs, to allow the C compiler to optimize the overlay memory and static space more efficiently. This makes your code faster and smaller.

    6) When well balanced, the system can be seen as a number of objects that do specific tasks. You can build layers of functionality and make the high-level layer simply to set the right sequence of events to get things done. This can allow effective code reuse and tremendously simplify maintenace.

    7) DOCUMENT, DOCUMENT, DOCUMENT your work and your ideas. Better to have a page of comments than to have no comments at all. If you want to go verbose, remember to change the comments if you change the code. Bugs are mismatches between comments and the code. Good comments tell what the programmer pretends the code to do, rather than describe literally what the code does.

    With those simple guidelines it is possible to do very heavy distributed systems, that manage multiple streams of interface serial data simultaneously, do motor control, and user interface, all at a relatively low latency.

  • The simplest design is a main loop in which all main events are checked and the actions are dispatched. This is simple yet very powerful

    8) AT THE ENTRY to each and every routine called from the workloop do a SIMPLE test (do I have anything to do) and, if not, get out of Dodge.

    Erik

  • Hey Jonny,

    Nice piece.

    Just do not say "... tell what the programmer PRETENDS the code to do..."

    eheheh

    to PRETEND = to Fake

    I guess you knew that.

    BTW. Did you go to High School in ETFSP?

    ED

  • Thank you! I could pretend that I intended to imply that most programmers' comments are really wishful thinking, but I stand corrected. That's just a bit of the lingua mater showing through.

    BTW, I am that same person. ETFSP, '82. Have we met there?