EmbeddedRelated.com
Forums
The 2024 Embedded Online Conference

Task priorities in non strictly real-time systems

Started by pozz January 3, 2020
I have always worked on non real-time systems, i.e. when the reaction of 
the system to an event should occur in a reasonable time. For example, 
when the user presses a button, a motor should start rotating. If the 
motor starts after 100us or 100ms is not so important.

I never used RTOS, so the architecture of my firmware is based on 
"superloop technique" (background tasks running in the loop and interrupts).

while(1) {
   task1();
   task2();
   ...
}

All the tasks don't block, ever. As a rule of thumb, I accept a block 
time of maximum 100us-1ms. When the task needs to block for a greater 
amount, I try to implement it as a state-machine, avoiding blocking.

The ISRs are very lightweight: they only set/get some flags or push/pop 
a byte to/from FIFO queues.
With 32-bits MCUs (modern Cortex-M MCUs), I can change 32-bits variables 
(mainly the ticks of the system) in ISRs without caring of 
race-conditions that could occur on 8-bits MCUS when the background 
tasks access the same variables.

In the past I used this architecture with success even in medium-complex 
systems featuring Ethernet, lwip, mbedTLS, USB and touchscreen (emWin).

Sincerely I think this architecture is good enough for all non real-time 
systems, so I don't understand why to use a RTOS in those cases.
However I need to use a RTOS (FreeRTOS) for the next project, because it 
is one of the requirement. It isn't a real-time system, but the RTOS is 
required.

I think I can convert my architecture to RTOS by creating a task for 
each of the function I call in the superloop and starting the OS 
scheduler. However now the task function can't return, so I can write it 
in the following way:

void task1_main(void) {
   while(1) {
     task1();
   }
}

task1() can be the *same* function of the superloop architecture.

I can assign each task the same priority: in this case, FreeRTOS will 
use round-robin scheduling, giving all the tasks the same opportunity to 
run.

