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

It is supports differential mode, negative numbers would be needed for the case where the negative ADC channel is higher than the positive one.
 
ADC library and teensy 2???

my goal is to use the ADC library with a teensy 3.1 and i have compiled an example sketch successfully. however it will be a while before i can get various PC board changes to allow the use of the 3.1 in my project. therefore i would like to test with the teensy 2 but i get all sorts of compile errors.

is it possible to use the ADC library with the teensy 2? any suggestions as to settings options or other magic needed to get it to work?
 
This library only works with Teensy 3.0 & 3.1. Teensy 2.0 is a very different kind of micro-processor.

About the singed integer return values: The standard arduino analogRead returns an int, so I kept it that way. Then I started returning negative values as error codes. Finally I started using error flags, so that void functions can also signal errors to the user, but analogRead still returns negative error codes.
 
Last edited:
The Teensy 3.1 actually has 20 pins that can do analog input: http://www.pjrc.com/teensy/pinout.html

  • Pins A0..A9 are available as through holes on the right side (with the usb adapter pointing up). As you mention, A4/A5 (or alternatively A2/A3) are used for i2c (SDA/SCL), but if you aren't using i2c, you can use them for analog inputs;
  • Pins A10..A11 are available as through holes on the right inside of the chip;
  • Pins A12 and A15-A17 are available as solder pads on the right side underneath the Teensy 3.1;
  • Pins A13 and A18-A20 are available as solder pads on the left side underneath the Teensy 3.1;
  • Pin A14 is available as a through hole on the back of the Teensy 3.1;
  • A18/A19 can be used for the second i2c bus if needed on the Teensy 3.1;
  • Teensy 3.0 did not have analog pins A14-A20. The A14 pin in Teensy 3.1 is the reset pin in Teensy 3.0, and pins A15-A20 are just digital pins.
 
Last edited:
Thanks Mike, I hadn't noticed the other inputs marked A14 to A20 in the bottom picture before. Those pics could really stand to be rotated 90 degrees for easier reading.
Teensy3.1.jpg
 
It seems like it's not possible to use the differential pairs without accessing the bottom pads - is that correct?
 
One differential pair is connected to the A10-A11 pins which can be accessed from above (you can solder two pin headers so they stick out from above), the second pair is connected to A12-A13 and as you say can only be accessed from below.
 
I didn't think of adding extra pin headers to be accessed from above, thanks for the heads-up. Maybe I was just too lazy to walk to the basement where the soldering iron is...
 
Ring buffer with DMA

I have a sketch working quite well using continuous analog read and ring buffer from this library. Definitely works better than just continuous read. I would like to take advantage of DMA and used the example as a guide to changing my sketch to use DMA ring buffer. The example runs fine but seems to read 0 every second read and I can not get the DMA version to work at all in my sketch.

Can someone tell me the exact sequence of steps that are necessary to use DMA ring buffer for continuous ADC ? A sample sketch would be helpful. I don't think that timer interrupts are suitable for my application but am willing to try that as well. Would like to get DMA ring buffer working first and I think it might be all I need.
 
So you took the example "ringBufferDMA.ino" and added continuous conversions and every second read is 0?
What do you mean by the DMA example runs fine, but the DMA version doesn't work? Which examples work and which don't and what modifications have you made, and if you have problems with a sketch, please post it!
 
I did not modify the DMA ring buffer example other than to change input pin from A9 to A2 and every second read is 0. i guess i should not have said it runs fine. I pared the sketch down to the minimum? I wanted to include stuff such as the encoder reading in case there is a conflict between libraries. I think there must be some basic misunderstanding as to how the DMA buffer works thta is hopefully easy to find in the code. I can add a bunch of comments to explain what I am trying to do but here is a summary.
I record pulses to audio using a hardware encoder. A quadrature encoder is the input device to initiate pulses. I read pulses from audio analog input and use sines to trigger a motor step in one direction and cosines to trigger a step in the other direction.



Code:
#define ENCODER_DO_NOT_USE_INTERRUPTS
//#define ENCODER_OPTIMIZE_INTERRUPTS

#include <Encoder.h>

#include <ADC.h>
ADC *adc = new ADC(); // adc object
//RingBuffer *buffer = new RingBuffer;
RingBufferDMA *dmaBuffer = new RingBufferDMA(2,ADC_0); // use dma channel 2 and ADC0
  
//#include <Encoder.h>
Encoder readEnc(5, 6);

const int numReadings = 2500;
const int readAudio = A2; // ADC0
int encoder_scale;
int minus_encoder_scale;

