Running Tasks with Conflicting Scheduled Times

Hi All,

I am trying to run two tasks of similar priorities, but with different scheduled periods.
I call them as such before starting the scheduler:

cmdSuccess = xTaskCreate(cmd_processing, "cp", 256, NULL, configMAX_PRIORITIES-1, &Handle_cmdProcTask);

 blinkSuccess = xTaskCreate(blink, "blink", 256, NULL, configMAX_PRIORITIES-1, &Handle_BlinkProcTask);

The first task reads in serial data input from the USB every 10 ms. The second makes an led toggle every 1 second.

They each have the following time execution periods,
blink_period = 1000 ms or 1 second
cmd_proc_period = 10 ms

It’s obvious that at 1000 ms both blink_period and cmd_proc_period are scheduled.

When I run the scheduler I see the LED flicker faintly at times at other times the LED is stuck on off or stuck on on. What can be the cause of this. Am I scheduling tasks wrong? I know that I can use vTaskDelay, but I want to schedule based xTaskGetTickCount(). In that way I know that tasks are running on a timeline and are scheduled simultaneously.

Each one then runs based on the following two task functions:

void blink(void* pvParameters){
 
   uint8_t state = 0;
   while(1){
      xSemaphoreTake(globSem, portMAX_DELAY);
      unsigned long time = xTaskGetTickCount()/portTICK_RATE_MS;
      xSemaphoreGive(globSem);

      if((time % blink_period) == 0){
         state ^= 1;

         digitalWrite(led, state);
        
      }
      taskYIELD();
   }
}

void cmd_processing(void* pvParameters){
   while(1){
      xSemaphoreTake(globSem, portMAX_DELAY);
      unsigned long time = xTaskGetTickCount()/portTICK_RATE_MS;
      xSemaphoreGive(globSem);
      if((time % cmd_proc_period) == 0){
 
         static uint32_t cnt = 0;
         static char param[50];
         static char cmd[50];
         static char c;
         static boolean cmdSet = false;

         if (SERIAL.available () > 0) {

            c = SERIAL.read ();

            if (c == '\n') {
               arg[cnt] = '\0';
               cnt = 0;
               cmdSet = false;
               
               detrmine_input((char*)cmd, (void*)arg);
            }
            else if (c == ' ') {
               cmd[cnt] = '\0';
               cnt = 0;
               cmdSet = true;
               
            }
            else {
               if (!cmdSet) {
                  cmd[cnt++] = c;
                  cmd[cnt] = '\0';
               }
               else {
                  arg[cnt++] = c;
                  arg[cnt] = '\0';
               }

            }

         }
        
      }
      taskYIELD();
   }
}

Where is “time” declared? You are protecting it with a mutex when you set it like it’s shared between the tasks, but then use it unprotected when if it was shared it could be changed by the other task. Trying to read code on my cell phone so maybe I got that wrong.

The structure of you code is very inefficient as both tasks spend most of their time competing with each other for cpu rather than blocking until exactly the time they actually have something to do - it would be more accurate to block on vTaskDelayUntil().

One big thing to note is your tasks aren’t running once every x milliseconds, but are CONTINUOUSLY running and only trying to do ‘active’ work 1 tick out of.a certain number.

You REALLY want tasks to use BLOCKING calls to make them wait until they are needed, things like vTaskDelay and vTaskDelayUntil to make them really just run at a specified rate.

@rtel and @richard-damon Thank you both for your reply! I will use blocking calls which makes sense when you are sharing a single CPU core. However, am I guaranteed that each task will run at the specified period, or does that depend on how long the task takes? Is there a way to measure the time a task takes to complete?

A task will execute immediately that it unblocks if it is the highest priority ready state task at that time - otherwise it will be delayed by higher priority tasks.

There are various ways of measuring a task’s execution time, including run-time stats, using trace macros (see traceTASK_SWITCHED_IN and/or traceTASK_SWITCHED_OUT), or third party trace tools such as Tracealyzer or Segger SystemView.