3
\$\begingroup\$

I am developing an UART protocol to ensure communication between two boards (Master board and Slave board). Slave board includes many sensors and actuators and Master board shall command this board by a set of commands like GET_SENSOR_VALUE and SET_MOTOR_SPEED etc ... Many functions shall be executed in my project and a permanent control of sensors values shall be done (Example: Master board shall get temperature value each 5 seconds, it shall get Battery's level each 5 seconds, it shall enable ventilation when temperature is high ...). All equipment are connected to Slave board (sensors and actuators) and the main role of Master board is monitoring.

I've been reading some articles about specifications and embedded systems design and figured out that the first thing to do before starting is to choose the kernel: RTOS or GPOS (Bare metal).

My question is: In my case, if I choose RTOS (in Master board), Should I implement my needs as separate tasks/functions?

  • temperature_task
  • humidity_task
  • battery_task
  • motor_task
  • ventilation_task

If yes, does UART support high speed context switch time? example:

00:00 temperature_task : GET_TEMPERATURE if temp>threshhold send START_VENTILATION
00:01 battery_task: GET_LEVEL if level < threshhold send TURN_OFF_SYSTEM
00:02 humidity_task: GET_HUMIDITY if level < threshhold send DO_SOMETHING
00:03 temperature_task : GET_TEMPERATURE if temp>threshhold send START_VENTILATION
00:04 battery_task: GET_LEVEL if level < threshhold send TURN_OFF_SYSTEM
00:05 humidity_task: GET_HUMIDITY if level < threshhold send DO_SOMETHING

\$\endgroup\$
  • \$\begingroup\$ it depends .. you can get each data by different tasks, but then you have to use some way to block the usage of the uart when another task is using the uart hardware (mutex for example) ... i'd do it in the same task, if each data must be retrieved separately then I'd implement a state machine .... about the contexto switching, depending on the microcontroller I'd either use DMA for sending and receiving data or block interruptions during the short time frame it's getting data ... I'd end up using DMA it's much easier \$\endgroup\$ – morcillo Mar 20 '18 at 17:07
  • 1
    \$\begingroup\$ Beware that having lots of trivial threads will use up RAM with a unique stack and tracking data for each. If you can run non-incompatible operations on a smaller number of threads, either by some lighter weight work unit mechanism or by stringing them together yourself, you'll save that - for example, have only one execution thread for all the sensors. Also you pretty much need to give the UART to one thread which mediates access; otherwise the time it takes to send or receive will either make an all-but-unsolvable consistency mess or cause you to slow to a crawl waiting on mutexes. \$\endgroup\$ – Chris Stratton Mar 20 '18 at 18:03
  • 1
    \$\begingroup\$ You don't justify the need for an RTOS - I would go without given your problem statement. If it's just for the joy of it, then by all means go, but your question should include a bit of a rationale. This will help you refine your actual question about tasks and the separation of concerns. \$\endgroup\$ – Tibo Mar 20 '18 at 20:20
2
\$\begingroup\$

Yes, it is generally a GREAT idea to separate functionality in this manner when an RTOS is in system. Great care needs to be taken to ensure mutual exclusion between tasks and shared resources such as common memory or peripherals as to avoid race conditions and unpredictable behavior. A common approach to this problem is by blocking each peripheral behind a "gate-keeper" task that manages the data flow in and out, typically issuing a callback function to the caller when the expected data has been received. Read this article (Chapter 7 goes over this specifically) from the FreeRTOS documentation. That being said, the bare metal approach can be perfectly adequate while having tighter coupling between functional modules of software.

\$\endgroup\$
  • 1
    \$\begingroup\$ If you have to lock a shared interface to avoid conflicting usage by different tasks, what benefit are you actually getting from paying for a separate stack for each sensor task? You're just adding a lot of overhead to re-creating serial execution, only with more complexity and less determinism. Tasks make sense when you have a complex ongoing operation that only rarely interferes with others; and when you can't readily refactor it as an event-driven state machine. \$\endgroup\$ – Chris Stratton Mar 20 '18 at 22:13
  • \$\begingroup\$ Agreed, you should always be able to reduce the software complexity to fit the application and should always be the first choice, however when (inevitably) new features need to be added or behavior modified, the separate task approach lends itself to more modular design and fast development. But you are correct in that to ensure deterministic behavior there is alot of overhead introduced. \$\endgroup\$ – Luke Gary Mar 20 '18 at 23:01
2
\$\begingroup\$

Normally UART is bidirectional (RX + TX wire), so you can send/receive without the need to switch.

I don't have (Arduino) experience with RTOS, however, splitting up functionality in functions is always a good thing to do. In this reasonable simple case you don't need tasks or an RTOS. This will mostly complicate the UART access and necessary data sharing between tasks.

About your 'timing' requirements: a second is a huge amount of time, even for a simple microcontroller like an Arduino. You probably can use both bare metal Arduinos (unless you plan to use complicated calculations/schemes), assuming you can fit all sensors to an Arduino (or Mega?).

If you make this fixed (e.g. always 20 bytes for accomodating all sensor values), you don't need a newline/end of message byte.

About the protocol: why not sending every x ms all data from all sensors as one package to the controller (slave to master). And the master can send when needed (on the fly for low latency) short messages like "Turn off system" or specific commands (including parameters).

Example of commands from slave to master:

Byte   Meaning
0      Temp sensor
1      Humidity level
2      Battery level (?)
3      ... other sensors

Example of commands from Master:

Byte 0    Byte 1
Command   Parameter    Meaning
  0         n.a.      Turn off system 
  1         n.a.      Turn on system
  2         0-255     Move servo to loc x (as example)

etc.

\$\endgroup\$
  • 1
    \$\begingroup\$ Tasks are very different from functions. Functions cost next to nothing, because the compiler can inline where it makes sense. In contrast, tasks are memory expensive - typically they are independent threads each needing their own stack, sometimes even constructs larger than a thread comparable to a desktop OS "process". \$\endgroup\$ – Chris Stratton Mar 20 '18 at 18:05
  • 1
    \$\begingroup\$ Beyond that mistake, this doesn't really seem to address the question being asked at all. \$\endgroup\$ – Chris Stratton Mar 20 '18 at 18:24
  • \$\begingroup\$ @ChrisStratton I was not talking about tasks, but about functions. However, I will change my answer to make it more clear. I think it answers the question at least partly (what protocol could be used), this is independent for tasks (one task suffices). \$\endgroup\$ – Michel Keijzers Mar 20 '18 at 20:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.