This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

What's a better way of waiting for a nested interrupt to trigger?

Other Parts Discussed in Thread: EK-TM4C1294XL

Hi,

Board: Tiva™ C Series TM4C1294

EK-TM4C1294XL

I want my switch button interrupt handler to trigger an ADC sample, and then wait for it to finish sampling before proceeding. Right now I'm polling a global variable until it has been set to "READY". Is there a better way?

int_status is a global variable.

/* priorities */
	IntPrioritySet(INT_ADC0SS0_TM4C129, 0x00);
	IntPrioritySet(INT_GPIOJ_TM4C129, 0xC0);

/* switch interrupt handler */
void swHandler(void)
{
	GPIOIntClear(GPIO_PORTJ_AHB_BASE, GPIO_INT_PIN_0);

	UARTprintf("Checking ADC Value...\n");
	ADCProcessorTrigger(ADC0_BASE, 0);
	int_status = WAITING;
	while (int_status == WAITING) {
		UARTprintf("Waiting...\n");
	}

	UARTprintf("Done. Value is %d\n\n", adc_value);
}

/* ADC Interrupt Handler */
void adchandle(void)
{
	ADCIntClear(ADC0_BASE, 0);
	ADCSequenceDataGet(ADC0_BASE, 0, &adc_value);
	int_status = READY;

}

  • While your code - as is - may run there are aspects which appear, "non-standard" - may warrant consideration.

    Long ago we were taught that interrupts should strive to be: "Short/sweet - do the minimum required & end!"  Your code runs afoul this guideline via: a) use of (multiple) UARTprintf() - big time eater and b) your "nesting" further delays the escape from swHandler.

    Beyond this - I note that your sequence of ADC operation is outside that which I believe to be more standard.  Here's our preferred ADC sequence:

    ADCProcessorTrigger()
    while(!ADCIntStatus())
    ADCIntClear()
    ADCSequenceDataGet()
    UARTprintf()

    Note especially the while loop following your call to ADCProcessorTrigger.  Minus this - your existing code "relies" upon its excessive time w/in the handler - to "avoid" erroneous conversions.  Many past StellarisWare/rebrandWare examples demonstrate this/similar ADC code sequences...

    To meet your requirement for switch-button trigger - suggest that you add a, "button-detected" variable w/in your swHandler.  Then - w/in the safety/comfort of "main()" you may periodically test 'button-detected" and call the ADC code sequence listed herein.  Note too that past (multiply offending) UARTprintf() has also fled from interrupt - resides now (where it more properly belongs) w/in main.  You may add an earlier call to UARTprintf() to duplicate your original, "Checking ADC Value" organization.

    Make sense?

  • Why do you believe all the work should be done in the main loop? Your method suggests using the "swHandler" to merely set a variable which then the main function polls. I could just get rid of the entire interrupt handler and poll directly in the main loop...

    Take for example the lwIP library (example code found in enet_lwip) - All the work is periodically done in interrupt handlers via systick. No work is done in the main function - doesn't think violate everything you're saying?

  • Normal human "keypress" is unlikely to happen beyond 60Hz - that's a fairly standard rate by which keypads/keyboards are scanned. 

    I stand by my past learnings, "interrupt handlers should be short/sweet."  Violating that long agreed guideline - via the use of multiple UARTprintf()s - is bit, "non-standard."

    Many of my group's designs must earn Gov't Agency approval.  And most all have keypads, soft-keys, and indeed we past employed a timer interrupt to regularly scan the keypad array.  And - as the keypad may be bit distant from our main pcb - our EMI/RFI testing too often became a challenge.  We now drive all keypad outputs - without any scanning - and simply await any of the keypad inputs, "change of state."  At that time - and that time only - do we initiate the keypad scan. 

    Depending upon the individual program size/load - it may be that a regular loop w/in main can indeed serve our keypad read function.  (i.e. most of the time - by far - no key activity is detected)  We have multiple interrupts in play - most more urgent than keypad scan.  (recall - keypad scan is a human-timed event - rather hard to "miss."

    You're silent on my identification of your use use of delay rather than more conventional ADC status read.  Believe that status read will better serve you.

    I saw your post "sit" - responded as I felt appropriate - and note your writing as tad harsh.  And no way is "everything" I said in violation - as you assert.  You did ask for an "opinion" did you not?

    It's fine to be unappreciative - but better to be a bit accurate when passing (unkind) judgment upon your sole responder... 

  • I agree cb1. If anything I'd be more emphatic.


    First as to the way you use nested interrupts. It is exactly equivalent to a function call in end effect. It is a little slower because of the extra overhead involved and more prone to hard to find/reproduce problems. However, given you are using serial print functions in the main interrupt they will dominate the processing time in any case. Side note: on some processors this approach is even more difficult since they lack some of the Cortex capabilities.

    The design purpose of nested interrupts is to reduce latency (basically response time) to high priority inputs.  Spending a long time in an interrupt runs directly counter to that purpose. There are ways to abuse interrupts to good effect but replacing a call instruction is seldom one of them.

    Second the printf families of functions are often non-reentrant and as such cannot be called from multiple interrupts and it is a good idea to avoid calling them from all interrupts. Uart functions may be re entrant but really require some sort of serialization if used from more than one interrupt.

    Third, have you considered what happens when the button switch input fires multiple times while servicing the first case? Personally, I've never seen a case where servicing a mechanical switch in as an interrupt was better than polling it in some fashion.

    If the switch interrupt is a stand in for a electronic input my initial thoughts would be to have the switch interrupt set up the a/d and exit. The a/d interrupt will then fire on its own.

    The question is how to respond to the a/d interrupt. There are two classic approaches. First is to use a real time kernel (RTK) and use its facilities to feed the data to a process thread. The second is to use flags to do this in the main function. In the second case main is acting as the RTK.

    Generally, you would use main in simple resource constrained applications. Otherwise, you move to an RTK once you are familiar with one. The biggest overhead is often just getting one running the first time.

    The use of systick does not run contrary to this. It is acting like main where the main infinite loop runs with a fixed period between executions rather than as fast as possible.

    It does point to an intermediate solution between a full fledged preemptive RTK and a main loop. That's a Run To Completion executive. That can be simpler than an RTK and more efficient but does require experience to use effectively. It will not replace the need for an RTK in all situations. It is one of the cases I mentioned where you can effectively abuse interrupts to good purpose.

    Robert

     

  • There is a pattern I didn't mention.

    That is to do everything in the interrupts. In that case you would trigger the a/d in the button interrupt and then exit that interrupt.  In the a/d interrupt you would do your processing and pass the data to a global buffer for the serial interrupt or a timer interrupt to format and send.

    You pay for this in increased latency but you minimize that by moving the serial output to another low priority interrupt or the idle function. You can see how reducing latency moves you inexorably to the model cb1 and I suggest.

    Ultimately, you will get the lowest interrupt latency by doing the least you can in the interrupt and doing your heavy lifting in another thread or task.

    Robert

    And Happy Thanksgiving

  • Robert Adsett72 said:
    You can see how reducing latency moves you inexorably to the model cb1 and I suggest.

    Beware Robert - less you land (avec moi) upon poster's cursed, "violator's list."

    Beyond that (agreed) inexorable movement to cb1/Robert model (i.e. sanity) the creation of an excessive number of interrupts is likely to degrade the overall performance of all interrupts.  Yes ARM has taken pains to reduce the impact of such - yet those safeguards force further care/complexity upon the interrupt design & implementation - potentially leading to new, complex timing related issues.

    Proper interrupt handlers are not often found on, "muscle beach" - preferring instead, "short/sweet/light" function execution.  Other threads or main() are broader shouldered - better to, "squat/lift/jerk" time-consuming aspects of the program...  (i.e. "heavy lifting")

  • cb1- said:

    Beware Robert - less you land (avec moi) upon poster's cursed, "violator's list."

    I can probably survive such a denouement.

    Sometimes lessons have to be learned the hard way. In that case best to learn early before the consequences become hard to undo.

    Robert

  • Hi cb1,

    Sorry about the late reply. I'll preface by saying apologies if the tone in my reply was harsh. I reread it and can see how it might have been misinterpreted - that was not my intention. I like to understand the reasons for why certain things should be done a certain way.

    My arguments came because of my university courses, where I have learnt that "polling is bad because it wastes CPU time which could be assigned for other tasks", and hence we learn about interrupt handlers and so forth (we were using interrupt handlers to trigger button presses on a touchscreen, similar to a switch). I have also learnt that, as you have said, interrupts should be short as possible. However, this was primarily learnt in an operating systems course, where there were multiple threads of execution. In that sense I completely agree interrupts should be short, because the os can switch over to another process and continue execution while the other process polls for something (in this case, A/D being ready).

    After re-reading the replies, I agree that my method was bad, and that I shouldn't exploit nested interrupts in this way.

    My program does not have an operating system, it's a single thread of execution. You wrote that the preferred way of A/D scanning is this:

    ADCProcessorTrigger()
    while(!ADCIntStatus())
    ADCIntClear()
    ADCSequenceDataGet()
    UARTprintf()

    in the main loop. Isn't this also a busy-waiting scenario? The main loop cannot progress any further until the A/D is ready, so it wastes CPU time. I like the idea behind Roberts second reply, in that you instantiate the A/D conversion then exit the first interrupt, and process the A/D result in the A/D interrupt when it is ready. You say that this method would degrade performance. Do you say this because the A/D conversion process is short relative to the time it takes to change into the interrupt routine, or another reason? What if the conversion time was long (e.g. few hundred ms)?

    I would like to clarify your solution to the switch button being pressed and processed in the main loop. Taking it one step back into a more general scenario where multiple events are waiting, do you suggest something like the following?

    main loop:

    while (1)

    {

    if(activity1 == READY){

    processActivityOne();

    }else if (activity2 == READY){

    processActivityTwo();

    }

    //etc etc

    }

    and the activity_x status are changed by an interrupt handler, or other software routine.

    Finally, you did not mention in your reply about my comment about the lwIP library doing everything in interrupt handlers. Do you think this is a bad method they have implemented, or is there a good reason for them writing the code in the way they have?

    Once again, apologies if my reply was harsh -  I do appreciate all answers.

    Thanks

  • Thimira G said:

    My arguments came because of my university courses, where I have learnt that "polling is bad because it wastes CPU time which could be assigned for other tasks"

    Two parts of the answer are contained in that quote.

    First, wasting CPU time only matters if there is something else it needs to do now. That leads back eventually to latency.

    Second,  tasks.  You need to have some way of organizing the work to be done.

    Another thing to consider is that in some cases polling is superior to interrupts. IMO mechanical switch inputs is one of those areas. And remember, polling can be interrupt driven.

    Robert

  • Thimira G said:
    ...did not mention...about my comment about the lwIP library doing everything in interrupt handlers.

    No I did not - and my intent was to respond to your general points - not to any specific code library.  (such demands further time/effort - I did not judge that as necessary for any 1st reply)

    Of course every program is unique - and program objectives & priorities may (properly) steer the programmer - so a, "one size fits all" answer is a compromise, at best.

    Many of my small firm's programs make great use of interrupts - yet the MCU still spends a good amount of time w/in it's, "main polling loop" (when no interrupt is active).  Engineering/Programming most always involve "trade-offs" - we find this method (usually) best for most of our program/project Apps.  (and we took past tech firm "public" - via just such methods)

    I am not the inventor of the ADC sequence I presented.  We've used it w/great success - the method your initial code displayed "relied" upon the "slowness/inefficiency" of multiple UARTprint calls.  As you now acknowledge - bloated interrupt service routines are not generally recommended.  To overcome the ADC's, "while loop's" limitation - we "escape" from that "idle" and perform a continuing "scan" of our main polling loop.  An interrupt could be used as well - we've found our method to meet our requirements.

    As to "harshness" - and "if" the tone was harsh - clearly it was!  I quote: "...doesn't think violate everything you're saying?"  I've been to both engineering & then law school - that, "violate everything" appears crafted - goes to motive/intent!  (thus NO "if")  Your new tone is far more collegial - yet as Robert has offered much valuable detail - better if he be acknowledged as well...

  • I think we need some sort of refence for embedded kernel patterns.

    I'll see if I can get a chance to give you an outline.

    In the meantime Thimira, consider this. You always need to organize your control. Even a bare metal application has some sort of kernel once you head beyond doing a single item.  It's just that the kernel may not be (easily) separable from the application.

     

    Robert