Help please to understand code

Thread Starter

rt694157

Joined Dec 15, 2019
57
Hi, I need help to understand below code. I have yet to understand that this code is designed for multitasking.

First of all I want to understand function task_dispatch(void) what it supposed to do?

C:
#include
#include
#include

void init_devices(void);
void timer0_init(void);
void reset_task(char tsk);
void set_task(char tsk);
void task_dispatch(void);
void task0(void);
void task1(void);
void task2(void);
void task3(void);
void task4(void);
void task5(void);
void task6(void);
void task7(void);

#define NUM_TASKS 8
char task_bits = 0;  /* lsb is hi priority task */
volatile char tick_flag = 0;    /* if non-zero, a tick has elapsed */
unsigned int task_timers[NUM_TASKS]={0,0,0,0,0,0,0,0};                  /* init the timers to 0 on startup */
static const PROGMEM char bit_mask[]={1,2,4,8,16,32,64,128};            /* value -> bit mask xlate table */

int main(void)
{

  init_devices();
//
//    start at least one task here
//
set_task(7);    //task7 runs
set_task(6);    //task6 runs

//      main loop

  while(1)
    {
    if (tick_flag)
      {
      tick_flag = 0;
      task_dispatch();              // well....
      }
    }
  return 0;
}
//
//    a task gets dispatched on every tick_flag tick (10ms)
//
void task_dispatch(void)
{
  /* scan the task bits for an active task and execute it */

  char task;
  

/* take care of the task timers. if the value ==0 skip it
    else decrement it. If it decrements to zero, activate the task associated with it */

  task=0;
  while (task < NUM_TASKS )
    {
    if (task_timers[task])
       {
         task_timers[task]--;            /* dec the timer */
       if (task_timers[task] == 0 )
               {
            set_task(task); /* if ==0 activate the task bit */
            }
       }
    task++;
    }

  task = 0; /* start at the most significant task */
  while (task <= NUM_TASKS )
    {
      if ((task_bits & pgm_read_byte(&bit_mask[task])))
              {
              break; /* if activate task found..*/
            }
      task++;         /* else try the next one */
    }
  switch(task)            /* if task bit is active..execute the task */
    {
    case 0:
      task0();
      break;
    case 1:
      task1();
      break;
    case 2:
      task2();
      break;
    case 3:
      task3();
      break;
    case 4:
      task4();
      break;
    case 5:
      task5();
      break;
    case 6:
      task6();
      break;
    case 7:
      task7();
      break;
    default:
      break;                  /* no task was active!! */
    }                     
}

// enable a task for execution
void set_task(char tsk)
{
  task_bits |= pgm_read_byte(&bit_mask[tsk]);       /* sets a task bit */
}
// disable a task from executing
void reset_task(char tsk)
{
  task_bits &= (~pgm_read_byte(&bit_mask[tsk]));  /* resets a task bit */
}

void task0(void)
{
  reset_task(0);
}
void task1(void)
{
  reset_task(1);
}
void task2(void)
{
  reset_task(2);
}
void task3(void)
{
  reset_task(3);
}
void task4(void)
{
  reset_task(4);
}
void task5(void)
{
  reset_task(5);
}
//
//    flash PORTB.4 at 2hz
//
void task6(void)
{
PORTB ^= (1<<4);
task_timers[6] = 25;        //every 250ms
reset_task(6);
}
//
//    flash PORTB.5 at 1hz
//
void task7(void)
{
PORTB ^= (1<<5);
task_timers[7] = 50;        //every 500ms
reset_task(7);
}
//call this routine to initialize all peripherals
void init_devices(void)
{
//stop errant interrupts until set up
cli(); //disable all interrupts


DDRB = 0x30;    //port 4 & 5 as outputs

timer0_init();

MCUCR = 0x00;
EICRA = 0x00; //extended ext ints
EIMSK = 0x00;

TIMSK0 = 0x02; //timer 0 interrupt sources

PRR = 0x00; //power controller
sei(); //re-enable interrupts
//all peripherals are now initialized
}
//TIMER0 initialize - prescale:1024
// WGM: CTC
// desired value: 10mSec
// actual value: 10.048mSec (-0.5%)
void timer0_init(void)
{
TCCR0B = 0x00; //stop
TCNT0 = 0x00; //set count
TCCR0A = 0x02;    //CTC mode
OCR0A = 0x9C;
TCCR0B = 0x05; //start timer
}

ISR(TIMER0_COMPA_vect)
{
//TIMER0 has overflowed
     tick_flag = 1;
}
Thanks in advance
 

Thread Starter

