T4 ADC Library V0

Status
Not open for further replies.

Bumbler

Member
There is more work to be done, but it seems to be functional, so I figured I would release what I have so far.

These files are a transcription of Pedvide's ADC library to the T4. My goal was to keep as much of the code from the T3 functional with the T4 as I could. So many of the examples from the T3 library work with little to no modification, but some things don't makes as much sense as they should. To use these files, replace the ones currently in the ADC library folder of the Teensy with these. Also, you need to move the RingBufferDMA.cpp and .h files out of the folder since I haven't gotten to those yet and the complier complains about them. The whole thing seems to work alright, but I have not done extensive testing across the board yet so if you run into bugs/issues, let me know.

I have used this to learn a lot more about the Teensy's and the ADC's in general, it has been a great learning experience for me, but my code leaves a lot to be desired. If anyone wants to improve upon it please do.

If anyone has questions, just drop them here. Hopefully these are of use to some people.

Some notes:

The T4 has no differential pins so if you try to use them, it will just throw a 'wrong-pin' error.

T4 only has 12 bits of resolution, higher selections default to 12.

While the T4 names the adc's 1 and 2, the code will still call them 0 and 1 to match the T3 convention.

I have not reworked the DMA ring-buffer files, so those ones don't work yet

For my "PDB", I used Qtimer 4 and buried the ISR in the .cpp file so you no longer need to write a seperate pdb_isr to clear the interrupt.

The Qtimer currently still software triggers the ADC instead of Hardware triggering it. It's on my list to change, but I haven't gotten there yet.

I have limited ability to test the frequency stability of my PDB, but cursory testing seems to match about right. I would greatly appreciate if someone could test the stability for me (it accepts frequencies from 20 - 75000000, but clearly won't work anywhere near the higher end).

I have been able to transfer 1.2-1.6 MSPS across the USB with my PDB so far, If I get to hardware triggering I imagine it will go higher.

If you are using only one ADC with the PDB, you may get an "adc1_isr definition error". Just add a dummy isr function somewhere in your code, eg: void adc1_isr(){}

TODO:

Merge this code back with Pedvide's so a single library handles all Teensy's
Create an 'Atomic.h' for T4 so it behaves better, rewrite code to match
Swtich the "PDB" to hardware triggering, look at using second timer so that each ADC can use different frequency
rewrite the DMA ring buffer to add T4.
Fix any bugs people find.
 

Attachments

  • ADC.h
    23.2 KB · Views: 162
  • ADC.cpp
    49.7 KB · Views: 184
  • ADC_Module.cpp
    49.1 KB · Views: 148
  • ADC_Module.h
    39 KB · Views: 144
checking out the new T4 ADC library

Thank you very much for getting this going! I think I installed it as directed, but I am wondering which examples should work.

"adc_pdb" only works if you comment out the pdb_isr() function, which I guess is what you meant about not needing a separate one to clear the interrupt.
Code:
adc_pdb: In function 'void pdb_isr()':
adc_pdb:113: error: 'PDB0_SC' was not declared in this scope
         PDB0_SC &=~PDB_SC_PDBIF; // clear interrupt
"analogReadIntervalTimer" fails to compile with this error:

Code:
analogReadIntervalTimer:188: error: 'ADC0_SC1A' was not declared in this scope
     uint8_t pin = ADC::sc1a2channelADC0[ADC0_SC1A&ADC_SC1A_CHANNELS]; // the bits 0-4 of ADC0_SC1A have the channel

Do you have an example of some working code with the new T4 ADC library using PDB?
I have a very precise and stable oscillator which can generate for example 1 kHz or 10 kHz output. I could get a batch of samples at a fixed rate using PDB, and then take a FFT of the output and look at the spectrum. Any instability in the effective sampling rate would show up as a wider spectral peak, or sidebands of some kind.

I tried a version of the adc_pdb example uncommenting the line in the adc0_isr() to blink the LED:
Code:
void adc0_isr() {
        adc->adc0->readSingle();
        digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN) );
}
and with a 1000 Hz sample rate there is no obvious jitter on the scope; less than a microsecond anyway.
 
Last edited:
Using a modified adc_pdb example from the ADC library and setting 100kHz sample rate, with the LED pin 13 toggling each sample. I am using a scope triggered on the pin 13 signal, and then zooming in the display to a point 5 msec (500 samples) later.
It is *usually* running right at 100 kHz with no visible jitter (< 10 nsec). Occasionally, I do see a momentary edge timing shift, up to about +/- 120 nsec.
This is the regular production Teensy 4.0 and code compiled with the "Optimize: Faster" setting.

