Two parallel task by timer2 but the sampling time is wrong

Hi, I am a beginner in Arduino and want to conduct two tasks with different sampling times. I have read the post “multiple tasks a time” which indicates the sequential manner of multiple tasks. But my question is what if completing one task takes longer than the sampling time of the other?

For instance, the belowing attached code, “twotaskssequence,” includes two tasks. The first is to analog read a signal every 0.01s (I assign the “value” in line 32 for illustration). The second task is to output 1000 cycles of PWM signals with a period of 1ms and then delay for 1s. The completion of the second task takes 2s. If the two tasks have to be conducted sequentially, I can’t guarantee the first task is conducted every 0.01. The first task actually needs over two seconds if waiting the second one to be completed.

//first task: analog read signal every 0.01s
// second task: ouput 1000 PWM cycles (period 1ms),it will take 1s to complete and then delay 1s
//int analogPin = A0; //analogread pin
#define pi 3.14
const int pwmPin = 10;
float value;  //assign value every 0.01s
unsigned long firtaskTime = 0;
unsigned long sectaskTime = 0;
unsigned long now;
const long intervalfir = 10;  // analogread every 0.01sec
const long intervalsec = 2000;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  //pinMode(analogPin, INPUT);
  pinMode(pwmPin, OUTPUT);
}

void loop() {

  now = millis();
  Serial.print(now);
  Serial.print("\t\t");
  Serial.print(firtaskTime);
  Serial.print("\t\t");
  Serial.print(sectaskTime);
  Serial.print("\t\t");
  Serial.println();
  //  first task analogread a signal every 0.01s (10ms)
  if ((now - firtaskTime) >= intervalfir) {
    value = sin(2 * pi * now);  // this could be analogread a realtime dc voltage;
    firtaskTime = millis();
  }
  // second task output 1000PWM cycles each period 1ms,it takes 1s to output all these PWM signals and then delay 1sec
  // The completion of second task takes 2 sec;
  if ((now - sectaskTime) >= intervalsec) {
    for (int k = 1000; k > 0; k--) {
      digitalWrite(pwmPin, HIGH);
      delayMicroseconds(500);
      digitalWrite(pwmPin, LOW);
      delayMicroseconds(500);
    }
    delay(1000);
    sectaskTime = millis();
  }


}

After I googled the solution, I decided to use the Timer2 to make the first task occur every 0.01s in the ISR function. The second task occurs in the main loop. Code is “twotaskparallel”. But if I run the code, I find the first task is conducted about every 20ms and the second task is conducted every 4secs.


#define pi 3.14
const int pwmPin = 10;
volatile float value;  //assign value every 0.01s
volatile long firtaskTime;
unsigned long sectaskTime = 0;
unsigned long now;
//const long intervalfir = 10;  // analogread every 0.01sec
const long intervalsec = 2000;
void setup() {

  cli();       //stop interrupts
               //set timer2interrupt at 100Hz
  TCCR2A = 0;  // set entire TCCR2A register to 0
  TCCR2B = 0;  // same for TCCR2B
  TCNT2 = 0;   //initialize counter value to 0
  // set compare match register for 100Hz increments
  OCR2A = 155;  // = (16*10^6) / (100*1024) - 1 (must be <256)
  // turn on CTC mode
  TCCR2A |= (1 << WGM21);
  // Set  1024 prescaler
  TCCR2B |= (1 << CS22) | (1 << CS20);
  // enable timer compare interrupt
  TIMSK2 |= (1 << OCIE2A);
  sei();
  pinMode(pwmPin, OUTPUT);
  Serial.begin(9600);
}
// I used Timer 2 so that I could record the timestamp in ISR
ISR(TIMER2_COMPA_vect) {
  firtaskTime = millis();
  value = sin(2 * pi * firtaskTime);
}

void loop() {
  // put your main code here, to run repeatedly:
  now = millis();
  Serial.print(now);
  Serial.print("\t\t");
  Serial.print(firtaskTime);
  Serial.print("\t\t");
  Serial.print(sectaskTime);
  Serial.print("\t\t");
  Serial.println();
  if ((now - sectaskTime) >= intervalsec) {
    for (int k = 1000; k > 0; k--) {
      digitalWrite(pwmPin, HIGH);
      delayMicroseconds(500);
      digitalWrite(pwmPin, LOW);
      delayMicroseconds(500);
    }
    delay(1000);
    sectaskTime = millis();
  }
}

Can anyone pls help to solve the issue?

Using a millis() based approach will become moot if you use for-loops with delays in them or use delay.

You will need to split your second task in small pieces using a millis() based approach for that.

Thanks for your prompt reply! But I searched the other posts, it seems only "Timer 0" will affect the usage of"millis()'. So I use "Timer 2".

I need the time stamp of ISR variables since I need to plot the time history of the data. I may need to do moving-average or filter to process the analog read data to debug my control algorithm later.

This would be a non-blocking task2 that will be 1000 blinks.

bool task2()
{
  static uint32_t lastUpdateTime;
  static uint8_t pinStatus;
  static uint16_t counter;
  if (millis() - lastUpdateTime >= 500)
  {
    counter++;
    lastUpdateTime = millis();
    pinStatus = !pinStatus;
    digitalWrite(pwmPin, pinStatus);
  }

  if (counter == 1000 * 2)
  {
    counter = 0;
    return true;
  }
  else
  {
    return false;
  }
}

Call it when it needs to be called; e.g.

