Help designing some multitasking practice excercises

Go To Last Post
19 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Newbie.  My programs (the few I've written, a long long time ago) have always been kind of brute force (if this, then that) just controlling buttons, on chip A/Ds, and LEDs/IOs.  I want to 'up' my game by getting a better understanding of the various different multitasking architectures/approaches, and use some practice exercises to develop code, learn, and add these concepts to my personal toolbox of techniques.  I've heard terms like polling, interrupt driven, system tick, cooperative, preemptive.  As a development platform, I'm using the ATmega328P Xplained Mini, which has a button, LED, and UART (or, I guess they call it USART).  I know it's got limited stuff on it so any multitasking exercises would be very contrived, but the Xplained Mini is really convenient (super cheap, really small board, connect direct to computer without separate emulator, I can toss in my backpack and play around at Starbucks, I've got those elements (button, LED, UART to/from RealTerm) working ... later, after getting a better handle on multitasking through these exercises, I'll add an Arduino prototyping shield with some I2C and SPI components to get some practice in chip/chip communication).

 

So, with a button, LED, and UART, I'm looking for your suggestions of some contrived exercises I could implement to get broad exposure to various multitasking approaches/architectures.  Thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have a look at the examples at:

http://www.femtoos.org/examples.html

 

Also head to the tutorials forum and have a look at the tutorials on multitasking.  Start here maybe:

https://www.avrfreaks.net/search/site/multitasking?f[0]=im_taxonomy_forums%3A636

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think there is a test for whether or not to carve an app up into tasks. Using the task switcher involves writing each task as a separate program loop, and these programs need to talk to each other without stomping on each other. I think one of the tests is: does writing the keypad handler and display handler as tasks make the overall job a little simpler? Think of a contrived example that has 3 tasks.  They assume the uart is initalized to 115200. One task runs every 10ms, one runs every 50ms, one runs every 250ms. Each prints out his number. Should see 111112111112... and a 3 somewhere in there. So you should be able to run 3 tasks... the keypad, the display and a debug termial using the uart like this. A 328 with 2K or ram only has ram space for about 3 tasks.

 

Imagecraft compiler user

Last Edited: Sun. Jan 11, 2015 - 06:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
A 328 with 2K or ram only has ram space for about 3 tasks.
Surely that depends on the tasks.

 

From Femtoos:

 

 

 

Granted the examples given are trivial, but claiming a limit of 3 tasks for a 2K device is a bit misleading.

 

The question of whether or not a real-time OS has merit on very small devices is one we'll set aside as an argument for another day ;)

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Sun. Jan 11, 2015 - 07:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now we are quibbling about what a task is. Back in the 70s, I got stumped when someone decided to put a terminal on the trainer so the instructor could enter the students name and some details and hit print and he'd get a piece of paper with his test score. Pretty sharp for '76 or so. All ran on a 1MHz 6800. The realtime part of the program let the student 'inspect' items on the aircraft by pressing onoff lit toggle pushbuttons near the interesting stuff that had slides to view on the projector, with pictures of the control surface up, centered and down. When a button was selected to view a slide, the button was flashing 250 ms on, 250 ms off, and the slide was displayed. So far so good. If the instructor got a bee in his bonnet right then to enter the student record into the terminal, he'd hit a function key on the terminal, get a prompt that said 'Enter name', and everything stopped to wait for chars. Of course, all the flashing buttons on the panel trainer froze while the instructor pecked in the name at one char per 5 secs. So I modified the 'getlinefromcomsole' routine to look for 'he hasnt hit a char in 100ms, run another pass of the program', save how many chars have been entered, next pass of the program look at the flag that was set that says 'instructor is entering line on console' and jump into the middle of the getlinefromconsole routine. Basically, it was a kludge, and would have been a lot easier to write this as a task. Sorry my explanation was so long and boring.

 

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A 328 with 2K or ram only has ram space for about 3 tasks.

The implication of that statement is that a task requires 682 bytes?!? That's quite a claim. Obviously in a pre-emptive OS a "task" requires at least a copy of the system registers so 32 R0..R31, SREG, SPH/SPL and PC. But that's just 37 bytes. A task presumably has a private stack too but assuming you are using 37 bytes for the context are you really saying the task requires 682-37 = 645 bytes for its stack? Many tasks can happily live with 32/64/128 bytes of stack. So why are you saying there's a 3 task limit in 2K?

 

Previously I took some FreeRTOS demo code and ran it on a 168 with each task either flashing LEDs or doing some UART stuff. I think I was easily able to have 5 tasks in the 1024 bytes of RAM in a mega168.

 

So I would dispute your 682 bytes/task requirement.

 

(as the figures from Joey for Femto-OS also seem to)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You guys sure are argumentative. Lets say I have a complicated app running on a 328 and its using half or twothirds of the 2K of ram. Now I add freertos to the mix and need three tasks. Betcha it dont fit.

 

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry but your argument makes no sense. By splitting the one app into three tasks exactly what extra "overheard" do you think is added? As I say a TCB is going to be about <=40 bytes per task then either they share RAM in a central pool or you give them each some stack space for "private" automatics but your half full 2K means there's still 1K to go. Each task can have 40 bytes for the TCB and 256 for a private stack and you still have the 1K of "shared"/global buffers or whatever.

 

So I just don't see it.

 

Yes RTOS usage does "cost" but the cost is probably only a few hundred bytes per task, if that.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's what I said Cliff. Start with 1.5K out of 2K used, add in freertos with 3 tasks with 200 bytes per task, and yer over.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And no again because of that 1.5K some of it just moves to the tasks. So the "cost up" of using the RTOS need not really be much more than 40 bytes per TCB and perhaps give them 32 bytes each for their CALL/RET stacks. the other data usage can just be from that common 1.5K pool.

 

Do you use RTOS on AVR yourself or have you at least run any experiments using FreeRTOS or similar? I think you have to to get a real feel for how much using the RTOS actually "costs". There is a cost involved but I don't think it's as bad as some wild rumours might suggest.

Last Edited: Mon. Jan 12, 2015 - 06:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

... getting back to my original question of some contrived exercises I could implement to get broad exposure to various multitasking approaches/architectures ...

 

I think what I'm going to do is blink on/off the LED at a slow rate, say 4 sec.  At any time that the button is pushed (even in the middle of an LED ON event), the blink rate will change to a very fast rate (say, 2 times/sec).  Then, pushing the button again returns the blink rate to the slow rate.  Of course, implementing this with an approach as outlined in the multitasking tutorials.

 

For a second phase, I'll add echoing input from the keyboard to the RealTerm display (without interruption to the LED blink rate).  Perhaps, a simple single digit calculator (addition only).

 

 

Open to any other inputs, suggestions that may provide more instructive exercises for multitasking ...

Last Edited: Mon. Jan 12, 2015 - 11:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To be honest you are probably going to find any small AVR example "contrived". It's very rare to do anything in a CPU as small as an AVR that absolutely must use an RTOS and multiple tasks/threads. Most AVR programs are structured as a main loop handling the "slow stuff" and a few ISRs handling anything that needs fast response. Everything you described above could easily be done like that without an RTOS. So it's more for education than any practical reason that you might use an RTOS.

 

An RTOS comes into its own when you have "too may plates spinning" and need to keep them all serviced "at the same time". The first true practical example I can think of might be doing TCP/IP and running more than one protocol server (HTTP, FTP, etc) on top of it "at the same time". When you reach that level of complexity then an RTOS finally starts to help rather than get in the way or an otherwise clean/simple design.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson,

 

Sorry if my original post was misleading.  I'm interested in learning correct construction of a "clean/simple design".  Not really a full RTOS.

 

This has me confused:  "Most AVR programs are structured as a main loop handling the "slow stuff" and a few ISRs handling anything that needs fast response."

 

I was thinking it would be the other way around?  Let me describe what I was thinking, please let me know where I've gone astray:

 

  • First, say there is a program that blinks on/off LED as fast as it can (obviously, you wouldn't be able to see it).  In the main loop, you could toggle a port pin, and the main loop will cycle through that toggle as fast as the CPU clock will let it.
  • Next, say we want to blink on/off the LED at a 1Hz rate.  You could put delays in the main loop, but then the CPU can't do anything else while those delays are there.  Instead, we set up a 10ms tick from an interrupt timer.  Every 10ms tick, we go off into a state machine that instead of using delays to blink the LED, it uses a state machine to count 10ms ticks and determine when it's time to toggle the LED for the desired rate.
  • Now, with the state machine implementation, we have something scalable, where we can add more to the state machine to handle other stuff (button operations, button debounce, UI system with the button/LED, whatever).
  • Now, looking at the structure, we have a state machine that's called every 10ms (not terribly fast?), and a main loop that will cycle as fast as the cpu (fast) minus the latency through the state machine (probably not too long?).
    • ​So, I was thinking that if there was something that needed to be polled very frequently, it would go into the main loop, and not the state machine called on 10ms ticks?
Last Edited: Tue. Jan 13, 2015 - 04:42 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jl-dev wrote:

 

So, I was thinking that if there was something that needed to be polled very frequently, it would go into the main loop, and not the state machine called on 10ms ticks?

 

Or, even better, hook it up to an external interrupt and get your ISR to service it.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

but then the CPU can't do anything else while those delays are there.

Not true. The usual problem with "soft delays" (unless you really do cli() before them) is that they can easily be interrupted and the time spent in ISR handling can string out the delay a bit - though if the ISR is well written it shouldn't really take more than a few microseconds.

 

So if you were doing an LED flasher where the flash rate could be varied by the press of a button I think I'd just have:

while(1) {
    delay(mydelay);
    PORTB ^= 0xFF;
}

and that flashes the LED. Then before this I'd set up a "button interrupt" - either a timer to have a nose at the button from time to time or (if careful with debounce) an ext-int or pcint that the button triggers. In the handler I'd have:

ISR(button_vect) {
    mydelay *= 2;
}

so each time the button was pressed the delay would be increased (until it wraps in the width of "mydelay").

 

An awful lot of MCU programs have a while() loop in main() handling "slow stuff" but a 10ms (perhaps 50ms or 100ms) timer "tick" interrupt that might actually be doing a number of things (though fairly quickly so as to not block main() for too long). It could do the LEd flashing and the button input polling (with debounce) too. I suppose this is "multi-tasking" but is not as "full on" as a true RTOS where you effectively have a number of those main() while(1) loops all sitting side by side and each doing their own thing.

Last Edited: Tue. Jan 13, 2015 - 05:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A task is usefull when the input to the task is not synchronized with the main loop looptime. Like someone typeing in a command line and handling echo and backspace, and the other stuff that is looping and blinking keeps on a ticking. Or sticking in a bill doesnt make the scrolling greeting message get all spasmo. Etc. How about this: 3 tasks. Task 1 prints "Hello from task 1" over and over every sec, in the top third of the window. Task 2 prints "Hello from task 2" over and over in the middle third every 2 sec. Task 3 is a monitor with a menu that lets you set the secs of task1 and task2. You need each task to not stomp on the other tasks use of the uart, you need to save where the cursor is in each window. You have to scoll the three windows. Enough meat to chew on?

 

 

 

Imagecraft compiler user

Last Edited: Wed. Jan 14, 2015 - 01:42 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bobgardner wrote:

How about this: 3 tasks. Task 1 prints "Hello from task 1" over and over every sec, in the top third of the window. Task 2 prints "Hello from task 2" over and over in the middle third every 2 sec. Task 3 is a monitor with a menu that lets you set the secs of task1 and task2. You need each task to not stomp on the other tasks use of the uart, you need to save where the cursor is in each window. You have to scoll the three windows. Enough meat to chew on?

 

Bob,

 

Thanks for your inputs!  I'll use this for my Phase 2, where I'm introducing the UART.  I'll probably have to take the baby step of just outputting things line by line (I'll have too read up about how to navigate on a RealTerm page), but it will get at a lot of the essence of the multitasking part.

 

Thanks again ...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jl-dev wrote:

Sorry if my original post was misleading.  I'm interested in learning correct construction of a "clean/simple design".  Not really a full RTOS.

 

Take a look a the Cyclic Executive Framework in http://www.embeddedrelated.com/s...

 

This allows your main loop to respond to timing events as well as any other flags or status bits you choose.  It's the fastest response you'll get without interrupts (not saying you shouldn't or couldn't also have interrupts active at the same time).  

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kk6gm,

 

Thanks for the link to that article.  Great article, and looks like a great blog too ...