What is wrong when using interrupt handlers as event listeners

My system is simple enough that it works without an OS, I just use interrupt handlers, as I would use an event listener in a desktop program. In everything that I read on the Internet, people try to spend as little time as possible on interrupt handlers and return control to tasks. But I do not have an OS or a real system of tasks, and I can not find design information on objects without an OS.

I have basically one interrupt handler that reads a piece of data from USB and writes data to memory, as well as one interrupt handler that reads data, sends data to GPIO and signs on a hardware timer.

What is wrong with using interrupts like me and using NVIC (I use cortex-M3) to manage the hierarchy of work?

+5
source share
5 answers

First of all, in the context of this question, let me refer to the OS as a scheduler.

Now, unlike threads, interrupt service routines are the “above” scheduling scheme.

In other words, the scheduler does not have “control” over them.

ISR enters execution as a result of the HW interrupt, which sets the PC to a different address in the code section (more precisely, to the interrupt vector, where you "do a few things" before calling the ISR).

Therefore, essentially the priority of any ISR is higher than the priority of the stream with the highest priority.

Thus, one obvious reason to spend as little time as possible on ISRs is the “side effect” that ISRs have on the planning scheme that you are developing for your system.

Since your system is exclusively controlled by interrupts (i.e. there is no scheduler and no threads), this is not a problem.

However, if nested ISRs are not allowed, interrupts must be disabled from the moment the interruption occurs and until the corresponding ISR is complete. In this case, if any interruption occurs during the execution of the ISR, your program will effectively ignore it.

So, the longer you spend inside the ISR, the higher the likelihood that you will “skip” the interrupt.

+6
source

In many desktop programs, events are sent to the queue, and there are several event loops that process this queue. This event loop processes an event by event, so it is not possible to interrupt one event with another. It is also a good practice in event-based programming to keep all event handlers as short as possible because they are not interrupted.

In open metal programming, interrupts are similar to events, but they are not queued.

  • the execution of interrupt handlers is not sequential; they can be interrupted by a higher priority interrupt (numeric number in Cortex-M3)
  • there is no queue with the same interrupts - for example, you cannot detect multiple GPIO interrupts while you are in this interrupt - this is why you should have all the routines as short as possible.

You can implement the queues yourself, feed these queues by interrupt, and consume these queues in your super-cycle (consume when all interrupts are disabled). With this approach, you can get sequential interrupt handling. If you keep your handlers short, this is mostly unnecessary, and you can directly work with handlers.

OS-based systems also have good practice in that they use queues, semaphores, and “interrupt handler tasks” to handle interrupts.

+3
source

Bare metal is ideal for binding applications or interrupts / events as long as you conduct analysis. Therefore, if you know what events / interruptions come at what price, and you can guarantee that you will process all of them in the desired / calculated period of time, you can certainly not rush to the event / interrupt handler, and not be fast and send the flag to the foreground task.

The general approach, of course, is to quickly enter and exit, while preserving enough information to process things in the foreground task. The foreground task is to spin its wheels, looking for event flags, prioritization, etc.

Of course, you could make this more complicated, and when an interrupt / event occurs, save the state and return to the forground handler in ground mode, and not in interrupt mode.

Now that all this is common, but specific to the dagger-m3, I don’t think that there really are modes like ARM with a big brother. As long as you use the real-time approach and make sure that your handlers are deterministic and you perform your system engineering and ensure that there are no situations where events / interrupts add up so that the answer is not deterministic, not too much late or too long or lose things all right

+2
source

What you should ask yourself: all events can be services on time under any circumstances:

For instance:

  • If your interrupt system was started before completion, would servicing one interrupt cause an unacceptable delay in servicing another?
  • On the other hand, if the interrupt system is priority and preventive, will the high priority interrupt service be unacceptably delayed lower?

In the latter case, you can use Rate Monotonic Analysis to prioritize for maximum responsiveness (the shortest run-time handlers get the highest priority). In the first case, your system may not have a certain degree of determinism, and the performance will be variable when loading events and changing code.

One approach is to divide the handler into critical and non-critical sections in real time, a critical time code can be executed in the handler, and then a flag to trigger a non-critical action that should be performed in the "background" context without interruption in the system with a large loop that just checks for event flags or shared data to complete the job. Often, all that may be required in the interrupt handler is copying some data to a timestamp so that some event-creating events are available for background processing without supporting the processing of new events.

For more complex scheduling, there are a number of simple, inexpensive, or free RTOS schedulers that provide multitasking, synchronization, IPC and synchronization services with very small fingerprints and can run on very inexpensive hardware. If you have a hardware timer and 10 KB of code (sometimes less), you can deploy RTOS.

+2
source

I take your described problem first

As I understand it, your goal is to create a device that, receiving commands from USB, outputs some GPIOs, such as LEDs, relays, etc. For a simple this task, your approach seems to be (if the USB layer can work with it properly).

There is a priority problem, although in this case it may happen that if you reboot the USB drive (with data from the other end of the cable), and interrupt handling will have a higher priority than the timer, GPIO processing, the GPIO side may skip marks ( like other explanations, interrupts cannot be queued).

In your case, this concerns what might be considered.

Some general recommendations

For "as little time as possible in an interrupt handler," the rationale is what others said: the OS can implement a queue, etc., however, hardware interrupts do not offer such concepts. If an event causing an interrupt occurs, the processor enters your handler. Then, until you process its source (for example, reading the receive reception register in the case of UART), you will lose all subsequent events of this event. After this moment, before exiting the handler, you can get whether the event occurred, but not how many times (if the event happened again when the processor was still processing the handler, the associated interrupt line is activated again, so after you return from the handler, the processor immediately returns it if nothing expects a higher priority).

Above, I described the general concept observed on 8-bit processors and 32-bit AVR (I have experience with them). A.

When developing such low-level systems (without an OS, one "background" task and some interruptions), it is important to understand what happens at each priority level (if you use one). In general, you do the most critical tasks in real time with the highest priority, taking care to quickly serve those who are more relaxed with lower priorities.

From another aspect, as a rule, at the design stage, you can plan how the system should respond to missed interrupts, since where there are interruptions, in any case there will be one missing. The critical data passing through the communication lines must have adequate checksums, especially the critical timer must be obtained from the counter register, and not from event counting, etc.

Another unpleasant part of interrupts is their asynchronous nature. If you fail to correctly design the associated locks, they will ultimately ruin something giving nightmares to this poorest soul who will have to debug it. "Spend as little time as possible in the interrupt handler," it is also recommended that the interrupt code be short enough, which means that less code is also needed for this problem. If you also worked with multitasking with the help of RTOS, you should know this part (there are some differences: the higher priority code for the interrupt handler does not need to be protected from the handler with a lower priority).

If you can correctly design your architecture with respect to the necessary asynchronous tasks, dispense with the OS (due to the lack of multitasking) may be even the best solution. He needs to think more about how to design it correctly, but in the future there will be much less problems with locking. I overcame some critical security projects developed within the framework of a single “task” with few interruptions, and the experience and maintenance requirements for these (especially bug tracking) were quite satisfactory compared to some other multitasking built in the company .

+2
source

Source: https://habr.com/ru/post/1202676/


All Articles