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

Thread: [Teensy 3.1] Problems with the ADC for my alpha spectroscopy project

  1. #1
    Junior Member
    Join Date
    Mar 2014
    Posts
    5

    [Teensy 3.1] Problems with the ADC for my alpha spectroscopy project

    [See very last post for solution/update]

    Howdy. I've been using the Teensy 3.1 as a multichannel analyzer (MCA) for my alpha spectroscopy project. I have the ADC set up for highest precision (disable fast speed, longest sample time, 32 samples; see code). Without doing all the tricks with separating VUSB and VIN, using capacitors, and what-not, I could squeeze out up to 14,5 bits of precision in 40 Ás. And that was in single-ended mode.

    So, the way I calculated the precision was measuring a steady 1, 2, and 3 volts from a power supply for 30 seconds and doing std(output) on the data with Matlab, so I would for example get a standard deviation of 3 channels on 16 bit which gives me an LSB of 150 ÁV or 14,5 bits. And this was using the USB power.

    The problem is that I get some weird spikes and valleys on my alpha spectroscopy histograms (images farther down), so I tried cutting VUSB and VIN and power directly to the 3V3 on Teensy with and without a 100 nF cap. That only made it worse. Using the "better" 1.2 volts reference also made it slightly worse. Alternate clock also made it slightly worse. I haven't got my hands on a wave generator yet, so I can't accurately calculate DNL/INL.

    Ever since that, I get a standard deviation somewhere between 1 and 50 for 16 bit, unrelated to the measured three voltages.

    Here's the Teensy code with all the standard ADC settings (which, apart from bit mode, I'd like to call ADC_max):
    Code:
    // __MK20DX256__ = Teensy 3.1
    #ifndef __MK20DX256__
      #error This code is for Teensy 3.1!
    #endif
    
    // Assumes Teensy 3.1
    
    // Result from ADC
    int ADC_res;
    
    void setup() {
      // Prepare USB connection
      Serial.begin(0);
      
      // Prepare output signal
      pinMode(13, OUTPUT);
      
      // Prepare reset signal
      pinMode(15, OUTPUT);
      
      // Prepare intterrupt signal
      pinMode(14, INPUT);
      attachInterrupt(14, processData, RISING);
      
      // Configure ADC
      /*
          0 ADLPC (Low-Power Configuration)
          0 ADIV (Clock Divide Select)
          0
          1 ADLSMP (Sample time configuration)
          0 MODE (Conversion mode selection) (00=8/9, 01=12/13, 10=10/11, 11=16/16 bit; diff=0/1)
          1
          0 ADICLK (Input Clock Select)
          0
      */
      ADC0_CFG1 = 0b00010100;
      
      /*
          0 MUXSEL (ADC Mux Select)
          0 ADACKEN (Asynchrononous Clock Output Enable)
          0 ADHSC (High-Speed Configuration)
          0 ADLSTS (Long Sample Time Select) (00=+20 cycles, 01=+12, 10=+6, 11=+2)
          0
      */
      ADC0_CFG2 = 0b00000;
      
      /*
          0 ADTRG (Conversion Trigger Select)
          0 ACFE (Compare Function Enable)
          0 ACFGT (Compare Function Greater than Enable)
          0 ACREN (Compare Function Range Enable)
          0 ACREN (COmpare Function Range Enable)
          0 DMAEN (DMA Enable)
          0 REFSEL (Voltage Reference Selection) (00=default,01=alternate,10=reserved,11=reserved)
      */
      ADC0_SC2 = 0b0000000;
      
      /*
          1 CAL (Calibration)
          0 CALF (read only)
          0 (Reserved)
          0
          0 ADCO (Continuous Conversion Enable)
          1 AVGS (Hardware Average Enable)
          1 AVGS (Hardware Average Select) (00=4 times, 01=8, 10=16, 11=32)
          1
      */
      ADC0_SC3 = 0b10000111;
      
      // Waiting for calibration to finish. The documentation is confused as to what flag to be waiting for (SC3[CAL] on page 663 and SC1n[COCO] on page 687+688).
      while (ADC0_SC3 & ADC_SC3_CAL) {}
      
      // Apply calibration data (code from officiel Teensy library)
      /* Teensyduino Core Library
      * http://www.pjrc.com/teensy/
      * Copyright (c) 2013 PJRC.COM, LLC.
      
      \teensy\cores\teensy3\analog.c
      */
      uint16_t sum;
      sum = ADC0_CLPS + ADC0_CLP4 + ADC0_CLP3 + ADC0_CLP2 + ADC0_CLP1 + ADC0_CLP0;
      sum = (sum / 2) | 0x8000;
      ADC0_PG = sum;
      sum = ADC0_CLMS + ADC0_CLM4 + ADC0_CLM3 + ADC0_CLM2 + ADC0_CLM1 + ADC0_CLM0;
      sum = (sum / 2) | 0x8000;
      ADC0_MG = sum;
      /* End of borrowed code */
    }
    
    void loop() {
    }
    
    void processData() {
      // Wait for signal to reach peak
      delayMicroseconds(15);
      
      // Signal HIGH that ADC and then USB will be running
      GPIOC_PDOR |= 1<<5;
      
      // Start ADC
      /*
          0 AIEN (Interrupt Enable)
          0 DIFF (0=single, 1=diff)
          0 ADCH (00000=A10 for DIFF=0; 00000=A10+A11 for DIFF=1; See page 98 in documentation)
          0
          0
          0
          0
      */
      ADC0_SC1A = 0b0000000;
      
      // Wait for ADC
      while (!(ADC0_SC1A & ADC_SC1_COCO)) {}
      
      // Retrieve data from ADC
      ADC_res = ADC0_RA;
      
      // Send data away
      Serial.println( ADC_res );
      Serial.send_now();
      
      // Set RESET signal to HIGH for 1 Ás
      GPIOC_PDOR |= 1; // 1 = 1<<0
      delayMicroseconds(1);
      
      // Signal LOW that ADC and USB are done (takes ~40 Ás with max cycles and sampling)
      // And set RESET signal to LOW
      GPIOC_PDOR &= ~(1<<5 | 1);
    }
    And the Matlab code:
    Code:
    %% Data collection %%
    % time to run in seconds
    t = 60*60;
    
    % Communication port to the Teensy
    COMPORT = 'COM8';
    
    % Prepare file with current date and time
    strname = ['data/' strjoin(fix(clock), '-') '.txt'];
    fid = fopen(strname, 'w');
    
    % Prepare serial communication
    s = serial(COMPORT);
    
    try
        fopen(s);
    catch %#ok<CTCH>
        % Failed, properly because the handle was not closed, so we're freeing
        % all the handles and trying again.
        delete(instrfindall)
        s = serial(COMPORT);
        fopen(s);
    end
    
    % Using try/catch to safely let go of serial handle in case of trouble
    try
        % Prepare timer
        tic
        
        % Flush buffer
        if (s.BytesAvailable > 0)
            fread(s, s.BytesAvailable);
        end
    
        % Data acquisition
        while (toc < t)
            % Read value from buffer
            val = fscanf(s);
            
            % Save value to file
            fwrite(fid, val);
        end
    catch err
        % Deinitialize
        fclose(s);
        delete(s);
        fclose(fid);
        clear s t
        rethrow(err);
    end
        
    % Deinitialize
    fclose(s);
    delete(s);
    fclose(fid);
    clear s t
    
    %% Data processing %%
    % Read from file
    assert(exist('strname', 'var')==1, 'voltage_reader error: strname not set.');
    if exist(strname, 'file') ~= 2
        error(['voltage_reader error: File (strname=' strname ') doesn''t exist.']);
    end
    fid = fopen(strname, 'r');
    
    try
        % Ignore two first lines in case of data collection started in the middle of a transfer
        output = textscan(fid, '%f', 'HeaderLines', 2);
        output = output{1};
        % Ignore last line in case of data collection ended in the middle of a transfer
        output(end) = [];
    catch err
        fclose(fid);
        clear fid
        rethrow(err);
    end
    
    % Get file last modified date minus creation date (=the duration for the experiment)
    listing = dir(strname);
    fname   = listing.name;
    fname   = fname(1:end-4);
    time    = (datenum(listing.date) - datenum(fname, 'yyyy-mm-dd-HH-MM-SS')) * 24*60; % in minutes
    
    % Deinitialize
    fclose(fid);
    clear fid listing fname
    
    % Make histogram (place into bins)
    [n, xout] = hist(output, 1000);
    
    % Show data
    figure;
    bar(xout, n);
    xlabel('Channel');
    ylabel('Counts');
    title(['Running time: ' int2str(time) ' minutes (' int2str(time*60) ' seconds)']);
    
    % Show log(data)
    figure;
    n = log(n);
    bar(xout, n);
    xlabel('Channel');
    ylabel('log(conts)');
    title(['Running time: ' int2str(time) ' minutes (' int2str(time*60) ' seconds)']);
    
    % Deinitialize
    clear xout n time
    Image time: http://imgur.com/a/kMyQI

    Alright, the first two images are from a measurement of Am-241 in an hour with ADC_max + 12 bit where the spikes/valleys are visible though nothing like the horribly bad measurement at ADC_max + 16 bit. The last two are a measurement of 2 V for 30 seconds at ADC_max + 16 bit. Again, spikes and valleys and not Gaussian. At 12 bits (not pictured), the 1, 2, and 3 volt measurements have std ~= 1 and a nice normal distribution, but the alpha spectroscopy measurement at 12 bit still shows those spikes and valleys.

    The alpha spectroscopy histogram should look very much like http://i.imgur.com/IgqPTYc.png (source: http://bingqingli.tumblr.com/post/49...e-spectroscopy)

    My problem is then the following: Even at 12 bit, the alpha spectroscopy measurement has those spikes and valleys even though all the steady voltage measurements show nice Gaussian distributions. Where is the problem? The errors seem systematic, so can I correct for this?

    P.S.
    Everything is done in single-ended mode on A10. (Differential between A10 and A11 doesn't really improve precision.)

    [See very last post for solution/update]
    Last edited by Daniel-Dane; 01-04-2015 at 06:20 PM.

  2. #2
    Senior Member
    Join Date
    Nov 2012
    Location
    Boston, MA, USA
    Posts
    1,112
    Quote Originally Posted by Daniel-Dane View Post
    So, the way I calculated the precision was measuring a steady 1, 2, and 3 volts from a power supply for 30 seconds and doing std(output) on the data with Matlab, so I would for example get a standard deviation of 3 channels on 16 bit which gives me an LSB of 150 ÁV or 14,5 bits. And this was using the USB power.
    How confidently do you know that the power supply is stable over time (to microvolt precision)? I would suggest using a precision voltage reference, or even a battery, to avoid any noise injection from switchig power supplies, ripple, etc. That allows you to seperate out the distribution of ADC results from the distribution of power supply output.

    About the difference between steady-state and dynamic results, one possibility is that the sampling capacitor in the ADC needs time to charge up to a new value. Using a flywheel capacitor and/or driving from a low impedance source will help it do that more rapidly.

    The regularly spaced gaps in the log plot power supply measurement are mysterious. It looks almost like rounding error except these are raw digital counts. Maybe an artifact of the ADC averaging process?

  3. #3
    Junior Member
    Join Date
    Mar 2014
    Posts
    5
    I have yet to test the voltage supply under load but I guess the 100 nF should have helped with the spikes. I'll get some batteries, but in the end the noise on the chip/board itself is too great for 16 bit - and that's okay. I am powering it with the single version of Tenma 72-10505. I'll get its name when I get to it.

    At 12 bit, I should be getting precise measurements since I already have the sample time at 20 extra cycles, an impedance at less than 150 Ω and a capacitor in my peak-hold circuit for when I do the alpha spectroscopy.

    If I lower the number of samples per conversion, it looks uglier and uglier, so I hope not.

    I'll try with the battery first then.

  4. #4
    Senior Member
    Join Date
    Oct 2012
    Location
    Portland OR
    Posts
    706
    Quote Originally Posted by Nantonos View Post
    [...]The regularly spaced gaps in the log plot power supply measurement are mysterious. It looks almost like rounding error except these are raw digital counts. Maybe an artifact of the ADC averaging process?
    I notice this:
    Code:
    // Apply calibration data
    If you have a calibration process, does that mean there is a scale and offset going on inside the chip, so the "raw digital counts" are not actually raw? That would explain regular gaps in a histogram. As would plotting a high-density histogram on a relatively low-resolution bitmap (pixel aliasing).

    This does not explain the data you show, though; your "16 bit 2V" histogram looks extremely non-gaussian as you point out. A battery will surely have lower noise than any bench power supply outside of an exotic standards-lab voltage reference. I'll try running your code on my T3.1 when I get a chance.

    One other thing- when I was playing around with the ADC on Teensy 3.0 I noticed the various different ADC inputs were not the same for offset or noise level, even though they connect to the same internal ADC on the other side of the mux. You might try a different input pin, just for fun.
    Last edited by JBeale; 03-11-2014 at 08:17 PM.

  5. #5
    Junior Member
    Join Date
    Mar 2014
    Posts
    5
    So I got hold of a HAMEG HM8030-4 for making sinus waves. Don't google it, because I haven't found the right one on the web for some reason.

    Here follows my experiment.

    Explanation
    Tenma: Tenma 72-10480 power supply at 3V3 fed into 3V3 on Teensy
    Battery: 3*AA (1.5 V) in series giving about 4.5-5 V into VIN on Teensy
    VUSB: VUSB and VIN connected, powered over USB
    Battery2: Battery as well as 2*AA (1.5 V) in series giving a little over 3 V fed into AREF.

    The setup
    AGND is connected to Tenma's GND at all times. Tenma is off when not in use.
    For all measurements, except those noted as @A2, A10 is used.
    The ground part of the connection between the generator and Teensy is connected to GND on the Teensy.
    The waves were from 0 V to about 1.5 V. Frequency was 1 kHz, 10 kHz, and 100 Hz, respectively.
    I've also noted on the pictures below the mode (10, 12, or 16 bit).
    I didn't use any capacitor, not on VIN, 3V3, AREF, nor A10/A2.

    This is the code I used on the Teensy (same as the steady voltage measurements from my first post):

    Code:
    void setup() {
      Serial.begin(0);
      
      /*
          0 ADLPC (Low-Power Configuration)
          0 ADIV (Clock Divide Select)
          0
          1 ADLSMP (Sample time configuration)
          1 MODE (Conversion mode selection) (00=8/9, 01=12/13, 10=10/11, 11=16/16 bit; diff=0/1)
          1
          0 ADICLK (Input Clock Select)
          0
      */
      ADC0_CFG1 = 0b00011100;
      
      /*
          0 MUXSEL (ADC Mux Select)
          0 ADACKEN (Asynchrononous Clock Output Enable)
          0 ADHSC (High-Speed Configuration)
          0 ADLSTS (Long Sample Time Select) (00=+20 cycles, 01=+12, 10=+6, 11=+2)
          0
      */
      ADC0_CFG2 = 0b00000;
      
      /*
          0 ADTRG (Conversion Trigger Select)
          0 ACFE (Compare Function Enable)
          0 ACFGT (Compare Function Greater than Enable)
          0 ACREN (Compare Function Range Enable)
          0 ACREN (COmpare Function Range Enable)
          0 DMAEN (DMA Enable)
          0 REFSEL (Voltage Reference Selection) (00=default,01=alternate,10=reserved,11=reserved)
      */
      ADC0_SC2 = 0b0000000;
      
      /*
          1 CAL (Calibration)
          0 CALF (read only)
          0 (Reserved)
          0
          0 ADCO (Continuous Conversion Enable)
          1 AVGS (Hardware Average Enable)
          1 AVGS (Hardware Average Select) (00=4 times, 01=8, 10=16, 11=32)
          1
      */
      ADC0_SC3 = 0b10000111;
      
      // Waiting for calibration to finish. The documentation is confused as to what flag to be waiting for (SC3[CAL] on page 663 and SC1n[COCO] on page 687+688).
      while (ADC0_SC3 & ADC_SC3_CAL) {}
      
      /* Teensyduino Core Library
      * http://www.pjrc.com/teensy/
      * Copyright (c) 2013 PJRC.COM, LLC.
      
      C:\Program Files (x86)\Arduino\hardware\teensy\cores\teensy3\analog.c
      */
      uint16_t sum;
      sum = ADC0_CLPS + ADC0_CLP4 + ADC0_CLP3 + ADC0_CLP2 + ADC0_CLP1 + ADC0_CLP0;
      sum = (sum / 2) | 0x8000;
      ADC0_PG = sum;
      sum = ADC0_CLMS + ADC0_CLM4 + ADC0_CLM3 + ADC0_CLM2 + ADC0_CLM1 + ADC0_CLM0;
      sum = (sum / 2) | 0x8000;
      ADC0_MG = sum;
      /* End of copyright notice */
    }
    
    void loop() {
      /*
          0 AIEN (Interrupt Enable)
          0 DIFF (0=single, 1=diff)
          0 ADCH (00000=DADP0 for DIFF=0; 00000=DAD0 for DIFF=1)
          0
          0
          0
          0
      */
      ADC0_SC1A = 0b0000000;
      
      // Wait for ADC
      while (!(ADC0_SC1A & ADC_SC1_COCO)) {}
      Serial.println( ADC0_RA );
      Serial.send_now();
    }
    The Matlab code for retrieving and loading the data sent from the Teensy is the same as in my first post.

    This is the code I used to make the plots:
    Code:
    function [dnl,inl,misscodes] = dnl_inl_sin(y)
    %DNL_INL_SIN
    % dnl and inl ADC output
    % input y contains the ADC output
    % vector obtained from quantizing a
    % sinusoid
    % Boris Murmann, Aug 2002
    % Bernhard Boser, Sept 2002
    % histogram boundaries
    minbin=min(y);
    maxbin=max(y);
    % histogram
    [h,x] = hist(y, minbin:maxbin);
    % cumulative histogram
    ch = cumsum(h);
    % transition levels found by:
    T = -cos(pi*ch/sum(h));
    % linearized histogram
    hlin = T(2:end) - T(1:end-1);
    % truncate at least first and last
    % bin, more if input did not clip ADC
    trunc=2;
    hlin_trunc = hlin(1+trunc:end-trunc);
    % calculate lsb size and dnl
    lsb= sum(hlin_trunc) / (length(hlin_trunc));
    dnl= [0 hlin_trunc/lsb-1];
    misscodes = length(find(dnl<-0.99));
    % calculate inl
    inl= cumsum(dnl);
    
    figure
    
    subplot(2, 2, 1);
    plot(x, h);
    xlabel('Channel');
    ylabel('Counts');
    title('Histogram');
    
    subplot(2, 2, 2);
    plot(1:length(dnl), dnl);
    xlabel('Channel');
    ylabel('DNL [LSB]');
    title(['DNL = +' num2str(max(dnl),2) ' / ' num2str(min(dnl),2) ' LSB. No. of missing codes (DNL < -0.99): ' int2str(misscodes)]);
    
    subplot(2, 2, 3);
    plot(x, log(h));
    xlabel('Channel');
    ylabel('log(counts)');
    title('Log-histogram');
    
    subplot(2, 2, 4);
    plot(1:length(inl), inl);
    xlabel('Channel');
    ylabel('INL [LSB]');
    title(['INL = +' num2str(max(inl),2) ' / ' num2str(min(inl),2) ' LSB.']);
    Which I got from http://www-inst.eecs.berkeley.edu/~e.../L12_2_f09.pdf (slide 18 on page 9). He says "codes", I use "channels".

    This produced the following data:
    http://imgur.com/a/AFukF

    Note that I did one measurement twice to see if and how the results changed. They seem to be the same within one magnitude and ignoring signs.

    I don't know what happens at 100 Hz. They might be the harmonics that the pdf is talking about on slide 15.

    Let us then ignore all the 100 Hz measurements and focus on the 10 kHz ones. At 12 bit, within one magnitude, I don't really see any difference between the different voltage inputs. At 10 bit, the INL is tolerable.

    Using A2 only made my results worse.

    Quote Originally Posted by JBeale View Post
    I notice this:
    Code:
    // Apply calibration data
    If you have a calibration process, does that mean there is a scale and offset going on inside the chip, so the "raw digital counts" are not actually raw? That would explain regular gaps in a histogram. As would plotting a high-density histogram on a relatively low-resolution bitmap (pixel aliasing).
    I doubt the calibration is messing up, but I can try without it. As for the last sentence, I've tried all kinds of different bins and have zoomed in on the figure (it's vectorized in Matlab), and the spikes and valleys are still there - well, until I get below 100 bins, but then all details are washed out.

    Quote Originally Posted by JBeale View Post
    This does not explain the data you show, though; your "16 bit 2V" histogram looks extremely non-gaussian as you point out. A battery will surely have lower noise than any bench power supply outside of an exotic standards-lab voltage reference. I'll try running your code on my T3.1 when I get a chance.
    I haven't yet tested the power supply under load, but I get equally bad results using batteries, so I'd like to rule all this "get better reference voltage" out, unless I've messed up badly somewhere.

    Quote Originally Posted by JBeale View Post
    One other thing- when I was playing around with the ADC on Teensy 3.0 I noticed the various different ADC inputs were not the same for offset or noise level, even though they connect to the same internal ADC on the other side of the mux. You might try a different input pin, just for fun.
    I tried A2, same results. I've read http://forum.pjrc.com/threads/15718-...-of-Teensy-3-0 but it looks like a static offset.

    Next up is trying without averaging and then without calibrating. While I am doing that, what could else be a problem?

    EDIT:
    By the way, the reference manual (K20P64M72SF1RM.pdf), in chaper 31.6.2, gives a list of things to do in order to improve performance. Most of the capacitors (if not all) are already on the board. It also ends with, "In 8-bit or 10-bit mode, the ADC is guaranteed to be monotonic and have no missing codes." I guess expecting anything above 10 bit is wishful thinking.

    The documentation (K20P64M72SF1.pdf) shows a (slightly) lower INL than I've obtained in table 25. These nonlinearities look about the same (slightly worse) than what the manufacturer obtained. Is this correct?

    I guess what I want to ask is, are these systematic spikes/valleys a product of high INL and thus only fixable by lowering the mode?
    Last edited by Daniel-Dane; 03-12-2014 at 04:46 PM.

  6. #6
    Senior Member
    Join Date
    Oct 2012
    Location
    Portland OR
    Posts
    706

    T3.1 ADC histogram gaps

    What external connections do you have on your Teensy? I tried running your code on my T3.1 and got no output. I guess it is waiting for some external trigger? I tried a simple program of my own running the ADC asynchronously with 16 sample averaging, with simple resistive divider to 3.3V on the A0 input, and I do see gaps in the histogram.
    Click image for larger version. 

