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: Extreme jitter with CPU-Timer0 at 10 µs period

Part Number: TMS320F28335
Other Parts Discussed in Thread: CONTROLSUITE

Tool/software: Code Composer Studio

Hello community,

I'm going to setup an precise CPU-interupt counter to capture the elapsed time in multiples of 10 µs. 

Actually I use the TMS320F28335 at a ControlCARD in R2.2, the Docking-Station USB-EMU in R3  and Code Composer Studio in the version: 6.2.0.00050 to realize my solution.

The task is to capture the time between a rising and a falling edge and calculate a time periode to set a output pin based on the same edges. The time between edges are calculated before setting the output. Cputimer0 is to capture the time in small steps.  Cputimer1 is to set the output-pin and Cputimer2 contains several state-machines and other code. 

The question is: Why is there a jitter in the processing time of the cputimer0 of 45ns up to 60 ns? At the oscilloscope, with toggled output pin and only one counter (++) inside the ISR of timer 0, is the ISR activation time of the CPU-Timer0-ISR not constant. 

Hint: The CPU-Frequency inside the ConfigCpuTimer()-function is aligned to improve the time capture precision and is choosen by calculation and series of measurements.

Underneath is my initialization code.

Maybe I get the chance to upload some plots of the oscilloscope, in the following days.

Thanks for your support!

Best regards,

