Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 11 of 11

Thread: T4 ADC Library V0

  1. #1
    Junior Member
    Join Date
    Dec 2018
    Location
    PNW
    Posts
    14

    T4 ADC Library V0

    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.
    Attached Files Attached Files

  2. #2
    Thanks for doing it Bumbler@.

  3. #3
    following

  4. #4
    Senior Member
    Join Date
    Oct 2012
    Location
    Portland OR
    Posts
    677

    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 by JBeale; 10-06-2019 at 06:39 AM.

  5. #5
    Senior Member
    Join Date
    Oct 2012
    Location
    Portland OR
    Posts
    677
    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);
    }
    Click image for larger version. 

Name:	T4-jitter-PDB.png 
Views:	17 
Size:	15.0 KB 
ID:	17810

  6. #6
    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?

  7. #7
    Senior Member
    Join Date
    Oct 2012
    Location
    Portland OR
    Posts
    677
    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.

  8. #8
    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.

  9. #9
    Member
    Join Date
    Aug 2019
    Location
    southwest USA
    Posts
    50
    Quote Originally Posted by Bumbler View Post
    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.

  10. #10
    Junior Member
    Join Date
    Oct 2019
    Location
    Germany Maintal
    Posts
    15
    I tryed to read the INTERNAL_TEMP_SENSOR

    Blocking mode analogRead () works.

    But non blocking not.

    I am still using T 3.6

  11. #11
    Member
    Join Date
    Aug 2019
    Location
    southwest USA
    Posts
    50
    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 by analog&RFmodels; 10-23-2019 at 11:59 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •