chipKIT® Development Platform

Inspired by Arduino™

Core Timer and Task Manager

Created Sun, 05 May 2013 22:29:25 +0000 by ricklon


ricklon

Sun, 05 May 2013 22:29:25 +0000

I've been reviewing these features. CoreTimer can have up to 3 timers registered, and they act as interrupts at a particular moment.

The Task Manager registers tasks to occur at certain intervals but isn't an interrupt. Is there a theoretical maximum number of tasks?

Like CoreTimer should delay() or delayMicroseconds() be avoided in the invoked code?

I've got a project that needs to have a burst of N number of IR led pulses, occurring at a regular intervals. The pulses have to be run 13 microseconds apart. It seems that the TaskManager would be good for something like that. I'd like to be running 4 of these IR leds with different numbers of pulses, at different intervals.

I would then register 4 tasks. Would it be reasonable to put the pulse/blink code into the task? Or should the task only change state, and the loop() function control the pulsing/blinking?

--Rick


EmbeddedMan

Mon, 06 May 2013 13:32:48 +0000

Rick,

This is similar in a lot of ways to the code I wrote for the SoftPWMServo library. Yes, it can be done, but as the number of asynchronous timing events increases, the jitter in all of them will increase.

What is the accuracy required for your IR protocol?

Depending upon that value, you can either tolerate the extra overhead of using multiple core timer functions (or the same one called with different data - same difference) or not. If not, then you'll need to code a single core timer routine and handle the IR routine in there for all of your IR devices. (Which is what I had to do for SoftPWMServo.) In other words, you'll have to handle all of the scheduling of the next core timer fire yourself inside that one routine. The reason this is done is so that if there is a edge that is coming up in the near future, and there isn't time to leave the routine and re-enter it, you have to detect this before you leave the routine, and stay in the routine long enough to fire all of the near-term edges.

*Brian


ricklon

Mon, 06 May 2013 15:52:49 +0000

Brian,

"if there is a edge that is coming up in the near future, and there isn't time to leave the routine and re-enter it,"

Whoa. Thanks for responding. That is very helpful.

I see the TaskManager has a resolution of milliseconds, and the coreTimer has a resolution in the micro seconds. My efforts to debug by putting Serial println statement in the function likely slowed it down as well.

Given this I would put the timing in a Core Timer routine, and then check the time for each of the events that are "registered" to occur. This would guarantee the frequency of event, and the bursts would all be controlled in that one routine.

I have an interrupt detecting the IR pulses. It checks the current state of whether the sensors are on, and measures the times between pulses. If this routine is fast enough it would be done before the next coure Timer event.

-_Rick

-_Rick


ricklon

Mon, 06 May 2013 15:57:28 +0000

Also, now that I think about it. This is very much like SoftPWMServo. I'm trying to create the ecoder, and decoder on the same board and I'm using IR.


EmbeddedMan

Mon, 06 May 2013 17:23:46 +0000

Doing software IR send and receive for 4 different IR systems is not trivial. Not that you're assuming that it is, of course. :-)

I will look at the code you sent me, and information for the IR protocol (is there a link for that?) and see if I can understand how you're going about things.

Another thing you're going to have to deal with is that the Core Timer is, I think, at one of the highest priority levels. So your IR receive interrupt may have to wait to be serviced for the maximum length of time that the Core Timer routine can run for. That may mean that you can't count incoming edges reliably.

What I'd do is instrument the heck out of the code with lots of GPIO sets and clears and watch everything happen in real time with a logic analyzer. There's no better way to see what's going on with these timing dependent protocols.

*Brian


ricklon

Mon, 06 May 2013 18:04:58 +0000

Thanks. I'm certainly trying to make something hard easier. I've been testing with on two IR leds. I have a huge supply of them so it's cheap to emit a lot of IR. Way harder to decode and understand it.

Part of the goal is achieved, I know better the difference between Core Timer, and Task Manager. I've been looking for examples that show the benefits of using one vs the other. It's pretty clear I can coordinate the output of many digital pins and use Core Timer to make sure they all behave precisely. It looks like a good practice is to register all those pins, and behaviors into a list that is checked and activated in the Core Timer.


EmbeddedMan

Tue, 07 May 2013 03:30:54 +0000

Rick - I'm looking at the code you sent me.

From looking at the code, it looks like you have two IR emitters, and one IR detector. The IR emitters are triggered from Task Manager tasks, the first every 250ms, and the second every 301ms. The first one will blink once, the second will blink 5 time with 13us between edges (13us high, 13us low).

The receiver is tied into interrupt 1, and an interrupt hook is called every time a rising edge is seen on INT1. This hook routine looks at which IR emitter was transmitting, and then increments a counter for each emitter on each edge.

