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.

CCS/TMS320F28335: Issues with CMPA register contiuous update via ADC_isr and EPWM_isr

Part Number: TMS320F28335

Tool/software: Code Composer Studio

Hello,

I tried working on the adc_soc example. I plan to generate adc_isr and epwm_isr such that the adc_isr will give me an average of 10 successive reading and the epwm_isr will update its CMPA registar with this average value. The code looks a little okay however its performance is pretty awful. I am always getting 100% duty cycle. It is as if the cmpa is not really being updated. I have gone over the code over and over, I simply don't know where I have gone wrong... but I have a feeling the issue will be something very little.

By the way, I read somewhere that adc_isr has a higher priority than epwm_isr, but since the adc is meant to be triggered by the epwm SOCA, does that mean in the very first conversion, the output of the adc will be zero as it has not yet been legitimately triggered so there will be no conversion?

I'd appreciate any advice and pointers these 2 issues.

Thanks.

David

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

// Prototype statements for functions found within this file.
__interrupt void adc_isr(void);
__interrupt void epwm4_isr(void);
void Gpio_select(void);
//void Setup_ePWM1A(void);
//void Setup_ePWM2A(void);
//void Setup_ePWM3A(void);
void Setup_ePWM4A(void);
// Global variables used in this example:
Uint16 i;
Uint16 LoopCount;
Uint16 Voltage1[10];
float VoltageAve;

main()
{
// Step 1. Initialize System Control:
// PLL, WatchDog, enable Peripheral Clocks
// This example function is found in the DSP2833x_SysCtrl.c file.
   InitSysCtrl();

   EALLOW;
   #if (CPU_FRQ_150MHZ)     // Default - 150 MHz SYSCLKOUT
     #define ADC_MODCLK 0x3 // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*3)   = 25.0 MHz
   #endif
   #if (CPU_FRQ_100MHZ)
     #define ADC_MODCLK 0x2 // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 100/(2*2)   = 25.0 MHz
   #endif
   EDIS;

   // Define ADCCLK clock frequency ( less than or equal to 25 MHz )
   // Assuming InitSysCtrl() has set SYSCLKOUT to 150 MHz
   EALLOW;
   SysCtrlRegs.HISPCP.all = ADC_MODCLK;
   EDIS;

// Step 2. Initialize GPIO:
// This example function is found in the DSP2833x_Gpio.c file and
// illustrates how to set the GPIO to it's default state.
// InitGpio();  // Skipped for this example
   Gpio_select();

// 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 DSP2833x_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 DSP2833x_DefaultIsr.c.
// This function is found in DSP2833x_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.ADCINT = &adc_isr;
   PieVectTable.EPWM4_INT = &epwm4_isr;
   EDIS;    // This is needed to disable write to EALLOW protected registers

// Step 4. Initialize all the Device Peripherals:
// This function is found in DSP2833x_InitPeripherals.c
// InitPeripherals(); // Not required for this example
   InitAdc();  // For this example, init the ADC

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

// Enable ADCINT in PIE
   PieCtrlRegs.PIEIER1.bit.INTx6 = 1;
   PieCtrlRegs.PIEIER3.bit.INTx4 = 1;
   IER |= 5;		// Enable CPU Interrupt 1 and 3
   EINT;          	// Enable Global interrupt INTM
   ERTM;          	// Enable Global realtime interrupt DBGM

   LoopCount = 0;
   //ConversionCount = 0;

// Configure ADC
   AdcRegs.ADCMAXCONV.all = 0;       		// Setup 1 conv's on SEQ1 ie number of conversion - 1
   AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x4; 	// Setup ADCINA4 as 1st SEQ1 conv.
   AdcRegs.ADCTRL1.bit.ACQ_PS = 15; 		//time between multiplexer switch and freezing of samples
   AdcRegs.ADCTRL1.bit.CONT_RUN = 1;		//Start all over again at the end of the 1st conversion w/o waiting for another trigger input signal
   AdcRegs.ADCTRL1.bit.SEQ_CASC = 1;		//Cascaded mode
   AdcRegs.ADCTRL1.bit.CPS = 0;				//ADCCLK = FCLK/CPS+1 = FCLK = 12.5MHz
   AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 = 1;	// Enable SOCA from ePWM to start SEQ1
   AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1;  	// Enable SEQ1 interrupt
   AdcRegs.ADCTRL2.bit.INT_MOD_SEQ1 = 0; 	// Enable SEQ1 interrupt every EOS
   AdcRegs.ADCTRL3.bit.SMODE_SEL = 0;		//SEQUENTIAL SAMPLING
   AdcRegs.ADCTRL3.bit.ADCCLKPS = 1;		//FCLK = HSPCLK/2*ADCCLKPS = FCLK/2 = 12.5MHz

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

// Wait for ADC interrupt
   for(;;)
   {
	  LoopCount++;
   }
}

