Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Do I need a RTOS?

12 views
Skip to first unread message

eeboy

unread,
Dec 23, 2008, 2:10:20 PM12/23/08
to
I am bringing up my first ARM project (based on LM3S1439) and am looking
for some advice. In my particular target application the processor will be
communicating with several peripherals via SPI (2) and UART, maintaining a
file system, keeping track of time and date as well as performing a lot of
GPIO manipulation. The board is also designed with a few external
communications ports so that future additions can be accommodated.

I can see lots of wait states in the manipulation of the GPIO. For example
I might have a function that sets a pin, waits for 500ms, then clears a
pin. I could probably get away with software delays at this moment but,
given the fact that the design can scale, I don't want to introduce this
inefficiency now only to have to remove it a few months down the road. That
500ms could be precious later on. I have several timers at my disposal but
I can foresee 'running out' in the future. I can think of some elaborate
solutions to this problem but it makes for messy code.

In general I was thinking I could implement a system tick which generated
an interrupt at a known interval (say 1ms). Upon each tick, I could examine
counters associated with the periodic tasks to see if they are due for
execution. The random interrupts would be handled by the specific interrupt
handler for that peripheral (example UART receive). That seems straight
forward to me. However, how best handle (cleanly) the toggling of a pin
after a certain delay? It's not a periodic task.

Should I use a full blown RTOS? If not how should I structure my
application? I've had a look at FreeRTOS but it looks to be much more than
I need and slightly intimidating.

Any suggestions? Please feel free to speak to me like a child as I am not
extremely knowledgeable of operating systems or software architecture(I am
a EE). Also, if you can point me to or provide examples that would be
extremely helpful! Help me wrap my brain around this problem.

Thanks!


Rich Webb

unread,
Dec 23, 2008, 3:15:07 PM12/23/08
to
On Tue, 23 Dec 2008 13:10:20 -0600, "eeboy" <ja...@jasonorsborn.com>
wrote:

>I am bringing up my first ARM project (based on LM3S1439) and am looking
>for some advice. In my particular target application the processor will be
>communicating with several peripherals via SPI (2) and UART, maintaining a
>file system, keeping track of time and date as well as performing a lot of
>GPIO manipulation. The board is also designed with a few external
>communications ports so that future additions can be accommodated.
>
>I can see lots of wait states in the manipulation of the GPIO. For example
>I might have a function that sets a pin, waits for 500ms, then clears a
>pin. I could probably get away with software delays at this moment but,
>given the fact that the design can scale, I don't want to introduce this
>inefficiency now only to have to remove it a few months down the road. That
>500ms could be precious later on. I have several timers at my disposal but
>I can foresee 'running out' in the future. I can think of some elaborate
>solutions to this problem but it makes for messy code.
>
>In general I was thinking I could implement a system tick which generated
>an interrupt at a known interval (say 1ms). Upon each tick, I could examine
>counters associated with the periodic tasks to see if they are due for
>execution. The random interrupts would be handled by the specific interrupt
>handler for that peripheral (example UART receive). That seems straight
>forward to me. However, how best handle (cleanly) the toggling of a pin
>after a certain delay? It's not a periodic task.
>
>Should I use a full blown RTOS? If not how should I structure my
>application? I've had a look at FreeRTOS but it looks to be much more than
>I need and slightly intimidating.

I still like John Larkin's comment from
<ioekp314l52vs01sv...@4ax.com>

'If ever you assign a programmer to do a deep-embedded app, and he says
"first, we'll have to write/buy an rtos", fire him instantly.'

The whole thread that contains that posting would be germane. You can
use the message ID above to hit the advanced search at groups.google.com
and bring up the archived thread.

With regards to the pin toggle, it depends somewhat on what exactly 500
msecs is: "about half a second" or "500 msec +/- 1 usec."

With no other constraints, I'd do as you are close to suggesting: have
the function set the GPIO pin and also set a tick register to (using
your numbers) 500. When the ticker counts down to zero, reset the GPIO.
You could do the decrement-test-reset in the timer interrupt or have
that isr touch a master tick and let your main loop observe that event
and handle any periodic housekeeping.

Throw the characters from the UART's isr into FIFOs and handle them when
nothing else is going on.

A reasonable overall approach is a main loop with a state machine
(switch statement) followed by any "do every time" event handlers, like
a switch debouncer.

--
Rich Webb Norfolk, VA

FreeRTOS.org

unread,
Dec 23, 2008, 3:17:58 PM12/23/08
to
>I am bringing up my first ARM project (based on LM3S1439) and am looking
> for some advice. In my particular target application the processor will be
> communicating with several peripherals via SPI (2) and UART, maintaining a
> file system, keeping track of time and date as well as performing a lot of
> GPIO manipulation. The board is also designed with a few external
> communications ports so that future additions can be accommodated.

It sounds like you have a real mixed bag of requirements, notably the file
system which can be moderately complex and require relatively slow to access
(flash write times).


<snip>

> In general I was thinking I could implement a system tick which generated
> an interrupt at a known interval (say 1ms). Upon each tick, I could
> examine
> counters associated with the periodic tasks to see if they are due for
> execution. The random interrupts would be handled by the specific
> interrupt
> handler for that peripheral (example UART receive). That seems straight
> forward to me. However, how best handle (cleanly) the toggling of a pin
> after a certain delay? It's not a periodic task.


This type of architecture is perfectly adequate in a lot of cases. There
are other alternatives too that don't involve an RTOS, for example a run to
completion scheduler, co-routines, or a simple round robin scheduler. One
of the main things you have to consider though is the maintainability over
time as requirements and hardware inevitably change.

Take a look at http://www.freertos.org/FAQWhat.html#WhyUseRTOS for other
topics that may sway your decision.


> Should I use a full blown RTOS? If not how should I structure my
> application?

FreeRTOS is not a full blown RTOS in the way that VxWorks or QNX are. It is
designed for microcontrollers and therefore very small. Its really a real
time kernel or real time scheduler.

> I've had a look at FreeRTOS but it looks to be much more than
> I need and slightly intimidating.

:o) I think it is about as simple as it could be. Maybe FreeRTOS is not
intimidating in itself but the shift required in the mindset required and
the concepts of a pre-emptive multitasking system that are - these would be
constant for any RTOS solution.

I am currently writing some new documentation that is more of a step by step
guide, rather than a dry reference. Maybe that will help. Unfortunately
its not ready yet.


> Any suggestions? Please feel free to speak to me like a child

Intimidating children is one of my favourite pastimes - LOL.


as I am not
> extremely knowledgeable of operating systems or software architecture(I am
> a EE). Also, if you can point me to or provide examples that would be
> extremely helpful! Help me wrap my brain around this problem.

My suggestion would be to download the FreeRTOS Luminary examples and play
around with them for a while. Then you can try sketching together one
solution that uses FreeRTOS and one that doesn't, to see which you are most
comfortable with. Then go for that. I'm sure personally I would find the
FreeRTOS solution the easiest, but I am already familiar with the concepts
and way of thinking about a multitasking app.

--
Regards,
Richard.

+ http://www.FreeRTOS.org
Designed for microcontrollers. More than 7000 downloads per month.

+ http://www.SafeRTOS.com
Certified by TÃœV as meeting the requirements for safety related systems.


Grant Edwards

unread,
Dec 23, 2008, 4:05:51 PM12/23/08
to
On 2008-12-23, FreeRTOS.org <noe...@given.com> wrote:

> This type of architecture is perfectly adequate in a lot of cases. There
> are other alternatives too that don't involve an RTOS, for example a run to
> completion scheduler, co-routines, or a simple round robin scheduler.

I recently used "protothreads" for a project and thought it was
pretty cool. You can structure your code as if your using a
multitasking kernel, but in fact you're using state machines
and co-operative tasking:

http://www.sics.se/~adam/pt/

In my case there was some hard-realtime stuff going on that
required exclusive and intensvie use of interrupts, so the
"tasks" could neither use nor disable interrupts ever.

As FreeRTOS.org said, "using an RTOS" isn't really a Yes/No
question. There's a whole spectrum ranging from coroutines,
up through a 2KB bare-bones scheduler all the way up to something
like QNX or another "real-time" Unix-like system requiring
several MB of RAM just to get started.

--
Grant Edwards grante Yow! I wish I was a
at sex-starved manicurist
visi.com found dead in the Bronx!!

FreeRTOS.org

unread,
Dec 23, 2008, 4:22:55 PM12/23/08
to
"Grant Edwards" <gra...@visi.com> wrote in message
news:Z4udnS8boYwyz8zU...@posted.visi...

> On 2008-12-23, FreeRTOS.org <noe...@given.com> wrote:
>
>> This type of architecture is perfectly adequate in a lot of cases. There
>> are other alternatives too that don't involve an RTOS, for example a run
>> to
>> completion scheduler, co-routines, or a simple round robin scheduler.
>
> I recently used "protothreads" for a project and thought it was
> pretty cool. You can structure your code as if your using a
> multitasking kernel, but in fact you're using state machines
> and co-operative tasking:

Protothreads are a really nice system when RAM is at a real premium. The
co-routines provided in FreeRTOS are very similar. My one gripe with them
is the heavy use of macros makes debugging a bit of a swine.


> In my case there was some hard-realtime stuff going on that
> required exclusive and intensvie use of interrupts, so the
> "tasks" could neither use nor disable interrupts ever.

The newer FreeRTOS ports have a full nesting scheme where interrupts above a
user definable priority level are unaffected by critical sections - the
downside of which is that they also cannot use FreeRTOS API function - but
this allows incredibly low jitter in high frequency interrupts. Ideal for
apps such as motor control. On a Cortex M3 running at 50MHz I have measured
the jitter to be 120ns, which is exactly the jitter that would be introduced
by the Cortex interrupt tail chaining, so the RTOS adds zero. The Cortex M3
interrupt model is perfect for this though, other architectures don't do so
well.

Grant Edwards

unread,
Dec 23, 2008, 4:56:52 PM12/23/08
to
On 2008-12-23, FreeRTOS.org <noe...@given.com> wrote:
> "Grant Edwards" <gra...@visi.com> wrote in message
> news:Z4udnS8boYwyz8zU...@posted.visi...
>> On 2008-12-23, FreeRTOS.org <noe...@given.com> wrote:
>>
>>> This type of architecture is perfectly adequate in a lot of cases. There
>>> are other alternatives too that don't involve an RTOS, for example a run
>>> to
>>> completion scheduler, co-routines, or a simple round robin scheduler.
>>
>> I recently used "protothreads" for a project and thought it was
>> pretty cool. You can structure your code as if your using a
>> multitasking kernel, but in fact you're using state machines
>> and co-operative tasking:
>
> Protothreads are a really nice system when RAM is at a real premium. The
> co-routines provided in FreeRTOS are very similar. My one gripe with them
> is the heavy use of macros makes debugging a bit of a swine.

True. If you end up with a typo in one of the protothread
macro parameters (e.g. PT_WAIT_UNTIL()), it can be a real
brain-teaser.

>> In my case there was some hard-realtime stuff going on that
>> required exclusive and intensvie use of interrupts, so the
>> "tasks" could neither use nor disable interrupts ever.
>
> The newer FreeRTOS ports have a full nesting scheme where
> interrupts above a user definable priority level are
> unaffected by critical sections

I presume it requires user-configurable hardware interrupt
priorites that also control interrupt nesting (not just the
more common "priority" control that determines which pending
interrupt gets serviced)?

> - the downside of which is that they also cannot use FreeRTOS
> API function - but this allows incredibly low jitter in high
> frequency interrupts. Ideal for apps such as motor control.
> On a Cortex M3 running at 50MHz I have measured the jitter to
> be 120ns, which is exactly the jitter that would be introduced
> by the Cortex interrupt tail chaining, so the RTOS adds zero.
> The Cortex M3 interrupt model is perfect for this though,
> other architectures don't do so well.

