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.

TMS320F280039C: ADC B and ADC C are not reading correct ADC values

Part Number: TMS320F280039C

Hello, 

I have a weird problem. I have one project that reads perfect values from ADC A, B, and C. Then I have another project with what looks like the same init code that reads ADCA perfectly, but ADC B and C are wrong by as much as 30 counts in some places. 

I've used Beyond Compare to compare the initialization between the 2 projects, is there anything that comes to mind I should check that could cause this? 

Init code: 

void Setup_FastADC()
{
    // Select interrupt source - this will be the last sample converted and therefore trigger the interrupt 1
    ADC_setInterruptSource(ADCC_BASE, ADC_INT_NUMBER1, ADC_DCBUS_SOCNUM);

    // Setup ADCs for PhA - this is triggered based off of counter EPWM1 SOCA (setup below)
    // Sample window must be > 75ns per datasheet, > 90ns for AGPIO per datasheet (at least > 11 SYSCLK cycles to be safe - 91.3ns)
    // Sample window must also be as long as 1 ADC clock cycle (40ns) - input sample window in SysClk cycles (11 cycle = 91.3ns)
    ADC_setupSOC(ADC_PHA_I_BASE, ADC_PHA_I_SOCNUM, ADC_TRIGGER_EPWM1_SOCA, ADC_PHA_I_CHANNEL, ADC_SAMPLE_WINDOW);
    ADC_setInterruptSOCTrigger(ADC_PHA_I_BASE, ADC_PHA_I_SOCNUM, ADC_INT_SOC_TRIGGER_NONE);

    // Setup ADCs for PhB - this is triggered based off of counter EPWM1 SOCA (setup below)
    // Sample window must be > 75ns per datasheet, > 90ns for AGPIO per datasheet (at least > 11 SYSCLK cycles to be safe - 91.3ns)
    // Sample window must also be as long as 1 ADC clock cycle (40ns) - input sample window in SysClk cycles (11 cycle = 91.3ns)
    ADC_setupSOC(ADC_PHB_I_BASE, ADC_PHB_I_SOCNUM, ADC_TRIGGER_EPWM1_SOCA, ADC_PHB_I_CHANNEL, ADC_SAMPLE_WINDOW);
    ADC_setInterruptSOCTrigger(ADC_PHB_I_BASE, ADC_PHB_I_SOCNUM, ADC_INT_SOC_TRIGGER_NONE);

    // Setup ADCs for PhC - this is triggered based off of counter EPWM1 SOCA (setup below)
    // Sample window must be > 75ns per datasheet, > 90ns for AGPIO per datasheet (at least > 11 SYSCLK cycles to be safe - 91.3ns)
    // Sample window must also be as long as 1 ADC clock cycle (40ns) - input sample window in SysClk cycles (11 cycle = 91.3ns)
    ADC_setupSOC(ADC_PHC_I_BASE, ADC_PHC_I_SOCNUM, ADC_TRIGGER_EPWM1_SOCA, ADC_PHC_I_CHANNEL, ADC_SAMPLE_WINDOW);
    ADC_setInterruptSOCTrigger(ADC_PHC_I_BASE, ADC_PHC_I_SOCNUM, ADC_INT_SOC_TRIGGER_NONE);

    // If we need any more PBB's we can copy them from above
    // Setup DCLink sampling -> This will trigger interrupt
    ADC_setupSOC(ADC_DCBUS_BASE, ADC_DCBUS_SOCNUM, ADC_TRIGGER_EPWM1_SOCA, ADC_DCBUS_CHANNEL, ADC_SAMPLE_WINDOW);
    ADC_setInterruptSOCTrigger(ADC_DCBUS_BASE, ADC_DCBUS_SOCNUM, ADC_INT_SOC_TRIGGER_NONE);
}