void Gpio_select(void)
{
	EALLOW;
	GpioCtrlRegs.GPAMUX1.all = 0;		// GPIO15 ... GPIO0 = General Puropse I/O
	//GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1;
	//GpioCtrlRegs.GPAMUX1.bit.GPIO2 = 1;
	//GpioCtrlRegs.GPAMUX1.bit.GPIO4 = 1;
	GpioCtrlRegs.GPAMUX1.bit.GPIO6 = 1;
	GpioCtrlRegs.GPAMUX2.all = 0;		// GPIO31 ... GPIO16 = General Purpose I/O
	GpioCtrlRegs.GPBMUX1.all = 0;		// GPIO47 ... GPIO32 = General Purpose I/O
	GpioCtrlRegs.GPBMUX2.all = 0;		// GPIO63 ... GPIO48 = General Purpose I/O
	GpioCtrlRegs.GPCMUX1.all = 0;		// GPIO79 ... GPIO64 = General Purpose I/O
	GpioCtrlRegs.GPCMUX2.all = 0;		// GPIO87 ... GPIO80 = General Purpose I/O

	GpioCtrlRegs.GPADIR.all = 0;
	//GpioCtrlRegs.GPADIR.bit.GPIO0  = 1;	// peripheral explorer: LED LD1 at GPIO0
	//GpioCtrlRegs.GPADIR.bit.GPIO2  = 1;	// peripheral explorer: LED LD1 at GPIO2
	//GpioCtrlRegs.GPADIR.bit.GPIO4  = 1;	// peripheral explorer: LED LD1 at GPIO4
	GpioCtrlRegs.GPADIR.bit.GPIO6  = 1;	// peripheral explorer: LED LD1 at GPIO6

	GpioCtrlRegs.GPBDIR.all = 0;		// GPIO63-32 as inputs
	GpioCtrlRegs.GPCDIR.all = 0;		// GPIO87-64 as inputs
	EDIS;
}