I don't see any problem with this plan. However, one important thing you have to be sure of is that the INT1 interrupt routine can get called even while you're within a Core Timer function. I'm not certain about that - have you tested it to make sure that it can? Also, for sure you can't have any serial stuff inside either the Core Timer routines or the interrupt hook, as that will prevent things from working right because of the tight timing.

Again, if you're having trouble figuring out why it's not working, toggling GPIO at critical sections and watching on a logic analyzer will be the quickest way to debug this type of code.

I really like the example you're building up here - it shows off some of the awesome stuff that chipKIT can do, while still being simple to understand. Nice work Rick. Make sure to post the final code when you've got it all working!

*Brian


ricklon

Tue, 07 May 2013 21:26:47 +0000

Great. I appreciate the feedback. I'm going to split the project into two. One that demonstrates Core Task Manager, and another that use what we were talking about here and registering with the Core TImer. More updates on there way.


ricklon

Thu, 09 May 2013 02:01:35 +0000

I've learned that the interrupt won't activate during the the task. So I use the task to change the state. The interrupt picks up the change after the state change occurs, and the task is exited. Whoever, the state change is long enough the interrupt is triggered multiple times before the next task step.

--Rick


dangeljs

Thu, 09 May 2013 21:12:48 +0000

Have you ever considered using the input capture module of the PIC? You could use it for capturing you IR receive signal. It would require writing directly to the registers to configure and limit you to only the IC pins, but it may save some overhead for your transmission code.


ricklon

Fri, 10 May 2013 16:22:41 +0000

I've thought about it, but I'm trying to go for an Arduino API type example. Here's the current version of the code. It's not got the cruft removed. I've added the idea of a measure, it's like a frame. When multiple detections occur in the measure they are ignored.

/*
* Fubarino Object Detection
 * Determine with one sensor which where an object is 
 * 2 IR leds
 * 1 IR Sensor
 */

//#define pinSrc 12 // used as external interrupt source
//PIN_INT1 for the FubarinoSD is Pin 0
#define pinInt PIN_INT1

#define SENSOR1_PIN 0
#define EMITTER1_PIN 12
#define EMITTER2_PIN 13
#define BEATS 6

int emmiter1_id;
int emmiter2_id;
int emmiter1_off;
int emmiter2_off;

unsigned long blink1_var;
unsigned long blink2_var;

volatile boolean emitter1State = LOW;
volatile boolean emitter2State = LOW;
volatile boolean prevEmitter1State = LOW;
volatile boolean prevEmitter2State = LOW;

volatile boolean detected = LOW;

volatile unsigned long emit1Count = 0;
volatile unsigned long emit2Count = 0;
volatile unsigned long detectCount = 0;
volatile unsigned long gTime;
volatile unsigned long pulseTime = 0;
volatile unsigned long gPrevTime = 0;
volatile int phaseA = 0;
volatile int phaseB = 0;
volatile int found;
volatile int prevFound;
volatile int measureA = 0;
volatile int measureB = 0;
volatile int prevMeasureA = -1;
volatile int prevMeasureB = -1;

//Prototypes
void blink_IR1(int id, void * tptr);
void blink_IR2(int id, void * tptr);
void readIRSensor();
void blink(int);

void setup() {
  Serial.begin(115200);
  delay(2000);
  // initialize the digital pin as an output.
  // Pin PIN_LED1 has an LED connected on most Arduino boards:
  pinMode(pinInt, INPUT);
  //debugging LED, shows when pulse found
  pinMode(PIN_LED1, OUTPUT);
  digitalWrite(PIN_LED1, HIGH);
  pinMode(SENSOR1_PIN, INPUT);
  pinMode(EMITTER1_PIN, OUTPUT);
  pinMode(EMITTER2_PIN, OUTPUT);

  digitalWrite(EMITTER1_PIN, LOW);
  digitalWrite(EMITTER2_PIN, LOW);    
  //blink before timers and interrups
  blinkAll(6);

  attachInterrupt(1, readIRSensor, RISING);
  emmiter1_id = createTask(blink_IR1, 25, TASK_ENABLE, &blink1_var);
  emmiter2_id = createTask(blink_IR2, 25, TASK_ENABLE, &blink2_var);

}

