Detecting Microsecond Pulse Duration

Hello.

I have a sensor that generates pulse when it gets triggered. Pulse width is 3us - 150us. I need to measure pulse width. I wrote following code:

float startTime[4];
float stopTime[4];
int counter=0;
int b=0;
int i=0;
int d=0;
unsigned long duration;
void setup() {
  Serial.begin(9600);
  pinMode(13,OUTPUT);
  pinMode(4,INPUT);
}

void loop() {


//wait until pin4 high
   startTime[i] = micros();
while(digitalRead(4) == 1){
  digitalWrite(13,HIGH);
 if(digitalRead(4) == 0){
    stopTime[i] = micros();
    counter++;
    i++;
      break;
    }
    }

 if(counter%5 == 0 && counter !=0){
    if(b==0){
    Serial.println(counter);
    for(; d<5 ; d++){
    Serial.println(stopTime[d] - startTime[d]);
    }

    
    b++;
    i=0;
    }
  }
}

Here it waits to detect digital pin 4 to high. When it goes high, startTime is recorded (the exact time before program goes into the while loop). And it waits until pin goes low. When pin low it records stopTime and breaks. At the buttom of this code I just print pulse width.

When I try this code with function generator (lets say 10kHz %50 duty cycle (100us)). Sometimes I get very different values. I mean it should print 100 us for 5 times but it prints like :

150
100
200
0
50
My question is if it is the right way to measure the pulse width accurately or should I need to try something else since it is very short pulse?

Which Arduino board do you use ?

I'm afraid that detecting a pulse of 3µs is not possible in software.

There is a standard Arduino "pulseIn()" function to measure pulse duration. It's limited to a minimum pulse width of 10 us because of the limited speed of port operations using the standard library.

It would be possible to write a tighter function for a specific microcontroller using direct port manipulation which would allow timing on the order of a single microsecond, but that would get relatively complicated.

With AVR-based Arduinos, it is no problem to measure pulse widths in that range, using a hardware timer.

To see how that is done, study this Timer tutorial. Scroll down to “Measuring a duty cycle using the input capture unit”.

Why are startTime and stopTime defined as floats rather than long integers?

Your code sets stopTime as soon as pin 4 goes low. But it's not clear to me how startTime is set by high on pin 4.

S.

millis() and micros() return unsigned long, your time variables should be unsigned long also.

The ESP32 has a module called the PCNT, Pulse Counter.

The PCNT is able to detect a pulse down to:

For the counter not to miss any pulses, the pulse duration should be longer than one APB_CLK cycle (12.5 ns). The pulses are sampled on the edges of the APB_CLK clock and may be missed, if fall between the edges. This applies to counter operation with or without a filter.

The PCNT can detect the width of the pulse through proper mode settings

esp_err_tpcnt_set_mode(pcnt_unit_tunit, pcnt_channel_tchannel, pcnt_count_mode_tpos_mode, pcnt_count_mode_tneg_mode, pcnt_ctrl_mode_thctrl_mode, pcnt_ctrl_mode_tlctrl_mode)
Set PCNT counter mode.

Return
ESP_OK Success

ESP_ERR_INVALID_STATE pcnt driver has not been initialized

ESP_ERR_INVALID_ARG Parameter error

Parameters
unit: PCNT unit number

channel: PCNT channel number

pos_mode: Counter mode when detecting positive edge

neg_mode: Counter mode when detecting negative edge

hctrl_mode: Counter mode when control signal is high level

lctrl_mode: Counter mode when control signal is low level

The PCNT can be set to call an ISR. The ISR can be triggered at the start and stop of the pulse. A timer can be used to get pulse length. The ESP32 cycle clock is in picoseconds for pulse width timings.

Thanks a lot for all answers. I will try port manipulation as MrMark said.

jremington:
With AVR-based Arduinos, it is no problem to measure pulse widths in that range, using a hardware timer.

To see how that is done, study this Timer tutorial. Scroll down to “Measuring a duty cycle using the input capture unit”.

What do you mean by AVR-based Arduinos. Is it like STM32 ?

srturner:
Why are startTime and stopTime defined as floats rather than long integers?

Your code sets stopTime as soon as pin 4 goes low. But it’s not clear to me how startTime is set by high on pin 4.

S.

Thanks I changed it to long integers. In my code startTime always updating but what is important for me is the exact time before first while loop. It worked but I had some issues. So I changed the code as :

 if(analogRead(A7)*5.0/1023.0 >= 2){
  startTime[i] = micros();
  while(1){
      if(analogRead(A7)*5.0/1023.0 < 2){
      sonuc[i] = micros()-startTime[i];
      counter++;
      i++;
      a=0;
      break;
      }
    }
 }

But with this code even if I send 200us pulse. I can’t always detect it. So I might try bare metal programming to directly control pins.

What do you mean by AVR-based Arduinos. Is it like STM32 ?[color=#222222][/color]

AVR refers to the 8-bit Atmel microcontrollers used in the Arduino Uno, Mega, Nano, etc.

STM32 is one example of a 32-bit ARM microcontroller. The ARM architecture is licensed by ARM Ltd, so one will find ARM cores in virtually every MCU vendor’s product line.

Why did you switch from digital to analog reads? If your input signal reliably rises to the logic high state of the device you're using, I'd go back to digital read. I may not be understanding the whole purpose of your code, but I'd try something like:

void setup() {
  unsigned long startTime = 0;
  unsigned long stopTime = 0;
}

void loop() {
  while (digitalRead(7)) {  //wait for low on pin 7
  }
  while (!digitalRead(7)) { //wait for high on pin 7
  }
  startTime = micros();     //high detected, set start time

  while (digitalRead(7)) {  //wait for low on pin 7
  }
  stopTime = micros();    //low detected, set stop time

  Serial.print(stopTime - startTime);
  delay(100); //or whatever
}

(Warning: untested and incomplete armchair code fragment. Worth what you paid for it...)
S.

A single analog reading takes over 100 µs so no use for these signals.

The input capture feature will be helpful, get up to 62.5 ns resolution in pulse length, 50 ns if replace the crystal with a 20 MHz.

The newer AVR 0 and 1 series have more advanced timers, they can measure pulse durations in hardware. Even easier.

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