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: Time critical operation needs to run in consistent time increments

Part Number: TMS320F28069
Other Parts Discussed in Thread: CONTROLSUITE, C2000WARE

Hello All,

I have a sw state machine that is currently running within Timer1 interrupt:

interrupt void cpu_timer1_isr(void)
{

	CpuTimer1.InterruptCount++;

	GpioDataRegs.GPASET.bit.GPIO9 = 1;
	GpioDataRegs.GPACLEAR.bit.GPIO9 = 1;

	cntOutputsHigh = 0;
	cntOutputsHigh += GpioDataRegs.GPADAT.bit.GPIO2;
	cntOutputsHigh += GpioDataRegs.GPADAT.bit.GPIO4;
	cntOutputsHigh += GpioDataRegs.GPADAT.bit.GPIO30;
	cntOutputsHigh += GpioDataRegs.GPADAT.bit.GPIO31;

	if(cntOutputsHigh != oldCntOutputsHigh) {
		oldCntOutputsHigh = cntOutputsHigh;
		switch(cntOutputsHigh){
			case 0:
				GpioDataRegs.GPASET.bit.GPIO5  		= 1;	//  Take 1x bit low  (J1-3)
				GpioDataRegs.GPASET.bit.GPIO8  		= 1;	//  Take 2x bit low  (J1-12)
				GpioDataRegs.GPASET.bit.GPIO25 		= 1;	//  Take 3x bit low  (J1-6)
				GpioDataRegs.GPBSET.bit.GPIO39 		= 1;	//  Take 4x bit low  (J1-14)
				break;
			case 1:
				GpioDataRegs.GPACLEAR.bit.GPIO5  	= 1;	//  Take 1x bit high (J1-3)
				GpioDataRegs.GPASET.bit.GPIO8  		= 1;	//  Take 2x bit low  (J1-12)
				GpioDataRegs.GPASET.bit.GPIO25 		= 1;	//  Take 3x bit low  (J1-6)
				GpioDataRegs.GPBSET.bit.GPIO39 		= 1;	//  Take 4x bit low  (J1-14)
				break;
			case 2:
//				GpioDataRegs.GPASET.bit.GPIO5  		= 1;	//  Take 1x bit low  (J1-3)
				GpioDataRegs.GPACLEAR.bit.GPIO8  	= 1;	//  Take 2x bit high (J1-12)
				GpioDataRegs.GPASET.bit.GPIO25 		= 1;	//  Take 3x bit low  (J1-6)
				GpioDataRegs.GPBSET.bit.GPIO39 		= 1;	//  Take 4x bit low  (J1-14)
				break;
			case 3:
//				GpioDataRegs.GPASET.bit.GPIO5  		= 1;	//  Take 1x bit low  (J1-3)
//				GpioDataRegs.GPASET.bit.GPIO8  		= 1;	//  Take 2x bit low  (J1-12)
				GpioDataRegs.GPACLEAR.bit.GPIO25 	= 1;	//  Take 3x bit high (J1-6)
				GpioDataRegs.GPBSET.bit.GPIO39 		= 1;	//  Take 4x bit low  (J1-14)
				break;
			case 4:
//				GpioDataRegs.GPASET.bit.GPIO5  		= 1;	//  Take 1x bit low  (J1-3)
//				GpioDataRegs.GPASET.bit.GPIO8 		= 1;	//  Take 2x bit low  (J1-12)
//				GpioDataRegs.GPASET.bit.GPIO25 		= 1;	//  Take 3x bit low  (J1-6)
				GpioDataRegs.GPBCLEAR.bit.GPIO39	= 1;	//  Take 4x bit high (J1-14)
				break;
			default:
				break;
		}
	}
}

The logic is working fine, the problem is that the interrupt timing is inconsistent. 

Here's the init code:

       ConfigCpuTimer(&CpuTimer1, 86, 1);  //

Using gpio9, I see the following using a data acquisition system:

So, there are about 12 set/clears in this 500us window, and they are disbursed randomly through time.

I suspect other interrupts are taking precedence over my state machine routine embedded in the timer1 isr.  That would explain the varying period.

I see in the manual (SPRS698D) a reference that looks promising:

Should I reconfigure my timers to use timer0 as the interrupt source for my state machine?