--
Grant Edwards grante Yow! JAPAN is a WONDERFUL
at planet -- I wonder if we'll
visi.com ever reach their level of
COMPARATIVE SHOPPING ...

eeboy

unread,
Dec 23, 2008, 8:26:19 PM12/23/08
to
Thank you all for your input. That's exactly what I needed. I've got a lot
to look over and take in. Protothreads looks like it may be a good option.

I stated my generalized thoughts on a suitable scheme but was having
difficulties with the problem where I needed to engage a pin, do something
else for 500ms (exactly) then return. I got to thinking that I could
implement some sort of queue/array that I could add to as I go along. In
that queue I could add a pointer to the function that I wish to execute and
a time to execute:

Task Function Time till execution
1 PowerMotorOff 124 ticks
2 StopWaitingForData 1000 ticks
3 ......................

So, on each system tick I would go through and evaluate the array for any
pending tasks which needed to be executed (time decremented to zero). I
guess it's kind of like a call back function. There would be no priority
given to these tasks but I don't think there really needs to be any for my
situation. The majority of these tasks are very simple and require very
little time to execute (like setting a bit in the I/O port register). The
issues that I can see are that I can't pass arguments and the code might
not be as modular as I'd like.

I may be describing exactly what a OS is to do... I don't know. However,
your feedback would help. Please shoot holes in my thought process.

Thanks!


Jonathan Kirwan

unread,
Dec 23, 2008, 9:33:01 PM12/23/08
to
I'm mostly responding to your point about "not a periodic task" when
discussing GPIO delays. None of what I'm saying should either
encourage you or discourage you from weighing all your other concerns.
But I thought I might point out one way to solve one problem to see if
that helps.

You pointed out the basic idea of having a regular timer (you
suggested a periodic one at 1ms.) That's reasonable and you can use
it for a lot of things (adjust the rate per your minimum delay need,
of course.) For some things you may have to deal with, you could set
up state machines attached to the timer which would get a small bit of
cpu for a moment each tick (or every so many ticks.) An example of
something somewhat complex that works okay this way would be reading
and writing a serial EEPROM "in the background." Normally, the state
machine would just stay in a quiescent state of "do nothing" until
some data needs to be transferred. Then the state machine would
transition out of the quiescent state and begin the transfer, moving
from state to state at the timer rate. (Assuming you didn't have a
hardware peripheral for all this, of course, and had to bit-bang the
transfer.)

However, you also said that the "the toggling of a pin after a certain
delay ... [is] not a periodic task." Well, counting down the counter
can be thought of as a periodic task. The problem then remains that
you might have a lot of counters to count down. A solution, so that
you only have one to worry about _ever_, is to use a delta queue for
such timing and to re-insert the desired routine given the delay to
the next event.

For example, suppose you wanted to toggle a particular pin so that it
was high for 50ms and low for 150ms. (Assume your 1ms timer event
exists and that delta queue insertion is already implemented [to be
discussed in a moment.]) The code might look like:

void SetPinHigh( void ) {
// some code needed to set the pin high
insertdq( SetPinLow, 50 );
}

void SetPinLow( void ) {
// some code needed to set the pin low
insertdq( SetPinHigh, 150 );
}

In the above case, you are responsible for queueing up the next
operation. But that is pretty easy, really. The number there just
specifies the number of timer ticks to wait out "from now." If the
delta queue handler does a reasonably fast job calling code when it is
supposed to and the operation isn't itself slow [if it is, just re-
insert the process at the start of the procedure instead of the end of
it], and/or if the timer is slow by comparison, then the triggered
events will appear to be consistently accurate.

Okay. So what is a delta queue? Assume the queue is empty. When you
try and insert a new entry (function pointer and time), it is inserted
immediately into the queue with the delay time as given. The timer
then decrements that counter and, when it reaches zero, it removes the
entry from the queue and executes the function. For the case where a
second entry is to be added before the first is executed, an example
might help:

insertdq( P1, 150 );
insertdq( P2, 250 );
insertdq( P3, 100 );
insertdq( P4, 200 );

Assume these happen back-to-back in short order, between 1ms timer
ticks, and assume the queue is empty to start. The queue will look
like the following:

After the 1st insert:
--> [ P1, 150 ] ; P1 wants to wait out 150 timer events

After the 2nd insert:
--> [ P1, 150 ] ; P1 still wants to wait out 150 timer events
[ P2, 100 ] ; P2 will then wait another 100 timer events

After the 3rd insert:
--> [ P3, 100 ] ; P3 is to wait out 100 timer events
[ P1, 50 ] ; P1 will now have to wait out only 50 more
[ P2, 100 ] ; P2 waits yet another 100 timer events

After the 4th insert:
--> [ P3, 100 ] ; P3 is to wait out 100 timer events
[ P1, 50 ] ; P1 will wait out an addition 50 (150 total)
[ P4, 50 ] ; P4 will wait yet another 50 (200 total)
[ P2, 50 ] ; P2 will wait still another 50 (250 total)

What happens in the insert code is that the time is compared to the
current entry in the queue (starts at the first.) If the time of the
process to be inserted is less or equal to it, then the process and
its time is inserted at that point (before the current entry) and the
time of the newly inserted process is subtracted from what was the
'current' entry, which had the greater or equal value. If the time of
the process to be inserted is greater, though, then the time value of
the current queue entry is subtracted from the inserting entry's time,
the current entry is advanced to the next entry, and the examination
continues as already described. Obviously, if you reach the end of
the queue, you just add the entry and its time.

If you use this method, your timer code will never have to decrement
more than one timer -- the one belonging to the queue entry at the top
of the queue. All of the other entries are automatically decremented
by doing that, as their times listed in the queue are simply how much
"more time" is needed by them than the process earlier than they are.

If the timer event handler encounters a situation where the top entry
on the queue is decremented to zero AND where one or more entries
after it also have their times listed as zero, then the timer event
handler should call the top entry's code and when that returns then
call the next entry, etc., until all 'zero' entries have been both run
and removed from the queue. It stops when it finds a queue entry with
a non-zero counter value. At that point, the count down can continue
at the next timer event.

I think you can see the value, and perhaps some of the cautions in the
above method. But it is simple enough. If you also use something
more complicated than just toggling a port pin, you can set up quite a
few 'state machines', each hooked to the timer event with an
appropriate delay queued up for the next time they need a little cpu.
The main code for a state machine might have a static 'state' variable
used to index through an array of state machine functions, calling
each one in turn and allowing each state to return the value of the
next state to use. This main code would then re-schedule another
execution by inserting itself after a fixed delay, if you want.

You could also consider looking up concepts such as thunks, iterators
of the CLU variety, and cooperative coroutines. It's not hard to
write up a little bit of code to support a thread switch.

One of the nice advantages of doing the little bit of coding you need,
yourself, is that you know what you have and understand it well.
Hauling in an operating system written for general purpose use can be
a fair learning curve, which may easily compare to the work involved
in writing your own delta queue code or thread switch function. Worth
considering.

Jon

eeboy

unread,
Dec 23, 2008, 11:31:13 PM12/23/08
to
That was an excellent example! It makes a lot of sense to me and I can't
see any immediate flaws with my intended application in mind. It also seems
'simple' enough that I can write it without investing a great deal of time.
I think I'll use that as my starting point. I am very much in agreement
with your comment on rolling your own. It's always been much more difficult
for me to inherit code or use others code than it is to make my own (within
reason of course). I'll get the benefit of learning how it works and what
it's positive and negative attributes are.

Just a few questions about implementation of such a scheme...

1) Are there any stats besides the number of items presently in the queue
that would be helpful to maintain?

2) I would envision using malloc and memcpy to implement the queue. Are
these appropriate (efficient enough) in the embedded world? I would assume
so but...

Also, any recommended books on this subject matter (keeping in mind that I
have zero experience in this realm)?

Thanks!

Jonathan Kirwan

unread,
Dec 24, 2008, 3:09:51 AM12/24/08
to
On Tue, 23 Dec 2008 22:31:13 -0600, "eeboy" <ja...@jasonorsborn.com>
wrote:

>That was an excellent example! It makes a lot of sense to me and I can't
>see any immediate flaws with my intended application in mind. It also seems
>'simple' enough that I can write it without investing a great deal of time.
>I think I'll use that as my starting point. I am very much in agreement
>with your comment on rolling your own. It's always been much more difficult
>for me to inherit code or use others code than it is to make my own (within
>reason of course). I'll get the benefit of learning how it works and what
>it's positive and negative attributes are.

Daring to assume you are responding to me, see replies below:

>Just a few questions about implementation of such a scheme...
>
>1) Are there any stats besides the number of items presently in the queue
>that would be helpful to maintain?

A singly-linked list of function addresses and delay times is probably
fine, given that I know nothing much more about your application.

>2) I would envision using malloc and memcpy to implement the queue. Are
>these appropriate (efficient enough) in the embedded world? I would assume
>so but...

My own penchant would be to entirely avoid malloc and its ilk.
Instead, I would make an educated guess about how many slots you will
need and allocate them as an array. More efficient, as compilers can
readily convert some of the references to elements to direct memory
references. And you get compile-time errors instead of run-time
errors you would have no idea what to do with if you got them at that
time.

I'd recommend that your array/list be arranged into three semantic
regions -- the head of one "eating" the tail of the next, in a sense.
Every slot in the list should be in one of three conceptual queues
(although they are all in the same physical array):

(1) the "avail" list of slots that can be used to add a new
function to be called later;
(2) the "sleep" list of functions that are still waiting for
their time to come; and
(3) the "ready" list of functions that have timed out but
have not yet been executed.

The three queue-head pointers are used to reference the first entry in
their particular queue. All linked entries in the chain, following
the one indicated by the queue-head, belong to that queue until the
first entry of the adjacent queue is seen. If two adjacent queue
heads reference the same entry then the "earlier" queue-head owns an
empty queue. I hope that's plain enough. But just to be certain,
examine the following diagram for clarification.

Sleep ---------------------------------> -> [] -> [] -> [] -
/ |
Ready -------------> -> [] -> [] -> [] - |
/ |
Avail ---> [] -> [] - |
|
^ |
|_________________________________________________|

The queue-head pointers can be advanced in only one direction. For
example, it is illegal to move an entry from the sleep-queue to the
avail-queue by backing up the avail-queue's "head." Instead, the
entry must be moved from the sleep-queue to the ready-queue first, by
advancing the sleep-queue "head." The avail-queue "head" is advanced
when a new entry is inserted into the sleep queue. The ready-queue
"head" is advanced when a function that is ready to execute has been,
in fact, executed. The sleep-queue "head" is advanced when an entry
has timed-out and is now ready to be executed.

A queue is empty when its "head" runs up against the following queue's
"head" (as I already mentioned.) So, the avail-queue is empty when
the avail-queue's "head" is equal to the ready-queue's "head". The
ready-queue "follows" the avail-queue. (Kind of like the scissors
paper rock game.)

If you follow this, so far, it's appropriate to examine the possible
"states" of the three queues to point out an important design issue.
Each queue may be either empty or not empty. With three queues, this
leads to eight states. The following table shows the different
combinations and compares the associated "head" pointers for the
queues with each other. I've used a (+) to indicate a non-empty queue
and a (-) to indicate an empty one.

Sleep Ready Avail Pointer conditions Description of condition
--------------------------------------------------------------------

- - - A==R, R==S, S==A Meaningless
+ - - A==R, R==S, S==A All slots are sleeping
- + - A==R, R==S, S==A All slots are ready to run
- - + A==R, R==S, S==A All slots are available
- + + A<>R, R<>S, S==A No slots are sleeping
+ - + A<>R, R==S, S<>A No slots are ready to run
+ + - A==R, R<>S, S<>A No slots available
+ + + A<>R, R<>S, S<>A No empty queues

