[ TUT ] [ C ] Multitasking - another approach (unfinished)

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

As requested I'll introduce an approach to multitasking on the AVR, this solution is platform dependent in respect to how the stackpointer indexes etc but don't worry, we won't need to think about that unless you don't use an AVR! This has worked on the few AVRs I've come across, atmega169 and the atmega328p on the Arduino board (if you've got an Arduino UNO aswell you can test the .hex-file submitted very easily!).

 

Alright, so what am I suggesting? In C's standard library there is a header file called setjmp.h that provides, to quote wikipedia, "non-local jumps". This can be used in a nifty way to achieve multitasking, read the link for more.

For education purposes, a relatively small program called tinythreads has been written, which is what this tutorial is about. I am not the author of this program, I'm not sure who is.

http://www.sm.luth.se/csee/courses/d0003e/labs/tinythreads.h

http://www.sm.luth.se/csee/courses/d0003e/labs/tinythreads.c

This template is incomplete though, with code missing for the crucial yield-method, and code for using mutexes (not necessarily needed for multitasking). This is used for universities so I will not disclose the code here (if you're a good googler you can still find it though, or pm me to get the missing pieces).

 

There is a lot to read about this if you want to learn the intricate details of the implementation of tinythreads, here are the lecture slides that describe the functionality:

http://www.sm.luth.se/csee/courses/d0003e/lectures/lecture4.pdf

The font is comic sans which makes your eyes bleed but please scroll through it anyway.

 

So what does tinythreads do? It allocates stack space to methods you spawn, and switches between them. The switching can be implemented differently but the most basic way is just in a round-robin fashion. The switch can be triggered by the thread itself by a call to yield (cooperative multitasking) or via some interrupt (preemptive multitasking). What yield does in it's most simple implementation is (except disabling interrupts) is enqueuing the currently running thread and dispatching the first waiting task in the ready-queue.

 

How then do you use it? First, pm me about the implementation of "yield" if you can't figure it out yourself. Then, in your c-file, simply include tinythreads.h. Then to run your desired tasks, call on the method spawn, supplying your task and an argument to it (if your task doesn't take any arguments then just write a zero or whatever (and if you want your task to take more arguments you can easily modify the code in tinythreads - the struct "thread_block" and the definition of spawn)).

Your tasks can either spawn another task or instance of itself, or be endless. I will provide examples.

 

// in this example, yield must be called from an ISR (preemptive)

static void task1(int i) {
    while (1) ... ;
}

static void task2(int i) {
    while (1) ... ;
}

int main(void) {
    spawn(task1, 1);
    spawn(task2, 1);
    while (1) ... ;
}
// in this example, yield is called from within each task (cooperative)

static void task1(int i) {
    while (1) {
        ...
        yield();
    };
}

static void task2(int i) {
    while (1) {
        ...
        yield();
    }
}

int main(void) {
    spawn(task1, 1);
    spawn(task2, 1);
    while (1) {
        ...
        yield();
    }
}
// in this example, the tasks spawn new tasks, yield is called from the ISR

static void task1(int i) {
    ...
    spawn(task1, i);
}

static void task2(int i) {
    ...
    spawn(task2, i);
}

int main(void) {
    spawn(task1, 1);
    spawn(task2, 1);
    while (1) ... ;
}
// in this example, the tasks spawn new tasks, yield is called from main

static void task1(int i) {
    ...
    spawn(task1, i);
}

static void task2(int i) {
    ...
    spawn(task2, i);
}

int main(void) {
    spawn(task1, 1);
    spawn(task2, 1);
    while (1) yield();
}

There are some things you can play with in tinythreads, for instance what a "thread_block" struct may contain, how big the stack for each task is (it's denoted 80 in the standard implementation but that's quite a lot, you can get by with much less depending on your tasks), and how many threads you allow (4 by default).

The .hex-file I've submitted is a program that blinks three LEDs, connected to Arduino UNOs digital pins 2, 4 and 7 aswell as the on-board orange LED, they blink at an increasing rate then starts over, triggered by the watchdog timer. My code is modified though and not as the original tinythreads, I can of course give this aswell if wanted. I'm not sure how the .hex behaves on other atmegas than the 328p.

Attachment(s): 

sol i sinne - brun inne

Last Edited: Wed. Nov 1, 2017 - 12:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's a video of how the program I attached behaves, for you who can't test it IRL. Four LEDs blink with increasing speed (if you look at one LED at a time it's easier to distinguish). It's built upon the tinythreads kernel but I've modified some details in predefined routines and also added a task scheduler which decides which thread to run based on the desired "delay" (a parameter I added to the thread_block struct in tinythreads.c), and not just blindly round-robin switch as the "default" implementation would do.

 

While the LEDs are not being turned on or off or the kernel is context switching, the system is in Standby Mode, CPU otherwise @62500Hz

sol i sinne - brun inne

Last Edited: Tue. Feb 3, 2015 - 04:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Tickstart, could you please provide the implementation of Yield() and your examples?

 

Much appreciated

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

JohanRoux wrote:

Hi Tickstart, could you please provide the implementation of Yield() and your examples?

 

Much appreciated

 

Did you read his...

This template is incomplete though, with code missing for the crucial yield-method, and code for using mutexes (not necessarily needed for multitasking). This is used for universities so I will not disclose the code here (if you're a good googler you can still find it though, or pm me to get the missing pieces).

Ross McKenzie ValuSoft Melbourne Australia

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

I did read it! I also read the part where the implementation of Yield() and the examples were offered... ("... pm me to get the missing pieces") 

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

So why did you post in the forum - instead of asking by PM, as directed?

 

cheeky

 

But it does seem like a pretty poor "Tutorial" if key parts are missing!

 

frown

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

I've edited the thread title to reflect this - "unfinished" can be removed when the text in #1 shows a full implementation.

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

My bad, sorry! This might sound silly but how do I go about "asking by PM"? How does that work?

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

JohanRoux wrote:
how do I go about "asking by PM"? How does that work?

  1. click on the username
  2. on the resulting page, in the right-hand sidebar, is a 'Send Private Message' link ...
  3. at top-right of every page in the forum, next to your username, is an envelope icon with a count of your new PMs - click it to see your PMs

 

and that's all there is to it ...

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

Thank you very much!