0

I am trying to program stm32 and use event driven architecture. For example I am going to toggle a pin when timer interrupt occurs and transfer some data to external flash when ADC DMA buffer full interrupt occurs and so on..

There will be multiple interrupt sources each with same priority which disables nesting.

I will use the interrupts to set a flag to signal my main that interrupt occured and process data inside main. There will be no processing/instruction inside ISRs.

What bothers me is that accessing a variable(flags in this case) in main and ISRs may cause race condition bug in the long run.

So I want to use an circular event queue instead of flags.

Only ISRs will be able to write to event queue buffer and increment "head". Only main will be able to read the event queue(and execute instructions according to event) and increment "tail".

Since ISR nesting is disabled and each ISR will access different element of event queue array and main function will only react when there is new event on event queue, race condition is avoided right? or am I missing something?

Please correct me if I am doing something wrong.

Thank you.

NOpE
  • 1
  • 1
  • 2
    This is not a true multithreaded environment, so a race condition might be really applicable only between the main code and the ISR. But it is easily solvable by disabling the interrupts in the critical section of the main code. – Eugene Sh. Mar 24 '21 at 20:11
  • Do `volatile int flag;` [global] and, in `main`, do: `int copy; cli(); copy = flag; flag = 0; sti(); if (copy) ...` The ISR can simply set `flag` as desired. – Craig Estey Mar 24 '21 at 20:18
  • You've explained the solution clearly enough, but you have not really explained the problem you are trying to solve. Describe the race condition scenario that will cause an error, because it is not clear what you mean. You could use bit-banding to read and clear flags atomically rather than using read-modify-write of event flags, or use separate Booleans. All having a queue will do, is you will process in the order the interrupts were processed, but that is non-deterministic because you have disabled preemption. It also sounds like you have invented a hypothetical problem. – Clifford Mar 24 '21 at 21:49
  • Sounds good to me. I often do this but with a simple multithread tasker where I can signal a semaphore to indicate that an event has been loaded onto the queue and so needs attention from the I/O handler thread:) – Martin James Mar 25 '21 at 01:34
  • @EugeneSh. I read somewhere that enabling and disabling interrupts causes big delays in interrupt response times. I don't know if its true but if it is, I need fast response from interrupts. – NOpE Mar 25 '21 at 15:08
  • @NOpE Quantify it, measure the impact and decide if it is good enough. I doubt that disabling interrupts for setting a flag as suggested will have any noticeable effect. – Eugene Sh. Mar 25 '21 at 15:10
  • @Clifford For example; I set a flag inside ISR and check the flag and reset it inside my main. If another interrupt has occured while I was resetting the flag inside the main, this may cause a race condition thus second interrupt will be lost. – NOpE Mar 25 '21 at 15:13
  • @NOpE You can't "lose" interrupts, at least as long as these are different interrupt sources (not sure if there is a FIFO for the same interrupt though). They are latched and will be taken once the global interrupts are re-enabled. You might want to read this: https://electronics.stackexchange.com/questions/216364/what-happens-on-stm32-when-multiple-uart-interrupts-are-triggered-at-the-same – Eugene Sh. Mar 25 '21 at 15:23
  • @NOpE : Your "for example" needs to be in the question. Generally on SO requests for clarification should be responded to by clarifying the question, not adding a comment. To answer your point - not if your flag set/clear operations are atomic. Which they will be if you use separate atomic flags or bit-banding as I suggested. An event queue would be useful if were possible say for a second interrupt from the same source to occur before you processed the first - however a simple counter would do the same thing if there were no associated data to queue as well as the event itself. – Clifford Mar 25 '21 at 15:39
  • My advice is don't go looking for hypothetical problems and trying to solve them generically. Your hypotehesis may not be correct (as in this case) and any real problems are better resolved specifically rather then generically because not all interrupt sources are the same. – Clifford Mar 25 '21 at 15:46
  • I would say that your rather simplistic approach to interrupt handling has little merit. If all an interrupt does is set a flag that is polled by the main thread, it would be far more efficient to simply poll the peripherals' hardware interrupt flags directly and switch interrupts off altogether. All interrupting peripherals have a register flag that is set on events even if the interrupts they drive are disabled. The only place it would make sense is if you had an RTOS with multiple thread handlers - in which case you'd use an RTOS event flag to trigger the threads rather than polling. – Clifford Mar 25 '21 at 15:47
  • @Clifford CPU is going to sleep if there is no tasks to do. In order to wake up from low power mode I have to use interrupts and ISRs. So polling interrupt flag method may not work for my case. – NOpE Mar 25 '21 at 18:53

1 Answers1

0

If the interrupt only sets a variable and nothing gets done until main context is ready to do it then there is really no reason to have an interrupt at all.

For example: if you get a DMA complete hardware interrupt and set a variable then all you have achieved is to copy one bit of information from a hardware register to a variable. You could have much simpler code with identical performance and less potential for error by instead of polling a variable just not enabling the interrupt and polling the hardware flag directly.

Only enable the interrupt if you are actually going to do something in interrupt context that cannot wait, for example: reading a UART received data register so that the next character received doesn't overflow the buffer.

If after the interrupt has done the thing that cannot wait it then needs to communicate something with main-context then you need to have shared data. This will mean that you need some way of preventing race-conditions. The simplest way is atomic access with only one side writing to the data item. If that is not sufficient then the old-fashioned way is to turn off interrupts when main context is accessing the shared data. There are more complicated ways using LDREX/STREX instructions but you should only explore these once you are sure that the simple way isn't good enough for your application.

Tom V
  • 4,827
  • 2
  • 5
  • 22
  • CPU is going to sleep if there is nothing to do in order to save power so I have to use interrupts to wake the CPU up. So I cannot use polling in main method. – NOpE Mar 25 '21 at 15:06