void loop()
{
    //  first task analogread a signal every 0.01s (10ms)
  if ((now - firtaskTime) >= intervalfir) {
    value = sin(2 * pi * now);  // this could be analogread a realtime dc voltage;
    firtaskTime = millis();
  }

  if(task2() == true)
  {
    Serial.println("Task 2 completed");
  }
}
1 Like

Thanks, it's good to learn a new usage of the bool and static variables. If my understanding is right, use the bool task2 could be anytime I want. But the issue is if I run task 2, it takes 2 seconds. Then when it turns into task1, the time interval between last task1 and current task1 is over 2sec, the 0.01 sampling time interval of task 1 can't be satisfied.

As I stated in the begining, task one have to be conducted every 0.01s and task 2 have to be conducted every 2s. They have to be conducted indepedently without interrupting each other.

I would also appreciated it if you could run my revised code according to the bool function.

`#define pi 3.14
const int pwmPin = 10;
float value;  //assign value every 0.01s
unsigned long firtaskTime = 0;
unsigned long sectaskTime = 0;
unsigned long now;
const long intervalfir = 10;  // analogread every 0.01sec
const long intervalsec = 2000;
void setup() {
  // put your setup code here, to run once:
// put your setup code here, to run once:
  Serial.begin(9600);
  //pinMode(analogPin, INPUT);
  pinMode(pwmPin, OUTPUT);
}

void loop() {
   now = millis();
  Serial.print(now);
  Serial.print("\t\t");
  Serial.print(firtaskTime);
  Serial.print("\t\t");
  Serial.print(sectaskTime);
  //Serial.print(lastUpdateTime);
  Serial.print("\t\t");
  Serial.println();
    //  first task analogread a signal every 0.01s (10ms)
  if ((now - firtaskTime) >= intervalfir) {
    value = sin(2 * pi * now);  // this could be analogread a realtime dc voltage;
    firtaskTime = millis();
  }
  
  task2 = true;

  if(task2() == true)
  {
    Serial.println("Task 2 completed");
  }
}

bool task2()
{
  static uint32_t lastUpdateTime;
  static uint8_t pinStatus;
  static uint16_t counter;
  if (millis() - lastUpdateTime >= 500)
  {
    counter++;
    lastUpdateTime = millis();
    pinStatus = !pinStatus;
    digitalWrite(pwmPin, pinStatus);
  }

  if (counter == 1000 * 2)
  {
    counter = 0;
    return true;
  }
  else
  {
    return false;
  }
  sectaskTime=lastUpdateTime;
}`

If my understanding is incorrect, pls forgive me.

Yes

The presented code in post #4 does not wait till task2 has done 1000 cycles. It runs task2() once quickly, increments the counter and continues. next it runs task2() quickly again, increments the counter and continues. And so on; till 1000 on/off cycles are completed.

task2() is a function; you can not assign a value to a function.

You should not put that in task2(). task2() is a standalone function and does not need any modifcation. I had a bug in the version in post #4 where I used millis() instead of micros(); fixed that in below.

Below a complete code to demonstrate

//first task: analog read signal every 0.01s
// second task: ouput 1000 PWM cycles (period 1ms),it will take 1s to complete and then delay 1s
//int analogPin = A0; //analogread pin
#define pi 3.14
const int pwmPin = 10;
float value;  //assign value every 0.01s
unsigned long firtaskTime = 0;
unsigned long sectaskTime = 0;
unsigned long now;
const long intervalfir = 10;  // analogread every 0.01sec
const long intervalsec = 2000;
void setup()
{
  // put your setup code here, to run once:
  Serial.begin(9600);
  //pinMode(analogPin, INPUT);
  pinMode(pwmPin, OUTPUT);
}

void loop()
{
  // keep track if task 2 is in progress
  static bool taskTwoInProgress = false;

  now = millis();
  Serial.print(now);
  Serial.print("\t\t");
  Serial.print(firtaskTime);
  Serial.print("\t\t");
  Serial.print(sectaskTime);
  Serial.print("\t\t");
  Serial.println();
  //  first task analogread a signal every 0.01s (10ms)
  if ((now - firtaskTime) >= intervalfir)
  {
    value = sin(2 * pi * now);  // this could be analogread a realtime dc voltage;
    firtaskTime = millis();
  }

  // if the second task has not been started yet and it's time to start it
  if (taskTwoInProgress == false && (now - sectaskTime) >= intervalsec)
  {
    // update time
    sectaskTime = millis();
    // set flag
    taskTwoInProgress = true;
  }

  // if the flag is set that task 2 is in progress
  if (taskTwoInProgress == true)
  {
    bool rb = task2();
    if (rb == true)
    {
      Serial.println(F("Task 2 completed"));
      taskTwoInProgress = false;
    }
  }
}

bool task2()
{
  static uint32_t lastUpdateTime;
  static uint8_t pinStatus;
  static uint16_t counter;
  if (micros() - lastUpdateTime >= 500)
  {
    counter++;
    lastUpdateTime = micros();
    pinStatus = !pinStatus;
    digitalWrite(pwmPin, pinStatus);
  }

  if (counter == 1000 * 2)
  {
    counter = 0;
    return true;
  }
  else
  {
    return false;
  }
}

PS
In future, please don't post screenshots of errors.

1 Like

Your loop is doing a LOT of printing, which is “quite slow.”

I see what you mean. The bool task2 enables to split the PWM output almost at the same sampling rate as task 1. That's fancinating :+1: ! Many thanks! I could revise my code based on this! Respect :saluting_face:

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