Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 16 of 16

Thread: Teensy 3.2 and laser rangefinder -- problem with interrupt

  1. #1
    Junior Member
    Join Date
    Dec 2018
    Posts
    5

    Teensy 3.2 and laser rangefinder -- problem with interrupt

    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

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    15,843
    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.

  3. #3
    Junior Member
    Join Date
    Dec 2018
    Posts
    5
    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 by kludahk; 12-10-2018 at 06:34 PM.

  4. #4
    Senior Member
    Join Date
    Apr 2013
    Posts
    1,938
    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.

  5. #5
    Senior Member
    Join Date
    Apr 2014
    Location
    -
    Posts
    9,756
    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.. ?

  6. #6
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    2,696
    Quote Originally Posted by kludahk View Post
    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.

  7. #7
    Junior Member
    Join Date
    Dec 2018
    Posts
    5
    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...

  8. #8
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    10,316
    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...

  9. #9
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    2,696
    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.

  10. #10
    Junior Member
    Join Date
    Dec 2018
    Posts
    5
    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,

  11. #11
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,854
    [edit - I didn't see #10 from @kludahk until after I posted this. Ignore it]

    Pete

  12. #12
    Senior Member
    Join Date
    Apr 2014
    Location
    -
    Posts
    9,756
    Quote Originally Posted by kludahk View Post
    disabled the sync pulse
    who would have thought that?

    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 by Frank B; 12-11-2018 at 08:29 PM.

  13. #13
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    15,843
    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
    
    // 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
    }
    
    // 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;
    
      // REQUIRED CODE
      // Attach Serial_RX pin for interrupt
      attachInterrupt( PIN_SRX, serialrx_isr, FALLING);
    
      // 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
    
        // 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);
        }
      }
    }
    
    
    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();
    }

  14. #14
    Junior Member
    Join Date
    Dec 2018
    Posts
    5
    Quote Originally Posted by Frank B View Post
    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.

  15. #15
    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.

  16. #16
    Senior Member
    Join Date
    Apr 2014
    Location
    -
    Posts
    9,756
    Quote Originally Posted by Johnniebenson View Post
    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?

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •