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

Yep, but should have version with t4 support in next teensyduino release. Or you can download from github
 
That shows the TeensyDuino install library location - is that were the latest Github Download was installed? The latest T_4 version isn't included in a TeensyDuino install yet AFAIK.

The Github download went to the TeensyDuino directly. I apologize in advance for my ignorance as I'm more of a hardware person and less software oriented but how or where should I be downloading/saving the file? Thanks for the help!
 
No problem - just saw room for confusion. IDE maintains a 'sketchbook' folder. In that folder place a folder 'libraries' and any library found there will be used before a same library from standard install locations.

In this case it may or may not make a difference, if it fully extracted it should have replaced the proper files. Also note once placed there under sketchbook - it will forever replace other installed libraries - until removed.

I am not in a place to test it here - but what is there should work - as KurtE noted if not now - then when integrated into the next TeensyDuino update.
 
I just tried to compile some of the examples(adc_test, analogRead) in the updated library and get quite a few "not declared in this scope" and "has no member" errors. The same happens with my own sketches. Did I not download the repository correctly or is there something else I missed?

Code:
ADC_Module.cpp: In constructor 'ADC_Module::ADC_Module(uint8_t, const uint8_t*, ADC_settings::ADC_REGS_t&)':

ADC_Module.cpp:61:28: error: 'XBARA1_IN_QTIMER4_TIMER3' was not declared in this scope

         , XBAR_IN(ADC_num? XBARA1_IN_QTIMER4_TIMER3 : XBARA1_IN_QTIMER4_TIMER0)

ADC_Module.cpp:61:55: error: 'XBARA1_IN_QTIMER4_TIMER0' was not declared in this scope

         , XBAR_IN(ADC_num? XBARA1_IN_QTIMER4_TIMER3 : XBARA1_IN_QTIMER4_TIMER0)

ADC_Module.cpp:62:29: error: 'XBARA1_OUT_ADC_ETC_TRIG10' was not declared in this scope

         , XBAR_OUT(ADC_num? XBARA1_OUT_ADC_ETC_TRIG10 : XBARA1_OUT_ADC_ETC_TRIG00)

ADC_Module.cpp:62:57: error: 'XBARA1_OUT_ADC_ETC_TRIG00' was not declared in this scope

         , XBAR_OUT(ADC_num? XBARA1_OUT_ADC_ETC_TRIG10 : XBARA1_OUT_ADC_ETC_TRIG00)


ADC_Module.cpp: In member function 'void ADC_Module::startQuadTimer(uint32_t)':

