Go Down

Topic: Measuring PWM width with pulsein() (Read 187 times) previous topic - next topic

Schoolboymoe

Hi
I'm in a project where we have a PWM signal with 3 pulses containing information is coming in, I would want to store them and then print them out. The pulses are 22-88ms in length. I have recently discovered pulseIn() and that would be a smart way to record the "HIGH"-time of the pulses?
Any recommendations on how to program this?

septillion

Problem with pulsIn() is it's a blocking function. So while waiting fr the pulse to finish you can't do anything.

22-88ms is a reasonable length. Just connect them to an external interrupt (or use pin change interrupt even) and use millis() to determine the length of the pulse (if resolution is sufficient). millisWhenFalling - millisWhenRising = pulsLength.
Use fricking code tags!!!!
I want x => I would like x, I need help => I would like help, Need fast => Go and pay someone to do the job...

NEW Library to make fading leds a piece of cake
https://github.com/septillion-git/FadeLed

wvmarle

At times of 22-88 ms I'd prefer to use micros() to do the timing, unless you know what times to expect and just have to distinguish between them.

pulseIn() produces microseconds. It's indeed blocking. It will do the job for you but you can't do anything else during the time the pulseIn() runs.

You also probably don't know when the pulse arrives. That's another issue. PulseIn() times out after 1 second (can be extended - see documentation) but if you do so you're having the processor sit there waiting. If you set up an interrupt you don't have to worry about sitting and waiting for the pulse to arrive.
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

Schoolboymoe

At times of 22-88 ms I'd prefer to use micros() to do the timing, unless you know what times to expect and just have to distinguish between them.

pulseIn() produces microseconds. It's indeed blocking. It will do the job for you but you can't do anything else during the time the pulseIn() runs.

You also probably don't know when the pulse arrives. That's another issue. PulseIn() times out after 1 second (can be extended - see documentation) but if you do so you're having the processor sit there waiting. If you set up an interrupt you don't have to worry about sitting and waiting for the pulse to arrive.
This one I could manage, how would I then set that up? Setting a trigger when it's RISING and FALLING?
I'm using an arduino uno and use pin 13 as a input, would I have to use pin 2 instead?
Sorry for being a noob, I'm very new to programming :)

wvmarle

No interrupts example:

Code: [Select]

unsigned long pulseStart;

void loop() {
  if (digitalRead(pulsePin) && havePulse == false) { // Start of pulse.
    havePulse = true;
    pulseStart = micros(); // millis() may do as well.
  }
  if (digitalRead(pulsePin) == LOW && havePulse) { // End of pulse.
    havePulse = false;
    pulseReceived = true;
    pulseLength = micros() - pulseStart;
  }
  if (pulseReceived) {
    dealWithIt();
  }
  doOtherStuff();
  doMoreStuff();
}


Probably good enough for your pulse length, but you must make sure the other functions are non-blocking, so no delay() or so.
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

Schoolboymoe

No interrupts example:

Code: [Select]

unsigned long pulseStart;

void loop() {
  if (digitalRead(pulsePin) && havePulse == false) { // Start of pulse.
    havePulse = true;
    pulseStart = micros(); // millis() may do as well.
  }
  if (digitalRead(pulsePin) == LOW && havePulse) { // End of pulse.
    havePulse = false;
    pulseReceived = true;
    pulseLength = micros() - pulseStart;
  }
  if (pulseReceived) {
    dealWithIt();
  }
  doOtherStuff();
  doMoreStuff();
}


Probably good enough for your pulse length, but you must make sure the other functions are non-blocking, so no delay() or so.
I guess I should declare havePulse and pulseReceived as Booleans?

wvmarle

Yes; and pulseLength as unsigned long. It's just an example, there's more undeclared stuff in there :)
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

Schoolboymoe

Hi guys, tested out this code, the problem is the only thing it prints out is 0's.

const int pulsePin = 2;
volatile unsigned long elapsed;
volatile unsigned long StartTime;
volatile unsigned long StopTime;


void setup() {
  pinMode(pulsePin, INPUT);
  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(pulsePin), rising, RISING);
  attachInterrupt(digitalPinToInterrupt(pulsePin), falling, FALLING);
}

void loop() {
  Serial.println(StartTime);
  Serial.println(elapsed);
 
}

void rising() {
  StartTime=micros();
}
void falling() {
  StopTime=micros();
  elapsed=StopTime-StartTime;
}

wvmarle

That probably means you didn't see a pulse yet, as you're printing StartTime and elapsed all the time, pulse or no pulse.

As falling comes second, you should set a flag there that shows loop() that a pulse has been detected. For testing, do the same with rising(). Then print based on those flags.

Code: [Select]

const int pulsePin = 2;
volatile unsigned long elapsed = 0;
volatile unsigned long StartTime;
volatile unsigned long StopTime;
volatile bool sawRisingEdge = false;

void setup() {
  pinMode(pulsePin, INPUT);
  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(pulsePin), rising, RISING);
  attachInterrupt(digitalPinToInterrupt(pulsePin), falling, FALLING);
}

void loop() {
  if (sawRisingEdge) { // Only print when a rising edge has been detected.
    sawRisingEdge = false;
    Serial.print (F("Detected rising edge at micros() = "));
    Serial.println(StartTime);
  }
  if (elapsed > 0) { // Only print when a subsequent falling edge has been detected.
    Serial.print(F("Falling edge followed "));
    noInterrupts();
    unsigned long e = elapsed; // always read >2 byte values with interrupts switched off.
    interrupts();
    Serial.print(e);
    Serial.println(F("┬Ás later."));
    elapsed = 0;
  }
}

void rising() {
  StartTime=micros();
  sawRisingEdge = 0;
}

void falling() {
  StopTime=micros();
  elapsed=StopTime-StartTime;
}
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

septillion

#9
Feb 14, 2018, 01:06 pm Last Edit: Feb 14, 2018, 01:56 pm by septillion
You can only assign a single function / mode to an interrupt.

So this:
Code: [Select]
attachInterrupt(digitalPinToInterrupt(pulsePin), rising, RISING);
  attachInterrupt(digitalPinToInterrupt(pulsePin), falling, FALLING);

only attaches the falling interrupt...

Or use a single call back for rising and falling (CHANGE) or change the mode in the interrupt again.
Use fricking code tags!!!!
I want x => I would like x, I need help => I would like help, Need fast => Go and pay someone to do the job...

NEW Library to make fading leds a piece of cake
https://github.com/septillion-git/FadeLed

wvmarle

Oh, didn't know that. Makes sense, though.
Then OP can better use a state check: digitalRead() in the ISR, base action on that. Should be fast enough to do in an ISR.
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

Go Up