As you can see, four of the states are indistinguishable from each
other if the only thing you do is compare pointers. This problem can
easily be resolved by insisting that the avail-queue never be allowed
to become empty (a simpler choice than keeping either of the other two
queues non-empty.) With that assertion made, we can conclude that
when the three queue head-pointers are equal to each other, the
avail-queue is full and the other two queues are empty.

I'd use at least three subroutines to manage the three queues: an
insert function which moves entries from the avail-queue to the
sleep-queue by advancing the head-pointer of the avail-queue; an
execute function which moves entries from the ready-queue to the
avail-queue by advancing the head-pointer to the ready-queue, and the
"Timer" function moves entries from the sleep-queue to the ready-queue
by advancing the head-pointer to the sleep-queue. These three
functions have exclusive access to the head-pointer of the their
respective queues and this design decision is crucial to removing the
need for interrupt masking in order to protect the data structures
from concurrency/re-entrancy problems.

>Also, any recommended books on this subject matter (keeping in mind that I
>have zero experience in this realm)?

Hmm. I first read the term "delta queue" in a book by Douglas Comer
in 1984 called "Operating System Design - The XINU Approach." Look on
this web page, 2nd item down, for it:

http://www.cs.purdue.edu/homes/dec/osbooks.html

There are subsequent books on XINU, but none so clear and easy to read
as the first one.

Jon

Jonathan Kirwan

unread,
Dec 24, 2008, 3:24:55 AM12/24/08
to
By the way, here are some questions you might want to ponder. I had
originally laid out what appeared to be a very simple arrangement.
However:

(1) Why did I choose a circular queue for my suggestion to you? (You
should be able to find __more__ than just one good reason.)

(2) Assuming (1) for whatever reason you may find, why not only have
an avail queue and a sleep queue? Why bother with a run queue since,
obviously, you may want to run the functions listed there right away
and so it will come to pass that they are immediately in the avail
queue after running? (Again, you might come up with more than one
good reason, here, too.)

Think about things a little. The answers to the above, by the way, is
not to be directly found in Comer's book that I mentioned in the
earlier post. To find these, you must consider just a little bit.

Anyway, best of luck. But I've found this technique to be extremely
easy to use and understand and remarkably versatile. It works hand
and glove with state machines, should you need them, as well. And it
can be adapted to pre-emption, if you are so inclined at some point,
where you "round-robbin" within the run queue, for example. Anyway,
it's very little code, very little data, and big-useful.

Jon

Legato

unread,
Dec 24, 2008, 5:32:56 AM12/24/08
to

"eeboy" <ja...@jasonorsborn.com> wrote in messge
news:Tc2dncCA-4QBqszU...@giganews.com...

The FreeRTOS site has a nice tutorial on RTOSes and why and when you need
them. Read it, and tell me what you think.

In general, if you are doing lots (more than 3) different taskson your MCU
which are timing dependent and with different timing requirements than you
need an RTOS.

Chris H

unread,
Dec 24, 2008, 6:37:26 AM12/24/08
to
In message <x7idnZ3HceyMJszU...@giganews.com>, eeboy
<ja...@jasonorsborn.com> writes

>2) I would envision using malloc and memcpy to implement the queue. Are
>these appropriate (efficient enough) in the embedded world?

No.... MISRA-C bans malloc

> I would assume
>so but...
>
>Also, any recommended books on this subject matter (keeping in mind that I
>have zero experience in this realm)?

Read MISRA-C

--
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
\/\/\/\/\ Chris Hills Staffs England /\/\/\/\/
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/

FreeRTOS.org

unread,
Dec 24, 2008, 6:52:22 AM12/24/08
to
"eeboy" <ja...@jasonorsborn.com> wrote in message
news:x7idnZ3HceyMJszU...@giganews.com...

> That was an excellent example! It makes a lot of sense to me and I can't
> see any immediate flaws with my intended application in mind.

Things to be careful of:

+ If you are inserting into the list of timers from within an interrupt
service routine then you will have to ensure the list remains very short,
otherwise the operation will take too long (walking the list).

+ If you are inserting into the list of timers from outside of an interrupt
service routine then you will have to handle the case where a timer
interrupt occurs while you are performing the insertion.

+ If the timer expires, you perform an action, then add yourself to the list
of timers again, you will have to ensure that the entire operation occurs
within one time slice, otherwise the frequency at which the function is
called will be too slow. This is because you are using a relative time (a
time from when the function is called) rather than an absolute time. You
can change the scheme to use an absolute time instead. (see the difference
between the vTaskDelay() and vTaskDelayUntil() functions in FreeRTOS).

Dave Boland

unread,
Dec 24, 2008, 10:11:55 AM12/24/08
to
eeboy,

Some guidelines I have found true are to use a RTOS when you have a
number of asynchronous interrupts, and used a timed loop (as you
suggested) when everything is predictable. For example, a RTOS is
good when dealing with a pumping station where you are controlling
some pumps and valves based on fluid quantity flow or in a tank. You
have no idea what the situation will be moment to moment, but the RTOS
will let you assign priorities to various tasks and run the most
important ones as soon as possible (there are no zero latency interrupts).

On the other hand, let's say you were designing an engine controller.
You could use a timed loop (and most of them do) to check the mass
air flow sensor, the coolant temperature, the battery voltage, and
engine position (% revolution), then calculate the plug and injector
firing time and duration.

Since I don't know your application well enough to advise, you will
have to ask yourself which model is closest to your needs. Keep in
mind that the RTOS involves more learning time and more over-head, but
it is more flexible. Also, some RTOS's do file systems, others don't.
Some will work on your processor, others won't.

Dave,

Mark Borgerson

unread,
Dec 24, 2008, 1:06:35 PM12/24/08
to
In article <git353$m20$1...@news.albasani.net>, legato_summ23
@mailinator.com says...
I would say that's true only if your longest interrupt handler take more
time than the timing precision of your most critical task. Otherwise
queues filled in the interrupt handler and emptied in a main loop may
be sufficient.

In a recent project, I managed to count two input frequencies with
PPM resolution, store data on an SD card, keep track of a process state
machine and respond to user input without an RTOS. It took a while to
get a handle on the timer resources of the processor (MSP430), but I
don't think an RTOS would have made things any easier.


Mark Borgerson


Jonathan Kirwan

unread,
Dec 24, 2008, 3:03:43 PM12/24/08
to
On Wed, 24 Dec 2008 11:52:22 -0000, "FreeRTOS.org" <noe...@given.com>
wrote:

>Things to be careful of:
>
>+ If you are inserting into the list of timers from within an interrupt
>service routine then you will have to ensure the list remains very short,
>otherwise the operation will take too long (walking the list).

A good point to keep in mind, although it remains true that inserting
processes that need to be run "soon" will get inserted quite quickly,
usually. (There is always the perverse case where you have hundreds
of functions to call all at once, I suppose, and earlier than the one
being inserted.) And if the function to be inserted needs to be
inserted towards the end, chances are that you can afford the time
needed to insert it there without

>+ If you are inserting into the list of timers from outside of an interrupt
>service routine then you will have to handle the case where a timer
>interrupt occurs while you are performing the insertion.

Not as I helped design the structure, illustrated here. If you look
at my layout, each queue owns exactly one pointer. No other function
may modify it and there is only one direction the pointer may be
moved. The upshot is that there is no need to guard them and no
problems created when one function is moving its pointer when the
timer event takes place, for example. I wrote a paragraph on this
narrow point, alone.

I've not had a problem with some actual coding (which I could post),
even with timer events (on one application that has been running as an
embedded instrument now for ... almost 20 years ... and remains a
flagship product, today) at 2us intervals -- which is very fast.

>+ If the timer expires, you perform an action, then add yourself to the list
>of timers again, you will have to ensure that the entire operation occurs
>within one time slice, otherwise the frequency at which the function is
>called will be too slow. This is because you are using a relative time (a
>time from when the function is called) rather than an absolute time. You
>can change the scheme to use an absolute time instead. (see the difference
>between the vTaskDelay() and vTaskDelayUntil() functions in FreeRTOS).

Yes. That can be an issue. It turns out that with the heart beat set
long enough, or with execution rates fast enough, re-inserting with a
call at the beginning of the code works without timing jitter. On a
processor with 75ns cycle times and 2us timer events, no jitter at
all. Scoped output is precise without variation.

One just needs to be aware, but not afraid.

Jon

Legato

unread,
Dec 24, 2008, 3:24:18 PM12/24/08
to

"Mark Borgerson" <mborg...@comcast.net> wrote in message
news:MPG.23bc19f69...@news.motzarella.org...

I'm pretty sure it would have, because that's what RTOS's are for: to get
tasks with wildly different timing requirements to work smoothly. Sure you
can do it yourself with some tricky programming, but a RTOS is much more
reliable and results in easier to understand and maintainable code.


Jonathan Kirwan

unread,
Dec 24, 2008, 3:31:46 PM12/24/08
to
On Wed, 24 Dec 2008 20:03:43 GMT, Jonathan Kirwan
<jki...@easystreet.com> wrote:

>A good point to keep in mind, although it remains true that inserting
>processes that need to be run "soon" will get inserted quite quickly,
>usually. (There is always the perverse case where you have hundreds
>of functions to call all at once, I suppose, and earlier than the one
>being inserted.) And if the function to be inserted needs to be
>inserted towards the end, chances are that you can afford the time

>needed to insert it there.

(left a dangling word there, sorry)

Also, the issue depends a lot on how many insertions there are, per
timer tick. One of the huge advantages of a delta queue is that the
timer event can be set VERY FAST, providing excellent resolution,
because it only looks at exactly ONE queue entry, ever. Because of
this, the price paid for a fast timer and fine-grained resolution is
uniquely low. If the number of pending processes/functions isn't too
large, there is no real worry. If it is, then you pay more for
insertions but you still pay almost zero for each timer tick.

One of the things which may be hidden by such a discussion, and which
I'd like to bring back onto the top of the table in plain view, is
that timer interrupts -- even well crafted ones -- eat up a chunk of
time. For example, if you have a timer event once each 100us and if
the timer event even when doing nothing more than keeping a counter
and checking a few things uses up 10us in its execution, then you are
using up 10us out of each 100us, or 10% of the CPU. The beauty of
this this arrangement is that the execution time of the timer is kept
deterministic (predictable) and short, regardless of the number of
functions waiting. Often, that benefit is very helpful.

In most of my applications using this technique, I have a modest
number of waiting functions (small array to keep, low insertion time)
and the arrangement allows incredible timing resolution if I want it.
And when that timing resolution isn't needed, it's even better.

Of course, I also have a fuller operating system I've written that I
use in some applications -- one that permits compile-time
configuration of (1) preemption vs cooperation, (2) the use of a real
time clock resource (or not), (3) the availability of process/thread
quantums, (4) the availability of a sleep queue, (5) process
priorities (or not), (6) semaphore queues, (7) process/thread
messages, (8) singly or doubly linked queues (memory saving option for
micros with less than 100 bytes of RAM, this can be very useful
despite the execution time costs), (9) limits on the process/thread
queues [I use fixed arrays for generated compiler code efficiency], to
name many. It relies upon the compilation process to include or
remove entire regions of code, to modify the means it uses to link and
unlink queue entries, to include or exclude data space, etc. It can
work on very tiny micros requiring as little as three bytes per thread
of data space and only a hundred words of code. But the delta queue
works for many applications, too, and is very simple to write and
understand well.

Jon

eeboy

unread,
Dec 24, 2008, 10:26:50 PM12/24/08
to
Jon,