rt694157

Joined Dec 15, 2019
57
It looks for tasks that are "ready to run" and it runs them when their turn comes up.
Program flash two LEDs at different different speeds

There are total 7 tasks but only one task will run at one time. Can you elaborate a little bit because I still don't understand it completely how program determine which task and when it should be run
 

Papabravo

Joined Feb 24, 2006
14,412
A task has one or more state variables associated with it. The purpose of task_dispatch() is to update and examine those variables for the purpose of deciding which task to run next. Each multitasking system is different in the number of state variables and how they are evaluated to make this decision. There is no "one size fits all" design. For example suppose you call task_dispatch() and there is only one task ready to run. The decision is simple it runs that task. Now suppose there are two tasks ready to run and one of them has been waiting to run for a longer period of time. It might decide to run the task which has been waiting the longest. But wait, suppose task priority is a state variable and the rule is that you run the highest priority task that is ready to run. You being to see the difficulty in having a "one size fits all" implementation.

What YOU need to do is examine the code to find out what the state variables are and how they factor into the decision making process.
 

andrewmm

Joined Feb 25, 2011
574
Program flash two LEDs at different different speeds

There are total 7 tasks but only one task will run at one time. Can you elaborate a little bit because I still don't understand it completely how program determine which task and when it should be run
From the name it could be just about anything,

Hopefully who ever wrote the code your using, has it documented,
which is what you need to do

At a higher level,
how are you on the basics on how task managers work in general, can you name the different types ?
 

Papabravo

Joined Feb 24, 2006
14,412
I told you in general terms what the function is supposed to do. I don't have the time or the inclination to offer a line-by-line exposition of the code since I do not have access to any documentation or the original designer's thought processes. I'm afraid you're just going to have to dig in and pull yourself up by the bootstraps. You could also abandon the effort and move on to something more tractable and come back to it later. I often do things that way.

PS The first question, which you have not yet answered: "What are the state variables?".
 
Last edited:

ci139

Joined Jul 11, 2016
1,696
a bit cleaned up
// what it seems to be is an early pass/-concept of something yet not tested/ready to use ?
C-like:
#include
#include
#include

void init_devices(void);

void timer0_init(void);

void reset_task(char tsk);
void set_task(char tsk);

void task_dispatch(void);

void task0(void);
void task1(void);
void task2(void);
void task3(void);
void task4(void);
void task5(void);
void task6(void);
void task7(void);

#define NUM_TASKS 8

                      char task_bits = 0; /* lsb is hi priority task */
             volatile char tick_flag = 0; /* if non-zero, a tick has elapsed */
unsigned int task_timers[NUM_TASKS] = {0,0,0,0,0,0,0,0}; /* init the timers to 0 on startup */
static const PROGMEM char bit_mask[] = {1,2,4,8,16,32,64,128}; /* value -> bit mask xlate table */

int main(void)
{ init_devices();

// start at least one task here

  set_task(7);    //task7 runs
  set_task(6);    //task6 runs

// main loop

  while(1)
  { if (tick_flag)
    { tick_flag = 0;
      task_dispatch();              // well....
    }
  } return 0;

}

//
//    a task gets dispatched on every tick_flag tick (10ms)
//

void task_dispatch(void)
{ /* scan the task bits for an active task and execute it */

  char task;

/* take care of the task timers. if the value ==0 skip it
    else decrement it. If it decrements to zero, activate the task associated with it */

  task=0;
  while (task < NUM_TASKS ) /* scan through all avail. tasks ??? */
  { if (task_timers[task])
    { task_timers[task]--;            /* dec the timer */
      if (task_timers[task] == 0 )
      { set_task(task); /* if ==0 activate the task bit */
      }
    } task++;
  }

  task = 0; /* !!! start at the most significant task !!! ( ??? a priority list ??? ) */
  while (task <= NUM_TASKS )  /* scan through all avail. tasks ??? ... again */
  { if ((task_bits & pgm_read_byte(&bit_mask[task])))
    { break; /* if activate task found.. ..dismiss the rest */
    } task++; /* else try the next one */
  }

// process the first active task found only

  switch(task) /* if task bit is active..execute the task */
  { case 0: task0(); break;
    case 1: task1(); break;
    case 2: task2(); break;
    case 3: task3(); break;
    case 4: task4(); break;
    case 5: task5(); break;
    case 6: task6(); break;
    case 7: task7(); break;
    default: break; /* no task was active!! */
  }                    
}

//
// enable a task for execution
//

void set_task(char tsk){ task_bits |= pgm_read_byte(&bit_mask[tsk]); }  /* sets a task bit */

