How to control the sequence of execution of functions ?

Hi,

I'm working on a simple code to control the sequence of execution of functions for multitasking.

I opened an example in RTOS library, this part:

    digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
    vTaskDelay( 1000 / portTICK_PERIOD_MS ); // wait for one second
    digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
    vTaskDelay( 1000 / portTICK_PERIOD_MS ); // wait for one second

I have done some projects with locking a function and take a millis() snap and check the required time reached then unlocking the function or do anything.

My goal now is the following:

  1. design a task manager, that has this feature of vTaskDelay but the group of sequence that use this feature should be put in a function so it run with vTaskDelay
  2. at the same time not blocking other functions in the system

Now my most important task is to learn how to do something like vTaskDelay ?

I have done some projects with locking a function and take a millis() snap and check the required time reached then unlocking the function or do anything.

My goal now is the following:

  1. design a task manager, that has this feature of vTaskDelay but the group of sequence that use this feature should be put in a function so it run with vTaskDelay
  2. at the same time not blocking other functions in the system

Now my most important task is to learn how to do something like vTaskDelay ?

1/ do not use task delay or any other function that delays the program execution
it is a bad practice
use some time comparison to control code execution

as for multi-task, search double link list implementation using C language
search google

you can also utilise or implement a simple state machine

hope this helps

1 Like

traja47:
1/ do not use task delay or any other function that delays the program execution
it is a bad practice

but I thin the task delay provided by freeRTOS isn't blocking, I guess.

as for multi-task, search double link list implementation using C language
search google
Insertion in a Doubly Linked List - GeeksforGeeks

yeah, it seems to be a bit difficult topic for me, but I got some idea of the link, thanks.
I can start with implementing structs that contain information about each task.

you can also utilise or implement a simple state machine

That's what I want to do, I have done some code using lock/unlock and checking flags.
I just now need to register the time values for each function

Sounds like a lot of work for negligible benefit. :roll_eyes:

Most things can be done using straightforward state machine code. Your so-called "locks" are just simple switch variables, possibly booleans.

1 Like

Paul__B:
Sounds like a lot of work for negligible benefit. :roll_eyes:

Most things can be done using straightforward state machine code. Your so-called "locks" are just simple switch variables, possibly booleans.

Why negligible benefit ?
Also considering a lot of work is actually programming practice for me to learn something.
Because if I have done this project, then I can use it to control any delays/timing required in my projects.
Yes, the locks are booleans.
I'm considering this struct as a start for my project.

// variables
static uint8_t task_counter;

// structs
typedef struct {
	uint8_t (*fun_ptr)();
    uint32_t task_st;
    uint32_t task_pr;
    bool task_lock;
}TASK_MANAGER;

Delay and timing code - using millis() - is done using functions. You don't need a RTOS to do that. :grinning:

1 Like

Paul__B:
Delay and timing code - using millis() - is done using functions. You don't need a RTOS to do that. :grinning:

Yes I know, I don't want to do RTOS exactly !
I want to do a simple task manager, in case if I need any timing management, then it shouldn't be too difficult to adapt any library with my task manager.
For example, if I wanted to do any timing for a button, LCD page, LED than goes on or off for certain amount of time .. then I really would need a time manager.
I have a project I done in the college for 3-way traffic light with:

  1. red light violation system
  2. speed capture
  3. traffic red light counter
    The streets traffic need timing for red, green and yellow management, the red light need a counter that counts every 1s with managing 3 counters.

The code is attached, I couldn't post it here.

  1. At the beginning, you can notice I'm using 3 variables for each task, my idea is to improve this part with a struct array.
  2. Also, the timing checking at the beginning of each function, how would you evaluate my code ? is the time checking ok or there are more effective ways to do it ?
    ==============================================================================
    Beside this, my idea in this thread is to design a system, that saves me of designing the timing system for any project and just focus on device drivers.
    Because after I design a task manager, I can proceed with my tft library project for my starting weather station, to control the GUI system and the other stuff related timing how frames are updated to the tft, when to read a data, when to send a frame to the tft that should go on for specific time and then maybe send another frame, ... etc. This is what I'm thinking about.

traffic_system.cpp (8.01 KB)

For example, if I wanted to do any timing for a button, LCD page, LED than goes on or off for certain amount of time .. then I really would need a time manager.

No you wouldn’t you just write it.

1 Like

Grumpy_Mike:
No you wouldn’t you just write it.