Andreas

    Flash_CPUScaleFactor = SCALE_FACTOR;
    Flash_CallbackPtr = NULL;

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

	
	while(SysCtrlRegs.PLLSTS.bit.PLLLOCKS != 1) { }

	
	Status = Example_CsmUnlock();
	if(Status != STATUS_SUCCESS)
	{
		error();
	}

	
	Example_MemCopy(&Flash28_API_LoadStart, &Flash28_API_LoadEnd, &Flash28_API_RunStart);
	
	Example_MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);

	
	InitFlash();

	
	EALLOW;
	SysCtrlRegs.HISPCP.all = ADC_MODCLK;			// HSPCLK = SYSCLKOUT/ADC_MODCLK
	EDIS;

	// Step 2. Initialize GPIO:
	GPIO_SETUP();


	// 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 registers
    //PieVectTable.ADCINT = &adc_isr;
    PieVectTable.XINT13 = &cpu_timer1_isr;
    PieVectTable.TINT2 = &cpu_timer2_isr;

    PieVectTable.XINT3 = &IL_hyst_isr_FE;		
    PieVectTable.XINT4 = &IL_hyst_isr_RE;		
    PieVectTable.TINT0 = &cpu_timer0_isr;		



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

    // Step 4. Initialize the Device Peripheral. This function can be
    //         found in DSP2833x_CpuTimers.c
    InitCpuTimers();    						// For this example, only initialize the Cpu Timers


   
    ConfigCpuTimer(&CpuTimer0, 149.88 , 10); 
    
    ConfigCpuTimer(&CpuTimer1, 149.88, 10); 
   
    ConfigCpuTimer(&CpuTimer2, 149.88, 1000);	


    CpuTimer0Regs.TCR.all=0x4C00;           	
    CpuTimer1Regs.TCR.all = 0x4C00; 			
    CpuTimer2Regs.TCR.all = 0x4C00; 			

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

    // Enable CPU int1 which is connected to CPU-Timer 0/ADC-Interrupt, CPU int13
    // which is connected to CPU-Timer 1, and CPU int 14, which is connected
    // to CPU-Timer 2:

    IER |= M_INT1;
    IER |= M_INT13;
    IER |= M_INT14;


    PieCtrlRegs.PIEIER1.bit.INTx7 = 1;			// Enable TINT0 in the PIE: Group 1 interrupt 7
    IER |= M_INT12;								// Aktiveren des CPU-INT 12, welcher der zuständige für den XINT3 und XINT4 ist
    PieCtrlRegs.PIEIER12.bit.INTx1=1; 			// Freigeben des XINT3 als INT12.1 für die CPU
    PieCtrlRegs.PIEIER12.bit.INTx2=1;			// Freigeben des XINT4 als INT12.2 für die CPU
    PieCtrlRegs.PIEACK.bit.ACK12=0; 			// Freigeben des INT?!?!?! nötig?
    XIntruptRegs.XINT3CR.bit.POLARITY=0; 		//--- Auf fallende Flanke eingestellt
    XIntruptRegs.XINT3CR.bit.ENABLE=1;			// XINT3 aktivieren
    XIntruptRegs.XINT4CR.bit.POLARITY=1; 		// ext. INT4 konfigurieren, dass Reaktion bei steigender Flanke stattfindet
    XIntruptRegs.XINT4CR.bit.ENABLE=1;			// XINT4 aktivieren

    // Enable ADCINT in PIE
    // PieCtrlRegs.PIEIER1.bit.INTx6 = 1;

    // Enable global Interrupts and higher priority real-time debug events:
    EINT;   									// Enable Global interrupt INTM
    ERTM;  										// Enable Global realtime interrupt DBGM


  • A second question is, why is there a failure of approx. 1.3ms at a observed time period of  10 sec.?

    I increase a counter value after 100 x 10µs interrupts.

  • Andreas,

    I will look into this, but a quick question - why are you not using the Capture Module (eCAP)? It was designed for this purpose.
     
    - Ken

  • Hello Ken,

    thanks for your fast response. I decided to use the edge-based interrupt to calculate the time between, because the measuered time can be between 1 sec. and several minutes. But it is necessary to set the output with an accuracy of 10 µsec., therefore should the measured time also in this accuracy.

    I thought, that the eCAP-module is not able to capture times in this dimension. It is possible to capture time periodes of several minutes?

    Best regards,

    Andreas

  • Andreas,

    Understand. With the 32-bit CPU Timers you can make use of the 16-bit clock prescaling. However, the eCAP modules are clocked at the SYSCLKOUT rate. (For a 150 MHz device running at the maximum speed, this would be 6.67 ns cycle time; 6.67 ns * 2^32 is approximately 28 s max).

    So, back to the question of why there is a jitter in the processing time of the CPU Timer 0? As you know, CPU Timers 0, 1, and 2 are identical, but only CPU Timer 0 goes through and is connected to the PIE block. This is not the case for CPU Timers 2 and 3, which are directly connected to the CPU interrupt core lines.

    I am still looking into this. I just wanted to point out this difference with CPU Timer 0, which might have something to do with your question - but I need to confirm.

    Best regards,
    Ken
  • Hello Ken,

    here there are some additional informations, after several hours of trouble-shooting.

    At first, there are two plots of the jitter-problem. I toggled a GPIO-PIN at the beginning and in the end of the CPU-timer0-ISR but flush with acknowledge of the interrupt. The code insinde this particular ISR was only a simple addition in a if-case. The initialization is the same as in the first post.

    The moment of interrupt varied unequal, but an extreme was 100 ns. The plots are triggred to an not displayed signal before.

    Pic. 1 Red-> ISR-Time of CPUTimer 0; triggered to the signal before (not displayed)

    Pic. 2 Red-> ISR-Time of CPUTimer 0; triggered to the signal before (not displayed) -> extreme jitter some moments later

    We tried the following configurtaions. There was all the time a jitter between 20 ns (with subjective decreased number of jitter) and 100 ns, with a innumerable number of bounds.

    1. Same configuration but without own program. Only the computing time has changed, but that is not the problem at an interrupt all 10 µsec. 

    2. Same  configuration but without timer 2, with the code of timer 2 in timer 1. No change of the jitter of timer 0.

    3. Same configuration but without timer 2 and 1, with the code of timer 2 in while (1) -loop, with request each tenth cpu-timer0 ISR. No change of the jitter of timer 0.

    4. Same configuration at the beginning with respectively code-placement, but with changed periode-time in cpu-timer configs.

    4.1 The periode-time of timer 1 has influence on the jitter of cpu-timer 0 -> only if is the same like cpu-timer0 the jitter is "only" 100ns. Is this value smaller or bigger, the jitter is in range of 1µs.

    4.2 The periode-time of timer 2 has influence on the jitter of timer 0 -> At a periode-time of 1msec. and a higher value of the periode, the jitter of timer 0 becomes not higher as 100 ns. It is the periode time smaller as 1msec., the subjective number of jitter increase.

    5. We tried to use the example-projekt "Example_2833xCPUTimer.c" from controlSUITE. So the jitter is still there, but not so distinct in subjective number and magnitude. We tried only CPU-Timer0, CPU-Timer0 with CPU-Timer 1 and the configuration of the first post. The jitter was only 20 ns up to 50ns.

    The jitter of cpu-timer0, to capture the time, leads to a jittering failure in time estimation, at a long periode, in the process. The magnitude is nearly by 60 µs over a time periode of 2.5 Sec.! This is a lot, but comprehensible with a jitter of +- 50-100 ns at the smallest time unit.

    This list is not complete, but contains the main-experiences.

    I will try to use only timer 1 and 2 for my program, as you sat in your last post.

    Best regards,

    Andreas

  • Andreas,

    Please remember that it takes a minimum of 14-16 cycles from generating an interrupt to entering the beginning of the ISR (and this will be longer if other interrupts are being processed during this time). This is most likely resulting in the interrupt jitter that you are experiencing.

    I hope this helps. If this answers your question, please click the green "Verified Answer" button. Thanks.

    - Ken
  • Hello Ken,

    thank you for your response.

    Based on your last answer, I tried to change the code placement, to use only CPU-timer-1. So all the code was running inside the CPU-timer 1, but there is no  identifiable improvement of the jitter, in number and magnitude. The same effect was, if I use only the CPU-Timer-2 for all the code. In each programm-tests, the not used timer and interrupts are disabled.

    1. Is it possible, that I use a wrong initializiation or wrong parameters to enable the interrupts? What are the correct values to initialize a accurate time acquisition, like I want to do. 

    Underneath is an abstract of my main.h-file. 

    #ifndef MAIN_H_
    #define MAIN_H_
    
    #include "DSP28x_Project.h"
    #include "DSP2833x_Gpio.h"
    #include "Flash2833x_API_Config.h"
    #include "Flash2833x_API_Library.h"
    #include <stdio.h>
    
    
    #include "GPIO_SETUP.h"
    #include <math.h>   		
    #include <stdbool.h>		
    
    
    #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
    #define ADC_CKPS   0x1   		// ADC module clock = HSPCLK/2*ADC_CKPS   = 25.0MHz/(1*2) = 12.5MHz
    #define ADC_SHCLK  0xf   		// S/H width in ADC module periods                        = 16 ADC clocks
    #define AVG        256  		// Average sample limit
    #define ZOFFSET    0x00  		// Average Zero offset
    #define BUF_SIZE   256 			// Sample buffer size
    
    __interrupt void cpu_timer1_isr(void);
    __interrupt void cpu_timer2_isr(void);
    __interrupt void IL_hyst_isr_FE(void); 	
    __interrupt void IL_hyst_isr_RE(void);	
    __interrupt void cpu_timer0_isr(void);	
    
    
    void ADC_Config(void);
    void error(void);
    void Example_MemCopy(Uint16*, Uint16* , Uint16*);
    Uint16 Example_CsmUnlock(void);

    And here are my include options of the project:

    "${CG_TOOL_ROOT}/include"
    "C:\ti\controlSUITE\libs\utilities\flash_api\2833x\28335\v210\include"
    "C:\ti\controlSUITE\device_support\f2833x\v140\DSP2833x_common\include"
    "C:\ti\controlSUITE\device_support\f2833x\v140\DSP2833x_headers\include"


    2. Or, is it possible, that the flashed code at the MCU is the cause for this problems?

    3. Or, is it a common magnitude of jitter for the F2833x-series?

    Thanks a lot!

    Best regadrs,

    Andreas

  • Andreas,

    In your earlier post the CPU Timers were configured as:

    ConfigCpuTimer(&CpuTimer0, 149.88 , 10);
    ConfigCpuTimer(&CpuTimer1, 149.88, 10);
    ConfigCpuTimer(&CpuTimer2, 149.88, 1000);

    Why is the frequency set to 149.88 rather than 150.0?

    - Ken
  • Hello Ken,

    I noticed at time measurement validation, that there is a linear dependency of failure between the captured time in MCU and the real time, given by a signal generator with an accurate period time. The longer the measured time the higher the failure/negative difference between the real time and the captured time.
    I recognized, that the number of decrement values, which will calculated in ConfigCpuTimer, are too high. So the real controller sysclock is lower than 150 MHz. This value of 149,88 MHz is the number of sysclock of my controller, it is the conclusion of calculation based on the deviations between the times. After I changed this configuration, the failure decreased from 5 ms to 0.5 ms at a captured period time of 5 sec.



    Best regards,
    Andreas
  • Andreas,

    When using an ISR to capture exact time measurements there will always be jitter since the context switch into the ISR will be different each time, including the asynchronous capture of the signal into the SYSCLK time domain. I hope this resolves the issue.

    - Ken
  • Hello Ken,

    I understand what you mean, with the asynchronous event time related to the SYSCLK.

    BUT this jitter is all the time active, also when no own code is inside to project and only the example initialization of cpu-timer 0 is done.

    So it is right, that the  TMS320F28335 can't capture the past time with a CPU-Timer with an accuracy of 10 µsec? What could be the alternative to realize my task?

    Best regards,

    Andreas

  • Andreas,

    The CPU timers can measure the time difference of a 10 µs event. However, the accuracy is about +/- 10 SYSCLKs - which is in range of the 45 - 60 ns you are experiencing. This is a characteristic of the device hardware. Your previous post is confusing with respect to accuracy - 10 µs is not the accuracy of the timer. Remember, control peripherals on the device are typically not used to measure events in minutes. As another reference you can see page 51 in the data sheet for the GPIO input timing:

    www.ti.com/lit/sprs439

    The only other suggest is to be creative and see if you can somehow use the timer capabilities in the ePWM module (e.g. use the input signal as either a sync or TRIP input to trigger something). Just a thought.

    - Ken
  • Hello Ken,

    thanks for your answer.

    I mean the accuracy of 10 µs if I use my own timer initializiation. But I know what you mean.

    Okay, I'm looking for a better solution, to improve my accuracy. 

    Thank you for your informations. Great work!

    Best regards,

    Andreas

  • Andreas,

    You are welcome.

    Best regards,
    Ken
  • Hello Ken,

    I found a solution for the jitter-problem. Based on this informations: () I included the IDLE-State- asm command inside the endless while(1)-loop inside the main(). I utilized the code of "Example_2833xIdleWake.c".

    There is NO more a jitter at the cpu-timer-0 interrupt handling.

      void main(void)
    { 
    ...
    ...
    ...
    ...   
     EALLOW;
      if (SysCtrlRegs.PLLSTS.bit.MCLKSTS != 1) // Only enter Idle mode when PLL is not in limp mode.
      {
         SysCtrlRegs.LPMCR0.bit.LPM = 0x0000;  // LPM mode = Idle
      }
     EDIS;
     while(1)
     {
        	  __asm(" IDLE");
     }
    
    } // main()