Odd interrupt pulse width measure issue

The Arduino Uno is manly being used to send out trigger pulses with a variable widths and receive strobe pulses and measure their widths. I’ve been rewriting it to remove delay() and other blocking code, but I’m having an issue with the ISR to measure the strobe pulse. When the Arduino immediately receives a strobe pulse in response to the generated trigger the reported strobe width (duration) is actually the triggers width.

Relevant code:

#define TRIGGER_OUT 13
#define STROBE 2


unsigned long trig_start = 0;
unsigned long trig_width = 0;
unsigned long trig_last = 0;
unsigned long current_micros = 0;

int num_triggers = 0;

boolean pos_trigger = true;
boolean pos_strobe = true;
boolean con_trig = false;            //true if currently running constant triggers
boolean seq_trig = false;            //true if currently running a trigger sequence
boolean trigger = false;             //true if trigger sequence is mid pulse
volatile boolean mid_pulse = false;  //true if currently measuring strobe pulse

int trigger_width = 2;
boolean white = false;

//For pwm of strobe signal
volatile unsigned long duration = 0;
volatile unsigned long pulse_start = 0;
volatile unsigned long pulse_end = 0;
// Interrupt Service Routine measures strobe pulse
void strobePulse ()
{
if (mid_pulse)
  {
    pulse_end = micros();
    duration = pulse_end - pulse_start;
    mid_pulse = false;
    pulse_end = 0;
    pulse_start = 0;
    detachInterrupt(digitalPinToInterrupt (STROBE));
  }
else if (((digitalRead(STROBE) == HIGH) && (pos_strobe)) || ((digitalRead(STROBE) == LOW) && (!pos_strobe)))
  {
    pulse_start = micros();
    mid_pulse = true;    
  }
}

 
void loop()
{
 if (Serial.available())
 {
 while (Serial.available() > 0)
 processIncomingByte(Serial.read());
 }
 current_micros = micros();
 if (trigger && (current_micros - trig_start >= trig_width))  // monitors time since last trigger started and stops trigger after trig_width
  {
   if (pos_trigger) 
        digitalWrite(TRIGGER_OUT, LOW);
   else
        digitalWrite(TRIGGER_OUT, HIGH);
        
  trigger = false;
  trig_last = current_micros;  //saves last trigger end time for trigger delay purposes
  }
  
 if ((!trigger) && (con_trig || seq_trig) && (current_micros - trig_last >= 1000000UL))   // 1 second delay between constant and sequence trigger pulses
  {
    if (con_trig)
      triggerPulse(trigger_width);
    else if ((seq_trig) && (num_triggers-- > 0))
    {
      triggerPulse(2);
      if (num_triggers <= 0)
        seq_trig = false;
    }
  }

 }
 
 void triggerPulse(int width)
{
  trig_width = ((unsigned long)width * 1000UL);
 if (pos_trigger)
 {
 digitalWrite(TRIGGER_OUT, HIGH);
 }
 else
 {
 digitalWrite(TRIGGER_OUT, LOW);
 }
 trig_start = micros();  //start time of trigger 
 trigger = true;         //tells main loop trigger is active
}

Not every strobe is measured, the interrupt is turned on with a serial command with:

attachInterrupt(digitalPinToInterrupt (STROBE), strobePulse, CHANGE);  //enable interrupt on state change of the STROBE pin

trigger strobe variations I’ve tried.

trigger: 10ms strobe: 20ms duration result: ~10000us

trigger: 20ms strobe: 50ms duration result: ~20000us

trigger: 20ms strobe: 10ms duration result: ~10000us
(so the result is right if the trigger is longer than the strobe, but that never really happens in our use case)

Also if I trigger our device from another source the arduino measures the strobe pulse accurately, even if the arduino is still generating an unused trigger. I suspect this is because they arduino trigger doesn’t line up with the strobe in this case. So it seems the real issue has something to do with the trigger and strobe starting very close together.

Any ideas on what’s going wrong?

Post your code (in code tags) all in one shot. Why make us stitch together all those pieces before pasting it into the IDE? Post a COMPLETE code, it MUST compile.

