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

Thread: Need help with an interrupt / timer project.

  1. #1
    Junior Member
    Join Date
    Nov 2021
    Posts
    8

    Need help with an interrupt / timer project.

    Hi -

    My project uses a fiber optics sensor to detect holes on a film strip, and after X amount of holes detected, a signal is sent to a camera to take a picture. This happens fast and continuously, up to 96 holes detected per second.

    This is the sensor amplifier I use:
    https://www.keyence.com/products/sen...odels/fs-n41p/

    ..and this is the fiber optics sensor:
    https://www.keyence.com/products/sen.../models/fu-20/

    I chose the Teensy 4.1 for this project because of it's speed. The hardware for my machine is hooked up and working properly.

    The code I have is very flawed and I can see issues when I run my machine - I should mention that I am very novice at coding.

    This is the code I currently use, it detects 4 holes and then takes a picture.

    Code:
    const int sensor = 23;
    int perf = 0; 
    int var = 0;
    int frame_counting = 0;
    int cameraTrigger= 32;
    
    
    void setup() {
    
       Serial.begin(115200);
      pinMode(led, OUTPUT);
         attachInterrupt(digitalPinToInterrupt(sensor), count, FALLING);
    
    }
    
    
    //
    void count() {
      if(digitalRead(sensor) > var)
    {
    var = 1;
    perf++;
    if((perf % 1) == 0){
      frame_counting++;
    
      digitalWrite(cameraTrigger, HIGH);
    delay(1);
    digitalWrite(cameraTrigger, LOW);
    
        
    Serial.print(frame_counting);
    Serial.print(" perf");
    Serial.println(" detected.");
    }
    
    }
    
    if(digitalRead(sensor) == 0) {
        var = 0;}
    
    delay(1);
    
    }
    
    
    void loop() {
    
    }

    I understand this is probably the wrong way to do this and that I am not using the fast timers on the Teensy. This is where I need some help.

    How could I make this efficient and precise.

    Also not sure if it's appropriate to ask here but I would offer this as a paid gig for anyone interested. Please let me know if that's not within the forum rules here and I'll delete this bit. If it's ok - and anyone is interested please PM me.

    Picture of the prototype machine below:

    thanks!

    Click image for larger version. 

Name:	IMG_4089.jpg 
Views:	33 
Size:	150.7 KB 
ID:	26699

  2. #2
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,617
    Just remove the delay() and the Serial.print.
    Delay() is unpredictable with the short times.

    If a delay is really needed, replace it by delayNanoseconds().

  3. #3
    Junior Member
    Join Date
    Nov 2021
    Posts
    8
    Quote Originally Posted by Frank B View Post
    Just remove the delay() and the Serial.print.
    Delay() is unpredictable with the short times.

    If a delay is really needed, replace it by delayNanoseconds().
    Thanks for the recommendation - I just tried it but it breaks the code, the counter gets out whack without the delay

  4. #4
    Senior Member
    Join Date
    Jul 2020
    Posts
    1,456
    Firstly there's no need for interrupts for something this slow (from the Teensy 4's perspective ). You have two
    tasks, counting incoming pulses and generating outgoing pulses - the delay you use for the outgoing pulse should
    not be affecting the former.

    Have you looked at the BlinkWithoutDelay example sketch - this is basically how to drive the output pulses without
    slowing down the input detection.

  5. #5
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,617
    Quote Originally Posted by jonesyro View Post
    Thanks for the recommendation - I just tried it but it breaks the code, the counter gets out whack without the delay
    Ok, make the variables you use volatile. Then it should not happen anymore.

  6. #6
    Junior Member
    Join Date
    Nov 2021
    Posts
    8
    Quote Originally Posted by Frank B View Post
    Ok, make the variables you use volatile. Then it should not happen anymore.
    Thanks Frank - Appreciate the help, I tried but it's still not working.

    When hooking an oscilloscope on the sensor itself I see every pulses (1 per hole), on the Teensy with my code, and with the "perfs" variable set to 1 (to replicate the pulses as if it was hooked up directly to sensor) it randomly skips pulses. When I set it to 4 pulses to one output trigger it's the same..

    If anyone has any recommendations where to find microcontroller programmers for hire who work with Teensy/Arduino please let me know.

  7. #7
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,704
    I'd first check the hardware with a simple sketch like shown below. Whenever it registers a falling edge at the sensor input it simply toggles the cameraTrigger.
    Code:
    #include "Arduino.h"
    
    constexpr int sensor        = 23;
    constexpr int cameraTrigger = 32;
    
    void onDetect()
    {
        digitalToggleFast(cameraTrigger);
    }
    
    void setup()
    {
        pinMode(cameraTrigger, OUTPUT);
        attachInterrupt(sensor, onDetect, FALLING);
    }
    
    void loop() {
    }
    Connect your scope to the sensor pin and the cameraTrigger pin. You should get something like

    Click image for larger version. 

