MCU architecture based on events or polls?

I have experience writing embedded systems based on events and polls (for tiny MCUs without a pre-installed OS).

In an event-based system, tasks typically receive events (messages) in a queue and process them in turn.

In a system based on surveys, tasks examine the status at a certain interval and respond to changes.

Which architecture do you prefer? Can coexist?

UPDATE: POINT EVENTS

SURVEY BASED ON
- Close coupling related to synchronization aspects (@Lundin)
* Can coexist next to the event system using queues (@ embedded.kyle)
* Good for small programs (@Lundin)

EVENT EVENTS
+ More flexible system in the end (@ embedded.kyle)
- RTOS version adds complexity (@Lundin)
* Small programs = state-run machines (@Lundin)
* Can be implemented using queues and a "super-loop" (inside the controller / main) (@ embedded.kyle)
* Only true "events" are hw interrupts (@Lundin)

RELATED QUESTIONS
* Looking for a comparison of different scheduling algorithms for a state machine (@ embedded.kyle)

RELATED INFORMATION
* "Prefer to use active objects instead of bare threads" (@Miro)
http://www.drdobbs.com/parallel/prefer-using-active-objects-instead-of-n/225700095
* "Correctly use Threads = Isolation + Asynchronous Messages" (@Miro) http://www.drdobbs.com/parallel/use-threads-correctly-isolation-asynch/215900465

+6
source share
3 answers

On the bare-bone MCU platform, there really is no such thing as an “event-driven”, even though the writing words are trying to tell you. The only real events you can get are hardware interrupts.

Depending on the nature of the application and its real-time requirements, interruptions may or may not be appropriate. As a rule, it is much easier to achieve deterministic real-time using a polling system. However, systems that rely solely on polling are very difficult to maintain because you get a tight connection between all aspects of synchronization.

Suppose you are trying to start an LCD that is slow. Instead of re-polling a certain timer when burning processor cycles in an empty cycle, you might decide to get some data on the bus at the same time. And then you want to print the data received on the LCD. This design created a tight connection between the LCD start-up time and the serial bus and another tight connection between the serial bus and data printing. From an object-oriented point of view, these things are generally not related to each other. If at some point in the future you accelerated the serial bus, then suddenly you encounter LCD printing errors because it did not finish working when you tried to print on it.

In a small program, polling is great, as in the example above. But if the program has growth potential, a survey will make it very difficult, and a tight connection will ultimately lead to many strange and fatal errors.

On the other hand, multithreading and RTOS adds quite a lot of additional complexity, which, in turn, can also lead to errors. Where to draw a line is not so easy to determine.

From personal experience, I would say that any program with a size of less than 20-30 thousand LOC will not use planning and multitasking, in addition to simple state machines. If the program is larger, I would consider a multi-tasking RTOS.

In addition, low-performance MCUs (8- and 16-bitters) are far from suitable for starting the OS. If you find that you need an OS to handle complexity on an 8- or 16-bit platform, you probably chose the wrong MCU to start with. I would be skeptical of any attempts to present the OS on anything less than 32-bit.

+4
source

In fact, event-driven programming and threads can be combined, and the resulting template is commonly known as “active objects” or “actors”.

Active objects (actors) are encapsulated, controlled by events that process each other asynchronously, reporting events to each other. Active objects process all events in their execution thread (at least conceptually, if a shared scheduler is used), so they avoid developing the most dangerous concurrency threats.

Actors and active objects are all the rage (again) in general computing (you can search for Erlang, Scala, Akka). Herb Sutter has written some good articles that explain the “active object” pattern: “Prefer to use active objects instead of a bare topic” (http://www.drdobbs.com/parallel/prefer-using-active-objects-instead -of-n / 225700095) and "Use Threads = Isolation + Asynchronous Messages Correctly" (http://www.drdobbs.com/parallel/use-threads-correctly-isolation-asynch/215900465)

Here's what Herb says in the first of these articles:

"The use of raw threads is directly related to many reasons ... Active objects significantly improve our ability to reason about our code and stream operations, providing us with higher-level abstractions and idioms that enhance the semantic level of our program and allow us to express our intentions directly. How and in all good templates, we also get the best vocabulary to talk about our design.Note that active objects are not new: UML and various libraries provide support active classes

So, all this is really not new. But what is perhaps less well-known, especially in the embedded system community, is that active objects are not only fully applicable to embedded systems, but are actually ideal for embedded systems, and they are lighter than traditional RTOS.

I have been using event-driven active objects for more than a decade and have created a family of active QP objects for embedded systems (see http://www.state-machine.com/ ). I would never go back to the Super Loop poll or raw RTOS.

+3
source

I prefer which architecture is best for the application.

Both can coexist in a multi-level queue architecture. One queue operates on the basis of a survey, which operates in the main loop. While the other, most likely given with higher priority events, works using interrupt-based preventive intervention.

See my answer to this SO question for a more detailed explanation and comparison of various scheduling algorithms.

+1
source

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


All Articles