Sorry, there’s a lot of extra bits that aren’t relevant to the issue. I thought it would be better to skip those parts. The complete code is attached (it was over 9000 character limit).

sketch_feb20a.ino (13.1 KB)

/Users/john/Documents/Arduino/sketch_dec06a/sketch_dec06a.ino: In function 'void increment_decrement(String, int, uint16_t&)':
/Users/john/Documents/Arduino/sketch_dec06a/sketch_dec06a.ino:261:18: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
   if (brightness == -1)brightness = 0;

That 'if' will never be true so brightness will underflow and go wonky.

johnwasser:

/Users/john/Documents/Arduino/sketch_dec06a/sketch_dec06a.ino: In function 'void increment_decrement(String, int, uint16_t&)':

/Users/john/Documents/Arduino/sketch_dec06a/sketch_dec06a.ino:261:18: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  if (brightness == -1)brightness = 0;



That 'if' will never be true so brightness will underflow and go wonky.

Thanks for the catch. I'll fix that up.

I don't think that will effect my pulse width problem though as I haven't been doing anything with the LEDs in my testing.

FadedSun:
Sorry, there's a lot of extra bits that aren't relevant to the issue.

If that's the case, it's best to post an MCVE. That's the smallest possible code that actually compiles and demonstrates the problem. Cut out all the unrelated, extraneous noise.

Ok here’s my MCVE version that exhibits the same behavior.

//  PIN Table
//
//  Arduino Pin     Function
//  D7          R1 Coil     Light Panel power
//  D6          R2 Coil     device power
//  D13         Trigger Out   To device
//  D2          Strobe In   From device
//
//  STROBE Commands
//  Command   Value Description
//  SS      None  Start a 5 second sequence to measure the strobe signal
//  SR      None  Read the pulse width measurement of the strobe signal

#define Relay1 7
#define Relay2 6

#define TRIGGER_OUT 13
#define STROBE 2

unsigned long trig_start = 0;
unsigned long trig_width = 10000;   //trigger width in microseconds
unsigned long trig_last = 0;
unsigned long current_micros = 0;

int num_triggers = 0;

boolean trigger = false;             //true if trigger sequence is mid pulse

//For pwm of strobe signal
volatile unsigned long duration = 0;
volatile unsigned long pulse_start = 0;
volatile unsigned long pulse_end = 0;
volatile boolean mid_pulse = false;  //true if currently measuring strobe pulse

// how much serial data we expect before a newline
const unsigned int MAX_INPUT = 25;


void setup()
{
  Serial.begin(9600); // opens serial port, sets data rate to 9600 bps

  pinMode(Relay1, OUTPUT);    
  pinMode(Relay2, OUTPUT);
  pinMode(TRIGGER_OUT, OUTPUT);
  pinMode(STROBE, INPUT);

  trig_last = micros();
  
  digitalWrite(TRIGGER_OUT, LOW);

  //**********Default States**********
  digitalWrite(Relay1,HIGH);
  digitalWrite(Relay2,LOW);

}

// Interrupt Service Routine measures strobe pulse
void strobePulse ()
{
if (mid_pulse)
  {
    pulse_end = micros();
    duration = pulse_end - pulse_start;
    mid_pulse = false;
    pulse_end = 0;
    pulse_start = 0;
    detachInterrupt(digitalPinToInterrupt (STROBE));
  }
else if (digitalRead(STROBE) == HIGH)
  {
    pulse_start = micros();
    mid_pulse = true;    
  }
}

 
void loop()
{
  if (Serial.available())
  {
    while (Serial.available() > 0)
      processIncomingByte(Serial.read());
  }
  
 current_micros = micros();
 
 if (trigger && (current_micros - trig_start >= trig_width))  // monitors time since last trigger started and stops trigger after trig_width
  {
  digitalWrite(TRIGGER_OUT, LOW);      
  trigger = false;
  trig_last = current_micros;  //saves last trigger end time for trigger delay purposes
  }
  
 if ((!trigger) && (current_micros - trig_last >= 1000000UL))   // 1 second delay between trigger pulses
  {
    digitalWrite(TRIGGER_OUT, HIGH);
    trig_start = current_micros;  //start time of trigger 
    trigger = true;         //tells main loop trigger is active
  }

 }
 

