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

I (also) needed to know if the ADC continuous readings were overwritten or not...
This snippet of code was found in the ADC_Module::analog_init() function:
Code:
#ifdef ADC_TEENSY_4
    // overwrite old values if a new conversion ends
    atomic::setBitFlag(adc_regs.CFG, ADC_CFG_OVWREN);
    // this is the only option for Teensy 3.x and LC
    #endif
 
Looking at the code for the ADC (teensy 4.0) I am particularly interested in using both ADCs synchronously. However in the ADC code I see the following remarks:

Code:
...
adc->startSynchronizedContinuous(Chan0Pin,Chan1Pin); 
...

    __disable_irq(); // both measurements should have a maximum delay of an instruction time
    adc0->startReadFast(pin0);
    adc1->startReadFast(pin1);
    __enable_irq();

I am less concerned about the delay till the ADCs (ADC0 and ADC1) are starting to sample, but more curious if they can start exactly at the same time (synchronously)? Not that first adc0 starts and after some time delay adc1 starts. Is that feasible with the software trigger?

When I output the samples I see a delay between the two channels, hence the question.
 
I think in Teensy 4 it's actually possible using one of the timers, but I haven't implemented it.

I am trying to figure out what the correct way is to establish continuous sampling with synchronized ADC0 and ADC1, including DMA to do the heavy lifting of transferring the data. For now, I have no working code yet.

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

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

// setup a DMA Channel.
// Now lets see the different things that RingbufferDMA setup for us before
abdma1.init(adc, ADC_0);
abdma1.userData(initial_average_value); // save away initial starting average

abdma2.init(adc, ADC_1);
abdma2.userData(initial_average_value); // save away initial starting average

adc->startSynchronizedContinuous(Vib0Pin, Vib1Pin);

With the original code:

Code:
bool ADC::startSynchronizedContinuous(uint8_t pin0, uint8_t pin1) {

    // check pins
    if ( !adc0->checkPin(pin0) ) {
        adc0->fail_flag |= ADC_ERROR::WRONG_PIN;
        return false;
    }
    if ( !adc1->checkPin(pin1) ) {
        adc1->fail_flag |= ADC_ERROR::WRONG_PIN;
        return false;
    }

    adc0->continuousMode();
    adc1->continuousMode();

    __disable_irq(); // both measurements should have a maximum delay of an instruction time
    adc0->startReadFast(pin0);
    adc1->startReadFast(pin1);
    __enable_irq();

    return true;
}

Now want to change it to absolute Synchronized and for this need to enable External Hardware Trigger (ETC).

For this ADTRG bit needs to be set in the general config register:
Code:
atomic::setBitFlag(adc_regs.CFG, ADC_CFG_ADTRG);

``In hardware triggered operation(ADTRG=1 and one external hardware trigger select event has occurred), continuous conversions begin after a hardware trigger event and continue until aborted.`` (Reference Manual)