Is it correct?
On 03/01/2020 14:41, pozz wrote:
> I have always worked on non real-time systems, i.e. when the reaction of > the system to an event should occur in a reasonable time. For example, > when the user presses a button, a motor should start rotating. If the > motor starts after 100us or 100ms is not so important. > > I never used RTOS, so the architecture of my firmware is based on > "superloop technique" (background tasks running in the loop and > interrupts). > > while(1) { > � task1(); > � task2(); > � ... > } > > All the tasks don't block, ever. As a rule of thumb, I accept a block > time of maximum 100us-1ms. When the task needs to block for a greater > amount, I try to implement it as a state-machine, avoiding blocking. > > The ISRs are very lightweight: they only set/get some flags or push/pop > a byte to/from FIFO queues. > With 32-bits MCUs (modern Cortex-M MCUs), I can change 32-bits variables > (mainly the ticks of the system) in ISRs without caring of > race-conditions that could occur on 8-bits MCUS when the background > tasks access the same variables. > > In the past I used this architecture with success even in medium-complex > systems featuring Ethernet, lwip, mbedTLS, USB and touchscreen (emWin). > > Sincerely I think this architecture is good enough for all non real-time > systems, so I don't understand why to use a RTOS in those cases.
RTOS's have their advantages and disadvantages. They can make it easier to guarantee particular timing requirements for high priority tasks, but make it harder for low priority tasks. They can make it easier to write individual tasks, but harder to write efficient inter-task data sharing. They can make it easier to modularise and separate the code, but harder to debug. An RTOS is /not/ necessary for real-time coding. Conversely, an RTOS can be useful even when you don't need real-time guarantees.
> However I need to use a RTOS (FreeRTOS) for the next project, because it > is one of the requirement. It isn't a real-time system, but the RTOS is > required. > > I think I can convert my architecture to RTOS by creating a task for > each of the function I call in the superloop and starting the OS > scheduler. However now the task function can't return, so I can write it > in the following way: > > void task1_main(void) { > � while(1) { > ��� task1(); > � } > } > > task1() can be the *same* function of the superloop architecture. > > I can assign each task the same priority: in this case, FreeRTOS will > use round-robin scheduling, giving all the tasks the same opportunity to > run. > > Is it correct?
You might be better using cooperative scheduling and : void task1_main(void) { while(1) { task1(); taskYIELD(); } } With cooperative scheduling, you know exactly when the current task can be changed - it can happen when /you/ want it to, due to a yield or a blocking OS call. With pre-emptive scheduling, you will have to go through your existing code and make very sure that you have locks or synchronisation in place for any shared resources or data.
On 2020-01-03 15:41, pozz wrote:
> I have always worked on non real-time systems, i.e. when the reaction of > the system to an event should occur in a reasonable time. For example, > when the user presses a button, a motor should start rotating. If the > motor starts after 100us or 100ms is not so important. > > I never used RTOS, so the architecture of my firmware is based on > "superloop technique" (background tasks running in the loop and > interrupts). > > while(1) { > � task1(); > � task2(); > � ... > } > > All the tasks don't block, ever. As a rule of thumb, I accept a block > time of maximum 100us-1ms. When the task needs to block for a greater > amount, I try to implement it as a state-machine, avoiding blocking.
... which can complicate the task's logical design, of course. That is the penalty you pay for the sequential-super-loop design. And the same (state-machine) approach has to be used if a task contains some long sequential computation that must be divided into shorter stages (in which case the state-machine approach can have a much worse impact on the task's design). Again, that is the price... [snip]
> Sincerely I think this architecture is good enough for all non real-time > systems, so I don't understand why to use a RTOS in those cases. > However I need to use a RTOS (FreeRTOS) for the next project, because it > is one of the requirement. It isn't a real-time system, but the RTOS is > required. > > I think I can convert my architecture to RTOS by creating a task for > each of the function I call in the superloop and starting the OS > scheduler. However now the task function can't return, so I can write it > in the following way: > > void task1_main(void) { > � while(1) { > ��� task1(); > � } > } > > task1() can be the *same* function of the superloop architecture.
A much simpler solution is to create *one* task that contains the *whole* superloop. An RTOS does not *require* multiple tasks.
> I can assign each task the same priority: in this case, FreeRTOS will > use round-robin scheduling, giving all the tasks the same opportunity to > run. > > Is it correct?
Probably not. If your task<n>() functions have some interactions through data (variables), your superloop design probably assumes that the functions are called in a certain order, or at least that they do not pre-empt each other. I am not very familiar with FreeRTOS, but to get round-robin scheduling of tasks of the same priority you probably have to insert "yield" points in the tasks (as David Brown's message explains) or you have to enable a pre-emptive time-slicing round-robin scheduling. Neither approach gives you a predictable ordering of the task<n> calls, and the time-slicing approach moreover allows the functions to pre-empt each other, which probably messes up the data-flow of your program. If the superloop design gives you sufficient performance and reactivity, just encapsulate it in a single task. -- Niklas Holsti Tidorum Ltd niklas holsti tidorum fi . @ .
On Friday, January 3, 2020 at 8:41:13 AM UTC-5, pozz wrote:
> I have always worked on non real-time systems, i.e. when the reaction of > the system to an event should occur in a reasonable time. For example, > when the user presses a button, a motor should start rotating. If the > motor starts after 100us or 100ms is not so important. > > I never used RTOS, so the architecture of my firmware is based on > "superloop technique" (background tasks running in the loop and interrupts). > > while(1) { > task1(); > task2(); > ... > } > > All the tasks don't block, ever. As a rule of thumb, I accept a block > time of maximum 100us-1ms. When the task needs to block for a greater > amount, I try to implement it as a state-machine, avoiding blocking. > > The ISRs are very lightweight: they only set/get some flags or push/pop > a byte to/from FIFO queues. > With 32-bits MCUs (modern Cortex-M MCUs), I can change 32-bits variables > (mainly the ticks of the system) in ISRs without caring of > race-conditions that could occur on 8-bits MCUS when the background > tasks access the same variables. > > In the past I used this architecture with success even in medium-complex > systems featuring Ethernet, lwip, mbedTLS, USB and touchscreen (emWin). > > Sincerely I think this architecture is good enough for all non real-time > systems, so I don't understand why to use a RTOS in those cases. > However I need to use a RTOS (FreeRTOS) for the next project, because it > is one of the requirement. It isn't a real-time system, but the RTOS is > required. > > I think I can convert my architecture to RTOS by creating a task for > each of the function I call in the superloop and starting the OS > scheduler. However now the task function can't return, so I can write it > in the following way: > > void task1_main(void) { > while(1) { > task1(); > } > } > > task1() can be the *same* function of the superloop architecture. > > I can assign each task the same priority: in this case, FreeRTOS will > use round-robin scheduling, giving all the tasks the same opportunity to > run. > > Is it correct?
This is why I prefer to code in an HDL where parallel tasks all run concurrently with no swapping of memory or sharing of resources. Life is so much simpler that way and systems are so less trouble prone. -- Rick C. - Get 1,000 miles of free Supercharging - Tesla referral code - https://ts.la/richard11209
On Fri, 3 Jan 2020 14:41:10 +0100, pozz <pozzugno@gmail.com> wrote:

>I have always worked on non real-time systems, i.e. when the reaction of >the system to an event should occur in a reasonable time. For example, >when the user presses a button, a motor should start rotating. If the >motor starts after 100us or 100ms is not so important. > >I never used RTOS, so the architecture of my firmware is based on >"superloop technique" (background tasks running in the loop and interrupts). > >while(1) { > task1(); > task2(); > ... >} > >All the tasks don't block, ever. As a rule of thumb, I accept a block >time of maximum 100us-1ms. When the task needs to block for a greater >amount, I try to implement it as a state-machine, avoiding blocking. > >The ISRs are very lightweight: they only set/get some flags or push/pop >a byte to/from FIFO queues. >With 32-bits MCUs (modern Cortex-M MCUs), I can change 32-bits variables >(mainly the ticks of the system) in ISRs without caring of >race-conditions that could occur on 8-bits MCUS when the background >tasks access the same variables. > >In the past I used this architecture with success even in medium-complex >systems featuring Ethernet, lwip, mbedTLS, USB and touchscreen (emWin). > >Sincerely I think this architecture is good enough for all non real-time >systems, so I don't understand why to use a RTOS in those cases. >However I need to use a RTOS (FreeRTOS) for the next project, because it >is one of the requirement. It isn't a real-time system, but the RTOS is >required. > >I think I can convert my architecture to RTOS by creating a task for >each of the function I call in the superloop and starting the OS >scheduler. However now the task function can't return, so I can write it >in the following way: > >void task1_main(void) { > while(1) { > task1(); > } >} > >task1() can be the *same* function of the superloop architecture. > >I can assign each task the same priority: in this case, FreeRTOS will >use round-robin scheduling, giving all the tasks the same opportunity to >run. > >Is it correct?
When you have priority based pre-emptive kernel available, why put everything on a single priority and run round robin between them ? Some RTOS systems do not support round-robin but each task must have a different priority. Designing some RT application is not hard. First you check how time critical each task is and how long it executes. For non-critical or long execution time tasks assign a priority level as _low_ as possibly. When most long execution time tasks gave received some low priority, they are only a few tasks left that will execute at high priority. Conceptually interrupt service routines is just a task with highest priority, but of course the ISR implementation is different. If there are time critical but long execution time tasks, better split it into two tasks, the long execution time task and drop its priority and remaining time critical but short execution task can have a high priority. Most of the time, tasks should just waiting for some event, such as an external event or a message from an other task. In a typical RTOS application, often all tasks are just waiting for an event and hence consume no CPU power. Now and then a task becomes runnable, does its job and then starts to wait for a new event. In rare occasions a low priority task becomes runnable. After a while a high priority task becomes runnable, pre-empts the low priority task, the high priority task does its job and waits for new event and the interrupted low priority task is resumed and runs to completation. This is quite similar to systems that allow nested interrupts. Of course nested interrupts can be avoided by moving the long execution ISR function to a high priority RTOS task.
On Fri, 03 Jan 2020 14:41:10 +0100, pozz wrote:

> I have always worked on non real-time systems, i.e. when the reaction of > the system to an event should occur in a reasonable time. For example, > when the user presses a button, a motor should start rotating. If the > motor starts after 100us or 100ms is not so important. > > I never used RTOS, so the architecture of my firmware is based on > "superloop technique" (background tasks running in the loop and > interrupts). > > while(1) { > task1(); task2(); ... > } > > All the tasks don't block, ever. As a rule of thumb, I accept a block > time of maximum 100us-1ms. When the task needs to block for a greater > amount, I try to implement it as a state-machine, avoiding blocking. > > The ISRs are very lightweight: they only set/get some flags or push/pop > a byte to/from FIFO queues. > With 32-bits MCUs (modern Cortex-M MCUs), I can change 32-bits variables > (mainly the ticks of the system) in ISRs without caring of > race-conditions that could occur on 8-bits MCUS when the background > tasks access the same variables. > > In the past I used this architecture with success even in medium-complex > systems featuring Ethernet, lwip, mbedTLS, USB and touchscreen (emWin). > > Sincerely I think this architecture is good enough for all non real-time > systems, so I don't understand why to use a RTOS in those cases. > However I need to use a RTOS (FreeRTOS) for the next project, because it > is one of the requirement. It isn't a real-time system, but the RTOS is > required. > > I think I can convert my architecture to RTOS by creating a task for > each of the function I call in the superloop and starting the OS > scheduler. However now the task function can't return, so I can write it > in the following way: > > void task1_main(void) { > while(1) { > task1(); > } > } > > task1() can be the *same* function of the superloop architecture. > > I can assign each task the same priority: in this case, FreeRTOS will > use round-robin scheduling, giving all the tasks the same opportunity to > run. > > Is it correct?
It's been a couple of years since I used FreeRTOS. I downloaded the latest version recently but have not had time to look at it. FreeRTOS gives you multiple ways to control a task - semaphores, timers message queues. The main entry point of your firmware is going to setup the various task and then start the scheduler. The scheduler will never return. The scheduler is going to start each task. The task will do some initialization, if needed, and then sit in the loop. The preemptive scheduler can be a little tricky. You have to watch for task critical sections. If tasks have a natural blocking point on a semaphore, timer, queue you can use the non preemptive scheduler. Tasks can also yield. If I remember correctly there are APIs to notify semaphores and queues from within a ISR. You have to use the correct ones. A ENET ISR can do some house keeping and notify the semaphore for the ENET driver task. Blinking a led is a simple matter of the task setting the led on, wait on a timer, set the led off, wait on timer. FreeRTOS takes care of all the work of deciding when to wake up the task waiting on the timer. There are arguments for and against RTOS but you said you had to use FreeRTOS. I think you will find it's easy to use .. BUT ... Read the docs and have them handy when working on your code. -- Chisolm Texas-American
On 1/3/20 8:41 AM, pozz wrote:
> I have always worked on non real-time systems, i.e. when the reaction of > the system to an event should occur in a reasonable time. For example, > when the user presses a button, a motor should start rotating. If the > motor starts after 100us or 100ms is not so important. > > I never used RTOS, so the architecture of my firmware is based on > "superloop technique" (background tasks running in the loop and > interrupts). > > while(1) { > &#4294967295; task1(); > &#4294967295; task2(); > &#4294967295; ... > } > > All the tasks don't block, ever. As a rule of thumb, I accept a block > time of maximum 100us-1ms. When the task needs to block for a greater > amount, I try to implement it as a state-machine, avoiding blocking. > > The ISRs are very lightweight: they only set/get some flags or push/pop > a byte to/from FIFO queues. > With 32-bits MCUs (modern Cortex-M MCUs), I can change 32-bits variables > (mainly the ticks of the system) in ISRs without caring of > race-conditions that could occur on 8-bits MCUS when the background > tasks access the same variables. > > In the past I used this architecture with success even in medium-complex > systems featuring Ethernet, lwip, mbedTLS, USB and touchscreen (emWin). > > Sincerely I think this architecture is good enough for all non real-time > systems, so I don't understand why to use a RTOS in those cases. > However I need to use a RTOS (FreeRTOS) for the next project, because it > is one of the requirement. It isn't a real-time system, but the RTOS is > required. > > I think I can convert my architecture to RTOS by creating a task for > each of the function I call in the superloop and starting the OS > scheduler. However now the task function can't return, so I can write it > in the following way: > > void task1_main(void) { > &#4294967295; while(1) { > &#4294967295;&#4294967295;&#4294967295; task1(); > &#4294967295; } > } > > task1() can be the *same* function of the superloop architecture. > > I can assign each task the same priority: in this case, FreeRTOS will > use round-robin scheduling, giving all the tasks the same opportunity to > run. > > Is it correct?
I would likely make the loop: while(1){ task1(); taskYield(); } so that after task1 finishes what it was doing, you automatically roll to the next task rather than looping task1 for a full tick period, then switching to task2(), and so on. If the tasks naturally block/yield within them, then that isn't as needed. Also, at that point you can undo all of your state-machining that was just to return and then resume where you left off. Which may make the code clearer. The one difficulty is that, as has been mentioned, when two (or more) tasks share data, you need to add the needed protections to the access as they can now be effectively simultaneous. (Not really if it is a single core processor, but you can switch from one task to another at virtually arbitrary points) As others have said, you can perhaps start with a single task written as before, and as you find the reasons they wanted an RTOS, move just that part into a separate task, maybe at a higher priority level.
Il 03/01/2020 15:19, David Brown ha scritto:
> On 03/01/2020 14:41, pozz wrote: >> I have always worked on non real-time systems, i.e. when the reaction of >> the system to an event should occur in a reasonable time. For example, >> when the user presses a button, a motor should start rotating. If the >> motor starts after 100us or 100ms is not so important. >> >> I never used RTOS, so the architecture of my firmware is based on >> "superloop technique" (background tasks running in the loop and >> interrupts). >> >> while(1) { >> &#4294967295; task1(); >> &#4294967295; task2(); >> &#4294967295; ... >> } >> >> All the tasks don't block, ever. As a rule of thumb, I accept a block >> time of maximum 100us-1ms. When the task needs to block for a greater >> amount, I try to implement it as a state-machine, avoiding blocking. >> >> The ISRs are very lightweight: they only set/get some flags or push/pop >> a byte to/from FIFO queues. >> With 32-bits MCUs (modern Cortex-M MCUs), I can change 32-bits variables >> (mainly the ticks of the system) in ISRs without caring of >> race-conditions that could occur on 8-bits MCUS when the background >> tasks access the same variables. >> >> In the past I used this architecture with success even in medium-complex >> systems featuring Ethernet, lwip, mbedTLS, USB and touchscreen (emWin). >> >> Sincerely I think this architecture is good enough for all non real-time >> systems, so I don't understand why to use a RTOS in those cases. > > RTOS's have their advantages and disadvantages. They can make it easier > to guarantee particular timing requirements for high priority tasks, but > make it harder for low priority tasks. They can make it easier to write > individual tasks, but harder to write efficient inter-task data sharing. > They can make it easier to modularise and separate the code, but harder > to debug. > > An RTOS is /not/ necessary for real-time coding. Conversely, an RTOS > can be useful even when you don't need real-time guarantees.
Yes, I agree upon everything.
>> However I need to use a RTOS (FreeRTOS) for the next project, because it >> is one of the requirement. It isn't a real-time system, but the RTOS is >> required. >> >> I think I can convert my architecture to RTOS by creating a task for >> each of the function I call in the superloop and starting the OS >> scheduler. However now the task function can't return, so I can write it >> in the following way: >> >> void task1_main(void) { >> &#4294967295; while(1) { >> &#4294967295;&#4294967295;&#4294967295; task1(); >> &#4294967295; } >> } >> >> task1() can be the *same* function of the superloop architecture. >> >> I can assign each task the same priority: in this case, FreeRTOS will >> use round-robin scheduling, giving all the tasks the same opportunity to >> run. >> >> Is it correct? > > You might be better using cooperative scheduling and : > > void task1_main(void) { > while(1) { > task1(); > taskYIELD(); > } > } > > With cooperative scheduling, you know exactly when the current task can > be changed - it can happen when /you/ want it to, due to a yield or a > blocking OS call. With pre-emptive scheduling, you will have to go > through your existing code and make very sure that you have locks or > synchronisation in place for any shared resources or data. >
You're right, cooperative scheduling is better if I want to reuse the functions used in superloop architecture (that is a cooperative scheduler).
Il 03/01/2020 15:28, Niklas Holsti ha scritto:
> On 2020-01-03 15:41, pozz wrote: >> I have always worked on non real-time systems, i.e. when the reaction >> of the system to an event should occur in a reasonable time. For >> example, when the user presses a button, a motor should start >> rotating. If the motor starts after 100us or 100ms is not so important. >> >> I never used RTOS, so the architecture of my firmware is based on >> "superloop technique" (background tasks running in the loop and >> interrupts). >> >> while(1) { >> &#4294967295;&#4294967295; task1(); >> &#4294967295;&#4294967295; task2(); >> &#4294967295;&#4294967295; ... >> } >> >> All the tasks don't block, ever. As a rule of thumb, I accept a block >> time of maximum 100us-1ms. When the task needs to block for a greater >> amount, I try to implement it as a state-machine, avoiding blocking. > > .... which can complicate the task's logical design, of course. That is > the penalty you pay for the sequential-super-loop design.
Yes, converting a blocking task in a non-blocking state-machine task can be hard, but it's complex to write tasks in a preemption schuduler (you need to know when to use locks, semaphores, mutexes and so on).
> And the same (state-machine) approach has to be used if a task contains > some long sequential computation that must be divided into shorter > stages (in which case the state-machine approach can have a much worse > impact on the task's design). Again, that is the price... > > &#4294967295;&#4294967295; [snip] >
Yes, but this isn't a very frequent situation, at least in my experience. I remember a similar case with mbedTLS where a session setup taked a very long time, because I didn't use a hw crypto engine. For this reason, mbedTLS guys added a macro to enable an "incremental" calculation (I don't remember now the exact name they used). In practice the big calculus is divided in chuncks.
>> Sincerely I think this architecture is good enough for all non >> real-time systems, so I don't understand why to use a RTOS in those >> cases. >> However I need to use a RTOS (FreeRTOS) for the next project, because >> it is one of the requirement. It isn't a real-time system, but the >> RTOS is required. >> >> I think I can convert my architecture to RTOS by creating a task for >> each of the function I call in the superloop and starting the OS >> scheduler. However now the task function can't return, so I can write >> it in the following way: >> >> void task1_main(void) { >> &#4294967295;&#4294967295; while(1) { >> &#4294967295;&#4294967295;&#4294967295;&#4294967295; task1(); >> &#4294967295;&#4294967295; } >> } >> >> task1() can be the *same* function of the superloop architecture. > > A much simpler solution is to create *one* task that contains the > *whole* superloop. > > An RTOS does not *require* multiple tasks.
Oh yes, this is another solution. However I'd like to migrate to a "full" RTOS approach step by step, and starting with the same tasks of the superloop should help on this way. One after the other, I could work on each task and convert it in a "standard" task that blocks, if needed.
>> I can assign each task the same priority: in this case, FreeRTOS will >> use round-robin scheduling, giving all the tasks the same opportunity >> to run. >> >> Is it correct? > > Probably not. > > If your task<n>() functions have some interactions through data > (variables), your superloop design probably assumes that the functions > are called in a certain order, or at least that they do not pre-empt > each other. > > I am not very familiar with FreeRTOS, but to get round-robin scheduling > of tasks of the same priority you probably have to insert "yield" points > in the tasks (as David Brown's message explains)
Only in cooperative scheduler.
> or you have to enable a > pre-emptive time-slicing round-robin scheduling.
This is the "default" FreeRTOS configuration.
> Neither approach gives you a predictable ordering of the task<n> calls, > and the time-slicing approach moreover allows the functions to pre-empt > each other, which probably messes up the data-flow of your program. > > If the superloop design gives you sufficient performance and reactivity, > just encapsulate it in a single task.
See my arguments against this approach above.
Il 03/01/2020 21:51, upsidedown@downunder.com ha scritto:
> On Fri, 3 Jan 2020 14:41:10 +0100, pozz <pozzugno@gmail.com> wrote: > >> I have always worked on non real-time systems, i.e. when the reaction of >> the system to an event should occur in a reasonable time. For example, >> when the user presses a button, a motor should start rotating. If the >> motor starts after 100us or 100ms is not so important. >> >> I never used RTOS, so the architecture of my firmware is based on >> "superloop technique" (background tasks running in the loop and interrupts). >> >> while(1) { >> task1(); >> task2(); >> ... >> } >> >> All the tasks don't block, ever. As a rule of thumb, I accept a block >> time of maximum 100us-1ms. When the task needs to block for a greater >> amount, I try to implement it as a state-machine, avoiding blocking. >> >> The ISRs are very lightweight: they only set/get some flags or push/pop >> a byte to/from FIFO queues. >> With 32-bits MCUs (modern Cortex-M MCUs), I can change 32-bits variables >> (mainly the ticks of the system) in ISRs without caring of >> race-conditions that could occur on 8-bits MCUS when the background >> tasks access the same variables. >> >> In the past I used this architecture with success even in medium-complex >> systems featuring Ethernet, lwip, mbedTLS, USB and touchscreen (emWin). >> >> Sincerely I think this architecture is good enough for all non real-time >> systems, so I don't understand why to use a RTOS in those cases. >> However I need to use a RTOS (FreeRTOS) for the next project, because it >> is one of the requirement. It isn't a real-time system, but the RTOS is >> required. >> >> I think I can convert my architecture to RTOS by creating a task for >> each of the function I call in the superloop and starting the OS >> scheduler. However now the task function can't return, so I can write it >> in the following way: >> >> void task1_main(void) { >> while(1) { >> task1(); >> } >> } >> >> task1() can be the *same* function of the superloop architecture. >> >> I can assign each task the same priority: in this case, FreeRTOS will >> use round-robin scheduling, giving all the tasks the same opportunity to >> run. >> >> Is it correct? > > When you have priority based pre-emptive kernel available, why put > everything on a single priority and run round robin between them ?
Because assigning different priorities to ask avoiding deadlocks could be tricky for me, because I have no experience with RTOS. And because I don't really have higher-priority tasks or hard real-time requirements.
> Some RTOS systems do not support round-robin but each task must have a > different priority.
FreeRTOS supports round-robin in preempetive scheduler when multiple tasks with the same priority keep running for too long (see configUSETIMESLICING).
> Designing some RT application is not hard. > > First you check how time critical each task is and how long it > executes. For non-critical or long execution time tasks assign a > priority level as _low_ as possibly. When most long execution time > tasks gave received some low priority, they are only a few tasks left > that will execute at high priority. Conceptually interrupt service > routines is just a task with highest priority, but of course the ISR > implementation is different. > > If there are time critical but long execution time tasks, better split > it into two tasks, the long execution time task and drop its priority > and remaining time critical but short execution task can have a high > priority. > > Most of the time, tasks should just waiting for some event, such as an > external event or a message from an other task. In a typical RTOS > application, often all tasks are just waiting for an event and hence > consume no CPU power. Now and then a task becomes runnable, does its > job and then starts to wait for a new event. > > In rare occasions a low priority task becomes runnable. After a while > a high priority task becomes runnable, pre-empts the low priority > task, the high priority task does its job and waits for new event and > the interrupted low priority task is resumed and runs to completation. > > This is quite similar to systems that allow nested interrupts. Of > course nested interrupts can be avoided by moving the long execution > ISR function to a high priority RTOS task.
Yes, the theory is this. I have to apply the theory in practice and could reserve some surprises. I am worried about deadlocks that could occur very infrequently, maybe when the product is on the field.

The 2024 Embedded Online Conference