int count = 0;
int encoder_step_count;

int newRead;
int min_time;

int up_thresh;
int down_thresh;
int up_zero;
int down_zero;
int inter_pulse;
int max_pulse_width;
int max_position;
int pause_loops;
int pause_loop_threshold;

int diff;
int center;
int zero_diff;
int zero_check;
int audio_min = 1000;   // initialize at high number
int audio_max = 0;   // initialize at low number :)
boolean sent_pulse = false;
boolean pause_begin;
boolean got_pause;
boolean playback = false;

// see setup for decoder variables

int audio_in;
int read_in;

int reading_index = 0;              // the index of the current reading

int readings[numReadings];  // the readings from the analog input
long total = 0;              // the running total
int average = 0;            // the average

long countloops;

void setup() {
//  digitalWriteFast(13, HIGH);
  digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
  delay(2000);  // this is needed for some fucking reason to let voltage stabilize???
  pinMode(1, OUTPUT); // step out clockwise  (used to drive audio encoder)
  pinMode(2, OUTPUT); // step out counterclockwise  (used to drive audio encoder)  
  pinMode(3, OUTPUT); // step
  pinMode(4, OUTPUT); // direction 
  pinMode(5, INPUT); // encoder, no pullup
  pinMode(6, INPUT); // encoder, no pullup  
  pinMode(9, INPUT_PULLUP); // limit CW
  pinMode(10, INPUT_PULLUP); // limit CCW 
  pinMode(13, OUTPUT);
  pinMode(A2, INPUT);
  pinMode(A4, INPUT);
  pinMode(21, INPUT_PULLUP); // switch for record/playback
  
  encoder_scale = 6;  // 
  minus_encoder_scale = encoder_scale * -1;
  
  diff = 300;  // 200 to 300? and is terrible 400 too high
  zero_diff = 80;  // was 80
  zero_check = 25;  // was 50 anything from about 7 to 150 probably fine keep it short to allow fast motion
  pause_loop_threshold = 3;
  max_pulse_width = 50;  // 50 seems right, do not change?
  inter_pulse = 2;  // time between multiple ticks from pulse sub should vary this in playback to smooth playing
  pause_loops = 0;
  
  adc->enableDMA(ADC_0);
  adc->enableInterrupts(ADC_0);  
  
// set parameters for ADC0
  adc->setAveraging(1); // needs to be maybe 5 to 10?
  adc->setResolution(10); // set bits of resolution seems to need to be 10 
  adc->setConversionSpeed(ADC_VERY_HIGH_SPEED); // change the conversion speed
  adc->setSamplingSpeed(ADC_VERY_HIGH_SPEED); // change the sampling speed  
  adc->startContinuous(readAudio, ADC_0);  // must be continuous read   
    
  int cal_count = 1000;
  for (int thisReading = 0; thisReading < cal_count; thisReading++){
    total += adc->analogReadContinuous(ADC_0);
    delayMicroseconds(100);
  }
  center = int (total / cal_count);
  up_thresh = center + diff; 
  down_thresh = center - diff;
  up_zero = center + zero_diff;
  down_zero = center - zero_diff;     
}

void loop() {
  int read_in;
//  buffer->write(adc->analogReadContinuous(ADC_0));  // comment this out if using DMA buffer
  newRead = readEnc.read(); 
  
  // the following could miss a step but the loop is fast enought that it probably won't if it does WTF
  if ((newRead > 0 ) and (digitalReadFast(10)== HIGH)){ 
    encoder_step_count ++;
  }
  if ((newRead < 0 ) and (digitalReadFast(9)== HIGH)){
    encoder_step_count --;
  }  
  readEnc.write(0);  // reset encodereach loop so it just reads direction single pulses
  if (encoder_step_count == encoder_scale){
    digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN)); 
    encoder_step_count = 0;  // might need to move this to next loop level
    if (!playback){
      record_cw();      
    }  
    digitalWriteFast(4, LOW); 
    for (int i=0; i < encoder_scale; i++){  
      delayMicroseconds(2);   // this delay is critical do not remove it but try 1 us      
      digitalWriteFast(3, HIGH);
      delayMicroseconds(1);
      digitalWriteFast(3, LOW);
    }
  }
  if (encoder_step_count == minus_encoder_scale){
    digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
    encoder_step_count = 0; 
    if (!playback){
      record_ccw();       
    }
    digitalWriteFast(4, HIGH); 
    for (int i=0; i < encoder_scale; i++){   
      delayMicroseconds(2);   // this delay is critical do not remove it but try 1 us
      digitalWriteFast(3, HIGH);
      delayMicroseconds(1); 
      digitalWriteFast(3, LOW);
    }
  }              
  pause_loops ++; // increments each loops is reset at pause begin  
