element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • 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 2135 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
  • DAB
    DAB over 5 years ago

    Nice update Jan.

     

    I usually use a timer and interrupts to perform ADC sampling, but then I had some time dependent signals I was sampling.

    Have you considered DMA or is your data need too small?

     

    DAB

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 5 years ago in reply to DAB
    I usually use a timer and interrupts to perform ADC sampling

    I'm using a scheduled RTOS job. Not an exact timer (it's depending on priority of competing jobs) , but that's not the need here in the DC load context.

     

     

    I had some time dependent signals I was sampling.

     

    That's not the case in this design. I need to get a current measurement  fairly fast. But there's no need to "sample the dynamic signal".

    So no need to deal with Niquist or jitter.

     

     

    Have you considered DMA or is your data need too small?

    The ADC is external to the microcontroller, and not memory mapped.

    Data exchange is via I2C. Only a few bytes. I could DMA the i2C but there's no need in this design.

     

     

    The requirements aren't that high speed-wise. But higher than my current implementation image.

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • More
    • Cancel
Comment
  • Jan Cumps
    Jan Cumps over 5 years ago in reply to DAB
    I usually use a timer and interrupts to perform ADC sampling

    I'm using a scheduled RTOS job. Not an exact timer (it's depending on priority of competing jobs) , but that's not the need here in the DC load context.

     

     

    I had some time dependent signals I was sampling.

     

    That's not the case in this design. I need to get a current measurement  fairly fast. But there's no need to "sample the dynamic signal".

    So no need to deal with Niquist or jitter.

     

     

    Have you considered DMA or is your data need too small?

    The ADC is external to the microcontroller, and not memory mapped.

    Data exchange is via I2C. Only a few bytes. I could DMA the i2C but there's no need in this design.

     

     

    The requirements aren't that high speed-wise. But higher than my current implementation image.

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • More
    • Cancel
Children
No Data
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