1

I got 2 tasks in MicroC to simulate a moving vehicle: ControlTask and VehicleTask. Now my project should replace the context switch with a timer for more appropriate timing but I can't seem to get it done. The program now uses the statement OSTimeDlyHMSM to implement periods but instead soft timers should be used with semaphores. OSTmrCreate in C/OS-II ReferenceManual (Chapter 16). I can start a timer then I can place it in the start code but I'm failing to call the timer and sync properly between the two tasks replacing the OSTimeDlyHMSM with a timer. I think my solution is getting more complicated than neccesary because I might not understand all details e.g. why I need semaphores and why it's more exact with a timer than the builtin OSTimeDlyHMSM. My complete effort this far looks like the following:

#include <stdio.h>
#include "system.h"
#include "includes.h"
#include "altera_avalon_pio_regs.h"
#include "sys/alt_irq.h"
#include "sys/alt_alarm.h"

#define DEBUG 1

#define HW_TIMER_PERIOD 100 /* 100ms */

/* Button Patterns */

#define GAS_PEDAL_FLAG      0x08
#define BRAKE_PEDAL_FLAG    0x04
#define CRUISE_CONTROL_FLAG 0x02
/* Switch Patterns */

#define TOP_GEAR_FLAG       0x00000002
#define ENGINE_FLAG         0x00000001

/* LED Patterns */

#define LED_RED_0 0x00000001 // Engine
#define LED_RED_1 0x00000002 // Top Gear

#define LED_GREEN_0 0x0001 // Cruise Control activated
#define LED_GREEN_2 0x0002 // Cruise Control Button
#define LED_GREEN_4 0x0010 // Brake Pedal
#define LED_GREEN_6 0x0040 // Gas Pedal

/*
 * Definition of Tasks
 */

#define TASK_STACKSIZE 2048

OS_STK StartTask_Stack[TASK_STACKSIZE]; 
OS_STK ControlTask_Stack[TASK_STACKSIZE]; 
OS_STK VehicleTask_Stack[TASK_STACKSIZE];

// Task Priorities

#define STARTTASK_PRIO     5
#define VEHICLETASK_PRIO  10
#define CONTROLTASK_PRIO  12

// Task Periods

#define CONTROL_PERIOD  300
#define VEHICLE_PERIOD  300

/*
 * Definition of Kernel Objects 
 */

// Mailboxes
OS_EVENT *Mbox_Throttle;
OS_EVENT *Mbox_Velocity;


// Semaphores
OS_EVENT *aSemaphore;
// SW-Timer
OS_TMR *SWTimer;
OS_TMR *SWTimer1;
BOOLEAN status;
/*
 * Types
 */
enum active {on, off};

enum active gas_pedal = off;
enum active brake_pedal = off;
enum active top_gear = off;
enum active engine = off;
enum active cruise_control = off; 

/*
 * Global variables
 */
int delay; // Delay of HW-timer 
INT16U led_green = 0; // Green LEDs
INT32U led_red = 0;   // Red LEDs

int sharedMemory=1;

void ContextSwitch()
{  
    printf("ContextSwitch!\n"); 
    sharedMemory=sharedMemory*-1;
}
int buttons_pressed(void)
{
  return ~IORD_ALTERA_AVALON_PIO_DATA(DE2_PIO_KEYS4_BASE);    
}

int switches_pressed(void)
{
  return IORD_ALTERA_AVALON_PIO_DATA(DE2_PIO_TOGGLES18_BASE);    
}

/*
 * ISR for HW Timer
 */
alt_u32 alarm_handler(void* context)
{
  OSTmrSignal(); /* Signals a 'tick' to the SW timers */

  return delay;
}

static int b2sLUT[] = {0x40, //0
                 0x79, //1
                 0x24, //2
                 0x30, //3
                 0x19, //4
                 0x12, //5
                 0x02, //6
                 0x78, //7
                 0x00, //8
                 0x18, //9
                 0x3F, //-
};

/*
 * convert int to seven segment display format
 */
int int2seven(int inval){
    return b2sLUT[inval];
}

/*
 * output current velocity on the seven segement display
 */
void show_velocity_on_sevenseg(INT8S velocity){
  int tmp = velocity;
  int out;
  INT8U out_high = 0;
  INT8U out_low = 0;
  INT8U out_sign = 0;

  if(velocity < 0){
     out_sign = int2seven(10);
     tmp *= -1;
  }else{
     out_sign = int2seven(0);
  }

  out_high = int2seven(tmp / 10);
  out_low = int2seven(tmp - (tmp/10) * 10);

  out = int2seven(0) << 21 |
            out_sign << 14 |
            out_high << 7  |
            out_low;
  IOWR_ALTERA_AVALON_PIO_DATA(DE2_PIO_HEX_LOW28_BASE,out);
}

