Daniel-Dane
Member
[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):
And the Matlab code:
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/49730966987/alpha-particle-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]
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/49730966987/alpha-particle-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: