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.

TMS320F28069: Analog-digital conversion doesn't work

Part Number: TMS320F28069

Hi everyone!

I'm a newbie to DSP-programming who has been dealing with a problem for a couple of time. So I hope some of you can help me.

My goal is to use the built-in ADC for converting various signals (only one at the same time) - for testing purposes I chose a sine, which comes from a waveform generator. I already checked the signal, after passing the amplifier and such stuff, via oscilloscope and the sine reaches the corresponding pin unbiased, so I think I can exclude problems in the circuit.


Due to I'm quite new to this matter, I followed one of TI's code examples (see code below). The positive thing is, that the AD-conversion "works", so I managed to get the ADC started. The issue is, that the conversion doesn't look very similar to a sine (see picture).

I tried to use the ePWM (as suggested in the example) and the CPU timer as trigger, but both basically lead to the same result. In the further text I'm referring to the version with the CPU timer, because it seems kind of more "intuitive" to me. The input signal is a 60kHz sine, so a 2µs timer for the sampling-rate should be enough. I guessed that using a single SOC might cause performance problems (which maybe would lead to the unpretty result), so I tried to use 4 of them (as mentioned in the handbook), but there was no improvement.

I have no idea, why the output looks that wrong, maybe some of you can help me?

Thank you!

PS: The code:

#include "DSP28x_Project.h" // Device Headerfile and Examples Include File

// Prototype statements for functions found within this file.

__interrupt void adc_isr(void);

void Adc_Config(void);

 

// Global variables used in this example:

Uint16 LoopCount;

Uint16 ConversionCount;

#define Anz  100

Uint16 Voltage2[Anz];

 

main()