I am a bit confused now... I have the original idea of the delta queue
that you presented. I understand it well. Now I am trying to translate that
to the setup you present below.

>I'd recommend that your array/list be arranged into three semantic
>regions -- the head of one "eating" the tail of the next, in a sense.
>Every slot in the list should be in one of three conceptual queues
>(although they are all in the same physical array):
>
> (1) the "avail" list of slots that can be used to add a new
> function to be called later;
> (2) the "sleep" list of functions that are still waiting for
> their time to come; and
> (3) the "ready" list of functions that have timed out but
> have not yet been executed.

Avail slots are by nature empty correct? I'll bring this up a bit
later...

>
> Sleep ---------------------------------> -> [] -> [] -> [] -
> / |
> Ready -------------> -> [] -> [] -> [] - |
> / |
> Avail ---> [] -> [] - |
> |
> ^ |
> |_________________________________________________|
>


The way I read the diagram and the text... they don't jive together. This
is how I understand it... Say my array is 8 slots long at some arbitrary
point in RAM.

Address Pointer Task TicksTilExecution
------------------------------------------------------
0x00020 RP TaskA
0x00021 TaskB
0x00022 SP TaskC 50
0x00023 TaskD 25
0x00024 TaskE 100
0x00025 AP --
0x00026 --
0x00027 --

After the two tasks (A and B) execute they disappear, RP now equals SP
indicating no running tasks and some time has disappeared from the next
sleep task. Assume I've also added another task F. My array now looks
like:

Address Pointer Task TicksTilExecution
------------------------------------------------------
0x00020 --
0x00021 --
0x00022 RP,SP TaskC 11
0x00023 TaskD 25
0x00024 TaskE 100
0x00025 TaskF 500
0x00026 AP --
0x00027 --

Assuming I add no more tasks my array would look like this once everything
has been executed:

Address Pointer Task TicksTilExecution
------------------------------------------------------
0x00020 --
0x00021 --
0x00022 --
0x00023 --
0x00024 --
0x00025 --
0x00026 AP,RP,SP --
0x00027 --

It seems efficient... it just looks odd since items don't "push to the
top" and the pointers loop around the boundaries. Do I understand right?

>As you can see, four of the states are indistinguishable from each
>other if the only thing you do is compare pointers. This problem can
>easily be resolved by insisting that the avail-queue never be allowed
>to become empty (a simpler choice than keeping either of the other two
>queues non-empty.) With that assertion made, we can conclude that
>when the three queue head-pointers are equal to each other, the
>avail-queue is full and the other two queues are empty.

I brought up the point of the avail slots being empty earlier because of
this paragraph. How do I keep something in an empty slot?

>I'd use at least three subroutines to manage the three queues: an
>insert function which moves entries from the avail-queue to the
>sleep-queue by advancing the head-pointer of the avail-queue; an
>execute function which moves entries from the ready-queue to the
>avail-queue by advancing the head-pointer to the ready-queue, and the
>"Timer" function moves entries from the sleep-queue to the ready-queue
>by advancing the head-pointer to the sleep-queue. These three
>functions have exclusive access to the head-pointer of the their
>respective queues and this design decision is crucial to removing the
>need for interrupt masking in order to protect the data structures
>from concurrency/re-entrancy problems.

From this paragraph I get three functions:

Insert: Walks queue between SP and AP to determine appropriate slot to add
new task, inserts (and moves others down if necessary) then increments AP

Execute: Increments the RP and calls the function pointed to

IntHandler (on system tick): Decrements tick count on the task pointed to
by the SP, if tick count==0 then increment SP and call Execute function
above

If I declare the SP, AP and RP static in their respective functions
couldn't that backfire on me?


>Hmm. I first read the term "delta queue" in a book by Douglas Comer
>in 1984 called "Operating System Design - The XINU Approach." Look on
>this web page, 2nd item down, for it:
>
>http://www.cs.purdue.edu/homes/dec/osbooks.html
>
>There are subsequent books on XINU, but none so clear and easy to read
>as the first one.

99 cents on Amazon... on its way. :)


Thanks!

Mark Borgerson

unread,
Dec 25, 2008, 12:41:39 AM12/25/08
to
In article <giu5pu$sg$1...@news.albasani.net>, legato...@mailinator.com
says...
RTOS or not, I still would have to write the interrupt handlers and
routines to do the frequency counting, SD card storage, and user
interface. I don't think an RTOS would have helped much, since the
only task requiring precise timing was the frequency counting. I think
that task would have been difficult for an RTOS without a lot of
interaction with the hardware timers.

Certainly an RTOS could have been used to create a task that would
start the timing interval and block until the interval was up (about
900mSec later). There would still have to be interrupt handlers to
count the input pulses and accumulate the master clock count.
I don't know if the result would have been simpler than a 20-line
state machine called from the 100hz RTC tick interrupt.

I suppose that the value of an RTOS goes up as some exponential of
the number of tasks. With one or two hard real-time tasks and
one or two less demanding tasks, I haven't found an RTOS to
be the right choice on MSP430 systems. On more complex systems
with faster ARM processors, I've found it simpler to loosely
couple the ARM with an MSP430 or other processor and have the
hard real-time stuff get it's own processor.

I do have one system that would benefit from an RTOS. However,
the powers that be have decreed that the system must use Linux.
Linux provides networking and a file system, but it makes
measuring and controlling latency on the real-time tasks
troublesome---to say the least.

I've got a half dozen various ARM7 systems sitting around
waiting for a free month for me to pull the MicroC/OS-II book
and CD off the shelf. Maybe when I get that month to
climb a bit higher on the RTOS learning curve, my opinion
will change.


Mark Borgerson

Jonathan Kirwan

unread,
Dec 25, 2008, 2:35:46 AM12/25/08
to
On Wed, 24 Dec 2008 21:26:50 -0600, "eeboy" <ja...@jasonorsborn.com>
wrote:

>I am a bit confused now... I have the original idea of the delta queue
>that you presented. I understand it well. Now I am trying to translate that
>to the setup you present below.

Well, that's what happens going from concept (easy to avoid thinking
about confusing details) to practical implementation (where all those
nasty details impinge), I suppose. Sorry about that.

>>I'd recommend that your array/list be arranged into three semantic
>>regions -- the head of one "eating" the tail of the next, in a sense.
>>Every slot in the list should be in one of three conceptual queues
>>(although they are all in the same physical array):
>>
>> (1) the "avail" list of slots that can be used to add a new
>> function to be called later;
>> (2) the "sleep" list of functions that are still waiting for
>> their time to come; and
>> (3) the "ready" list of functions that have timed out but
>> have not yet been executed.
>
>Avail slots are by nature empty correct? I'll bring this up a bit
>later...

Imagine an array of structures with the structure looking like:

struct q {
unsigned char next; // array idx of next entry
unsigned char prev; // array idx of previous entry
unsigned int delta; // ticks
void (*f)( void ); // function to call, when ready
};

Something like that, anyway. You can adjust the size of the next and
prev pointers, as needed; also the delta value may be arranged as you
see fit. I suppose, ditto for f. Anyway, let's discuss something
along those lines.

So you have some array:

struct q myq[QMAX];

And queue pointers:

unsigned char sleepq;
unsigned char readyq;
unsigned char availq;

Initialize those as:

sleepq= readyq= availq= 0;

That's pretty specific and I hope it isn't confusing so far. QMAX is
whatever you want, I suppose.

Then initialize each entry in the array so that every entry at 'i' (in
other words, myq[i]) is set so that .next=n+1, .prev=n-1, .delta=0,
and .f=0.

Obviously, set these, as well:

myq[0].prev= N-1;
myq[QMAX-1].next= 0;

(Otherwise, you haven't got it circularly arranged.)

I hope all this is specific enough. The above case has all three
pointers pointing to the same place, so that means that they are all
in the avail queue, as I earlier defined them to be.

>> Sleep ---------------------------------> -> [] -> [] -> [] -
>> / |
>> Ready -------------> -> [] -> [] -> [] - |
>> / |
>> Avail ---> [] -> [] - |
>> |
>> ^ |
>> |_________________________________________________|
>>

>The way I read the diagram and the text... they don't jive together. This
>is how I understand it... Say my array is 8 slots long at some arbitrary
>point in RAM.

>Address Pointer Task TicksTilExecution
>------------------------------------------------------
>0x00020 RP TaskA
>0x00021 TaskB
>0x00022 SP TaskC 50
>0x00023 TaskD 25
>0x00024 TaskE 100
>0x00025 AP --
>0x00026 --
>0x00027 --

>After the two tasks (A and B) execute they disappear, RP now equals SP
>indicating no running tasks and some time has disappeared from the next
>sleep task.

Yes, when RP == SP then "No slots are ready to run."

>Assume I've also added another task F. My array now looks like:

>Address Pointer Task TicksTilExecution
>------------------------------------------------------
>0x00020 --
>0x00021 --
>0x00022 RP,SP TaskC 11
>0x00023 TaskD 25
>0x00024 TaskE 100
>0x00025 TaskF 500
>0x00026 AP --
>0x00027 --

Yes, I think I'm following.

>Assuming I add no more tasks my array would look like this once everything
>has been executed:
>
>Address Pointer Task TicksTilExecution
>------------------------------------------------------
>0x00020 --
>0x00021 --
>0x00022 --
>0x00023 --
>0x00024 --
>0x00025 --
>0x00026 AP,RP,SP --
>0x00027 --

Pretty much. When all the pointers are equal, the avail queue is full
and the other two are defined as empty. A queue is empty when its
"head" runs up against the following queue's "head."

>It seems efficient... it just looks odd since items don't "push to the
>top" and the pointers loop around the boundaries. Do I understand right?

Sounds like you are working it the way I have in the past, if I'm
following you.

>>As you can see, four of the states are indistinguishable from each
>>other if the only thing you do is compare pointers. This problem can
>>easily be resolved by insisting that the avail-queue never be allowed
>>to become empty (a simpler choice than keeping either of the other two
>>queues non-empty.) With that assertion made, we can conclude that
>>when the three queue head-pointers are equal to each other, the
>>avail-queue is full and the other two queues are empty.
>
>I brought up the point of the avail slots being empty earlier because of
>this paragraph. How do I keep something in an empty slot?

Hmm? Everything from AP up until RP is free. What do you mean "keep
something in an empty slot?" If all the pointers point to the same
place, everything is empty ... by definition.

>>I'd use at least three subroutines to manage the three queues: an
>>insert function which moves entries from the avail-queue to the
>>sleep-queue by advancing the head-pointer of the avail-queue; an
>>execute function which moves entries from the ready-queue to the
>>avail-queue by advancing the head-pointer to the ready-queue, and the
>>"Timer" function moves entries from the sleep-queue to the ready-queue
>>by advancing the head-pointer to the sleep-queue. These three
>>functions have exclusive access to the head-pointer of the their
>>respective queues and this design decision is crucial to removing the
>>need for interrupt masking in order to protect the data structures
>>from concurrency/re-entrancy problems.
>
>From this paragraph I get three functions:
>
>Insert: Walks queue between SP and AP to determine appropriate slot to add
>new task, inserts (and moves others down if necessary) then increments AP

Insert adds a new entry into the sleep-queue. I don't know if you
care to do this, but it is possible for you to shut down the timer
when there are no sleep queue entries. If you decide to do that, the
timer must be restarted when a new entry is inserted into an empty
sleep-queue.

Actually the insert code is made still simpler, due to the required
assumption that there is always space in the avail queue. (If you
remember, I wrote, "This problem can easily be resolved by insisting


that the avail-queue never be allowed to become empty (a simpler
choice than keeping either of the other two queues non-empty.) With
that assertion made, we can conclude that when the three queue
head-pointers are equal to each other, the avail-queue is full and the
other two queues are empty."

Besides, making sure there is an extra array entry is cheap and having
the insert code shorter and faster is a very good thing.

The insertion function is a bit more complicated than the other two.
The other queues simply advance their head-pointers, as needed. It is
not necessary to change the ordering of the circularly-linked entries
to perform their functions. However, this routine must not only move
an entry from the avail-queue to the sleep-queue, but it may also be
required to re-order entries in the sleep queue. I think I talked
about this, earlier. But as you know, you need to insert the next
process/thread in the right place. The other queues have it very
easy, by comparison. So maybe it is a doubly-good thing that you
assume there is always space in the avail queue.

The insert routine may only modify the availq variable. The other two
head-pointers may not be changed by insert. This fact requires some
insertions, those that must be placed at the head of the sleep-queue,
to be performed without changing the head-pointer to the sleep-queue.
(Vital, to make sure the structures do NOT require special guarding
from interrupts.) So keep that in mind when writing this routine.

>Execute: Increments the RP and calls the function pointed to

This routine executes the functions that were placed in the ready
queue by the timer's interrupt handler. As each function is executed,
the queue is updated to move that entry into the avail-queue. How you
decide to execute them is another issue. You might simply execute all
of those with .delta == 0, removing each in sequence, until a .delta
!= 0 entry is found. Or you might come up with another method --
perhaps firing off only one per timer interval (but then you'd need to
guard the timer's decrement process so that it doesn't decrement an
entry that is already zero and you'd pay a small price in terms of
timing jitter.) Your call, really.

The execute routine may only modify the readyq variable. As before,
the other two head-pointers may not be changed.

>IntHandler (on system tick): Decrements tick count on the task pointed to
>by the SP, if tick count==0 then increment SP and call Execute function
>above

Hmm.

The timer event (interrupt) routine "awakens" any sleeping routines in
the queue that have used up their delay time by moving them from the
sleep-queue to the ready-queue. This is done by advancing the
sleep-queue's head-pointer, sleepq. (As I mentioned before, you might
want to consider adding a feature where an empty sleep-queue causes
the timer to be turned off.)

The timer routine may only modify the sleepq variable. The other two
head-pointers may not be changed here, following the paradigm earlier
expressed.

However... you said, "call Execute function above" at some point. I
... well, I wouldn't necessarily do that there. What I often arrange
is that there is ... in main() ... a busy loop. Something like:

for ( ; ; )
execute();

It is there that I call the execute function, over and over, pumping
processes out. This is why I said you also might not want to always
fire out the processes right away in the timer, itself. Or even in
bursts when all their .delta's == 0. Instead, fire off one at a time
with execute() and call execute() as your basic busy loop in main.

Just a possibility to consider.

>If I declare the SP, AP and RP static in their respective functions
>couldn't that backfire on me?

I set them up as global for the three functions we discussed and also
an initialize routine (which should be called when main() starts.)

>>Hmm. I first read the term "delta queue" in a book by Douglas Comer
>>in 1984 called "Operating System Design - The XINU Approach." Look on
>>this web page, 2nd item down, for it:
>>
>>http://www.cs.purdue.edu/homes/dec/osbooks.html
>>
>>There are subsequent books on XINU, but none so clear and easy to read
>>as the first one.
>
>99 cents on Amazon... on its way. :)

