This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C1294NCPDT: Noise in ADC measurements

Part Number: TM4C1294NCPDT
Other Parts Discussed in Thread: TEST2

Dear support,

I've made several aquisitions to measure the wave applied to a BLDC motor (back EMF). I'm using a voltage divider with a RC filter and also reading the values with an oscilloscope.

The problem is, in some velocities of the motor, it seems to have a lot of noise. Here I present the ADC Input measured in 64 kHz.

My pwmInput varies between 7500 and 12500 arbitrary units, which are, in practical terms, varying the input duty cycle from 1 to 2 ms. In the chart below, before the 16000th sample, the wave I'm measuring has a frequency of 1,68 kHz, and above it has 2,04 kHz. I've also performed a Medium Average Filter (MAF), with 3 and 10 samples, as shown in the chart below:

1,68 kHz wave:

2,04 kHz wave:

Some possibilities I've discarted:

  • Sampling problem: The max frequency of the wave I'm measuring is 2 kHz, so 64 kHz should be good enough. I've tried also with 16 and 100 kHz, the aquisition remains the same;
  • ADC distance: Some people recommended me to short the distance between the ADC and the aquisition point. I've made a shield, which is attached below the launchpad;
  • Voltage divider: I've read in a document that, for impedance matching purposes, the resistor of the measured point should be 1 kOhm. I was using 10 kOhm, changed to 1 kOhm, it doesn't show so many differences in the ADC reading.

I'm run out of ideas, so, I'd really appreciate some help with this issues.

Here's the .xlsx with the graph. I've also made a drive with different aquisitons and with the original .csv files: 

0217_Test2.xlsx

Code used (although is pretty similar to the one from the related question):

//*****************************************************************************
// Libraries
//*****************************************************************************

#include <stdint.h>
#include <stdbool.h>

#include "driverlib/adc.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/pwm.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/timer.h"
#include "driverlib/uart.h"

#include "drivers/pinout.h"

#include "inc/hw_gpio.h"
#include "inc/hw_ints.h"        // Added for the Timer
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_adc.h"
#include "inc/hw_timer.h"
#include "inc/hw_nvic.h"
#include "inc/hw_sysctl.h"

#include "utils/uartstdio.h"


//*****************************************************************************
// Info
//*****************************************************************************
// uint32_t - unsigned integer of 32 bits - range from 0 to 2^32 - 1

//*****************************************************************************
// Variable Declaration
//*****************************************************************************

// ADC
uint32_t ui32adcValues[4];              // Variable for storing values from ADC
volatile bool bDataReady = false;       // Flag to indicate the ADC is ready

// ADC Filter
uint32_t adcSumFilterCounter = 1;                   // Variable used for counter
uint32_t ADC_SUM_FILTER_SAMPLES_NUMBER = 1;        // Size of the sum filter
uint32_t analogValue_ADC3 = 0;                      // Variable for storing values

// System
uint32_t g_ui32SysClock;                // System clock rate in Hz.
uint32_t SYSTEM_CLOCK  = 60000000;      // 64 MHz

// PWM
float analogValue = 0, analogAvg = 0, pwmMaxFloat = 6000;

float adcToPwmRatio = 0;            // Ratio between used pwm values and ADC resolution
float pwmMin = 7500;                 // Minimum value of the pwm, motor at zero speed
float pwmMax = 14500;                 // Maximum value of the pwm, motor at full speed
float analogValueFloat_ADC3;        // Value from ADC after filter to acquire

uint32_t sumCounter = 0, iSamples = 1000, pwmValue = 0;
volatile uint32_t pwmInput = 1;
uint32_t pwmDutyCycle = 15000;      // 500 Hz

// Timer
volatile unsigned long g_ui32TimerIntCount = 0;
volatile unsigned long g_ui32SecondsOnTime = 0;
#define APP_TICKS_PER_SEC  100;
bool bTimerDone = false;
uint32_t SampleFreq = 0;


// Measuring time needed for acquisition
uint8_t measureCounter = 0;


// Measuring, StateMachine
volatile enum {
    IDLE,
    INITIAL_MEASUREMENTS,
    INITIAL_CALCULATION,
    WAIT_FOR_POS_CYCLE,
    WAIT_FOR_HALF_CYCLE,
    WAIT_FOR_FULL_CYCLE,
    UPDATE_MEASUREMENTS
} MeasState;


// Speed cycling State Machine
volatile enum {
    SPEED1,
    SPEED2,
    SPEED3,
    SPEED4,
    SPEED5,
    SPEED6,
    STOP
} CyclicState;


uint32_t iSpeedStateDelay = 0;
uint32_t pwmMeas = 0;


#define HYSTH_MIN_ADC                   250      // Minimum value for the ADC recognize the motor is in motion
#define INITIAL_MEAS_NUMBER             50      // Initial measurement numbers

uint32_t sumADC3    = 0;        // Stores the cummulative sum of ADC3
uint32_t iADCSum3   = 0;        // Initial Counter for the number of measurements done by ADC3
uint32_t nCycle3    = 0;        // Steady counter for the number of measurements done by ADC3