//  read_in = buffer->read();
  read_in = dmaBuffer->read();
  if (! pause_begin){
    if ((read_in > down_zero) && (read_in < up_zero)){ // might want && this with above if statement
      pause_begin = true;
      pause_loops = 0;
    }
  }
  if ((pause_begin) && ((read_in < down_zero) || (read_in > up_zero))){ // just left a pause
     if (pause_loops > pause_loop_threshold){  
       inter_pulse =  max(pause_loops/(encoder_scale * 4),max_pulse_width);  // need to figure out how ong the loop time is to get this right
       if (read_in > up_thresh){
         cwpulse();
       }
       if (read_in < down_thresh){
         ccwpulse();
       }
    pause_begin = false;  // reset  
    }
  }   
}
void dma_ch2_isr(void) {
    //int t = micros();
    //digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));

//    Serial.println("DMA_CH2_ISR"); //Serial.println(t);

    DMA_CINT = 2; // clear interrupt

    // call write to tell the buffer that we wrote a new value
    dmaBuffer->write();

    //digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));

}
void adc0_isr(void) {
    //int t = micros();
//    Serial.println("ADC0_ISR"); //Serial.println(t);
    ADC0_RA; // clear interrupt
}

void record_cw(void){ // output cw pulse to encoder
    digitalWriteFast(1, HIGH); // pin 1 high CW step this was 2 by mistake might be reason for jitter
    delayMicroseconds(2);  // might need to make this longer was 1 trying 2
    digitalWriteFast(1, LOW); // pin 1 low CW step    
} 
void record_ccw(void){ // output ccw pulse to encoder
    digitalWriteFast(2, HIGH); // pin 2 high CCW step
    delayMicroseconds(2);
    digitalWriteFast(2, LOW); // pin 2 low CCW step   
} 

void cwpulse(void){ // interrupt code for input CW step (not using interrupt now)
  digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
  if (digitalReadFast(10)== HIGH){ // pin 10 limit open
    digitalWriteFast(4, LOW); // pin 4 low for CW 
    for (int i=0; i < encoder_scale; i++){
      delayMicroseconds(inter_pulse);      
      digitalWriteFast(3, HIGH); // pin 3 high
      delayMicroseconds(1);
      digitalWriteFast(3, LOW); // pin 3 low
    }    
  } 
} 
void ccwpulse(void){ // interrupt code for input CCW step (not using interrupt now)
  digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
  if (digitalReadFast(9)== HIGH){ // pin 9 limit open
    digitalWriteFast(4, HIGH); // pin 4 high for CCW
    for (int i=0; i < encoder_scale; i++){  
      delayMicroseconds(inter_pulse);   // needed for time to read the analog input  
      digitalWriteFast(3, HIGH); // pin 3 high      
      delayMicroseconds(1);
      digitalWriteFast(3, LOW); // pin 3 low      
    }    
  }  
}
 
Can you try switching the adc interrupt off? I think that might be the problem.
Originally I included the DMA stuff more as a test than as a finished thing, but I haven't really worked on that much.
 
Compiling using this Library

Hello!

I appreciate all of the hard work to create this post. I am trying to include this library to run the two ADC simultaneously to read in two different voltages, however, I cant seem to get the library to load properly. I've followed the instructions but I get the following error. Compiling the examples gives the same error.

Code:
C:\Users\Somniare\Documents\Arduino\libraries\ADC/ADC.h:52:24: fatal error: DMAChannel.h: No such file or directory
compilation terminated.

I am using TeensyDuino 1.19 and Arduino 1.0.5-r2
 
Mmm, I compiled the library with the latest release of Teensyduino (1.20), it's possible that it doesn't work with the 1.19 version.
Can you update your Teensyduino and see if it works?
 
Yes, indeed DMAChannel.h is one of the new features in 1.20. It's so much better than the old, conflict-prone way used with 1.19 and older, so you should definitely install 1.20-rc5.
 
When using the 1.2V scale does that also mean the max safe voltage on the analog pins is 1.2V or can they still be driven to 3.3V even if they cant read it any higher then 1.2v?
A project im working on will be sending analog voltages to several pins, I plan to use the 1.2V internal ref but im not sure if I should install 1.2V protection Zener's or if 3-3.3V will be ok.