void Init_ADC()
{
    // asysctl initialization
    // Disables the temperature sensor output to the ADC.
    ASysCtl_disableTemperatureSensor();
    // Set the analog voltage reference selection to internal.
    ASysCtl_setAnalogReferenceInternal( ASYSCTL_VREFHI );
    // Set the internal analog voltage reference selection to 1.65V.
    ASysCtl_setAnalogReference1P65( ASYSCTL_VREFHI );

    // Setup ADC
    // Three ADC modules in this device
    // initialize the ADC handles
    adcHandle[0] = ADCA_BASE;
    adcHandle[1] = ADCB_BASE;
    adcHandle[2] = ADCC_BASE;

    // initialize the ADC results
    adcResult[0] = ADCARESULT_BASE;
    adcResult[1] = ADCBRESULT_BASE;
    adcResult[2] = ADCCRESULT_BASE;

    // Set ADC voltage ref
    ADC_setVREF(adcHandle[0], ADC_REFERENCE_EXTERNAL, ADC_REFERENCE_3_3V);
    ADC_setVREF(adcHandle[1], ADC_REFERENCE_EXTERNAL, ADC_REFERENCE_3_3V);
    ADC_setVREF(adcHandle[2], ADC_REFERENCE_EXTERNAL, ADC_REFERENCE_3_3V);

    // Set main clock scaling factor (60MHz max clock for the ADC module)
    // ADC clock set up for 30 MHz
    ADC_setPrescaler(adcHandle[0], ADC_CLK_DIV_2_0);
    ADC_setPrescaler(adcHandle[1], ADC_CLK_DIV_2_0);
    ADC_setPrescaler(adcHandle[2], ADC_CLK_DIV_2_0);

    // Set the ADC interrupt pulse generation to end of conversion
    ADC_setInterruptPulseMode(adcHandle[0], ADC_PULSE_END_OF_CONV);
    ADC_setInterruptPulseMode(adcHandle[1], ADC_PULSE_END_OF_CONV);
    ADC_setInterruptPulseMode(adcHandle[2], ADC_PULSE_END_OF_CONV);

    // Disables SOC burst mode.
    ADC_disableBurstMode(adcHandle[0]);
    ADC_disableBurstMode(adcHandle[1]);
    ADC_disableBurstMode(adcHandle[2]);

    // Set priority of SOCs
    ADC_setSOCPriority(adcHandle[0], ADC_PRI_ALL_HIPRI);
    ADC_setSOCPriority(adcHandle[1], ADC_PRI_ALL_HIPRI);
    ADC_setSOCPriority(adcHandle[2], ADC_PRI_ALL_HIPRI);

    // Enable ADCs
    ADC_enableConverter(adcHandle[0]);
    ADC_enableConverter(adcHandle[1]);
    ADC_enableConverter(adcHandle[2]);

    // Delay to allow ADCs to power up
    SysCtl_delay(5000U);

    // Setup ADCs for fast samples - A, B, C, DC Link
    Setup_FastADC();

    // Init motor PWM to trigger ADC
    MotorPWM_Init();

    // Enable ADC interrupt - Enables ADC interrupts in group 1 - priority 12
    // Kicks off the fast thread
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
    Interrupt_register(INT_ADCC1, &adcC1ISR); // register ISR
}

void Enable_ADCInt()
{
    // Enable fast thread interrupt
    Interrupt_enable(INT_ADCC1);

    // Enable user mux interrupts
    Interrupt_enable(INT_ADCA2);
    Interrupt_enable(INT_ADCB2);
    Interrupt_enable(INT_ADCC2);
    Interrupt_register(INT_ADCA2, &adcA2ISR); // register ISR
    Interrupt_register(INT_ADCB2, &adcB2ISR); // register ISR
    Interrupt_register(INT_ADCC2, &adcC2ISR); // register ISR

    // Enable fast thread ADC interrupt
    ADC_enableInterrupt(ADCC_BASE, ADC_INT_NUMBER1);
    ADC_clearInterruptStatus(ADCC_BASE, ADC_INT_NUMBER1);
    ADC_disableContinuousMode(ADCC_BASE, ADC_INT_NUMBER1);
}