Name:	Screenshot 2021-11-29 072828.png 
Views:	27 
Size:	17.4 KB 
ID:	26705

    Depending on the signal quality of the sensor the interrupt might trigger a couple of times per one signal edge, so look out for fast (<Ás) signal transitions on the trigger output. Would be good if you could post a screenshot/photo of the scope trace.

  8. #8
    Junior Member
    Join Date
    Nov 2021
    Posts
    8
    Quote Originally Posted by luni View Post
    I'd first check the hardware with a simple sketch like shown below. Whenever it registers a falling edge at the sensor input it simply toggles the cameraTrigger.
    Code:
    #include "Arduino.h"
    
    constexpr int sensor        = 23;
    constexpr int cameraTrigger = 32;
    
    void onDetect()
    {
        digitalToggleFast(cameraTrigger);
    }
    
    void setup()
    {
        pinMode(cameraTrigger, OUTPUT);
        attachInterrupt(sensor, onDetect, FALLING);
    }
    
    void loop() {
    }
    Connect your scope to the sensor pin and the cameraTrigger pin. You should get something like

    Click image for larger version. 

Name:	Screenshot 2021-11-29 072828.png 
Views:	27 
Size:	17.4 KB 
ID:	26705

    Depending on the signal quality of the sensor the interrupt might trigger a couple of times per one signal edge, so look out for fast (<Ás) signal transitions on the trigger output. Would be good if you could post a screenshot/photo of the scope trace.

    That's a very good call - I don't have a 2 channels scope but will order one.

  9. #9
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    394
    @luni could you describe the conditions where an edge interrupt can be triggered multiple times for the same edge?

  10. #10
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,704
    @shawn: see e.g. here: https://forum.pjrc.com/threads/66741...l=1#post274793. Also, the sensor seems to have an open collector output. How is that connected? Does it work reliably with 3.3V? Lot of stuff can go wrong on the hardware side which I personally would check before debugging software.

    @jonesyro: Here a quick sketch showing how to generate the camera pulses in case the hardware works. I assume that you need some delay after you detected a 'hole' to optimize timing and added this. When the sensor detects the edge it triggers a one shot timer with the required delay time. The callback of this timer sets the cameraTrigger high and triggers another timer which resets the cameraTrigger after the integration time. Pretty straight forward.

    Code:
    #include "Arduino.h"
    #include "TeensyTimerTool.h"
    using namespace TeensyTimerTool;
    
    constexpr int sensor        = 23;
    constexpr int cameraTrigger = 32;
    
    int delayTime       = 2500; // camera opens 2.5ms after sensor edge detected
    int integrationTime = 1000;
    int nrOfHoles       = 4;
    
    OneShotTimer t_start(TCK);
    OneShotTimer t_stop(TCK);
    
    void onDetect()
    {
        static int cnt = 0;
        if (++cnt == nrOfHoles)
        {
            cnt = 0;
            t_start.trigger(delayTime); // switch on cammera after the delay time
        }
    }
    
    void onStartIntegration()
    {
        digitalWriteFast(cameraTrigger, HIGH); // start integration
        t_stop.trigger(integrationTime);       // switch camera off after integration time
    }
    
    void onStopIntegration()
    {
        digitalWriteFast(cameraTrigger, LOW); // stop integration
    }
    
    void setup()
    {
        pinMode(cameraTrigger, OUTPUT);
    
        t_start.begin(onStartIntegration);
        t_stop.begin(onStopIntegration);
    
        attachInterrupt(sensor, onDetect, FALLING);
    }
    
    void loop()
    {
    }
    Result with a delay of 2.5ms and an integration time of 1ms:

    Click image for larger version. 