//
// disable a task from executing
//

void reset_task(char tsk){ task_bits &= (~pgm_read_byte(&bit_mask[tsk])); }  /* resets a task bit */

void task0(void){ reset_task(0); }
void task1(void){ reset_task(1); }
void task2(void){ reset_task(2); }
void task3(void){ reset_task(3); }
void task4(void){ reset_task(4); }
void task5(void){ reset_task(5); }

//
//    flash PORTB.4 at 2hz
//

void task6(void)
{ PORTB ^= (1<<4);
  task_timers[6] = 25;        //every 250ms
  reset_task(6);
}

//
//    flash PORTB.5 at 1hz
//

void task7(void)
{ PORTB ^= (1<<5);
  task_timers[7] = 50;        //every 500ms
  reset_task(7);
}

//
// call this routine to initialize all peripherals
//

void init_devices(void)
{ //stop errant interrupts until set up
  cli(); //disable all interrupts

  DDRB = 0x30;    //port 4 & 5 as outputs

  timer0_init();

  MCUCR = 0x00;
  EICRA = 0x00; //extended ext ints
  EIMSK = 0x00;

  TIMSK0 = 0x02; //timer 0 interrupt sources

  PRR = 0x00; //power controller
  sei(); //re-enable interrupts
  //all peripherals are now initialized
}

// TIMER0 initialize - prescale:1024
// WGM: CTC
// desired value: 10mSec
// actual value: 10.048mSec (-0.5%)

void timer0_init(void)
{ TCCR0B = 0x00; //stop
  TCNT0 = 0x00; //set count
  TCCR0A = 0x02;    //CTC mode
  OCR0A = 0x9C;
  TCCR0B = 0x05; //start timer
}

ISR(TIMER0_COMPA_vect){ tick_flag = 1; } //TIMER0 has overflowed

//
// ENDS
// .
 

BobaMosfet

Joined Jul 1, 2009
1,208
Hi, I need help to understand below code. I have yet to understand that this code is designed for multitasking.

First of all I want to understand function task_dispatch(void) what it supposed to do?

C:
#include
#include
#include

void init_devices(void);
void timer0_init(void);
void reset_task(char tsk);
void set_task(char tsk);
void task_dispatch(void);
void task0(void);
void task1(void);
void task2(void);
void task3(void);
void task4(void);
void task5(void);
void task6(void);
void task7(void);

#define NUM_TASKS 8
char task_bits = 0;  /* lsb is hi priority task */
volatile char tick_flag = 0;    /* if non-zero, a tick has elapsed */
unsigned int task_timers[NUM_TASKS]={0,0,0,0,0,0,0,0};                  /* init the timers to 0 on startup */
static const PROGMEM char bit_mask[]={1,2,4,8,16,32,64,128};            /* value -> bit mask xlate table */

int main(void)
{

  init_devices();
//
//    start at least one task here
//
set_task(7);    //task7 runs
set_task(6);    //task6 runs

//      main loop

  while(1)
    {
    if (tick_flag)
      {
      tick_flag = 0;
      task_dispatch();              // well....
      }
    }
  return 0;
}
//
//    a task gets dispatched on every tick_flag tick (10ms)
//
void task_dispatch(void)
{
  /* scan the task bits for an active task and execute it */

  char task;


/* take care of the task timers. if the value ==0 skip it
    else decrement it. If it decrements to zero, activate the task associated with it */

  task=0;
  while (task < NUM_TASKS )
    {
    if (task_timers[task])
       {
         task_timers[task]--;            /* dec the timer */
       if (task_timers[task] == 0 )
               {
            set_task(task); /* if ==0 activate the task bit */
            }
       }
    task++;
    }

  task = 0; /* start at the most significant task */
  while (task <= NUM_TASKS )
    {
      if ((task_bits & pgm_read_byte(&bit_mask[task])))
              {
              break; /* if activate task found..*/
            }
      task++;         /* else try the next one */
    }
  switch(task)            /* if task bit is active..execute the task */
    {
    case 0:
      task0();
      break;
    case 1:
      task1();
      break;
    case 2:
      task2();
      break;
    case 3:
      task3();
      break;
    case 4:
      task4();
      break;
    case 5:
      task5();
      break;
    case 6:
      task6();
      break;
    case 7:
      task7();
      break;
    default:
      break;                  /* no task was active!! */
    }                   
}

// enable a task for execution
void set_task(char tsk)
{
  task_bits |= pgm_read_byte(&bit_mask[tsk]);       /* sets a task bit */
}
// disable a task from executing
void reset_task(char tsk)
{
  task_bits &= (~pgm_read_byte(&bit_mask[tsk]));  /* resets a task bit */
}