Thanks for the help! 

  • Hello Derek,

    Are both projects running on the same hardware? Hardware factors that could influence conversion accuracy include the input network design and the VREFHI reference circuit design. The reference design could be especially important if you're performing synchronous conversions on the three ADCs - you should have a high-bandwidth op amp buffer driving the pin behind the reference voltage source (refer to the TRM ADC chapter under "More Information" for examples on this).

    Best regards,
    Ibukun

  • Hi Ibunkun, 

    They are actually running on 2 different hardware. I will take a look at the reference voltage to see if maybe it is wrong on this hardware. 

  • Hi Derek,

    Yes, good idea to also check the accuracy of the reference source.

    Best regards,

    Ibukun

  • Hi Ibukun, 

    Sorry, I was working on my other project using the same MCU and now am back to this one! 

    After talking to my hardware design team there are 2 versions of the circuit card with 2 different 3V reference voltages. Both reference voltages measure 3V but the rise time and slope of them are different. I cannot find a rise time requirement for VREFH in the ADC documentation. What is the requirement here? 

    Working circuit card: The slope from 0V to 3V is constant (Note my probe has a 1V offset – meter measures 3V on IP3REF).

    Circuit card that has an offset: 

  • Actually - I think the circuit card difference is a red herring. I think earlier when I thought the other hardware fixed the issue I might have had software loaded that added a -6V bias and sent me down a rabbit hole. On my other card my ADCs also are off by a little. I looks like my VREHF is 3 and not 3.3V - could this be part of the issue? 

    I found someone who had a similar issue: https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/1135572/tms320f280021-q1-c2000-adc-wrong-reading-values/4215440?tisearch=e2e-sitesearch&keymatch=adc%25252520not%25252520reading%25252520correct#4215440

    Their issue was they were not allowing enough time for the sampling capacitor in the ADC to charge. I think this is the ADC_SAMPLE_WINDOW I put into this function, is that correct? This for me is set to 11 - which should be 91.67ns. The demo project uses 14. I have tried, 14, 20, 50 put into this function and none of the values changed the conversion value. 

        ADC_setupSOC(ADC_PHA_I_BASE, ADC_PHA_I_SOCNUM, ADC_TRIGGER_EPWM1_SOCA, ADC_PHA_I_CHANNEL, ADC_SAMPLE_WINDOW);
    

    This is my init code - do you see anything missing if my VREFH is 3V?

    void Setup_FastADC()
    {
        // Select interrupt source - this will be the last sample converted and therefore trigger the interrupt 1
        ADC_setInterruptSource(ADCC_BASE, ADC_INT_NUMBER1, ADC_DCBUS_SOCNUM);
    
        // Setup ADCs for PhA - this is triggered based off of counter EPWM1 SOCA (setup below)
        // Sample window must be > 75ns per datasheet, > 90ns for AGPIO per datasheet (at least > 11 SYSCLK cycles to be safe - 91.3ns)
        // Sample window must also be as long as 1 ADC clock cycle (40ns) - input sample window in SysClk cycles (11 cycle = 91.3ns)
        ADC_setupSOC(ADC_PHA_I_BASE, ADC_PHA_I_SOCNUM, ADC_TRIGGER_EPWM1_SOCA, ADC_PHA_I_CHANNEL, ADC_SAMPLE_WINDOW);
        ADC_setInterruptSOCTrigger(ADC_PHA_I_BASE, ADC_PHA_I_SOCNUM, ADC_INT_SOC_TRIGGER_NONE);
    
        // Setup ADCs for PhB - this is triggered based off of counter EPWM1 SOCA (setup below)
        // Sample window must be > 75ns per datasheet, > 90ns for AGPIO per datasheet (at least > 11 SYSCLK cycles to be safe - 91.3ns)
        // Sample window must also be as long as 1 ADC clock cycle (40ns) - input sample window in SysClk cycles (11 cycle = 91.3ns)
        ADC_setupSOC(ADC_PHB_I_BASE, ADC_PHB_I_SOCNUM, ADC_TRIGGER_EPWM1_SOCA, ADC_PHB_I_CHANNEL, ADC_SAMPLE_WINDOW);
        ADC_setInterruptSOCTrigger(ADC_PHB_I_BASE, ADC_PHB_I_SOCNUM, ADC_INT_SOC_TRIGGER_NONE);
    
        // Setup ADCs for PhC - this is triggered based off of counter EPWM1 SOCA (setup below)
        // Sample window must be > 75ns per datasheet, > 90ns for AGPIO per datasheet (at least > 11 SYSCLK cycles to be safe - 91.3ns)
        // Sample window must also be as long as 1 ADC clock cycle (40ns) - input sample window in SysClk cycles (11 cycle = 91.3ns)
        ADC_setupSOC(ADC_PHC_I_BASE, ADC_PHC_I_SOCNUM, ADC_TRIGGER_EPWM1_SOCA, ADC_PHC_I_CHANNEL, ADC_SAMPLE_WINDOW);
        ADC_setInterruptSOCTrigger(ADC_PHC_I_BASE, ADC_PHC_I_SOCNUM, ADC_INT_SOC_TRIGGER_NONE);
    
        // If we need any more PBB's we can copy them from above
        // Setup DCLink sampling -> This will trigger interrupt
        ADC_setupSOC(ADC_DCBUS_BASE, ADC_DCBUS_SOCNUM, ADC_TRIGGER_EPWM1_SOCA, ADC_DCBUS_CHANNEL, ADC_SAMPLE_WINDOW);
        ADC_setInterruptSOCTrigger(ADC_DCBUS_BASE, ADC_DCBUS_SOCNUM, ADC_INT_SOC_TRIGGER_NONE);
    
    }
    
    void Init_ADC()
    {
        // asysctl initialization
        // Disables the temperature sensor output to the ADC.
        ASysCtl_disableTemperatureSensor();
        // Set the analog voltage reference selection to internal.
        ASysCtl_setAnalogReferenceInternal( ASYSCTL_VREFHI );
        // Set the internal analog voltage reference selection to 1.65V.
        ASysCtl_setAnalogReference1P65( ASYSCTL_VREFHI );
    
        // Setup ADC
        // Three ADC modules in this device
        // initialize the ADC handles
        adcHandle[0] = ADCA_BASE;
        adcHandle[1] = ADCB_BASE;
        adcHandle[2] = ADCC_BASE;
    
        // initialize the ADC results
        adcResult[0] = ADCARESULT_BASE;
        adcResult[1] = ADCBRESULT_BASE;
        adcResult[2] = ADCCRESULT_BASE;
    
        // Set ADC voltage ref
        ADC_setVREF(adcHandle[0], ADC_REFERENCE_EXTERNAL, ADC_REFERENCE_3_3V);
        ADC_setVREF(adcHandle[1], ADC_REFERENCE_EXTERNAL, ADC_REFERENCE_3_3V);
        ADC_setVREF(adcHandle[2], ADC_REFERENCE_EXTERNAL, ADC_REFERENCE_3_3V);
    
        // Set main clock scaling factor (60MHz max clock for the ADC module)
        // ADC clock set up for 30 MHz
        ADC_setPrescaler(adcHandle[0], ADC_CLK_DIV_2_0);
        ADC_setPrescaler(adcHandle[1], ADC_CLK_DIV_2_0);
        ADC_setPrescaler(adcHandle[2], ADC_CLK_DIV_2_0);
    
        // Set the ADC interrupt pulse generation to end of conversion
        ADC_setInterruptPulseMode(adcHandle[0], ADC_PULSE_END_OF_CONV);
        ADC_setInterruptPulseMode(adcHandle[1], ADC_PULSE_END_OF_CONV);
        ADC_setInterruptPulseMode(adcHandle[2], ADC_PULSE_END_OF_CONV);
    
        // Disables SOC burst mode.
        ADC_disableBurstMode(adcHandle[0]);
        ADC_disableBurstMode(adcHandle[1]);
        ADC_disableBurstMode(adcHandle[2]);
    
        // Set priority of SOCs
        ADC_setSOCPriority(adcHandle[0], ADC_PRI_ALL_HIPRI);
        ADC_setSOCPriority(adcHandle[1], ADC_PRI_ALL_HIPRI);
        ADC_setSOCPriority(adcHandle[2], ADC_PRI_ALL_HIPRI);
    
        // Enable ADCs
        ADC_enableConverter(adcHandle[0]);
        ADC_enableConverter(adcHandle[1]);
        ADC_enableConverter(adcHandle[2]);
    
        // Delay to allow ADCs to power up
        SysCtl_delay(5000U);
    
        // Setup ADCs for fast samples - A, B, C, DC Link
        Setup_FastADC();
    
        // Init motor PWM to trigger ADC
        MotorPWM_Init();
    
        // Enable ADC interrupt - Enables ADC interrupts in group 1 - priority 12
        // Kicks off the fast thread
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
        Interrupt_register(INT_ADCC1, &adcC1ISR); // register ISR
    }
    

    Thanks for the help! 

  • It looks like there are only options for VREFH to be 2.5V or 3.3V - how do I set it to be 3V? 

        ADC_setVREF(adcHandle[0], ADC_REFERENCE_EXTERNAL, ADC_REFERENCE_3_3V);
    

    typedef enum
    {
        ADC_REFERENCE_3_3V,
        ADC_REFERENCE_2_5V
    } ADC_ReferenceVoltage;

  • I think I have this problem: https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/1135572/tms320f280021-q1-c2000-adc-wrong-reading-values/4215440?tisearch=e2e-sitesearch&keymatch=adc%2520not%2520reading%2520correct%2520value#4215440

    I set ADC_SAMPLE_WINDOW to the max value 511 here: 

    ADC_setupSOC(ADC_PHA_I_BASE, ADC_PHA_I_SOCNUM, ADC_TRIGGER_EPWM1_SOCA, ADC_PHA_I_CHANNEL, ADC_SAMPLE_WINDOW);

    Now my ADC reading is correct. I need to check with my hardware team looking at my circuit what my S+H time from section 16.13.2 (choosing an acquisition value) of the TRM is. 

  • Derek, this option applies if you are using the internal reference, in which case you would pass one of the two options as the third argument to ADC_setVREF(). In external reference mode, ADC_ReferenceVoltage has no effect.

  • Derek,

    Yes, I recommend looking at the following sections of the TRM under the ADC chapter:

    • SOC Principle of Operation > ADC acquisition (sample and hold) window
    • Additional Information > Choosing an Acquisition Window Duration

    ADC_SAMPLE_WINDOW is the number of SYSCLK cycles the ADC will spend charging the sample-and-hold capacitor before opening the sample switch and beginning the conversion. If your calculated minimum ACQPS is an issue, you may be able to improve by using a fast op amp buffer (this also helps to minimize memory crosstalk between conversions).

    Related application note: ADC Input Circuit Evaluation for C2000 MCUs

  • Hi Ibukun, 

    The issue is not S+H time. We have a noise issue. S+H time is fine. I am getting a lot of IGBT firing noise on VREFH. 

    I noticed something in my trend. Until 460V, ADCs are always perfect. My converter stays in state 1 until 460V. It then enters state 2 and enables the gate drivers. With this MCU I was driving PWMs to the IGBTs all the time (software bug), not just when the PWM was enabled. This caused IGBTs to start firing at 460V and I see 8kHz noise on the VREFH line.

    I can actually toggle my gate drivers on and off with pwm being driven and fix / cause issues on my ADCs: 

  • Derek,

    Glad to hear you were able to resolve the issue. Sounds like it would be a good idea to isolate/shield the VREFHI line and ensure you have sufficient decoupling capacitance as close to the VREFHI pin as possible.

    Best regards,
    Ibukun