Geez, that is cheap. Definitely get a copy. It is VERY readable --
in fact, you will rarely see something that nicely spoken for someone
who wants to understand a nice, clear O/S design and hasn't a lot of
experience with operating systems. I recommend it to anyone doing
embedded work.

Jon

eeboy

unread,
Dec 25, 2008, 3:58:15 AM12/25/08
to
>Hmm? Everything from AP up until RP is free. What do you mean "keep
>something in an empty slot?" If all the pointers point to the same
>place, everything is empty ... by definition.

Ha! Ok.... my confusion here was all based on the definition of empty.
When you said empty I was thinking that you were referring to no tasks in
the queue and requesting that I always keep some task in the queue.
However, in fact, you are simply stating that the queue length (QMAX) needs
to be chosen such that it is sufficiently large to handle the worst case.
In other words... there must always be a hole in the available queue.

>However... you said, "call Execute function above" at some point. I
>... well, I wouldn't necessarily do that there. What I often arrange
>is that there is ... in main() ... a busy loop. Something like:
>
> for ( ; ; )
> execute();
>
>It is there that I call the execute function, over and over, pumping
>processes out. This is why I said you also might not want to always
>fire out the processes right away in the timer, itself. Or even in
>bursts when all their .delta's == 0. Instead, fire off one at a time
>with execute() and call execute() as your basic busy loop in main.
>
>Just a possibility to consider.

I thought about having the main loop call the execute function but I had
two concerns:

1) If I call execute, I can not utilize the sleep pointer in comparison to
the ready pointer (ie: do until RP==SP) to determine if anything needs
execution. I guess this isn't a concern because I could simply look for
.delta ==0.

2) The more idle tasks that I stuff in my main loop the more my queue
execution precision declines. Is it your intention that the only thing in
my main loop be the execute function?

I do see the drawback of calling execute from within the ISR though. It
doesn't allow me to immediately respond to other interrupts coming in and
that could be troublesome if the function I am to execute is of substantial
length.

In a somewhat related question, at what point do you draw the line for
implementing a software delay (burn cycles) versus implementing some sort
of call back with the method described above.

For example, I might have a situation where I must wait 20ms after writing
to an external Flash memory device or perhaps a 300ms to wait after
initializing an external delay. Where/how do you determine where to draw
the line?

Ok... I think I have a clear picture. I'll get busy on implementing but I
am sure I'll have more questions at some point.

Thanks again!

Jonathan Kirwan

unread,
Dec 25, 2008, 5:52:47 AM12/25/08
to
On Thu, 25 Dec 2008 02:58:15 -0600, "eeboy" <ja...@jasonorsborn.com>
wrote:

>>Hmm? Everything from AP up until RP is free. What do you mean "keep
>>something in an empty slot?" If all the pointers point to the same
>>place, everything is empty ... by definition.
>
>Ha! Ok.... my confusion here was all based on the definition of empty.
>When you said empty I was thinking that you were referring to no tasks in
>the queue and requesting that I always keep some task in the queue.
>However, in fact, you are simply stating that the queue length (QMAX) needs
>to be chosen such that it is sufficiently large to handle the worst case.
>In other words... there must always be a hole in the available queue.

Yes. Always. Solves problems.

>>However... you said, "call Execute function above" at some point. I
>>... well, I wouldn't necessarily do that there. What I often arrange
>>is that there is ... in main() ... a busy loop. Something like:
>>
>> for ( ; ; )
>> execute();
>>
>>It is there that I call the execute function, over and over, pumping
>>processes out. This is why I said you also might not want to always
>>fire out the processes right away in the timer, itself. Or even in
>>bursts when all their .delta's == 0. Instead, fire off one at a time
>>with execute() and call execute() as your basic busy loop in main.
>>
>>Just a possibility to consider.
>
>I thought about having the main loop call the execute function but I had
>two concerns:
>
>1) If I call execute, I can not utilize the sleep pointer in comparison to
>the ready pointer (ie: do until RP==SP) to determine if anything needs
>execution. I guess this isn't a concern because I could simply look for
>.delta ==0.

I'm not following the above point. In fact, I do use RP == SP for
that determination. If they are equal, there is nothing to run.

>2) The more idle tasks that I stuff in my main loop the more my queue
>execution precision declines. Is it your intention that the only thing in
>my main loop be the execute function?

For those applications where small timing jitter is important, yes.
That's all there is. The idea here is "pump" execution when you have
a moment to do so. Just let the timer move processes to the ready
queue, but don't force their execution right away. That allows you a
lot more control about _when_. If you "force" execution on the basis
of the timer event, which can be fine too, then things start getting a
little more complicated when you need to hold things back for some
critical moment, should one arise. You can always call it from the
timer event, of course. But making it a separate function has value,
too.

One of the things this saves you from is having to deal with
pre-emption or setting up separate stacks for co-routines/processes,
while still having pretty good precision on your timing. One of the
nice advantages of the delta queue is that there is only one entry to
examine so it is very, very fast. This permits excellent resolution
in time if you want it (by setting up a faster clock event), moving an
entry from the sleep queue to the run queue. And if your for(;;)
execute(); loop is no more than that, your variation in starting such
functions is rather small on most micros. If done in the timer event,
it's extremely predictable as moving the run queue head forward one
position is ... very fast and consistent. Of course, if you have
several queued up with .delta=0 and ready to be moved and run.. then
you cannot run them all together at once.

In practice, it's just something to be aware of, but not overly
concerned about. Usually, I know almost to a microsecond how long
each function requires (or, at least, its maximum extent in time) and
what its periodic timing is supposed to be. The rest is just a matter
of setting it up in the first place and just letting them run.

It isn't a panacea, though.

>I do see the drawback of calling execute from within the ISR though. It
>doesn't allow me to immediately respond to other interrupts coming in and
>that could be troublesome if the function I am to execute is of substantial
>length.

Yes.

>In a somewhat related question, at what point do you draw the line for
>implementing a software delay (burn cycles) versus implementing some sort
>of call back with the method described above.

What would the callback do for you?

>For example, I might have a situation where I must wait 20ms after writing
>to an external Flash memory device or perhaps a 300ms to wait after
>initializing an external delay. Where/how do you determine where to draw
>the line?

If you don't have anything else to do, then there is no issue. Just
wait, I suppose. But there is no reason why other functions might not
be scheduled to run at other times. For flash transactions, where
there are necessary delays as well, you can keep a queue within that
module that tracks pending operations and have it schedule itself,
appropriately. If there is a delay to be done, it just schedules its
next event after the delay. Since no other module even cares about
the queue of flash transactions except perhaps to add some more to it,
they can be doing their thing in between. But the flash module will
be sleeping on the queue until some delay has taken place and before
going on to the next transaction to be done.

Maybe I didn't understand your point, though.

And I didn't say NOT to perform the execute() from your timer event.
Just suggested that you should keep in mind you don't necessarily have
to do that and that it can actually be better sometimes not to do
that. If you know enough about your scheduled functions to know it
won't be an issue, do it. I have had things like a serial eeprom
module driven hard by the timer interrupt; performing exactly one
state transition and then exiting. Ran in the background without any
problems. But I knew that each state transition was short, too. It's
really up to you.

You can also just do some work between calls to execute(), too. If
you really do need pre-emption, though, or want to benefit from the
clean coding that can take place when threads have separate stacks of
their own, you may want to consider an operating system of a more
traditional form -- cooperative or pre-emptive. I have to admit it is
very risky quarterbacking some project I know nothing much about. So
all this I've discussed may not be right for you.

>Ok... I think I have a clear picture. I'll get busy on implementing but I
>am sure I'll have more questions at some point.

If I'm around (and I haven't been for quite some time), I'll respond.

>Thanks again!

No problem.

Jon

Legato

unread,
Dec 25, 2008, 6:38:07 AM12/25/08
to

"Mark Borgerson" <mborg...@comcast.net> wrote in message
news:MPG.23bc74a8...@news.motzarella.org...

I guess it depends. If you can do all those things within individual time
slots without fear of missing a beat then, yes, it may be easier to do
without an RTOS. However, if you need to make a state-machine to get the
timing to work then I'd opt for an RTOS. Writing to Flash, for example, is
ussually slow. As is sending and receiving through a UART. If you also need
to do some high-speed sampling, then RTOS'es are ussually a benefit.

CBFalconer

unread,
Dec 25, 2008, 2:28:06 PM12/25/08
to
eeboy wrote:
>
> Thank you all for your input. That's exactly what I needed. I've
> got a lot to look over and take in. Protothreads looks like it
> may be a good option.