uint32_t avgADC3       = 0;            // Average value of ADC3
uint32_t currentFreq3  = 0;            // Current Frequency of ADC3
long inv_deltaT     = 16000000;     // Frequency of ADC sampling - 16 kHz

bool bStartRunning  = 0;

//*****************************************************************************
// The error routine that is called if the driver library encounters an error.
//*****************************************************************************
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif

//*****************************************************************************
// ADC0 Sequence ISR - 19.01.2021
//*****************************************************************************
void
ADC0Sequence0ISR(void)
{

    if (measureCounter == 1) {
        GPIOPinWrite(GPIO_PORTL_BASE, GPIO_PIN_3, 0);
        measureCounter = 2;
    }

    if (measureCounter == 0) {
        GPIOPinWrite(GPIO_PORTL_BASE, GPIO_PIN_3, GPIO_PIN_3);
        measureCounter = 1;
    }

    if (measureCounter == 2) {
        measureCounter = 0;
    }

    ADCIntClear(ADC0_BASE, 0);

    ADCSequenceDataGet(ADC0_BASE, 0, ui32adcValues);

    if (!bDataReady)
    {
        bDataReady = true;
    }

}

//*****************************************************************************
// ADC Initialization - 19.01.2021
//*****************************************************************************
void
ConfigureADC(void)
{
    SysCtlPeripheralReset(SYSCTL_PERIPH_GPIOE);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    SysCtlPeripheralReset(SYSCTL_PERIPH_ADC0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    SysCtlDelay(10);

    GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3 | GPIO_PIN_2 | GPIO_PIN_1 | GPIO_PIN_0 );

    //
    // Configure the ADC to use PLL at SYSTEM_CLOCK (96 MHz) divided by 3 to get an ADC
    // clock of 32 MHz, sampling at FULL rate.
    //
    ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PLL | ADC_CLOCK_RATE_FULL, 50);

    //
    // Wait for the clock configuration to set.
    //
    SysCtlDelay(10);

    // Choose Sequencer 2 and set it at the highest Priority.
    ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_TIMER,  0);
    //ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_ALWAYS,  0);

    ADCSequenceStepConfigure(ADC0_BASE,0,0, ADC_CTL_CH0);
    ADCSequenceStepConfigure(ADC0_BASE,0,1, ADC_CTL_CH1);
    ADCSequenceStepConfigure(ADC0_BASE,0,2, ADC_CTL_CH2);
    ADCSequenceStepConfigure(ADC0_BASE,0,3, ADC_CTL_CH3 | ADC_CTL_IE | ADC_CTL_END);

    ADCSequenceEnable(ADC0_BASE, 0);
    ADCIntClear(ADC0_BASE, 0);
    ADCIntEnable(ADC0_BASE, 0);
    IntEnable(INT_ADC0SS0);
}

//*****************************************************************************
// PWM Initialization - 13.09.2020
//*****************************************************************************
void Init_PWM(){

    // 1.
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);            // Enable Port F
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF));

    // 2.
    SysCtlPeripheralDisable(SYSCTL_PERIPH_PWM0);
    SysCtlPeripheralReset(SYSCTL_PERIPH_PWM0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);
    while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_PWM0)));


    // PWM0_BASE: 0 -> 0 from M_0_PWM1
    // PWM_OUT_1 and PWM_OUT_1_BIT: 1 from M0PWM_1
    // PWM_GEN_0: GEN_0 for PWM0 and PWM1, GEN_1 for PWM2 and PWM3...


    PWMClockSet(PWM0_BASE, PWM_SYSCLK_DIV_8);           // SYSTEM_CLOCK / 32 = 3 MHz

    GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_1);        // Pin 1 from PortF - PF1
    GPIOPinConfigure(GPIO_PF1_M0PWM1);
    PWMGenConfigure(PWM0_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);
    PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, pwmDutyCycle);       // 120 MHz / pwmMax = pwmFreq
    PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1, 1);
    PWMGenEnable(PWM0_BASE, PWM_GEN_0);
    PWMOutputState(PWM0_BASE, PWM_OUT_1_BIT, true);

    PWMIntEnable(PWM0_BASE, PWM_INT_GEN_0);
    PWMGenIntTrigEnable(PWM0_BASE, PWM_GEN_0, PWM_TR_CNT_ZERO);
    IntEnable(INT_PWM0_0);

}

//*****************************************************************************
// UART Initialization - 19.01.21
//*****************************************************************************
void
InitConsole(void){

    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);        // Enable GPIO port A which is used for UART0 pins.

    //
    // Configure the pin muxing for UART0 functions on port A0 and A1.
    // This step is not necessary if your part does not support pin muxing.
    //
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);

    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);        // Enable UART0 so that we can configure the clock.

    UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);   // Use the internal 16MHz oscillator as the UART clock source.

    //
    // Select the alternate (UART) function for these pins.
    //
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    UARTStdioConfig(0, 115200, 16000000);               // Initialize the UART for console I/O.
}