Name:	T31-hist.jpg 
Views:	357 
Size:	53.6 KB 
ID:	1622

    Code:
    // Analog input test for Teensy 3.1
    
    #define ADCRES 16            // bits of resolution in ADC
    #define ADCMAX (1<<ADCRES)       // maximum possible reading from ADC
    #define READAVG 16            // how many samples readADC averages internally (1..16)
    
    const int analogInPin = A0;  // Analog input is AIN0 (Teensy3 pin 14, next to LED)
    const int LED1 = 13;         // output LED connected on Arduino digital pin 13
    
    int sensorValue = 0;        // value read from the ADC input
    long oldT;
    
    void setup() {    // ==============================================================
    unsigned int x;
    
          analogReference(EXTERNAL);  // set analog reference to internal ref
          analogReadRes(ADCRES);          // Teensy 3.0: set ADC resolution to this many bits
          analogReadAveraging(READAVG);    // average this many readings
         
          Serial.begin(115200);       // baud rate is ignored with Teensy USB ACM i/o
          digitalWrite(LED1,HIGH);   delay(1000);   // LED on for 1 second
          digitalWrite(LED1,LOW);    delay(3000);   // wait for slow human to get serial capture running
    
          do {
          x = analogRead(analogInPin);
          Serial.println(x);
          } while (1);
         
    } // ==== end setup() ===========
    
    void loop() {}

  7. #7
    Junior Member
    Join Date
    Mar 2014
    Posts
    5
    Yeah, the code in #1 is triggered by the amplifier. The code in #5 is pretty much the same, just running in loop(). The missing codes in your histogram is no biggie, at least the histogram is Gaussian.

    EDIT:
    I ran your code for 30 seconds with and without AGND connected to Tenma's GND. The 2360th line always skips a newline. Must be Matlab. The channels flip badly. Notice how the channel flips are symmetrical around the mean.

    Click image for larger version. 

