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

Thread: ADC library update, now with support for Teensy 3.1

Threaded View

  1. #1
    Senior Member
    Join Date
    Jul 2013

    ADC library, with support for Teensy 3.0, 3.1/3.2, LC, 3.5 and 3.6

    Hello everybody,

    I've have finally updated the ADC library and now it supports the Teensy 3.1/3.2 two ADC.
    The code now has grown to about 1600 lines of actual code, and I think it's a good design to implement boards with even more ADCs.

    The library supports Teensy 3.0, 3.1/3.2, LC, 3.5, and 3.6.

    The main class is ADC.
    ADC *adc = new ADC(); // adc object
    This object controls the ADCs. For example, to measure the voltage at pin A9, you can do
    int value = adc->analogRead(A9);
    just as with the old version of this library. If you have a Teensy 3.0 it will return the value measured by the only ADC module.
    If you have a Teensy 3.1/3.2, it will look whether this pin can be accessed by ADC0, ADC1 or both. If only one ADC module can access it, it will use it. If both can do it, it will assign this measurement to the ADC with less workload (for example if one is doing a continuous measurement already).
    If you have a preference for one module in particular you can do:
    int value = adc->analogRead(A9, ADC_0);
    If the pin can't be accessed by the ADC you selected it will return ADC_ERROR_VALUE.

    It's also possible to access directly the adc modules with

    Overview of modes of conversion

    All modes of conversion of the ADC are implemented in the same fashion:
    Using 1 ADC
    Single-shot mode Single-shot mode Continuous mode
    return value return immediately
    analogRead startSingleRead startContinuous
    analogReadDifferential startSingleDifferential startContinuousDifferential
    readSingle() analogReadContinuous()
    Using both ADCs (Synchronous mode)
    Single-shot mode Single-shot mode Continuous mode
    return value return immediately
    analogSyncRead startSynchronizedSingleRead startSynchronizedContinuous
    analogSyncReadDifferential startSynchronizedSingleDifferential startSynchronizedContinuousDifferential
    readSynchronizedSingle readSynchronizedContinuous

    Programmable Gain Amplifier

    PGA can be activated with enablePGA(gain), where gain=1, 2, 4, 8, 16, 32 or 64. It will amplify signals of less that 1.2V only in differential mode (see examples). Only for Teensy 3.1/3.2.

    Periodic conversions

    There are two ways to do periodic conversions: using an IntervalTimer or using the PDB.

    It's possible (and easy) to use the IntervalTimer library with this library, see analogReadIntervalTimer.ino in the examples folder.

    PDB is supported on both ADCs, see the adc_pdb.ino example. The same frequency is used for both ADCs, unlike using IntervalTimers.

    Error detection
    Use adc->printError() to print any internal error (wrong pin, etc.).
    Use adc->resetError() to clean any errors.


    All analog pins can be measured, but not by both ADCs. The following images show which ADC can access each pin.
    If the text next to the pin number says ADC0_SE## then it can be measured by ADC0, if it says ADC1_SE## by ADC1. Some pins can be measured by both.

    Teensy 3.0:
    Click image for larger version. 

Name:	Teensy3_0_AnalogCard.png 
Views:	5136 
Size:	461.5 KB 
ID:	1792
    Teensy 3.1/3.2:
    Click image for larger version. 

Name:	Teensy3_1_AnalogCard.png 
Views:	16244 
Size:	539.2 KB 
ID:	1793
    Teensy 3.5:
    Click image for larger version. 

Name:	Teensy3_5_AnalogCard.jpg 
Views:	1288 
Size:	112.0 KB 
ID:	11814
    Teensy 3.6:
    Click image for larger version. 

Name:	Teensy3_6_AnalogCard.jpg 
Views:	2641 
Size:	127.0 KB 
ID:	11811

    Synchronous measurements

    ADC::Sync_result result = adc->analogSyncRead(pin1, pin2);
    It will set up both ADCs and measure pin1 with ADC0 and pin2 with ADC1. The result is stored in the struct ADC::Sync_result, with members .result_adc0 and .result_adc1 so that you can get both. ADC0 has to be able to access pin1 (same for pin2 and ADC1), to find out see in the image that next to the pin number is written ADC0_xxxx or ADC1_xxx (or both).

    I've made a simple test with a wavefunction generator measuring a sine wave of 1 Hz and 2 V amplitude with both ADCs at the same time.
    I've measured it synchronously in the loop(){} with a delay of 50ms (so 20Hz sampling), the results are here:
    Click image for larger version. 