void processIncomingByte(const byte inByte)
{
  static char input_line[MAX_INPUT];
  static unsigned int input_pos = 0;

  switch (inByte)
  {

  case '\n':   // end of text
    input_line[input_pos] = 0;  // terminating null byte

    // terminator reached! process input_line here ...
    process_data(input_pos, input_line);
    // reset buffer for next time
    memset(input_line, 0, MAX_INPUT);
    input_pos = 0;
    break;

  case '\r':   // discard carriage return
    break;

  default:
    // keep adding if not full ... allow for terminating null byte
    if (input_pos < (MAX_INPUT - 1))
    {
      input_line[input_pos++] = inByte;
      //command += inByte;
    }
    break;

  }  // end of switch

} // end of processIncomingByte 


void process_data(int len,const char * data)
{

  String value;
  String command = String(data[0]) + String(data[1]);

  if (len == 5)value = String(data[2]) + String(data[3]) + String(data[4]);
  else if (len == 7)value = String(data[2]) + String(data[3]) + String(data[4]) + String(data[5]) + String(data[6]);

  if (command == "SS")
  {
    attachInterrupt(digitalPinToInterrupt (STROBE), strobePulse, CHANGE);  //enable interrupt on state change of the STROBE pin
//    measure();
  }
  else if (command == "SR")
  {
    Serial.println(String(duration)); //write last measuered pulse to serial port
    duration = 0;
  }
  
}  // end of process_data

This code provides a constant 10ms duration trigger at 1 second intervals. SS command enables strobe measurement and SR writes the last recorded pulse width out.

Results:

trigger: 10ms
strobe: 20ms
measured pulse : 9980 (microseconds)

trigger: 10ms
strobe: 5ms
measured pulse : 4984 (microseconds)

So somehow the ISR is measuring whatever pulse is shorter instead of only interrupting on the STROBE change.

Your description of both what you’re trying to do and the problem you’re having is totally unclear. What signal are your trying to measure the width of? What is generating this signal? Do you have independent measurement of this signal’s actual width so that you know if the results produced by your code are right or wrong?

gfvalvo:
Your description of both what you’re trying to do and the problem you’re having is totally unclear. What signal are your trying to measure the width of? What is generating this signal? Do you have independent measurement of this signal’s actual width so that you know if the results produced by your code are right or wrong?