void task0(void)
{
  reset_task(0);
}
void task1(void)
{
  reset_task(1);
}
void task2(void)
{
  reset_task(2);
}
void task3(void)
{
  reset_task(3);
}
void task4(void)
{
  reset_task(4);
}
void task5(void)
{
  reset_task(5);
}
//
//    flash PORTB.4 at 2hz
//
void task6(void)
{
PORTB ^= (1<<4);
task_timers[6] = 25;        //every 250ms
reset_task(6);
}
//
//    flash PORTB.5 at 1hz
//
void task7(void)
{
PORTB ^= (1<<5);
task_timers[7] = 50;        //every 500ms
reset_task(7);
}
//call this routine to initialize all peripherals
void init_devices(void)
{
//stop errant interrupts until set up
cli(); //disable all interrupts


DDRB = 0x30;    //port 4 & 5 as outputs

timer0_init();

MCUCR = 0x00;
EICRA = 0x00; //extended ext ints
EIMSK = 0x00;

TIMSK0 = 0x02; //timer 0 interrupt sources

PRR = 0x00; //power controller
sei(); //re-enable interrupts
//all peripherals are now initialized
}
//TIMER0 initialize - prescale:1024
// WGM: CTC
// desired value: 10mSec
// actual value: 10.048mSec (-0.5%)
void timer0_init(void)
{
TCCR0B = 0x00; //stop
TCNT0 = 0x00; //set count
TCCR0A = 0x02;    //CTC mode
OCR0A = 0x9C;
TCCR0B = 0x05; //start timer
}

ISR(TIMER0_COMPA_vect)
{
//TIMER0 has overflowed
     tick_flag = 1;
}
Thanks in advance
Part of being a developer is figuring out code you see to learn from. Did you flow chart it? Did you read the data sheets for the MCU in question? How much effort did you put into figuring this out, or did you just throw up your hands and ask others?

I'm not picking on you, I'm asking a fundamental question that anybody interested in embedded development and electronics should be asking themselves- Do I *really* have what it takes or am I just wanting something I'm not actually skilled enough or have enough 'grit' to do?

The above questions your asking about make it appear as if you haven't figure out your 'approach' to problems. Many people never do. You need to come to term with your abilities, figure out how you like to learn and understand, and then when faced with a problem, manipulate it into the best way you can learn. To solve the above you need to (at a minimu, and in your own way):

understand the specific data sheet (and any example code they provide)
understand exact;y what prescaling really is- how it works, not just the concept of it
and then be able to understand how to set bits in registers, based on the MCU datasheet
And finally, pull all that together with flowcharts for each function and one for overall.

If you shy away from flow-charts... maybe this isn't a good 'fit' for you as an area of interest. A flow chart doesn't have to follow a specific protocol- just scribble some kind of diagram down, on paper, that *you* can understand and follow to develop a picture of the algorithms. Doing so will help you think about things in a more complete way. Learning this skill will help you throughout any career in developing.
 

Papabravo

Joined Feb 24, 2006
14,412
Since you don't seem to be able to answer a simple question, let us try a more Socratic approach.
  1. Is task_bits a state variable?
  2. Is tick_flag a state variable?
  3. Is the eight member array task_timers a set of state variables?
Can you do simple questions with yes/no answers?
Once you know what the state variables are you can proceed to deeper questions like:
  1. How does a task suspend itself?
  2. How does a task get executed?
  3. Can a task be preempted, or it is allowed to run to "completion"?
  4. How can a task make itself an "idle" task that is always "Ready To Run"?
  5. How can a task suspend itself for some fixed amount of time to give other tasks a chance to run?
  6. Is it possible for there to be a deadlock situation where there are no tasks that are "Ready to Run?
  7. What determines a task's priority?
  8. Can a task change it's priority?
These are only some of the questions you need to answer for your self. Give it a try.
 

Thread Starter

rt694157

Joined Dec 15, 2019
57
What YOU need to do is examine the code to find out what the state variables are and how they factor into the decision making process.
I searched google to find out what the state variable is

A state variable is one of the set of variables that are used to describe the mathematical "state" of a dynamical system.

I understand that we are checking state through the switch case statement that in the code.
 

bug13

Joined Feb 13, 2012
1,864
This one that I wrote maybe of interest to you, I think it's a little tidier.
https://github.com/jmswu/schedular

C:
// magical function to get system tick
// usually from a timer
uint32_t get_system_tick();

// task 1
uint8_t task_one(){
    // do stuff
}