Name:	Screenshot 2021-11-30 080844.png 
Views:	15 
Size:	23.9 KB 
ID:	26725

    Zoomed in:

    Click image for larger version. 

Name:	Screenshot 2021-11-30 080922.png 
Views:	14 
Size:	27.4 KB 
ID:	26726

  11. #11
    Junior Member
    Join Date
    Nov 2021
    Posts
    8
    Thanks very much @Luni for debugging this and your code.

    I really appreciate it. I'm getting the scope tomorrow or the day after and will update with results. Makes a lot of sense to check hardware first, the 2 channels scope will take care of that.



    Quote Originally Posted by luni View Post
    @shawn: see e.g. here: https://forum.pjrc.com/threads/66741...l=1#post274793. Also, the sensor seems to have an open collector output. How is that connected? Does it work reliably with 3.3V? Lot of stuff can go wrong on the hardware side which I personally would check before debugging software.

    @jonesyro: Here a quick sketch showing how to generate the camera pulses in case the hardware works. I assume that you need some delay after you detected a 'hole' to optimize timing and added this. When the sensor detects the edge it triggers a one shot timer with the required delay time. The callback of this timer sets the cameraTrigger high and triggers another timer which resets the cameraTrigger after the integration time. Pretty straight forward.

    Code:
    #include "Arduino.h"
    #include "TeensyTimerTool.h"
    using namespace TeensyTimerTool;
    
    constexpr int sensor        = 23;
    constexpr int cameraTrigger = 32;
    
    int delayTime       = 2500; // camera opens 2.5ms after sensor edge detected
    int integrationTime = 1000;
    int nrOfHoles       = 4;
    
    OneShotTimer t_start(TCK);
    OneShotTimer t_stop(TCK);
    
    void onDetect()
    {
        static int cnt = 0;
        if (++cnt == nrOfHoles)
        {
            cnt = 0;
            t_start.trigger(delayTime); // switch on cammera after the delay time
        }
    }
    
    void onStartIntegration()
    {
        digitalWriteFast(cameraTrigger, HIGH); // start integration
        t_stop.trigger(integrationTime);       // switch camera off after integration time
    }
    
    void onStopIntegration()
    {
        digitalWriteFast(cameraTrigger, LOW); // stop integration
    }
    
    void setup()
    {
        pinMode(cameraTrigger, OUTPUT);
    
        t_start.begin(onStartIntegration);
        t_stop.begin(onStopIntegration);
    
        attachInterrupt(sensor, onDetect, FALLING);
    }
    
    void loop()
    {
    }
    Result with a delay of 2.5ms and an integration time of 1ms:

    Click image for larger version. 

Name:	Screenshot 2021-11-30 080844.png 
Views:	15 
Size:	23.9 KB 
ID:	26725

    Zoomed in:

    Click image for larger version. 

Name:	Screenshot 2021-11-30 080922.png 
Views:	14 
Size:	27.4 KB 
ID:	26726

  12. #12
    Junior Member
    Join Date
    Nov 2021
    Posts
    8
    Quote Originally Posted by luni View Post
    @shawn: see e.g. here: https://forum.pjrc.com/threads/66741...l=1#post274793. Also, the sensor seems to have an open collector output. How is that connected? Does it work reliably with 3.3V? Lot of stuff can go wrong on the hardware side which I personally would check before debugging software.

    @jonesyro: Here a quick sketch showing how to generate the camera pulses in case the hardware works. I assume that you need some delay after you detected a 'hole' to optimize timing and added this. When the sensor detects the edge it triggers a one shot timer with the required delay time. The callback of this timer sets the cameraTrigger high and triggers another timer which resets the cameraTrigger after the integration time. Pretty straight forward.

    Result with a delay of 2.5ms and an integration time of 1ms:

    Click image for larger version. 

Name:	Screenshot 2021-11-30 080844.png 
Views:	15 
Size:	23.9 KB 
ID:	26725

    Zoomed in:

    Click image for larger version. 

