ADC library, with support for Teensy 4, 3.x, and LC

Thanks for looking into it. If I invert polarity to ADC_1; battery negative to A12/ADC1_DP0 and battery positive to A13/ADC1_DM0, the counts are positive.
 
gofaster,

I've looked into it and I've fixed the second problem you had. Indeed at 16 bits, synch analog differential returned half the correct value. This is now fixed at the github repo (see first page).

I can't replicate the first problem, however. I've checked at different resolutions and it works well. I don't have a Teensy 3.2, only 3.1's, but I don't think that's the problem. Are you sure your wiring is fine? Remember that the result is V(A12)-V(A13).
Please double and triple check, if it's still wrong I'd look for somebody with a Teensy 3.2 to check, in case is a hardware problem.
 
Hi Pedvide -

First, please accept my apologies for the false alarm. I triple checked this time and pulled the Teensy out of the fixture to verify. Indeed I had done a mental flip when I soldered the jumpers to the A12/A13 pads.:(
The patch does fix the SynchRead values. Thanks again for taking the time to look into this and for developing this library!
 
Hi, folks. I have some questions about the Teensy 3.1/3.2 ADC capabilities. In particular, I need ~1ns resolution on the input signal, but not a high sample rate (400 to 800 KHz is fine, which the Teensy can do). I can add an external sample/hold to achieve the 1ns resolution, but I would have to synchronize it to the ADC somehow. Is there any way to know when a new sample is starting? I just need an edge to trigger the external S/H. Thanks.
 
If the A/D clock is stable enough, just drive a cheap analog switch that has time-to-off of ~1ns, into a 10pF hold cap, and from there an opamp can drive the Teensy. Getting the RC time constant into the hold cap below 1ns is not hard. A GPIO could be used to drive the analog switch enable, though I would prefer to use continuous sampling mode.
 
If the A/D clock is stable enough, just drive a cheap analog switch that has time-to-off of ~1ns, into a 10pF hold cap, and from there an opamp can drive the Teensy. Getting the RC time constant into the hold cap below 1ns is not hard. A GPIO could be used to drive the analog switch enable, though I would prefer to use continuous sampling mode.

Granted that you get some sort of sample and hold that take less than 1ns and somehow you can read the sample, process and do something with it in only 1 clock step.
Problem is that if I'm not wrong one clock step in teensy at 96Mhz takes 10.4166667 nanoseconds.
 
So let me see if I understand what you want:
You want your measurements to start at a given time with a precision +-1ns, and repeat them at a +-500 kHz rate?
You have some external circuitry to signal the Teensy when to start the measurement, I assume.
On the other hand you say that you'd like to use the continuous mode. So the problem is to start the measurements at the right time.

The main thing to check is how long does the sampling phase take (the input voltage is "stored" in a capacitor before being converted).
A fast measurement takes about 1 us, if the sampling takes 10% of that, it's already 100 ns, so the value you are converting is some sort of average sample of 100 ns.

The programming datasheet has information about how long this phase takes, see page 680 ff. The fastest sampling possible is 6 ADCK for single measurements (or 1st in continuous) and 4 ADCK for continuous. For the standard speed of 96 MHz, F_BUS=48 MHz, and at the fastest settings ADCK=24 MHz, the sampling takes either 250 ns or 167 ns.
The conversion step takes 20 ADCK + 5 F_BUS = 0.94 us, the total time is therefore about 1.11 us.

I'd say it's not doable.
 
Using ADC library with platformio

I'm trying to move a sketch over to platformio and use CLion as the IDE, but when I build the project the ADC.h header wasn't found.

So, I downloaded from github and moved it into the lib/ subdirectory, but now it complains about the F_BUS macro not being defined:

Code:
--------------------------------------------------------------------------------
arm-none-eabi-g++ -o .pioenvs/teensy31/src/main.o -c -std=gnu++98 -fno-rtti -fdata-sections -ffunction-sections -Wno-unused-parameter -fno-exceptions -Wextra -fno-delete-null-pointer-checks -fmessage-length=0 -mthumb -Wno-missing-field-initializers -c -fno-builtin -O2 -fomit-frame-pointer -Wall -MMD -mcpu=cortex-m4 -DMBED_BUILD_TIMESTAMP=1464364165.29 -DTARGET_TEENSY3_1 -DTOOLCHAIN_GCC_ARM -DTOOLCHAIN_GCC -DTARGET_RTOS_M4_M7 -DTARGET_K20DX256 -DTARGET_CORTEX_M -DTARGET_LIKE_MBED -DTARGET_LIKE_CORTEX_M4 -DTARGET_M4 -D__MBED__=1 -DTARGET_Freescale -D__CORTEX_M4 -DARM_MATH_CM4 -DTARGET_K20XX -D__MK20DX256__ -DPLATFORMIO=021100 -I.pioenvs/teensy31/FrameworkArduino -I.pioenvs/teensy31/FrameworkMbedInc248832578 -I.pioenvs/teensy31/FrameworkMbedInc-213564679 -I.pioenvs/teensy31/FrameworkMbedInc-250686161 -I.pioenvs/teensy31/FrameworkMbedInc-1845648957 -I.pioenvs/teensy31/FrameworkMbedInc-666725808 -I.pioenvs/teensy31/FrameworkMbedInc357213333 -I.pioenvs/teensy31/FrameworkMbedInc1049033388 -I.pioenvs/teensy31/FrameworkMbedInc-194069199 -I.pioenvs/teensy31/Led -I.pioenvs/teensy31/SerialPanel -I.pioenvs/teensy31/ADC -Isrc src/main.cpp
In file included from .pioenvs/teensy31/ADC/ADC.h:44:0,
from src/main.cpp:5:
.pioenvs/teensy31/ADC/ADC_Module.h:302:2: error: #error "F_BUS must be 108, 60, 56, 54, 48, 40, 36, 24, 4 or 2 MHz"
#error "F_BUS must be 108, 60, 56, 54, 48, 40, 36, 24, 4 or 2 MHz"

Is anyone familiar with how to install this ADC library into a platformio based project?

Or please point me in the right direction if I should be asking elsewhere!
 
Well in any case you should start with an easier sketch like Blink.
F_CPU is passed to the compiler as a flag (see how arduino does it), F_BUS is then derived from F_CPU (usually it's half)
 
It works without any problems for me.

```
$ mkdir /tmp/teensy
$ platformio init -d /tmp/teensy -b teensy31
$ git clone https://github.com/pedvide/ADC.git /tmp/teensy/lib/ADC
$ cp /tmp/teensy/lib/ADC/examples/readPin/readPin.ino /tmp/teensy/src/readAllPins.ino
$ platformio run -d /tmp/teensy
```

The result: http://pastebin.com/xNY8C33D

P.S: I've jsut created an issue and we will add this library to our registry.

Thanks Ivan.

I've added a question on your PlatformIO community, because I'd like to make this library global, and keep it up-to-date outside of my specific project. -- https://community.platformio.org/t/installing-a-teensy-library-from-github-into-global-scope/478
 
Hi, Thanks a lot for your job! I have just one question:
I have upload on teensy3.2 your exemple called "ringBufferDMA.ino" (https://github.com/pedvide/ADC/blob/master/examples/ringBufferDMA/ringBufferDMA.ino). I don't understand why once the Buffer is full, only the first half become full and the second stay the same. Here is an exemple of what i get on my consol withe a buffer_size =2. I convert the value, read and print.
Buffer: Address, Value
1FFF8200, 306
1FFF8202, 373
Start DMA

Conversion:
ADC0_ISR
read(): 657
Buffer: Address, Value
1FFF8200, 657
1FFF8202, 373

Conversion:
ADC0_ISR
read(): 373
Buffer: Address, Value
1FFF8200, 582
1FFF8202, 373

Conversion:
ADC0_ISR
read(): 638
Buffer: Address, Value
1FFF8200, 638
1FFF8202, 373

Conversion:
ADC0_ISR
read(): 373
Buffer: Address, Value
1FFF8200, 774
1FFF8202, 373

Why the second half (here 373) is never changed?

thank you for your help

Maxime D
 
I have another noob question,
What is the difference between a ringBuffer object and a DMAringBuffer object?

Thanks in advance
 
Hi All,

Firstly, thank you very much for this library and hard work Pedvide, it truelly is excellent. Long time lurker on this thread as I'm trying to improve the sample rate I'm seeing on my teensy. Ideally I'm trying to get to 10KHz(ish) but currently I run into issues with data rate above 4KHz.

I've modified some sample code and added in interrupt based sampling. I've also used FlexiTimer2 (also MsTimer2 to see if it made a difference) to run a blink interupt to give the ADC something to sample.

The issue I run into is that at higher interupt rates I stop getting reliable readings (shown in the included picture).
Is this an issue with using the ADC in an interupt routine at a high rate, or is it that I'm running up against the Teensy sampling limit?

I've tried reading the rest of this thread but I can't seem to find anyone with a similar issue.

Running up to date Teensyduino and teensy 3.2
Capture.jpg

/* Example for synchonized measurements using both ADC present in Teensy 3.1
You can change the number of averages, bits of resolution and also the comparison value or range.
*/

#include<TimerOne.h>
#include<ADC.h>
#include<MsTimer2.h>
#include<FlexiTimer2.h>
const int led = LED_BUILTIN;
const int readPin = A1;
const int readPin2 = A3;
int count;
bool x = HIGH;
ADC *adc = new ADC(); // adc object
int value = 0;
int value2 = 0;
unsigned long blinkCopy; // holds a copy of the blinkCount
volatile unsigned long blinkCount = 0; // use volatile for shared variables
int ledState = LOW;
ADC::Sync_result result;
const int led_pin = 13;
void get_ADC();
void flash();

void setup()
{
pinMode(led_pin, OUTPUT);
pinMode(readPin, INPUT);
pinMode(readPin2, INPUT);
Timer1.initialize(500); //250microseconds = 4Khz
Timer1.attachInterrupt(get_ADC);
Serial.begin(9600);

adc->setAveraging(1); // set number of averages
adc->setResolution(8); // set bits of resolution
adc->setConversionSpeed(ADC_VERY_HIGH_SPEED); // change the conversion speed
adc->setSamplingSpeed(ADC_VERY_HIGH_SPEED); // change the sampling speed
adc->setAveraging(1, ADC_1); // set number of averages
adc->setResolution(8, ADC_1); // set bits of resolution
adc->setConversionSpeed(ADC_VERY_HIGH_SPEED, ADC_1); // change the conversion speed
adc->setSamplingSpeed(ADC_VERY_HIGH_SPEED, ADC_1); // change the sampling speed
adc->startSynchronizedContinuous(readPin, readPin2);

FlexiTimer2::set(500, 1.0/1000, flash); // call every 500 1ms "ticks"
// FlexiTimer2::set(500, flash); // MsTimer2 style is also supported
FlexiTimer2::start();
}

void loop()
{

}

void get_ADC(void)
{
result = adc->readSynchronizedContinuous();
result.result_adc0 = (uint16_t)result.result_adc0;
Serial.println(result.result_adc0 * 3.3 / adc->getMaxValue(ADC_0), DEC);
}

void flash()
{
static boolean output = HIGH;

digitalWrite(led_pin, output);
output = !output;
}

Appreciate any help given.
 
Hi All,

Firstly, thank you very much for this library and hard work Pedvide, it truelly is excellent. Long time lurker on this thread as I'm trying to improve the sample rate I'm seeing on my teensy. Ideally I'm trying to get to 10KHz(ish) but currently I run into issues with data rate above 4KHz.

I've modified some sample code and added in interrupt based sampling. I've also used FlexiTimer2 (also MsTimer2 to see if it made a difference) to run a blink interupt to give the ADC something to sample.

The issue I run into is that at higher interupt rates I stop getting reliable readings (shown in the included picture).
Is this an issue with using the ADC in an interupt routine at a high rate, or is it that I'm running up against the Teensy sampling limit?

I've tried reading the rest of this thread but I can't seem to find anyone with a similar issue.

Running up to date Teensyduino and teensy 3.2
View attachment 7800



Appreciate any help given.

Whats the source you are reading?
 
I'm sampling pin 14 + 15 (A0 & A1) as I need to read simultaneously, but I'm only using 15 (A1) at the moment. There's just a wire between that and Pin 13, the LED, so that I'm reading something. Didn't want to generate a signwave/pwm in case the additional processing requirements impinged on the sample rate.
 
I'm sampling pin 14 + 15 (A0 & A1) as I need to read simultaneously, but I'm only using 15 (A1) at the moment. There's just a wire between that and Pin 13, the LED, so that I'm reading something. Didn't want to generate a signwave/pwm in case the additional processing requirements impinged on the sample rate.

Hmm, if your using the serial then try upping the baud rate..
You can easily get sampling speeds as low as 5uS or less. So you need to look at things other then the ADC;)
 
Last edited:
Hmm, if your using the serial then try upping the baud rate..
You can easily get sampling speeds as low as 5uS or less. So you need to look at things other then the ADC;)

The Baud rate has had no affect on the data quality so far, I have increased it to its maximum, although IIRC somewhere Paul was saying that the Teensy does what it wants with the baud rate? Might be making that up.

While i've seen plenty of claims that the Teensy will do 5uS sampling, I've not come across anyone that's actually doing it successfully, if you know of anyone/any threads please point me in the right direction.

*Edit*
Is there maybe a more efficient way of storing data than just flinging values onto the serial port?
 
Last edited:
I'm using the dual synced ADC-12bit and cannot get it to go any faster than 16usec. A timer is set up to run at 16usec and generates an interrupt that triggers the ADC. If I run the timer any faster then the period of capture for 640 samples gets longer because it starts to miss an event.
 
The Baud rate has had no affect on the data quality so far, I have increased it to its maximum, although IIRC somewhere Paul was saying that the Teensy does what it wants with the baud rate? Might be making that up.

While i've seen plenty of claims that the Teensy will do 5uS sampling, I've not come across anyone that's actually doing it successfully, if you know of anyone/any threads please point me in the right direction.

*Edit*
Is there maybe a more efficient way of storing data than just flinging values onto the serial port?

Im reading 8 analog pins(12bit) for an E-gocart project, IIRC i'm reading 2 pins synchronously in around 8.6uS.


Here is a test code I used to get my numbers(looks to be the same as Post #105 of the thread), you can change the 3.27V I use in the formulas to match what you 3v3 rail is on your teensy.
Code:
#include <ADC.h>

const int aThrottle = A10;    //Throttle
const int aCurrent = A18;  //Current
const int aBusV = A11;   //BusV
const int aBattV = A19;  //BattV
const int aMotorT = A12;   //MotorTemp
const int aHST = A20;  //HS-Temp


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


void setup() {

    pinMode(LED_BUILTIN, OUTPUT);
    pinMode(aThrottle, INPUT);
    pinMode(aCurrent, INPUT);
    pinMode(aBusV, INPUT);
    pinMode(aBattV, INPUT);
    pinMode(aMotorT, INPUT);
    pinMode(aHST, INPUT);


    Serial.begin(9600);

    ///// ADC0 ////
    //adc->setReference(ADC_REF_INTERNAL, ADC_0); change all 3.3 to 1.2 if you change the reference

    adc->setAveraging(1); // set number of averages
    adc->setResolution(12); // set bits of resolution

    // it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED_16BITS, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
    // see the documentation for more information
    adc->setConversionSpeed(ADC_HIGH_SPEED); // change the conversion speed
    // it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
    adc->setSamplingSpeed(ADC_HIGH_SPEED); // change the sampling speed

    ////// ADC1 /////
    adc->setAveraging(1, ADC_1); // set number of averages
    adc->setResolution(12, ADC_1); // set bits of resolution
    adc->setConversionSpeed(ADC_HIGH_SPEED, ADC_1); // change the conversion speed
    adc->setSamplingSpeed(ADC_HIGH_SPEED, ADC_1); // change the sampling speed



    delay(500);
    Serial.println("end setup");
}

int value[16] = {0};
int value2[16] = {0};
int value3[16] = {0};
int value4[16] = {0};
int value5[16] = {0};
int value6[16] = {0};
int testtime;

ADC::Sync_result result;

void loop() {
  
int i;

elapsedMicros sinceStart; // start the clock

for (i = 0; i < 16; i++) {
    result = adc->analogSynchronizedRead(aThrottle, aCurrent);
    value[i] = result.result_adc0;
    value2[i] = result.result_adc1;
    result = adc->analogSynchronizedRead(aBusV, aBattV);
    value3[i] = result.result_adc0;
    value4[i] = result.result_adc1;
    result = adc->analogSynchronizedRead(aMotorT, aHST);
    value5[i] = result.result_adc0;
    value6[i] = result.result_adc1;

}

testtime = sinceStart;   //how long it took to read all 6 pins 16 times.
Serial.print("Full read time was ");
Serial.print(testtime);
Serial.println(" us");

//First ADC array                        
    Serial.print("Pin: ");   //Copy and paste hell from here on out...
    Serial.print(aThrottle);
    Serial.print(", value ADC0: ");

for (i = 0; i < 16; i++) {
    Serial.print(value[i]*3.27/adc->getMaxValue(ADC_0), DEC);   //using 3.27 as the true voltage value
        Serial.print(" ");  
}

//Second ADC array
    Serial.println(" ");
    Serial.print("Pin: ");   //Copy and paste hell from here on out...
    Serial.print(aCurrent);
    Serial.print(", value ADC1: ");

for (i = 0; i < 16; i++) {
    Serial.print(value2[i]*3.27/adc->getMaxValue(ADC_1), DEC);  
        Serial.print(" ");
}

//Third ADC array
    Serial.println(" ");
    Serial.print("Pin: ");   //Copy and paste hell from here on out...
    Serial.print(aBusV);
    Serial.print(", value ADC0: ");

for (i = 0; i < 16; i++) {
    Serial.print(value3[i]*3.27/adc->getMaxValue(ADC_0), DEC);
        Serial.print(" ");  
}

//Forth ADC array
    Serial.println(" ");
    Serial.print("Pin: ");   //Copy and paste hell from here on out...
    Serial.print(aBattV);
    Serial.print(", value ADC1: ");

for (i = 0; i < 16; i++) {
    Serial.print(value4[i]*3.27/adc->getMaxValue(ADC_1), DEC);
      Serial.print(" ");  
}

//Fifth ADC array
    Serial.println(" ");
    Serial.print("Pin: ");   //Copy and paste hell from here on out...
    Serial.print(aMotorT);
    Serial.print(", value ADC0: ");

for (i = 0; i < 16; i++) {
    Serial.print(value5[i]*3.27/adc->getMaxValue(ADC_0), DEC);
      Serial.print(" ");  
}

//Sixth ADC array
    Serial.println(" ");
    Serial.print("Pin: ");   //Copy and paste hell from here on out...
    Serial.print(aHST);
    Serial.print(", value ADC1: ");

for (i = 0; i < 16; i++) {
    Serial.print(value6[i]*3.27/adc->getMaxValue(ADC_1), DEC);
      Serial.print(" ");  
}

Serial.println(" ");
  delay(1000);
}
 
MaximeD,

I'll rewrite RingBuffer and DMARingBuffer soon. They never really worked well.
The new design will be:
RingBuffer: Purely software circular buffer, baed on interrupts
DMARingBuffer: Hardware based Ping-pong buffer
 
Back
Top