daily_automated
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,98 @@
|
||||
Static Tests of the Arduino Internal ADC.
|
||||
|
||||
Several people have asked about the DC accuracy of the Arduino ADC when used in my data logging applications at slow sample rates.
|
||||
|
||||
Here are my results of some "hobby level" measurements of the Arduino ADC.
|
||||
|
||||
One question is how important is the ADC clock rate. I did measurents for an ADC clock rate of 125 kHz to 2MHz.
|
||||
|
||||
Another question is how much does Noise Reduction Mode help. I did a series of measurements using this mode.
|
||||
|
||||
Noise Reduction Mode only reduced the mean absolute error slightly.
|
||||
|
||||
I do calibration to remove Offset Error and Gain Error. Calibration is very important for good accuracy.
|
||||
|
||||
These tests depend on the Arduino voltage regulator providing a stable voltage during the tests. The Arduino ADC reference voltage is Vcc for these tests. This may not be realistic for practical applications
|
||||
|
||||
Integral Non-linearity (INL) is the main remaining source of error.
|
||||
|
||||
Here are my results for static (DC) tests of the internal ADC for three UNOs.
|
||||
|
||||
The Arduinos are powered by a high quality nine volt power supply.
|
||||
|
||||
These tests measure a DC level so do not include problems due to time jitter, S/H time, and other dynamic errors.
|
||||
There are several studies of the dynamic behavior of the Arduino ADC that determine ENOB (Effective Number Of Bits).
|
||||
|
||||
I used a shield with a 12-bit MCP4921 DAC to generate voltage levels. This ADC has an output buffer so it provides a very low impedance source.
|
||||
|
||||
I measured the voltage of the DAC with a calibrated 18-bit MCP3422 ADC on the shield.
|
||||
|
||||
I used DAC levels from 20 to 4075 to avoid zero offset errors at low voltages and DAC buffer problems at high voltages.
|
||||
|
||||
Each series of measurements has 4056 data points.
|
||||
|
||||
This is a voltage range of about 0.023 to 4.972 volts.
|
||||
|
||||
I calibrated the Arduino ADC for each series of measurements with a linear fit of the form.
|
||||
|
||||
v = a + b*adcValue
|
||||
|
||||
Errors are the difference between the value measured with the 18-bit ADC and the calibrated value measured with the AVR ADC.
|
||||
|
||||
I also show the results for no calibration, the NoCal column, using the datasheet formula.
|
||||
|
||||
Vin = Vref*adcValue/1024
|
||||
|
||||
|
||||
The rows in the tables tables are.
|
||||
|
||||
Min - minimum error in millivolts
|
||||
|
||||
Max - maximum error in millivolts
|
||||
|
||||
MAE - mean absolute error in millivolts
|
||||
|
||||
|
||||
The columns in the tables are:
|
||||
|
||||
Ideal - results for a perfect 10-bit ADC for comparison.
|
||||
|
||||
NoCal - datasheet formula (5/1024)*adcValue with Noise Reduction Mode.
|
||||
|
||||
NR128 - Noise Reduction mode with Prescaler of 128 (ADC clock of 125 kHz).
|
||||
|
||||
PS128 - analogRead with Prescaler of 128 (ADC clock of 125 kHz).
|
||||
|
||||
PS64 - analogRead with Prescaler of 64 (ADC clock of 250 kHz).
|
||||
|
||||
PS32 - analogRead with Prescaler of 32 (ADC clock of 500 kHz).
|
||||
|
||||
PS16 - analogRead with Prescaler of 16 (ADC clock of 1 MHz).
|
||||
|
||||
PS8 - analogRead with Prescaler of 8 (ADC clock of 2 MHz).
|
||||
|
||||
|
||||
Results for three UNO Arduinos
|
||||
|
||||
First Arduino - Error Millivolts
|
||||
|
||||
Ideal NoCal NR128 PS128 PS64 PS32 PS16 PS8
|
||||
Min -2.44 -2.43 -3.72 -4.01 -3.88 -4.53 -6.57 -27.18
|
||||
Max 2.44 11.69 3.74 4.24 4.15 5.17 8.69 23.21
|
||||
MAE 1.22 5.02 1.33 1.38 1.37 1.44 1.96 4.11
|
||||
|
||||
Second Arduino - Error Millivolts
|
||||
|
||||
Ideal NoCal NR128 PS128 PS64 PS32 PS16 PS8
|
||||
Min -2.44 -9.24 -4.87 -4.86 -5.05 -5.34 -6.52 -24.04
|
||||
Max 2.44 11.62 3.95 4.64 4.69 5.71 8.41 21.29
|
||||
MAE 1.22 5.33 1.41 1.43 1.44 1.53 2.02 4.05
|
||||
|
||||
Third Arduino - Error Millivolts
|
||||
|
||||
Ideal NoCal NR128 PS128 PS64 PS32 PS16 PS8
|
||||
Min -2.44 -7.88 -4.12 -4.40 -4.32 -4.41 -6.97 -26.93
|
||||
Max 2.44 12.53 3.80 4.04 4.18 5.27 8.84 24.59
|
||||
MAE 1.22 4.85 1.29 1.33 1.34 1.42 1.91 4.10
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
@@ -0,0 +1,21 @@
|
||||
Maximum Sample Rate Table
|
||||
|
||||
ADC clock kHz
|
||||
125 250 500 1000
|
||||
pins
|
||||
1 7692 14286 25000 40000
|
||||
2 3810 6667 11111 16667
|
||||
3 2572 4790 8421 13559
|
||||
4 1942 3636 6452 10526
|
||||
5 1559 2930 5229 8602
|
||||
6 1303 2454 4396 7273
|
||||
7 1119 2111 3791 6299
|
||||
8 980 1852 3333 5556
|
||||
9 872 1649 2974 4969
|
||||
10 786 1487 2685 4494
|
||||
11 715 1354 2446 4103
|
||||
12 656 1242 2247 3774
|
||||
13 606 1148 2078 3493
|
||||
14 563 1067 1932 3252
|
||||
15 525 996 1806 3042
|
||||
16 493 935 1695 2857
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef AnalogBinLogger_h
|
||||
#define AnalogBinLogger_h
|
||||
//------------------------------------------------------------------------------
|
||||
// First block of file.
|
||||
struct metadata_t {
|
||||
unsigned long adcFrequency; // ADC clock frequency
|
||||
unsigned long cpuFrequency; // CPU clock frequency
|
||||
unsigned long sampleInterval; // Sample interval in CPU cycles.
|
||||
unsigned long recordEightBits; // Size of ADC values, nonzero for 8-bits.
|
||||
unsigned long pinCount; // Number of analog pins in a sample.
|
||||
unsigned long pinNumber[123]; // List of pin numbers in a sample.
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
// Data block for 8-bit ADC mode.
|
||||
const size_t DATA_DIM8 = 508;
|
||||
struct block8_t {
|
||||
unsigned short count; // count of data bytes
|
||||
unsigned short overrun; // count of overruns since last block
|
||||
unsigned char data[DATA_DIM8];
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
// Data block for 10-bit ADC mode.
|
||||
const size_t DATA_DIM16 = 254;
|
||||
struct block16_t {
|
||||
unsigned short count; // count of data bytes
|
||||
unsigned short overrun; // count of overruns since last block
|
||||
unsigned short data[DATA_DIM16];
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
// Data block for PC use
|
||||
struct adcdata_t {
|
||||
unsigned short count; // count of data bytes
|
||||
unsigned short overrun; // count of overruns since last block
|
||||
union {
|
||||
unsigned char u8[DATA_DIM8];
|
||||
unsigned short u16[DATA_DIM16];
|
||||
} data;
|
||||
};
|
||||
#endif // AnalogBinLogger_h
|
||||
@@ -0,0 +1,82 @@
|
||||
#include <stdio.h>
|
||||
#include "AnalogBinLogger.h"
|
||||
FILE *source;
|
||||
FILE *destination;
|
||||
int count = 0;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
metadata_t meta;
|
||||
adcdata_t adc;
|
||||
// Make sure no padding/size problems.
|
||||
if (sizeof(meta) != 512 || sizeof(adc) != 512) {
|
||||
printf("block size error\n");
|
||||
return 0;
|
||||
}
|
||||
if (argc != 3) {
|
||||
printf("missing arguments:\n");
|
||||
printf("%s binFile csvFile\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
source = fopen(argv[1], "rb");
|
||||
if (!source) {
|
||||
printf("open failed for %s\n", argv[1]);
|
||||
return 0;
|
||||
}
|
||||
if (fread(&meta, sizeof(meta), 1, source) != 1) {
|
||||
printf("read meta data failed\n");
|
||||
return 0;
|
||||
}
|
||||
if ( meta.pinCount == 0
|
||||
|| meta.pinCount > (sizeof(meta.pinNumber)/sizeof(meta.pinNumber[0]))
|
||||
|| meta.adcFrequency < 50000 || meta.adcFrequency > 4000000) {
|
||||
printf("Invalid meta data\n");
|
||||
return 0;
|
||||
}
|
||||
destination = fopen(argv[2], "w");
|
||||
if (!destination) {
|
||||
printf("open failed for %s\n", argv[2]);
|
||||
return 0;
|
||||
}
|
||||
int pinCount = meta.pinCount;
|
||||
printf("pinCount: %d\n", pinCount);
|
||||
printf("Sample pins:");
|
||||
for (unsigned i = 0; i < meta.pinCount; i++) {
|
||||
printf(" %d", meta.pinNumber[i]);
|
||||
}
|
||||
printf("\n");
|
||||
printf("ADC clock rate: %g kHz\n", 0.001*meta.adcFrequency);
|
||||
float sampleInterval = (float)meta.sampleInterval/(float)meta.cpuFrequency;
|
||||
printf("Sample rate: %g per sec\n", 1.0/sampleInterval);
|
||||
printf("Sample interval: %.4f usec\n", 1.0e6*sampleInterval);
|
||||
|
||||
fprintf(destination, "Interval,%.4f,usec\n", 1.0e6*sampleInterval);
|
||||
// Write header with pin numbers
|
||||
for (int i = 0; i < ((int)meta.pinCount - 1); i++) {
|
||||
fprintf(destination, "pin%d,", meta.pinNumber[i]);
|
||||
}
|
||||
fprintf(destination, "pin%d\n", meta.pinNumber[meta.pinCount - 1]);
|
||||
unsigned maxCount = meta.recordEightBits ? DATA_DIM8 : DATA_DIM16;
|
||||
while (!feof(source)) {
|
||||
if (fread(&adc, sizeof(adc), 1, source) != 1) break;
|
||||
if (adc.count > maxCount) {
|
||||
printf("****Invalid data block****\n");
|
||||
return 0;
|
||||
}
|
||||
if (adc.overrun) {
|
||||
fprintf(destination, "Overruns,%d\n", adc.overrun);
|
||||
}
|
||||
for (int i = 0; i < adc.count; i++) {
|
||||
unsigned value = meta.recordEightBits ? adc.data.u8[i] : adc.data.u16[i];
|
||||
if ((i + 1)%pinCount) {
|
||||
fprintf(destination, "%d,", value);
|
||||
} else {
|
||||
fprintf(destination, "%d\n", value);
|
||||
}
|
||||
}
|
||||
count += adc.count;
|
||||
}
|
||||
printf("%d ADC values read\n", count);
|
||||
fclose(source);
|
||||
fclose(destination);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
AnalogBinLogger.ino logs analog data to a binary SD file at high rates.
|
||||
|
||||
Samples are logged at regular intervals by using timer1. Timer/Counter1
|
||||
Compare Match B is used to trigger the ADC for the first pin in a sample.
|
||||
The ADC is triggered for remaining sample pins in the ADC conversion complete
|
||||
interrupt routine.
|
||||
|
||||
Data is captured in the ADC interrupt routine and saved in 512 byte buffers.
|
||||
|
||||
Buffered data is written to the SD in a function called from loop(). The
|
||||
entire data set is written to a large contiguous file as a single multi-block
|
||||
write. This reduces write latency problems.
|
||||
|
||||
Many inexpensive SD cards work well at lower rates. I used a $6.00
|
||||
SanDisk 4 GB class 4 card for testing.
|
||||
|
||||
SanDisk class 4 cards work well at fairly high rates. I used the 4 GB SanDisk
|
||||
card to log a single pin at 40,000 samples per second.
|
||||
|
||||
You may need to increase the time between samples if your card has higher
|
||||
latency. Using a Mega Arduino can help since it has more buffering.
|
||||
|
||||
The bintocsv folder contains a PC program for converting binary files to
|
||||
CSV files. Build it from the included source files. bintocvs is a command line program.
|
||||
|
||||
bintocsv binFile csvFile
|
||||
|
||||
AnalogBinLogger requires a recent version of the SdFat library. The SdFat
|
||||
folder contains a beta version I used for development.
|
||||
|
||||
The latest stable version is here:
|
||||
http://code.google.com/p/sdfatlib/downloads/list
|
||||
|
||||
You also need to install the included BufferedWriter library. It provides
|
||||
fast text formatting.
|
||||
|
||||
Example data for a 2 kHz sine wave logged at 40,000 samples per second is
|
||||
shown in DATA.PNG and FFT.PNG shows a FFT of the data. See ExcelFFT.pdf
|
||||
in the ADCdocs folder for details on calculating a FFT.
|
||||
|
||||
The accuracy of the ADC samples depends on the ADC clock rate. See the
|
||||
ADC_ENOB.PNG file for a plot of accuracy vs ADC clock frequency.
|
||||
|
||||
See files in the ADCdocs folder for more information on ADC accuracy.
|
||||
|
||||
To modify this program you will need a good knowledge of the Arduino
|
||||
ADC, timer1 and C++ programming. This is not for the newbie.
|
||||
|
||||
I have an LED and resistor connected to pin 3 to signal fatal errors and
|
||||
data overruns. Fatal errors are indicated by a blinking led. Overrun errors
|
||||
are indicated by a solid lit led. The count of samples dropped is written
|
||||
to the SD and data logging continues.
|
||||
|
||||
You can disable the error led feature by setting the error pin number negative:
|
||||
|
||||
To use AnalogBinLogger, install these items.
|
||||
|
||||
Place the BufferWriter and SdFat folders in your sketchbook libraries folder.
|
||||
|
||||
Place the AnalogIsrLogger folder in your sketchbook folder.
|
||||
|
||||
You must edit the configuration constants at the beginning of the program
|
||||
to set the sample pins, sample rate, and other configuration values.
|
||||
|
||||
Initially the program is setup to log the first five analog pins at 5000
|
||||
samples per second. Change these values to suit your needs.
|
||||
|
||||
See RateTable.txt for maximum allowed sample rates vs pin count and ADC clock
|
||||
frequency.
|
||||
|
||||
The program has four commands:
|
||||
|
||||
c - convert file to CSV
|
||||
d - dump data to Serial
|
||||
e - overrun error details
|
||||
r - record ADC data
|
||||
|
||||
All commands can be terminated by entering a character from the serial monitor.
|
||||
|
||||
The c command converts the current binary file to a text file. Entering a
|
||||
character on the serial monitor terminates the command.
|
||||
|
||||
The d command converts the binary file to text and displays it on the serial
|
||||
monitor. Entering a character on the serial monitor terminates the command.
|
||||
|
||||
The e command displays details about overruns in the current binary file.
|
||||
Data overruns happen when data samples are lost due to long write latency
|
||||
of the SD.
|
||||
|
||||
The r command will record ADC data to a binary file. It will terminate
|
||||
when a character is entered on the serial monitor or the the maximum file
|
||||
block count has been reached.
|
||||
|
||||
A number of program options can be set by changing constants at the beginning
|
||||
of the program.
|
||||
Reference in New Issue
Block a user