EmbeddedRelated.com
Forums
The 2024 Embedded Online Conference

Do I need a RTOS?

Started by eeboy December 23, 2008
In article <giu5pu$sg$1@news.albasani.net>, legato_summ23@mailinator.com 
says...
> > "Mark Borgerson" <mborgerson@comcast.net> wrote in message > news:MPG.23bc19f698182a2898968c@news.motzarella.org... > > In article <git353$m20$1@news.albasani.net>, legato_summ23 > > @mailinator.com says... > >> > >> "eeboy" <jason@jasonorsborn.com> wrote in messge > >> news:Tc2dncCA-4QBqszUnZ2dnUVZ_gqdnZ2d@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. > >> > > >> > 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. > >> > > >> > >> 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. > >> > >> > > 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. > > 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. >
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
On Wed, 24 Dec 2008 21:26:50 -0600, "eeboy" <jason@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
>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!
On Thu, 25 Dec 2008 02:58:15 -0600, "eeboy" <jason@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
"Mark Borgerson" <mborgerson@comcast.net> wrote in message 
news:MPG.23bc74a8118c36998968d@news.motzarella.org...
> In article <giu5pu$sg$1@news.albasani.net>, legato_summ23@mailinator.com > says... >> >> "Mark Borgerson" <mborgerson@comcast.net> wrote in message >> news:MPG.23bc19f698182a2898968c@news.motzarella.org... >> > In article <git353$m20$1@news.albasani.net>, legato_summ23 >> > @mailinator.com says... >> >> >> >> "eeboy" <jason@jasonorsborn.com> wrote in messge >> >> news:Tc2dncCA-4QBqszUnZ2dnUVZ_gqdnZ2d@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. >> >> > >> >> > 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. >> >> > >> >> >> >> 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. >> >> >> >> >> > 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. >> >> 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. >> > 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 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.
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>
Chris H wrote:
> eeboy <jason@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. -- 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>
In article <givrbd$53k$1@news.albasani.net>, legato_summ23
@mailinator.com says...
> > "Mark Borgerson" <mborgerson@comcast.net> wrote in message > news:MPG.23bc74a8118c36998968d@news.motzarella.org... > > In article <giu5pu$sg$1@news.albasani.net>, legato_summ23@mailinator.com > > says... > >> > >> "Mark Borgerson" <mborgerson@comcast.net> wrote in message > >> news:MPG.23bc19f698182a2898968c@news.motzarella.org... > >> > In article <git353$m20$1@news.albasani.net>, legato_summ23 > >> > @mailinator.com says... > >> >> > >> >> "eeboy" <jason@jasonorsborn.com> wrote in messge > >> >> news:Tc2dncCA-4QBqszUnZ2dnUVZ_gqdnZ2d@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. > >> >> > > >> >> > 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. > >> >> > > >> >> > >> >> 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. > >> >> > >> >> > >> > 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. > >> > >> 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. > >> > > 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 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. >
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
On Thu, 25 Dec 2008 12:38:07 +0100, "Legato"
<legato_summ23@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
"Jonathan Kirwan" <jkirwan@easystreet.com> wrote in message 
news:lqv7l41uf17qlk82d7thujcn71bsrtk9m0@4ax.com...
> On Thu, 25 Dec 2008 12:38:07 +0100, "Legato" > <legato_summ23@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. -- Regards, Richard. + http://www.FreeRTOS.org Designed for microcontrollers. More than 7000 downloads per month. + http://www.SafeRTOS.com Certified by T&#4294967295;V as meeting the requirements for safety related systems.

The 2024 Embedded Online Conference