LPC55S28 PWM timing alignment problem

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPC55S28 PWM timing alignment problem

Jump to solution
623 Views
WilliamW
Contributor III

I have code that aligns the timing of two PWMs so that one has a specific offset from the other. My project has optimizations disabled.  I thought that it was working well until I discovered that the offset necessary for proper timing had changed.  I was able to reproduce the problem by adding a function call (Test function commented out) well before the time critical functionality of restarting the two timers.  The difference between the timing for this example is ~72ns but I've found that other code changes result in larger or smaller differences.  Calling the AlignPWMs function again with the default value, using our device's command line interface, produces the same timing.  Calling the AlignPWMs function with other values produces offset changes that are consistent with the default value.  In summary, until I change the code again, values passed to the AlignPWMs function produce offsets that are consistent with the default value through reset and loss of power.

I've made a number of attempts to determine the cause of the problem hence the use of "__attribute__((aligned(32)))", "__DSB()", "__ISB()" and "__DMB()".  Which I've used to attempt to align the code, synchronize instructions and data, and control the order of explicit memory operations.  I've also reviewed the disassembly and found that, other than the change for the function call, that the same assembly code is produced.

I believe that there must be something different in the way ARM is handling the two CTIMER_StartTimer function calls that is causing something to change with code changes, but I'm at a loss as to what could be causing it or what I should be doing to correct the problem.  Any suggestions would be appreciated.  I would also be open to other ways to align the two PWMs.

void Test( void )
{
}

__attribute__((aligned(32)))
void AlignPWMs( uint16_t nOffset )
{
//	Test();

    /* Disable the global interrupt to guarantee timing */
    uint32_t primaskValue = DisableGlobalIRQ();

    CTIMER_StopTimer( CTIMER0 );
    CTIMER_Reset( CTIMER0 );

    CTIMER_StopTimer( CTIMER3 );
    CTIMER_Reset( CTIMER3 );

    CTIMER0->TC = nOffset;

    __DSB(); 	// Data Synchronization Barrier - completes all explicit memory accesses
    __ISB(); 	// Instruction Synchronization Barrier - flushes the pipeline in the processor

    __DMB();	// Data Memory Barrier - Ensures the apparent order of the explicit memory operations
    CTIMER_StartTimer( CTIMER0 );
    __DMB();	// Data Memory Barrier - Ensures the apparent order of the explicit memory operations
    CTIMER_StartTimer( CTIMER3 );
    __DMB();	// Data Memory Barrier - Ensures the apparent order of the explicit memory operations

    EnableGlobalIRQ( primaskValue );
}

 

0 Kudos
Reply
1 Solution
526 Views
WilliamW
Contributor III

I have worked around the problem by making the time sensitive code as simple as possible.  I dereferenced the peripheral register addresses ahead of time and did an assignment instead of using the bitwise or equal operator.  I also chained the assignment of the two expressions instead of using two statements.  This makes the assembly only 5 lines and seems to keep consistent timing after making code changes both inside and outside of the function.  

// Get the address of the TCR location to be change so that
// dereferencing does not need to occur in the time critical code
volatile uint32_t * px = & ( CTIMER3->TCR );
volatile uint32_t * py = & ( CTIMER0->TCR );

__DSB(); 	// Data Synchronization Barrier - completes all explicit memory accesses
__ISB(); 	// Instruction Synchronization Barrier - flushes the pipeline in the processor

__DMB();	// Data Memory Barrier - Ensures the apparent order of the explicit memory operations
* px = * py = CTIMER_TCR_CEN_MASK;
__DMB();	// Data Memory Barrier - Ensures the apparent order of the explicit memory operations

 I see that the CTIMER pins I am using for the two PWMs also support the SCTIMER functionality in a configuration where I could implement your suggestion if it becomes necessary in the future.

View solution in original post

0 Kudos
Reply
4 Replies
603 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi,

I suppose it is okay to call the AlignPWMs() so that the counter of CTimer0 and CTimer3 has a fixed offset.

What is your problem now?

BR

XiangJun Rong

 

0 Kudos
Reply
592 Views
WilliamW
Contributor III

The problem is that adding code to my project, outside the two lines that restart the timers, causes the offset of the PWMs to change.  In the example I gave adding the Test function caused a change of 72ns.  We found this problem because other changes to code in the project outside this function seem to cause the PWM timing offset to change as well.  I'm confused because changing code that is not in the critical timing section that enables the two timers is causing the offset between the two PWM timers to change.  My expectation is that if the critical timing section that enables the two timers is not changed then the timing of the offset should always be exactly the same.

The way it is working now, when I change code in my project the actual timing offset between the PWMs changes so that if I pass in 150 as the offset it won't produce the same offset in the PWM timing as seen on an oscilloscope as it did before the code is changed in my project even though the code had nothing to do with the restarting of the two timers.

0 Kudos
Reply
580 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi,

From your description, it appears that you have solved your issue.

If you have not solved, I suggest you use SCT, it supports phase shift feature.

BR

XiangJun Rong

0 Kudos
Reply
527 Views
WilliamW
Contributor III

I have worked around the problem by making the time sensitive code as simple as possible.  I dereferenced the peripheral register addresses ahead of time and did an assignment instead of using the bitwise or equal operator.  I also chained the assignment of the two expressions instead of using two statements.  This makes the assembly only 5 lines and seems to keep consistent timing after making code changes both inside and outside of the function.  

// Get the address of the TCR location to be change so that
// dereferencing does not need to occur in the time critical code
volatile uint32_t * px = & ( CTIMER3->TCR );
volatile uint32_t * py = & ( CTIMER0->TCR );

__DSB(); 	// Data Synchronization Barrier - completes all explicit memory accesses
__ISB(); 	// Instruction Synchronization Barrier - flushes the pipeline in the processor

__DMB();	// Data Memory Barrier - Ensures the apparent order of the explicit memory operations
* px = * py = CTIMER_TCR_CEN_MASK;
__DMB();	// Data Memory Barrier - Ensures the apparent order of the explicit memory operations

 I see that the CTIMER pins I am using for the two PWMs also support the SCTIMER functionality in a configuration where I could implement your suggestion if it becomes necessary in the future.

0 Kudos
Reply