Can't detect short pulses with GPIO

I am thoroughly puzzled about a problem I’m having with a P1. I have it mounted on a custom board, and I’m feeding it a pulse train. This train is modulated with a square wave at 40khz (25 us period), and I need to detect exactly when the first rising edge is. If I feed it a short train (1.5ms) and wait for it to pick it up with this pseudocode,

//trigger the pulse train
digitalWrite(D0, HIGH);
while(pinReadFast(D3) == 0 && i<200000){
            i++;
}
digitalWrite(D0,LOW);

It doesn’t even register that the pin’s state has changed. I’ve timed the execution of the loop, and it seems to be around 0.07 us; more than enough to capture the 25us pulse.

I’ve tried turning off wifi and noInterrupts() to see if it’s some kind of background process, but I still haven’t found the problem. I’ve tried linking an interrupt to the pin’s state. Same thing. Just too slow.

Anyone know what’s going on?

Do you have “pinMode(D3, INPUT)” in your setup()?

Are you using "digitalWrite(D3,…)"in your code to turn on and off the internal pullup? If so then see this

and change your code to use pinMode(D3, INPUT_PULLUP)

1 Like

With something that fast I think you need to use an interrupt.

With your code above you are driving the pin HIGH, how do you expect the line to get pulled LOW?
If you manage, you might strain the GPIO or your ext signal source.

Next pinReadFast() assumes the pinMode() to be set for INPUT (no sanity check - unlike digitalRead()).

I’d definetly go for an interrupt and avoid polling as the code snippet shows.

@Arthur_dent_42_121, @ScruffR and @Bspranger are correct about using an interrupt. Polling is often a hit and miss method and requires that you sample at least twice as fast as the rate of change of the signal (Nyquist) or an edge may be missed. Using interrupts guarantees the capture of the edge. :smile:

I do have the pin set as an input, and if I send a really long signal it will pick it up eventually. Sorry for the confusing digitalWrite(), that’s to trigger the pulse train from an external source.

The digitalWrite was in error; it’s supposed to be on pin D0, not D3. In the actual code, I have it on the correct pin. Sorry about the confusion.

I have tried an interrupt, and I’m still getting the weird behaviour. The interrupt will trigger only after like 3ms.

I’m pretty sure that I’m sampling at least 100 times faster than the switching frequency, so that shouldn’t be a problem in this case. However, I have also tried an interrupt, and still had the same problem. I’ll get some scope traces so it’s easier to see what’s going on.

Could you show your ISR, when you got the 3ms delay.

Interrupts should be way faster than that and I’m pretty sure it’s rather the measuring setup than a misbehaviour of the interrupt :wink:

Here’s a scope trace of the polling method. The blue trace goes high when I start polling, and then the thing just completely misses the ~5000 20us pulses:

The code that I used for the interrupt;

setup(){
pinMode(D3, INPUT);
attachInterrupt(D3, interruptHandler, CHANGE);
}

void interruptHandler(){
    digitalWrite(D0, LOW); //Just for testing
}

I tried the interrupt method again, to no avail. Here’s the scope trace:

.

Again, you can see that the interrupt just didn’t catch any of the pulses.

If I wait for a bit, on perhaps the 10th loop of this, it’ll finally catch a pulse:

But it caught it in the middle of a pulse, not at the beginning.

Wierd.

Have a try with this

SYSTEM_MODE(MANUAL)

volatile uint32_t ms;

void setup()
{
  pinMode(D0, OUTPUT);
  digitalWriteFast(D0, HIGH);

  pinMode(D3, INPUT);
  attachInterrupt(D3, interruptHandler, RISING);
}

void loop()
{
  if (ms && (micros() - ms > 10000))
  {
    digitalWriteFast(D0, HIGH);
    ms = 0;
  }
}

void interruptHandler() 
{
  if (ms) return;
  digitalWriteFast(D0, LOW); //Just for testing
  ms = micros();
}

Could you also zoom in a bit on your signal train?

Thanks for taking the time to help with this. Much appreciated.

I tried that code, I had to put a small delay in the main loop to trigger the pulses, hope that didn’t affect anything:

