How to keep the user interface responsive? - Page 1
Forum section: electronics / general

author: e100
author_rank: Frequent Contributor
timestamp: October 15, 2013, 12:51:29 pm
content: I'm at the beginning of an embedded design for a flow meter which has a software loop with the following tasks:Measure sensor valueUpdate displayListen for communication from computer and send latest resultsProcess user input / update displayThe user interface will probably use a rotary encoder with a push switch to move a cursor around a screen and enter data.What's the normal way of capturing user input while the software is busy doing time critical tasks such as taking a measurement or sending data? Do you hook the input switches to an interrupt then set a flag that marks the interrupted measurement or communication operation as failed, then gracefully exit that section of code and jump to the user interface handler code?Is there a better way?Mike

author: nowlan
author_rank: Frequent Contributor
timestamp: October 15, 2013, 12:56:48 pm
content: What is the platform?Usually your mcu will have serial interrupt handler to manage communication.The rotary encoder may be on another interrupt (lower priority), or you could just poll it.I used to just read button during a main loop.

author: c4757p
author_rank: Super Contributor
timestamp: October 15, 2013, 12:59:29 pm
content: It sounds like a lot of what you are doing can be done in the background while your code does other things. Just loop through all those tasks and service each one when data is available - don't wait on anything. If you must perform a long computation, use a state machine that performs it in pieces, one piece per loop iteration. Depending on how complex your task is, you may want to code up a simple cooperative task manager. And if your task isverycomputation-heavy, you might consider a larger microcontroller which can support a real-time system or perhaps an additional, small microcontroller to handle the interface.

author: dannyf
author_rank: Super Contributor
timestamp: October 15, 2013, 01:32:14 pm
content: Quote1. Use interrupts to capture inputs;2. Use interrupts / hardware to send data;3. Use dma, if available.

author: e100
author_rank: Frequent Contributor
timestamp: October 15, 2013, 01:36:33 pm
content: Quote from: nowlan on October 15, 2013, 12:56:48 pmIt's a atmega328.My main concern is not losing user input because there's nothing more annoying than having to press a button multiple times because the MCU wasn't ready to receive data.

author: c4757p
author_rank: Super Contributor
timestamp: October 15, 2013, 01:38:13 pm
content: If your microcontroller, running at upwards of 20 MHz, is so busy it can't notice a button press lasting at least a handful of milliseconds, your code needs a serious overhaul.

author: dannyf
author_rank: Super Contributor
timestamp: October 15, 2013, 01:44:24 pm
content: QuoteIt could be that he is doing long delays, or waiting for a slow peripheral to finish. Yes, I do think there is so much processing power for something like that to happen.

author: denizcan
author_rank: Regular Contributor
timestamp: October 15, 2013, 02:09:39 pm
content: If you can use an operation system, it is easier. You may assign a task that scans the buttons. UI work on other task. And listens incoming messages (such as using mailbox) also updates the screen. Wait's are not done by loops, but really handled by OS.But if this is beyond your aim, the responsiveness may be based on those:1) UART communication is controlled by interrupt. If you are passing ASCII code, like terminal communication, you can buffer incoming and outgoing to free the main control loop waiting communication to complete.2) Maim loop consists of state machines. In each state you do one thing. You do not introduce any wait loop. Delaying is made by a timer that ticks in periodic fashion, and a counter that tracks how long in that state the machine should stay..for instance, the main loop shall like this:Code:while(1){processTimer();processUI();processButtons();}processTimer checks the timeout of the timer, and sets timerTick value to 1 if timeout happens, for just 1 loop.. that way you may increment each state machine's own timer..suppose you are blinking a text, and processUI module is something like this:Code:void handleMainScreen(){if (timerTick){if (++flashCounter == 500){flashState = !flashState;if (flashState)paintInverted();elsepaintNormal();}}}void processUI(){switch (uiState){case US_MAINSCREEN: handleMainScreen(); break;case US_SOMEVALUE: handleSomeValue(); break;...}}As you see, there is no wait loop that prevents other parts of the program to hang.. Timing is done by "timerTick" variable, that is set for just one loop when periodic timeout (for instance in each 1ms) happens.. You may use same timerTick to check the buttons in processButtons function. Use simple filtering to prevent glitches etc..It is a lot easier if you use OS though.. But it also has some learning curve and daemons in it aswell..

author: Harvs
author_rank: Super Contributor
timestamp: October 15, 2013, 02:11:15 pm
content: If you can find yourself a cheap copy of this book, it's got some good basics for multitasking with small MCUs:http://www.amazon.com/Embedded-Multitasking-Technology-Keith-Curtis/dp/0750679182

author: Kjelt
author_rank: Super Contributor
timestamp: October 15, 2013, 02:19:03 pm
content: As denizcan already said don't use a loop which does not guarantee that each event will be handled in time but write a simple scheduler.You measure the minimum interval each possible events (without interrupt) needs to be handled/checked/polled, the lowest will be your timertick base time the others are related to this.Then measure the processing time for each task (best and worst case).Then make the timing puzzle complete and see if your microcontroller can handle if all tasks are worst case and interrupts are handled.Or write the program and measure and log the idle time of the microcontroller.

author: Bored@Work
author_rank: Super Contributor
timestamp: October 15, 2013, 07:09:52 pm
content: Quote from: mojo-chan on October 15, 2013, 02:48:17 pmYes you can, but really ...Clocking a Mega with 20 MHz gives you a lot of ompf. When we were young we used to code interactive computer games on 1 MHz machines. With one hand tied to the back. In winter. Uphill. No shoes. If you get the idea.If you go, for example, for a screen update rate of 10 fps you have 100 ms per main loop iteration, or 2 000 000 CPU cycles per loop. Most (all?) ATMega instructions are single cycle. That is a lot of instructions you can execute per loop iteration. In fact I wouldn't even start with 20 MHz CPU clock. The ATmegas default 8 MHz or 1 MHz is a good start.