// task 2
uint8_t task_two(){
    // do stuff
}

void main(){
    
    // setup schedular
    schedular_init(get_system_tick);
    
    schedular_add(task_one, 10);    // run task every 10 ticks
    schedular_add(task_two, 20);    // run task every 20 ticks
    
    while(1){
        schedular_run();
    }
    
}
bit tidier:
 

ci139

Joined Jul 11, 2016
1,696
long ago i used a similar structure to the "intensity vector" and "tasks flag" below
C-like:
unsigned int task_timers[NUM_TASKS] = {0,0,0,0,0,0,0,0}; /* init the timers to 0 on startup */
static const PROGMEM char bit_mask[] = {1,2,4,8,16,32,64,128}; /* value -> bit mask xlate table */
in principle but not exactly

so that the "task flag" listed the "must service ASAP during one call" routines near MSB or LSB (don't remember)
and then there were "interruptible" interrupts "which may be serviced when the sys is more idle" at the other side of the vector . . .
( the execution rules gradually changed from ASAP to "LOOSE" defined more exactly elsewhere . . .
. . . was a GAME/PAINTER/EXCEL like Graphical tool that needed to be fine tuned to best actual combined to visual performance by those interrupt timing parameters . . .)
 
Last edited:

Papabravo

Joined Feb 24, 2006
14,412
I searched google to find out what the state variable is

A state variable is one of the set of variables that are used to describe the mathematical "state" of a dynamical system.

I understand that we are checking state through the switch case statement that in the code.
That is a general answer, but what I want to know is can you identify the variables by name in the offered code fragment and explain how they are used to make the decision about what to do and when?
 
Last edited:

vanderghast

Joined Jun 14, 2018
40
A little bit late, I am sorry, but if you are still interested...

It looks like some code for a microcontroller, probably an ARM one, but I don't recognize which one. Some names refer (seem to refer) to registers probably defined in one of the #include, such as PORTB. PORTB would be a port of 16 bits, but you can only reach all the 16 bits at a time ( or 8 bits at a time, using PORT4 or PORT3, on ARM) so the binary "or" is used to alternate one of the bit. You would need an expansion to C to be able to reach each bits individually, or the proper assembler, or bit banding. Note that ^= becomes 3 instructions in machine code, so it can be interrupted at two places by an external process, which could be "unsafe" in some critical situation, but blinking a LED is not such a critical operation, so ^= is probably justified, for shake of simplicity (the declaration of bit banding would be 3 times faster, but who cares, and the declarations for bit banding would be more obscure and machine dependent).

As for the dispatch function, seems to be built like a prehistoric NVIC (Nested Vectored Interrupt Controller ) where functions can be in a QUEUE ordered by some predefined (fixed) priority. Each function can ask to be executed (or served) by raising its flag and the prehistoric-NVIC executes (like a dispatch of the CPU resource) the one having the most priority having its flag arisen. It is probably some code intended to introduce later on the NVIC which does that, and more, much more efficiently (since it works in hardware, independently of the CPU, among other considerations).
 

Papabravo

Joined Feb 24, 2006
14,412
A little bit late, I am sorry, but if you are still interested...

It looks like some code for a microcontroller, probably an ARM one, but I don't recognize which one. Some names refer (seem to refer) to registers probably defined in one of the #include, such as PORTB. PORTB would be a port of 16 bits, but you can only reach all the 16 bits at a time ( or 8 bits at a time, using PORT4 or PORT3, on ARM) so the binary "or" is used to alternate one of the bit. You would need an expansion to C to be able to reach each bits individually, or the proper assembler, or bit banding. Note that ^= becomes 3 instructions in machine code, so it can be interrupted at two places by an external process, which could be "unsafe" in some critical situation, but blinking a LED is not such a critical operation, so ^= is probably justified, for shake of simplicity (the declaration of bit banding would be 3 times faster, but who cares, and the declarations for bit banding would be more obscure and machine dependent).

As for the dispatch function, seems to be built like a prehistoric NVIC (Nested Vectored Interrupt Controller ) where functions can be in a QUEUE ordered by some predefined (fixed) priority. Each function can ask to be executed (or served) by raising its flag and the prehistoric-NVIC executes (like a dispatch of the CPU resource) the one having the most priority having its flag arisen. It is probably some code intended to introduce later on the NVIC which does that, and more, much more efficiently (since it works in hardware, independently of the CPU, among other considerations).
It is not code for an ARM processor. It is for one of the members of the Atmel ATmega series. The ATmega328P is used in some versions of the Arduino.
 
Top