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

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
    Moderator Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    1,020
    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
    15,943
    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
    15,943
    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
    15,943
    I made a little video about this pulse count testing.

    Last edited by PaulStoffregen; 11-30-2017 at 08: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
    15,943
    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:	8 
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;
    }

Posting Permissions

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