author: Kremmen
author_rank: Super Contributor
timestamp: October 15, 2013, 08:28:41 pm
content: OK, is it my turn now?What others have suggested will certainly work and i'm not saying you shouldn't do it that way. But, if it was me i would most likely grab a small real-time OS and use that. More specifically in this case, the FreeRTOS that will definitely work for a 328. Some will argue that it is overkill, but so what.As i see it, the pros and cons:+ The functionality and processing can be truly separated into isolated tasks with little or no interference.+ Tasks can be written as easily re-usable modules (think UI/CLI for instance). Easy to transplant into the next project with hardly any strings attached.+ Avoid many pitfalls in interactions between independent functionality+ depending on choice of OS, you might get some of your high level functionality for free (check FreeRTOS and ChibiOS at least)- learning curve if you never did this before (but +: once you do, you have a new powerful tool at your disposal)- some extra resources (RAM mostly) needed to maintain task context. Not a real problem in most cases.- you need to do a bit more overall system design and some of it under the constraints of the OS. This could also be a + as it makes you actually design certain things and not just wing it.Having done this who knows how many times, for me it is almost a no-brainer. Who cares if you could do it without an OS, with one you will standardize the solution logic and that will carry on to the next project and the next, iteratively shortening development time. I mean, you don't get money back from unused memory so by all means trade overkill for standardization.As to user interface, you of course code that as a separate task receiving input from a message queue and pushing output to another queue. You then have other tasks to write user input into, and read responses from, those queues as appropriate. Now the input can come from a serial channel in this project or from a touch screen in the next and your UI task never needs to care. And so on.Edit: typos.

author: tszaboo
author_rank: Super Contributor
timestamp: October 15, 2013, 09:27:33 pm
content: I did ultrasonic flow sensing projects in the past. In fact my thesis was ultrasonic flow sensing. People tend to under-estimate the required computing power required for such task. It is not a catwalk.I think 20MHz and 8 bit is far from enough power to process all the data. One working application was using PIC32@80Mhz, mine was dsPIC33 and 40MHz, and it did not cut it with the required resolution.You just need too much time to process all the data, and there aren't too many cpu cycles to process it. Even if it works, you add 1-2 extra features, and the whole system breaks, as interrupts run on top of each other. DMA can help, but not on an atmega.So use more power, and plan before, when the CPU is going to do what. And use some decent architecture, with 32 bit and lots of MHz.

author: dannyf
author_rank: Super Contributor
timestamp: October 15, 2013, 09:30:56 pm
content: QuoteIt is always easier to use a bigger / faster chip to do a given job.It is much harder to do the opposite.

author: Stonent
author_rank: Super Contributor
timestamp: October 15, 2013, 10:57:46 pm
content: Quote from: dannyf on October 15, 2013, 09:30:56 pmAs the great Colin Chapman is quoted as saying "Simplify, then add lightness"

author: sleemanj
author_rank: Super Contributor
timestamp: October 16, 2013, 05:54:15 am
content: There is always the "throw hardware at it" solution - dedicate a very small microcontroller (ATTiny etc) to handling all the UI, then have the primary microcontroller (ATMega etc) communicate with the UI one to get current settings, pass collected and processed data for display etc when it's good and ready.

author: Kremmen
author_rank: Super Contributor
timestamp: October 16, 2013, 07:22:45 am
content: Quote from: e100 on October 15, 2013, 01:36:33 pmUser input is by nature slow. You will have to make some truly rookie mistakes to miss any of that, whatever the nature of signaling will be. That said, i would still come up with an interrupt based input collector because it makes the architecture cleaner and avoids unwanted strings between unrelated functions.Quote from: NANDBlog on October 15, 2013, 09:27:33 pmThere are many kinds of flow sensors. Let's hear from the OP if this is at all relevant before jumping to conclusions. His could be a simple aperture disk with differential pressure sensors, making the measurement almost trivial in comparison. Then again, it may not.Since this is a "real" product, a proper complexity analysis should be performed to get an idea about the required processor capacity to run the tasks, especially the calculation part. The complexity can be roughly estimated by characterizing the measurement algorithms. I assume the complexity to be closed form polynomial time, but depending on the selected tech and the resulting nature of the data in the worst case a linear optimization could be needed in which case the nature of the beast changes radically.Assuming polynomial it should be straightforward to write down the closed form, account for transformations to and from internal presentation and calculate the processor time for one iteration. Based on iteration frequency you then get the rough performance requirement in floating or fixed-point ops per second.Until you do something like that, or actually try a prototype in practice, it is premature to say whether a given MCU has the kick do do it.I am not saying you are wrong and if you have experience that indicates more calc power is needed then that may well be the case. First it should be made sure however that you are not comparing apples and oranges here. So let's hear from the OP hopefully, what kind of flow meter he has in his hands.