void loop() {
  digitalWrite(PIN_LED1, LOW);
  // if (detected && ( prevMeasureA == measureA) || (prevMeasureB == measureB) ) {
  if (detected && ( prevMeasureA < measureA || prevMeasureB < measureB )) {
    Serial.print("{ \"IRDetect\": ");
    Serial.print(detectCount);
    Serial.print(" ,\"Emmit1\": ");
    Serial.print((int)emitter1State);
    Serial.print(" ,\"count\": ");
    Serial.print(emit1Count);
    Serial.print(" ,\"Emmit2\": ");
    Serial.print((int)emitter2State);
    Serial.print(" ,\"count\": ");
    Serial.print(emit2Count);
    //Serial.print(" ,\"pulse uS\": ");
    //Serial.print(pulseTime);
    Serial.print(" ,\"measureA\": ");
    Serial.print(measureA);
    Serial.print(" ,\"phaseA\": ");
    Serial.print(phaseA);
    Serial.print(" ,\"measureB\": ");
    Serial.print(measureB);
    Serial.print(" ,\"phaseB\": ");
    Serial.print(phaseB);
    Serial.print(" ,\"found\": ");
    Serial.print(found);
    Serial.print(" ,\"prevFound\": ");
    Serial.print(prevFound);

    if ( found == 0) {
      Serial.print(" ,\"Error\": \"0\"");
    }
    else if(found == 1) {
      prevEmitter1State = emitter1State;
      Serial.print(" ,\"Obj\": \"Left\"");
    } 
    else if(found == 2) {
      prevEmitter2State = emitter2State;
      Serial.print(" ,\"Obj\": \"Right\"");

    }
    Serial.println("}");
    prevFound = found;
    prevMeasureA = measureA;
    prevMeasureB = measureB;
    detected = false;
  }
}

void readIRSensor() {
  gTime = micros();
  pulseTime = gTime - gPrevTime;
  gPrevTime = gTime;
  detectCount++;
  detected = true;
  found = 0;
  if(emitter1State) {
    emit1Count++;
    found = 1;
  } else if (emitter2State) {
    emit2Count++;
    found = 2;
  }
  digitalWrite(PIN_LED1, HIGH);
}

void blink_IR1(int id, void * tptr) {
  if(phaseA >= BEATS) {
    phaseA = 0;
    measureA++;
  }

  if (phaseA== 1) {
    emitter1State = true;
    phaseA++;
    digitalWrite(EMITTER1_PIN, emitter1State);   // Toggle pin state
  }
  else  {
    emitter1State = false;
    digitalWrite(EMITTER1_PIN, emitter1State);   // Toggle pin state
    phaseA++;
  }
}

void blink_IR2(int id, void * tptr) {

  if(phaseB >= BEATS) {
    phaseB = 0;
    measureB++;
  }
  if (phaseB == 3) {
    emitter2State = true;
    phaseB++;
    digitalWrite(EMITTER2_PIN, emitter2State);   // Toggle pin state
  }
  else   {
    emitter2State = false;
    digitalWrite(EMITTER2_PIN, emitter2State);   // Toggle pin state
    phaseB++;
  }
}

void blinkAll(int loops)
{
  for (int ii = 0; ii < loops; ii++)
  {
    digitalWrite(PIN_LED1, HIGH);
    digitalWrite(EMITTER1_PIN, HIGH);
    digitalWrite(EMITTER2_PIN, HIGH);
    delay(250);
    digitalWrite(PIN_LED1, LOW);
    digitalWrite(EMITTER1_PIN, LOW);
    digitalWrite(EMITTER2_PIN, LOW);     
    delay(250);
  }
}

ricklon

Thu, 16 May 2013 16:20:49 +0000

I removed the portion that through out the multiple detections, and added code that detected if both sensors were true in the same measure. In that case an object was declared in front. At that point it basically works.

Would like to get rid of the extra interrupt data, but otherwise it behaves.

/*
* Fubarino Object Detection
 * Determine with one sensor which where an object is 
 * 2 - 4 IR leds
 * 1 IR Sensor
 */

//#define pinSrc 12 // used as external interrupt source
//PIN_INT1 for the FubarinoSD is Pin 0
#define pinInt PIN_INT1

#define SENSOR1_PIN 0
#define EMITTER1_PIN 12
#define EMITTER2_PIN 13
#define BEATS 6

int emmiter1_id;
int emmiter2_id;


unsigned long blink1_var;
unsigned long blink2_var;

volatile boolean emitter1State = LOW;
volatile boolean emitter2State = LOW;
volatile boolean prevEmitter1State = LOW;
volatile boolean prevEmitter2State = LOW;

volatile boolean detected = LOW;
volatile boolean e1detected = LOW;
volatile boolean e2detected = LOW;

volatile unsigned long emit1Count = 0;
volatile unsigned long emit2Count = 0;
volatile unsigned long detectCount = 0;

volatile int phaseA = 0;
volatile int phaseB = 0;
volatile int prevPhaseA = -1;
volatile int prevPhaseB = -1;
volatile int measureA = 0;
volatile int measureB = 0;
volatile int prevMeasureA = -1;
volatile int prevMeasureB = -1;