ADC_Module.cpp:1506:24: error: 'struct IMXRT_REGISTER32_t' has no member named 'CTRL'

     if ( IMXRT_ADC_ETC.CTRL & ADC_ETC_CTRL_SOFTRST) {// SOFTRST

ADC_Module.cpp:1506:31: error: 'ADC_ETC_CTRL_SOFTRST' was not declared in this scope

     if ( IMXRT_ADC_ETC.CTRL & ADC_ETC_CTRL_SOFTRST) {// SOFTRST

ADC_Module.cpp:1508:44: error: 'struct IMXRT_REGISTER32_t' has no member named 'CTRL'

         atomic::clearBitFlag(IMXRT_ADC_ETC.CTRL, ADC_ETC_CTRL_SOFTRST);

ADC_Module.cpp:1512:23: error: 'struct IMXRT_REGISTER32_t' has no member named 'CTRL'

         IMXRT_ADC_ETC.CTRL |= 

ADC_Module.cpp:1513:14: error: 'ADC_ETC_CTRL_TSC_BYPASS' was not declared in this scope

             (ADC_ETC_CTRL_TSC_BYPASS | ADC_ETC_CTRL_DMA_MODE_SEL | ADC_ETC_CTRL_TRIG_ENABLE(1 << ADC_ETC_TRIGGER_INDEX)); // 0x40000001;  // start with trigger 0

ADC_Module.cpp:1513:40: error: 'ADC_ETC_CTRL_DMA_MODE_SEL' was not declared in this scope

             (ADC_ETC_CTRL_TSC_BYPASS | ADC_ETC_CTRL_DMA_MODE_SEL | ADC_ETC_CTRL_TRIG_ENABLE(1 << ADC_ETC_TRIGGER_INDEX)); // 0x40000001;  // start with trigger 0

ADC_Module.cpp:1513:119: error: 'ADC_ETC_CTRL_TRIG_ENABLE' was not declared in this scope

             (ADC_ETC_CTRL_TSC_BYPASS | ADC_ETC_CTRL_DMA_MODE_SEL | ADC_ETC_CTRL_TRIG_ENABLE(1 << ADC_ETC_TRIGGER_INDEX)); // 0x40000001;  // start with trigger 0

ADC_Module.cpp:1514:23: error: 'struct IMXRT_REGISTER32_t' has no member named 'TRIG'

         IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(0);   // chainlength -1 only us

ADC_Module.cpp:1514:88: error: 'ADC_ETC_TRIG_CTRL_TRIG_CHAIN' was not declared in this scope

         IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(0);   // chainlength -1 only us

ADC_Module.cpp:1515:23: error: 'struct IMXRT_REGISTER32_t' has no member named 'TRIG'

         IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_1_0 =

ADC_Module.cpp:1516:37: error: 'ADC_ETC_TRIG_CHAIN_IE0' was not declared in this scope

             ADC_ETC_TRIG_CHAIN_IE0(1) /*| ADC_ETC_TRIG_CHAIN_B2B0 */

ADC_Module.cpp:1517:41: error: 'ADC_ETC_TRIG_CHAIN_HWTS0' was not declared in this scope

             | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(adc_pin_channel) ;

ADC_Module.cpp:1517:85: error: 'ADC_ETC_TRIG_CHAIN_CSEL0' was not declared in this scope

             | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(adc_pin_channel) ;

ADC_Module.cpp:1523:27: error: 'struct IMXRT_REGISTER32_t' has no member named 'DMA_CTRL'

             IMXRT_ADC_ETC.DMA_CTRL |= ADC_ETC_DMA_CTRL_TRIQ_ENABLE(ADC_ETC_TRIGGER_INDEX);

ADC_Module.cpp:1523:89: error: 'ADC_ETC_DMA_CTRL_TRIQ_ENABLE' was not declared in this scope

             IMXRT_ADC_ETC.DMA_CTRL |= ADC_ETC_DMA_CTRL_TRIQ_ENABLE(ADC_ETC_TRIGGER_INDEX);

ADC_Module.cpp:1528:23: error: 'struct IMXRT_REGISTER32_t' has no member named 'CTRL'

         IMXRT_ADC_ETC.CTRL &= ~(ADC_ETC_CTRL_TSC_BYPASS); // 0x40000001;  // start with trigger 0

ADC_Module.cpp:1528:33: error: 'ADC_ETC_CTRL_TSC_BYPASS' was not declared in this scope

         IMXRT_ADC_ETC.CTRL &= ~(ADC_ETC_CTRL_TSC_BYPASS); // 0x40000001;  // start with trigger 0

ADC_Module.cpp:1529:23: error: 'struct IMXRT_REGISTER32_t' has no member named 'CTRL'

         IMXRT_ADC_ETC.CTRL |= ADC_ETC_CTRL_DMA_MODE_SEL | ADC_ETC_CTRL_TRIG_ENABLE(1 << ADC_ETC_TRIGGER_INDEX);     // Add trigger 

ADC_Module.cpp:1529:31: error: 'ADC_ETC_CTRL_DMA_MODE_SEL' was not declared in this scope

         IMXRT_ADC_ETC.CTRL |= ADC_ETC_CTRL_DMA_MODE_SEL | ADC_ETC_CTRL_TRIG_ENABLE(1 << ADC_ETC_TRIGGER_INDEX);     // Add trigger

ADC_Module.cpp:1529:110: error: 'ADC_ETC_CTRL_TRIG_ENABLE' was not declared in this scope

         IMXRT_ADC_ETC.CTRL |= ADC_ETC_CTRL_DMA_MODE_SEL | ADC_ETC_CTRL_TRIG_ENABLE(1 << ADC_ETC_TRIGGER_INDEX);     // Add trigger

ADC_Module.cpp:1530:23: error: 'struct IMXRT_REGISTER32_t' has no member named 'TRIG'

         IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(0);   // chainlength -1 only us

ADC_Module.cpp:1530:88: error: 'ADC_ETC_TRIG_CTRL_TRIG_CHAIN' was not declared in this scope

         IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(0);   // chainlength -1 only us

ADC_Module.cpp:1531:23: error: 'struct IMXRT_REGISTER32_t' has no member named 'TRIG'

         IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_1_0 =

ADC_Module.cpp:1532:35: error: 'ADC_ETC_TRIG_CHAIN_IE0' was not declared in this scope

           ADC_ETC_TRIG_CHAIN_IE0(1) /*| ADC_ETC_TRIG_CHAIN_B2B0 */

ADC_Module.cpp:1533:39: error: 'ADC_ETC_TRIG_CHAIN_HWTS0' was not declared in this scope

           | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(adc_pin_channel) ;

ADC_Module.cpp:1533:83: error: 'ADC_ETC_TRIG_CHAIN_CSEL0' was not declared in this scope

           | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(adc_pin_channel) ;

ADC_Module.cpp:1536:27: error: 'struct IMXRT_REGISTER32_t' has no member named 'DMA_CTRL'

             IMXRT_ADC_ETC.DMA_CTRL |= ADC_ETC_DMA_CTRL_TRIQ_ENABLE(ADC_ETC_TRIGGER_INDEX);

ADC_Module.cpp:1536:89: error: 'ADC_ETC_DMA_CTRL_TRIQ_ENABLE' was not declared in this scope

             IMXRT_ADC_ETC.DMA_CTRL |= ADC_ETC_DMA_CTRL_TRIQ_ENABLE(ADC_ETC_TRIGGER_INDEX);
 
I just tried to compile some of the examples(adc_test, analogRead) in the updated library and get quite a few "not declared in this scope" and "has no member" errors. The same happens with my own sketches. Did I not download the repository correctly or is there something else I missed?

Might help to know which version you downloaded? What version of Teensyduino? Which Teensy? ...
 
Oh yes, sorry. I'm using a Teensy 4.0, I downloaded the ADC library from GitHub a few hours ago, so that should be the most recent one. Teensyduino is Version 1.48.
 
I am guessing you need 1.49 of Teensyduino. (Or better yet try out current 1.50 beta 1)

Or you can grab the updated imxrt.h file from cores: https://github.com/PaulStoffregen/cores/tree/master/teensy4
(This would replace the version of the file in your Arduino install location (<where arduino is>/hardware/teensy/avr/cores/teensy4)

Since 1.48 was released we filled in some more of the data structures of the underlying hardware...
 
will the ADC library be available for the teensy 4 soon ?
There is a version of it that works today that I believe was included in Teensyduino 1.49.

As the previous post was talking about, you can also download latest from github... But it requires 1.49's imxrt.h file...
 
Hello all,
I'm having trouble compiling any of the ADC examples for Teensy LC only- it builds fine for other 3.x devices. Using Teensduino 1.50, I get an error over 'enableDMA', as in, it doesn't exist in the LC build configuration (adc->enableDMA(adc_num); ). I'm aware that the LC has limited DMA capabilities so suspect there may be a backwards compatibility issue.
Before I start hacking into the library files, I thought I'd ask the experts.

[EDIT] I attempted to blindly fix the compilation error by changing AnalogBufferDMA.cpp:163 from adc->enableDMA(); to adc->adc[adc_num]->enableDMA(); as it is in other target configurations. While this built successfully, running adc_test failed on all tests.

[UPDATE] despite failing the pullup test, the ADC values seem to be correct with the aforementioned fix.
 
Last edited:
Hi all,
I have some small annoying issue with ADC.h and teensy 4.0. I work with this lib together with Teensy 3.6 and it is really great, but when I switched to 4.0 I can't compile my codes. Apparently some commands are not recognized as members of ADC.h.
For example, "adc->adc0->setReference(ADC_REFERENCE::REF_1V2);" can't compile on teensy 4.0 while with 3.6 it goes fine...
I reinstall arduino and teensyduino several times with different versions (1.8.10-12 and 1.48-1.50).
What am I doing wrong?
Thanks in advanced.
 
Thanks for the quick reply Frank B.
So, Teensy4.0 don't have lower voltage reference for the analog read? It can only work with 3.3V ref?
 
Hello,

I'm using ADC library on Teensy4. I read data on adc0 at 12bit resolution. Conseguently the readen integer range is 0-4095. I saw that every 256bit there's an hole in reading of 7bit. For example bit from 505 to 511 are missing. any help?

The same is at 10bit of resolution.
 
Sorry if this has been asked, maybe I missed it somehow. I have a few general and noob questions regarding ADC:

- Do I understand correctly that when I start a continuous conversion with e.g. "adc->startContinuous(A9)" this will be performed by a separate controller/CPU and won't slow down the main execution of my loop?
- When I read a value with "adc->analogReadContinuous(A9)" does that read the latest measured value? If so, what happens with the previous values? (I don't need them anyway but I ask just to clarify the concept for myself, are these values discarded?)
- If I read a value with "adc->analogReadContinuous(A9)" too soon after I started a continuous measurement, will this call block until the conversion is finished or it will return immediately. If it will return immediately, what value will I receive, zero?
- Depending on the answer of previous questions, is there any chance that a call to "analogReadContinuous" can block for some reason until a value is obtained? To clarify, I would like to avoid that, I prefer having an older value served immediately, rather than block and wait there.
- Can I supply an arbitrary resolution number, or there are predefined ones? For instance, can I configure a 9-bit resolution? :) If so, is the measurement automatically scaled to fit entirely in that range or there are some other considerations? If so, what's the max value that I can use to determine the proper voltage?

Apologies again for the (most probably) silly questions.
 
... questions regarding ADC:

- Do I understand correctly that when I start a continuous conversion with e.g. "adc->startContinuous(A9)" this will be performed by a separate controller/CPU and won't slow down the main execution of my loop?
- When I read a value with "adc->analogReadContinuous(A9)" does that read the latest measured value? If so, what happens with the previous values? (I don't need them anyway but I ask just to clarify the concept for myself, are these values discarded?)
- If I read a value with "adc->analogReadContinuous(A9)" too soon after I started a continuous measurement, will this call block until the conversion is finished or it will return immediately. If it will return immediately, what value will I receive, zero?
- Depending on the answer of previous questions, is there any chance that a call to "analogReadContinuous" can block for some reason until a value is obtained? To clarify, I would like to avoid that, I prefer having an older value served immediately, rather than block and wait there.
- Can I supply an arbitrary resolution number, or there are predefined ones? For instance, can I configure a 9-bit resolution? :) If so, is the measurement automatically scaled to fit entirely in that range or there are some other considerations? If so, what's the max value that I can use to determine the proper voltage?

...

There is an example for ReadContinuous() :: …\hardware\teensy\avr\libraries\ADC\examples\analogContinuousRead\analogContinuousRead.ino

Adjust that for personal use case and looking at that should allow seeing the function and performance. Can add elapsedMicros() to observe timing.

That is the only way to get best specific answers. There is a DOC HTML in the library - but such details are not provided as the example will quickly provide.

Post if that leaves questions on function within that sketch.
 
There is an example for ReadContinuous() :: …\hardware\teensy\avr\libraries\ADC\examples\analogContinuousRead\analogContinuousRead.ino

Adjust that for personal use case and looking at that should allow seeing the function and performance. Can add elapsedMicros() to observe timing.

That is the only way to get best specific answers. There is a DOC HTML in the library - but such details are not provided as the example will quickly provide.

Post if that leaves questions on function within that sketch.

Yes, I’ve seen this example but I hoped I could have precise answers (probably from the author) based on the hardware architecture and the ADC library design rather than having to measure since measuring won’t answer most of my questions that are related to synchronization and concurrent executions. I believe those should be easy answers, am I right? Measuring seems like a last resort.
 
Yes, I’ve seen this example but I hoped I could have precise answers (probably from the author) based on the hardware architecture and the ADC library design rather than having to measure since measuring won’t answer most of my questions that are related to synchronization and concurrent executions. I believe those should be easy answers, am I right? Measuring seems like a last resort.

I'd go with the opposite - what you see is what you get - without regard to intended behavior. It isn't posted what Teensy is in use. And using the code the best way will come with familiarity that would come with mods to that example that shows proper and what seems to be full usage - including _isr() usage.

Reading that sample shows a .complete call that indicates if data is ready to read with no wait. Easy to see what happens is read when .complete says NO.

A quick wait and series of calls in setup() will indicate is more than one reading is buffered.

If results from testing are confusing or in error - then waiting on author or other familiar with the code with posted sample would be needed.
 
Yes, I’ve seen this example but I hoped I could have precise answers (probably from the author) based on the hardware architecture and the ADC library design rather than having to measure since measuring won’t answer most of my questions that are related to synchronization and concurrent executions. I believe those should be easy answers, am I right? Measuring seems like a last resort.
If you are looking for precise information, sometimes the best thing to do is to look at the reference manual for the chip you are using.

Not sure which one you are using. But for example with T4, search the word continuous in chapter 65. It is not a different processor, but the ADC operations are done by it's own internal hardware... Again as for taking CPU resources that can depend on your configuration settings.

It more or less says, that when you startup a continuous update, the conversion will continue to until stopped. What the system does with the data depends on how you have things set up.
For example you could set an interrupt to happen when the conversion is complete, or you could have DMA setup to store out the data. Or if neither of those two is set, last one wins.

What happens if you start it up and then try to read before it has a chance to complete?
Might have to look at the code.

If you are worried, you can probably call the isComplete() method which will tell if there is a value available.
But if you call it, looking at sources you see:
Code:
    int analogReadContinuous() __attribute__((always_inline)) {
        #ifdef ADC_TEENSY_4
        return (int16_t)(int32_t)adc_regs.R0;
        #else
        return (int16_t)(int32_t)adc_regs.RA;
        #endif
    }
So it just retrieves whatever is currently in the appropriate analog converters value register. So no wait involved.
 
Good addition KurtE - I went to the DOCS directory in the ADC folder hoping to find details provided and then finding nothing but simple comments - decided - not wanting to look through sources that the best start to actual 'runtime' answers would be the sample. It seemed each of the questions could easily be coded into that example - which demonstrates setResolution and setAveraging which both are factors in the sample time.
 
KurtE, thanks a lot, that helped me much and pointed me at where to look for the actual implementation. I guess by just using the continuous read I will have what I need without blocking my main loop and without slowing down my program. But I’ll have to test it anyway.
 
I was looking through the code and I think I kind of see how it works now. However I have new questions :)

Seems like analogReadContinuous() will just give the last value stored in the corresponding ADC register without any wait.
- When is a new value being measured? Is a new measurement started immediately after the previous one finished or after the previous value was read by analogReadContinuous? If I don't read a value for long time, is sampling paused, so to speak?

My intuition tells me a measurement is performed constantly one after another, regardless of whether values are read by analogReadContinuous(), however if that's the case, I don't understand the purpose of isComplete() function. If there will always be a value to read, then this method will always return true except for the very beginning when you start the continuous mode and for a short time there won't be any value.
 
Again there many ways to do things, and which work well for you is hard to say.

I assume you already read the first message of this thread, which has some good summary (https://forum.pjrc.com/threads/25532-ADC-library-with-support-for-Teensy-4-3-x-and-LC)

Plus he has several pages of documentation which is great for libraries like this, many (like many of my own) have very little if any documentation.

And of course this library is just a front end for the hardware. Many of these methods simply set or read hardware registers. So again if you wish to fully understand how it is or can work, try looking at the appropriate chapter of the reference manual for the chip you are using. Which one are you using?

And many times the only way I find out how things work is by trying it out...

Again many times the answer to your questions, is: it depends.

Does the next one overwrite? Depends... For example if you look at the reference manual for T4 (IMXRT1060RM) in the ADC chapter (65) - At the ADCx_CFG register you will see a setting:
OVWREN -

Data Overwrite Enable
Code:
Controls the overwriting of the next converted Data onto the existing (previous) unread data into the Data
result register.
1 Enable the overwriting.
0 Disable the overwriting. Existing Data in Data result register will not be overwritten by subsequent
converted data.

So if I am running code that I am not sure how it works, I will often simply try it, and if I am still confused, I will have simple example sketch that maybe after I setup the continuous analog reads, I will serial print out the contents of several of these registers and see what they are actually configured to do...

isComplete is more often used when you are not necessarily doing continuous operations, but instead doing something like:

startSingleRead(14) ... run some code ... isComplete() ... if so readSingle()
 
Back
Top