cembeddedsignal-processingmicrocontrolleradc

How to obtain stable ADC readings from a DC battery using EK-TM4C123GXL(ARM Cortex M4)?


I am working on a project using the Texas Instruments EK-TM4C123GXL LaunchPad to measure the voltage of a breadboard power supply module(hw-131) . I am using the onboard ADC module to read the voltage, but I am facing some issues:

  1. Problem Description:
    • I have connected the 3.3V terminal of the power supply module to the ADC input pin (PE3/AIN0) of the TM4C123GXL.
    • The ADC conversion is done using the internal 3.3V reference voltage.
    • I have already confirmed with a multimeter that the voltage input to the TM4C123GXL from the power module is stable at 2.8 volts, with fluctuations of no more than ±0.05 volts.
    • During testing, I noticed that the ADC output values are unstable and fluctuate significantly, even when the input voltage remains steady.
  2. What I've Tried:
    • I checked the ADC configuration and clock settings, and I have ensured that appropriate sampling time and resolution are being used.
    • I also implemented averaging of multiple samples, but this did not completely eliminate the fluctuations.
  3. Relevant Code:
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_ints.h"
#include "driverlib/sysctl.h"
#include "driverlib/adc.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
#include "driverlib/pin_map.h"

#define NUM_SAMPLES 8

// Function to configure UART0 for communication
void ConfigureUART(void)
{
    // Enable UART0 and GPIOA peripherals
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    // Configure PA0 and PA1 as UART pins
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    // Initialize UART0 with a baud rate of 115200
    UARTStdioConfig(0, 115200, SysCtlClockGet());
}

// ADC0 interrupt handler
void ADC0IntHandler(void)
{
    // Clear the ADC interrupt flag
    ADCIntClear(ADC0_BASE, 0);

    uint32_t ui32ADC0Value[NUM_SAMPLES];
    uint32_t ui32Sum = 0;
    uint32_t i = 0;

    // Get the ADC sequence data
    ADCSequenceDataGet(ADC0_BASE, 0, ui32ADC0Value);

    // Sum the ADC values
    while(i < NUM_SAMPLES){
        ui32Sum += ui32ADC0Value[i];
        i++;
    }

    // Calculate the average ADC value
    uint32_t ui32Average = ui32Sum / NUM_SAMPLES;

    // Print the average ADC value to the UART
    UARTprintf("ADC Average Value: %d\n", ui32Average);

    // Calculate the input voltage in volts (assuming 3.3V reference voltage)
    float voltage = (ui32Average * 3.3) / 4096.0;
    uint32_t v_int_part = voltage; // Integer part of the voltage
    uint32_t v_frac_part = (voltage - v_int_part) * 1000; // Fractional part in millivolts

    // Print the calculated voltage to the UART
    UARTprintf("Input Voltage: %d.%03d V\n", v_int_part, v_frac_part);
}

int main(void)
{
    // Set the system clock to 50MHz (200MHz PLL / 4)
    SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);

    // Configure UART for debugging
    ConfigureUART();

    // Enable the ADC0 and GPIOE peripherals
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);

    // Wait for the peripherals to be ready
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0) || !SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOE))
    {
    }

    // Configure PE3 as ADC input
    GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);

    // Set the ADC clock source and rate
    ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PIOSC | ADC_CLOCK_RATE_FULL, 8);

    // Configure ADC sequence 0 to be triggered by the processor
    ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_PROCESSOR, 0);

    // Configure all 8 steps in the sequence to sample from CH0 (PE3)
    ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH0);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_CH0);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH0);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 3, ADC_CTL_CH0);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 4, ADC_CTL_CH0);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 5, ADC_CTL_CH0);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 6, ADC_CTL_CH0);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 7, ADC_CTL_CH0 | ADC_CTL_IE | ADC_CTL_END); // Enable interrupt on last step

    // Enable ADC sequence 0
    ADCSequenceEnable(ADC0_BASE, 0);

    // Clear any pending ADC interrupts
    ADCIntClear(ADC0_BASE, 0);

    // Enable ADC interrupts
    ADCIntEnable(ADC0_BASE, 0);

    // Enable the ADC0 sequence 0 interrupt in the NVIC
    IntEnable(INT_ADC0SS0);

    // Set ADC0 sequence 0 interrupt priority to highest (0)
    IntPrioritySet(INT_ADC0SS0, 0x00);

    while(1)
    {
        // Trigger the ADC conversion
        ADCProcessorTrigger(ADC0_BASE, 0);

        // Delay to simulate some processing time
        SysCtlDelay(SysCtlClockGet() / 6);
    }
}

  1. My Questions:
    • Are there any methods to stabilize these readings, such as better software techniques?
    • Do I need to make specific ADC configurations to stabilize the input signal?
    • What are the best practices for this type of application that you would recommend?
  2. Circuit: my circuit
  3. fluctuate data as shown:
    • when the power supply module ON when the power supply module ON
    • when the power supply module OFF] when the power supply module OFF

It seems that power supply module OFF fluctuate more significantly than power supply module ON.

Any suggestions or guidance would be greatly appreciated! Thank you!


Solution

  • The large fluctuations are errors in your debug output and not "real". For example when you have an ADC value 9 corresponding to (9 x 3.3) / 4096 = 0.007 Volts, your debug code is outputting 0.7 Volts.

    It looks as if your UARTprintf function does not support the %03d format-specifier. Or the code you have posted is not the same as the code generating that output. Save yourself the trouble, and use integer arithmetic and output the value in millivolts.

    Otherwise I would say that the raw ADC fluctuation is a plausible level electrical noise from your power supply, the MCU itself, or even cross-talk from the UART output.

    Your attempt at averaging is ineffective, taking only 8 samples probably at a far higher frequency than the noise you are trying to remove. A larger moving average or an average taken from many readings over a long period of perhaps 0.5 seconds, or even an FIR or IIR low-pass filter would produce far better results. Either way your signal conditioning needs to be appropriate to the characteristics of the noise you are observing. Note that such a filter will perform poorly if the sample rate is variable, which it will be if you attempt to output readings to the UART in the interrupt handler - just one more way your debug code will give you misleading results.