author: tszaboo
author_rank: Super Contributor
timestamp: October 16, 2013, 07:59:21 am
content: Quote from: Kremmen on October 16, 2013, 07:22:45 amYes, I specially marked, "ultrasonic" in my post. This is usually the easiest to implement mechanically, requiring only some cheap 1$ transducers. The drawback is the required processing power.People tend to under estimate (financially) the cost of their own time. My experience it they rather spend countless hours of time to make a high processing power algorithm working on a inappropriately slow architecture, while for 2-3$ more, they would have enough to make it easy. And then they make 2-3 of it.I get that it is challenging, and optimizing code is awesome. But then you optimized the whole project the wrong way.

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: October 16, 2013, 08:30:17 am
content: You should never sit waiting for anything.Your main program should be a loop that polls all the tasks in turn, and each task should return immediately if there is nothing to do.Anything that is time-critical - encoder, serial input, buttons should be handled by an interrupt which does as little as possible to record & service the event, and sets a flag to tell the foreground task to deal with it next time it gets polled. (And don't forget to use thevolatilequalifier on any flags set by interrupts - this is extremely important - look it up)Anything where you are sending out data to a slow interface - serial, LCD etc. can be handled by using a buffer - put your data (e.g. a text prompt) in the buffer, and the buffer gets emptied as & when space becomes available either by an interrupt or by a polled task.As long as you deal with high-priority stuff in an interrupt, your foreground task latency can be a few tens of milliseconds before anything gets noticeably sluggish. That's tens of thousands of instructions, so all but the most complex tasks can usually be done in one go as required.State machines are your friend.It's all about starting with the right structure.

author: nctnico
author_rank: Super Contributor
timestamp: October 16, 2013, 10:30:13 am
content: I agree with Mike.My software is divided in modules each with a specific function. Handle input, process data, handle an EEPROM, etc, etc. Each module has an init() and a run() function. The init function is called at startup the run function is being called from main continuously. The run functions should never wait for a long time. If I need a long delay I use a timer. This timer gets incremented from the timer interrupt. In a run function I simple do this:Code:unsigned int local_delay;void run(){//do something every secondif ((systimer-local_delay)>TICKS_PER_SECOND){local_delay=systimer;}}With the use of statemachine you can extend this mechanism by resetting the local_delay in one state and wait for it to expire in another state.

author: Kjelt
author_rank: Super Contributor
timestamp: October 16, 2013, 11:16:30 am
content: Quote from: nctnico on October 16, 2013, 10:30:13 amDo you agree on everything with Mike? Because the last time I suggested the same as Mike in a different topic you found it bad advice and said that the processing should be done in the ISRQuote from: mikeselectricstuff on October 16, 2013, 08:30:17 amFor the record I agree fully with Mike but also with Kremmen that if an RTOS is a feasible option one should really consider using it instead of programming it your self.

author: dannyf
author_rank: Super Contributor
timestamp: October 16, 2013, 11:16:38 am
content: QuoteThere are quite a few issues in that short piece of code.I would instead do something like this:Code:void run(void (*func_ptr)(void)){static uint32_t local_delay=0;if ((systimer_get()-local_delay)>=TICKS_PER_SECOND) {local_delay+=TICKS_PER_SECOND; //keep the timing errors / residualsfunc_ptr(); //do something every second}}It provides more of a modular structure. If you know which functional calls it executes, you don't need the function pointer.

author: dannyf
author_rank: Super Contributor
timestamp: October 16, 2013, 11:26:16 am
content: QuoteNot sure. And RTOS adds considerable complexity + overhead.Pus, a state machine is essentially a rtos in that it is a scheduler.Something like this would basically work:Code:switch (proc_cnt) {case PROC_1: proc1(); break;case PROC_2: proc2(); break;...case PROC_N: procN(); break;default: proc_all();}proc_cnt=(proc_cnt==PROC_N)?0:(proc_cnt+1); //advance to the next procEach time you run that block, it executes a different process. It does make certain assumptions about the processes but that's the basic framework for a rtos.

author: c4757p
author_rank: Super Contributor
timestamp: October 16, 2013, 11:31:34 am
content: My opinion in the whole RTOS thing is that you probably don't need it. Basic task scheduling can be done in literally less work than figuring out and implementing the RTOS. Just cycle through tasks, like Mike said, and never wait on anything. For anything time-oriented, I keep a tick counter at a fixed frequency, and either selectively execute tasks when their time is up, or just make the counter available to the tasks. Your whole scheduler can be a couple dozen words of ROM.

author: dannyf
author_rank: Super Contributor
timestamp: October 16, 2013, 11:35:41 am
content: QuoteBecause the wind was blowing in a different direction then,

author: amyk
author_rank: Super Contributor
timestamp: October 16, 2013, 11:51:02 am
content: Quote from: c4757p on October 16, 2013, 11:31:34 amI agree with this. Unless you have a ton of other peripherals, processing power, and memory, all an RTOS adds is bloat. I prefer to have UI stuff done on interrupts - inputs are on one, and screen updates are sync'd to the refresh interval (30Hz) by another one.

author: denizcan
author_rank: Regular Contributor
timestamp: October 16, 2013, 12:20:53 pm
content: RTOS is not a bloat. The RTOS designs are bloat.. Some of them are designed by inexperienced graduate students. They are ugly, and not nice to use.. For instance:Code:xTaskCreate( vCheckTask, "check", STACK_SIZE, NULL, TASK_PRIORITY, NULL );vTaskStartScheduler();What an ugly way to start a task.. Just from this code I can give a speech about why this is really bad design, also its internals will be bad. Do you directly know what first NULL parameter refer to? What about second NULL? What would you benefit from expressing return types on function names? Also they use application stack for what? To run idle task..Whole application stack is gone for nothing..A good RTOS is really but really lightweight.. It just costs tasks stack RAM. Nearly nothing else.. It will save CPU time as well. Just think about the switch states in your main loop. In each loop CPU recalculates states.. Unless one state machine changed it is unnecessary.. If you are running on battery you are wasting power for nothing for most of the time..And think of and RTOS. CPU switches to a task if it needs to run.. For instance it is awaken by an interrupt, or a mailbox, or an event set by interrupt or other task.. Until then CPU forgets that task..What about prioritization.. It is very difficult with state machines. For instance, you have to state machines. One takes a lot of time to compute a long string. But the other needs to be responded quickly.. CPU should preempt long task to process time critical task.. You will use interrupt for it right? May be a software interrupt (for instance a GPIO interrupt, pin is set by software)What if your time critical task is also a little long as well? You will use nested interrups right? It goes and goes on.. Actually this is fairly common scenario on embedded world. This topic is one of them.. If you are using graphical LCD's, you have to do lots of reads and writes to prepare screen, transfer lots of bytes to display.. While you are doing this there might be incoming transfer from UART, CAN, or even worst from SPI or USB. CPU should pause what is doing, and respond the request..This is easily done by an RTOS.. Only cost is a few bytes of extra stack.. That's it..

author: dannyf
author_rank: Super Contributor
timestamp: October 16, 2013, 12:32:41 pm
content: QuoteI have an ocean in Arizona to sell you,Seriously, it is very simple to test. Pick your "not-ugly" RTOS and pick a few typical tasks and code for them.

author: c4757p
author_rank: Super Contributor
timestamp: October 16, 2013, 12:36:44 pm
content: Quote from: denizcan on October 16, 2013, 12:20:53 pmLordy, I hope not.

author: Codemonkey
author_rank: Regular Contributor
timestamp: October 16, 2013, 12:38:26 pm
content: Quote from: denizcan on October 16, 2013, 12:20:53 pmGo read up on Hungarian notation some time. Its actually quite useful.

author: dannyf
author_rank: Super Contributor
timestamp: October 16, 2013, 12:44:31 pm
content: QuoteNested interrupts are one of those things that are fun to talk about and pain to implement.

author: e100
author_rank: Frequent Contributor
timestamp: October 16, 2013, 12:47:50 pm
content: Here's a picture of the flow meter turbine and a section of 40mm tube to give you an idea of the scale.It's a helical design similar to the fins on an archery arrow. Trying to do an aerofoil cross section is too difficult at this scale when you only have hand tools.Hopefully this design will be tolerant to small bits of leaves etc. and functional over a wide range of flow rates. Initial experiments show that the rotation speed will be 0.5 to 20Hz. Previous experience also shows that some kind of moving average calculation will need to be done over a period of about 5 seconds to remove the noise due to the turbulent flow.The nylon/glass bearing are the smallest I could find on Ebay that are salt water resistant. Miniature 316 stainless cost a fortune. I plan to put fairings between the bearings and the beginning/end of the turbine to reduce eddy turbulence and shield the races from debris.I plan to use a magnet on the rotating bit and a A3144 Hall effect sensor outside the housing. Hopefully this will give a bounce free output.Originally I had planned to use a differential pressure sensor arrangement but the turbulent flow gave pressure fluctuations 10x higher than the average pressure difference. The confined nature of the plumbing doesn't allows for flow straighteners.I then tried a shunt and turbine flow meter designed for beverage dispensers. This works well at high flow rates but has stiction problems at low flow rates, and requires a filter to be positioned upstream. Small Pelton wheel flow meters also require upsteam filters.

author: dannyf
author_rank: Super Contributor
timestamp: October 16, 2013, 12:53:00 pm
content: QuoteInput capture or a simple external interrupt will capture that easily. Or polling against a free running timer.QuoteA digital low-pass filter will do, assuming that your pressure doesn't change too quickly.

author: denizcan
author_rank: Regular Contributor
timestamp: October 16, 2013, 01:23:19 pm
content: Quote from: dannyf on October 16, 2013, 12:32:41 pmI do it every day.. Even on tiny chips.. On ARM it is even more nicer, as exception handling has separate stack, task stacks are just large to support that task.. What's the catch? On Atmega, 64B out of stack of 512B RAM is big for most applications? On ARM 256B out of 4KB? Or 1KB out of 32KB? Than you are using wrong chip for that application even without RTOS.. Do your application use all the RAM? Seriously, what do you do with the RAM not utilized by your application? Cut and paste to other chip? If you think of task switching, pushing and poping 6-8 registers is the cost.. On modern CPU's there are single instructions to do that.. What's the cost of a switch state? comparison, jump, comparison jump? What is the cost of 2 state machines? 4, 6? Ahh, you always use jump tables, right?Most of all, what is the cost of your time spending on designing state diagrams, writing lots of enum's, case's, state='s, if (timer == xx)'s?

author: Kremmen
author_rank: Super Contributor
timestamp: October 16, 2013, 04:58:12 pm
content: Quote from: dannyf on October 16, 2013, 11:26:16 amYour mileage _will_ vary. Not all RTOSes add that much complexity or overhead, and those may also be in the eye of the beholder. Let me mention FreeRTOS since i did so already earlier and someone else has also done so. The scheduler is basically one small(ish) source file containing the processor specific context switch that is quite easily understood and ported to new architectures. And you are guaranteed not to have any strings attached to any other piece of code, specifically your app code which is the point. Other OS features are included if needed but if not, they just aren't there so no overhead for what you don't use. And don't forget that if you use a feature then someone, be it you or the RTOS coder, included that "overhead" into your app. You sure you will be more efficient than the OS implementors?As to overhead. It seems to be a default shot from the hip that an OS is always bloated and always introduces overhead and complexity. Not necessarily - sometimes it may introduce simplicity and remove overhead - by replacing your possibly suboptimal ad hoc code with better optimized solution that has been extensively verified.QuoteThat is not a state machine, and also it is not at all like an RTOS scheduler works, which i would think is the main reason to actually use one. The above piece of code lacks those scheduling and priorization mechanisms typically and necessarily built into a proper RTOS. This is a simple round robin. It will work in certain kinds of applications, but the inescapable downside is the necessity to build the timesharing visibly into the task source code. In other words a task will hog the entire MCU time for ever unless it somehow explicitly releases control by artificially terminating (while still maintaining state for the next iteration). Since this is exactly what a pre-emptive (or even a cooperative) scheduler does, it begs the question what is the added value of doing it yourself on the application level?Quote from: c4757p on October 16, 2013, 11:31:34 amA person only needs an upturned boat to get out of the rain, a credit card and a toothbrush. A toothbrush is only necessary because the card is too inconvenient to use as a brush. Everything else is unnecessary so in that sense no, you probably don't _need_ an RTOS.But like so many, many other things, it may make your life considerably simpler and less interesting.Again, yours is a _time scheduler_ all right, but that's it. It is nothing more. If that works for you then no problem. But even here i see a key expression - "...never wait for anything..." And what do you do if you _must_ wait because you _cannot_ proceed before an event takes place? You store state and iteratively check the event until you can advance the state. So you end up building state machines in your code anyway, and explicitly jump from state to state. It can be done but if you had the support of a true pre-emptive scheduler you mostly wouldn't _need_ any state machines because the interruptions would be invisible to your application. Either by the (true OS) scheduler breaking a busy wait or by your code directly yielding by waiting an OS event (which would be the "correct" way to do it) and then just continuing when the event triggers.Quote from: dannyf on October 16, 2013, 12:32:41 pmHeh, done it more times than i can possibly count. The first RTOSes i have used in real life were self coded in the early 80's because you couldn't find ready made ones anywhere. So we rolled our own in a small team of SW engineers. These babies were not used to make Arduino leds blink but to control things like flexible manufacturing centers for heavy steel industry (like manipulating and positioning steel sheets over 1" thick for hydraulic X-Y shears and stuff like that). You _really_ didn't want them to do anything stupid like hogging the system resources unexpectedly. And they didn't, so there. I don't know about ugliness or beauty otherwise, but they did the job so i guess that qualifies as "not-ugly".Both ways have worked for me - either rolling my own from start to finish, or using a RTOS. From direct experience, most of the criticism against RTOSes is plain bullshit and simply not accurate. Yes they do consume resources and yes there are tradeoffs but then the exact same logic applies to code one writes from scratch. The processor does not work by magic - the code will be somewhere. More and more i have been happy to leave the internal housekeeping to a suitable OS and concentrate on the beef. I have not been disappointed yet.But all of the above is of course just my personal opinion worth all the money you paid for it.

author: dannyf
author_rank: Super Contributor
timestamp: October 16, 2013, 05:09:05 pm
content: QuoteGreat. Let's take blinking two leds on PIC12F675 for a test drive. You write a piece of code on your "non-ugly" RTOS and I will write mine and we can compare.If PIC12F675 isn't sufficiently tiny, we can try PIC10F220. Is that fair enough?

author: nctnico
author_rank: Super Contributor
timestamp: October 16, 2013, 09:39:01 pm
content: Quote from: Kjelt on October 16, 2013, 11:16:30 amThere is no need to go sideways in a discussion nit picking on details. Mike's primary advice is not to wait for a long time in the main process but on the other hand don't get anal about it so do time critical stuff in interrupts.

author: nctnico
author_rank: Super Contributor
timestamp: October 16, 2013, 09:46:21 pm
content: Quote from: denizcan on October 16, 2013, 12:20:53 pmThat depends on how you organize your software. If you write everything as non-waiting then a scheduler (calling run functions from main) will be quick no matter what the load is. I've worked on projects with 30 or 40 'tasks' which included handling 3 ISDN-30 trunks in which an RTOS wasn't necessary at all. I didn't initiate those projects; it was just cleverly designed to begin with. In my experience an RTOS is only necessary if a calculation takes so long it needs to be interrupted to process something else. Another downside of timeslicing is that you'll need semaphores to safeguard shared data which also introduces the risk of a deadlock between processes.If you look at the source of software which runs on Linux/Windows you'll see that there is often a loop from which several sub tasks are called and when those are done there is a sleep to tell the OS to give CPU time to other processes. So even though a full blown OS is available many software designers choose to avoid the complexity of running several parallel tasks.

author: dannyf
author_rank: Super Contributor
timestamp: October 16, 2013, 10:15:50 pm
content: QuoteIn C, with start-up code + overhead, blinking two leds takes 14 bytes of flash.Try to do that in your favorite, "non-ugly" RTOS.

author: c4757p
author_rank: Super Contributor
timestamp: October 16, 2013, 10:21:12 pm
content: Oh come on, blinking two LEDs is a ridiculous example. That's like saying a four-function calculator is too difficult because you can recall that 2+3=5 faster than you can enter it on the keypad.

author: dannyf
author_rank: Super Contributor
timestamp: October 16, 2013, 10:24:59 pm
content: QuoteFairly enough. As I proposed earlier, pick your favorite tasks and implement them on your favorite RTOS and we will then compare.

author: c4757p
author_rank: Super Contributor
timestamp: October 16, 2013, 10:28:40 pm
content: Oh, I agree. I wouldn't use an RTOS for this. But you can make your case better than that...

author: denizcan
author_rank: Regular Contributor
timestamp: October 17, 2013, 12:11:36 am
content: Quote from: dannyf on October 16, 2013, 10:24:59 pmDo you often take the things to extreme? Do you use single tool for everything? I don't use any Microchip product however, I would use PIC10F series to do very very simple task in very confined space.. Nearly just a single thing, or may be a second but simple second.. That's it.. What gain will we have using PIC10F series and RTOS? We are talking about multiple tasks that run parallel: communication, user interface, time critical complex responses etc.For instance, what e100 asks is perfectly RTOS candidate.. Main task deals with user interface, other task scans buttons and transmits it as key down, key up messages to message queue of UI task. Other task deals with communication. Transmits the request as messages to main task.. Even on Atmega88 it can work.. I've been not using Atmega, as ARM M0 chips are cheaper than that.. I would put 4x4mm ARM though..For tiny, if you re-read my post, you would see something like 64B out of 512B as a measure of second task's payload in RAM.. I don't remember I claimed RTOS for every chip. And I am not claiming RTOS wont add some ROM space.. But most of the time, even with RTOS, plenty of RAM and ROM is left in many chips.. I usually use remaining ROM for different fonts, graphics, sound, audio etc.. What I referred as "cost of switch statement" is not the total overhead of ROM, but CPU time lost in the unnecessary state machine loops. Think about this, if you didn't plan to avoid redundant state machine's state check, I mean you call state machine's function that has main switch-case block, CPU will have to run into a lot of compare, jump, compare jump instructions.. This is lost time also lost energy if your application is running by batteries. If you use RTOS, your task would not bother CPU until it is awaken by what it is waiting for..How many lines of code would you write to implement reading an I2C sensor, displaying it, setting an alarm level, and responding uart communication.. How much time would you spend on state machine? Than customer wants new stuff, new commands send from UART etc.. They always want something more when they see their project is running. This is my measurement. First how much time, second how easy to add new features, and third how many lines looks same.. If I am writing similar thing again and again, I am doing something wrong..BTW, what most developers miss is the cost of the time. You can buy chips, there is no online store that you can buy time.. Measurement of how much ROM and RAM overhead is one thing, but if the chip youhave to/mayuse have enough of them, than it is not a good measure..An example of measurement.. I have Callback class, which costs 8 bytes of RAM on 32 bit, 4 bytes on 8 and 16 bit.. You can register Event (object that task can wait for), function, and even a nonvirtual method of any object.. (This is not allowed by C++ however I found very simple workaround)With callback class you can do following:Code:void handleTimerTick(void* sender, void* param){// do something, caution this is called by IRQ}int main(){core.init();Timer t;t.setPeriod(100);t.onTick = handleTimerTick;t.start();while (1){// do somethingwait(250);}}Timer calls handleTimerTick in each 100ms.. Same Callback and same Timer class:Code:int main(){core.init();Event e;Timer t;t.onTick = e;t.setPeriod(100);t.start();while (1){// do something// do other thinge.wait();}}Here Timer t sets Event e in every 100ms.. Your loop is repeated in each 100ms, even the code before e.wait() takes different time in each loop.. For example, suppose on one loop it took 60ms to run to e.wait, it will wait 40ms to rerun, and 30ms in other run it will take 70ms to rerun.. I can give lots of different usage of Callback, connecting objects to objects -for instance which makes code reuse really easy..Isn't that wise?Same Timer class, same Callback class, just used for different kind of stuff. Isn't it worth of 4 bytes of RAM (8 and 16 bits), or 32 bytes of RAM (32 bits) if you have 512B of RAM? Isn't "larger than 1KB" common nowadays? Could you use ROM, and RAM usage instead of time you spent, code cleanness, ease of adding new feature as a measure here? Except that you have to use very very tiny ROM, very very tiny RAM that are forced by the projects constraints.. Till now, I never came across that constraint. May be many LED's driven by many 6 pin MCU and serial communication of multiple LED driver MCU's.. Or something similar, ok, don't bother using RTOS there..

author: nctnico
author_rank: Super Contributor
timestamp: October 17, 2013, 12:16:53 am
content: Now try to use printf in the timer event.

author: dannyf
author_rank: Super Contributor
timestamp: October 17, 2013, 12:59:20 am
content: QuoteWell, that's why you need a variety of tasks to get a sense of how two approaches fair in real world situations.I picked the simplest. If you don't like it, pick your own and we can always compare the two approaches.

author: dannyf
author_rank: Super Contributor
timestamp: October 17, 2013, 01:02:07 am
content: QuoteI picked PIC10F since you mentioned earlier that you run RTOS on tiny chips every day and I wanted to make it easier for you to come up with examples that help you prove your case.If you think PIC10F is too tiny or not tiny enough, I am happy with you pick a "tiny" (whatever it may mean for you) mcu and throw a few examples there using RTOS.

author: dannyf
author_rank: Super Contributor
timestamp: October 17, 2013, 01:05:49 am
content: QuoteNo, you didn't.But you did claim using RTOS on "tiny" chips. I am flexible on what "tiny" means - I am OK with you picking whatever chip, tiny or otherwise, as long as you can provide a few examples so I can replicate them without RTOS to help you show that RTOS adds no overhead (other than few bytes on the stack).Simple as that.

author: denizcan
author_rank: Regular Contributor
timestamp: October 17, 2013, 01:23:27 pm
content: Quote from: dannyf on October 17, 2013, 01:05:49 amOh, ok.. For me, tiny is how much area the chip sits on.. Usually resources are also related to the chip's area, as they occupy space in the die.. With that definition, SO8 package is not tiny for me, even Microchip claims they are..Here is the code:Code:#include <hal.h>Pin led1;Pin led2;TaskEx<64> task1;void handleTask1(Task* task, void* param){while (1){led1 = 1;wait(100);led1 = 0;wait(900);}}int main(void){core.init();led1.init(P2_0, OUTPUT);led2.init(P2_1, OUTPUT);task1.start(handleTask1);while (1){led2.set();wait(200);led2.clear();wait(400);}}It works on both MSP and ARM platform.. Without changing anything.. I added led1=0 which compiles to Pin::setValue(Bool value) and led2.set() as well.. led2.set() generates less code, as it directly sets known value.. It is just at your convenience.. This code blinks led1 100ms on, 900ms off; and led2 200ms on, 400ms off.. I added phase shift to make it a little interesting.. This compiles to 876B of RAM, 3114 Bytes of Flash on ARM. 176 bytes is what Acore library consumes, including TaskEx<64> 64 bytes of stack, 21 bytes of task control block, 21 bytes for main task.. Acore does not use idle task.. It sees sets mainTask object and adds main stack to it.. After a call to core.init, you are in mainTask.. When it is idle, it just sleeps the MCU.. Arm also allows interrupts to have separate stack, which makes tasks stacks really tiny.. You can decrease the stack as we don't call any function, but MSP uses same stack on interrupts, so lets leave there..~700 bytes of RAM comes from uVision, I think 512B of it is the main stack, I don't know what is the rest uVision uses.. I don't use heap, or dynamic stuff unless it is necessary.. Optimization is basic -which makes the assembly more understandable-.For blinking two LED, your flash will be 20-30 bytes at most, mine will start from 2K on MSP, starts from 3K on ARM, But there is Pin class which enables you to access pins without knowing anything with the target platform, just name the pin, and pin flags (OUTPUT, INPUT, PULLUP, PULLDOWN) etc, you are ready to go.. Core::init (that initializes FLL on MSP, PLL on ARM), enables timer peripheral to create 1ms tick on MSP, start SysTick timer on ARM; multiple timer code that enables you to use Timer class anywhere.. Callback class that enables you to bind event, function, method.. Event class that you can sync tasks, Mailbox class and its template class that you can post values in any type to other tasks.. etc. etc. Tons of framework code that will help your further progress. We have not used them but as they are integrated into core, some of their code is injected.. Do you think this is a lot? What about ease of use? I even didn't lookup the PDF, headers etc..http://www.digikey.com/product-detail/en/STM32F051K4U6/497-12949-ND/3193384This is 5mm x 5mm. On this chip there is 4K RAM 16K of Flash, at $2.6 of price break 10; if you are willing to pay ~25 cents more you will gain 4K more RAM, 48K more flash, which makes 8K RAM 64K Flash.. From my experience 4K RAM and 16K flash is enough for many MCU application unless you are doing graphical intensive staff, which adds pixel handling, lcd driver, Graphics that draws pixels, fonts, lines, window manager, ready to use widgets, and also uses flash for fonts, bitmaps etc. etc.. Are we talking about 25 cents here?By the way, I should add, I don't use macros to configure a library. I found it annoying.. You just add <hal.h> if you want to access peripherals, or <core.h> if you want to use just core, mcu definitions, tasking, etc and start using.. I made default configuration and configuration tools if you need to move away from default.. Add those code to the list above.. Only macro I used is preprocessor symbol LPC1756 or STM32F407VG on uVision only.. As uVision requires you to select the MCU, but it does not expose to the code, which is also ridiculous.. I don't know if they expose now, but in the past I could not find.. If you know anything about that, I would appreciate and library becomes macroless..

author: dannyf
author_rank: Super Contributor
timestamp: October 17, 2013, 01:29:03 pm
content: QuoteWell, I am happy to break a PIC10F220 so we can compare the die size vs. your ARM chip.QuoteIt seems that 876 bytes of ram is considerably bigger than 4 bytes of ram that my code used; 3115 bytes of flash is considerably bigger than 14 bytes of flash that my code used.Bare metal 1, RTOS 0.Next round?

author: denizcan
author_rank: Regular Contributor
timestamp: October 17, 2013, 01:58:08 pm
content: Quote from: nctnico on October 17, 2013, 12:16:53 amWhy do you use printf on IRQ? There is two methods:1. set an event, and handle it on a task. You can guess how.2. I forgot to mention, there is WorkItem class, which is processed by WorkList..This is a little interesting.. You register a function or method to WorkItem, and add the WorkItem to WorkList.. Each WorkList has its own task, and a linked list of triggered WorkItems, and a linked list of idle WorkItem's..Code:WorkItem workItem;workItem.onProcess = processPrintf;workList.add(workItem);when you call workItem.process with or without a parameter, the actual work is handled by workList's task.. you may also bind workItem to Timer.. it actually binds WorkItem::process to Callback onTick..Code:timer.onTick = workItem;As you guess it, processPrintf is run by workList after timer ticks.. A second method, if you want to pass a parameter to worker, you can do it on handleTimerTick function, the function you set to timer.onTick callback:Code:void processPrintf(void* sender, void* param){printf("some value: %d", (Int)param);}void handleTimerTick(void* sender, void* param){workList.tryProcess(processPrintf, (void*)1);}I used workList.tryProcess, not workList.process.. The workList.process waits for empty WorkItem, workList.tryProcess looks for an empty, if there isn't it returns false.. WorkList should have enough idle WorkItem's for catch every irq.. If not, it will miss some irq.. You set the number of items when you define workList: WorkListEx<8> workList.. With methods workList.process, and workList.tryProcess, workItem.process you can pass parameter to worker function/method..Most of the time I use Mailbox instead of WorkList.. WorkList comes from my early days before using preemptive multitasking.. But sometimes WorkList makes it easier than a Mailbox.. Especially if you want to divide some part of the process..I just write from scratch, there might be errors.. And sorry guys, due to path the conversation passed, it started to look like advertisement of my Armin library.. I am not selling it though.. Please just look it as methodology.. You can do it this way as well, if you implement correct classes..Ok, for dannyf: I step back.. I use a lot of RAM and a lot of ROM.. But I can code really fast, and nearly bug free, and nearly without thinking, and reading PDF's.. Most of the code I write works virtually changing nothing on all the MCU's I ported Armin..

author: dannyf
author_rank: Super Contributor
timestamp: October 17, 2013, 02:05:29 pm
content: QuoteHow do you then reconcile with the following statement you made earlier?QuoteQuoteWithout thinking, without reading datasheets, coding on an OS (any os), yet (nearly) bug free. If that's not a miracle, I don't know what is.I would submit to you that even if you don't write a single line of code, as long as it runs on an OS, your code has bugs.All for the fun.

author: denizcan
author_rank: Regular Contributor
timestamp: October 17, 2013, 02:31:03 pm
content: LOL, I don't know why, I don't think we can agree on anything..I said "I use a lot of RAM and ROM" because I was sure you were going to ask how much resources the WorkList methodology I gave use..)Nearly bug free is another story. If you add a well tested abstraction to basic functions you do, you would have only application related functions to deal with, and only application related bugs.. If you fix a bug on a framework, you also fixed other projects.. This is better than fixing every project.. Actually this is a knife cuts both side.. If you are not good at framework coding, then you introduce new bugs to all projects.. But as far as I see, if you give enough attention, having a framework is better than not having..Not kidding..

author: nctnico
author_rank: Super Contributor
timestamp: October 17, 2013, 03:21:45 pm
content: Quote from: denizcan on October 17, 2013, 01:58:08 pmNot in an IRQ but just when an event happens. Sometimes I want to print a value every X seconds. If an IRQ is hidden under so many layers of abstraction its easy to put code in a place where it shouldn't be. In some of my projects I have to wait for the UART buffer to become empty. If I write something to the UART in an IRQ and it waits for the buffer to become empty the whole thing is locked because the IRQ from the UART isn't served.QuoteThat sounds pretty complicated. I like to test my software on interns and see how long it takes them to figure out how to add new modules and make changes. Its a good measure to see how easy or hard it is to have the software maintained by someone else.

author: denizcan
author_rank: Regular Contributor
timestamp: October 17, 2013, 07:39:19 pm
content: QuoteYou say that someone is prone to put IRQ forbidden stuff on onTick or similar event which is called from an IRQ.. Nice point, and I loved the idea.. I'll think about that.. I may move all timer related stuff to task level. It is easy for timers, however need to think on other peripheral abstractions, and notations.. For instance you would want fast response, in IRQ level on GpioInterrupt class, or ExternalInterrupt which bonds to a pin.. Thinking on this, here comes an idea:Code:timer.onTick = makeAsync(handleTimerTick);makeIrqSafe or something like that.. now handleTimerTick is handled by task, not irq.. what about this?Testing on interns are good measure.. But why do you think the second WorkList example is complicated? It just passes flow to a task.. Would it be easier to understand if we drop "workList" idiom, and just do it like that:Code:void doPrintf(void* sender, void* param){printf("....", (Int)param);}void handleTimerTick(void* sender, void* param){callAsync(doPrintf, 1);}I know, as you mentioned and I described above, being able to IRQ safe is the best, however just thinking about calling a function from IRQ.. callAsync hides WorkList stuff, it uses defaultWorkList. It is overloaded to make usage easier, this one uses Int as param..What do you think?

author: nctnico
author_rank: Super Contributor
timestamp: October 17, 2013, 08:06:40 pm
content: Now you have to pass parameters from the IRQ thread to the main thread. Printf is a fine example because it can have a variable list of parameters and in some cases these parameters are pointers (a string for example). So you'd have to allocate a buffer, use snprintf inside the IRQ to fill the buffer, pass the buffer and de-allocate the buffer in doPrintf.

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: October 17, 2013, 08:18:23 pm
content: Quote from: denizcan on October 17, 2013, 07:39:19 pmIf your IRQ code is so big you need to call functions, chances are you're doing it wrong

author: nctnico
author_rank: Super Contributor
timestamp: October 17, 2013, 08:46:21 pm
content: Quote from: mikeselectricstuff on October 17, 2013, 08:18:23 pmThat depends on what you are doing. For DSP like applications it would be foolish not to do all the processing inside the interrupt so calling functions is perfectly normal in those situations. Another nice example is an USB stack. These are often so complicated that an interrupt consists of calling lots of functions.

author: denizcan
author_rank: Regular Contributor
timestamp: October 17, 2013, 09:51:41 pm
content: Quote from: mikeselectricstuff on October 17, 2013, 08:18:23 pmMike, calling function, actually calling through Callback, makes IRQ handling a little longer but flexible.. For instance, to bind an GPIO interrupt to a function:Code:GpioInterrupt syncIrq;int main(){syncIrq.setPin(P1_0);syncIrq.onTrigger = handleSyncEvent;syncIrq.enable(IS_RISING);}Here you binded P1_0's rising edge to your handleSyncEvent function, even without knowing the actual implementation..This also gives plasticity. For instance CC1101 chip uses 2 GPIO pins to report it's state. You may pass what pins it shall use to CC1101Spi class, then it will direct those pin irq's to its method, as sample above.. So, you don't have to write an irq handler function and direct it to CC1101 vice versa..I know it will make irq a little longer, but I think it is worth.. Especially on ARM as nested irq handling is intrinsic and one IRQ with higher order does not wait lower one.. Still, you have to make sure that you made IRQ callback as light as possible..

author: denizcan
author_rank: Regular Contributor
timestamp: October 17, 2013, 10:01:16 pm
content: Quote from: nctnico on October 17, 2013, 08:06:40 pmDo you think you really have to pass whole processed value to a task? Why don't we just use Timer as a trigger? Because one might write printf on onTick event? In the example callAsync triggers defaultWorkList's task to run given function, with given simple pointer size parameter (32 bit on ARM, 16 bit on MSP and AVR).. It runs as such it runs in IRQ.. So you may handle as you are in IRQ, but you are in task level..

author: e100
author_rank: Frequent Contributor
timestamp: October 17, 2013, 11:13:03 pm
content: I found a scheduler and explanation herehttp://bleaklow.com/2010/07/20/a_very_simple_arduino_task_manager.htmlwith downloadable source code herehttp://bleaklow.com/files/2010/Task.tar.gzThe comments at the bottom seem favourable.Does this look like a reasonable way of doing things?Mike

author: nctnico
author_rank: Super Contributor
timestamp: October 17, 2013, 11:47:15 pm
content: Quote from: denizcan on October 17, 2013, 10:01:16 pmThat depends on where the text for the printf is originating from. You can't just pass a pointer to data from the IRQ thread to the user thread. You never know whether the pointer still points to valid data. You need to wrap the data to which the pointer points into a package and hand that over to the other thread OR safeguard the data the pointer points to remains valid.

author: denizcan
author_rank: Regular Contributor
timestamp: October 18, 2013, 12:14:47 am
content: Quote from: e100 on October 17, 2013, 11:13:03 pmDon't stick your self to a chip and to a system that is designed for/has 2KB RAM.. If you can do, go with ARM, and RTOS'es that run on them.. That way, if memory is not enough, you can switch one size larger..

author: denizcan
author_rank: Regular Contributor
timestamp: October 18, 2013, 12:23:10 am
content: Quote from: nctnico on October 17, 2013, 11:47:15 pmIn timer I don't think you create large data.. I think what's on your mind is UART.. I have really unusual and effective different approach on UART.. However, lets assume we created large data in IRQ, that does not fit in pointer size value, then mailbox might be the solution..Code:typedef struct{Int something;UInt otherThing;} SomeData;Mailbox<SomeData, 8> mailbox;void handleSomeEvent(void* sender, void* param){// prepare dataSomeData data;data.something = 1234;data.otherThing = 5678;mailbox.tryPush(data);}void handleTask(Task* t, void* param){while (1){SomeData data;mailbox.pop(data);printf("etc etc", data.something, data.otherThing);}}The loop in handleTask is on hold until something is pushed into mailbox..

author: denizcan
author_rank: Regular Contributor
timestamp: October 18, 2013, 12:42:10 am
content: Also suppose you made printing central, that prints different incoming data from multiple sources.. Than multiple mailbox's you may wait:Code:void handlePrintingTask(Task* t, void* param){while (1){Result r = waitAny(mailbox1.getPushEvent(), mailbox2.getPushEvent(), 100);if (r == 0){printf("timeout");continue;}if (r & BIT0){DataType1 data;mailbox1.pop(data)printf(..)}if (r & BIT1){DataType2 data;mailbox2.pop(data)}}}I know a little odd approach..