Only want to setup one hardware trigger register ADC0_HC0 and ADC1_HC0, to trigger ADC0 and ADC1 in sync mode. However setting ADC_ETC CTRL (67.5.1.2 ADC_ETC Global Control Register (CTRL)) looks less straight forward, (what todo with TSC_BYPASS, and DMA_MODE_SEL, in the end, want to use DMA so need to setup ETC_DMA_CTRL and TRIG0_CTRL as well. The last-mentioned register specifies the Sync mode ( bit 16), Trigmode 1 to set to software trigger and as last bit SW_TRIG in this same register to set the chain of conversions in motion.

With this, in theory, it looks like I do not need a timer, instead, it is using the ETC but still can be triggered by software. As mentioned before tried several things but none of them are working yet. All remarks and tips are welcome.

Thanks
Bob
 
Last edited:
I've been going through the examples in the library, and it looks great - thanks for creating it!

I also found this bit in the datasheet for the Teensy 3.2 MCU, and I'm wondering if your ADC library supports it (or could support it in the future):

• Operate the MCU in Wait or Normal Stop mode before initiating (hardware-triggered conversions) or immediately after initiating (hardware- or software-triggered conversions) the ADC conversion.
• For software triggered conversions, immediately follow the write to SC1 with a Wait instruction or Stop instruction.
• For Normal Stop mode operation, select ADACK as the clock source. Operation in Normal Stop reduces VDD noise but increases effective conversion time due to stop recovery.
• There is no I/O switching, input or output, on the MCU during the conversion.
 
I'm not sure what exactly you want to do, you quote quite a few things...

The ADC library supports using the async clock source (ADACK), so it should ran in low power modes without problems. You can use adc->adcX->setConversionSpeed, use the ADACK settings.
 
Apologies, I did quote too much. It's entering Wait mode for a software-triggered conversion in order to reduce noise that seemed most interesting for me. I may not end up using it, though, so if it's a big dev effort it probably wouldn't be worth doing just for me. Seems like it might be more useful for people who need super-quiet ADC readings and can take what I'm guessing would be a fairly big speed hit.
 
Well, it's not really a ADC feature it seems. You can use the library to start a software or hardware conversion and then you can set the board in wait mode. That I don't know how to do, but it's probably in the core library already.
 
If a noob wants to learn how to use this library, where would I look for the information?

First, I don't know how to install it properly, I downloaded the folder from github and pasted it into the library destination on my computer and I can include the ADC.h but I see the example also includes the ADC_util.h I can't seem to include it. I can write it in my file but the letters are not changing color as they should.

Anyone who can steer me in the right direction as to how to learn this stuff?
 
The syntax highlighting is not semantic. It doesn't always work right, because it's extra decoration added by the text editor, not actual meaningful information extracted by the compiler. In the end, what the compiler does is what matters.

If you #include <ADC_util.h> and then build the sketch, do you get an error about a missing file, or does it work?
 
The syntax highlighting is not semantic. It doesn't always work right, because it's extra decoration added by the text editor, not actual meaningful information extracted by the compiler. In the end, what the compiler does is what matters.

If you #include <ADC_util.h> and then build the sketch, do you get an error about a missing file, or does it work?

First of all, is the correct way to add the library to copy the contents to the libraries folder?

I haven't finished writing the code so didn't test anything. I will try it.
 
Copying to the library folder should work fine. I don't know that there's necessarily a "single correct way."

Btw, I highly recommend developing in very small increments. Compiling code, and putting it on the teensy is super cheap, and tells you a lot!

So, start with a bare minimum sketch just to make sure that the files are found:

Code:
#include <ADC.h>
#include <ADC_util.h>

void setup() {}

void loop() {}

If this builds, then the Arduino IDE finds the files needed.

Also: When you unpack the library in the library folder, you might want to give that folder the name "ADC" to match the library. If it has some version number in it, that may confuse things (I'm not sure -- the Arduino IDE has some "magic" behaviors that are not standard for most development tools, and aren't always clearly documented.)
 
Copying to the library folder should work fine. I don't know that there's necessarily a "single correct way."

Btw, I highly recommend developing in very small increments. Compiling code, and putting it on the teensy is super cheap, and tells you a lot!

So, start with a bare minimum sketch just to make sure that the files are found:

Code:
#include <ADC.h>
#include <ADC_util.h>

void setup() {}

void loop() {}

If this builds, then the Arduino IDE finds the files needed.

Also: When you unpack the library in the library folder, you might want to give that folder the name "ADC" to match the library. If it has some version number in it, that may confuse things (I'm not sure -- the Arduino IDE has some "magic" behaviors that are not standard for most development tools, and aren't always clearly documented.)

I haven't actually started programming anything. I have a project in mind (electronic drum) and I'm still learning coding and researching how other people have done similar things.

Getting the ADC reads as fast as possible is very important for this application since I want to read many sensors and it needs to be done very fast as latency is the biggest problem to overcome. Optimally I would want 3ms from when the drum is struck until the MIDI signal reaches a PC to play the sound and 2ms is needed to scan the waveform from the sensor so it doesn't leave much time to do the calculations.

The next question would be how many reads is possible with a teensy4 using this library. I want to read about 10 sensors and store them in buffers so it needs to read from different analog inputs every time, not the same one over and over.
 
Copying to the library folder should work fine. I don't know that there's necessarily a "single correct way."

Btw, I highly recommend developing in very small increments. Compiling code, and putting it on the teensy is super cheap, and tells you a lot!

So, start with a bare minimum sketch just to make sure that the files are found:

Code:
#include <ADC.h>
#include <ADC_util.h>

void setup() {}

void loop() {}

If this builds, then the Arduino IDE finds the files needed.

Also: When you unpack the library in the library folder, you might want to give that folder the name "ADC" to match the library. If it has some version number in it, that may confuse things (I'm not sure -- the Arduino IDE has some "magic" behaviors that are not standard for most development tools, and aren't always clearly documented.)

I compiled the code you suggested and there were no errors.
 
It depends on the Teensy that you have and the speed you set up, but usually a measurement takes at minimum 1 us, so probably it's doable to read 10 sensors in 1 ms. It will depend on what kind of processing you do.
 
It depends on the Teensy that you have and the speed you set up, but usually a measurement takes at minimum 1 us, so probably it's doable to read 10 sensors in 1 ms. It will depend on what kind of processing you do.

I'm using a teensy 4.

I need to store the peak velocities and time differences between peaks of three selected sensors (the drum has 10 sensors total). Scan time will be about 2 ms which is the time after a threshold is reached when it looks for peaks. I'm not sure if I should somehow store the data in buffers and process after the scan time has passed or if the teensy 4 is fast enough to do the processing between analog reads (store biggest peak, start/stop timers etc.)
 
I am trying out the examples with Teensy 4.1 and the adc_timer, adc_dma and adc_timer_dma examples can be compiled and uploaded by there is no print out in the serial monitor and teensy seems to be frozen. I tried several other examples and they worked just fine. Has anyone been able to run the mentioned examples on Teensy 4.1?
 
I am trying out the examples with Teensy 4.1 and the adc_timer, adc_dma and adc_timer_dma examples can be compiled and uploaded by there is no print out in the serial monitor and teensy seems to be frozen. I tried several other examples and they worked just fine. Has anyone been able to run the mentioned examples on Teensy 4.1?

What version of TeensyDuino is in use? ADC library has been undergoing changes to work with T_4.0 and 4.1.

Using the TD 1.53b2 version the adc_dma required some edits to the resolution/averaging function calls like this for adc0 and adc1 - but then it is working fine.
Code:
  adc->adc0->setAveraging(2);
  adc->adc0->setResolution(12);

It can also be an issue with trying two analog reads in parallel if they are tied exclusively to the same base ports.
 
I'm using TeensyDuino v1.5.2 and Arduino IDE v1.8.9. It appears the examples that don't work require ADC_USE_DMA and/or ADC_USE_TIMER to be defined otherwise empty setup() and loop() are compiled. I had assumed those are automatically defined macros based on the target board but the macros are not defined. I tried manually adding them in the examples and adc_timer now seems to work but adc_dma fails compile with error:

undefined reference to `AnalogBufferDMA::init(ADC*, signed char)'
 
Is this the proper way to do analog reads of all sensors one by one with this library? I'm using a teensy 4.

Or do I have to keep the #define teensy 4 etc?

Code:
#include <ADC.h>
#include <ADC_util.h>



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



#define PINS 14
#define DIG_PINS 10
#define PINS_DIFF 0
uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13};
uint8_t adc_pins_dig[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9};
uint8_t adc_pins_diff[] = {};



void setup()
{

Serial.begin(9600);



    ///// ADC0 ////
    adc->adc0->setAveraging(1);                                    	// set number of averages
    adc->adc0->setResolution(10);                                   	// set bits of resolution
    adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED); 	// change the conversion speed
    adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED);     	// change the sampling speed



    ////// ADC1 /////
    adc->adc1->setAveraging(1);                                    	// set number of averages
    adc->adc1->setResolution(10);                                   	// set bits of resolution
    adc->adc1->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED); 	// change the conversion speed
    adc->adc1->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED);     	// change the sampling speed



    delay(500);

}

void loop()

{

uint32_t sensorValue[10];

for (int i = 0; i < PINS; i++)
  {
    sensorValue[i] = adc->analogRead(adc_pins[i]);
  }


}

Can I remove the digital pins from the code since I'm not using them?

Also what are these:
#define PINS_DIFF 0
uint8_t adc_pins_diff[] = {};

I'm new at coding and trying to learn how to use this library.
 
It's fixed now in github.

Thank you for the fix. Example adc_dma now works and adc_timer and adc_timer_dma also work if the #ifdef is moved similar to the fix in adc_dma.

I have now tried a few examples but I am not sure what's the best way to use the library to accomplish my goal of synchronized reading of multiple channel pairs in quick succession as fast as possible at regular interval. I thought DMA would be the way but looking at the examples I don't see how it can be done. I am new to the library and Teensy so any guidance would be greatly appreciated.
 
Back
Top