/*
 * shows the target velocity on the seven segment display (HEX5, HEX4)
 * when the cruise control is activated (0 otherwise)
 */
void show_target_velocity(INT8U target_vel)
{
}

/*
 * indicates the position of the vehicle on the track with the four leftmost red LEDs
 * LEDR17: [0m, 400m)
 * LEDR16: [400m, 800m)
 * LEDR15: [800m, 1200m)
 * LEDR14: [1200m, 1600m)
 * LEDR13: [1600m, 2000m)
 * LEDR12: [2000m, 2400m]
 */
void show_position(INT16U position)
{
}

/*
 * The function 'adjust_position()' adjusts the position depending on the
 * acceleration and velocity.
 */
 INT16U adjust_position(INT16U position, INT16S velocity,
                        INT8S acceleration, INT16U time_interval)
{
  INT16S new_position = position + velocity * time_interval / 1000
    + acceleration / 2  * (time_interval / 1000) * (time_interval / 1000);

  if (new_position > 24000) {
    new_position -= 24000;
  } else if (new_position < 0){
    new_position += 24000;
  }

  show_position(new_position);
  return new_position;
}

/*
 * The function 'adjust_velocity()' adjusts the velocity depending on the
 * acceleration.
 */
INT16S adjust_velocity(INT16S velocity, INT8S acceleration,  
               enum active brake_pedal, INT16U time_interval)
{
  INT16S new_velocity;
  INT8U brake_retardation = 200;

  if (brake_pedal == off)
    new_velocity = velocity  + (float) (acceleration * time_interval) / 1000.0;
  else {
    if (brake_retardation * time_interval / 1000 > velocity)
      new_velocity = 0;
    else
      new_velocity = velocity - brake_retardation * time_interval / 1000;
  }

  return new_velocity;
}

/*
 * The task 'VehicleTask' updates the current velocity of the vehicle
 */
void VehicleTask(void* pdata)
{ 
  INT8U err;  
  void* msg;
  INT8U* throttle; 
  INT8S acceleration;  /* Value between 40 and -20 (4.0 m/s^2 and -2.0 m/s^2) */
  INT8S retardation;   /* Value between 20 and -10 (2.0 m/s^2 and -1.0 m/s^2) */
  INT16U position = 0; /* Value between 0 and 20000 (0.0 m and 2000.0 m)  */
  INT16S velocity = 0; /* Value between -200 and 700 (-20.0 m/s amd 70.0 m/s) */
  INT16S wind_factor;   /* Value between -10 and 20 (2.0 m/s^2 and -1.0 m/s^2) */

  printf("Vehicle task created!\n");

  while(1)
    {
      err = OSMboxPost(Mbox_Velocity, (void *) &velocity);

      OSTimeDlyHMSM(0,0,0,VEHICLE_PERIOD); 

      /* Non-blocking read of mailbox: 
       - message in mailbox: update throttle
       - no message:         use old throttle
      */
      msg = OSMboxPend(Mbox_Throttle, 1, &err); 
      if (err == OS_NO_ERR) 
         throttle = (INT8U*) msg;

      /* Retardation : Factor of Terrain and Wind Resistance */
      if (velocity > 0)
         wind_factor = velocity * velocity / 10000 + 1;
      else 
         wind_factor = (-1) * velocity * velocity / 10000 + 1;

      if (position < 4000) 
         retardation = wind_factor; // even ground
      else if (position < 8000)
          retardation = wind_factor + 15; // traveling uphill
        else if (position < 12000)
            retardation = wind_factor + 25; // traveling steep uphill
          else if (position < 16000)
              retardation = wind_factor; // even ground
            else if (position < 20000)
                retardation = wind_factor - 10; //traveling downhill
              else
                  retardation = wind_factor - 5 ; // traveling steep downhill

      acceleration = *throttle / 2 - retardation;     
      position = adjust_position(position, velocity, acceleration, 300); 
      velocity = adjust_velocity(velocity, acceleration, brake_pedal, 300); 
      printf("Position: %dm\n", position / 10);
      printf("Velocity: %4.1fm/s\n", velocity /10.0);
      printf("Throttle: %dV\n", *throttle / 10);
      show_velocity_on_sevenseg((INT8S) (velocity / 10));
    }
} 

/*
 * The task 'ControlTask' is the main task of the application. It reacts
 * on sensors and generates responses.
 */