Im using several op amps to buffer the analog voltages but I like to have some protections in place.
 
The manual doesn't say much about this:
For proper conversion, the input voltage must fall between VREFH and VREFL. If the input is equal to or exceeds VREFH, the converter circuit converts the signal to 0xFFF (full scale 12-bit representation), 0x3FF (full scale 10-bit representation) or 0xFF (full scale 8-bit representation). If the input is equal to or less than VREFL, the converter circuit converts it to 0x000.

The datasheet says that in operating conditions, Vin should be between VREFH and VREFL.

The pins are of course safe until 3.3 V (or 5.0 V for Teensy 3.1 analog and digital pins), but I guess it's possible that the ADC gets damaged. I can't give you a definite answer, sorry.
 
Yes, indeed DMAChannel.h is one of the new features in 1.20. It's so much better than the old, conflict-prone way used with 1.19 and older, so you should definitely install 1.20-rc5.

I am using the TAR download rather than the installer as I am using Eclipse. Is there a new version of that TAR file with DMAChannel in it, the one I downloaded a few days ago (teensy_1.15_mac_1.14.tar.gz) does not seem to have it.
 
Update: 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!!

It's important to create the ADC object before the Audio objects, for example:

Code:
// FFT Test
//
// Compute a 1024 point Fast Fourier Transform (spectrum analysis)
// on audio connected to the Left Line-In pin.  By changing code,
// a synthetic sine wave can be input instead.
//
// The first 40 (of 512) frequency analysis bins are printed to
// the Arduino Serial Monitor.  Viewing the raw data can help you
// understand how the FFT works and what results to expect when
// using the data to control LEDs, motors, or other fun things!
//
// This example code is in the public domain.




#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>

#include <ADC.h>

// CREATE THE ADC OBJECT FIRST, BEFORE ALL AUDIO STUFF!!
ADC *adc = new ADC(); // adc object
const int readPin = A3; // ADC0

// Create the Audio components.  These should be created in the
// order data flows, inputs/sources -> processing -> outputs
// Using adafruit mic-amp, edit library to turn-off internal voltage reference in 
// inputAnalog.cpp 

AudioInputAnalog       adc1(15);
AudioSynthWaveformSine sinewave;
AudioAnalyzeFFT1024    myFFT;



// Connect either the live input or synthesized sine wave
AudioConnection patchCord1(adc1, 0, myFFT, 0);
//AudioConnection patchCord1(sinewave, 0, myFFT, 0);




void setup() {
  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(12);
  
  pinMode(15, INPUT);
  pinMode(14, INPUT);
  
  // Configure the window algorithm to use
  //myFFT.windowFunction(AudioWindowHanning1024);
  myFFT.windowFunction(NULL);

  // Create a synthetic sine wave, for testing
  // To use this, edit the connections above
  sinewave.amplitude(0.8);
  sinewave.frequency(1034.007);


  adc->setAveraging(32, ADC_1); // set number of averages
  adc->setResolution(16, ADC_1); // set bits of resolution
  adc->setConversionSpeed(ADC_VERY_LOW_SPEED, ADC_1); // change the conversion speed
  adc->setSamplingSpeed(ADC_VERY_LOW_SPEED, ADC_1); // change the sampling speed

}

int value = 0;

void loop() {
  float n;
  int i;
  
  value = adc->analogRead(readPin, ADC_1); // read a new value, will return ADC_ERROR_VALUE if the comparison is false.

  if (myFFT.available()) {
    // each time new FFT data is available
    // print it all to the Arduino Serial Monitor
    //Serial.print("T: "); //Testing touchRead
    //Serial.print(touchRead(0));
    Serial.print("\t FFT: ");
    for (i=0; i<40; i++) {
      n = myFFT.read(i);
      if (n >= 0.005) {
        Serial.print(n);
        Serial.print(" ");
      } else {
        Serial.print("  -  "); // don't print "0.00"
      }
    }
    Serial.println();   

    Serial.print("Pin: ");
    Serial.print(readPin);
    Serial.print(", value ADC1: ");
    Serial.println(value*3.3/adc->getMaxValue(ADC_1), DEC);
  }
}
 
Last edited:
Pedvide,
just looking at some of the library, mainly synchronized reads. I had planned to read 5 pins but may add 1 more so I don't have to jump between 2 different read modes. Sadly I am traveling all week so reading up and making some hopefully working test software is as far as I can go.

I have to say some of the library goes right over my head.
 
Back
Top