Teensy 3.2 and laser rangefinder -- problem with interrupt

kludahk

Member
Hello,

I'm using a Teensy 3.2 to receive data from a LightWare SF30/C laser rangefinder (datasheet here). The board receives data (distance measurements) over serial (pins 0, 1) without trouble. I can verify this by reading them in loop(). The board also drives a motor through a Pololu motor driver from pin 23. This also works fine.

The laser has a sync wire which is pulled low at the instant a measurement is taken, which should enable precise timing. I'm attempting to trigger an interrupt when this occurs, in order to capture the time. There's a screen capture from a scope on page 9 of the datasheet, showing that line is pulled low for about 5us (I think?) I don't have a scope, but I can detect the signal by reading from the wire using analogRead(); periodically the level goes to 1. I've connected the sync wire to pin 2.

The problem is this: using attachInterrupt with FALLING, the interrupt is never called. The output on Serial is an endless stream of zeroes (as you'd expect.)

If I use LOW, the program appears to hang, the motor does not move and there's no traffic on Serial.

Here is my working, stripped-down code:

Code:
#define PWM_1       23       // PWM output pin 1
#define LASER_SYNC  2        // Laser sync pin

volatile unsigned long rangeTime;   // Time of next laser pulse on serial.
volatile bool rangeUpdate;          // True if a range update is expected.

void setup() {

  rangeUpdate = false;

  Serial.begin(115200); 

  pinMode(PWM_1, OUTPUT); // Pin for the drive motor speed
  
  pinMode(LASER_SYNC, INPUT_PULLUP); // Pin for laser sync
  
  attachInterrupt(LASER_SYNC, laserSyncISR, LOW); // Interrupt for laser pulse  -- HANGS WITH LOW; NO INTERRUPT WITH FALLING

  analogWrite(PWM_1, 64); // Start the motor.

  interrupts();

}

void loop() {
  Serial.println(rangeTime);
}

void laserSyncISR() {
  rangeTime = 1; //micros();
  rangeUpdate = true;
}

If anyone can suggest what might be happening here, or any information I've omitted I'd be grateful for the input!

Rob
 
Question: Should the LASER_SYNC be PULLUP? Won't the device maintain that high until it drops it? As PULLUP it might not get to drop as well.

There was a recent note about the minimum dwell time on change - IIRC 5us is at or under that - the manual would have that number.

I'd try it interrupt as CHANGE perhaps and then just make sure it doesn't double trigger - but one edge might show up.
 
Thanks for the reply. I have actually tried with all of FALLING, LOW, CHANGE, RISING and HIGH. No action from anything, though only LOW hangs. INPUT and INPUT_PULLUP give the same result. (It's fairly clear I've flailed a bit.)

I considered that the pulse might be too short. I have emailed the engineers about this (no response yet, though they've been helpful in the past). How would one go about detecting such a short pulse?
 
Last edited:
One possible test here is to add a button to your interupt pin and just make sure you can fire things with a longer press that way to confirm the code can in fact trigger, is triggering from the right pin etc.
 
Hm, is this signal needed ? You'll get the data over the serial interface anyway, and you need serial to do settings.

Maybe the signal isn't just there, and is constant low at the moment, until you use serial.. ?
 
How would one go about detecting such a short pulse?

Do you have a scope or logic analyzer?

Try adding delay(100); in loop() so as not to overwhelm serial output.
Try moving analogWrite() before attachInterrupt(), that way motor should always run.
If the interrupt pin is firing faster than a few MHz, ISR will consume all the cycles ...

As GremlinWranger suggested, hook up a button on the interrupt pin.
 
GremlinWrangler, I actually hooked up the serial RX line to the sync pin to see if the interrupt is working. It is.

Frank B, the signal is needed because I need an accurate time to compute the position of the target (based on the position and orientation of the laser itself, which are also carefully timed.)

manitou, I'm attempting to locate an instructor or student at my university who can help me access a scope. Given the other questions (which are all pointing in the same direction) I'm beginning to suspect the instrument itself. The maximum frequency of the laser is about 20kHz, but the pulse length is about 5us (given my reading of the scope screenshot in the datasheet).

Thanks for the assistance so far. Let's see what the scope and the engineer say...
 
I may be wrong, but looking at the data sheet, it looks like the sync signal only goes down to 2volts, so maybe it is not showing as a low signal. You might try something like a voltage divider or something to drop the low to a lower value...
 
I ran your sketch with pin 2 jumpered to pin 23 and do a count++ in the ISR and print count in loop with delay(1000); attachInterrupt with LOW, hangs. With FALLING, I get 488 PWM counts/sec. so use FALLING -- though you said you tried it. I didn't see the 2v low for sync signal in data sheet -- maybe i didn't read closely enough.
 
I have a funny/disappointing update: LightWare, the company that makes the laser I'm using has been having some growing pains due to exploding demand, apparently. The model of laser I received had been upgraded but the documentation lagged. The pulse rate and serial packet format changed, among other things, but I was able to figure those out.

Unfortunately, they also disabled the sync pulse, as I'm apparently the first customer to want it. Their engineer sent me a firmware upgrade to re-enable the sync wire, so I'll be able to test when I get back to my home lab. I suspect that the promising analog output I got above was due to an error on my part (if the sync wire was indeed disabled.) I'll still try to get a hold of a scope and check it, but this may turn out to be one of those did-you-remember-to-plug-it-in problems.

I'll update with the outcome for posterity's sake, ;)
 
disabled the sync pulse

who would have thought that? :rolleyes:

you have a pretty exact timing with the first byte received. you know the bitrate and protocol (8N1?) - it's doable to substract the time needed for the transmission, so you know when the measurement was.
if that is too late, one could sync with the first incoming bit, perhaps.
but I'm curious: what's that needed for? -
"18317 readings per second", 18kHz, and you need even more precision -- for what? seems to be an interesting project.
 
Last edited:
Wanting to know when GPS signal transmission started I put an _isr on the Rx line to capture message start bit. There was a lot of other code running calcs and the time to process/receive the first GPS byte was arbitrary.

I factored that code into a self testing sample here for Serial1 Rx wired to Tx. That code used CycleCounter as it is more accurate and faster to read the micros or millis so it is here:

Code:
#define qBlink() (GPIOC_PTOR = 32)  // Pin13 on T3.x & LC // requires: pinMode(LED_BUILTIN, OUTPUT);

volatile uint32_t ccTimeRcv;
#define SOME_BAUD 115200
#define PIN_SRX 0

[B]// REQUIRED CODE
void serialrx_isr() {
  ccTimeRcv = ARM_DWT_CYCCNT; // Record time as desired
  // Detach _isr() to avoid chatter/repeats during character transmission
  detachInterrupt(PIN_SRX); // must re-attach interrupt on receive complete
  qBlink(); // TOGGLE LED to show functionality
}[/B]

// Code to trigger _isr() when incoming Serial message starts
// code marked as follows is all that is needed ::   // REQUIRED CODE
// This sample uses Serial1 and requires a wire jumper pin 0 to pin 1
elapsedMillis WhenSend;
void setup( ) {

  // Enable CPU Cycle Counter - if desired for time base
  ARM_DEMCR |= ARM_DEMCR_TRCENA;
  ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;

[B]  // REQUIRED CODE
  // Attach Serial_RX pin for interrupt
  attachInterrupt( PIN_SRX, serialrx_isr, FALLING);[/B]

  // Do app specific setup here including Serial port .begin
  Serial1.begin(SOME_BAUD);   // REQUIRED CODE
  pinMode( LED_BUILTIN, OUTPUT );
  while (!Serial && millis() < 5000 );
  Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
  while ( Serial1.available() ) Serial1.read(); // discard test char
  WhenSend = 0;
}

uint32_t ccTimeSend;
uint32_t ccTimeSum = 0;
uint32_t ccii = 11; // set to discard first avg after Serial1 first xfer
void loop() {
  if ( WhenSend > 1000 ) { // REMOVE this for external Serial1 connection
    WhenSend = 0;
    ccTimeSend = ARM_DWT_CYCCNT;
    // This sample sends to itself to show functionality
    Serial1.write( ' ' );
  }

  if ( Serial1.available() ) {
    while ( Serial1.available() ) Serial1.read(); // discard test char
    DoStats(); // extraneous timing to monitor Tx to Rx time, varies with buad

[B]    // REQUIRED CODE
    if ( 1 ) // Do this when data transmission is done to be ready for next Start
    {
      // (RE)Attach Serial_RX pin for interrupt
      attachInterrupt( PIN_SRX, serialrx_isr, FALLING);
    }[/B]
  }
}


void DoStats() {
  Serial.print( "DELAY TIME Cycle Counts =" );
  Serial.print( (ccTimeRcv - ccTimeSend));
  ccii++;
  ccTimeSum += (ccTimeRcv - ccTimeSend);
  if ( ccii >= 10 ) {
    Serial.print( " :: DELAY TIME AVG =" );
    Serial.print( ccTimeSum / ccii );
    ccii = 0;
    ccTimeSum = 0;
    Serial.println();
  }
  Serial.println();
}
 
but I'm curious: what's that needed for?

I'm building a predictive terrain-following system for a UAV, for remote-sensing purposes -- that is, there are certain parameters that need to be optimized by the trajectory, which requires something resembling a forward terrain model. 18kHz (20kHz with my upgraded model) is overkill for this application, but it can be set to any of several frequencies between about 40Hz and 20kHZ.

You're right about the timing being "good enough" but I'm one of those pedantic fools that wants everything to be as accurate as it can possibly be, even if I don't know what I'm doing. Using the arrival of a range on the wire would be good enough, given the other sources of error, and using the interrupt instead introduces the problem of matching ranges to timestamps. However, I think I've resolved that problem well enough for now by counting interrupts and received ranges, which "catch up" to the interrupt counts on each call to loop(). The trick is to see how high the pulse frequency can get before the buffer is overrun. It doesn't make sense to use too large a buffer, because the timestamped ranges (and position info) have to be forwarded to another computer to do the actual flight planning and spacing out the points at a lower frequency is better than having large chunks of dropped points.

The good news is, the sync wire was indeed turned off, and it does indeed work with the firmware upgrade.
 
if you want to use an interrupt pin (and what you are trying to do is not a good use of an interrupt), then you should connect the buffer between the Teensy and the sensor via an I2C bus.
 
if you want to use an interrupt pin (and what you are trying to do is not a good use of an interrupt), then you should connect the buffer between the Teensy and the sensor via an I2C bus.

What has this to do with this thread from !2018!" ?
Are you a spam bot?
 
Back
Top