How ?
for example, how to provide 1s increment for traffic 7-seg 2-digit counters ? also how to manage the timing between 3 traffic light colors red, green and yellow ?

You can re-write my traffic light project in your way so I can understand.

Using the principles of a state machine you simply alter the time before the next state of that thread is called. So to flash an LEDs for the walk indicator, one state was a flashing state. It toggled the LEDs a certain number of times and the next time interval was short, in line with the flashing rate. When that state had finished the walk LED was turned off, the state was moved on to the next one and the interval changed to a longer time between evocations. The same goes for the timing of the traffic light LEDs.

I wrote a pedestrian crossing program that did a similar thing in Python using the same techniques. It was for my book Raspberry Pi for Dummies edition 3.

1 Like

OK, here's a version of the pedestrian crossing, with encapsulated timing and debounce functions. Didn't have the enthusiasm for the full junction code.

// Pedestrian crossing lights
// Code by Paul__B of Arduino forum.

const int led13Pin =  13;    // LED pin number
const int PgrnPin =  6;
const int PredPin =  5;
const int RgrnPin =  4;
const int RyelPin =  3;
const int RredPin =  2;
const int button1 =  10;

int led13State = LOW;        // initialise the LED
int PgrnState = LOW;
int PredState = LOW;
int RgrnState = LOW;
int RyelState = LOW;
int RredState = LOW;
char bstate1 = 0;
boolean press = false;

unsigned long count1 = 0;   // will store last time LED was updated
unsigned long count2 = 0;
unsigned long count3 = 0;
unsigned long count4 = 0;
unsigned long count5 = 0;
unsigned long bcount1 = 0; // button debounce timer.  Replicate as necessary.

// Have we completed the specified interval since last confirmed event?
// "marker" chooses which counter to check 
boolean timeout(unsigned long *marker, unsigned long interval) {
  if (millis() - *marker >= interval) { 
    *marker += interval;    // move on ready for next interval
    return true;       
  } 
  else return false;
}

void setout(unsigned long *marker) {
  *marker = millis();             // initialise
}

// Deal with a button read; true if button pressed and debounced is a new event
// Uses reading of button input, debounce store, state store and debounce interval.
boolean butndown(char button, unsigned long *marker, char *butnstate, unsigned long interval) {
  switch (*butnstate) {               // Odd states if was pressed, >= 2 if debounce in progress
  case 0: // Button up so far, 
    if (button == HIGH) return false; // Nothing happening!
    else { 
      *butnstate = 2;                 // record that is now pressed
      *marker = millis();             // note when was pressed
      return false;                   // and move on
    }

  case 1: // Button down so far, 
    if (button == LOW) return false; // Nothing happening!
    else { 
      *butnstate = 3;                 // record that is now released
      *marker = millis();             // note when was released
      return false;                   // and move on
    }

  case 2: // Button was up, now down.
    if (button == HIGH) {
      *butnstate = 0;                 // no, not debounced; revert the state
      return false;                   // False alarm!
    }
    else { 
      if (millis() - *marker >= interval) {
        *butnstate = 1;               // jackpot!  update the state
        return true;                  // because we have the desired event!
      }
      else 
        return false;                 // not done yet; just move on
    }

  case 3: // Button was down, now up.
    if (button == LOW) {
      *butnstate = 1;                 // no, not debounced; revert the state
      return false;                   // False alarm!
    }
    else { 
      if (millis() - *marker >= interval) {
        *butnstate = 0;               // Debounced; update the state
        return false;                 // but it is not the event we want
      }
      else 
        return false;                 // not done yet; just move on
    }
  default:                            // Error; recover anyway
    {  
      *butnstate = 0;
      return false;                   // Definitely false!
    }
  }
}


void setleds() {
  digitalWrite(led13Pin, led13State);
  digitalWrite(PredPin, PredState);
  digitalWrite(PgrnPin, PgrnState);
  digitalWrite(RredPin, RredState);
  digitalWrite(RyelPin, RyelState);
  digitalWrite(RgrnPin, RgrnState);
}

boolean ispress() { // One-directional read of button - sets but does not clear!
  if (butndown(digitalRead(button1), &bcount1, &bstate1, 10UL )) {
    press = true;
  } 
  return(press);
}