void ControlTask(void* pdata)
{
  INT8U err;
  INT8U throttle = 40; /* Value between 0 and 80, which is interpreted as between 0.0V and 8.0V */
  void* msg;
  INT16S* current_velocity;

  printf("Control Task created!\n");

  while(1)
    {
      msg = OSMboxPend(Mbox_Velocity, 0, &err);
      current_velocity = (INT16S*) msg;

      err = OSMboxPost(Mbox_Throttle, (void *) &throttle);

      OSTimeDlyHMSM(0,0,0, CONTROL_PERIOD);
    }
}

/* 
 * The task 'StartTask' creates all other tasks kernel objects and
 * deletes itself afterwards.
 */ 

void StartTask(void* pdata)
{
  INT8U err;
  void* context;

  static alt_alarm alarm;     /* Is needed for timer ISR function */

  /* Base resolution for SW timer : HW_TIMER_PERIOD ms */
  delay = alt_ticks_per_second() * HW_TIMER_PERIOD / 1000; 
  printf("delay in ticks %d\n", delay);

  /* 
   * Create Hardware Timer with a period of 'delay' 
   */
  if (alt_alarm_start (&alarm,
      delay,
      alarm_handler,
      context) < 0)
      {
          printf("No system clock available!n");
      }

  /* 
   * Create and start Software Timer 
   */

  SWTimer = OSTmrCreate(0,
  CONTROL_PERIOD/(4*OS_TMR_CFG_TICKS_PER_SEC),
  OS_TMR_OPT_PERIODIC,
  ContextSwitch,
  NULL,
  NULL,
  &err);
     if (err == OS_ERR_NONE) {
  /* Timer was created but NOT started */
  printf("SWTimer was created but NOT started \n");
  }

  status = OSTmrStart(SWTimer,
    &err);
  if (err == OS_ERR_NONE) {
  /* Timer was started */
    printf("SWTimer was started!\n");
  }
  /*
   * Creation of Kernel Objects
   */

  // Mailboxes
  Mbox_Throttle = OSMboxCreate((void*) 0); /* Empty Mailbox - Throttle */
  Mbox_Velocity = OSMboxCreate((void*) 0); /* Empty Mailbox - Velocity */

  /*
   * Create statistics task
   */

  OSStatInit();

  /* 
   * Creating Tasks in the system 
   */


  err = OSTaskCreateExt(
       ControlTask, // Pointer to task code
       NULL,        // Pointer to argument that is
                    // passed to task
       &ControlTask_Stack[TASK_STACKSIZE-1], // Pointer to top
                             // of task stack
       CONTROLTASK_PRIO,
       CONTROLTASK_PRIO,
       (void *)&ControlTask_Stack[0],
       TASK_STACKSIZE,
       (void *) 0,
       OS_TASK_OPT_STK_CHK);

  err = OSTaskCreateExt(
       VehicleTask, // Pointer to task code
       NULL,        // Pointer to argument that is
                    // passed to task
       &VehicleTask_Stack[TASK_STACKSIZE-1], // Pointer to top
                             // of task stack
       VEHICLETASK_PRIO,
       VEHICLETASK_PRIO,
       (void *)&VehicleTask_Stack[0],
       TASK_STACKSIZE,
       (void *) 0,
       OS_TASK_OPT_STK_CHK);

  printf("All Tasks and Kernel Objects generated!\n");

  /* Task deletes itself */

  OSTaskDel(OS_PRIO_SELF);
}

/*
 *
 * The function 'main' creates only a single task 'StartTask' and starts
 * the OS. All other tasks are started from the task 'StartTask'.
 *
 */

