How to best convert legacy polling embedded firmware architecture into an event driven one?
I've got a family of embedded products running a typical main-loop based firmware with 150+k lines of code. A load of complex timing critical features is realized by a combination of hardware interrupt handlers, timer polling and protothreads (think co-routines). Actually protothreads are polling well and "only" are syntactic sugar to mimic pseudo-parallel scheduling of multiple threads (endless loops). I add bugfixes and extensions to the firmware all the time. There are about 30k devices of about 7 slightly different hardware types and versions out in the field.
For a new product-family member I need to integrate an external FreeRTOS-based project on the new product only while all older products need to get further features and improvements.
In order to not have to port the whole complex legacy firmware to FreeRTOS with all the risk of breaking perfectly fine products I plan to let the old firmware run inside a FreeRTOS task. On the older products the firmware shall still not run without FreeRTOS. Inside the FreeRTOS task the legacy firmware would consume all available processor time because its underlying implementation scheme is polling based. Due to the fact that its using prothreads (timer and hardware polling based behind the scenes) and polls on a free-running processor counter register I hope that I can convert the polling into a event driven behavior.
Here are two examples:
// first example: do something every 100 ms
if (GET_TICK_COUNT() - start > MS(100))
{
start = GET_TICK_COUNT();
// do something every 100 ms
}
// second example: wait for hardware event
setup_hardware();
PT_WAIT_UNTIL(hardware_ready(), pt);
// hardware is ready, do something else
So I got the impression that I can convert these 2 programming patterns (e.g. through macro magic and FreeRTOS functionality) into an underlying event based scheme.
So now my question: has anybody done such a thing already? Are there patterns and or best practices to follow?
[Update]
Thanks for the detailed responses. Let me comment some details: My need is to combine a "multithreading-simulation based legacy firmware (using coo-routines implementation protothreads)" and a FreeRTOS based project being composed of a couple of interacting FreeRTOS tasks. The idea is to let the complete old firmware run in its own RTOS task besides the other new tasks. I'm aware of RTOS principles and patterns (pre-emption, ressource sharing, blocking operations, signals, semaphores, mutexes, mailboxes, task priorities and so forth). I've planed to base the interaction of old and new parts exactly on these mechanisms. What I'm asking for is 1) Ideas how to convert the legacy firmware (150k+ LOC) in a semi-automated way so that the busy-waiting / polling schemes I presented above are using either the new mechanisms when run inside the RTOS tasks or just work the old way when build and run as the current main-loop-kind of firmware. A complete rewrite / full port of the legacy code is no option. 2) More ideas how to teach the old firmware implementation that is used to have full CPU resources on its hands to behave nicely inside its new prison of a RTOS task and not just consume all CPU cycles available (when given the highest priority) or producing new large Real Time Latencies when run not at the highest RTOS priority.
I guess here is nobody who already has done such a very special tas. So I just have to do the hard work and solve all the described issues one after another.
embedded polling freertos firmware event-driven
add a comment |
I've got a family of embedded products running a typical main-loop based firmware with 150+k lines of code. A load of complex timing critical features is realized by a combination of hardware interrupt handlers, timer polling and protothreads (think co-routines). Actually protothreads are polling well and "only" are syntactic sugar to mimic pseudo-parallel scheduling of multiple threads (endless loops). I add bugfixes and extensions to the firmware all the time. There are about 30k devices of about 7 slightly different hardware types and versions out in the field.
For a new product-family member I need to integrate an external FreeRTOS-based project on the new product only while all older products need to get further features and improvements.
In order to not have to port the whole complex legacy firmware to FreeRTOS with all the risk of breaking perfectly fine products I plan to let the old firmware run inside a FreeRTOS task. On the older products the firmware shall still not run without FreeRTOS. Inside the FreeRTOS task the legacy firmware would consume all available processor time because its underlying implementation scheme is polling based. Due to the fact that its using prothreads (timer and hardware polling based behind the scenes) and polls on a free-running processor counter register I hope that I can convert the polling into a event driven behavior.
Here are two examples:
// first example: do something every 100 ms
if (GET_TICK_COUNT() - start > MS(100))
{
start = GET_TICK_COUNT();
// do something every 100 ms
}
// second example: wait for hardware event
setup_hardware();
PT_WAIT_UNTIL(hardware_ready(), pt);
// hardware is ready, do something else
So I got the impression that I can convert these 2 programming patterns (e.g. through macro magic and FreeRTOS functionality) into an underlying event based scheme.
So now my question: has anybody done such a thing already? Are there patterns and or best practices to follow?
[Update]
Thanks for the detailed responses. Let me comment some details: My need is to combine a "multithreading-simulation based legacy firmware (using coo-routines implementation protothreads)" and a FreeRTOS based project being composed of a couple of interacting FreeRTOS tasks. The idea is to let the complete old firmware run in its own RTOS task besides the other new tasks. I'm aware of RTOS principles and patterns (pre-emption, ressource sharing, blocking operations, signals, semaphores, mutexes, mailboxes, task priorities and so forth). I've planed to base the interaction of old and new parts exactly on these mechanisms. What I'm asking for is 1) Ideas how to convert the legacy firmware (150k+ LOC) in a semi-automated way so that the busy-waiting / polling schemes I presented above are using either the new mechanisms when run inside the RTOS tasks or just work the old way when build and run as the current main-loop-kind of firmware. A complete rewrite / full port of the legacy code is no option. 2) More ideas how to teach the old firmware implementation that is used to have full CPU resources on its hands to behave nicely inside its new prison of a RTOS task and not just consume all CPU cycles available (when given the highest priority) or producing new large Real Time Latencies when run not at the highest RTOS priority.
I guess here is nobody who already has done such a very special tas. So I just have to do the hard work and solve all the described issues one after another.
embedded polling freertos firmware event-driven
Things like interrupts vs polling are low-level concepts, whereas processes and threads are higher level, and event-driven even higher level. There's no obvious benefit of using event-driven patterns in real-time code. Event-driven design is more suitable for GUI and high-level programming. Mostly it is just another buzzword, which by itself does not give any obvious benefits nor disadvantages, it is just a design pattern which may or may not be useful.
– Lundin
Nov 20 at 11:45
Just have look at the two examples take out of the current firmware: the first is based on the polling pattern. The second is actually based on the polling pattern as well behind the scenes - but I want to replace polling with blocking primitives from FreeRTOS when possible. So I get an event driven scheme and are not wasting CPU power any more.
– Marcel
Nov 20 at 16:03
1
@Lundin : Mostly you make complete sense, but "no obvious benefit of using event-driven patterns in real-time code." makes no sense to me. Most hard real-time systems react to external real-time events - that is rather the definition. While you can create a deterministic system using simple time-triggered methods, many tasks require near instantaneous response to some real-time event. Perhaps you have a different concept of "event driven"; while the term is often applied to UI frameworks, it has other contexts.
– Clifford
Nov 20 at 21:24
@Clifford My definition of event-driven is that you have an API where you can register events by passing on numerous function pointers, and will then have callback functions executed when an event occurs. Such ascreate_menu(on_enter, on_exit, on_selected)
, where the parameters are function pointers of a given format. What you describe rather sounds like interrupt handlers. Though of course, you can execute callbacks from inside an ISR, but that requires that the person who wrote the callback knows what they are doing, and keep it minimal.
– Lundin
Nov 21 at 7:59
@Lundin I see. The term is rather more general than that, and pre-dates GUI frameworks by many years. I am not talking about interrupt handlers, but more specifically real-time events, such as event: limit switch opened, reaction: motor stops within 1ms. That may involve interrupts, but that is an implementation detail.
– Clifford
Nov 21 at 8:08
add a comment |
I've got a family of embedded products running a typical main-loop based firmware with 150+k lines of code. A load of complex timing critical features is realized by a combination of hardware interrupt handlers, timer polling and protothreads (think co-routines). Actually protothreads are polling well and "only" are syntactic sugar to mimic pseudo-parallel scheduling of multiple threads (endless loops). I add bugfixes and extensions to the firmware all the time. There are about 30k devices of about 7 slightly different hardware types and versions out in the field.
For a new product-family member I need to integrate an external FreeRTOS-based project on the new product only while all older products need to get further features and improvements.
In order to not have to port the whole complex legacy firmware to FreeRTOS with all the risk of breaking perfectly fine products I plan to let the old firmware run inside a FreeRTOS task. On the older products the firmware shall still not run without FreeRTOS. Inside the FreeRTOS task the legacy firmware would consume all available processor time because its underlying implementation scheme is polling based. Due to the fact that its using prothreads (timer and hardware polling based behind the scenes) and polls on a free-running processor counter register I hope that I can convert the polling into a event driven behavior.
Here are two examples:
// first example: do something every 100 ms
if (GET_TICK_COUNT() - start > MS(100))
{
start = GET_TICK_COUNT();
// do something every 100 ms
}
// second example: wait for hardware event
setup_hardware();
PT_WAIT_UNTIL(hardware_ready(), pt);
// hardware is ready, do something else
So I got the impression that I can convert these 2 programming patterns (e.g. through macro magic and FreeRTOS functionality) into an underlying event based scheme.
So now my question: has anybody done such a thing already? Are there patterns and or best practices to follow?
[Update]
Thanks for the detailed responses. Let me comment some details: My need is to combine a "multithreading-simulation based legacy firmware (using coo-routines implementation protothreads)" and a FreeRTOS based project being composed of a couple of interacting FreeRTOS tasks. The idea is to let the complete old firmware run in its own RTOS task besides the other new tasks. I'm aware of RTOS principles and patterns (pre-emption, ressource sharing, blocking operations, signals, semaphores, mutexes, mailboxes, task priorities and so forth). I've planed to base the interaction of old and new parts exactly on these mechanisms. What I'm asking for is 1) Ideas how to convert the legacy firmware (150k+ LOC) in a semi-automated way so that the busy-waiting / polling schemes I presented above are using either the new mechanisms when run inside the RTOS tasks or just work the old way when build and run as the current main-loop-kind of firmware. A complete rewrite / full port of the legacy code is no option. 2) More ideas how to teach the old firmware implementation that is used to have full CPU resources on its hands to behave nicely inside its new prison of a RTOS task and not just consume all CPU cycles available (when given the highest priority) or producing new large Real Time Latencies when run not at the highest RTOS priority.
I guess here is nobody who already has done such a very special tas. So I just have to do the hard work and solve all the described issues one after another.
embedded polling freertos firmware event-driven
I've got a family of embedded products running a typical main-loop based firmware with 150+k lines of code. A load of complex timing critical features is realized by a combination of hardware interrupt handlers, timer polling and protothreads (think co-routines). Actually protothreads are polling well and "only" are syntactic sugar to mimic pseudo-parallel scheduling of multiple threads (endless loops). I add bugfixes and extensions to the firmware all the time. There are about 30k devices of about 7 slightly different hardware types and versions out in the field.
For a new product-family member I need to integrate an external FreeRTOS-based project on the new product only while all older products need to get further features and improvements.
In order to not have to port the whole complex legacy firmware to FreeRTOS with all the risk of breaking perfectly fine products I plan to let the old firmware run inside a FreeRTOS task. On the older products the firmware shall still not run without FreeRTOS. Inside the FreeRTOS task the legacy firmware would consume all available processor time because its underlying implementation scheme is polling based. Due to the fact that its using prothreads (timer and hardware polling based behind the scenes) and polls on a free-running processor counter register I hope that I can convert the polling into a event driven behavior.
Here are two examples:
// first example: do something every 100 ms
if (GET_TICK_COUNT() - start > MS(100))
{
start = GET_TICK_COUNT();
// do something every 100 ms
}
// second example: wait for hardware event
setup_hardware();
PT_WAIT_UNTIL(hardware_ready(), pt);
// hardware is ready, do something else
So I got the impression that I can convert these 2 programming patterns (e.g. through macro magic and FreeRTOS functionality) into an underlying event based scheme.
So now my question: has anybody done such a thing already? Are there patterns and or best practices to follow?
[Update]
Thanks for the detailed responses. Let me comment some details: My need is to combine a "multithreading-simulation based legacy firmware (using coo-routines implementation protothreads)" and a FreeRTOS based project being composed of a couple of interacting FreeRTOS tasks. The idea is to let the complete old firmware run in its own RTOS task besides the other new tasks. I'm aware of RTOS principles and patterns (pre-emption, ressource sharing, blocking operations, signals, semaphores, mutexes, mailboxes, task priorities and so forth). I've planed to base the interaction of old and new parts exactly on these mechanisms. What I'm asking for is 1) Ideas how to convert the legacy firmware (150k+ LOC) in a semi-automated way so that the busy-waiting / polling schemes I presented above are using either the new mechanisms when run inside the RTOS tasks or just work the old way when build and run as the current main-loop-kind of firmware. A complete rewrite / full port of the legacy code is no option. 2) More ideas how to teach the old firmware implementation that is used to have full CPU resources on its hands to behave nicely inside its new prison of a RTOS task and not just consume all CPU cycles available (when given the highest priority) or producing new large Real Time Latencies when run not at the highest RTOS priority.
I guess here is nobody who already has done such a very special tas. So I just have to do the hard work and solve all the described issues one after another.
embedded polling freertos firmware event-driven
embedded polling freertos firmware event-driven
edited Nov 21 at 10:47
asked Nov 20 at 10:19
Marcel
245
245
Things like interrupts vs polling are low-level concepts, whereas processes and threads are higher level, and event-driven even higher level. There's no obvious benefit of using event-driven patterns in real-time code. Event-driven design is more suitable for GUI and high-level programming. Mostly it is just another buzzword, which by itself does not give any obvious benefits nor disadvantages, it is just a design pattern which may or may not be useful.
– Lundin
Nov 20 at 11:45
Just have look at the two examples take out of the current firmware: the first is based on the polling pattern. The second is actually based on the polling pattern as well behind the scenes - but I want to replace polling with blocking primitives from FreeRTOS when possible. So I get an event driven scheme and are not wasting CPU power any more.
– Marcel
Nov 20 at 16:03
1
@Lundin : Mostly you make complete sense, but "no obvious benefit of using event-driven patterns in real-time code." makes no sense to me. Most hard real-time systems react to external real-time events - that is rather the definition. While you can create a deterministic system using simple time-triggered methods, many tasks require near instantaneous response to some real-time event. Perhaps you have a different concept of "event driven"; while the term is often applied to UI frameworks, it has other contexts.
– Clifford
Nov 20 at 21:24
@Clifford My definition of event-driven is that you have an API where you can register events by passing on numerous function pointers, and will then have callback functions executed when an event occurs. Such ascreate_menu(on_enter, on_exit, on_selected)
, where the parameters are function pointers of a given format. What you describe rather sounds like interrupt handlers. Though of course, you can execute callbacks from inside an ISR, but that requires that the person who wrote the callback knows what they are doing, and keep it minimal.
– Lundin
Nov 21 at 7:59
@Lundin I see. The term is rather more general than that, and pre-dates GUI frameworks by many years. I am not talking about interrupt handlers, but more specifically real-time events, such as event: limit switch opened, reaction: motor stops within 1ms. That may involve interrupts, but that is an implementation detail.
– Clifford
Nov 21 at 8:08
add a comment |
Things like interrupts vs polling are low-level concepts, whereas processes and threads are higher level, and event-driven even higher level. There's no obvious benefit of using event-driven patterns in real-time code. Event-driven design is more suitable for GUI and high-level programming. Mostly it is just another buzzword, which by itself does not give any obvious benefits nor disadvantages, it is just a design pattern which may or may not be useful.
– Lundin
Nov 20 at 11:45
Just have look at the two examples take out of the current firmware: the first is based on the polling pattern. The second is actually based on the polling pattern as well behind the scenes - but I want to replace polling with blocking primitives from FreeRTOS when possible. So I get an event driven scheme and are not wasting CPU power any more.
– Marcel
Nov 20 at 16:03
1
@Lundin : Mostly you make complete sense, but "no obvious benefit of using event-driven patterns in real-time code." makes no sense to me. Most hard real-time systems react to external real-time events - that is rather the definition. While you can create a deterministic system using simple time-triggered methods, many tasks require near instantaneous response to some real-time event. Perhaps you have a different concept of "event driven"; while the term is often applied to UI frameworks, it has other contexts.
– Clifford
Nov 20 at 21:24
@Clifford My definition of event-driven is that you have an API where you can register events by passing on numerous function pointers, and will then have callback functions executed when an event occurs. Such ascreate_menu(on_enter, on_exit, on_selected)
, where the parameters are function pointers of a given format. What you describe rather sounds like interrupt handlers. Though of course, you can execute callbacks from inside an ISR, but that requires that the person who wrote the callback knows what they are doing, and keep it minimal.
– Lundin
Nov 21 at 7:59
@Lundin I see. The term is rather more general than that, and pre-dates GUI frameworks by many years. I am not talking about interrupt handlers, but more specifically real-time events, such as event: limit switch opened, reaction: motor stops within 1ms. That may involve interrupts, but that is an implementation detail.
– Clifford
Nov 21 at 8:08
Things like interrupts vs polling are low-level concepts, whereas processes and threads are higher level, and event-driven even higher level. There's no obvious benefit of using event-driven patterns in real-time code. Event-driven design is more suitable for GUI and high-level programming. Mostly it is just another buzzword, which by itself does not give any obvious benefits nor disadvantages, it is just a design pattern which may or may not be useful.
– Lundin
Nov 20 at 11:45
Things like interrupts vs polling are low-level concepts, whereas processes and threads are higher level, and event-driven even higher level. There's no obvious benefit of using event-driven patterns in real-time code. Event-driven design is more suitable for GUI and high-level programming. Mostly it is just another buzzword, which by itself does not give any obvious benefits nor disadvantages, it is just a design pattern which may or may not be useful.
– Lundin
Nov 20 at 11:45
Just have look at the two examples take out of the current firmware: the first is based on the polling pattern. The second is actually based on the polling pattern as well behind the scenes - but I want to replace polling with blocking primitives from FreeRTOS when possible. So I get an event driven scheme and are not wasting CPU power any more.
– Marcel
Nov 20 at 16:03
Just have look at the two examples take out of the current firmware: the first is based on the polling pattern. The second is actually based on the polling pattern as well behind the scenes - but I want to replace polling with blocking primitives from FreeRTOS when possible. So I get an event driven scheme and are not wasting CPU power any more.
– Marcel
Nov 20 at 16:03
1
1
@Lundin : Mostly you make complete sense, but "no obvious benefit of using event-driven patterns in real-time code." makes no sense to me. Most hard real-time systems react to external real-time events - that is rather the definition. While you can create a deterministic system using simple time-triggered methods, many tasks require near instantaneous response to some real-time event. Perhaps you have a different concept of "event driven"; while the term is often applied to UI frameworks, it has other contexts.
– Clifford
Nov 20 at 21:24
@Lundin : Mostly you make complete sense, but "no obvious benefit of using event-driven patterns in real-time code." makes no sense to me. Most hard real-time systems react to external real-time events - that is rather the definition. While you can create a deterministic system using simple time-triggered methods, many tasks require near instantaneous response to some real-time event. Perhaps you have a different concept of "event driven"; while the term is often applied to UI frameworks, it has other contexts.
– Clifford
Nov 20 at 21:24
@Clifford My definition of event-driven is that you have an API where you can register events by passing on numerous function pointers, and will then have callback functions executed when an event occurs. Such as
create_menu(on_enter, on_exit, on_selected)
, where the parameters are function pointers of a given format. What you describe rather sounds like interrupt handlers. Though of course, you can execute callbacks from inside an ISR, but that requires that the person who wrote the callback knows what they are doing, and keep it minimal.– Lundin
Nov 21 at 7:59
@Clifford My definition of event-driven is that you have an API where you can register events by passing on numerous function pointers, and will then have callback functions executed when an event occurs. Such as
create_menu(on_enter, on_exit, on_selected)
, where the parameters are function pointers of a given format. What you describe rather sounds like interrupt handlers. Though of course, you can execute callbacks from inside an ISR, but that requires that the person who wrote the callback knows what they are doing, and keep it minimal.– Lundin
Nov 21 at 7:59
@Lundin I see. The term is rather more general than that, and pre-dates GUI frameworks by many years. I am not talking about interrupt handlers, but more specifically real-time events, such as event: limit switch opened, reaction: motor stops within 1ms. That may involve interrupts, but that is an implementation detail.
– Clifford
Nov 21 at 8:08
@Lundin I see. The term is rather more general than that, and pre-dates GUI frameworks by many years. I am not talking about interrupt handlers, but more specifically real-time events, such as event: limit switch opened, reaction: motor stops within 1ms. That may involve interrupts, but that is an implementation detail.
– Clifford
Nov 21 at 8:08
add a comment |
2 Answers
2
active
oldest
votes
In an RTOS you create and run tasks. If you are not running more than one task, then there is little advantage in using an RTOS.
I don't use FreeRTOS (but have done), but the following applies to any RTOS, and is pseudo-code rather then FreeRTOS API specific - many details such as task priorities and stack allocation are deliberately missing.
First in most simple RTOS, including FreeRTOS, main()
is used for hardware initialisation, task creation, and scheduler start:
int main( void )
{
// Necessary h/w & kernel initialisation
initHardware() ;
initKernel() ;
// Create tasks
createTask( task1 ) ;
createTask( task2 ) ;
// Start scheduling
schedulerStart() ;
// schedulerStart should not normally return
return 0 ;
}
Now let us assume that your first example is implemented in task1
. A typical RTOS will have both timer and delay functions. The simplest to use is a delay, and this is suitable when the periodic processing is guaranteed to take less than one OS tick period:
void task1()
{
// do something every 100 ms
for(;;)
{
delay( 100 ) ; // assuming 1ms tick period
// something
...
}
}
If the something
takes more than 1ms in this case, it will not be executed every 100ms, but 100ms plus the something
execution time, which may itself be variable or non-deterministic leading to undesirable timing jitter. In that case you should use a timer:
void task1()
{
// do something every 100 ms
TIMER timer = createTimer( 100 ) ; // assuming 1ms tick period
for(;;)
{
timerWait() ;
// something
...
}
}
That way something
can take up to 100ms and will still be executed accurately and deterministically every 100ms.
Now to your second example; that is a little more complex. If nothing useful can happen until the hardware is initialised, then you may as well use your existing pattern in main()
before starting the scheduler. However as a generalisation, waiting for something in a different context (task or interrupt) to occur is done using a synchronisation primitive such as a semaphore or task event flag (not all RTOS have task event flags). So in a simple case in main()
you might create a semaphore:
createSemaphore( hardware_ready ) ;
Then in the context performing the process that must complete:
// Init hardware
...
// Tell waiting task hardware ready
semaphoreGive( hardware_ready ) ;
Then in some task that will wait for the hardware to be ready:
void task2()
{
// wait for hardware ready
semaphoreTake( hardware_ready ) ;
// do something else
for(;;)
{
// This loop must block is any lower-priority task
// will run. Equal priority tasks may run is round-robin
// scheduling is implemented.
...
}
}
add a comment |
You are facing two big gotchas...
- Since the old code is implemented using protothreads (coroutines), there is never any asynchronous resource contention between them. If you split these into FreeRTOS tasks, there there will be preemptive scheduling task switches; these switches may occur at places where the protothreads were not expecting, leaving data or other resources in an inconsistent state.
- If you convert any of your protothreads' PT_WAIT calls into real waits in FreeRTOS, the call will really block. But the protothreads assume that other protothreads continue while they're blocked.
So, #1 implies you cannot just convert protothreads to tasks, and #2 implies you must convert protothreads to tasks (if you use FreeRTOS blocking primitives, like xEventGroupWaitBits().
The most straightforward approach will be to put all your protothreads in one task, and continue polling within that task.
Thats exactly the kind of way I want to go. Please see my update in the question. You seam to have seen such stuff already. Any further hints for me?
– Marcel
Nov 21 at 10:49
your second point is a big issue on my list. My current idea is to convert most or all protothread code behind the scenes into a semi-blocking combination of waiting for the real event (e.g. data was sent on a serial channel) and asking a new internal timer module if any other protothread is un-blocking and we need to go there. I find it a very interesting puzzle to solve...
– Marcel
Nov 21 at 11:12
also @Marcel: I have never used FreeRTOS but glancing over the documentation it looks like they implemented exactly the ProtoThreads functionality - they termed it Coroutines there. Maybe the mapping is close to 1:1
– Vroomfondel
Nov 21 at 13:31
@Doug Currie Oh thats a missunderstanding. The legacy firmware use not based on FreeRTOS Coroutines / protothreads. I used directly Adam Dunkels protothread implementation. Its just under 50 lines of C headers. Nothing more. I won't port that to FreeRTOS.
– Marcel
Nov 22 at 9:35
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53390809%2fhow-to-best-convert-legacy-polling-embedded-firmware-architecture-into-an-event%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
In an RTOS you create and run tasks. If you are not running more than one task, then there is little advantage in using an RTOS.
I don't use FreeRTOS (but have done), but the following applies to any RTOS, and is pseudo-code rather then FreeRTOS API specific - many details such as task priorities and stack allocation are deliberately missing.
First in most simple RTOS, including FreeRTOS, main()
is used for hardware initialisation, task creation, and scheduler start:
int main( void )
{
// Necessary h/w & kernel initialisation
initHardware() ;
initKernel() ;
// Create tasks
createTask( task1 ) ;
createTask( task2 ) ;
// Start scheduling
schedulerStart() ;
// schedulerStart should not normally return
return 0 ;
}
Now let us assume that your first example is implemented in task1
. A typical RTOS will have both timer and delay functions. The simplest to use is a delay, and this is suitable when the periodic processing is guaranteed to take less than one OS tick period:
void task1()
{
// do something every 100 ms
for(;;)
{
delay( 100 ) ; // assuming 1ms tick period
// something
...
}
}
If the something
takes more than 1ms in this case, it will not be executed every 100ms, but 100ms plus the something
execution time, which may itself be variable or non-deterministic leading to undesirable timing jitter. In that case you should use a timer:
void task1()
{
// do something every 100 ms
TIMER timer = createTimer( 100 ) ; // assuming 1ms tick period
for(;;)
{
timerWait() ;
// something
...
}
}
That way something
can take up to 100ms and will still be executed accurately and deterministically every 100ms.
Now to your second example; that is a little more complex. If nothing useful can happen until the hardware is initialised, then you may as well use your existing pattern in main()
before starting the scheduler. However as a generalisation, waiting for something in a different context (task or interrupt) to occur is done using a synchronisation primitive such as a semaphore or task event flag (not all RTOS have task event flags). So in a simple case in main()
you might create a semaphore:
createSemaphore( hardware_ready ) ;
Then in the context performing the process that must complete:
// Init hardware
...
// Tell waiting task hardware ready
semaphoreGive( hardware_ready ) ;
Then in some task that will wait for the hardware to be ready:
void task2()
{
// wait for hardware ready
semaphoreTake( hardware_ready ) ;
// do something else
for(;;)
{
// This loop must block is any lower-priority task
// will run. Equal priority tasks may run is round-robin
// scheduling is implemented.
...
}
}
add a comment |
In an RTOS you create and run tasks. If you are not running more than one task, then there is little advantage in using an RTOS.
I don't use FreeRTOS (but have done), but the following applies to any RTOS, and is pseudo-code rather then FreeRTOS API specific - many details such as task priorities and stack allocation are deliberately missing.
First in most simple RTOS, including FreeRTOS, main()
is used for hardware initialisation, task creation, and scheduler start:
int main( void )
{
// Necessary h/w & kernel initialisation
initHardware() ;
initKernel() ;
// Create tasks
createTask( task1 ) ;
createTask( task2 ) ;
// Start scheduling
schedulerStart() ;
// schedulerStart should not normally return
return 0 ;
}
Now let us assume that your first example is implemented in task1
. A typical RTOS will have both timer and delay functions. The simplest to use is a delay, and this is suitable when the periodic processing is guaranteed to take less than one OS tick period:
void task1()
{
// do something every 100 ms
for(;;)
{
delay( 100 ) ; // assuming 1ms tick period
// something
...
}
}
If the something
takes more than 1ms in this case, it will not be executed every 100ms, but 100ms plus the something
execution time, which may itself be variable or non-deterministic leading to undesirable timing jitter. In that case you should use a timer:
void task1()
{
// do something every 100 ms
TIMER timer = createTimer( 100 ) ; // assuming 1ms tick period
for(;;)
{
timerWait() ;
// something
...
}
}
That way something
can take up to 100ms and will still be executed accurately and deterministically every 100ms.
Now to your second example; that is a little more complex. If nothing useful can happen until the hardware is initialised, then you may as well use your existing pattern in main()
before starting the scheduler. However as a generalisation, waiting for something in a different context (task or interrupt) to occur is done using a synchronisation primitive such as a semaphore or task event flag (not all RTOS have task event flags). So in a simple case in main()
you might create a semaphore:
createSemaphore( hardware_ready ) ;
Then in the context performing the process that must complete:
// Init hardware
...
// Tell waiting task hardware ready
semaphoreGive( hardware_ready ) ;
Then in some task that will wait for the hardware to be ready:
void task2()
{
// wait for hardware ready
semaphoreTake( hardware_ready ) ;
// do something else
for(;;)
{
// This loop must block is any lower-priority task
// will run. Equal priority tasks may run is round-robin
// scheduling is implemented.
...
}
}
add a comment |
In an RTOS you create and run tasks. If you are not running more than one task, then there is little advantage in using an RTOS.
I don't use FreeRTOS (but have done), but the following applies to any RTOS, and is pseudo-code rather then FreeRTOS API specific - many details such as task priorities and stack allocation are deliberately missing.
First in most simple RTOS, including FreeRTOS, main()
is used for hardware initialisation, task creation, and scheduler start:
int main( void )
{
// Necessary h/w & kernel initialisation
initHardware() ;
initKernel() ;
// Create tasks
createTask( task1 ) ;
createTask( task2 ) ;
// Start scheduling
schedulerStart() ;
// schedulerStart should not normally return
return 0 ;
}
Now let us assume that your first example is implemented in task1
. A typical RTOS will have both timer and delay functions. The simplest to use is a delay, and this is suitable when the periodic processing is guaranteed to take less than one OS tick period:
void task1()
{
// do something every 100 ms
for(;;)
{
delay( 100 ) ; // assuming 1ms tick period
// something
...
}
}
If the something
takes more than 1ms in this case, it will not be executed every 100ms, but 100ms plus the something
execution time, which may itself be variable or non-deterministic leading to undesirable timing jitter. In that case you should use a timer:
void task1()
{
// do something every 100 ms
TIMER timer = createTimer( 100 ) ; // assuming 1ms tick period
for(;;)
{
timerWait() ;
// something
...
}
}
That way something
can take up to 100ms and will still be executed accurately and deterministically every 100ms.
Now to your second example; that is a little more complex. If nothing useful can happen until the hardware is initialised, then you may as well use your existing pattern in main()
before starting the scheduler. However as a generalisation, waiting for something in a different context (task or interrupt) to occur is done using a synchronisation primitive such as a semaphore or task event flag (not all RTOS have task event flags). So in a simple case in main()
you might create a semaphore:
createSemaphore( hardware_ready ) ;
Then in the context performing the process that must complete:
// Init hardware
...
// Tell waiting task hardware ready
semaphoreGive( hardware_ready ) ;
Then in some task that will wait for the hardware to be ready:
void task2()
{
// wait for hardware ready
semaphoreTake( hardware_ready ) ;
// do something else
for(;;)
{
// This loop must block is any lower-priority task
// will run. Equal priority tasks may run is round-robin
// scheduling is implemented.
...
}
}
In an RTOS you create and run tasks. If you are not running more than one task, then there is little advantage in using an RTOS.
I don't use FreeRTOS (but have done), but the following applies to any RTOS, and is pseudo-code rather then FreeRTOS API specific - many details such as task priorities and stack allocation are deliberately missing.
First in most simple RTOS, including FreeRTOS, main()
is used for hardware initialisation, task creation, and scheduler start:
int main( void )
{
// Necessary h/w & kernel initialisation
initHardware() ;
initKernel() ;
// Create tasks
createTask( task1 ) ;
createTask( task2 ) ;
// Start scheduling
schedulerStart() ;
// schedulerStart should not normally return
return 0 ;
}
Now let us assume that your first example is implemented in task1
. A typical RTOS will have both timer and delay functions. The simplest to use is a delay, and this is suitable when the periodic processing is guaranteed to take less than one OS tick period:
void task1()
{
// do something every 100 ms
for(;;)
{
delay( 100 ) ; // assuming 1ms tick period
// something
...
}
}
If the something
takes more than 1ms in this case, it will not be executed every 100ms, but 100ms plus the something
execution time, which may itself be variable or non-deterministic leading to undesirable timing jitter. In that case you should use a timer:
void task1()
{
// do something every 100 ms
TIMER timer = createTimer( 100 ) ; // assuming 1ms tick period
for(;;)
{
timerWait() ;
// something
...
}
}
That way something
can take up to 100ms and will still be executed accurately and deterministically every 100ms.
Now to your second example; that is a little more complex. If nothing useful can happen until the hardware is initialised, then you may as well use your existing pattern in main()
before starting the scheduler. However as a generalisation, waiting for something in a different context (task or interrupt) to occur is done using a synchronisation primitive such as a semaphore or task event flag (not all RTOS have task event flags). So in a simple case in main()
you might create a semaphore:
createSemaphore( hardware_ready ) ;
Then in the context performing the process that must complete:
// Init hardware
...
// Tell waiting task hardware ready
semaphoreGive( hardware_ready ) ;
Then in some task that will wait for the hardware to be ready:
void task2()
{
// wait for hardware ready
semaphoreTake( hardware_ready ) ;
// do something else
for(;;)
{
// This loop must block is any lower-priority task
// will run. Equal priority tasks may run is round-robin
// scheduling is implemented.
...
}
}
edited Nov 20 at 21:27
answered Nov 20 at 21:12
Clifford
58.1k858125
58.1k858125
add a comment |
add a comment |
You are facing two big gotchas...
- Since the old code is implemented using protothreads (coroutines), there is never any asynchronous resource contention between them. If you split these into FreeRTOS tasks, there there will be preemptive scheduling task switches; these switches may occur at places where the protothreads were not expecting, leaving data or other resources in an inconsistent state.
- If you convert any of your protothreads' PT_WAIT calls into real waits in FreeRTOS, the call will really block. But the protothreads assume that other protothreads continue while they're blocked.
So, #1 implies you cannot just convert protothreads to tasks, and #2 implies you must convert protothreads to tasks (if you use FreeRTOS blocking primitives, like xEventGroupWaitBits().
The most straightforward approach will be to put all your protothreads in one task, and continue polling within that task.
Thats exactly the kind of way I want to go. Please see my update in the question. You seam to have seen such stuff already. Any further hints for me?
– Marcel
Nov 21 at 10:49
your second point is a big issue on my list. My current idea is to convert most or all protothread code behind the scenes into a semi-blocking combination of waiting for the real event (e.g. data was sent on a serial channel) and asking a new internal timer module if any other protothread is un-blocking and we need to go there. I find it a very interesting puzzle to solve...
– Marcel
Nov 21 at 11:12
also @Marcel: I have never used FreeRTOS but glancing over the documentation it looks like they implemented exactly the ProtoThreads functionality - they termed it Coroutines there. Maybe the mapping is close to 1:1
– Vroomfondel
Nov 21 at 13:31
@Doug Currie Oh thats a missunderstanding. The legacy firmware use not based on FreeRTOS Coroutines / protothreads. I used directly Adam Dunkels protothread implementation. Its just under 50 lines of C headers. Nothing more. I won't port that to FreeRTOS.
– Marcel
Nov 22 at 9:35
add a comment |
You are facing two big gotchas...
- Since the old code is implemented using protothreads (coroutines), there is never any asynchronous resource contention between them. If you split these into FreeRTOS tasks, there there will be preemptive scheduling task switches; these switches may occur at places where the protothreads were not expecting, leaving data or other resources in an inconsistent state.
- If you convert any of your protothreads' PT_WAIT calls into real waits in FreeRTOS, the call will really block. But the protothreads assume that other protothreads continue while they're blocked.
So, #1 implies you cannot just convert protothreads to tasks, and #2 implies you must convert protothreads to tasks (if you use FreeRTOS blocking primitives, like xEventGroupWaitBits().
The most straightforward approach will be to put all your protothreads in one task, and continue polling within that task.
Thats exactly the kind of way I want to go. Please see my update in the question. You seam to have seen such stuff already. Any further hints for me?
– Marcel
Nov 21 at 10:49
your second point is a big issue on my list. My current idea is to convert most or all protothread code behind the scenes into a semi-blocking combination of waiting for the real event (e.g. data was sent on a serial channel) and asking a new internal timer module if any other protothread is un-blocking and we need to go there. I find it a very interesting puzzle to solve...
– Marcel
Nov 21 at 11:12
also @Marcel: I have never used FreeRTOS but glancing over the documentation it looks like they implemented exactly the ProtoThreads functionality - they termed it Coroutines there. Maybe the mapping is close to 1:1
– Vroomfondel
Nov 21 at 13:31
@Doug Currie Oh thats a missunderstanding. The legacy firmware use not based on FreeRTOS Coroutines / protothreads. I used directly Adam Dunkels protothread implementation. Its just under 50 lines of C headers. Nothing more. I won't port that to FreeRTOS.
– Marcel
Nov 22 at 9:35
add a comment |
You are facing two big gotchas...
- Since the old code is implemented using protothreads (coroutines), there is never any asynchronous resource contention between them. If you split these into FreeRTOS tasks, there there will be preemptive scheduling task switches; these switches may occur at places where the protothreads were not expecting, leaving data or other resources in an inconsistent state.
- If you convert any of your protothreads' PT_WAIT calls into real waits in FreeRTOS, the call will really block. But the protothreads assume that other protothreads continue while they're blocked.
So, #1 implies you cannot just convert protothreads to tasks, and #2 implies you must convert protothreads to tasks (if you use FreeRTOS blocking primitives, like xEventGroupWaitBits().
The most straightforward approach will be to put all your protothreads in one task, and continue polling within that task.
You are facing two big gotchas...
- Since the old code is implemented using protothreads (coroutines), there is never any asynchronous resource contention between them. If you split these into FreeRTOS tasks, there there will be preemptive scheduling task switches; these switches may occur at places where the protothreads were not expecting, leaving data or other resources in an inconsistent state.
- If you convert any of your protothreads' PT_WAIT calls into real waits in FreeRTOS, the call will really block. But the protothreads assume that other protothreads continue while they're blocked.
So, #1 implies you cannot just convert protothreads to tasks, and #2 implies you must convert protothreads to tasks (if you use FreeRTOS blocking primitives, like xEventGroupWaitBits().
The most straightforward approach will be to put all your protothreads in one task, and continue polling within that task.
answered Nov 20 at 20:57
Doug Currie
35.2k76108
35.2k76108
Thats exactly the kind of way I want to go. Please see my update in the question. You seam to have seen such stuff already. Any further hints for me?
– Marcel
Nov 21 at 10:49
your second point is a big issue on my list. My current idea is to convert most or all protothread code behind the scenes into a semi-blocking combination of waiting for the real event (e.g. data was sent on a serial channel) and asking a new internal timer module if any other protothread is un-blocking and we need to go there. I find it a very interesting puzzle to solve...
– Marcel
Nov 21 at 11:12
also @Marcel: I have never used FreeRTOS but glancing over the documentation it looks like they implemented exactly the ProtoThreads functionality - they termed it Coroutines there. Maybe the mapping is close to 1:1
– Vroomfondel
Nov 21 at 13:31
@Doug Currie Oh thats a missunderstanding. The legacy firmware use not based on FreeRTOS Coroutines / protothreads. I used directly Adam Dunkels protothread implementation. Its just under 50 lines of C headers. Nothing more. I won't port that to FreeRTOS.
– Marcel
Nov 22 at 9:35
add a comment |
Thats exactly the kind of way I want to go. Please see my update in the question. You seam to have seen such stuff already. Any further hints for me?
– Marcel
Nov 21 at 10:49
your second point is a big issue on my list. My current idea is to convert most or all protothread code behind the scenes into a semi-blocking combination of waiting for the real event (e.g. data was sent on a serial channel) and asking a new internal timer module if any other protothread is un-blocking and we need to go there. I find it a very interesting puzzle to solve...
– Marcel
Nov 21 at 11:12
also @Marcel: I have never used FreeRTOS but glancing over the documentation it looks like they implemented exactly the ProtoThreads functionality - they termed it Coroutines there. Maybe the mapping is close to 1:1
– Vroomfondel
Nov 21 at 13:31
@Doug Currie Oh thats a missunderstanding. The legacy firmware use not based on FreeRTOS Coroutines / protothreads. I used directly Adam Dunkels protothread implementation. Its just under 50 lines of C headers. Nothing more. I won't port that to FreeRTOS.
– Marcel
Nov 22 at 9:35
Thats exactly the kind of way I want to go. Please see my update in the question. You seam to have seen such stuff already. Any further hints for me?
– Marcel
Nov 21 at 10:49
Thats exactly the kind of way I want to go. Please see my update in the question. You seam to have seen such stuff already. Any further hints for me?
– Marcel
Nov 21 at 10:49
your second point is a big issue on my list. My current idea is to convert most or all protothread code behind the scenes into a semi-blocking combination of waiting for the real event (e.g. data was sent on a serial channel) and asking a new internal timer module if any other protothread is un-blocking and we need to go there. I find it a very interesting puzzle to solve...
– Marcel
Nov 21 at 11:12
your second point is a big issue on my list. My current idea is to convert most or all protothread code behind the scenes into a semi-blocking combination of waiting for the real event (e.g. data was sent on a serial channel) and asking a new internal timer module if any other protothread is un-blocking and we need to go there. I find it a very interesting puzzle to solve...
– Marcel
Nov 21 at 11:12
also @Marcel: I have never used FreeRTOS but glancing over the documentation it looks like they implemented exactly the ProtoThreads functionality - they termed it Coroutines there. Maybe the mapping is close to 1:1
– Vroomfondel
Nov 21 at 13:31
also @Marcel: I have never used FreeRTOS but glancing over the documentation it looks like they implemented exactly the ProtoThreads functionality - they termed it Coroutines there. Maybe the mapping is close to 1:1
– Vroomfondel
Nov 21 at 13:31
@Doug Currie Oh thats a missunderstanding. The legacy firmware use not based on FreeRTOS Coroutines / protothreads. I used directly Adam Dunkels protothread implementation. Its just under 50 lines of C headers. Nothing more. I won't port that to FreeRTOS.
– Marcel
Nov 22 at 9:35
@Doug Currie Oh thats a missunderstanding. The legacy firmware use not based on FreeRTOS Coroutines / protothreads. I used directly Adam Dunkels protothread implementation. Its just under 50 lines of C headers. Nothing more. I won't port that to FreeRTOS.
– Marcel
Nov 22 at 9:35
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53390809%2fhow-to-best-convert-legacy-polling-embedded-firmware-architecture-into-an-event%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Things like interrupts vs polling are low-level concepts, whereas processes and threads are higher level, and event-driven even higher level. There's no obvious benefit of using event-driven patterns in real-time code. Event-driven design is more suitable for GUI and high-level programming. Mostly it is just another buzzword, which by itself does not give any obvious benefits nor disadvantages, it is just a design pattern which may or may not be useful.
– Lundin
Nov 20 at 11:45
Just have look at the two examples take out of the current firmware: the first is based on the polling pattern. The second is actually based on the polling pattern as well behind the scenes - but I want to replace polling with blocking primitives from FreeRTOS when possible. So I get an event driven scheme and are not wasting CPU power any more.
– Marcel
Nov 20 at 16:03
1
@Lundin : Mostly you make complete sense, but "no obvious benefit of using event-driven patterns in real-time code." makes no sense to me. Most hard real-time systems react to external real-time events - that is rather the definition. While you can create a deterministic system using simple time-triggered methods, many tasks require near instantaneous response to some real-time event. Perhaps you have a different concept of "event driven"; while the term is often applied to UI frameworks, it has other contexts.
– Clifford
Nov 20 at 21:24
@Clifford My definition of event-driven is that you have an API where you can register events by passing on numerous function pointers, and will then have callback functions executed when an event occurs. Such as
create_menu(on_enter, on_exit, on_selected)
, where the parameters are function pointers of a given format. What you describe rather sounds like interrupt handlers. Though of course, you can execute callbacks from inside an ISR, but that requires that the person who wrote the callback knows what they are doing, and keep it minimal.– Lundin
Nov 21 at 7:59
@Lundin I see. The term is rather more general than that, and pre-dates GUI frameworks by many years. I am not talking about interrupt handlers, but more specifically real-time events, such as event: limit switch opened, reaction: motor stops within 1ms. That may involve interrupts, but that is an implementation detail.
– Clifford
Nov 21 at 8:08