void setup() {
  Serial.begin(9600);
  pinMode(led13Pin, OUTPUT);      
  pinMode(PgrnPin, OUTPUT);      
  pinMode(PredPin, OUTPUT);      
  pinMode(RgrnPin, OUTPUT);      
  pinMode(RyelPin, OUTPUT);      
  pinMode(RredPin, OUTPUT);      
  pinMode(button1, INPUT);      
  digitalWrite(button1,HIGH);        // internal pullup all versions
  press = false;
  Serial.println("Starting ...");
}

void loop() {
  // All red phase
  RredState = HIGH;
  RyelState = LOW; 
  RgrnState = LOW; 
  PredState = HIGH;
  PgrnState = LOW; 
  setleds();
  Serial.println("Red phase");
  setout(&count3);  
  while (!timeout(&count3, 3000UL )) {
    ispress();  // Check on the button
  }

  // Road Green
  RredState = LOW;
  RyelState = LOW; 
  RgrnState = HIGH; 
  PredState = HIGH;
  PgrnState = LOW; 
  setleds();
  Serial.println("Road green");
  setout(&count3);  
  while (!timeout(&count3, 8000UL )) { // Reasonable time on green
    ispress();  // Check on the button
  }
  Serial.println("Green stale, wait on button");

  while ( press == false )  // Now wait for the button 
  {
    if (timeout(&count2, 300UL )) {
      if (led13State == LOW) {
        led13State = HIGH;
      }
      else {
        led13State = LOW; 
      } 
      digitalWrite(led13Pin, led13State);
    }
    ispress();   
  }
  led13State = LOW; 
  digitalWrite(led13Pin, led13State);

  Serial.println("Button sensed");
  setout(&count3);  
  while (!timeout(&count3, 4000UL )) { // Do not respond immediately!
  }

  // Road Yellow
  RredState = LOW;
  RyelState = HIGH; 
  RgrnState = LOW; 
  PredState = HIGH;
  PgrnState = LOW; 
  setleds();
  Serial.println("Road yellow");
  setout(&count3);  
  while (!timeout(&count3, 5000UL )) {
  }

  // Road Red
  RredState = HIGH;
  RyelState = LOW; 
  RgrnState = LOW; 
  PredState = HIGH;
  PgrnState = LOW; 
  setleds();
  Serial.println("Road red");
  setout(&count3);  
  while (!timeout(&count3, 3000UL )) {
  }

  // Walk Green
  RredState = HIGH;
  RyelState = LOW; 
  RgrnState = LOW; 
  PredState = LOW;
  PgrnState = HIGH; 
  setleds();
  press = false;  
  Serial.println("Walk");
  setout(&count3);  
  while (!timeout(&count3, 6000UL )) {
  }

  // Flash Don't Walk
  RredState = HIGH;
  RyelState = LOW; 
  RgrnState = LOW; 
  PgrnState = LOW; 
  Serial.println("Flash Don't Walk");
  setout(&count3);  
  while (!timeout(&count3, 7000UL )) {
    if (timeout(&count2, 500UL )) {
      if (PredState == LOW) {
        PredState = HIGH;
      }
      else {
        PredState = LOW; 
      } 
      setleds();
    }
    ispress();  // Check on the button
  }

}
1 Like

Grumpy_Mike:
Using the principles of a state machine you simply alter the time before the next state of that thread is called. So to flash an LEDs for the walk indicator, one state was a flashing state. It toggled the LEDs a certain number of times and the next time interval was short, in line with the flashing rate. When that state had finished the walk LED was turned off, the state was moved on to the next one and the interval changed to a longer time between evocations. The same goes for the timing of the traffic light LEDs.

Yes, I'm interested in state machine code and not a full rtos, I just want to improve my coding a bit.
I really want you to download my traffic code in post #6 and check my coding approach and probably provide me with some feedback.
Also I want you to provide me an example of the way of state machine you explained.
Do you mean the time managing in the example you explained is done with millis() ?

I wrote a pedestrian crossing program that did a similar thing in Python using the same techniques. It was for my book Raspberry Pi for Dummies edition 3.

That's nice, I also done some projects for the department and they were ok.

Do you mean the time managing in the example you explained is done with millis() ?

Yes that is all you need.

I really want you to download my traffic code in post #6 and check my coding

Well I can’t do it for a day or two, as it is late where I am and I have writing deadlines to reach.

In the mean time I did once, about 13 years ago, write a long state machine for a game, this is a link to the project and the code can be downloaded from this page. The whole sequence is one long state machine so that the lid could be closed at any time in the game.
http://www.thebox.myzen.co.uk/Hardware/Dice_Game.html

