element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Test & Tools
  • Technologies
  • More
Test & Tools
Documents Programmable Electronic Load - ADC Firmware
  • Blog
  • Forum
  • Documents
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Test & Tools to participate - click to join for free!
Actions
  • Share
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 3 May 2020 10:24 AM Date Created
  • Last Updated Last Updated: 6 Oct 2021 10:20 PM
  • Views 2284 views
  • Likes 5 likes
  • Comments 17 comments
Related
Recommended

Programmable Electronic Load - ADC Firmware

This post documents the ADC firmware for the electronic load we made here on element14.

image

 

ADC

 

image

 

ADC samples are taken all the time, based on a TI-RTOS schedule.

Data is written to a round-robin buffer with 2 buckets.

 

volatile ADCValues adcRoundRobin[2];
volatile uint32_t adcRoundRobinIndex[ADC_ACTIVE_INPUTS] = {0};

 

One bucket has stable data and can be read whenever needed. A read pointer points to that stable bucket.

The other bucket is used to write sample data. Once samples from 4 channels are collected, the read pointer is toggled so that it points to that fresh data.

I've optimised this. I've added a read pointer for each channel now:

 

volatile uint32_t adcRoundRobinIndex[ADC_ACTIVE_INPUTS] = {0};

 

Once a channel is collected, the read pointer for that channel is toggled so that it points to the fresh data.

 

 

void *threadADC(void *arg0) {
    uint32_t i;


    a_i2cTransaction.writeBuf = a_txBuffer;
    a_i2cTransaction.readBuf = a_rxBuffer;
    a_i2cTransaction.slaveAddress = ADC_I2C_ADDR;


    // this buffer value never changes. Let's set it at the start.
    // If for some reason this becomes a variable value,
    // move to sampleADC()
    a_txBuffer[2] = ADS1115_CFG_L;


    while (1)
    {
        for (i =0; i< ADC_ACTIVE_INPUTS; i++) {
            // we write value to the inactive robin
            // store value of ADC[i]
            // the ADC needs time between channel selection and sampling
            // we assign 1/ADC_ACTIVE_INPUTS of the task sleep time to
            // each of the ADC_ACTIVE_INPUTS samples
            // this puts more burden on the RTOS switcher - a compromise
            // - but certainly preferable to a loop
            // (except when later on we find out that the wait is only a few cpu cycles)
            adcRoundRobin[adcRoundRobinIndex[i] ? 0 : 1].raw[i] = sampleADC(i, THREAD_USLEEP_ADC / ADC_ACTIVE_INPUTS);
            // after value(s) written, we activate the inactive robin
            adcRoundRobinIndex[i] = adcRoundRobinIndex[i] ? 0 : 1;
        }

    }
}

 

Because there is time needed between switching ADC channels and taking the sample, we give each of the four ADC channels 1/4th of the TI-RTOS schedule that they can spend on that time.

 

x = sampleADC(i, (UInt)arg0/4);

 

Sampling is done via I²C. First part is switching the ADC channel. Then a sleep to give the ADC time, then fetch:

 

uint16_t sampleADC(uint32_t uModule, UInt uSleep) {
    uint16_t uRetval = 0u;

    // ...


     a_txBuffer[1] = array_ADS1115_CFG_H[uModule];

    // ...

    /* Init ADC and Start Sampling */
    if (! I2C_transfer(i2c_implGetHandle(), &a_i2cTransaction)){
        System_printf("Sampling Start Failed \n");
    }

    // there's a pause required between channel selection and data retrieval
    // we consume that part of the task sleep time that's assigned to us by the task.
    Task_sleep(uSleep);

    // ...

    /* Read ADC */
    if (I2C_transfer(i2c_implGetHandle(), &a_i2cTransaction)) {
        uRetval = ((a_rxBuffer[0] << 8) | a_rxBuffer[1]);
    }
    else {
        System_printf("ADC Read I2C Bus fault\n");
    }

    return uRetval;
}

 

Reading is done via a helper function - exposed as API to the program. This can be used anytime without locking or semaphore, because the time between switching the read pointer and the next possible write in that buffer is way longer (several orders of magnitude) than calling the helper function is an atomic command.

 

uint16_t adcImplGetAdc(uint32_t uModule) {
    return adcRoundRobin[adcRoundRobinIndex[uModule]].raw[uModule];
}

  • Share
  • History
  • More
  • Cancel
  • Sign in to reply

Top Comments

  • Andrew J
    Andrew J over 5 years ago in reply to Jan Cumps +3
    This will be interesting - I'm creating ADC interaction with Arduino code. Have you characterised the Zero Scale Error, Full Scale Error, Offset and Gain? It would seem from my brief checking of data sheets…
  • Jan Cumps
    Jan Cumps over 5 years ago in reply to Andrew J +3
    Have you characterised the Zero Scale Error, Full Scale Error, Offset and Gain? It would seem from my brief checking of data sheets (small sample) that ADCs are better than DACs in these specs but can…
  • Jan Cumps
    Jan Cumps over 5 years ago +2
    I'm going to try and improve this part of the firmware. At this moment it's a round-robin system that has data that's between 333 ms and 1s old. That's an eternity. Code is here: https://github.com/jancumps…
