I am in the process of making a MCA (multichannel analyzer) and I require my teensy 4 to act as a SPI slave while being an ADC. I also require the ADC to have pulse pileup detection for overly high count rates. I am thinking this would be part of DMA (digital memory access)? I can find no references for pulse pile-up detection in the teensy literature. The idea is to dump the teensy 4.0s information via SPI into teensy 3.6 to do the math and binning of pulses for an energy histogram. Alternatively a Raspberry pi could also work with a python version of pulse capturing and binning energies.

The processing sketch ( which was featured on the American Journal of Physics 2018) works on the teensy 3.6 flawlessly... but I don't have the shield from TI (Digilent Analog Shield) as they're now no longer in production.

Can the Teensy dump ADC information via SPI and have real time pulse pileup detection/protection?

Below is the master code for the MCA based on the DAS from (TI)

* This code accompanies the article "Gamma Spectroscopy with Arduino UNO" by C.M. Lavelle and published in the American Journal of Physics.
* The Digilent Analog Shield (DAS) is used, and is compatable with UNO and DUE and chipkit products.
* If using with DUE, the SPI must be called.
* The DAS libraries must be included.
* This program outputs the pulse spectrum recorded on the peak and hold circuit (PKD01, PHIC in paper) to the serial port.
* The user is intended to cut and paste the data from the serial port to a text file for processing and plotting.
* The format is the spectrum on a single line, followed by the total milliseconds of measurement time since the device was reset.
* This is followed by several lines of "-1" to separate repeated data prints.
* The date will export at regular intervals as the spectrum accumulates.
* The user will note several delays and serial flush commands, these were found by the author to help ensure performance over long periods.
* The author has also found arduino uno pin 11 to be experiencing pickup, and should be avoided for selection of the logic pins.
* Initially, the HV is set to ~27 V and the cutoff to ~35 mV.
* Increasing the HV can increase signal intensity from the SiPM, but also may increase noise, and CUTOFF may need to be raised.
* The code functions by
* 1. Initialize (setup)
* a. Setup serial port
* b. Set PHIC to "off"
* c. Set HV to ~27 V if using a SENSL product (+2.5V over breakdown)
* HV Output = (ADC-32768)/32768*5*(100/2.5) = (ADC-32768)*0.006103515625
* 2. Measurement (loop)
* a. Set PHIC to tracking state
* b. Repeatedly measure the PHIC output on the DAS (analog.read)
* c. If the PHIC is storing a pulse >cutoff, put the PHIC into a hold state and set tracking to off
* d. Determine which element of the array the pulse belongs to (Eqn. 1 in the paper) and increment the count by 1
* e. reset the PHIC
* f. Check to see if it is time to export data (every 1000 events or 10 seconds)

#include <SPI.h> // required for ChipKIT and Arduino DUE
#include <ADC.h>
#include <ADC_util.h> // Include to use analog shield.
int resetPin = 8; // this pin resets the track/hold state
int detPin = 9; // track/hold pin

const int readPin = A9; // ADC0
const int readPin2 = A2; // ADC1

ADC *adc = new ADC(); // adc object;

void setup()
Serial.begin(115200); // Serial port setup
pinMode(resetPin, OUTPUT); // Logic Setup for PHIC control
pinMode(detPin, OUTPUT);

digitalWrite(resetPin,HIGH); // Set PHIC to non-track/non-hold state


/* data - variable to hold the measurement
* totalcnts - total number of events counted.
* Asize - spectrum number of channels. 512 points or less due to memory restrictions on the UNO.
* scale - makes the ADC maximum values no more than the array size
* spec - array variable holding the spectrum
* cutoff - the lower level noise floor. Peaks with ADC values above this are recorded.
* currentmillis,previousmillis, cntr - sets up the output timer to determine when to print to serial port
unsigned int data = 0;
long totalcnts = 0;
int Asize = 512;
int scale = 32768/Asize;
int spec[512] = {};
int cutoff = 32850; // nominal lower level cutoff.
long currentmillis = 0;
long previousmillis = 0;
long cntr = 0;

void loop() {

// set pins to low to set PHIC to tracking

// DAS analog read the peak and hold
const int readPin = 14; // ADC0 data = analog.read(0);

// if its above cutoff, indicating a peak is present, measure the peak
if (data > cutoff){

// Set tracking to off to avoid a later event distorting the current event's measurement.

// wait 45 us (settling time)

// Average 5 measurements

// Begin reset of the PHIC
// this will continue while other things are happening,
// reducing the total mount of time to wait for the reset.

// find out where in the pulse height diagram to put it (Eqn 1)
// The "2" is essenitally a software adjustable gain.
// Make sure the array index chosen is >=0 and <512, if so record the event and increment the counter.
int loc = floor(((data - 32768)*2/scale));
if (loc >= 0 & loc <512){
spec[loc] = spec[loc]+1;
cntr = cntr+1;
totalcnts = totalcnts+1;

// wait 10 us more to ensure the PHIC is fully reset.

// after event, decide if its time to write data.
// It is placed in the event recording IF statement to speed up the number of DAS reads on the PHIC per second.
currentmillis = millis();
if (currentmillis - previousmillis > 10000 || cntr > 1000){
for (int i = 0; i < Asize; i++){
// use serial.println for the serial plotter
Serial.print(" ");
// Wait for the 512 points to finish printing to the serial port.

// Print ancillary data followed by a delimiter of -1, then wait again for it to complete the operation.
// This is a good place to print things like the temperature, GPS coordinates, or the exposure time.
for (int i=0;i<3;i++){Serial.println(-1);}

// reset counters and timer
cntr = 0;
previousmillis = millis();