__interrupt void  adc_isr(void)
{

  for (i=0;i<10;i++)
  {
	Voltage1[i] = AdcMirror.ADCRESULT0;
	VoltageAve += Voltage1[i];
  }
  VoltageAve = (3*VoltageAve)/10/4096;
   // Reinitialize for next ADC sequence
  AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1;         // Reset SEQ1
  AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;       // Clear INT SEQ1 bit
  PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;   // Acknowledge interrupt to PIE
  return;
}
__interrupt void  epwm4_isr(void)
{
  EPwm4Regs.CMPA.half.CMPA = VoltageAve*(EPwm4Regs.TBPRD);
  // Reinitialize for next ADC sequence
  EPwm4Regs.ETCLR.bit.INT = 1;       // Clear INT SEQ1 bit
  PieCtrlRegs.PIEACK.all = PIEACK_GROUP3;   // Acknowledge interrupt to PIE
  return;
}
void Setup_ePWM4A(void)
{
	EPwm4Regs.TBCTL.bit.CLKDIV = 0;			//SysClkOut/1 ie default
	EPwm4Regs.TBCTL.bit.HSPCLKDIV = 1;		//SysClkOut/2 ie default
	EPwm4Regs.TBCTL.bit.CTRMODE = 0;		//UP COUNT
	EPwm4Regs.TBCTL.bit.PRDLD = 0;			//TBPRD period shadow, load on ctr = 0
	EPwm4Regs.TBCTL.bit.SYNCOSEL = 3;		//disable sync output select
	EPwm4Regs.TBPRD = 4989/22;				//330kHz
	EPwm4Regs.CMPA.half.CMPA = 0;			//initial CMPA = 0
	EPwm4Regs.AQCTLA.all = 0x0012;			//ctr = 0 (set), ctr = cmpa on up count (clear)
	EPwm4Regs.ETSEL.bit.SOCAEN = 1;        // Enable SOC on A group
	EPwm4Regs.ETSEL.bit.SOCASEL = 1;       // Select SOC from CTR=0
	EPwm4Regs.ETPS.bit.SOCAPRD = 1;        // Generate pulse on 1st event
	EPwm4Regs.ETSEL.bit.INTEN = 1;        // enable pwm interrupt
	EPwm4Regs.ETSEL.bit.INTSEL = 2;        // enable interrupt on ctr = PRD
	EPwm4Regs.ETPS.bit.INTPRD = 1;        // interrupt on 1st event
}

  • Hello,
    I am writing to let you know that a C2000 team member has been assigned to this post and should be answering shortly.

    Regards
    Baskaran
  • Hi David,

    Regarding your CMPA update question, have you checked whether Voltage1 and VoltageAve variables are being updated as expected? If not, you could consider adding the volatile keyword for these variables.
    Can you confirm that both the ADC and EPWM ISRs are generated by placing breakpoints inside the ISRs and seeing whether they are reached?

    Regards,
    Elizabeth

  • Thanks for your response.

    Both isr's are generated. adc_isr first before and after 10 loops, it goes over to epwm_isr.

    Also, both variables are being updated.

    I look forward to your response.

    Kind Regards.

    David.
  • Hello David,
    -----------------------------------------------
    __interrupt void adc_isr(void)
    {

    for (i=0;i<10;i++)
    {
    Voltage1[i] = AdcMirror.ADCRESULT0;
    VoltageAve += Voltage1[i];
    }
    VoltageAve = (3*VoltageAve)/10/4096;

    I don't think AdcMirror.ADCRESULT0 is changing over your summing interval, so it appears to be superfluous by adding 10 times and dividing by 10.
    Looks like VoltageAve has a leftover value when it starts with at the beginning of each interrupt
  • Thank you very much.

    1. Leftover issue: I think I can solve that by just placing 

    VoltageAve = 0;

    before the for loop. I guess I just thought it will be overwritten by 

    Voltage1[i] = AdcMirror.ADCRESULT0;

    maybe I was wrong.

    2. About superfluous addition, my plan is to take 10 successive samples and average them instead of just accepting a single sample without query. Maybe my approach is wrong as I am not really an experienced programmer.

    Can you advise me on a better approach, pls?

    Regards.

    David.

  • Hi David,

    I focus on FPGA's and not C programming but since you asked:

    My interpretation is that AdcMirror.ADCRESULT0 is not changing over your summing period.

    Do you agree?  My interpretation could be wrong.

    Usually I do not respond to questions that result in more work for me, but that is not always the case.

    This is from the Example_2833xAdcSoc.c file

    __interrupt void adc_isr(void)
    {
    Voltage1[ConversionCount] = AdcRegs.ADCRESULT0 >>4;
    Voltage2[ConversionCount] = AdcRegs.ADCRESULT1 >>4;

    // If 40 conversions have been logged, start over
    if(ConversionCount == 9)
    {
    ConversionCount = 0;
    }
    else
    {
    ConversionCount++;

    }

    // *******************************************************  insert begin

    // this may not be entirely correct and has not been tested

    // TI typically does something like this and they call it a box car average.

    // They have a note that says it is not part of the control loop but is for the GUI

    VoltageAve = 0;

    for (i=0;i<10;i++)
      {
        VoltageAve += Voltage1[i];
      }
    VoltageAve = (3*VoltageAve)/10/4096;

    //*************************************** insert end

    // Reinitialize for next ADC sequence
    AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1; // Reset SEQ1
    AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1; // Clear INT SEQ1 bit
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge interrupt to PIE

    return;
    }

  • Thanks a lot for your efforts.
    I appreciate you.
    I just tried compiling the corrected code and it keeps saying "unrecognized token", I sincerely don't know why! I'll still keep checking though...
    Kind regards.

    David.

  • It may need
    Uint16 ConversionCount;

    Uint16 Voltage2[10];


    after // Global variables used in this example: towards the top of the file.

    or you can delete all references to Voltage2;

  • Thank for your quick response.
    I added those declaration appropriately, as a matter of fact I just editing the ADC_soc example. I code totally makes sense and it ordinarily should run but I don't know why it keep saying "unrecognized token".
    David.
  • This post suggested adding --verbose_diagnostics to see where it is coming from

    https://e2e.ti.com/support/development_tools/compiler/f/343/p/435084/1557966?tisearch=e2e-sitesearch&keymatch=*F283*%20unrecognized%20token#1557966

    Perhaps if you cut and pasted code from the posting to the editor, some funny characters confused the compiler.

  • I tried adding -verbose_diagnostics and it doesn't work as expected. I would advise against using it.
  • Thanks a lot for your efforts.
    I have also been trying to get it to work but it's not coming along yet. If it doesn't, maybe I'll have to settle for using the values as the come without any averaging.
    Kind regards.
    David.
  • I put the slice of code into Example_2833xAdcSoc.c with a pristine project
    __interrupt void
    adc_isr(void)
    {
    Voltage1[ConversionCount] = AdcRegs.ADCRESULT0 >>4;
    Voltage2[ConversionCount] = AdcRegs.ADCRESULT1 >>4;

    //
    // If 40 conversions have been logged, start over
    //
    if(ConversionCount == 9)
    {
    ConversionCount = 0;
    }
    else
    {
    ConversionCount++;
    }

    // ******************************************************* insert begin

    // this may not be entirely correct and has not been tested

    // TI typically does something like this and they call it a box car average.

    // They have a note that says it is not part of the control loop but is for the GUI

    VoltageAve = 0;

    for (i=0;i<10;i++)
    {
    VoltageAve += Voltage1[i];
    }
    VoltageAve = (3*VoltageAve)/10/4096;
    //*************************************** insert end
    //
    // Reinitialize for next ADC sequence
    //
    AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1; // Reset SEQ1
    AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1; // Clear INT SEQ1 bit
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge interrupt to PIE

    return;
    }

    I had to put
    int i;
    float VoltageAve;
    as a global variable.

    It compiled OK but I have no way to run it.

    In the long run, I don't think averaging makes much of a difference because it will distort the control loop
    if it is included within the control loop.

    An analog filter just before the A/D for anti-aliasing and noise reduction
    is probably a better way to go anyways.

    Good Luck!
  • Thanks a lot. I will import another project and try it again.
    Regards.
    David.
  • David,
    Should make it
    volatile float VoltageAve;

    so the compiler does not optimize it away since it
    is not used anywhere
  • Thanks a lot.
    "Volatile float" really helped a lot but the loop still didn't work
    what I am using now is something like this:

    VoltageAve = Voltage1[0]+Voltage1[1]+Voltage1[2]+Voltage1[3]+Voltage1[4]+Voltage1[5]+Voltage1[6]+Voltage1[7]+Voltage1[8]+Voltage1[9];


    I know it is super crude but I don't know why the loop doesn't work as expected.
    I appreciate all your efforts.
    David.

  • Glad to hear you are making progress.