Usenet readers generally have the ability to quote material from
answered messages. This enables the resultant message to be
understandable. You need to realize that Usenet is a 'best
efforts' delivery system, and that nothing is guaranteed. There is
no reason to assume that your readers have read, or ever will be
able to read, previous messages.

See some of the following:
<http://www.catb.org/~esr/faqs/smart-questions.html>
<http://www.caliburn.nl/topposting.html>
<http://www.netmeister.org/news/learn2quote.html>
<http://cfaj.freeshell.org/google/> (taming google)
<http://members.fortunecity.com/nnqweb/> (newusers)

--
Merry Christmas, Happy Hanukah, Happy New Year
Joyeux Noel, Bonne Annee, Frohe Weihnachten
Chuck F (cbfalconer at maineline dot net)
<http://cbfalconer.home.att.net>

CBFalconer

unread,
Dec 25, 2008, 2:34:29 PM12/25/08
to
Chris H wrote:
> eeboy <ja...@jasonorsborn.com> writes
>
>> 2) I would envision using malloc and memcpy to implement the queue.
>> Are these appropriate (efficient enough) in the embedded world?
>
> No.... MISRA-C bans malloc

Which is a silly attitude. Often malloc functions are the only way
to provide efficient use of limited memory. The critical thing is
that the user is aware of the ways in which malloc can fail, and
what the causes can be, and take appropriate steps. For example,
if all malloc allocations are always of the same size, most faults
are impossible. At the same time, with proper use, you will always
be aware of a malloc failure.

Mark Borgerson

unread,
Dec 25, 2008, 2:52:40 PM12/25/08
to
In article <givrbd$53k$1...@news.albasani.net>, legato_summ23
@mailinator.com says...
That depends on the definition of 'high speed sampling'. I was doing
12 channels at 120Hz by sampling at every timer tick with the MSP430.
That took about 20% of the CPU bandwidth with 4X oversampling. The
interrupt service routine put the data into a dual-port memory where
another processor fetched it, buffered it, and wrote it to hard disk.
Average current consumption over several months was about 14mA.
(somewhat less most of the time, about 300mA for a few seconds when
writing to the hard disk every 15 minutes or so.) 12 channels at
120Hz is not really high speed sampling----but the megabytes add
up when you do it continuously for several months. (Oceanographers
love to collect all the data they can get----when it costs tens of
thousands of dollars per day to get to the equatorial Pacific and
set a mooring, then come back and pick it up months later, they
tend to collect as much data as possible and let the grad students
sort out the good stuff afterwards.)

For really high-speed sampling, say 200Ksamples/sec, I don't
know that an RTOS would be much help. On smaller/slower processors, a
task switch every 5 microseconds may not leave much time for
other tasks.

If 'high speed' means about 1KSamples/second, an RTOS may
be quite handy, particularly if you have multiple channels
with different sampling rates. However, I've also done 1KHz sampling
with an interrupt handler and a very simple state machine.


Mark Borgerson


Jonathan Kirwan

unread,
Dec 25, 2008, 5:41:34 PM12/25/08
to
On Thu, 25 Dec 2008 12:38:07 +0100, "Legato"
<legato...@mailinator.com> wrote:

>However, if you need to make a state-machine to get the
>timing to work then I'd opt for an RTOS.

One might. But using the idea that a state-machine is required to get
timing done wouldn't be a reason to force the issue.

On instruments that have been running around the world and have been,
in some incarnation or another (various products in product lines)
doing that since around 1993, I've used state machines hooked to a
timer interrupt to handle serial eeprom updates, synchronous
communications with a DSP, and keyboard and display updates. No
"RTOS" involved. Oh, and it also handled precise time delays in the
main code for determining update rates and proper averaging for the
consumer applications expecting measurements at certain intervals.

What an operating system (and I'm loathe to use the phrase 'real time'
in front of it, because of the abuse of that term) might have done for
me in aspects of the above case is to have made state machines become
simpler straight-line code that is often less mystical to an outsider
who didn't sit down and design the code. A state machine for running
a flash driver or serial eeprom driver isn't hard to develop -- it
almost falls out of the timing diagrams on data sheets -- but it's not
entirely clear code to another person without good documentation, that
includes the data sheet at hand as well as programmer-mentality used
in developing the state machine.

But it doesn't mean an RTOS is right. Any operating system developed
for a broader marketplace (whether open code and free, open code and
proprietary, shareware, commercial, or any other "property" concept)
will have many facets included with it that you don't need for your
application. Those facets, if not able to be compiled out, are
included in the application and take up space, code and data, if
nothing else. Likely, they will add execution time as well as code
bug risks. And if you are doing certain medical applications, often
adding much more work in terms of exercising every corner of them.
They add learning curves, not infrequently, too. And because of
bright lines of demarcation in their design, they may unnaturally
force code development along lines that one wouldn't have wanted to
choose had they been able to custom-code the operating system for
their needs.

Broad-based operating systems have a cost on many scales. Sometimes,
they are well worth their weight. But a state machine getting the
timing right is far down on my list of reasons to go looking for one.

>Writing to Flash, for example, is ussually slow.

And...? Is that all it takes to get you to use someone else's
operating system designed for who knows how broad a range of
interests?

>As is sending and receiving through a UART.

Cripes. UART communications has been one of those things I would have
never, ever brought up as a reason to use an O/S. If bidirectional,
the traditional approach is to use two upper-level routines and two
lower-level routines, one upper and lower for each of outbound and
inbound characters. The upper and lower halves are decoupled from
each other through what is commonly known as a buffer. Standard fair,
really. And no operating system involved. Just your garden variety
"device driver" concept, which works as well within an RTOS as for an
application without an RTOS involved. It's just plain "a good idea"
everywhere.

>If you also need
>to do some high-speed sampling, then RTOS'es are ussually a benefit.

Hardly. One of my instruments samples at rates as fast as 650ns. The
A/D converter is bit-banged, too, to operate it. That means there is
no specialized hardware there -- it is entirely software raising and
lowering control lines and grabbing data from it, instruction by
instruction. The variability in operating those lines is 5ns (driven
by the processor's own datasheet) and the software itself supports a
precise resolution in timing of 75ns -- one instruction cycle. The
application requires a range of sampling that goes from 650ns to as
long as 50us, so we are talking of about 100:1 range in sampling time.
No RTOS. Measurement rates, summarizing the gathered data, occur at
fixed intervals that are set at compile-time, but usually on the order
of from 1Hz to 10Hz.

The delta queue method I had been talking about here, in fact, was
applied in this application, almost trivially. No RTOS. Note that
here we are talking about quite a wide range of timing requirements,
including millisecond timings for eeprom and flash and UART, tens of
milliseconds for keyboard debounce, nanosecond timings for ADC, all
the way up to roughly 1-10Hz for the end user. So quite a span in
timing. No RTOS.

On the other hand, I've written my own compile-time customizable
oeprating system, too, which includes options to include or exclude a
real-time clock, process quantums, process priorities, sleep queues,
semaphore queues, process messaging, per process exception handling,
cooperative or preemptive switches, and so on. And I didn't do that
just for my health. So I do understand the value of an operating
system and I'm not trying to argue there is no value to them for an
application. Just that I don't see where you draw your lines
comparing much to where I draw mine.

--

I'm not sure, of course, but it seems you are far too easily tipped in
that direction by the least problem. Your earlier comment, "that's


what RTOS's are for: to get tasks with wildly different timing

requirements to work smoothly" is way off the mark I see. It is truly
quite trivial to handle widely different timings _without_ an RTOS
hauled into the picture, in fact. The reasoning should turn on
solving somewhat different problems than what you seem to imagine.

(Perhaps we'll even get to those areas of application space where I
see them having good value. But there is too much dross in the
conversation to go there right now.)

Jon

FreeRTOS.org

unread,
Dec 26, 2008, 5:59:46 AM12/26/08
to
"Jonathan Kirwan" <jki...@easystreet.com> wrote in message
news:lqv7l41uf17qlk82d...@4ax.com...

> On Thu, 25 Dec 2008 12:38:07 +0100, "Legato"
> <legato...@mailinator.com> wrote:
>
>>However, if you need to make a state-machine to get the
>>timing to work then I'd opt for an RTOS.
>
> One might. But using the idea that a state-machine is required to get
> timing done wouldn't be a reason to force the issue.
>
> On instruments that have been running around the world and have been,
> in some incarnation or another (various products in product lines)
> doing that since around 1993, I've used state machines hooked to a
> timer interrupt to handle serial eeprom updates, synchronous
> communications with a DSP, and keyboard and display updates. No
> "RTOS" involved.

I always advocate using the right tools for the job - sometimes that is with
an RTOS, sometimes without. I like using some of these 'automated' state
machine paradigms, when that suites the job. However, while I can cut the
grass with scissors, it would work, I would always choose to use a lawn
mower if the lawn was more than one foot square.

I often give talks where one of the subjects is when an RTOS is useful and
when not, and having multiple state machines to control fast and slow device
(especially if nested) is one indicatation that an RTOS might be the best
solution. In the same talk I take a piece of LCD code that I downloaded
from the net which is implemented as a state machine - then first ask the
class to tell me how it works (nobody has got it right yet), followed by
asking how it would need changing if the LCD controller changed and worked
at twice the speed (nobody has got it right yet). Finally we convert the
state machine to a piece of sequential code that uses the services of the
RTOS.

I have not actually done the counting, but would guess there was a lines of
code ratio of about 5 to 1, and the RTOS solution is so simple you could not
mistake how it worked because all the timing information is abstracted away.
The RTOS solution is also very maintainable - especially if you had to
maintain it when somebody else had written it. Plus the RTOS solution is
many more times efficient as it does not repeatedly call a state machine
when there is nothing to do or just to click through a timing state - plus
the RTOS solution allows messages to be written to the LCD from multiple
tasks without the application writer even having to consider it - plus the
RTOS solution allows messages to be written to the LCD from interrupts
without the application writer having to consider mutual exclusion - plus
.......... The original state machine solution did work though, but I would
guess it took ages to write and even longer to debug.

Despite being the author of a kernel I provide it for free so I have no
vested interest in going around saying "though shalt always use an RTOS",
but in the example you give I would suggest it would have been so much
simpler - maybe not if it was the first time you had been confronted with an
RTOS, but definitely after you were familiar with the concepts and methods
used. Using an RTOS would also not prevent you from using high priority
interrupts (that are not affected by critical sections) to perform other
timing critical bits of functionality.

bigbrownbeast...@googlemail.com

unread,
Dec 26, 2008, 1:09:41 PM12/26/08
to
John,

it sounds like you have your head in the sand.

Jonathan Kirwan

unread,
Dec 26, 2008, 4:35:31 PM12/26/08
to
On Fri, 26 Dec 2008 10:59:46 -0000, "FreeRTOS.org" <noe...@given.com>
wrote:

>"Jonathan Kirwan" <jki...@easystreet.com> wrote in message
>news:lqv7l41uf17qlk82d...@4ax.com...
>> On Thu, 25 Dec 2008 12:38:07 +0100, "Legato"
>> <legato...@mailinator.com> wrote:
>>
>>>However, if you need to make a state-machine to get the
>>>timing to work then I'd opt for an RTOS.
>>
>> One might. But using the idea that a state-machine is required to get
>> timing done wouldn't be a reason to force the issue.
>>
>> On instruments that have been running around the world and have been,
>> in some incarnation or another (various products in product lines)
>> doing that since around 1993, I've used state machines hooked to a
>> timer interrupt to handle serial eeprom updates, synchronous
>> communications with a DSP, and keyboard and display updates. No
>> "RTOS" involved.
>
>I always advocate using the right tools for the job - sometimes that is with
>an RTOS, sometimes without. I like using some of these 'automated' state
>machine paradigms, when that suites the job. However, while I can cut the
>grass with scissors, it would work, I would always choose to use a lawn
>mower if the lawn was more than one foot square.

I found a problem in the bright line drawn by the person I responded
to, regarding when an operating system is appropriate. I haven't made
any judgments, though, about whether or not some specific application,
one where I haven't much knowledge about, should or should not use an
operating system.

Here, you do exactly what I don't want to do. In fact, you start out
almost declaring that I decided to use a scissors to cut a lawn, when
that was hardly the case at all. But I will stop short of arguing
with you, since you have no idea what processor I was using or other
important aspects of the project which bounded my choices. Suffice it
that I've written more than one operating system before (including a
widely used, large scale time-sharing system) and at the time had that
very handy compile-time configurable operating system I already
mentioned -- which I assure you would have been far more appropriate
to the application than FreeRTOS would have been in that circumstance.