//trigger pulses, takes a few ms, blocking call
if (ms && (micros() - ms > 10000))
   {
     digitalWriteFast(D0, HIGH);
     ms = 0;
   }

That worked better than before, but not as well as I need:

As you can see, it again tripped the interrupt in the middle of the pulse, not the first rising edge. Here’s a zoomed image:

And now this is just getting crazy. I put this code on it in the main loop:

delay(100);
//trigger pulse
digitalWrite(D0,HIGH); // just for timing testing
delayMicroseconds(1);
for(int i=0;i<400000;i++){
    digitalWriteFast(D0, pinReadFast(D3));
}
digitalWrite(D0,HIGH);
delayMicroseconds(1); // just for timing testing

Literally just setting one pin to the other pin’s state. I timed this, and the full 400,000 samples run in 57 ms, so 0.11 us per sample; 105 samples per pulse. Should be impossible to miss the pulse. Yet it misses the >5000 pulses in the train almost all the time:

It should be copying the yellow trace exactly on the blue trace.

Weird.

I’m also very confused with your results.

I’ve just tested the timing of my code above and get a latency of avg. 360ns between triggering the interrupt and D0 responding.

So I’ve no idea what’s happening on your end.

Now I try to whip up a “copy-input-to-output” ISR and have a look at my scope.


With this code

SYSTEM_MODE(MANUAL)

volatile uint32_t ms;

void setup()
{
  pinMode(D0, OUTPUT);
  digitalWriteFast(D0, HIGH);

  pinMode(D3, INPUT_PULLDOWN);
  attachInterrupt(D3, interruptHandler, CHANGE);
}

void loop() { }

void interruptHandler() 
{
  digitalWriteFast(D0, pinReadFast(D3));
}

I get a signal-response delay of 800ns and can pick up and mirror a 200kHz 20% to 80% duty cycle square wave. When I change the duty cycle below 20% or above 80% the interrupt seems to stop being triggered.


Just a very heretic thought :sunglasses: Could you double check if you’ve got the correct pins wired - I just tried another Photon and got odd readings, till I relized I was checking triggering D2 and had D3 floating (INPUT instead of INPUT_PULLDOWN) :stuck_out_tongue_winking_eye:

    SYSTEM_MODE(MANUAL)

void setup()
{
    pinMode(DAC1, OUTPUT); 
    analogWrite(DAC1, 400); // just setting something up for the pulses
  pinMode(D0, OUTPUT);
  digitalWriteFast(D0, HIGH);
  pinMode(D1, OUTPUT);
  pinMode(D3, INPUT);
  attachInterrupt(D3, interruptHandler, CHANGE);
}

void loop()
{
    delay(100); // triggering some pulses
    for(int i=0;i<100;i++){
      digitalWriteFast(D1, HIGH);
      delayMicroseconds(12);
      digitalWriteFast(D1, LOW);
      delayMicroseconds(12);
  }
}

void interruptHandler() 
{
  digitalWriteFast(D0, pinReadFast(D3));
}

???

I don’t even.

DAC1 will give you an analog voltage and not a PWM signal, if this is your intention with

    pinMode(DAC1, OUTPUT); 
    analogWrite(DAC1, 400); // just setting something up for the pulses

And how have you wired your setup (e.g. which pin triggers D3)?

OK, I’ve now tried out your code from above (D1 -> D3 -> scope Ch1, D0 Ch2) and I get a good copy of D1 on D0 with a delay of 1µs


Have you tested with my code?

Yup, the code I tested above was yours, with my triggering code.

In regards to your previous comment, I have confirmed that the pin I’m using is the right one, and I’ve tried using a different one.

D1 drives a h-bridge at 40khz, sending an ultrasonic ping out of a transducer. This bounces off a wall and goes into another transducer. Then, I have a notch filter and a two stage amplifier that brings the recieved pulse to 3.3v amplitude so we can detect it on D3 digitally. DAC1 sets the threshold on a comparator on the receiver.

I’m very puzzled about this and since I can even reduce to delayMicroseconds(1); and still get a near perfect mirror response on my Photon(s), I’d have to hand this over to someone who can test this on a P1 (or have you got a Photon you could repeat your tests?)

Maybe @peekay123 or @BDub can help out with a P1.