Name:	Screenshot 2021-11-30 080922.png 
Views:	14 
Size:	27.4 KB 
ID:	26726

    I got the 2 channel scope this am.

    Hooked up channel 1 (yellow) to the trigger ground and signal. Channel 2 is on ground and Teensy pin 32. (this is the output to the camera)

    The original code I posted above confirms what I was seeing in camera. It randomly miss 1 hole. see screenshot below:

    Click image for larger version. 

Name:	OriginalCode.jpg 
Views:	16 
Size:	149.4 KB 
ID:	26760

    With @luni code it seems to send multiple little signals here's the scope for this code.

    Click image for larger version. 

Name:	Luni.jpg 
Views:	15 
Size:	158.0 KB 
ID:	26762

    and zoomed in:

    Click image for larger version. 

Name:	luni_zoomed.jpg 
Views:	15 
Size:	146.8 KB 
ID:	26761

    More details:

    - The trigger outputs 12V so I send the signal through an "IC 7805 Voltage Regulator" and then a 5V to 3.3V regulator so I don't fry the Teensy 4.1
    - The direct signal from the trigger is constant, not missing holes.

    I'm new to scopes so bare with me. I hooked up the probes and did an "Auto Setup", I ran the machine and captured some signals, hit the "RUN/STOP" button to freeze what I was seeing and zoomed in using the horizontal& vertical positions. Maybe there's a better way, I haven't messed with triggers etc.. very basic setup but it shows the issue.

    Thanks for checking this out.

  13. #13
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,704
    - The trigger outputs 12V so I send the signal through an "IC 7805 Voltage Regulator" and then a 5V to 3.3V regulator so I don't fry the Teensy 4.1
    This is absolutely not the way how to do it. I'm surprised that this even works. You should use a real level shifter, something like a 74LVC125 or similar. (I need to leave now, so no time to explain further but I'm sure others will chime in).

    Anyway, since in seems to "work" somehow I'd be interested in what happens if you do a pinmode(sensor, INPUT_PULLUP)? This will activate the schmitt trigger feature on the input pin which might be enough to get rid of the multiple pulses.

    Edit: sorry, the 74LVC can probably not handle your 12V output...

  14. #14
    Junior Member
    Join Date
    Nov 2021
    Posts
    8
    Quote Originally Posted by luni View Post
    This is absolutely not the way how to do it. I'm surprised that this even works. You should use a real level shifter, something like a 74LVC125 or similar. (I need to leave now, so no time to explain further but I'm sure others will chime in).

    Anyway, since in seems to "work" somehow I'd be interested in what happens if you do a pinmode(sensor, INPUT_PULLUP)? This will activate the schmitt trigger feature on the input pin which might be enough to get rid of the multiple pulses.

    Edit: sorry, the 74LVC can probably not handle your 12V output...
    Thanks @luni

    The input_pullup fixed the multiple pulses but it's still acting whacky. Skipping pulses from time to time.

    I went back to basics with this code and I'm getting really precise triggers on every hole - no missed pulses, rising edges are matched perfectly.

    This is more precise than my original code and the new one.

    Code:
    const int sensor = 23;
    int cameraTrigger= 32;
    
    void setup() {
    
       pinMode(sensor, INPUT);
      pinMode(cameraTrigger, OUTPUT);
    
    }
    
    
    void loop() {
      
    int val = digitalRead(sensor);
    
    if(val == HIGH)
    
    {
      digitalWrite(cameraTrigger, HIGH);
    }
    else
    {
        digitalWrite(cameraTrigger, LOW);
    }
    
    }
    Now if the counting could be done using this simple code that would fix all my issues. Detect 4 holes, send the signal to the camera.. [repeat]

    Maybe I don't need to mess with interrupts? It's not like this is going that fast and the code above seems to be solid for single hole triggers.

    Here's the scope:

    Click image for larger version. 

Name:	IMG_4118.jpg 
Views:	16 
Size:	82.8 KB 
ID:	26763

    and zoomed:

    Click image for larger version. 