And I chose not to use it, for reasons you cannot possibly know about
here.

>I often give talks where one of the subjects is when an RTOS is useful and
>when not, and having multiple state machines to control fast and slow device
>(especially if nested) is one indicatation that an RTOS might be the best
>solution.

It really depends on a lot of other factors, though. What I didn't
agree with Legato about was that "that's what RTOS's are for: to get
tasks with wildly different timing requirements to work smoothly."

That isn't what they are made for -- not in my mind, anyway. And it
doesn't rise very high on my list of considerations why I'd choose to
use one.

However, you talk about "multiple state machines ... especially if
nested." Then we are on to a different subject. On that point, ...
it depends.

One of the reasons I don't like state machines is that they can easily
wind up obfuscating some otherwise rather simple concept. One usually
needs global variables (or static ones, anyway) to keep track of the
current state of the machine so that the next invocation can start
from there. That can be simply handled, for sets of trivial states,
or it can get nasty-looking. So the clarity of the code depends and,
thus, the appropriateness of the tool as well. But where you have a
separate stack for a thread, for example, much of that "getting back
to where you were, last time" goes away and the code can be very much
simpler to read and maintain. I definitely appreciate the semantics
of threads and processes.

Anyway, separate issues. I don't want to belabor all this. I entered
this area for one original reason -- to offer the idea of delta queues
to a poster, with a willingness to dig into quite specific details as
needed -- and for another off-hand one where I disagreed with the
statement, "that's what RTOS's are for: to get tasks with wildly
different timing requirements to work smoothly." Exploring all of the
various boundaries where operating systems, state machines, and a bevy
of other ideas may or may not be appropriate and debating those edges
from varying perspectives is a matter of a book, perhaps.. or several
of them. I'm not prepared to go there, right now.

For now, my contributions will be limited to delta queues and my
disagreement about the way Legato chose to write "that's what RTOS's
are for." I don't think that's their purpose.

I don't find much disagreement with the broader aspects of your
comments here. Broadly, I much prefer the clarity of threads and
processes. But that's broadly speaking. I can provide specific cases
that would argue well that a state machine is clearer, maintainable,
and far less prone to programming mistakes. But I just don't want to
get that deeply into the details right now -- it's way beyond the
reach I am willing to go in the near term. So if you can't imagine
such cases, then for now we'll just have to disagree.

>I often give talks where one of the subjects is when an RTOS is useful and
>when not, and having multiple state machines to control fast and slow device
>(especially if nested) is one indicatation that an RTOS might be the best
>solution. In the same talk I take a piece of LCD code that I downloaded
>from the net which is implemented as a state machine - then first ask the
>class to tell me how it works (nobody has got it right yet), followed by
>asking how it would need changing if the LCD controller changed and worked
>at twice the speed (nobody has got it right yet). Finally we convert the
>state machine to a piece of sequential code that uses the services of the
>RTOS.

I think a skilled programmer can pretty much argue that green is red
and that blue is pink, if they've a mind to do that. But the fact
remains that there are times and places for each of a rather wide
variety of tool selections.

Just so you understand me, as I earlier pointed out I didn't write
operating systems for my health -- I needed their benefits or else
others did and I understood the reasons why. I'm not attacking the
idea of operating systems, generally, nor FreeRTOS specifically. I
just don't want anyone imagining that this is a true line of
demarcation that always applies: "that's what RTOS's are for: to get
tasks with wildly different timing requirements to work smoothly."

It simply isn't the line to be drawn. So far as I am concerned,
anyway.

Jon

Steve at fivetrees

unread,
Dec 26, 2008, 8:23:34 PM12/26/08
to
"eeboy" <ja...@jasonorsborn.com> wrote in message
news:Tc2dncCA-4QBqszU...@giganews.com...
>I am bringing up my first ARM project (based on LM3S1439) and am looking
> for some advice. In my particular target application the processor will be
> communicating with several peripherals via SPI (2) and UART, maintaining a
> file system, keeping track of time and date as well as performing a lot of
> GPIO manipulation. The board is also designed with a few external
> communications ports so that future additions can be accommodated.

<snip>


> Should I use a full blown RTOS? If not how should I structure my
> application? I've had a look at FreeRTOS but it looks to be much more than
> I need and slightly intimidating.
>
> Any suggestions? Please feel free to speak to me like a child as I am not
> extremely knowledgeable of operating systems or software architecture(I am
> a EE). Also, if you can point me to or provide examples that would be
> extremely helpful! Help me wrap my brain around this problem.

I would say: no, you don't need an RTOS. You need to think like the EE you
are and design a synchronous state-machined system that implements your
functionality.

I'd recommend a so-called round-robin (or, more fancily, co-operative
multitasking). Implement a tight endless loop that a) deals with necessary
background tasks, such as maintaining timers, and b) monitors states - e.g.
flags set by timers or interrupts, or events set by other tasks. Never
pend - i.e. never wait with sleeps, but by maintaining state machines and
returning to the main loop - all calls *must* return within whatever latency
you care to define. (There are many possible refinements to this idea, in
terms of balancing high- and low-priority tasks, loop latency,
responsiveness, etc, but they're now under your direct control.) Again,
think in terms of a clock and synchronous state transitions.

A *huge* advantage of this approach is synchronicity - as with hardware
logic design, you don't have to worry about races and mutexes, since all
your active tasks can be effectively atomic.(And it becomes easy enough to
measure real CPU usage.) You *do*, however, need to actively limit the
runtime of each call.

HTH, in haste,

Steve
--
http://www.fivetrees.com


Steve at fivetrees

unread,
Dec 26, 2008, 8:32:37 PM12/26/08
to
"eeboy" <ja...@jasonorsborn.com> wrote in message
news:O-udnRbNLLEmEszU...@giganews.com...

> Thank you all for your input. That's exactly what I needed. I've got a lot
> to look over and take in. Protothreads looks like it may be a good option.
>
> I stated my generalized thoughts on a suitable scheme but was having
> difficulties with the problem where I needed to engage a pin, do something
> else for 500ms (exactly) then return.

If the 500ms needs to be exact, consider achieving this by hardware linkage.
It's not generally a good idea to do h/w IO in interrupts, but so long as it
has no side-effects (a read-modify-write needs to be atomic), it can be
done.

Anything done by a main loop, whether cooperative or pre-emptive, is only as
exact as the lengthiest route from iteration to iteration.

Steve
--
http://www.fivetrees.com


Chris H

unread,
Dec 27, 2008, 7:07:40 AM12/27/08
to
In message <4953E045...@yahoo.com>, CBFalconer
<cbfal...@yahoo.com> writes

>Chris H wrote:
>> eeboy <ja...@jasonorsborn.com> writes
>>
>>> 2) I would envision using malloc and memcpy to implement the queue.
>>> Are these appropriate (efficient enough) in the embedded world?
>>
>> No.... MISRA-C bans malloc
>
>Which is a silly attitude.

Not at all

> Often malloc functions are the only way
>to provide efficient use of limited memory.

Then deviate... in writing giving reason for this specific case in your
project

CBFalconer

unread,
Dec 27, 2008, 6:50:22 PM12/27/08
to
Chris H wrote:
> CBFalconer <cbfal...@yahoo.com> writes
>> Chris H wrote:
>>> eeboy <ja...@jasonorsborn.com> writes
>>>
>>>> 2) I would envision using malloc and memcpy to implement the queue.
>>>> Are these appropriate (efficient enough) in the embedded world?
>>>
>>> No.... MISRA-C bans malloc
>>
>> Which is a silly attitude.
>
> Not at all
>
>> Often malloc functions are the only way to provide efficient use
>> of limited memory. <there was more, but the p'graph got snipped>

>
> Then deviate... in writing giving reason for this specific case in
> your project

If they deviate from the C standard you are not using C. That only
leaves a very few possibilities, such as failure to recover large
enough spaces, and the use of lazy assignment (usually switch
offable). That was why I mentioned assigning uniformly sized
blocks.

Jonathan Kirwan

unread,
Dec 27, 2008, 6:58:35 PM12/27/08
to
On Sat, 27 Dec 2008 18:50:22 -0500, CBFalconer <cbfal...@yahoo.com>
wrote:

>Chris H wrote:
>> CBFalconer <cbfal...@yahoo.com> writes
>>> Chris H wrote:
>>>> eeboy <ja...@jasonorsborn.com> writes
>>>>
>>>>> 2) I would envision using malloc and memcpy to implement the queue.
>>>>> Are these appropriate (efficient enough) in the embedded world?
>>>>
>>>> No.... MISRA-C bans malloc
>>>
>>> Which is a silly attitude.
>>
>> Not at all
>>
>>> Often malloc functions are the only way to provide efficient use
>>> of limited memory. <there was more, but the p'graph got snipped>
>>
>> Then deviate... in writing giving reason for this specific case in
>> your project
>
>If they deviate from the C standard you are not using C. That only
>leaves a very few possibilities, such as failure to recover large
>enough spaces, and the use of lazy assignment (usually switch
>offable). That was why I mentioned assigning uniformly sized
>blocks.

I think he meant "deviate [from MISRA-C]" in that MISRA-C (and I'm
guessing here) permits deviations so long as the reasons for the
deviation are well-documented. I think he was responding to your
statement that malloc() may be uniquely appropriate, at times, and
defending MISRA-C's "ban malloc" by saying that you can have
exceptions to their ban, if documented.

Of course, I'm just guessing. He'll need to respond, I suppose. Just
offering my interpretation for what it is worth.

Jon

Chris H

unread,
Dec 28, 2008, 3:52:33 AM12/28/08
to
In message <4956BF3E...@yahoo.com>, CBFalconer
<cbfal...@yahoo.com> writes
>Chris H wrote:
>> CBFalconer <cbfal...@yahoo.com> writes
>>> Chris H wrote:
>>>> eeboy <ja...@jasonorsborn.com> writes
>>>>
>>>>> 2) I would envision using malloc and memcpy to implement the queue.
>>>>> Are these appropriate (efficient enough) in the embedded world?
>>>>
>>>> No.... MISRA-C bans malloc
>>>
>>> Which is a silly attitude.
>>
>> Not at all
>>
>>> Often malloc functions are the only way to provide efficient use
>>> of limited memory. <there was more, but the p'graph got snipped>
>>
>> Then deviate... in writing giving reason for this specific case in
>> your project
>
>If they deviate from the C standard you are not using C.

Idiot you are on C.A.E not c.l.c

I meant deviate from MISRA-C

Chris H

unread,
Dec 28, 2008, 3:53:36 AM12/28/08
to
In message <f2gdl4pqt89rv8prn...@4ax.com>, Jonathan Kirwan
<jki...@easystreet.com> writes

Hi Jon,

Your interpretation is correct.

Mark Borgerson