Parents
  • Andrew J
    Andrew J over 5 years ago

    Hi Jan,

     

    Over the last couple of days I've been looking at improving the results of sampling with the ADC I have (MCP3428.)  This is theory reading as I've not undertaking detailed testing yet - I really need to move off the breadboard - but it was trying to determine whether or not I should buffer the ADC inputs and put a low pass filter (for noise) in front as well to finalise my circuit and create a PCB.

     

    What I've gathered so far...

    The voltage divider you are using is going to impact the sampling accuracy of the ADC because its resistance is going to couple with the sampling capacitor and give rise to a RC time constant in charging it up between samples.  IIRC you are using an ADS1115 which has a MUX in front of a single ADC and it wants a low impedance source (the mux/single ADC is important because of the way that means the ADC samples across the channels and the RC constant.)  If your sampling frequency is too fast you won't give the sampling capacitor time to discharge/charge between samples and this will manifest itself as inaccuracies on the channel you are reading because it will still maintain some charge from the previous channel, or would not have had chance to charge completely.  With the voltage divider you may find a buffer useful, but then you will want to put a series resistance between its output and the ADC input.  See where that is going....In any case, it seems better to use a low pass filter between the buffer and the pin and that has the benefit of (a) giving the op amp a low capacitance load on its output; (b) a charging reservoir for the sampling capacitor to improve the possible sampling rate.  OR make sure the sampling rate - that is, the number of times you ask it to sample - is low enough to not impact the results.

     

    Now, let me caveat all the above by saying you know a lot more than I do about this stuff and I may be reading incorrectly into this stuff.  I've also not yet thought how the RDY pin (byte in my case) would impact that - if the sampling rate will be slowed down by waiting for that indicator which would mitigate the RC issue, but it's not clear from the datasheet that implies high impedance on the inputs impacts accuracy. 

     

    Here are my references:

    http://www.ti.com/lit/an/spna061/spna061.pdf

    and

    https://www.embeddedrelated.com/showarticle/110.php

     

    I ran through the TI paper making calculations and the embeddedrelated paper, which I came across afterwards, actually confirms the calculations I did.  I'm still at a loss to the value of the R in the low pass filter.  My calcs give it as a smidgen under 54000Ohms which is clearly wrong!  So I'm still confused on that bit.

     

    Hope this was of some help and hasn't been a distraction.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
Comment
  • Andrew J
    Andrew J over 5 years ago

    Hi Jan,

     

    Over the last couple of days I've been looking at improving the results of sampling with the ADC I have (MCP3428.)  This is theory reading as I've not undertaking detailed testing yet - I really need to move off the breadboard - but it was trying to determine whether or not I should buffer the ADC inputs and put a low pass filter (for noise) in front as well to finalise my circuit and create a PCB.

     

    What I've gathered so far...

    The voltage divider you are using is going to impact the sampling accuracy of the ADC because its resistance is going to couple with the sampling capacitor and give rise to a RC time constant in charging it up between samples.  IIRC you are using an ADS1115 which has a MUX in front of a single ADC and it wants a low impedance source (the mux/single ADC is important because of the way that means the ADC samples across the channels and the RC constant.)  If your sampling frequency is too fast you won't give the sampling capacitor time to discharge/charge between samples and this will manifest itself as inaccuracies on the channel you are reading because it will still maintain some charge from the previous channel, or would not have had chance to charge completely.  With the voltage divider you may find a buffer useful, but then you will want to put a series resistance between its output and the ADC input.  See where that is going....In any case, it seems better to use a low pass filter between the buffer and the pin and that has the benefit of (a) giving the op amp a low capacitance load on its output; (b) a charging reservoir for the sampling capacitor to improve the possible sampling rate.  OR make sure the sampling rate - that is, the number of times you ask it to sample - is low enough to not impact the results.

     

    Now, let me caveat all the above by saying you know a lot more than I do about this stuff and I may be reading incorrectly into this stuff.  I've also not yet thought how the RDY pin (byte in my case) would impact that - if the sampling rate will be slowed down by waiting for that indicator which would mitigate the RC issue, but it's not clear from the datasheet that implies high impedance on the inputs impacts accuracy. 

     

    Here are my references:

    http://www.ti.com/lit/an/spna061/spna061.pdf

    and

    https://www.embeddedrelated.com/showarticle/110.php

     

    I ran through the TI paper making calculations and the embeddedrelated paper, which I came across afterwards, actually confirms the calculations I did.  I'm still at a loss to the value of the R in the low pass filter.  My calcs give it as a smidgen under 54000Ohms which is clearly wrong!  So I'm still confused on that bit.

     

    Hope this was of some help and hasn't been a distraction.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
Children
  • Jan Cumps
    Jan Cumps over 5 years ago in reply to Andrew J
    The voltage divider you are using is going to impact the sampling accuracy of the ADC because its resistance is going to couple with the sampling capacitor and give rise to a RC time constant in charging it up between samples.

     

    This is a test setup to see if ADC B reports voltage from ADC A. Just that.

    Something that takes care that all 4 inputs have a different voltage level, so that I can see if the DAC doesn't accidentally report the sample of the previous channel.

     

    I have tools to properly drive them (either using the 4 DACs that I have available, or a low impedance voltage divider).

    Not needed here. I just need 4 voltage levels that I can recognise in the results.

     

    edit: the goal is to see if my timing optimisation doesn't result in reading from the wrong channel.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Andrew J
    Andrew J over 5 years ago in reply to Jan Cumps

    Ok, got it.  I was thinking of using a ramp output from a AWG - I haven’t checked much further than thinking yet!

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube