3

I'm working on an embedded project in C on a stm32f4xx uC.

I have a portion of a code that does an loop-operation XYZ continuously, and from time to time a TIM4 interrupt changes some global parameters and causes the operation XYZ to restart.

code is something like this:

for (;;) {
       //line A
    XYZ;
       //line B
}

XYZ is a complex operation involving tranfers of data between buffers and others.

The TIM4 interrupt handler does this: stops XYZ & changes some globals that afect XYZ operations.

So basically I want XYZ to execute repeatedly and TIM4 interrupt to stop XYZ, change the parameters and then the loop must restart by restarting XYZ with the new global parameters.

PROBLEM IS: Since XYZ has many instructions, TIM4 IRQ may come right in the middle of it and, after the IRQHandler changes the globals, the operations resume from the middle of XYZ which ruins the program.

MY INITIAL SOLUTION: Disable interrupts on line A with __disable_irq() and restore them on line B with __enable_irq()

Fails because the XYZ complex operation must use other interrupts (other than TIM4).

NEXT SOLUTION Disable only TIM4 interrupt on line A with:

TIM_ITConfig(TIM4, TIM_IT_Update , DISABLE) 

and enable it back on line B with:

TIM_ITConfig(TIM4, TIM_IT_Update , ENABLE)

Fails because I am losing the interrupt: when the int is restored, the interrupt that arrived during XYZ is ignored. This is a big problem (one of the reasons is that TIM4 IRQHandler changes the globals and then activates the TIM4 again to give an interrupt later, I do this because the period between interrupts varies).

Can anyone give me a solution to this problem? Is there a better way to disable/restore TIM4 IRQ and NOT lose any interrupt?

1
  • For what reason does XYZ need to stop processing? Is it because its data is invalid due to the interrupt overwriting info, or because it is actually desired to stop XYZ whenever a new interrupt comes in? If it's just due to data overwriting, in the ISR you could copy data into a temporary and set a flag, then in the main loop use the flag to know when to operate on the new data... Commented Dec 11, 2012 at 15:27

5 Answers 5

1

You could operate on a copy of the global variables and swap in the new value from the interrupt once you're done with XYZ.

It's not clear from the question whether you need to stop processing of XYZ immediately when the globals change, or if you can wait till XYZ finishes processing to swap in new copies of the variables. I'll operate under the assumption that you need to break out of processing XYZ but it's easy enough to not.

Code would look something like this:

volatile int x;
int x_prime;

int main(void)
{
    while(1)
    {
        //copy in new global parameter value
        x_prime = x;

        while(1)
        {
            //do stuff with x_prime
            if (x_prime != x)
            {
                break;
            }

            //do more stuff with x_prime
            if (x_prime != x)
            {
                break;
            }
        }
    }
}

// interrupt handler
void TIM_IT_Update(void)
{
    x++;
}

The break patterns assume that you're not changing x_prime. If you need to modify x_prime, you'll need another copy.

Since the interrupt is never disabled, you never have to worry about losing any of them. And since you're operating on a copy of the parameters changed by the interrupt, it doesn't matter if the interrupt changes the parameters in the middle of execution because you're not looking at those values until you make copies.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the hint, but it's not suitable for me because I have a complex system of buffers and global variables that are altered by the IQRHandler and that XYZ operates with. And to make it clear, I would prefer to finish XYZ and then process the IRQ.
1

There's a few options potentially available (I'm not 100% on ARM architecture):

  • Alter the interrupt priority/level mask register to only mask off TIM4, leaving other interrupts to happen. Hopefully, if TIM4 is fired whilst masked, on restoring the level mask it will remember & fire the ISR.
  • Mask off interrupts and manually check for the TIM4 interrupt flag being set during XYZ
  • Break XYZ into smaller sections and only mask off TIM4 when absolutely necessary.
  • Operate on a copy of the data, optionally checking the TIM4 interrupt flag to decide whether to continue/keep the result or to discard & restart.
  • Check the time & avoid starting XYZ if TIM4 is likely to fire soon, or only run XYZ N times after TIM4 fires.

3 Comments

Hi, the first option you mentioned is exactly what I had in mind, but my question was also HOW I could do that. Anyway, right now I implemented a work-around, by declaring a global flag who's value is changed by the IRQ (the IRQ does nothing more than changing the global flag) and inside the loop, after finishing XYZ, I test the flag and if it's set I do the original IRQ Handler operations and then I reset the flag. It works like a charm! However I would like to know if there's a way to mask only the TIM4 IRQ but in the same time not lose the IRQ. Thanks for the tips!
Option 1 can be done by setting TIM4 to a low priority, and then setting the global interrupt priority mask / level (I don't know what this would be on ARM) to ignore interrupts that low down, making the ones you still care about high enough they're not masked. See: stackoverflow.com/questions/13713064/… for more vague guidance ;)
@BogdanAlexandru: I doubt that the ARM will ignore interrupts when masked. The bit is still set, but the ISR not triggered. I assume it will at least have a buffer of 1 interrupt, but that might not be good enough as it seems the XYZ is a very time demanding function. Maybe it is possible to set the depth of this buffer in the NVIC? I might be wrong here, but have you check the documentation for this?
1

When I find myself in a similar situation, where processing may take longer time than the interruption period, I use a FIFO to detach the processing from the incoming data. I.E: TIM4 fills a FIFO. XYZ (or a manager) consume the FIFO and process the data.

(I feel that your design may be wrong, since you shouldn't be using globals to control the data or process flow. Book reference for study on the matter: Making Embedded Systems: Design Patterns for Great Software)

Comments

0

Before XYZ make a copy of everything from the buffer and work with copies. I believe it's the best way, helped during writing a gps parser.

Comments

0

Have you considered using a RTOS for your system? I realize that would require some restructuring, but it may provide you the task handling flexibility and resolution you need for your system. If you're using a STM32's CubeIDE, enabling, configuring, and getting running with a RTOS is fairly straightforward.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.