Any other ideas or suggestions?

Thanks in advance for your valuable input!

  • Here are the missing images from the original posting:

  • Hi Robin,

    You are right in pointing out that only Timer 0 can be used for CLA. I have highlighted the path of the interrupt in the diagram below. You can find this in the Technical Reference Manual for TMS320xF2806x: http://www.ti.com/lit/ug/spruh18g/spruh18g.pdf 

    In the same manual, section 1.4.5, you will be able to understand more clearly from the diagram that only Timer0 is connected to PIE block. Timer 1 and 2 are directly connected to CPU, so they cannot be used by CLA for interrupts. 

    Please use Timer 0 to generate the interrupts from CLA and check if you are able to generate consistent interrupts.

  • Thanks Ritvik, I appreciate the help.  I see that the CLA can provide parallel processing to get some time critical code to run more consistently.

    I've been poring over the documentation, but I'm confused.

    9.3.2 in SPRUH18G typifies the init sequence.  Below is what I've done, and where I'm confused.

    1. Copy CLA code into the CLA program RAM
    Here's the code in the initialization area of main():
        memcpy(&Cla1ProgRunStart, &Cla1ProgLoadStart, (Uint32)&Cla1ProgLoadSize);
    (I don't know where Cla1ProgRunStart is declared or defined.  How will this memcpy() know to encompass my stateMachineTask() code?)

    2. Initialize CLA data RAM:
       No data needed.  The algorithm I want to use is a simple state machine that samples the output state of 4 GPIOs and sets 4 other GPIOs based on the states of the 4 tested.

    3. Configure the CLA registers:
       - Enable the CLA clock in the PCLKCR3 register:
         Here's the code to init the clock
              SysCtrlRegs.PCLKCR3.bit.CPUTIMER0ENCLK = 1;
       -Populate the CLA task interrupt vectors: MVECT1 to MVECT8:
         Here's the code that sets the vectors, previously added by a different eng.
              Cla1Regs.MVECT1 = ((Uint16)Fir256Task - (Uint16)&Cla1ProgRunStart);
              Cla1Regs.MVECT8 = ((Uint16)CircBufClearTask - (Uint16)&Cla1ProgRunStart);
         I think I need to provide a similar statement.  Does this look right?
              Cla1Regs.MVECT7 = ((Uint16)StateMachineTask - (Uint16)&Cla1ProgRunStart);
    (It looks like I'll create a function called stateMachineTask() that contains the state machine code?  Can this be a std C function?)
       -Select the task interrupt sources:
         Here's how that is done
             Cla1Regs.MPISRCSEL1.bit.PERINT8SEL = 2;
       -Enable IACK to start a task from software if desired:
         I'm not sure if I need this for my state machine code.  It was apparently used elsewhere, because if I disable it, other code stops working.  Here's the init code
              Cla1Regs.MCTL.bit.IACKE    = 1;
       -Map CLA data RAM to CLA space if necessary:
         This code was previously setup by a different sw eng.  I don't think it will interfere with what I'm trying to do, but let me know if you disagree.
              Cla1Regs.MMEMCFG.bit.RAM0E    = 1;
              Cla1Regs.MMEMCFG.bit.RAM1E    = 1;
              Cla1Regs.MMEMCFG.bit.RAM2E    = 1;
              Cla1Regs.MMEMCFG.bit.RAM0CPUE    = 1;
       -Map CLA program RAM to CLA space:
         This code was also previously setup by a different sw eng.  I don't think it will interfere with what I'm trying to do, but let me know if you disagree.
              Cla1Regs.MMEMCFG.bit.PROGE    = 1;

    4. Initialize the PIE vector table and registers:
         This code was previously setup by a different sw eng.
              PieCtrlRegs.PIEIER1.bit.INTx7 = 1;
              PieCtrlRegs.PIEIER1.bit.INTx8 = 1;
              PieVectTable.CLA1_INT1 = &cla1_task1_isr;
         I think I need to provide a similar statement.  Does this look right?
              PieVectTable.TINT0 = &cla1_task7_isr;
    (See my comments at the bottom.)

    5. Enable CLA tasks/interrupts:
         This code was previously setup by a different sw eng.
              Cla1Regs.MIER.bit.INT1 = 1;
              Cla1Regs.MIER.bit.INT8 = 1;
         I think I need to provide a similar statement.  Does this look right?
              Cla1Regs.MIER.bit.INT7 = 1;

    6. Initialize other peripherals:
         The project uses several of the on-board ADCs, but the SOC is triggered by sw.  I don't know if the CLA is actually being used.  Some of the setup work was already done, but the code seems to be ignored.  The service that the existing CLA code would provide is actually being provided by the CPU instead via other code.


    I'm having confusion over the difference and uses of MVECT assigned addresses versus the peripheral interrupt vector table.

    Timer 0 initiates a TINT0 interrupt, and it looks like the PIE Mux'd peripheral int vector table 5-11 indicates program execution jumps to the address stored in 0xD4C (per table 5-11).

    But, MVECT7 seems to contain the task's interrupt vector.  The code shown above seems to load the address of the task into MVECT.  So, if my stateMachineTask() code's start address is in MVECT, then what is the purpose of TINT0s vector to 0xD4C?

    Thanks for the help!

  • Hi,

    The MVECTs should point to the CLA tasks. You should not need to do any offsetting or subtraction of vectors as you have shown. Please see the CLA examples in controlSUITE and C2000Ware. You should create a CLAcode section similar to a ramfuncs section and then perform a memcpy of that section at the start of main. Then you can simply assign the CLA task vectors to MVECTs using the tasks alias or symbol.

    Also, make sure you have enabled the clock to the CLA properly.

    Your best bet is to see the CLA examples we provide.

    sal
  • Thanks Sal.  I'm working on figuring out what needs to be there to run the state machine from CLA, and what needs to NOT be there.  I'm looking at the sample code trying to figure it out.  More on that later...

    I misspoke earlier about the state machine.  It's not testing the state of 4 GPIO's, it's testing the state of 4 ePWMs.  In the C28xx CPU code, I test the GpioDataRegs.GPADAT.bit.GPIOx to see if the ePWM state is hi or lo.  (This works great, except for time consistency.  By using a CPU timer1 interrupt to run the code, other interrupts cause inconsistency in the time interval.)

    It seems fortunate though that I'm using ePWMs instead of GPIOs, since the F28069's CLA apparently DOESN'T have direct access to the GPIO registers, but it DOES have direct access to the ePWM registers.

    The first question is, how do I test the output state of the ePWMs from the CLA?

    The second question is, since the F28069's CLA doesn't have direct access to the GPIO registers, how do I set their output states after the state machine has determined which outputs to set in a time consistent fashion?

    Thanks in advance!

  • Hi Robin,

    1) I am not sure there is a way to test the state of the PWMs with the CLA directly other than looking at the PWM registers. I suppose you could set-up some lookback to the ADC and generate an interrupt which triggers a CLA task to check this.

    2) I think the best way is to write some data to the Message RAM or CLA Data RAM for the C28x to read after the Task has completed. Once the Task is completed, you can trigger a C28x interrupt which in the ISR could then read the message data from the CLA and configure the GPIOs as desired.

    sal
  • Hi Sal,

    1) I am not sure there is a way to test the state of the PWMs with the CLA directly other than looking at the PWM registers.

    Is there a way to look at PWM output levels by looking at the PWM registers?  If so, which register would contain that info?

    2) I think the best way is to write some data to the Message RAM or CLA Data RAM for the C28x to read after the Task has completed. Once the Task is completed, you can trigger a C28x interrupt which in the ISR could then read the message data from the CLA and configure the GPIOs as desired.

    I see what you're saying, I'll investigate this further.

    Thanks Sal!

  • Hi Robin,

    What information exactly are you looking for?

    I would start by looking through the EPWM registers in the TRM, Section 3.4.

    www.ti.com/.../spruh18g.pdf

    sal
  • Hi Sal,

    Every ~10us, my state machine needs to know if the outputs of the 4 ePWMs are hi or lo. 

    I started out using a C28XX timer interrupt to check their outputs by looking at the GPIODAT registers.  The output of the state machine is determined by the output states of the 4 ePWMs.  The logic works great, but unfortunately timing of that operation was not consistent due to other interrupts inserting delays.

    So, as I move the code out of the C28XX and into the CLA, I need to know how to check those ePWM outputs.  It looks to me like the CLA doesn't have access to the GPIODAT registers, but it does have direct access to the ePWM registers.  I just don't know if any of the ePWM registers contain information about the output state of the ePWMs.

    Which register will tell me the output state of an ePWM?

    Thanks,
    robin

  • Robin,

    I don't see a register in the PWM module which provides the states at its outputs.  Normally the best way to obtain the current state of the PWM would be to do as you are doing: read the GPIO pins directly.  However, as you point out, the CLA does not have access to the GPDAT registers.  I can think of only two practical solutions:

    - Trigger a CPU interrupt from Timer0, read the GPxDAT registers on the CPU side, store the information in message RAM, and trigger a CLA task from the CPU ISR.  Then we are back to the timing concerns you raised earlier in your post.

    - If you have a fixed PWM configuration, you should be able to determine the PWM status by reading the PWM timer count and compare registers. The CLA has access to those registers, so with a known PWM pattern you should be able to infer the instantaneous PWM state this way.

    Regards,

    Richard

  • Hi Richard,

    Would it be advisable to configure the ePWM for up/down count (EPwm2Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;), and then use the TBSTS register (EPwm2Regs.TBSTS.bit.CTRDIR) to determine output level?

    Thanks,
    robin

  • Hi Robin,

    Yes, I had in mind something like that. Depending how you have the PWM configured you should be able to determine PWM status by reading the TB counter, compare registers, and possible the direction bit in TBSTS. You don't necessarily have to be using up-down count mode to do this.

    Regards,

    Richard
  • Hi Richard,

    The input side of my state machine is now working, based on the PWM changes noted previously.  Now I'm working on getting four outputs to pins on the device.  As a reminder, I'm setting this up to be run from the CLA.  As such, the methodology has to employ only registers directly accessible from CLA.

    I will need four outputs to independently change state no more often than every 125us with an accuracy of plus or minus a few microseconds.  I might use a polling rate of ~10us.  The clock is running at 86MHz, so there should be plenty of time.

    I've ruled out using most CLA accessible modules (comparator, eCAP, eQEP, and ADC), except for ePWM.  I know this is an unconventional use of PWMs, but I'm  running out of options. 

    Here's the scenario;  I need to toggle four independent output pins on the device from the CLA.  Poring over SPRUH18G and the SPRS698F, I get the sense that I can control the output states of ePWM outputs like this:

    1. Set up the GP MUX registers to assign four ePWMs to output pins.
    2. Set up the TZ registers to control those four ePWMs.
    3. In CLA C code, software force a trip on one or more of the ePWMs.
    4. The result is a change in state of one or more ePWM outputs.

    Does this make sense?  I don't want to invest a lot of time into this if it's not feasible.

    Thank you for your valuable help!

    robin

  • Hi Robin,

    Your approach should work.  You would configure the appropriate PWM logic using the TZA or TZB fields in TZCTL, then force a one shot trip with the OSHT bit in TZFRC.  For the next transition you would clear the event with TZCLR, re-configure TZCLT, and repeat.  It looks OK, but I have not tried using the PWM in this way.

    Alternatively, or if you run into difficulty, you could try re-configuring the AQ module each time to force a high/low level.  The disadvantage of that is you'd need a (counter=compare) match for the transition so the counter would have to run through CMPA/B and there would be a small delay, but it seems this is not so time critical and you could choose a high PWM frequency.

    Regards,

    Richard

  • Hi Richard,

    Excellent!  I'm now able to toggle the ePWM output as you described, but I need to eliminate the signal the ePWM is generating on its own.

    Here's the signal that the ePWM is generating before implementing the forced trip:

    Here’s what it looks like after implementing the forced trip:

    As you can see in the images, the ePWM is generating a low duty cycle periodic pulse every 650us, roughly. I had it setup that way before making these forced trip changes. The new state machine code forces the trip when the output should be high, and clears it when it should be low, without regard to the signal being generated by the ePWM on its own.

    What’s the appropriate way to disable the periodic signal that’s being generated by the PWM? Normally I would disable the ePWM by disabling its clock. I think it needs the clock to perform the forced trip function. Would it be appropriate to just eliminate the two lines of code that sets and clears the output when the timer reaches zero and period? As shown commented out here:

    Is there a better way?

    Thanks,

    robin

     

  • Hi Robin,

    I think so, yes.  Try removing those two lines first. 

    The clock input to the PWM module needs to be enabled, but I don't think the PWM timer actually needs to be running since you are forcing the trip.  Try freezing the timer:

    EPwm1Regs.TBCTL.bit.CTRMODE = 3; // freeze TB counter

    ...then manipulating TZCLR, TZCTL, and TZFRC as before.  I think that should be enough.

    Regards,

    Richard

     

  • Hi Richard,

    OK, I wrote and proved code in C28x.  I made it modular, so it could be more easily ported to the CLA. 

    And, now I've moved it to the CLA.  It appears I was able to get all the registers setup properly, because I see things happening in my CLA function.  Timer0 is creating an interrupt and the right stuff is happening.

    However, I'm having a problem writing to the ePWM register.  It's a protected register, which requires EALLOW/EDIS to write to it from C28x.  That doesn't work in CLA.  I've found reference to using MEALLOW/MEDIS from the CLA here:
    "processors.wiki.ti.com/index.php/Control_Law_Accelerator_(C2000_CLA)_FAQ" as follows:

    Q: Some of those registers you mentioned are EALLOW protected from rogue writes by the main CPU. Does the CLA have this protection as well?

    There is a bit called MEALLOW in the CLA status register that enables/disables the protection for CLA writes. This is set and cleared by the MEALLOW/MEDIS CLA instructions. This protection is independent of the main CPU's EALLOW bit. That is, the main CPU can enable writes via EALLOW, but the register will still be protected from CLA writes via MEALLOW.


    When applying the MEALLOW and MEDIS, building the code produces the error:
    #20 identifier "MEALLOW" is undefined

    Is there a macro definition that I'm missing?

    Is there a different way to control that bit?

    Maybe allow writes like this:

    EALLOW;
    Cla1Regs._MSTF.bit.MEALLOW = 1;
    EDIS;

    and disallow writes like this:

    EALLOW;
    Cla1Regs._MSTF.bit.MEALLOW = 0;
    EDIS;

    Thanks for your help.

    robin

  • Addendum to the last post:

    I added the following code to my module header file:
    #define MEALLOW __asm(" MEALLOW")
    #define MEDIS __asm(" MEDIS")
    and the ePWM outputs are now responding to the state machine code running from the CLA.

    I'll do some testing to see how the timing has improved (relative to original post) and report my findings.

    Thanks again.
    Robin
  • OK, my state machine is working in the CLA, but now the DSP filters no longer work.  They have always been working in the C28, never the CLA, and I don't intend to put them in the CLA.

    The expressions watch window shows them as either "+inf" or "-inf".  Through some troubleshooting, I can see that the filter alogrithm (biquad) is starting as expected, but with each iteration, the values continually climb into the overflow state that reads out as "inf".

    My guess is that I've somehow broken the IQ Math functions.  Is it possible that adding the CLAMath library caused a conflict with the IQMath lib?  Here are the includes in the project.h file:

    Here's a snippet of the filter code:

    Any ideas?

    Thanks,
    robin

  • Robin,

    Sorry I missed your previous posts.

    There should be no conflict between IQmath and the CLA library.  When you iterate through the filter, does the result of the first multiply seem correct?  If so it implies IQmath is not the issue.  Can you check your coefficient settings in a watch window to ensure they are as expected?  If so, try running the filter code in a different project to see if it behaves differently.

    BTW, I notice you're setting MATH_TYPE and GLOBAL_Q in your source file, but those are normally set in "IQmathLib.h", which I'd recommend doing.  Can you check there is no conflict there?

    Regards,

    Richard

  • The filters weren't working because the sin() function had no declaration.  I had commented out math.h at some point and forgot about it.  After removing the comment, the filters started working.

    This has been a long and winding road, but it's all working.

    Four tones are being generated with ePWMs, and the state machine code, running in the CLA, looks at the PWM registers to determine waveform output states.

    The state machine's outputs are controlling ePWM outputs as if they were GPIO's.

    The original problem of time inconsistency has improved dramatically.

    Thanks to all who contributed!

  • Hi Robin,

    Very glad to hear you have it working.  Well done and thanks for letting us know.

    Regards,

    Richard