//*****************************************************************************
// Timer Initialization - 26.01.2021
//*****************************************************************************
void
ConfigureTimer(void) {

    //
    // Enable the peripherals used by this example.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);

    //
    // Configure a 16-bit periodic timer.
    //
    TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PERIODIC);

    //
    // Set ADC sampling frequency to be 16KHz i.e. every 62.5uS.
    //
    TimerLoadSet(TIMER0_BASE, TIMER_A, (g_ui32SysClock/64000) - 1);

    //
    // Enable the ADC trigger output for Timer A.
    //
    TimerControlTrigger(TIMER0_BASE, TIMER_A, true);

    //
    // Enable processor interrupts.
    //
    IntMasterEnable();

    //
    // Enable Timer 0 which will start the whole application process.
    //
    TimerEnable(TIMER0_BASE, TIMER_A);
}


int
main(void){

    g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
                     SYSCTL_OSC_MAIN | SYSCTL_USE_PLL |
                     SYSCTL_CFG_VCO_480), SYSTEM_CLOCK);              // Run from the PLL at SYSTEM_CLOCK MHz


    InitConsole();      // Set up the serial console to use for displaying messages
    ConfigureTimer();   // Set up the Timer
    ConfigureADC();     // Set up the ADC
    Init_PWM();         // Set up the PWM

    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);            // Enable Port N
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPION));

    GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_1);     // Enable the GPIO pins for the LED D1 (PN1).


    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOL);            // Enable Port L
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOL));

    GPIOPinTypeGPIOOutput(GPIO_PORTL_BASE, GPIO_PIN_3);     // Enable the GPIO pins for the PL3


    IntMasterEnable();

    // Conversion relative to max and min pwm used and ADC reading
    adcToPwmRatio = (pwmMax - pwmMin)/4095.0;       // 4095 = 2^12 bits
    pwmInput = pwmMin;                              // Start on pwmMin value

    UARTprintf("pwmValue,");
    UARTprintf("AIN0,");   // \r to replace, \n to new line
    //UARTprintf("AIN1,");
    //UARTprintf("AIN2,");
    UARTprintf("AIN3\n");
    //UARTprintf("avgADC3");
    //UARTprintf("currentFreq3");

    // Initiate MeasState in IDLE
    MeasState = IDLE;


    // Infinite loop
    while(1) {

        if (bDataReady) {

            UARTprintf("%3d,", pwmInput);
            UARTprintf("%4d,", ui32adcValues[0]);   // \r to replace, \n to new line
            //UARTprintf("%4d,", ui32adcValues[1]);
            //UARTprintf("%4d,", ui32adcValues[2]);
            UARTprintf("%4d\n", ui32adcValues[3]);
            //UARTprintf("%4d,", avgADC3);
            //UARTprintf("%8d\n", currentFreq3);

            bDataReady = false;
        }


        if (iSpeedStateDelay < 191000){
            iSpeedStateDelay = iSpeedStateDelay + 1;
        }

        if (iSpeedStateDelay == 20000){
            pwmInput = 7500;
        }

        if (iSpeedStateDelay == 40000){
            pwmInput = 8500;
        }

        if (iSpeedStateDelay == 60000){
            pwmInput = 9500;
        }

        if (iSpeedStateDelay == 80000){
            pwmInput = 10500;
        }

        if (iSpeedStateDelay == 100000){
            pwmInput = 11500;
        }

        if (iSpeedStateDelay == 120000){
            pwmInput = 12500;
        }

        if (iSpeedStateDelay == 140000){
            pwmInput = 12000;
        }

        if (iSpeedStateDelay == 160000){
            pwmInput = 10000;
        }

        if (iSpeedStateDelay == 170000){
            pwmInput = 8200;
        }

        if (iSpeedStateDelay == 180000){
            pwmInput = 7750;
        }

        if (iSpeedStateDelay == 190000){
            pwmInput = 7500;
        }

    }
}

Best regards,

Gustavo Wegher

  • The most common problem I have seen is source impedance and sample time. If your source impedance it too large for your sample time, the other channels you convert will affect your result. Possible solutions: 

    1. Decrease your source impedance

    2. Increase your sample time

    3. Add an external sample capacitor

    See: https://www.ti.com/lit/an/spna061/spna061.pdf

  • Hello Gustavo,

    Reviewing your code I noticed you configured 4 ADC steps and 1 PWM generator. Note center aligned synchronous PWM generators with up/dwn count is most typical. It is important to synchronize the ADC sample with the PWM generator/s. ADC module triggered via a timer versus using a generator trigger source may require delay as to slow/sync sampling window to the desired signal acquisition point/s. Even though sampling  window may be twice Nyquist frequency, that does not infer the point of signal acquisition or count will be maintained or even achieved.

    BTW: If you are attempting to create a sinusoidal space vector modulation scheme that may be an futile attempt via 1294 MCU class. Otherwise a six step trapezoidal wave is very doable easily achieved but requires look up table/s for Hall code generation.