The software link is a bit hard to find. It is contained in this paragraph.

The Gcode files for cutting this along with the PDF of the box cutout and software can be downloaded here. Just a point here that I did get two wooden dice, but one was not quite a cube it was longer by a few mm in one direction, so if you are going to get one then take your Verner callipers with you, and make sure you get one as close to a cube as possible.

Grumpy_Mike:
Well I can’t do it for a day or two, as it is late where I am and I have writing deadlines to reach.

if have the time later, I would be honored to get a feedback from you !

In the mean time I did once, about 13 years ago, write a long state machine for a game, this is a link to the project and the code can be downloaded from this page. The whole sequence is one long state machine so that the lid could be closed at any time in the game.
http://www.thebox.myzen.co.uk/Hardware/Dice_Game.html

wow thanks for the link, there are other projects too ! that's really nice.
but I didn't know how to download the code.

OK, found the folder.

Is everything done in "LiquidCrystalM" library ?

wolfrose:
Is everything done in "LiquidCrystalM" library ?

No that just handles the LCD. Conventional LCD libraries use large delays to make sure the LCD is not busy before the next command is given. But with the addition of an extra wire from the LCD you can tell if the LCD is busy or not and therefore it will be much quicker to respond. From memory I think my library is about four times faster.

That's nice.

I have a new problem now regarding declaring a typedef struct and trying to access it from other source files.

I want the object of struct to be shared between two libraries. How to do that ?

Here is my code arrangement:

  1. task_manager.h
#ifndef task_manager_h
#define task_manager_h

#include <inttypes.h>
#include <Arduino.h>
#include <SPI.h>
#include <string.h>
#include <avr/pgmspace.h>
#include <avr/io.h>

#ifdef __cplusplus
    extern "C" {
#endif

// definitions
#define SYSTEM_TASKS 2 // set the number of tasks in the system

// variables
static uint8_t task_counter;
static uint8_t task_counts;

// structs
typedef struct {
	uint8_t (*fun_ptr)();
    uint32_t task_st;
    uint32_t task_pr;
    bool task_start_lock;
    bool task_full_lock;
}TASK_MANAGER;

TASK_MANAGER task[SYSTEM_TASKS];

// functions
void task_manager_init(uint8_t tasks);
void task_manager(void);

#ifdef __cplusplus
    }
#endif

#endif
  1. task_manager.c
#include "task_manager.h"

void task_manager_init(uint8_t tasks){
	Serial.println("system init");
	TASK_MANAGER task[tasks] = {0};
	TASK_MANAGER *task_ptr;
	task_ptr = &task[0];
	task_counts = tasks;
	task_counter = 0;
}

void task_manager(void){
	if(task_counter < task_counts){					// if there is any task need a timing management	
		if(task_ptr[task_counter].task_start_lock){
			if(micros() - task_ptr[task_counter].task_st >= task_ptr[task_counter].task_pr){
				task_ptr[task_counter].task_start_lock = 0;
				task_counter++;						// for incrementing approach #Note.1
			}		
		}
	}
	else{
		task_counter = 0;							// starting all over again
		Serial.println("starting all over again");
	}
}
  1. sensors_modules.h
#ifndef sensors_modules_h
#define sensors_modules_h

#include <inttypes.h>
#include <Arduino.h>
#include <avr/pgmspace.h>
#include <avr/io.h>

// functions
void blink_led(uint32_t time, uint8_t task_number);

#endif
  1. sensors_modules.c
    Note: I haven't put practical code here, just used Serial.print for testing.
#include <inttypes.h>
#include <Arduino.h>
#include <SPI.h>
#include "task_manager.h"
#include "sensors_modules.h"

void blink_led(uint32_t time, uint8_t task_number){	
	Serial.println("task_start_lock");
	Serial.println(task_ptr[0].task_start_lock);
	Serial.println("task_full_lock");
	Serial.println(task_ptr[0].task_full_lock);
}

OK I don't know if I solved the problem partially or completely.

But I did the following:

  1. declared the "typedef struct{ ... } TASK_MANAGER;" in task_manager.h
  2. defined and initialized an array of object "TASK_MANAGER task[MAX_TASKS] = {0};" globally in task_manager.cpp
  3. defined and initialized a pointer to the struct.
  4. In init function, I assigned the struct to the pointer.
  5. used it in another source file with extern keyword.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.