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.

Reseting the Loop in Main, After Returning From the Interrupt

Using TM4C123GXL launchpad. Have a while loop in the main program, which mostly consist of delays functions. Is it possible to reset/restart the loop (start from the beginning) after returning from an interrupt (such as edge-triggered or UART type interrupts)? Don't want to do this by simply checking a global variable in almost every line, inside the main loop and delays.

If this is possible, could you please give me some tips how to do it? A simple example is also highly appreciated. Thank you.

P.S. 1: Probably it is a weird question, but I couldn't find anything about this subject.

P.S. 2: I'm mostly using  Direct Register Macro coding style, but TivaWare method is also welcomed.

  • Hello Emrah,

    If the main loop is such that you do not want to add the line of code to reset the device (the API is called SysCtlReset) then in the condition where the issue occurs, you may want to call the API directly.

    Regards
    Amit
  • Hi Amit,

    I've a bit different take re: poster's issue:  (Pardon I'm in the field and net is "in/out.")

    What we've done is, "hijack the interrupt return's address" and replace it w/the "top of user's "while loop."

    I thought that was R14 - but no - that's the return for sub-routines.   I don't use this method often enough to recall the location of the "interrupt's return address."   I suspect there is a register which holds the interrupt's return address - and if that address can be replaced w/the address of user's "while loop" - that should satisfy...

    My "dog-eared" copy of Joseph Yiu's book  notes IRQs "address offsets" as sequencing from 0X40 thru 0X3FF.   (0X40 is IRQ0.)   I believe our changing of the content of the interrupt's (proper) offset address is how we (likely) achieved poster's goal...

  • Hi Amit

    1) If I use SysCtlReset, don't I lose the global variable values that I grabbed from the interrupt? Which is quite unwanted for me.
    2) Doesn't it introduce uncertainty in terms of response time, for real time systems?

  • Hello cb1,

    I think I now understand "Reseting the Loop in Main" as posted by Emrah. There must be some conditions and/or restrictions that must be taken into account when constructing the "hijack of the PC", that the end user has to ensure is met in the while loop?

    Regards
    Amit
  • Hi Amit,

    Yes - I think that's what poster sought. I've got Joseph's book open - and I'm turning pages - I KNOW we did this via 8051 - many times!

    To my mind - poster seeks to AVOID "returning to the place in code when the interrupt occurred."   Instead - when those (noted) interrupts occur - poster seeks to vector to the "top of the while loop."

    Reading further/deeper into Joseph Yiu's book - it DOES appear that R14 (aka LR) (Link Register) contains the, "Interrupt Return Address."   Now the LR is one of 8 registers "pushed to the stack" when an interrupt occurs.   Once the interrupt handler has "completed" the registers "pushed to stack" will be restored.   And it is the fully automatic (i.e. "hands off") process carried out by the ARM MCU - which now complicates matters.   (at least for moi - now!)

    As so often is noted - the devil is in the detail - and "How to intercept the LR" (as it is pushed from the stack) without having it capture the PC (Program Counter) remains - to me (now) uncertain...  (i.e. should the LR "capture" the PC - the interrupt will not return to poster's "while loop" as he/she seeks...instead the program will return to the place it was when the interrupt occurred!)   (cb1 continues to read...i.e. Ratz!)

  • Hi Amit,   (and poster)

    Getting closer - I'll quote from Joseph Yiu's book: "Cortex M4 automatically pushes Registers: R0-R3, R12, LR, PSR, & PC (that's 8 registers) into the stack at interrupt entry - and pops them back at interrupt exit.   It is this "pop back" which I believe most challenges.

    I believe that the poster's objective is best realized by "replacing" the "LR" value with the (known) address of his/her "while loop."   Now actually doing this has challenged due to the "automation" w/in the M4 - which strives to be quick & efficient - yet (impedes, or may impede) our attempt to "intercept LR and alter it."

    This statement w/in Joseph's book is especially troubling: "Because POP is one of the instructions that can start interrupt returns - we can combine the register restore & interrupt return in the same instruction.   (emphasis mine!)   It is this (in this case, unwanted) "combination" which may pass the content w/in "LR" directly into the "PC" - with a quickness that prevents our intercept (and then modification) of LR.   We must perform that intercept and (then) value modification to meet this user's desire.

    More to come - perhaps w/luck - a (real) answer...

  • Hello cb1,

    It seems to create a calling function for the while loop.

    void
    PeriphIntHandler(void)
    {
    if(SomeCond)
    {
    CallLoopCode
    }
    }

    int main(void)
    {
    CallCleanUpCode(); // Clean peripherals for a good known start
    CallLoopCode();
    }

    void CallLoopCode()
    {
    while(1)
    {
    //
    // Interrupt may be called at any time
    //
    if(CondA)
    TaskA;
    if(CondB)
    TaskB;
    }
    }

    On Exit from the condition in the interrupt handler which requires the calling the main loop (restart the main loop) call the CallLoopCode API. It needs to make sure that any stale peripheral state is also cleared

    Regards,
    Amit
  • Hi Amit,

    I first thought of that method too (i.e. have each of those select interrupt handlers make a call to poster's "while loop.")   But...that handler has (never) completed in that manner - and once the call to, "CallLoopCode()" itself completes - will not the program return to the code space location in which the (original) interrupt occurred?   And that is "not" the poster's objective.

    That was the justification for my suggestion - to "capture and then alter" the interrupt's "return address" - which I believe is stored w/in R14 (LR.)   This was not difficult to do w/past (simpler) MCUs.  But as I read good 30 pages in Mr. Yiu's book - the degree of automation w/in ARM makes this technique more difficult.

    I believe that the poster could - fairly easily - create a series of sequential delays w/in main - and then quite deliberately - toggle a GPIO (at a known point in his code) and the MCU would then push the LR onto the stack.   By setting a break-point w/in that interrupt service handler - and then observing the various registers - and comparing each w/the (known) "program location when the interrupt occurred" - the LR can be identified. Is that strategy clear?

    Once that's known & mastered - step 2 would be to attempt to modify the value held w/in LR.  (i.e. set LR to the address of the while loop)   I can't recall if the LR can be "unstacked" without automatically copying LR to PC - which violates poster's spec...

    Does this make sense?   At least logically?  (I recognize that "doing it" is more detailed than simple theory - I'm w/out our trusty IAR so I can't "experiment" until mid-day, tomorrow.)   Have tried to (somewhat) "lighten your load."   Again I KNOW we did this quite nicely w/enfeebled 8051.   (never did I think I'd say that...8051 bought me couple of houses, sailboat, ticket-seeking car...)

  • Dear Cb1 & Amit

    As far as I understood, the most efficient solution may require modifying the LR's behavior via some assembly coding. But the problem is, I wasn't even able to finish TivaWare's Workshop PDF since it was quite complicated for me. Now, imagine how hard for me to modify the registers, without breaking the program :)

    ----

    After reading your posts and doing some trial and errors, I have came up with a solution. Although I didn't modify the return location after the interrupt, but reduced the system's response time to order of 100us (I guess). Which is quite enough for my real time system, unless there are some hidden delays.

    The Example Code: PF2 (blue LED) is toggling with lower frequency as the time pass, until SW1 (PF0) is pressed. If a falling edge interrupt occurs via PF0, program returns back to ZTART position. (ZTART is outside the while loop.)

    I'm adding the DRM style code. Seems like it responds quite fast, but didn't check the exact time via a non-intrusive method such as with an oscilloscope (since I don't have any). The program could be quite complicated with many delays inside the main, but I chose a quite simple example to make things more simple. With some modification, I can use this in the real time system :

     I've mainly used:

    *sequential delays of size 100us, after each delay occurrence of interrupt is checked via 2 of the global variables comparisons.

    *2 global variables, 1 modified inside the interrupt handler, other is modified inside the main program.

    * goto command

    Thank you for all the support and help.

    // 23.12.2015 by Emrah E, special thanks to Amit and Cb1 in TI support
    // Original code is written by Jonathan W. Valvano. This a modified version: 
    // PF2 is the output LED toggling on/off with lower frequency as the time pass, unless a falling edge interrupt occurs
    // (via PF4). (~ at every 100us interrupt is checked.)
    #define NVIC_EN0_R (*((volatile unsigned long *)0xE000E100)) // IRQ 0 to 31 Set Enable Register #define NVIC_PRI7_R (*((volatile unsigned long *)0xE000E41C)) // IRQ 28 to 31 Priority Register #define GPIO_PORTF_DIR_R (*((volatile unsigned long *)0x40025400)) #define GPIO_PORTF_DATA_R (*((volatile unsigned long *)0x400253FC)) #define GPIO_PORTF_IS_R (*((volatile unsigned long *)0x40025404)) #define GPIO_PORTF_IBE_R (*((volatile unsigned long *)0x40025408)) #define GPIO_PORTF_IEV_R (*((volatile unsigned long *)0x4002540C)) #define GPIO_PORTF_IM_R (*((volatile unsigned long *)0x40025410)) #define GPIO_PORTF_RIS_R (*((volatile unsigned long *)0x40025414)) #define GPIO_PORTF_ICR_R (*((volatile unsigned long *)0x4002541C)) #define GPIO_PORTF_AFSEL_R (*((volatile unsigned long *)0x40025420)) #define GPIO_PORTF_PUR_R (*((volatile unsigned long *)0x40025510)) #define GPIO_PORTF_PDR_R (*((volatile unsigned long *)0x40025514)) #define GPIO_PORTF_DEN_R (*((volatile unsigned long *)0x4002551C)) #define GPIO_PORTF_AMSEL_R (*((volatile unsigned long *)0x40025528)) #define GPIO_PORTF_PCTL_R (*((volatile unsigned long *)0x4002552C)) #define SYSCTL_RCGC2_R (*((volatile unsigned long *)0x400FE108)) #define SYSCTL_RCGC2_GPIOF 0x00000020 // port F Clock Gating Control //SysTick Variables #define NVIC_ST_CTRL_R (*((volatile unsigned long *)0xE000E010)) #define NVIC_ST_RELOAD_R (*((volatile unsigned long *)0xE000E014)) #define NVIC_ST_CURRENT_R (*((volatile unsigned long *)0xE000E018)) void DisableInterrupts(void); // Disable interrupts void EnableInterrupts(void); // Enable interrupts long StartCritical (void); // previous I bit, disable interrupts void EndCritical(long sr); // restore I bit to previous value void SysTick_Init(void){ NVIC_ST_CTRL_R = 0; // disable SysTick during setup NVIC_ST_CTRL_R = 0x00000005; // enable SysTick with core clock } // global variable visible in Watch window of debugger // increments at least once per button press volatile unsigned long FallingEdges = 0;
    // global variable, which is updated after returning from the interrupt volatile unsigned long compareFallingEdges = 0; void EdgeCounter_Init(void){ SYSCTL_RCGC2_R |= 0x00000020; // (a) activate clock for port F FallingEdges = 0; // (b) initialize counter GPIO_PORTF_DIR_R &= ~0x10; // (c) make PF4 in (built-in button) GPIO_PORTF_DIR_R |= 0x04; // make PF2 output GPIO_PORTF_AFSEL_R &= ~0x14; // disable alt funct on PF4 & PF2 GPIO_PORTF_DEN_R |= 0x14; // enable digital I/O on PF4 & PF2 GPIO_PORTF_PCTL_R &= ~0x000F0F00; // configure PF4 & PF2 as GPIO GPIO_PORTF_AMSEL_R = 0; // disable analog functionality on PF GPIO_PORTF_PUR_R |= 0x10; // enable weak pull-up on PF4 GPIO_PORTF_IS_R &= ~0x10; // (d) PF4 is edge-sensitive GPIO_PORTF_IBE_R &= ~0x10; // PF4 is not both edges GPIO_PORTF_IEV_R &= ~0x10; // PF4 falling edge event GPIO_PORTF_ICR_R = 0x10; // (e) clear flag4 GPIO_PORTF_IM_R |= 0x10; // (f) arm interrupt on PF4 NVIC_PRI7_R = (NVIC_PRI7_R&0xFF00FFFF)|0x00A00000; // (g) priority 5 NVIC_EN0_R = 0x40000000; // (h) enable interrupt 30 in NVIC EnableInterrupts(); // (i) Clears the I bit } void GPIOPortF_Handler(void){ GPIO_PORTF_ICR_R = 0x10; // acknowledge flag4 FallingEdges = FallingEdges + 1; } // The delay parameter is in units of the 16 MHz core clock. (62.5 ns) void SysTick_Wait(unsigned long delay){ NVIC_ST_RELOAD_R = delay-1; // number of counts to wait NVIC_ST_CURRENT_R = 0; // any value written to CURRENT clears while((NVIC_ST_CTRL_R&0x00010000)==0) { // wait for count flag } } // 1600*62.5ns equals 0.1ms = 100us void SysTick_Wait100us(unsigned long delay){ unsigned long i; for(i=0; i<delay; i++) { if(compareFallingEdges==FallingEdges) // checking if interrupt occured or not in every 100us { SysTick_Wait(1600); // wait 100us }else { break; } } } //debug code unsigned long delay; int main(void){ EdgeCounter_Init(); // initialize GPIO Port F interrupt SysTick_Init(); // initialize SysTick Counter ZTART: GPIO_PORTF_DATA_R &= ~0x04; // clearing what is written on PF2, if an interrupt occurs // system returns here compareFallingEdges=FallingEdges; // when compareFallingEdges=FallingEdges, system is ready // for a new interrupt delay = 1; // reseting the delay after the interrupt while(1){ if(compareFallingEdges==FallingEdges) // checking if an interrupt occured or not { SysTick_Wait100us(delay); GPIO_PORTF_DATA_R ^= 0x04; }else { goto ZTART; // if interrupt occurs, program goes back to where ZTART is } delay *= 2; // reducing the PF2 frequency as the time pass } }


  • emrah erden said:
    the most efficient solution may require modifying the LR's behavior

    and

    emrah erden said:
    After reading your posts and doing some trial and error, I have came up with a solution.

    Good job my friend - and I'm sure both vendor's Amit & (outsider) cb1 & crue are most impressed w/your "willingness" to perform "well thought & disciplined - Trial & Error!"

    Mastering such logical methods is sure to aid you (both) "on the future job or in expanding your hobby capability!"   Again - well done.

    I've one (just one!) slight squawk w/your method (perhaps more so w/your explanation.)   Below your writing & my (slight) protest:

    emrah erden said:
    If a falling edge interrupt occurs via PF0, program returns back to ZTART position. (ZTART is outside the while loop.)

    Now that may (appear) to be true - yet the program - upon completing the interrupt handler - "must" return to the program location which was stored w/in "LR" at the onset of the interrupt.   I'm unsure if we can "defeat and/or modify" this behavior (or if we want to!)  {this the gospel according to "saint cb1" - which never sounds right...}

    Now as you note - this (interrupt) return (to a program location - potentially anywhere w/in your code) occurs quickly - and may be (little) noted - but I assure you - it occurs!   And I continue to "stand" with my belief that w/out "capturing & then modifying LR" - all alternate methods will suffer this (random, non-deterministic) behavior.   (that is so as your users may push that "button" at any time - or any place - w/in your code - and it is that specific code location - to which the interrupt will return - unless we alter "LR.")

    I'm more a biz & finance/legal guy than program coder - I know I've done this years ago (w/8051 & Z8) - and noted/hoped that, "Even a blind squirrel (cb1 - not you!) will (occasionally) blunder upon an acorn...(or program solution)"   Bon chance mon ami.

  • Dear Cb1

    1) About the quotation: "If a falling edge interrupt occurs via PF0, program returns back to ZTART position. (ZTART is outside the while loop.)". It was a faulty, and incomplete argument. You are completely correct about the objection.

    A more correct side note could be: After the interrupt, program moves to GPIOPortF_Handler, in there change the global variable "FallingEdges". And it returns back to its original position at onset of the interrupt. At worst condition it will wait 100us. Later, compare FallingEdges and compareFallingEdges variables, and finally will go to ZTART position.

    2) I was looking for an answer, which is applicable even by a newbie like me. Modifying the LR must be the best solution, but I lack the knowledge to apply it into my code. Sometimes getting 70 is much better than getting nothing, while just focusing to get all. Anyway I'm just a hobbyist, non-deterministic but very short delays will not affect my programs in a very bad way unless they stack up (so I will try to avoid from that formation).

    You and Amit were always been there when I had a problem with TM4C and electronics. All my problems were solved all the time, thank you friends.
  • My friend Emrah,

    You display great kindness - yet it's Amit who is "always" there.   I'm here sporadically - in satisfaction of "community service" - and strive to help when/where and if able.

    Indeed for your application the method you've adopted will work.   Yet there will be (some) here who (will) require the direct return to (other) than the program location which existed at the "moment of interrupt."   And that "need" nicely over-lapped yours - thus I "investigated."   (not w/success, yet - but the day is young - and motivation strong...)

    Amit & other helpers always appreciate a thoughtfully considered post - that was yours - and if you can maintain that care & diligence I predict great progress for you.

    BTW - you display great organization in your writing.   You present findings & requirements thoughtfully - in good order - and appear careful not to (mix) "desires" w/"findings."   And you clearly identify your objective - which my firm enforces by my demand that each/every crue member (to include moi) has a "current sticky" at the ready - which lists top 3 goals - for each "a.m./p.m." session.   Each of us (better) have confined our efforts (thus focus) to one of those "Big 3!"   Of course there will always be (non-top 3) "items of interest" - and we "scratchpad those for later" - thus maintaining our focus (while capturing the novel)  - which best insures we meet our objectives...   Smart people - especially smart people - have many interests.   "Holding the ship to course" while noting (and logging) "treasure islands" is my role as small biz owner...

    Again good job - take care...

  • Sorry friend, didn't realize that you are working on the problem. As you can see I haven't yet marked an answer as correct yet, also my solution was not the most efficient one. So no need to rush, ship is at course, but extra treasure is always welcomed ;)

    Send my best wishes to the good old dog-eared companion, waiting for your return from the kingdom of registers