{

// Step 1. Initialize System Control:

// PLL, WatchDog, enable Peripheral Clocks

// This example function is found in the F2806x_SysCtrl.c file.

InitSysCtrl();

 

// Step 2. Initialize GPIO:

// This example function is found in the F2806x_Gpio.c file and

// illustrates how to set the GPIO to it's default state.

// InitGpio(); // Skipped for this example

// Step 3. Clear all interrupts and initialize PIE vector table:

// Disable CPU interrupts

DINT;

// Initialize the PIE control registers to their default state.

// The default state is all PIE interrupts disabled and flags are cleared.

// This function is found in the F2806x_PieCtrl.c file.

InitPieCtrl();

// Disable CPU interrupts and clear all CPU interrupt flags:

IER = 0x0000;

IFR = 0x0000;

// Initialize the PIE vector table with pointers to the shell Interrupt

// Service Routines (ISR).

// This will populate the entire table, even if the interrupt is not used in this example. This is useful for debug purposes.

// The shell ISR routines are found in F2806x_DefaultIsr.c.

// This function is found in F2806x_PieVect.c.

InitPieVectTable();

// Interrupts that are used in this example are re-mapped to

// ISR functions found within this file.

EALLOW; // This is needed to write to EALLOW protected register

PieVectTable.ADCINT1 = &adc_isr;

EDIS; // This is needed to disable write to EALLOW protected registers

//memcpy( &RamfuncsRunStart, &RamfuncsLoadStart, &RamfuncsLoadEnd - &RamfuncsLoadStart);

// Step 4. Initialize all the Device Peripherals:

// This function is found in F2806x_InitPeripherals.c

// InitPeripherals(); // Not required for this example

InitAdc(); // For this example, init the ADC

 

// Step 5. User specific code, enable interrupts:

// Enable ADCINT1 in PIE

PieCtrlRegs.PIEIER1.bit.INTx1 = 1; // Enable INT 1.1 in the PIE

IER |= M_INT1; // Enable CPU Interrupt 1

EINT; // Enable Global interrupt INTM

ERTM; // Enable Global realtime interrupt DBGM

LoopCount = 0;

ConversionCount = 0;

// Configure ADC

EALLOW;

AdcRegs.ADCSAMPLEMODE.all = 0;

AdcRegs.ADCCTL2.bit.ADCNONOVERLAP = 1; // Enable non-overlap mode

AdcRegs.ADCCTL1.bit.INTPULSEPOS = 1; // ADCINT1 trips after AdcResults latch

AdcRegs.INTSEL1N2.bit.INT1E = 1; // Enabled ADCINT1

AdcRegs.INTSEL1N2.bit.INT1CONT = 0; // Enable ADCINT1 Continuous mode

AdcRegs.INTSEL1N2.bit.INT1SEL = 3; // setup EOC1 to trigger ADCINT1 to fire

AdcRegs.ADCSOC0CTL.bit.CHSEL = 1; // set SOC0 channel select to ADCINA1

AdcRegs.ADCSOC1CTL.bit.CHSEL = 1; // set SOC1 channel select to ADCINA1

AdcRegs.ADCSOC2CTL.bit.CHSEL = 1; // set SOC0 channel select to ADCINA0

AdcRegs.ADCSOC3CTL.bit.CHSEL = 1; // set SOC1 channel select to ADCINA1

AdcRegs.ADCSOC0CTL.bit.TRIGSEL = 1; // set SOC0 start trigger on EPWM1A, due to round-robin SOC0 converts first then SOC1

AdcRegs.ADCSOC1CTL.bit.TRIGSEL = 1; // set SOC1 start trigger on EPWM1A, due to round-robin SOC0 converts first then SOC1

AdcRegs.ADCSOC2CTL.bit.TRIGSEL = 1; // set SOC2 start trigger on EPWM1A, due to round-robin SOC0 converts first then SOC1

AdcRegs.ADCSOC3CTL.bit.TRIGSEL = 1; // set SOC3 start trigger on EPWM1A, due to round-robin SOC0 converts first then SOC1

AdcRegs.ADCSOC0CTL.bit.ACQPS = 6; // set SOC0 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

AdcRegs.ADCSOC1CTL.bit.ACQPS = 6; // set SOC1 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

AdcRegs.ADCSOC2CTL.bit.ACQPS = 6; // set SOC0 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

AdcRegs.ADCSOC3CTL.bit.ACQPS = 6; // set SOC1 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)

//AdcRegs.SOCPRICTL.bit.ONESHOT = 1;

AdcRegs.SOCPRICTL.bit.SOCPRIORITY = 0;

EDIS;

//ePWM trigger - not used

/*

// Assumes ePWM1 clock is already enabled in InitSysCtrl();

EPwm1Regs.ETSEL.bit.SOCAEN = 1; // Enable SOC on A group

EPwm1Regs.ETSEL.bit.SOCASEL = 4; // Select SOC from CMPA on upcount

EPwm1Regs.ETPS.bit.SOCAPRD = 1; // Generate pulse on 1st event

EPwm1Regs.CMPA.half.CMPA = 0x0010; // Set compare A value 0x0080

EPwm1Regs.TBPRD = 0x1f; // Set period for ePWM1

EPwm1Regs.TBCTL.bit.CTRMODE = 0; // count up and start

*/

InitCpuTimers(); // For this example, only initialize the Cpu Timers

// Configure CPU-Timer 0, 1, and 2 to interrupt every second:

// 80MHz CPU Freq, 1 second Period (in uSeconds)

ConfigCpuTimer(&CpuTimer0, 90, 2);

CpuTimer0Regs.TCR.all = 0x4000; // Use write-only instruction to set TSS bit = 0

// Wait for ADC interrupt

for(;;)

{

LoopCount++;

}

}

__interrupt void adc_isr(void)

{

Voltage2[ConversionCount] = AdcResult.ADCRESULT0;

ConversionCount++;

Voltage2[ConversionCount] = AdcResult.ADCRESULT1;

ConversionCount++;

Voltage2[ConversionCount] = AdcResult.ADCRESULT2;

ConversionCount++;

Voltage2[ConversionCount] = AdcResult.ADCRESULT3;

// If Anz conversions have been logged, start over

if(ConversionCount >= Anz)

{

ConversionCount = 0;

}

else ConversionCount++;

AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; //Clear ADCINT1 flag reinitialize for next SOC

PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge interrupt to PIE

return;

}