Code:
void setup() {

// ...other stuff same as stock library example...

uint32_t freq = 100000;  // try a sampling rate of 100 kHz
   
adc->adc0->stopPDB();
adc->adc0->startSingleRead(readPin); // call this to setup everything before the pdb starts, differential is also possible
adc->enableInterrupts(ADC_0);
adc->adc0->startPDB(freq); //frequency in Hz
#if ADC_NUM_ADCS>1
adc->adc1->stopPDB();
adc->adc1->startSingleRead(readPin2); // call this to setup everything before the pdb starts
adc->enableInterrupts(ADC_1);
adc->adc1->startPDB(freq); //frequency in Hz
#endif

} // end setup()

void loop() {
    delay(10);
}

T4-jitter-PDB.png
 
Hi JBeale, I need to sample at 100KHZ on both of the T4 ADCs in parallel.

Can this be done with PDB or should I use a timer? Can you post an example?
 
I believe that is exactly what I did, 100 kHz sampling on both ADC_0 and ADC_1 using the "adc_pdb" library example and modified as I showed in my last post. However this code simply reads the data and throws it away. You would need to add the code inside the interrupt and maybe also in the main loop to store it or calculate with it or transmit it out in some way, whatever your application requires.
 
Thanks JBeale. Can you post here the full example?

The one in post #5 above does not include the reading (the standard Teensy dual channel example uses a single isr and a single read that return a struct with two values) and the setup of the two ADC channels in #5 above seems to be independent of each other so it's not clear how the samplings events are synchronized between the two channels.

Having the full example will help understanding how dual channel sampling works with PDB.
 
There is more work to be done, but it seems to be functional, so I figured I would release what I have so far.


TODO:

Merge this code back with Pedvide's so a single library handles all Teensy's
Create an 'Atomic.h' for T4 so it behaves better, rewrite code to match
Swtich the "PDB" to hardware triggering, look at using second timer so that each ADC can use different frequency
rewrite the DMA ring buffer to add T4.
Fix any bugs people find.

Bumbler,

V0 works well. took some code that does interval timer driven 12 bit manual conversions (value=adc->analogRead(readPin)
every 4 microseconds (250 khz). In the T3.6 at 180 mhz atod hardware averaging was 1, sampling and conversion were
VERY_HIGH_SPEED, i averaged 8 whole time domain sets, did float fft, and the spurious free dynamic range was 69 db.
With T4.0 at 600 mhz and your V0, same 4 microseconds, there was time to do hardware averaging of 8, sampling and
conversion were HIGH_SPEED, i averaged 8 whole time domain sets, did float fft, and the spurious free dynamic range
improved to 79 db.

Using T4.0 VERY_HIGH_SPEED and hardware averaging of 16 gave spurious free dynamic range of only 56 db.

so i am thinking that V0 of your code is excellent, and the atod in the T4.0 is very usable.

thank you for sharing that with everybody.
 
I tryed to read the INTERNAL_TEMP_SENSOR

Blocking mode analogRead () works.

But non blocking not.

I am still using T 3.6
 
i'm sure you know this already, but V0 will not compile some adc code if target is Teensy LC
(and i suspect same for Teensy 3.6) altho just fine w Teensy4 (see my earlier post this thread
re how well V0 and T4 adc work)

work around is easy - just put the original versions of these pgms back in the adc lib folder(ADC.cpp, ADC.h,
ADC_Module.cpp, ADC_Module.h) that came w Teensyduino 1.48. so use your versions Teensy 4 and
TD 1.48 versions else. for what i am doing i did not have to put RingBufferDMA.cpp and RingBufferDMA.h back
in the folder. all under win7x64, Arduino 1.8.5, TeensyDuino 1.48 release.

here is a code excerpt - big pgm - but if you need i can boil down to minimum that shows problem and post.


Code:
#include <ADC.h>

  const int readPin = A10; // ADC0



void getad() {
    cli();
    value = adc->analogRead(readPin);
    if (of==0) ary.adin[count]  = value;
    else       ary.adin[count] += value;
    count = count + 1;
    if(count > dn5) flagg = 1;
    sei();
}
 
Last edited:
Status
Not open for further replies.
Back
Top