Name:	sadness.jpg 
Views:	187 
Size:	37.5 KB 
ID:	1627Click image for larger version. 

Name:	sadness 2.jpg 
Views:	163 
Size:	39.8 KB 
ID:	1625Click image for larger version. 

Name:	sadness 3.jpg 
Views:	140 
Size:	41.2 KB 
ID:	1626

    What is happening? ):

    EDIT2:
    Hmm, looking at your graph again, I think 30 seconds was way too short. I gave it a few minutes and threw a few channels per bin. The result is much nicer, though I still see those flips:
    http://i.imgur.com/opozWTc.png
    Last edited by Daniel-Dane; 03-13-2014 at 12:42 PM. Reason: Ran your code

  8. #8
    Junior Member
    Join Date
    Mar 2014
    Posts
    5
    Eureka?

    I've swapped the module for another, run the code from #6, and waddya kno'; at one channel per bin, I see no flipping and no missing codes. I am letting it run for an hour on the alpha source, and my hopes are high.

    I've also noticed significantly more and, eh, better (as in not distorted) ringing on the oscilloscope, if it's not on 50 Ω. I guess the old board was defect.

    EDIT [SOLUTION]:
    It was partly due to a faulty module and partly due to running in 16 bit mode. Switch to 10 bit, and the data should look much better. I ended up doing 10 hours at 10 bit and got a quite good result.

    Also, I got bad results using differential mode, but I didn't extensively test it out (with both modules), so I can't say what the cause was.

    To optimize for speed, I didn't use library functions and did everything as low-level as possible. Here's my code, if you want to be inspired:

    Code:
    // __MK20DX256__ = Teensy 3.1
    #ifndef __MK20DX256__
      #error This code is for Teensy 3.1!
    #endif
    
    // Assumes Teensy 3.1
    
    // Result from ADC
    int ADC_res;
    
    void setup() {
      // Prepare USB connection
      Serial.begin(0);
      
      // Prepare output signal
      pinMode(13, OUTPUT);
      
      // Prepare reset signal
      pinMode(15, OUTPUT);
      
      // Prepare intterrupt signal
      pinMode(14, INPUT);
      attachInterrupt(14, processData, RISING);
      
      // Configure ADC
      /*  DEFAULT
          0       ADLPC (Low-Power Configuration)
          0       ADIV (Clock Divide Select)
          0
          0       ADLSMP (Sample time configuration)
          0       MODE (Conversion mode selection) (00=8/9, 01=12/13, 10=10/11, 11=16/16 bit; diff=0/1)
          0
          0       ADICLK (Input Clock Select)
          0
      */
      ADC0_CFG1 = 0b00011000;
      
      /*  DEFAULT
          0       MUXSEL (ADC Mux Select)
          0       ADACKEN (Asynchrononous Clock Output Enable)
          0       ADHSC (High-Speed Configuration)
          0       ADLSTS (Long Sample Time Select) (00=+20 cycles, 01=+12, 10=+6, 11=+2)
          0
      */
      ADC0_CFG2 = 0b00000;
      
      /*  DEFAULT
          0       ADTRG (Conversion Trigger Select)
          0       ACFE (Compare Function Enable)
          0       ACFGT (Compare Function Greater than Enable)
          0       ACREN (Compare Function Range Enable)
          0       ACREN (COmpare Function Range Enable)
          0       DMAEN (DMA Enable)
          0       REFSEL (Voltage Reference Selection) (00=default,01=alternate,10=reserved,11=reserved)
      */
      ADC0_SC2 = 0b0000000;
      
      /*  DEFAULT
          0       CAL (Calibration)
          0       CALF (read only)
          0       (Reserved)
          0
          0       ADCO (Continuous Conversion Enable)
          0       AVGS (Hardware Average Enable)
          0       AVGS (Hardware Average Select) (00=4 times, 01=8, 10=16, 11=32)
          0
      */
      ADC0_SC3 = 0b10000111;
      
      // Waiting for calibration to finish. The documentation is confused as to what flag to be waiting for (SC3[CAL] on page 663 and SC1n[COCO] on page 687+688).
      while (ADC0_SC3 & ADC_SC3_CAL) {}
      
      // Apply calibration data (code from officiel Teensy library)
      /* Teensyduino Core Library
      * http://www.pjrc.com/teensy/
      * Copyright (c) 2013 PJRC.COM, LLC.
      
      \teensy\cores\teensy3\analog.c
      */
      uint16_t sum;
      sum = ADC0_CLPS + ADC0_CLP4 + ADC0_CLP3 + ADC0_CLP2 + ADC0_CLP1 + ADC0_CLP0;
      sum = (sum / 2) | 0x8000;
      ADC0_PG = sum;
      sum = ADC0_CLMS + ADC0_CLM4 + ADC0_CLM3 + ADC0_CLM2 + ADC0_CLM1 + ADC0_CLM0;
      sum = (sum / 2) | 0x8000;
      ADC0_MG = sum;
      /* End of borrowed code */
    }
    
    void loop() {
    }
    
    void processData() {
      // Wait for signal to reach peak
      delayMicroseconds(15);
      
      // DEBUG: Signal HIGH that ADC and then USB will be running
      //GPIOC_PDOR |= 1<<5;
      
      // Start ADC
      /*  DEFAULT
          0       AIEN (Interrupt Enable)
          0       DIFF (0=single, 1=diff)
          1       ADCH (00000=A10 for DIFF=0; 00000=A10+A11 for DIFF=1; See page 98 in documentation)
          1
          1
          1
          1
      */
      ADC0_SC1A = 0b0000000;
      
      // Wait for ADC
      while (!(ADC0_SC1A & ADC_SC1_COCO)) {}
      
      // Retrieve data from ADC
      ADC_res = ADC0_RA;
      
      // Send data away
      Serial.println( ADC_res );
      Serial.send_now();
      
      // Set RESET signal to HIGH for 1 Ás
      GPIOC_PDOR |= 1; // 1 = 1<<0
      delayMicroseconds(1);
      
      // DEBUG: Signal LOW that ADC and USB are done (takes ~40 Ás with max cycles and sampling at 16 bit)
      // And set RESET signal to LOW
      //GPIOC_PDOR &= ~(1<<5 | 1);
      GPIOC_PDOR &= ~1;
    }
    The reset signal opens a MOSFET that short circuits the capacitor holding the signal from the peak detector.

    https://imgur.com/a/Qmkuw

    In the above album, the first image is the 10 hours of data collection from the alpha spectroscopy (teal) with the theoretical normal distributions on top (blue, red, green; black is sum of the three).

    The second image is the peak detector. The 10 MOhm (R3) resistor I added because of a relatively large leak current from the MOSFET when the reset signal went low.

    And finally, the third image is the oscilloscope used for debugging. The magenta signal is the trigger from the amplifier connected to the pre-amp of the detector. When it goes high, the Teensy waits 15 microseconds to ensure the peak detector is ready and then starts measuring. The amplitude of the teal signal is propertional to the alpha particle energy. The yellow signal is the output of the peak detector. When the Teensy sets the reset signal to high, the yellow signal drops and is thus ready for the next alpha particle.

    PM me if you have questions. I get the PM's forwarded to my email, so I will notice your message.
    Last edited by Daniel-Dane; 01-04-2015 at 06:23 PM.

  9. #9
    Junior Member
    Join Date
    Jun 2019
    Posts
    13
    I know this thread is quite old, however could you provide a picture of the circuit you constructed?

Posting Permissions

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