Name:	SynchronousADC.jpg 
Views:	3732 
Size:	105.1 KB 
ID:	1794
    In red and black are the measurements on each pin, as you see, the difference is very small, so the system seems to work.

    Voltage reference

    The measurements are done comparing to a reference of known voltage. There are three options (two for Teensy LC):
    1. ADC_REFERENCE::REF_3V3: All boards have the 3.3 V output of the regulator (VOUT33 in the schematics). If VIN is too low, or you are powering the Teensy directly to the 3.3V line, this value can be lower. In order to know the value of the final measurement you need to know the value of the reference in some other way. This is the default option.
    2. ADC_REFERENCE::REF_EXT: The second option available to all boards is the external reference, the pin AREF. Simply connect this pin to a known voltage and it will be used as reference.
    3. ADC_REFERENCE::REF_1V2: Teensy 3.x have an internal 1.2V reference. Use it when voltages are lower than 1.2V or when using the PGA in Teensy 3.1/3.2.

    To change the reference:
    adc->setReference(option, ADC_x);
    ADC_REFERENCE is a class enum. The advantage of class enums with respect to #defines is that the values are checked at compile time, that is, if you write something like ADC_REFERENCE::REF_5V5, the compiler will tell you that value doesn't exist. If you try to use an int (from a define, for example), the compiler will complain again. This may save you from very sneaky bugs, where you use a value that does something different than what you expect.

    Sampling and conversion speed

    The measurement of a voltage takes place in two steps:
    1. Sampling: Load an internal capacitor with the voltage you want to measure. The longer you let this capacitor be charged, the closest it will resemble the voltage.
    2. Conversion: Convert that voltage into a digital representation that is as close as possible to the selected resolution.

    You can select the speed at which the sampling step takes place. Usually you can increase the speed if what you measure has a low impedance. However, if the impedance is high you should decrease the speed.
    adc->setSamplingSpeed(speed); // change the sampling speed
    • ADC_SAMPLING_SPEED::VERY_LOW_SPEED is the lowest possible sampling speed (+24 ADCK). (ADCK is the ADC clock speed, see below).
    • ADC_SAMPLING_SPEED::VERY_HIGH_SPEED is the highest possible sampling speed (0 ADCK added).

    The conversion speed can also be changed, and depends on the bus speed:
    adc->setConversionSpeed(speed); // change the conversion speed
    This will change the ADC clock, ADCK. And affects all stages in the measurement. You can check what the actual frequency for the current bus speed is in the header ADC_Module.h.
    • ADC_CONVERSION_SPEED::VERY_LOW_SPEED is guaranteed to be the lowest possible speed within specs for resolutions less than 16 bits (higher than 1 MHz), it's different from ADC_LOW_SPEED only for 24, 4 or 2 MHz.
    • ADC_CONVERSION_SPEED::LOW_SPEED is guaranteed to be the lowest possible speed within specs for all resolutions (higher than 2 MHz).
    • ADC_CONVERSION_SPEED::HIGH_SPEED_16BITS is guaranteed to be the highest possible speed within specs for all resolutions (lower or eq than 12 MHz).
    • ADC_CONVERSION_SPEED::HIGH_SPEED is guaranteed to be the highest possible speed within specs for resolutions less than 16 bits (lower or eq than 18 MHz).
    • ADC_CONVERSION_SPEED::VERY_HIGH_SPEED may be out of specs, it's different from ADC_HIGH_SPEED only for 48, 40 or 24 MHz.

    There's another option for the ADC clock, the asynchronous ADC clock, ADACK. It's independent on the bus frequency and has 4 settings: ADACK_2_4, ADACK_4_0, ADACK_5_2 and ADACK_6_2. The numbers indicate the frequency of the clock in MHz (so the first is 2.4 MHz).

    Both the sampling and the conversion speed affect the time it takes to measure, there are some tests in the continuous conversion examples.

    Other conversion sources

    All boards have a few additional internal conversion sources. The possible options are in the ADC_INTERNAL_SOURCE class enum:

    To convert from the temperature sensor voltage to ºC you can use:
    value = adc->analogRead(ADC_INTERNAL_SOURCE::TEMP_SENSOR);
    float volts = value*3.3/adc->getMaxValue(ADC_0);
    The constants 1.7 mV/ºC and 0.72 mV depend somewhat on the specific board, so a calibration is recommended.

    Library size
    The library uses both program and RAM space. The exact amount depends on the Teensy used and whether optimizations were used.
    The results in bytes are the respective progmem or ram of the analogRead.ino example minus that of the blinky example, the percentages are the memory used out of the total:

    Teensy 3.0, 96 MHz, Serial: 5744 B (11%) program storage space and 132 B (14%) dynamic memory space used.
    Teensy 3.1, 96 MHz, Serial, no optimizations: 6176 B (6%) program storage space and 136 B (3%) dynamic memory space used.
    Teensy 3.1, 96 MHz, Serial, with optimizations (default): 9244 B (8%) program storage space and 1116 B (7%) dynamic memory space used.
    Teensy LC, 48 MHz, Serial, no optimizations (default): 11120 B (33%) program storage space and 128 B (28%) dynamic memory space used.
    Teensy LC, 48 MHz, Serial, with optimizations: 14212 B (41%) program storage space and 1112 B (53%) dynamic memory space used.

    The progmem usage for Teensy LC is higher due to the use of floats (multiplying by 3.3). If possible, don't use floats at all in Teensy LC as they use about 6 kB of space (2 kB for Teenst 3.x).

    Now it works with the Audio library:
    Teensy 3.0 is compatible, except if you try to use the Audio library ADC input (AudioInputAnalog), because there's only one ADC module so you can't use it for two things.
    Teensy 3.1 is compatible. If you want to use AudioInputAnalog and the ADC you have to use the ADC_1 module, the Audio library uses ADC_0. Any calls to functions that use ADC_0 will most likely crash the program. Note: make sure that you're using pins that ADC_1 can read (see pictures above)! Otherwise the library will try to use ADC_0 and it won't work!!

    ADC library now supports Teensy LC!! Finally!

    PDB is supported on both ADCs

    Use adc->printError() to print any internal error (wrong pin, etc.)

    Support for Teensy 3.5 and 3.6

    New format for setReference, setSamplingSpeed, and setConversionSpeed. The information above has been updated.

    Added support for pins A25, A26 in Teensy 3.5

    Still TODO:

    Create Audio library object to handle either ADC. -> Paul added ADC1 to the Audio library ( So I don't think I'll work on this. There may still be some advantages in using the ADC library there though.

    Rework RingBuffer and RingBufferDMA, as they don't really work well now.
    Last edited by Pedvide; 10-18-2017 at 06:41 PM. Reason: updates

Posting Permissions

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