Cooperative Multitasking Design Using State Machines

Thread Starter

Sparsh45

Joined Dec 6, 2021
143
I am planning to develop cooperative multitasking in c language. I'm looking for suggestions on how to write a generic code for cooperative multitasking using a state machine.

  1. The code must be reusable. It must run on typical low-end embedded system with an 8-bit CPU clocked at 25 MHz, a few KB of RAM, and perhaps 32 KB of flash memory.
  2. System tick should be user defined. Default it should be 1ms
  3. Maximum 8 number of tasks can be created.

There are many ways to develop it but suggest me any good option.

  1. I would generate system tick using timer interrupt
  2. I would keeps track of time by counting ticks
  3. I would use switch case statement for task state.
  4. I would use a function to create the task.
  5. i would use list as an array of task

What is the feedback of embedded programmers on the choices I made?
 

BobTPH

Joined Jun 5, 2013
8,813
Using state machines is an alternative to true multitasking. Your post demonstrates confusion about the two. It is also confused between cooperative and preemptive. There is no time quantum in cooperative. Each task runs until it voluntarily gives up control.

To me, a multitasking system has multiple threads of execution that can be written pretty much as if they were the only task running.

Let’s go back to your original example of blinking 3 LEDs. Your solution to it was a single task solution. A multitask solution would look very different. It would have a single function that blinks 1 LED. You would the create 3 tasks, each running that same function but with different parameters. The multitasking system would handle everything else.

Bob
 

Thread Starter

Sparsh45

Joined Dec 6, 2021
143
Let’s go back to your original example of blinking 3 LEDs. Your solution to it was a single task solution. A multitask solution would look very different. It would have a single function that blinks 1 LED. You would the create 3 tasks, each running that same function but with different parameters. The multitasking system would handle everything else.

Bob
I don't understand how one function can be used for all three tasks. How to create 3 tasks , do we have to write each in a separate function?

LED 1 change state every 100ms
LED 2 change state every 200ms
LED 3 change state every 300ms

This is the code we are talking about it.

C:
LED1
LED2
LED3

enum LED_STATE ( STATE1 = 1, STATE2 = 2, STATE3 = 3)STATE;