int main(void) {

  printf("Cruise Control\n");
  aSemaphore = OSSemCreate(1); // binary semaphore (1 key)  

  OSTaskCreateExt(
     StartTask, // Pointer to task code
         NULL,      // Pointer to argument that is
                    // passed to task
         (void *)&StartTask_Stack[TASK_STACKSIZE-1], // Pointer to top
                             // of task stack 
         STARTTASK_PRIO,
         STARTTASK_PRIO,
         (void *)&StartTask_Stack[0],
         TASK_STACKSIZE,
         (void *) 0,  
         OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

  OSStart();

  return 0;
}

Running the above program the callback contextswitch is executed but it doesn't yet solve the problem of using timers instead of the builtin yield and how to apply it with semaphores.

Cruise Control
delay in ticks 100
SWTimer was created but NOT started 
SWTimer was started!
All Tasks and Kernel Objects generated!
Vehicle task created!
Control Task created!
ContextSwitch!
Position: 0m
Velocity:  0.5m/s
Throttle: 4V
ContextSwitch!
Position: 0m
Velocity:  1.0m/s
Throttle: 4V
Position: 0m
Velocity:  1.5m/s
Throttle: 4V
ContextSwitch!
Position: 0m
Velocity:  2.0m/s
Throttle: 4V
ContextSwitch!
Position: 1m
Velocity:  2.5m/s
Throttle: 4V
ContextSwitch!
Position: 2m
Velocity:  3.0m/s
Throttle: 4V
ContextSwitch!

Update 141001 15:57 CET

2 semaphores + 2 timers seem like a good improvement. I hope it can be checked or tested...

#include <stdio.h>
#include "system.h"
#include "includes.h"
#include "altera_avalon_pio_regs.h"
#include "sys/alt_irq.h"
#include "sys/alt_alarm.h"

#define DEBUG 1

#define HW_TIMER_PERIOD 100 /* 100ms */

/* Button Patterns */

#define GAS_PEDAL_FLAG      0x08
#define BRAKE_PEDAL_FLAG    0x04
#define CRUISE_CONTROL_FLAG 0x02
/* Switch Patterns */

#define TOP_GEAR_FLAG       0x00000002
#define ENGINE_FLAG         0x00000001

/* LED Patterns */

#define LED_RED_0 0x00000001 // Engine
#define LED_RED_1 0x00000002 // Top Gear

#define LED_GREEN_0 0x0001 // Cruise Control activated
#define LED_GREEN_2 0x0002 // Cruise Control Button
#define LED_GREEN_4 0x0010 // Brake Pedal
#define LED_GREEN_6 0x0040 // Gas Pedal

/*
 * Definition of Tasks
 */

#define TASK_STACKSIZE 2048

OS_STK StartTask_Stack[TASK_STACKSIZE]; 
OS_STK ControlTask_Stack[TASK_STACKSIZE]; 
OS_STK VehicleTask_Stack[TASK_STACKSIZE];

// Task Priorities

#define STARTTASK_PRIO     5
#define VEHICLETASK_PRIO  10
#define CONTROLTASK_PRIO  12

// Task Periods

#define CONTROL_PERIOD  300
#define VEHICLE_PERIOD  300

/*
 * Definition of Kernel Objects 
 */

// Mailboxes
OS_EVENT *Mbox_Throttle;
OS_EVENT *Mbox_Velocity;


// Semaphores
OS_EVENT *aSemaphore;
OS_EVENT *aSemaphore2;
// SW-Timer
OS_TMR *SWTimer;
OS_TMR *SWTimer1;
BOOLEAN status;
/*
 * Types
 */
enum active {on, off};

enum active gas_pedal = off;
enum active brake_pedal = off;
enum active top_gear = off;
enum active engine = off;
enum active cruise_control = off; 

/*
 * Global variables
 */
int delay; // Delay of HW-timer 
INT16U led_green = 0; // Green LEDs
INT32U led_red = 0;   // Red LEDs

int sharedMemory=1;
void TimerCallback(params)
{
    // Post to the semaphore to signal that it's time to run the task.
    OSSemPost(aSemaphore); // Releasing the key
}
void ContextSwitch()
{  
    printf("ContextSwitch!\n"); 
    sharedMemory=sharedMemory*-1;
}
int buttons_pressed(void)
{
  return ~IORD_ALTERA_AVALON_PIO_DATA(DE2_PIO_KEYS4_BASE);    
}

int switches_pressed(void)
{
  return IORD_ALTERA_AVALON_PIO_DATA(DE2_PIO_TOGGLES18_BASE);    
}

/*
 * ISR for HW Timer
 */
alt_u32 alarm_handler(void* context)
{
  OSTmrSignal(); /* Signals a 'tick' to the SW timers */

  return delay;
}

void release()
{
  printf("release key!\n");
  //OSSemPost(aSemaphore); // Releasing the key
  OSSemPost(aSemaphore2); // Releasing the key
  printf("released key!\n");
}
void release2()
{
  printf("release2!\n");
  OSSemPost(aSemaphore2); // Releasing the key
  printf("release2!\n");
}

static int b2sLUT[] = {0x40, //0
                 0x79, //1
                 0x24, //2
                 0x30, //3
                 0x19, //4
                 0x12, //5
                 0x02, //6
                 0x78, //7
                 0x00, //8
                 0x18, //9
                 0x3F, //-
};

/*
 * convert int to seven segment display format
 */
int int2seven(int inval){
    return b2sLUT[inval];
}

/*
 * output current velocity on the seven segement display
 */
void show_velocity_on_sevenseg(INT8S velocity){
  int tmp = velocity;
  int out;
  INT8U out_high = 0;
  INT8U out_low = 0;
  INT8U out_sign = 0;

  if(velocity < 0){
     out_sign = int2seven(10);
     tmp *= -1;
  }else{
     out_sign = int2seven(0);
  }

  out_high = int2seven(tmp / 10);
  out_low = int2seven(tmp - (tmp/10) * 10);

  out = int2seven(0) << 21 |
            out_sign << 14 |
            out_high << 7  |
            out_low;
  IOWR_ALTERA_AVALON_PIO_DATA(DE2_PIO_HEX_LOW28_BASE,out);
}

/*
 * shows the target velocity on the seven segment display (HEX5, HEX4)
 * when the cruise control is activated (0 otherwise)
 */
void show_target_velocity(INT8U target_vel)
{
}

/*
 * indicates the position of the vehicle on the track with the four leftmost red LEDs
 * LEDR17: [0m, 400m)
 * LEDR16: [400m, 800m)
 * LEDR15: [800m, 1200m)
 * LEDR14: [1200m, 1600m)
 * LEDR13: [1600m, 2000m)
 * LEDR12: [2000m, 2400m]
 */
void show_position(INT16U position)
{
}

/*
 * The function 'adjust_position()' adjusts the position depending on the
 * acceleration and velocity.
 */
 INT16U adjust_position(INT16U position, INT16S velocity,
                        INT8S acceleration, INT16U time_interval)
{
  INT16S new_position = position + velocity * time_interval / 1000
    + acceleration / 2  * (time_interval / 1000) * (time_interval / 1000);

  if (new_position > 24000) {
    new_position -= 24000;
  } else if (new_position < 0){
    new_position += 24000;
  }

  show_position(new_position);
  return new_position;
}

/*
 * The function 'adjust_velocity()' adjusts the velocity depending on the
 * acceleration.
 */
INT16S adjust_velocity(INT16S velocity, INT8S acceleration,  
               enum active brake_pedal, INT16U time_interval)
{
  INT16S new_velocity;
  INT8U brake_retardation = 200;

  if (brake_pedal == off)
    new_velocity = velocity  + (float) (acceleration * time_interval) / 1000.0;
  else {
    if (brake_retardation * time_interval / 1000 > velocity)
      new_velocity = 0;
    else
      new_velocity = velocity - brake_retardation * time_interval / 1000;
  }

  return new_velocity;
}

/*
 * The task 'VehicleTask' updates the current velocity of the vehicle
 */
void VehicleTask(void* pdata)
{ 
  INT8U err;  
  void* msg;
  INT8U* throttle; 
  INT8S acceleration;  /* Value between 40 and -20 (4.0 m/s^2 and -2.0 m/s^2) */
  INT8S retardation;   /* Value between 20 and -10 (2.0 m/s^2 and -1.0 m/s^2) */
  INT16U position = 0; /* Value between 0 and 20000 (0.0 m and 2000.0 m)  */
  INT16S velocity = 0; /* Value between -200 and 700 (-20.0 m/s amd 70.0 m/s) */
  INT16S wind_factor;   /* Value between -10 and 20 (2.0 m/s^2 and -1.0 m/s^2) */

  printf("Vehicle task created!\n");
// Create a semaphore to represent the "it's time to run" event.
    // Initialize the semaphore count to zero because it's not time
    // to run yet.

    // Create a periodic software timer which calls TimerCallback()
    // when it expires.

    /* 
   * Create and start Software Timer 
   */

  SWTimer1 = OSTmrCreate(0,
  CONTROL_PERIOD/(4*OS_TMR_CFG_TICKS_PER_SEC),
  OS_TMR_OPT_PERIODIC,
  TimerCallback,
  NULL,
  NULL,
  &err);
     if (err == OS_ERR_NONE) {
  /* Timer was created but NOT started */
  printf("SWTimer1 was created but NOT started \n");
  }

  status = OSTmrStart(SWTimer1,
    &err);
  if (err == OS_ERR_NONE) {
  /* Timer was started */
    printf("SWTimer1 was started!\n");
  }

  while(1)
    {
      OSSemPend(aSemaphore, 0, &err); // Trying to access the key   
      err = OSMboxPost(Mbox_Velocity, (void *) &velocity);

      //OSTimeDlyHMSM(0,0,0,VEHICLE_PERIOD); 

      /* Non-blocking read of mailbox: 
       - message in mailbox: update throttle
       - no message:         use old throttle
      */
      msg = OSMboxPend(Mbox_Throttle, 1, &err); 
      if (err == OS_NO_ERR) 
         throttle = (INT8U*) msg;

      /* Retardation : Factor of Terrain and Wind Resistance */
      if (velocity > 0)
         wind_factor = velocity * velocity / 10000 + 1;
      else 
         wind_factor = (-1) * velocity * velocity / 10000 + 1;

      if (position < 4000) 
         retardation = wind_factor; // even ground
      else if (position < 8000)
          retardation = wind_factor + 15; // traveling uphill
        else if (position < 12000)
            retardation = wind_factor + 25; // traveling steep uphill
          else if (position < 16000)
              retardation = wind_factor; // even ground
            else if (position < 20000)
                retardation = wind_factor - 10; //traveling downhill
              else
                  retardation = wind_factor - 5 ; // traveling steep downhill

      acceleration = *throttle / 2 - retardation;     
      position = adjust_position(position, velocity, acceleration, 300); 
      velocity = adjust_velocity(velocity, acceleration, brake_pedal, 300); 
      printf("Position: %dm\n", position / 10);
      printf("Velocity: %4.1fm/s\n", velocity /10.0);
      printf("Throttle: %dV\n", *throttle / 10);
      show_velocity_on_sevenseg((INT8S) (velocity / 10));
      //OSSemPost(aSemaphore); // Releasing the key

    }
} 

/*
 * The task 'ControlTask' is the main task of the application. It reacts
 * on sensors and generates responses.
 */

void ControlTask(void* pdata)
{
  INT8U err;
  INT8U throttle = 40; /* Value between 0 and 80, which is interpreted as between 0.0V and 8.0V */
  void* msg;
  INT16S* current_velocity;

  printf("Control Task created!\n");

  while(1)
    {
      OSSemPend(aSemaphore2, 0, &err); // Trying to access the key   
      msg = OSMboxPend(Mbox_Velocity, 0, &err);
      current_velocity = (INT16S*) msg;
      printf("Control Task!\n");
      err = OSMboxPost(Mbox_Throttle, (void *) &throttle);
      //OSSemPost(aSemaphore2); // Releasing the key
      //OSTimeDlyHMSM(0,0,0, CONTROL_PERIOD);
    }
}

/* 
 * The task 'StartTask' creates all other tasks kernel objects and
 * deletes itself afterwards.
 */ 

void StartTask(void* pdata)
{
  INT8U err;
  void* context;

  static alt_alarm alarm;     /* Is needed for timer ISR function */

  /* Base resolution for SW timer : HW_TIMER_PERIOD ms */
  delay = alt_ticks_per_second() * HW_TIMER_PERIOD / 1000; 
  printf("delay in ticks %d\n", delay);

  /* 
   * Create Hardware Timer with a period of 'delay' 
   */
  if (alt_alarm_start (&alarm,
      delay,
      alarm_handler,
      context) < 0)
      {
          printf("No system clock available!n");
      }

  /* 
   * Create and start Software Timer 
   */

  SWTimer = OSTmrCreate(0,
  CONTROL_PERIOD/(4*OS_TMR_CFG_TICKS_PER_SEC),
  OS_TMR_OPT_PERIODIC,
  release,
  NULL,
  NULL,
  &err);
     if (err == OS_ERR_NONE) {
  /* Timer was created but NOT started */
  printf("SWTimer was created but NOT started \n");
  }

  status = OSTmrStart(SWTimer,
    &err);
  if (err == OS_ERR_NONE) {
  /* Timer was started */
    printf("SWTimer was started!\n");
  }
  /*
   * Creation of Kernel Objects
   */

  // Mailboxes
  Mbox_Throttle = OSMboxCreate((void*) 0); /* Empty Mailbox - Throttle */
  Mbox_Velocity = OSMboxCreate((void*) 0); /* Empty Mailbox - Velocity */

  /*
   * Create statistics task
   */

  OSStatInit();

  /* 
   * Creating Tasks in the system 
   */


  err = OSTaskCreateExt(
       ControlTask, // Pointer to task code
       NULL,        // Pointer to argument that is
                    // passed to task
       &ControlTask_Stack[TASK_STACKSIZE-1], // Pointer to top
                             // of task stack
       CONTROLTASK_PRIO,
       CONTROLTASK_PRIO,
       (void *)&ControlTask_Stack[0],
       TASK_STACKSIZE,
       (void *) 0,
       OS_TASK_OPT_STK_CHK);

  err = OSTaskCreateExt(
       VehicleTask, // Pointer to task code
       NULL,        // Pointer to argument that is
                    // passed to task
       &VehicleTask_Stack[TASK_STACKSIZE-1], // Pointer to top
                             // of task stack
       VEHICLETASK_PRIO,
       VEHICLETASK_PRIO,
       (void *)&VehicleTask_Stack[0],
       TASK_STACKSIZE,
       (void *) 0,
       OS_TASK_OPT_STK_CHK);

  printf("All Tasks and Kernel Objects generated!\n");

  /* Task deletes itself */

  OSTaskDel(OS_PRIO_SELF);
}

/*
 *
 * The function 'main' creates only a single task 'StartTask' and starts
 * the OS. All other tasks are started from the task 'StartTask'.
 *
 */

int main(void) {

  printf("Cruise Control 2014\n");
  aSemaphore = OSSemCreate(1); // binary semaphore (1 key)  
  aSemaphore2 = OSSemCreate(0); // binary semaphore (1 key)    
  OSTaskCreateExt(
     StartTask, // Pointer to task code
         NULL,      // Pointer to argument that is
                    // passed to task
         (void *)&StartTask_Stack[TASK_STACKSIZE-1], // Pointer to top
                             // of task stack 
         STARTTASK_PRIO,
         STARTTASK_PRIO,
         (void *)&StartTask_Stack[0],
         TASK_STACKSIZE,
         (void *) 0,  
         OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

  OSStart();

  return 0;
}

Output:

Cruise Control 2014
delay in ticks 100
SWTimer was created but NOT started 
SWTimer was started!
All Tasks and Kernel Objects generated!
Vehicle task created!
SWTimer1 was created but NOT started 
SWTimer1 was started!
Control Task created!
Position: 0m
Velocity:  0.4m/s
Throttle: 3V
release key!
released key!
Control Task!
Position: 0m
Velocity:  0.9m/s
Throttle: 4V
release key!
released key!
Control Task!
Position: 0m
Velocity:  1.4m/s
Niklas Rosencrantz
  • 25,640
  • 75
  • 229
  • 424
  • 1
    Do you must use semaphore to sync the ControlTask and VehicleTask or can you just use the OSMbox only? if you don't have to use semaphore, you can try to remove `OSTimeDlyHMSM()` and increases the time wait in the `OSMboxPend()` to the desired value such as `VEHICLE_PERIOD` or `CONTROL_PERIOD`. – SSC Sep 30 '14 at 09:32
  • @SSC Thank you that's very interesting. But it is a university project that the professor instructed my explicitly to use semaphores to "connnect" the timers (plural so iI suppose I should use two timers). I can run and change code but I have trouble synchronizing the tasks and a get a race-codintion if I use semaphores without the `OSTimeDlyHMSM()`. I also do not fully understand why a software timer should be more exact than the bultin capiabilities and I believe that I should use 2 timers + semaphores to make more exact task scheduling / context switching. – Niklas Rosencrantz Sep 30 '14 at 09:36
  • 2
    In that case, you can try to create two times and two semaphores (one for each task). In each of the timer's callback handler (such as `alarm_handler()` in your code) do a `OSSemPost()`. At the beginning of your `VehicleTask` and `ControlTask` do a `OSSemPend()` with 0 as the timeout value(means wait forever). So when the timer goes off, your task can run one loop and stop by the semaphore and wait for next timer goes off. – SSC Sep 30 '14 at 09:47
  • @SSC I think that will work. I'm going to try that a.s.a.p. I think I understand that the timers in each task don't actually switch the tasks but only manipulates the semaphores, that seems a very good idea. – Niklas Rosencrantz Sep 30 '14 at 10:13

3 Answers3

1

OSTimeDlyHMSM() This call allows you to specify the delay time in HOURS, MINUTES, SECONDS and MILLISECONDS instead of ticks. This means you don't let a task work until a proper time is out. Even when all other task worked and/or the CPU is "free" your task is not going to continue its work until the proper time ticks, in your case seconds or whatever, are out. If you really need an event to be happened after such a "big" time, its still worth using this of course.

However the approach of timer usage should not be messed up with Semaphores. The latter are used against data violation and to protect shared resource or critical sections of code from multiple reentrance.

Without deep digging into your code, I would suggest to try to organize your system with OSTimeDly() where possible. As for semaphores, use them when you have a situation that more than one task can call the same function asynchronously or access the same resource like memory,registers, data buses or any hardware.

Ruslan Gerasimov
  • 1,752
  • 1
  • 13
  • 20
  • Thanks for the answer! It did help me understand though I'm still not sure how to proceed. Do you think that I should use a callback? Should a put one timer each in each task? What should the timer do? My university professor instructed me to `Use soft timers for this purpose and connect them with semaphores to your tasks. Use the same period for the soft timers as in the original skeleton program.` I could solve 2/3 projects in this excersive with semaphores and context switches but I'm sort of stuck on synchronizing without the `OSTimeDlyHMSM`. I can always RTFM more. – Niklas Rosencrantz Sep 30 '14 at 10:10
  • 1
    Yes, you should use timer in every task. Other wise the task without such a call will never leave the CPU. Simple `while(1);` can block all other tasks from their executions if it goes wothout a system call putting it into suspend state - like OSTimeDly() and others do. When you call such a "time-fucntion" or others putting a task into suspend state - it will put a current task to _suspend_ state, switch tp other tasks, if any, and the scheduler will restore its execution at least after the timeout is gone. – Ruslan Gerasimov Sep 30 '14 at 10:15
  • 1
    Well. setting semaphores is a valid means of comunicating between a hardware timer interrupt and a preemptive, multithreading kernel. – Martin James Sep 30 '14 at 10:51
  • 1
    Protecting a shared resource is only one application of a semaphore. You can also use a semaphore to signal that an event has occurred. And using a semaphore to signal that a periodic timer has expired is a very reasonable solution to the question. – kkrambo Sep 30 '14 at 12:17
  • Thanks for all the help. Now I used 2 semaphores and 2 timers getting pretty good results (?). I updated the question with the new version of the program + the output. – Niklas Rosencrantz Oct 01 '14 at 14:00
  • 1
    Resource protection is more safely done with a mutex (a specific kind of semaphore for teh purpose). A mutex can only be released by teh same context in which it was taken, and normally provides a mechanism to prevent priority inversion. A normal counting or binary semaphore is more general than a mutex. – Clifford Oct 05 '14 at 13:20
1

My impression is that you have two tasks that you want to run at regular intervals. And you want to accomplish this with a software timer and semaphore. If that's correct then you can accomplish this as follows.

Each task will use it's own semaphore and timer. A semaphore can be used as a signal that an event has occurred. In this case use the semaphore to indicate that the timer has expired and it's time for the task to run. Set up the software timer to expire periodically and call a callback function. The callback function should post to the semaphore. The task should pend on the semaphore within its while loop. This way the task runs one iteration through the loop every time the timer expires.

Here is some psuedo-code:

void TimerCallback(params)
{
    // Post to the semaphore to signal that it's time to run the task.
}

void TaskFunction(void)
{
    // Create a semaphore to represent the "it's time to run" event.
    // Initialize the semaphore count to zero because it's not time
    // to run yet.

    // Create a periodic software timer which calls TimerCallback()
    // when it expires.

    while(1)
    {
        // Wait until it's time to run by pending on the semaphore.

        // Do task specific stuff.
    }
}

The periodic nature of this software-timer-and-semaphore implementation is more precise than using OSTimeDlyHMSM(). The reason is that the software timer period runs independent of the execution time of the task. But the period of OSTimeDlyHMSM() is in addition to the execution time of the task. And the execution time of the task may vary from one iteration to the next if it is preempted by other tasks or interrupts. So using OSTimeDlyHMSM() is not a very precise way to get a periodic event.

kkrambo
  • 6,643
  • 1
  • 17
  • 30
  • Thanks! I rewrote the program and now it switches better. I have doubts about the exact period though and I should clean the code. If you want to have a look, I updated my question with code that now has 2 semaphores and 2 timers. Thanks also for clarifying why software timers are more exact. – Niklas Rosencrantz Oct 01 '14 at 14:01
  • 1
    I'm glad it's working better. I took a closer look at your new code and your use of the mailboxes in addition to the semaphores seems strange to me. Typically a task waits for an event or message at only one place. So waiting for an event signal and then immediately waiting for a mailbox message is unusual. Imagine if the mailbox message wasn't available then the task would get stuck there and maybe miss several semaphore event signals while it's waiting for the mailbox message. I don't really understand your application though so maybe you have some guarantee that mail is waiting. – kkrambo Oct 01 '14 at 16:08
1

The problem with both OSTimeDlyHMSM() and OSTimeDly() for periodic processes is that they do not take into account the processing time of the rest of the thread. For example in:

// Task loop
for(;;)
{
    OSTimeDly( 100 ) ;

    doStuffHere() ;
}

You migh expect doStuffHere() to run every 100 ticks, which it will unless doStuffHere() itself takes longer the 1 tick period. It may for example itself contain a delay, or block on some other event.

// Task loop
for(;;)
{
    OSTimeDly( 100 ) ;

    doStuffHere() ;
}

By using a timer, this can be overcome:

// Note this is illustrative and not uC/OS-II code
timer_handle = createTimer( 100, PERIODIC ) ;

// Task loop
for(;;)
{
    waitForTimer( timer_handle ) ;

    doStuffHere() ;
}

Here, doStuffHere() only needs to take less than 100 ticks for the loop to be exactly periodic - that is far better than one tick.

All that said if you can guarantee that that doStuffHere() will take less than one tick - even when pre-empted by higher priority tasks, then a delay is much simpler.

A further advantage of using a timer on the other hand is that a blocking on a delay can only react to expiry of the delay, whereas a task blocking on a semaphore can react to any event that gives the semaphore, so can be caused to run on multiple events including the timer. Similarly for other IPC mechanisms such as queues or event flags.

Clifford
  • 88,407
  • 13
  • 85
  • 165