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

Thread: PMT pulse counting using Teensy 3.2

  1. #1
    Junior Member
    Join Date
    Nov 2017
    Posts
    2

    PMT pulse counting using Teensy 3.2

    Hello,

    For the last 20 years I've been programming embedded controllers, 8051 family, Zilog, Rabbit, Atmel, using Basic and C languages and sometimes assembler for time critical pieces of code.
    Since more or less 1 year I'm using Arduino (Uno, Mega) boards through the Arduino IDE and AVR Studio.

    For a new project of the chemistry department of the University I work for, I need to count TTL level pulses coming from the amplifier of a Photo Multiplier tube. I tried this with the mentioned Arduino boards and arrived to the limit of the power processing of the controllers.
    What I did was feed the TTL pulse to an external interrupt pin. In the ISR (INT0_vect) I increment a long unsigned. I can count no more than 250 K pulses per second.
    I would like to have a counting error of no more than 0,01%.

    I have no experience with ARM processors so the last days I have been reading a lot of information on this forum. I did not find yet what I'm looking for.
    My question is, if I use the same principle (external interrupt) using a Teensy 3.2 running at 72 MHz, will I be able to count up to 5 million pulses per second ?
    And in the meanwhile send the count value to a remote location using serial communication ?

    Thanks for your comments.
    Janjos

  2. #2
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,384
    The interrupt solution is most probably not the most reliable and quickest way to go. I’d rather suggest that you look at the source code of the Teensy FreqCount library which uses an internal timer/counter directly without the interrupt overhead and an at least go up to more than 10 MHz.

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    19,927
    No, it definitely will not count to 5 MHz using interrupts.

    I did a quick test just now.

    Teensy 3.2 running at 72 MHz can handle up to 1.05 MHz.

    Teensy 3.6 running at 180 MHz can handle up to 2.5 MHz.

    The key to getting this performance is NVIC_SET_PRIORITY to raise the priority level of the interrupt. Here's the code I tested on both boards.

    Code:
    volatile unsigned long count=0;
    unsigned long prior_count=0;
    
    void pulse() {
      count = count + 1;
    }
    
    void setup() {
      pinMode(3, INPUT_PULLDOWN);
      attachInterrupt(digitalPinToInterrupt(3), pulse, RISING);
      NVIC_SET_PRIORITY(IRQ_PORTA, 0);
    }
    
    void loop() {
      unsigned long new_count = count;
      if (new_count != prior_count) {
        Serial.println(new_count);
        prior_count = new_count;
        delay(10); // print at reasonable speed
      }
    }

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    19,927
    If using the LPTRM hardware to count the pulses, up to 30 MHz should be possible.

  5. #5
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    19,927
    I made a little video about this pulse count testing.

    Last edited by PaulStoffregen; 11-30-2017 at 07:49 AM.

  6. #6
    Junior Member
    Join Date
    Nov 2017
    Posts
    2
    @Theremingenieur
    I did look at the library's source code, I'm still "digesting" it, still trying to understand how the timer/counter registers get set. Thank you for the tip.

    @Paul
    Thank you for testing this on both Teensy 3.2 and 3.6. The method I was using, the pulse generating an external interrupt on Arduino UNO pin 5, improves with the processing power and speed of the used processor but does not work for higher frequencies.
    Thank you also for explaining and testing your method using your library (https://github.com/PaulStoffregen/FreqCount).

    I ordered a Teensy 3.2 from your Belgium distributor Velleman but I'm still waiting for it.
    In the meanwhile, I did a test using your library in the UNO and it is working up to 6 million pulses per second.

    For all others that might have a similar project, I can confirm that what Paul did on the second half of the video, is the way to go.

    Janjos

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    19,927
    For anyone finding this page later, here's a copy of the code used in the 2nd part of the video.

    Code:
    #include <FreqCount.h>
    
    unsigned long count = 0;
    unsigned long prior_count=0;
    
    void setup() {
      FreqCount.begin(100);
    }
    
    void loop() {
      if (FreqCount.available()) {
        count = count + FreqCount.read();
        if (count != prior_count) {
          Serial.println(count);
          prior_count = count;
        }
      }
    }

  8. #8
    Junior Member bananaflux's Avatar
    Join Date
    Aug 2015
    Location
    DC
    Posts
    4
    One might consider this tangentially related, but I see PMTs and pulse counting and get excited. In addition to pulse frequency information, the elapsed time between pulses is useful for certain types of experiments.

    Under sufficient reverse bias, a select few LEDs behave like Single Photon Avalanche Detectors (SPADs), albeit with horrible quantum efficiency. I use these LEDs in a didactic environment though, where this specification matters not. The Teensy makes a great frequency/time-interval counting instrument for conditioned SPAD pulses. A "discriminator" circuit (just an LM311 comparator) is constructed to set the detected pulse threshold voltage and the output is sent to Teensy pin 13. The Teensy enumerates as a keyboard and depending on the button pressed, it will either output frequency data directly to a spreadsheet ad infinitum or will collect and store up to 20k time interval samples in RAM then spit them out to a spreadsheet for data analysis. The code below uses elapsedMicros for timing, but I imagine there is a way to deploy one of the hardware counters to count clock ticks for finer time-interval resolution...as I assume the PulsePosition library is able to achieve 20ns resolution. It seems this clock cycle counting method is employed in the FreqMeasure library, but I was initially scared of trying it because of the stated frequency "limits". Someday I will test this and report back.

    In any case, I would love to get any feedback on my code!

    Click image for larger version. 

Name:	SPADLabCircuit.jpg 
Views:	117 
Size:	109.4 KB 
ID:	12209

    Code:
    #include <FreqCount.h>
    #include <Bounce.h>
    
    const int sensePin = 13;
    const int intervalPin = 1;
    const int freqPin = 12;
    const int intervalLED = 23;
    const int freqLED = 14;
    const unsigned long senseLength = 2000;
    
    volatile unsigned int bangTime = 0;
    volatile int dataSent;
    
    Bounce intervalButton = Bounce(intervalPin, 25);
    Bounce freqButton = Bounce(freqPin, 25);
    
    void setup() {
      pinMode(intervalPin, INPUT_PULLUP);
      pinMode(freqPin, INPUT_PULLUP);
    
      pinMode(intervalLED, OUTPUT);
      pinMode(freqLED, OUTPUT);
      digitalWrite(intervalLED, LOW);
      digitalWrite(freqLED, LOW);
    
      Serial.begin(115200);
    }
    
    int16_t countMode = 0;     //mode = 0: nothing happens; mode = 1: frequency counter operational; mode = 2: Interval Timer
    int16_t intervalCount = 0;
    int16_t intervalData[20000];
    
    elapsedMillis elapsedTime;
    elapsedMicros senseTest;
    
    void loop() {
    
      if (freqButton.update()) {
        if (freqButton.fallingEdge() && countMode == 0) {
          FreqCount.begin(1000);
          elapsedTime = 0;
          Keyboard.print("time [ms]");
          Keyboard.set_key1(KEY_TAB);
          Keyboard.send_now();
          Keyboard.set_key1(0);
          Keyboard.send_now();
          Keyboard.println("Frequency [Hz]");
          digitalWrite(freqLED, HIGH);
          countMode = 1;
        }
        else if (freqButton.fallingEdge() && countMode == 1) {
          FreqCount.end();
          digitalWrite(freqLED, LOW);
          countMode = 0;
          Keyboard.set_modifier(MODIFIERKEY_CTRL);
          Keyboard.set_key1(KEY_HOME);
          Keyboard.send_now();
          Keyboard.set_modifier(0);
          Keyboard.set_key1(0);
          Keyboard.send_now();
    
        }
      }
    
      if (intervalButton.update()) {
        if (intervalButton.fallingEdge() && countMode == 0) {
          digitalWrite(intervalLED, HIGH);
          countMode = 2;
    
          Keyboard.print("integration time [ms]: ");
          Keyboard.println(senseLength);
          Keyboard.println("interval time [us]");
    
          dataSent = 1;
          elapsedTime = 0;
          senseTest = 0;
          intervalCount = 0;
          attachInterrupt(digitalPinToInterrupt(sensePin), isr, RISING);
    
        }
      }
    
      if (countMode == 1 && FreqCount.available()) {
        unsigned long freq = FreqCount.read();
        Keyboard.print(elapsedTime);
        Keyboard.set_key1(KEY_TAB);
        Keyboard.send_now();
        Keyboard.set_key1(0);
        Keyboard.send_now();
        Keyboard.println(freq);
        Serial.println(freq);
      }
    
      if (countMode == 2 && dataSent == 0) {
        intervalData[intervalCount] = bangTime;
        //Serial.print(bangTime); Serial.print(" - "); Serial.print("intervalData["); Serial.print(intervalCount); Serial.print("] = ");Serial.println(intervalData[intervalCount]);
        intervalCount++;
        dataSent = 1;
      }
    
      if (countMode == 2 && (elapsedTime > senseLength)) {
        detachInterrupt(digitalPinToInterrupt(sensePin));
        digitalWrite(intervalLED, LOW);
        countMode = 0;
    
        for (int i = 1; i < intervalCount; i++) {
          Keyboard.println(intervalData[i]);
          Serial.println(intervalData[i]);
        }
        
        Keyboard.set_modifier(MODIFIERKEY_CTRL);
        Keyboard.set_key1(KEY_HOME);
        Keyboard.send_now();
        Keyboard.set_modifier(0);
        Keyboard.set_key1(0);
        Keyboard.send_now();
    
      }
    
    }
    
    void isr() {
      bangTime = senseTest;
      senseTest = 0;
      dataSent = 0;
    }

  9. #9
    Junior Member
    Join Date
    Sep 2018
    Posts
    5
    hi Paul
    I saw your discussion and testing in video
    i'm very interesting for using teensy to count the PMT pulse
    my PMT is:
    https://www.hamamatsu.com/jp/en/prod...110/index.html
    my pulse-pair resolution need 20 ns and my output pulse width is about 10 ns
    i'm using Oscilloscope to detect now (result as the following)
    https://imgur.com/iCZ4S9L
    https://imgur.com/FvnMEyl
    but it is not really convenient so i want to change it to teensy
    is that teensy Hardware allow the specification in my case?
    and is that the code fine or i have to add something for my case?(ex:setting gate time)
    Looking forward to your reply!

  10. #10
    Junior Member
    Join Date
    May 2019
    Posts
    5
    Dear all,

    I was wondering if somebody has implemented the Photon Counter and could report real values...

    Also, I was wondering if the Teensy3.6 could be set up similar as the National Instruments boards counters work, see attached figure, and at which speed.
    My objective is also to count photons, but I just need to count the arrival respect some zero time that is reset with a trigger signal.

    I am not asking that somebody does the work for me, but if some experienced user could tell me which type of performance can I realistically expect from Teensy3.6 (the fastest Arduino-like that I know of), it really help me to decide if yes or not is worth spending the significant amount of time that such project will take.

    Thanks in advanceClick image for larger version. 

Name:	Buffered_Edge_Counting.png 
Views:	0 
Size:	25.0 KB 
ID:	16669

  11. #11
    Senior Member
    Join Date
    Oct 2015
    Location
    Roma (IT, EU)
    Posts
    158
    You may use the method previously described by Paul Stoffregen to count SOURCE pulse up to 30 MHz (on Teensy 3.6), and enable INT on rising edge of Sample Clock to actually fill the buffer with the current Counter value.
    This is OK if your Sample Clock is up to, say, 2-2.5 MHz (maybe 3 if you overclock the Teensy to 240 MHz).

    I don't pretend this to be the best way to do it: just what *I* would do, as a basic-level Teensy programmer.

  12. #12
    Junior Member
    Join Date
    May 2019
    Posts
    5
    Thanks XFER.

    The idea is to fill a buffer with the number of clock ticks since a trigger signal happens (SOURCE in the figure) each time that a rising edge occurs ("Sample Clock" in the figure, this is coming from the photon multiplier). Because a clock tick is associated to a time, it is possible to convert to time if needed.

    Above, Paul Stoffregen wrote: "If using the LPTRM hardware to count the pulses, up to 30 MHz should be possible." If it is possible to achieve 30MHz time resolution and rate, then we are in business! (my avalanche photo diode has a dead time of 45ns, so 30MHz is enough to not miss a count!!!)

    So, I am not sure how to interpret your answer: which value is the good one? 30MHz or 2MHz?

    All the best,

  13. #13
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    8,350
    What are the values to count? Photon detection instances AND clock ticks after the 'arming of the counter' - will that follow the 45 ns dead time? How many photons appear at a time - and will it be a single burst or across multiple clocks for some duration?

    Are clock ticks regular? Do they need to be counted or just kept in sync? The Teensy has a counter for it's clock 240 MHz or 256 MHz are possible 'cycle counters'.

    If that started with the 'arming' signal then the counter value could be placed as measured CPU clock cycles without actually catching each clock tick.

    The T_3.6 can run OC'd up to 256 MHz - but there is a Teensy 4 Beta unit under test - details on that Beta thread - expected for production this 'summer' that runs at 600 MHz. So if you started with a T_3.6 the T_4 might arrive in time to improve upon it - depending on the code and features in use, support for existing libraries/features is the goal - and the same Arduino environment.

  14. #14
    Senior Member
    Join Date
    Oct 2015
    Location
    Roma (IT, EU)
    Posts
    158
    Quote Originally Posted by jofre View Post
    So, I am not sure how to interpret your answer: which value is the good one? 30MHz or 2MHz?
    The 30 MHz figure is only for counting pulses of your "SOURCE"-labelled signal, which would only increment a counter.
    The 2 MHz figure is for interrupt-triggering by your "Sample Clock"-labelled signal: slower, because it has to trigger an Interrupt Service Routine to add the counter value to the buffer

    But as I said, I don't pretend this to be the best solution: it's just that *I* can't come out with a better one.
    You'd better follow advices by the real masters, like Defragster and of course Paul Stoffregen himself (The Man Who Designed Teensy )

  15. #15
    Junior Member
    Join Date
    May 2019
    Posts
    5
    OK. I see that my explanation of what I have in mind is not clear enough... I try again.

    I have done a new diagram.

    Click image for larger version. 

Name:	edge_counter.png 
Views:	0 
Size:	26.9 KB 
ID:	16673

    The idea is to write down in a "Buffer" the number of "Clock Ticks" since the "Reset/Trigger Signal" when a "TTL from Photo Multiplier" signal arrives.

    So, two physical inputs into the board: the "reset/trigger Signal" and the "TTL from Photo Multiplier"

    I am assuming that the increment of "Clock Ticks" is done internally and does not need to be done in software.

    I am also assuming that is possible to reset the clock to zero.

    No maths involved, no averaging, no adding, nothing: just write an uint to a buffer.

    The photons arrival can be at any moment, there is no particular pattern. However, given the limitation of my current detector, they will not be arriving faster than every 45ns. I would like to point out that if it is possible to know when the photon arrives with better time resolution, then this is even better!!!

    Now, Defragster seem to indicate a time resolution of 256MHz (3.9ns) with Teensy3.6 and 600MHz (1.66ns) with Teensy4.0. If those values are true, this could be a small revolution in my community as funding for fundamental science is getting harder to obtain, and to have a photon counter with this specifications at such cost would be absolutely fantastic!

    Another tricky part, as my experiments with Arduino have show me, is getting the data to the computer, without loosing counts. There is something called DMA that is supposed to help with this, but I have not time to look into the details (OK, now is when you all guys are laughing right? I am not an electrical engineering, just a experimental physicists who got started with Arduino a few years ago and is always trying to push the limits of the equipment I have)

    By the way, thanks Defragster and XFer for taking time to reply!!!

  16. #16
    Senior Member
    Join Date
    Oct 2015
    Location
    Roma (IT, EU)
    Posts
    158
    Quote Originally Posted by jofre View Post
    I have done a new diagram.
    Much clearer, now.

    The photons arrival can be at any moment, there is no particular pattern. However, given the limitation of my current detector, they will not be arriving faster than every 45ns. I would like to point out that if it is possible to know when the photon arrives with better time resolution, then this is even better!!!

    Now, Defragster seem to indicate a time resolution of 256MHz (3.9ns) with Teensy3.6 and 600MHz (1.66ns) with Teensy4.0
    That's the internal clock period of the CPU, and so its the max frequency of the internal counter (your "clock ticks").
    But, a potential period as short as 45 ns for the photon detector (your "TTL" signal) is indeed very, very fast.
    With my suggested interrupt-driven approach ("TTL" to a pin with interrupt triggered by rising edge), just entering the Interrupt Service Routine will require about 66us on a Teensy 3.6 overclocked to 240 MHz (extrapoled from https://forum.pjrc.com/threads/45413...l=1#post148851).

    So I'd say disregard my suggestion: simple, but too slow.
    Let's see what the Masters suggest.

  17. #17
    Senior Member
    Join Date
    Oct 2015
    Location
    Roma (IT, EU)
    Posts
    158
    Mmmm... alternative approach: polling on "TTL". Since you don't have much work to do, polling in a tight loop could be feasible.
    You would wait for a low->high pattern and then pushing the internal tick counter value into the buffer.
    The "digitalReadFast()" function should be really fast (don't know the exact figure) on a T3.6, quite faster than a ISR.

    Still, let's wait for others to chime in!

Posting Permissions

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