3
votes

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?

5
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... - Ross

5 Answers

1
votes

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.

1
votes

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.
1
votes

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)

0
votes

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.

0
votes

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.