//Prototypes
void blink_emitter1(int id, void * tptr);
void blink_emitter2(int id, void * tptr);
void readIRSensor();
void blink(int);

void setup() {
  Serial.begin(115200);
  delay(2000);
  // initialize the digital pin as an output.
  // Pin PIN_LED1 has an LED connected on most Arduino boards:
  pinMode(pinInt, INPUT);
  //debugging LED, shows when pulse found
  pinMode(PIN_LED1, OUTPUT);
  digitalWrite(PIN_LED1, HIGH);
  pinMode(SENSOR1_PIN, INPUT);
  pinMode(EMITTER1_PIN, OUTPUT);
  pinMode(EMITTER2_PIN, OUTPUT);

  digitalWrite(EMITTER1_PIN, LOW);
  digitalWrite(EMITTER2_PIN, LOW);    
  //blink before timers and interrups
  blinkAll(6);

  attachInterrupt(1, readIRSensor, RISING);
  emmiter1_id = createTask(blink_emitter1, 13, TASK_ENABLE, &blink1_var);
  emmiter2_id = createTask(blink_emitter2, 13, TASK_ENABLE, &blink2_var);

}

void loop() {
  digitalWrite(PIN_LED1, LOW);
  if (detected) {      
    Serial.print("{ \"IRDetect\": ");
    Serial.print(detectCount);
    Serial.print(" ,\"measureA\": ");
    Serial.print(measureA);
    Serial.print(" ,\"measureB\": ");
    Serial.print(measureB);
    Serial.print(" ,\"phaseA\": ");
    Serial.print(phaseA);
    Serial.print(" ,\"phaseB\": ");
    Serial.print(phaseB);
    Serial.print(" ,\"Emmit1\": ");
    Serial.print((int)emitter1State);
    Serial.print(" ,\"prevEmmit1\": ");
    Serial.print((int)prevEmitter1State);
    Serial.print(" ,\"count\": ");
    Serial.print(emit1Count);
    Serial.print(" ,\"Emmit2\": ");
    Serial.print((int)emitter2State);
    Serial.print(" ,\"prevEmmit2\": ");
    Serial.print((int)prevEmitter2State);
    Serial.print(" ,\"count\": ");
    Serial.print(emit2Count);


    if(emitter1State) {
      prevEmitter1State = emitter1State;
      Serial.print(" ,\"Obj\": \"Right\"");
    } 
    if (prevMeasureA == measureA) {
      if (e1detected && e2detected)
      {
        Serial.print(" ,\"Obj\": \"Front\"");
      }
    } 
   if(emitter2State) {
      prevEmitter2State = emitter2State;
      Serial.print(" ,\"Obj\": \"Left\"");
    } 
    Serial.println("}");
    prevMeasureA = measureA;
    prevMeasureB = measureB;
    prevPhaseA = phaseA;
    detected = false;
  }

}

void readIRSensor() {
  digitalWrite(PIN_LED1, HIGH);
  if(emitter1State) {
    emit1Count++;
    detectCount++;
    detected = true;
    e1detected = true;
  }  
  else if (emitter2State) {
    emit2Count++;
    detectCount++;
    detected = true;
    e2detected = true;
  }
}

void blink_emitter1(int id, void * tptr) {
  if(phaseA >= BEATS) {
    phaseA = 0;
    measureA++;
    e1detected = false;
  }

  if (phaseA== 1) {
    emitter1State = true;
    phaseA++;
    digitalWrite(EMITTER1_PIN, emitter1State);   
  }
  else  {
    emitter1State = false;
    phaseA++;
    digitalWrite(EMITTER1_PIN, emitter1State);   

  }
}

void blink_emitter2(int id, void * tptr) {
  if(phaseB >= BEATS) {
    phaseB = 0;
    measureB++;
    e2detected = false;
  }
  if (phaseB == 3) {
    emitter2State = true;
    phaseB++;
    digitalWrite(EMITTER2_PIN, emitter2State);  
  }
  else  
  {
    emitter2State = false;
    phaseB++;
    digitalWrite(EMITTER2_PIN, emitter2State);  
  }

}

void blinkAll(int loops)
{
  for (int ii = 0; ii < loops; ii++)
  {
    digitalWrite(PIN_LED1, HIGH);
    digitalWrite(EMITTER1_PIN, HIGH);
    digitalWrite(EMITTER2_PIN, HIGH);
    delay(250);
    digitalWrite(PIN_LED1, LOW);
    digitalWrite(EMITTER1_PIN, LOW);
    digitalWrite(EMITTER2_PIN, LOW);     
    delay(250);
  }
}