void main ()
{
    unit8_t Timer1 = 0 ;
    unit8_t Timer2 = 0 ;
    unit8_t Timer3 = 0 ;

    LED1 = ON ;
    LED2 = ON ;
    LED3 = ON ;

    STATE = STATE1;

    while (1)
    {
        if (Timer_Flag == 1)
        {                        // Timer_Flag set every 10ms
            Timer_Flag = 0;
       
            //increment each timer every 10ms
            Timer1++ ;
            Timer2++ ;
            Timer3++ ;
   
            switch (STATE)
            {
                case STATE1 :
                            if ( Timer1 == 10 )   // check if time is 100ms
                               {
                                 LED1 =~ LED1 ;   // change state of LED1
                                 Timer1 = 0;      // clear Timer1 for next time
                                 STATE = STATE2 ; // Move to next STATE
                               }
                               break;
                case STATE2 :
                            if ( Timer2 == 20 )   // check if time is 200ms
                               {
                                 LED2 =~ LED2 ;   // change state of LED2
                                 Timer2 = 0;      // clear Timer2 for next time
                                 STATE = STATE3 ; // Move to next STATE
                               }
                              break
                case STATE3 :
                              if ( Timer3 == 30 )  // check if time is 300ms
                              {
                                LED3 =~ LED3 ;     // change state of LED3
                                Timer3 = 0;        // clear Timer3 for next time
                                STATE = STATE1 ;   // return to STATE1
                              }
                              break;          
            }

}

// interrupt every 10ms
void ISR_Timer_Interrupt ()
{
    if ( T1IF = SET )
    {
      T1IF = 0;
      Timer_Flag = 1;
    }
}
 

BobTPH

Joined Jun 5, 2013
8,813
The code could look like this:

Code:
extern void start_task(func_ptr ptr, ...);

void blink(int time, int port, int bit)
{
    while (1)
    {
        port = port | 1 << bit;
        delay(time);
        port = port & ~(1 << bit);
        delay(time);
    };
};

void main()
{
    start_task(blink, 100, PORTA, 0);
    start_task(blink, 200, PORTA, 3);
    start_task(blink, 300, PORTB, 2);
}
See how simple this is compared to what you wrote? This is why we have multitasking systems.

Bob
 

Thread Starter

Sparsh45

Joined Dec 6, 2021
143
See how simple this is compared to what you wrote? This is why we have multitasking systems.
It looks great. It's easier than what I was trying to do. This is one way to implement cooperative multitasking.

Do you have any idea to design cooperative multitasking using state machine which will be reusable ?

I don't understand how tasks will be created and how tasks will be scanned in cooperative multitasking .
 

BobTPH

Joined Jun 5, 2013
8,813
Do you have any idea to design cooperative multitasking using state machine which will be reusable ?
No, because a state machine is specific to each task you need to run, there us nothing reusabl unless you need to riun the same task for a different project.

I don't understand how tasks will be created and how tasks will be scanned in cooperative multitasking .
That is the question, isn’t it? Writing the different tasks is simple, but implementing my start_task function is not.

Bob
 

BobTPH

Joined Jun 5, 2013
8,813
In my example, the delay function is where the task switching and scheduling is invoked. It is a critical part of the multitasking system.

Bob
 

Thread Starter

Sparsh45

Joined Dec 6, 2021
143
There is no time quantum in cooperative. Each task runs until it voluntarily gives up control.
Bob

Let's say there are three Tasks

  1. LED 1 flashing every 200ms
  2. LED 2 flashing every 300ms
  3. LED 3 flashing every 400ms

As you said, Each task runs until it voluntarily gives up control.

I'm trying to figure out how each LED task controls the CPU? I do not understand when the LED task will run and when the task will give up control of the CPU in cooperative design ?
 
Last edited:

ApacheKid

Joined Jan 12, 2015
1,533
Bob

Let's say there are three Tasks

  1. LED 1 flashing every 200ms
  2. LED 2 flashing every 300ms
  3. LED 3 flashing every 400ms

As you said, Each task runs until it voluntarily gives up control.

I'm trying to figure out how each task controls the CPU? I do not understand when the task will run and when the task will give up control of the CPU cooperative design ?
I tried to explain this earlier I think, the best way to understand this is with a simple analogy, all of the problems and challenges that come up in the analogy will also come up with a computer implementation too, all a computer really does is manipulate information so we must focus on what information we are dealing with and why and how.

The CPU is a person.

A set of different activities are the "tasks".

We have one person and several tasks to accomplish, for example cook dinner, paint a room and put clothes away.

People do this by multitasking:

1. Choose a task to do - Cook.
2. Put oven on, get ingredients from fridge.
3. Rather than wait for oven to reach temp, find something else to do next...

As soon as the person has to wait they stop doing that task and another task gets chosen:

2. Choose a task to do - Paint.
3. Clean surface to be painted.
4. Get tin of paint from garage.
5. Open tin.
6. Start to paint wall.
7. After 30 minutes stop painting and find something else to do next...

So the person has painted for a solid 30 minutes but now wants to take care of clothes:

8. Choose a task to do - Get clothes from laundry room
9. Dump the clothes on the bed.
10. Start putting socks away.
11. After 10 minutes stop and find something else to do next...

12. Go to oven.
13. Is it heated up yet?
14. No: Choose a task to do: Paint.
15. Yes: Mix ingredients
16. Put into baking dish.
17. Put dish in oven.

18. Choose a task to do - Paint.

and so on, this is what we're really talking about, if you fully analyze this situation, fully define the steps, the details the rules the person must follow, if you do that then you'll understand what the CPU has to do, what its roles is, what the important state information is and so on.

If you cannot write down reasonably precise rules for physical activities like this then you'll never be able to write then for a real machine.

In other words you MUST fully understand the problem domain before you start to design the solution domain.

So look at the above what kinds of things are we dealing with?

  • A set of tasks, each one has a set of steps and decisions to make.
  • A time to spend on a task or a point at which to stop doing a task and start another.
  • A way to remember exactly where we were on a task at the time we stop doing it.
  • A way to look at the set of tasks and decide which one to do next.

This could be a simple notepad with a page for each task. The page would contain the task steps and we'd use a pen to mark which step we are on when we decide to stop doing that task.

Get that all straight in your mind, then you can begin to design code because this is all the scheduler is doing, this is what it does, a computer scheduler is no different.
 
Last edited:

Thread Starter

Sparsh45

Joined Dec 6, 2021
143
@ApacheKid

What is your understanding for three tasks with single CPU

  1. LED 1 flashing every 200ms
  2. LED 2 flashing every 300ms
  3. LED 3 flashing every 400ms
I know that CPU takes very less time to control the LED.

can you explain for me how tasks take control of the CPU when they are needed and give up when they are not needed for LED flashing example.
 

ApacheKid

Joined Jan 12, 2015
1,533
@ApacheKid

What is your understanding for three tasks with single CPU


I know that CPU takes very less time to control the LED.

can you explain for me how tasks take control of the CPU when they are needed and give up when they are not needed for LED flashing example.
No, I really don't think I can say anything more on this subject.
 

BobTPH

Joined Jun 5, 2013
8,813
It gives up control when it calls the delay function. It will get control back soon after the delay time is over. There is no guarantee that it will be exactly at that time, we rely on the fact that no task runs for a long time without giving up control.

There are typically various calls that give up control. One would be yield, which gives up control until the next time the task is at the head of the queue in the round robin order. Another might be one that gets a character from a serial line.

Note that all of these functions are part of the OS.

Bob
 

Thread Starter

Sparsh45

Joined Dec 6, 2021
143
It gives up control when it calls the delay function. It will get control back soon after the delay time is over. There is no guarantee that it will be exactly at that time, we rely on the fact that no task runs for a long time without giving up control.
I am really want to know time when CPU is free and when it is used by Task. Before coding, Can we find out mathematically which task is using CPU and when is releasing it ?

I just want to make a graph for the solution which will represent the time on the X axis and represent what tasks are being executed on the Y axis.

I think this type of graph will help me to clear my confusion
 

BobTPH

Joined Jun 5, 2013
8,813
You could, but what us the point? To me, a multitasking OS relieves the user from having to know those details. You are too focused on the details and are missing the big picture, which is that the OS gives the illusion that the tasks are running in prallel.

Bob
 

Thread Starter

Sparsh45

Joined Dec 6, 2021
143
You are too focused on the details and are missing the big picture
I was trying to understand mathematically what you said. I was hoping that you would explain the given statement with reference to my example as I am not able to do it.

Cooperative means that individual tasks routinely give up control of the CPU when they no longer need it. Each task run until it voluntarily gives up control.

I have three tasks and how does each LED task decide whether to give up the control of the CPU Or not. you said i have to call the delay function but i don't understand it. How does the task give up the CPU control when the delay function is called?
 

Thread Starter

Sparsh45

Joined Dec 6, 2021
143
Hey @Sparsh45

I have already done it for you here

Hope this helps :)
I don't really understand your suggestions Here in post #47.

Do you mean I need to implement task's state something looks like below example

C:
enum Task_STATE {READY_To_RUN, RUNNING, WAITING};
 
enum Task_STATE STATE_FLAG;
 
 
int main(void)
{
 
  
    STATE_FLAG = READY_To_RUN;
  
    while(1)
    {
 
     if (Timer_Flag == 1)
        {   
            // Timer_Flag set every 10 ms   
            Timer_Flag = 0;
            {
      
              switch (STATE_FLAG)
               {
                   case READY_To_RUN :
                   // Pick the taks that is ready to run
                   STATE_FLAG =  RUNNING;
                   break;
 
                   case  RUNNING :
                   // Run the task
                   STATE_FLAG = WAITING;
                   break;
 
                   case WAITING :
                   // waiting
                   STATE_FLAG = READY_To_RUN;
                   break;
 
                  default:
                  // Unknown State Encountered, Reset System to READY_To_RUN,
                  STATE_FLAG = READY_To_RUN;
               }
            }
 
        }
    }
 
    return (0);
}

// interrupt every 10 ms
void ISR_Timer_Interrupt ()
{
    if ( T1IF = SET )
    {
      T1IF = 0;
      Timer_Flag = 1;
    }
}
 
Top