unread,
Dec 29, 2008, 1:33:34 AM12/29/08
to
In article <Z9KdnX5h3_IkGMjU...@pipex.net>,
st...@NOSPAMTAfivetrees.com says...

> "eeboy" <ja...@jasonorsborn.com> wrote in message
> news:O-udnRbNLLEmEszU...@giganews.com...
> > Thank you all for your input. That's exactly what I needed. I've got a lot
> > to look over and take in. Protothreads looks like it may be a good option.
> >
> > I stated my generalized thoughts on a suitable scheme but was having
> > difficulties with the problem where I needed to engage a pin, do something
> > else for 500ms (exactly) then return.
>
> If the 500ms needs to be exact, consider achieving this by hardware linkage.
> It's not generally a good idea to do h/w IO in interrupts, but so long as it
> has no side-effects (a read-modify-write needs to be atomic), it can be
> done.
>

I'm not sure I understand your last sentence. Are you saying that it is
not a good idea to use interupts to service hardware such as UARTS, SPI
devices or other hardware I/O devices?


> Anything done by a main loop, whether cooperative or pre-emptive, is only as
> exact as the lengthiest route from iteration to iteration.
>
> Steve


Mark Borgerson

Steve at fivetrees

unread,
Dec 29, 2008, 4:41:23 AM12/29/08
to
"Mark Borgerson" <mborg...@comcast.net> wrote in message
news:MPG.23c20f18a...@news.motzarella.org...

> In article <Z9KdnX5h3_IkGMjU...@pipex.net>,
> st...@NOSPAMTAfivetrees.com says...
>>
>> If the 500ms needs to be exact, consider achieving this by hardware
>> linkage.
>> It's not generally a good idea to do h/w IO in interrupts, but so long as
>> it
>> has no side-effects (a read-modify-write needs to be atomic), it can be
>> done.
>
> I'm not sure I understand your last sentence. Are you saying that it is
> not a good idea to use interupts to service hardware such as UARTS, SPI
> devices or other hardware I/O devices?

I was clearly too brief ;).

Somewhat less briefly: there needs to be a clear distinction between the
hardware resources owned by the top level, and those owned by interrupts.
Reasonably often a single port has pins that are serviced by interrupts, and
others that are not. In such cases one has to be sure that an interrupt
doesn't come along and change the state of the port (or its DDR) while the
top level is doing something with it, hence my reference to
read-modify-writes being atomic. Again, reasonably often one needs to
maintain a copy of the state of the entire port in software - the same
cautions apply.

As a general rule, unless the hardware support is time-critical (as is the
case with serial comms or SPI), it's better to set a flag in software under
interrupt control and allow the top level to maintain the hardware. YMMV.

Steve
--
http://www.fivetrees.com


Steve at fivetrees

unread,
Dec 29, 2008, 5:56:02 PM12/29/08
to
"Legato" <legato...@mailinator.com> wrote in message
news:givrbd$53k$1...@news.albasani.net...

>
> I guess it depends. If you can do all those things within individual time
> slots without fear of missing a beat then, yes, it may be easier to do
> without an RTOS. However, if you need to make a state-machine to get the
> timing to work then I'd opt for an RTOS.

Oooh, what an interesting statement.

Me, I love state machines. I'd much rather have a hierarchy of state
machines than an RTOS. Hell, for even a non-timing-critical sequential
system within an alien non-RT OS, I'd still use a state machine. The reason
is perhaps less to do with latency than elegance: I find a task described
entirely sequentially (with sleep()s etc) tends to have a lot of duplication
of error handling - a thread from earlier this year here, where a couple of
the people whose opinions I would otherwise respect took time to defend the
dreaded goto on this basis, illustrated my point rather well ;). (And no,
try/catch doesn't cut it either. Slightly better than the goto, but still a
nappy layer.)

YMMV, and I'd be interested to hear it.

Steve
--
http://www.fivetrees.com


Steve at fivetrees

unread,
Dec 29, 2008, 6:02:02 PM12/29/08
to
"Jonathan Kirwan" <jki...@easystreet.com> wrote in message
news:lqv7l41uf17qlk82d...@4ax.com...
> On Thu, 25 Dec 2008 12:38:07 +0100, "Legato"
> <legato...@mailinator.com> wrote:
>
>>However, if you need to make a state-machine to get the
>>timing to work then I'd opt for an RTOS.
>
> One might. But using the idea that a state-machine is required to get
> timing done wouldn't be a reason to force the issue.
>
> On instruments that have been running around the world and have been,
> in some incarnation or another (various products in product lines)
> doing that since around 1993, I've used state machines hooked to a
> timer interrupt to handle serial eeprom updates, synchronous
> communications with a DSP, and keyboard and display updates. No
> "RTOS" involved. Oh, and it also handled precise time delays in the
> main code for determining update rates and proper averaging for the
> consumer applications expecting measurements at certain intervals.

<rest of excellent message reluctantly snipped for brevity>

Bravo, I say, bravo <whistle>. Good show, old chap.

Seriously - apart from my comment about state machines as a better
mousetrap - you nailed it, dude. Absolutely my point of view too. Thanks for
saving me all that typing :).

Steve
--
http://www.fivetrees.com


Steve at fivetrees

unread,
Dec 29, 2008, 6:20:05 PM12/29/08
to
"FreeRTOS.org" <noe...@given.com> wrote in message
news:gj2df5$5jd$1...@news.motzarella.org...

Fascinating. That state machine must have really sucked.

I've had the opposite experience, but possibly that's because the state
machines were designed by me, and I'm quite good/experienced/old ;). My main
aim in such cases is to make things so clear they can't possibly be either
wrong or misunderstood. It's actually not all that hard.

However, re code downloaded from the net, I have to agree there - the
majority of 3rd-party state-machine implementations I've seen really do
suck. That doesn't mean the basic concept is flawed, though. It just means
people think sequentially, and have trouble thinking synchronously. Unless
of course you're a hardware designer, in which case such things come
naturally ;).

> I have not actually done the counting, but would guess there was a lines
> of code ratio of about 5 to 1, and the RTOS solution is so simple you
> could not mistake how it worked because all the timing information is
> abstracted away. The RTOS solution is also very maintainable - especially
> if you had to maintain it when somebody else had written it. Plus the
> RTOS solution is many more times efficient as it does not repeatedly call
> a state machine when there is nothing to do or just to click through a
> timing state

<snip>

There is some truth in this, but not 5:1. Given Moore's Law, I'm happy to
sacrifice some efficiency for robustness and clarity. But probably not 5:1.
More like 1.5:1.

I call this argument the sock argument - is the sock inside-in or
inside-out? You can have time-based or task-based paradigms - some are good,
some are bad. But thinking entirely sequentially, with no eye to
cooperation, tends to result in sucky code either way.

Steve
--
http://www.fivetrees.com


Vladimir Vassilevsky

unread,
Dec 29, 2008, 6:23:31 PM12/29/08
to

Steve at fivetrees wrote:


> Me, I love state machines. I'd much rather have a hierarchy of state
> machines than an RTOS.

I'd rather bear with the preemptive multitasking overhead then do a
cascade of the state machines.

> Hell, for even a non-timing-critical sequential
> system within an alien non-RT OS, I'd still use a state machine. The reason
> is perhaps less to do with latency than elegance: I find a task described
> entirely sequentially (with sleep()s etc) tends to have a lot of duplication
> of error handling - a thread from earlier this year here, where a couple of
> the people whose opinions I would otherwise respect took time to defend the
> dreaded goto on this basis, illustrated my point rather well ;). (And no,
> try/catch doesn't cut it either. Slightly better than the goto, but still a
> nappy layer.)
>
> YMMV, and I'd be interested to hear it.

In a state machine, the responsiveness is determined by the state with
the longest execution time. Suppose you have a math task which takes a
long time to calculate (~1 second). Okay, this task can be broken in a
sequence of the small states and the tons of the state variables, which
is going to be messy and difficult to maintain. Nevertheless you can't
split a library function such as sin() or log() into a sequence of
states. Neither you can split a function like fread(), so if there is a
file system related delay, then everything is stalled.


Vladimir Vassilevsky

DSP and Mixed Signal Design Consultant

http://www.abvolt.com

Steve at fivetrees

unread,
Dec 29, 2008, 6:41:16 PM12/29/08
to
"Vladimir Vassilevsky" <antispa...@hotmail.com> wrote in message
news:NZc6l.9907$as4....@nlpi069.nbdc.sbc.com...

> Steve at fivetrees wrote:
>> Me, I love state machines. I'd much rather have a hierarchy of state
>> machines than an RTOS.
>
> I'd rather bear with the preemptive multitasking overhead then do a
> cascade of the state machines.

Ok, noted. (Although "cascade" for me seems scarily chaotic compared to
"hierarchy" ;).)

>> Hell, for even a non-timing-critical sequential system within an alien
>> non-RT OS, I'd still use a state machine. The reason is perhaps less to
>> do with latency than elegance: I find a task described entirely
>> sequentially (with sleep()s etc) tends to have a lot of duplication of
>> error handling - a thread from earlier this year here, where a couple of
>> the people whose opinions I would otherwise respect took time to defend
>> the dreaded goto on this basis, illustrated my point rather well ;). (And
>> no, try/catch doesn't cut it either. Slightly better than the goto, but
>> still a nappy layer.)
>>
>> YMMV, and I'd be interested to hear it.
>
> In a state machine, the responsiveness is determined by the state with the
> longest execution time. Suppose you have a math task which takes a long
> time to calculate (~1 second). Okay, this task can be broken in a sequence
> of the small states and the tons of the state variables, which is going to
> be messy and difficult to maintain. Nevertheless you can't split a
> library function such as sin() or log() into a sequence of states. Neither
> you can split a function like fread(), so if there is a file system
> related delay, then everything is stalled.

All good points, well made.

I'd counter only with:

- Yes, I've occasionally had to split a lengthy (time-wise, not
action-wise) process into substates. Not too often, but yes, at such times
pre-emption seems attractive ;).

- You absolutely have a point re e.g. fread() - assuming you're using a
classic function call provided by an OS, in which case all bets are off. If
you're writing your own under a cooperative multitasker, you'd probably
split it into states anyway ;).

Thanks for response - fascinating discussion.

Steve
--
http://www.fivetrees.com


Jon Kirwan

unread,
Dec 29, 2008, 6:43:11 PM12/29/08
to

Thanks, Steve. But of course, over time here I've learned that you
and I think far too much alike!! I'm still wondering if we disagree
on anything. I can only imagine what it would be like working on a
project, together. (A real joy, I'm sure.)

Yes, I happen to appreciate state machines, too. I wrote here, "I can


provide specific cases that would argue well that a state machine is
clearer, maintainable, and far less prone to programming mistakes."

And that is not just words, but fact. There are times when I'll head
towards a state machine well before I will examine any other option
because of how consistently well they work and the ease in writing
them to cope with myriad complex logical possibilities with robust,
bullet-proof perfection. Some places, they are nearly indispensable.

Jon

Steve at fivetrees

unread,
Dec 30, 2008, 10:41:01 AM12/30/08
to
"Jon Kirwan" <jo...@infinitefactors.org> wrote in message
news:glmil4tn7i69tejh9...@4ax.com...

Consider me suitably flattered ;).

> Yes, I happen to appreciate state machines, too. I wrote here, "I can
> provide specific cases that would argue well that a state machine is
> clearer, maintainable, and far less prone to programming mistakes."
> And that is not just words, but fact. There are times when I'll head
> towards a state machine well before I will examine any other option
> because of how consistently well they work and the ease in writing
> them to cope with myriad complex logical possibilities with robust,
> bullet-proof perfection. Some places, they are nearly indispensable.

Once again, well said.

Steve
--
http://www.fivetrees.com


0 new messages