The Arduino is generating a trigger pulse on pin 13. An external device receives that pulse and sends back a "strobe" pulse to the Arduino as an input on pin 2. I'm trying to measure the width of the received pulse on pin 2 to determine if it's within a tolerance. The width of the return pulse is dependent on external factors and is not linked to the width of the trigger pulse on pin 13 (though there's nothing preventing them from being the same width at times).

The issue I'm having is that the ISR seems to be measuring the trigger pulse width (generated by the Arduino) instead of the input strobe width. This happens whenever the trigger pulse has a shorter duration than the strobe pulse. As the interrupt is only supposed to trigger on a state change for the strobe (pin 2), I don't understand how the trigger pulse falling edge would ever enter the ISR.

I do have both the strobe and trigger hooked up to a oscilloscope and can verify that the pulse widths are as expected.

Good description, thanks. Before looking at your code again, let me throw this out there: using one of the Arduino’s hardware timers might be a superior method. What is the expected min / max range of the “strobe” pulse’s width? With what precision are you trying to measure the pulse? Are you currently using Timer 1 for anything in your full code?

gfvalvo:
Good description, thanks. Before looking at your code again, let me throw this out there: using one of the Arduino’s hardware timers might be a superior method. What is the expected min / max range of the “strobe” pulse’s width? With what precision are you trying to measure the pulse? Are you currently using Timer 1 for anything in your full code?

It’s possible for strobe width to be in the 100’s of ms, but for testing purposes it really only need to be able to measure 10 and 20ms pulses. A wider range would be a bonus in case testing requirements change in the future. Precision is a tough one to answer right now, as there has been talk of loosening the criteria. I would say about ± 5% will be fine, the arduino seems to be capable of far more precision than we need, I believe i’m seeing less than 1% when it’s measuring correctly. I’m not using Timer 1 that I can tell, I looked into it a bit and it looks above my current knowledge :slight_smile:

As and added bit of info, attached are 2 oscilloscope shots. Yellow is the strobe line (pin 2 input) and blue is the trigger line (pin 13 output from arduino). In one image the strobe goes high almost immediately in response to the trigger. This is how a normal test would look and gives a inaccurate pulse measurement (appears to be the width of the trigger).

The second image the trigger and strobe are offset from each other. This wouldn’t normally be possible in the actual use case, but I forced it with an external trigger source for testing purposes. This DOES measure the correct STROBE pulse width. So I suspect the issue lies somewhere in the trigger and strobe activating to close together.

Thank you for your help.

Edit: sorry, re-uploaded attachments in correct format.

aysnc signals.jpg

synced signals.jpg

aysnc signals.jpg

synced signals.jpg

The code is rather poorly organized and disjointed. It doesn’t seem to coordinate very well the arming of the interrupt and the sending of the trigger pulse. Below is an outline of the technique I would use. It compiles but is untested. You’ll need to fill in the commented-out part in loop() with the proper conditions for determining when to send the trigger pulse and actually sending it. Be sure to interlock sending trigger with ‘!measurementRunning’.

Aside from that, the code for handling Serial input is not very good. Take a look at @Robin2’s Serial Input Tutorial.

volatile bool isrActive = false;
volatile bool measurementReady = false;
volatile uint32_t measurementResult;

#define STROBE 2

void setup() {
  Serial.begin(115200);
  delay(1000);
  pinMode(STROBE, INPUT);
  attachInterrupt(digitalPinToInterrupt (STROBE), strobePinISR, CHANGE);
}

void loop() {
  uint32_t localMeasurementResult;
  static bool measurementRunning = false;

/*
  if ((time to send trigger) && !measurementRunning) {
    EIFR |= INTF0;
    isrActive = true;
    // Send the trigger pulse
    measurementRunning = true;
  }
*/

  if (measurementReady && measurementRunning) {
    noInterrupts();
    localMeasurementResult = measurementResult;
    interrupts();
    Serial.println(localMeasurementResult);
    measurementRunning = false;
    measurementReady = false;
  }
}

void strobePinISR() {
  static uint32_t pulseStartTime;
  static bool foundRising = false;

  uint32_t currentMicros = micros();
  if (!isrActive) {
    return;
  }

  if (PIND & (1 << PD2)) {
    pulseStartTime = currentMicros;
    foundRising = true;
  } else {
    if (foundRising) {
      measurementResult = currentMicros - pulseStartTime;
      foundRising = false;
      isrActive = false;
      measurementReady = true;
    }
  }
}

Thank you for the code, sorry it took a bit to respond. I was pulled away on other things and it also took me a bit to research and understand some of the bits of your code (I'm still very new with the Arduino).

I implemented the direct port manipulation for both the strobe reading and the trigger commands. This improved the consistency of trigger pulse and strobe measurements. I also implemented clearing the interrupt flag, I was unaware that flag could be sitting there and triggering the interrupt as soon as it was enabled.

I did finally get my sketch working as needed, but I think the issue was less to do with the code and more a design issue. The device was outputting a 3.3V strobe, which I understand it still marginally readable on the 5V Arduino input. But on closer examination it appears there was a tiny voltage dip in the strobe signal whenever the trigger changed state. I'm not sure yet if the source of that is the Arduino or the device being tested. But it seems it was enough of a change that the already low 3.3V signal read as low to the arduino and triggers the ISR. This is how I was ending up with the triggers pulse measured instead of the strobes.

I solved this by temporarily disabling interrupts and clearing the interrupt state when the trigger changes state.