PPS: The output signal

  • Thanks for reaching out to the E2E forum.  While checking the output of the waveform generator at the pin is a good first step, there still may be issues with the circuit being able to properly drive the sample and hold capacitor inside the MCU when the ADC samples.  Even leaving the scope probe on the signal when sampling can change the capacitance profile in such a way to corrupt the signal.

    The ADC internal network is shown below; between the op-amp/ RC network/ sample and hold time, we need to ensure that the sampling cap can charge to 13-bit accuracy to avoid degrading the signal.  An easy thing to try would be to increase the ACQPS value to see if you can note a difference(the sample rate will reduce so be aware of that).

    Best,

    Matthew

  • Hi Matthew!

    Thank you very much for your quick respond!

    Meanwhile I found out, that the for-loop, which waits for the ADC interrupt is accessed only rarely (so the loop count doesn't increase significantly). So it seems like the CPU is busy with the interrupts. Due to he is busy I suppose, that he is even overextended, which might cause the very bad signal.

    For that purpose I increased the number of SOCs (to the maximum of 16). That lead me to a significant improovement of the output (although it still isn't great). 

    After that I tried to realize your hint. I changed ACQPS from 0x06 (image above) to 0x0F (image below), but the result only looks slightly better.

    Increasing ACQPS to higher values doesn't change the result significantly (it even makes it worse, compared with 0x0F).

    Another observation: Changing the CPU timer interval from 2 -> 3 makes the output more "periodic" (no errors like at the beginning in the images above), but also more "polygonal" (as expected, when increasing the interval).

    Maybe it is still a performance problem (?).

    So from x-coordinate 10 to 60 it looks pretty good, but for the rest I'd like to improove the result further. Furthermore the image above was taken at 60kHz, for my final application I need to convert clearly higher frequencys, where the conversion still is quite bad.

    Do you have any hints, how I could manage this?

    Thank you very much!

  • I believe you are on the right track wrt the source of the problem, that is we are missing ADC ISRs, or not getting the data from the ADC fast enough before it over-writes itself. Looking back at your original waveforms this makes more sense now as we are seeing current values mixed with new values, etc.

    You have already done the 1st step, that is to use the full ADC results so that we store 16 values before asking the MCU to read them. In this way the ISR triggers don't happen so often(recall that when ISR is received we have about 8 cycle CPU hit since this is a program discontinuity) Another trick is to break up the 16 into 8 result chunks, i.e. assign 1 ISR to the first 8 conversions and a different ADC ISR(there are 9 available) to the 2nd 8 conversions. This way, when the ISR comes you don't have to be concerned with over-writing the 1st couple of conversion before the MCU can read/store them.

    Another option is to use the CLA to capture the ADC results, so that the CPU resource/latency is not a concern for capturing the ADC results. Not sure of your exact PN, if the CLA is present or not. The CLA is a task driven architecture, always waiting for its trigger sources, vs executing code free running.

    You can safely go back to your org ACQPS settings; as per above I think the problem is in the digital domain.

    Let me know if you have more questions.

    Best,
    Matthew
  • Hi!

    Using two ISR's instead of one helped a lot - the time between starting and ending of one ISR reduced by a factor of 8 - keeping in concern, that we now use two of them still gives an improvement by a factor of 4. So I can lower the CPU - trigger - period, which improves the result significantly. Thanks!
    I already considered using the CLA. I think this might solve the present problem, but in the end the goal is to convert a high-frequent signal over a few milliseconds, so there will be a load of samples. I doubt, that the CLA has enough memory for that.

    Meanwhile I figured out, that having the program on the flash might be the reason for the performance problems. Using the memcopy - function rerecords the ISR's on the RAM (as far as I understood). This resulted in a speed-up by a factor of 10, which should suffice for the current requirements.

    Thanks for your help!