Name:	IMG_4119.jpg 
Views:	14 
Size:	80.3 KB 
ID:	26764

  15. #15
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,704
    Can you zoom into the falling edge? Since the signal is going down very slow, I'd be surprised if you don't see multiple transitions if you look closely. This would generate the same counting issues as you see with the interrupt based code.

    Anyway, you can try this:
    Code:
    #include "Arduino.h"
    
    constexpr int sensor        = 23;
    constexpr int cameraTrigger = 32;
    
    void setup()
    {
        pinMode(sensor, INPUT_PULLDOWN);
        pinMode(cameraTrigger, OUTPUT);
    
        analogWriteFrequency(0, 96);
        analogWrite(0, 128);
    }
    
    unsigned cnt    = 0;
    unsigned oldVal = LOW;
    
    void loop()
    {
        unsigned val = digitalRead(sensor);
        if (val != oldVal)
        {
            if (val == HIGH) // rising edge
            {
                cnt++;
                if (cnt >= 4)
                {
                    digitalWrite(cameraTrigger, HIGH);
                    cnt = 0;
    
                    delayMicroseconds(500);
                }
            }
            else // falling edge
            {
                digitalWrite(cameraTrigger, LOW);
                delayMicroseconds(500);
            }
            oldVal = val;
        }
    }
    Click image for larger version. 

Name:	Screenshot 2021-12-03 080243.jpg 
Views:	14 
Size:	32.0 KB 
ID:	26768

    I changed the pin mode to INPUT_PULLDOWN to activate the Schmitt trigger on the pin which should help reducing multiple transitions. I also added a delay after detection of a signal edge which might also help. You can increase the time if necessary.

    And: did I mention already that voltage regulators are not meant to be used for level shifting?

  16. #16
    Junior Member
    Join Date
    Nov 2021
    Posts
    8
    Quote Originally Posted by luni View Post
    Can you zoom into the falling edge? Since the signal is going down very slow, I'd be surprised if you don't see multiple transitions if you look closely. This would generate the same counting issues as you see with the interrupt based code.

    Anyway, you can try this:
    Code:
    #include "Arduino.h"
    
    constexpr int sensor        = 23;
    constexpr int cameraTrigger = 32;
    
    void setup()
    {
        pinMode(sensor, INPUT_PULLDOWN);
        pinMode(cameraTrigger, OUTPUT);
    
        analogWriteFrequency(0, 96);
        analogWrite(0, 128);
    }
    
    unsigned cnt    = 0;
    unsigned oldVal = LOW;
    
    void loop()
    {
        unsigned val = digitalRead(sensor);
        if (val != oldVal)
        {
            if (val == HIGH) // rising edge
            {
                cnt++;
                if (cnt >= 4)
                {
                    digitalWrite(cameraTrigger, HIGH);
                    cnt = 0;
    
                    delayMicroseconds(500);
                }
            }
            else // falling edge
            {
                digitalWrite(cameraTrigger, LOW);
                delayMicroseconds(500);
            }
            oldVal = val;
        }
    }
    Click image for larger version. 

Name:	Screenshot 2021-12-03 080243.jpg 
Views:	14 
Size:	32.0 KB 
ID:	26768

    I changed the pin mode to INPUT_PULLDOWN to activate the Schmitt trigger on the pin which should help reducing multiple transitions. I also added a delay after detection of a signal edge which might also help. You can increase the time if necessary.

    And: did I mention already that voltage regulators are not meant to be used for level shifting?

    Well that did it! Super precise trigger now. Thanks a lot @Luni - now I got to figure out how to shift the levels correctly Next part will be adding ability to set the pulse length out of pin 32

  17. #17
    Senior Member
    Join Date
    Jul 2020
    Posts
    1,456
    Note that logic input signals have a _minimum_ slew-rate specification, typically for a fast CMOS chip it might be over 100V/Ás.
    The T4.x chip requires logic transitions to be 25ns or faster when a pin is not in hysteresis mode, which is ~130V/Ás.

    This is to prevent false transitions due to noise on the inputs and supply rail while the input voltage is in the forbidden zone
    (which is 1V--2.3V for the T4.x when not in hysteresis mode). CMOS inputs, unless Schmitt-trigger, can act as high gain
    analog amplifiers and amplify noise greatly at the mid-voltage point.

Posting Permissions

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