Most Efficient Way to Generate Varying Duty Cycle and PWM

Technical questions regarding the XTC tools and programming with XMOS.
kster59
XCore Addict
Posts: 162
Joined: Thu Dec 31, 2009 8:51 am

Most Efficient Way to Generate Varying Duty Cycle and PWM

Post by kster59 »

XMOS devices are different from other MCUs in that usually there is some hardware NCO timer to generate PWM and Duty Cycle.

I have figured out the most efficient way to generate PWM values is to left shift a 32bit number into a 32 bit buffered 1 bit port. The frequency is set with a clock block on the 1bit port. This requires only 1 write every 32 cycles to generate my PWM with 32 slots. Alternatively you can use 8x 32bit numbers and get 256 bit resolution. So writing 0x0000ffff into a 32bit port will generate 50% duty cycle with the frequency base of 32* the clock block I'm timing off of.

Anyway, how about varying duty cycle with optimal spacing (50% duty should be 1010101 etc, 25% should be 100010001000, etc)? Usually this is done in hardware with a NCO with adding a number to a base and if the number overflows then set it to 1.

What's the best way to do it in Xc taking advantage of clock blocks and buffered ports?

I could store a big array of lookup tables to follow a sequence but that doesn't seem that efficient for something that should be simple.


User avatar
lilltroll
XCore Expert
Posts: 956
Joined: Fri Dec 11, 2009 3:53 am
Location: Sweden, Eskilstuna

Post by lilltroll »

Do you just want to calculate a semi-static "optimal" sequence, or is it timevariant ?
Probably not the most confused programmer anymore on the XCORE forum.
kster59
XCore Addict
Posts: 162
Joined: Thu Dec 31, 2009 8:51 am

Post by kster59 »

It should be optimal.

I have read through the assembly instructions and so far I come up:

while(1)
{
{result,carry}=ladd(result,phase,0);
onebitport <: carry;
}

But that would require two instructions + the loop jump=3 instructions so probably not doable at 100mhz with other stuff going on.

There's probably a better way to this common problem.
User avatar
Woody
XCore Addict
Posts: 165
Joined: Wed Feb 10, 2010 2:32 pm

Post by Woody »

You may find that you get better results by using timestamping. Each port has a timer which is incremented every time it receives a clock pulse. Timestamping allows you to specify the exact time (clock number) that you want a signal transition to occur on.

Code: Select all

int portTime;

pwmPort <: 0 @ portTime;  // Find out the current port time
portTime += 20;
pwmPort @ portTime <: 1;
portTime += 60;
pwmPort @ portTime <: 0;
portTime += 47;
pwmPort @ portTime <: 1;
portTime += 33;
pwmPort @ portTime <: 0;
As you can see from this example you can first issue an output and read the time that it occured at, then you can set a time in the future when you want the next output to occur and then schedule that.

See section 4.3 "Performing I/O on Specific Clock Edges" of Programming XC on XMOS Devices for more details: http://www.xmos.com/support/documentation
kster59
XCore Addict
Posts: 162
Joined: Thu Dec 31, 2009 8:51 am

Post by kster59 »

Previously I used timestamping a lot.

Then I realized timestamping is a blocking call.

Buffered ports offer way better performance than timestamping so I avoid timestamping whenever possible now.

For example, a<:0b11110000; can be written at 100mhz if a is an 8bit port or faster.

Certainly if I do a <: 1 @ count; a<: 1 @ count+1;
//this requires two instructions, one to write in a and one to increment count.

So to do a bitwise operation on every bit, I need 64 cycles for a 32 bit number while with a buffered port I can do 1 operation every 32 cycles.

What would really be cool would be /timestamped buffered/ outputs.

This is originally what I thought XMOS did so that a<: 1 @ count; is stored in the buffer and it executes the next line.

However, it is not that way as far as I can tell.
kster59
XCore Addict
Posts: 162
Joined: Thu Dec 31, 2009 8:51 am

Post by kster59 »

Consider the below usage at 50% duty cycle. The fastest I can get is 50/3/2=8.33mhz @ 50% duty cycle.

Code: Select all

#include <platform.h>

out buffered port:1 pIO = XS1_PORT_1E;
clock b_clk = XS1_CLKBLK_1;

int main() {
	unsigned short count;

	configure_clock_rate(b_clk,50,3); //set speed here
	configure_out_port(pIO,b_clk,0);
	start_clock(b_clk);

	pIO <: 0 @ count;
	while(1)
	{
		count+=2;
		pIO @ count <: 1;
		count+=2;
		pIO @ count <: 0;
	}

	return 0;
}

Compare this to:

Code: Select all

#include <platform.h>

out buffered port:32 pIO = XS1_PORT_1E;
clock b_clk = XS1_CLKBLK_1;

int main() {
	unsigned short count;

	configure_clock_rate(b_clk,100,1); //set speed here
	configure_out_port(pIO,b_clk,0);
	start_clock(b_clk);

	pIO <: 0 @ count;
	while(1)
	{
		pIO<:0xAAAAAAAA;
	}

	return 0;
}


Here I generate 50% duty cycle at 100mhz. I can put 4 extra PWM pins in there and it still works.

Timestamped I/O has serious limitations that limit its usefulness.

Take another example of poor use of timestamped I/O is the serial UART examples. I can run those to about 2mbps but not any faster. Rewriting those with buffered I/O I can easy pass 25mbps.
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

Looked at this a while ago and there are a whole load of different ways to generate PWM using narrow or wide ports, software timers or port timers and different port modes. Here's a table comparing some of the methods - this was a case study for 6 channels of PWM, like you need for 3 phase motors..
pwm methods.PNG
The PWM woody is talking about is the 'port timer' method which is very good...but the buffered/serialised way kster59 is talking about can produce perfect PWM over all duties..

Also, something cool that Kster59 was referring to is the sigma delta 1-bit DAC. Very good for pushing harmonics up in frequency!
http://omino.com/blog/2007/the-1-bit-dac/
You do not have the required permissions to view the files attached to this post.
User avatar
Woody
XCore Addict
Posts: 165
Joined: Wed Feb 10, 2010 2:32 pm

Post by Woody »

kster59 wrote:Previously I used timestamping a lot.

Then I realized timestamping is a blocking call.
This is not strictly true. The code can schedule an output at a specific time e.g.

Code: Select all

 pwmPort @ portTime <: 0;
and the code will not pause, but will continue to execute code and the port will perform the output automatically at the correct time.

However the port can not store more than one scheduled output for the future, So for this code

Code: Select all

 pwmPort @ portTimeA <: 0;
pwmPort @ portTimeB <: 1;
The first line will execute and schedule an output to occur at portTimeA. Since the port can't hold 2 scheduled events, the second code line will pause until the output scheduled in the first line has occured. So at portTimeA, the thread will continue and schedule the output at portTimeB before continuing execution.

Timestamped inputs will always pause until the time specified.
kster59
XCore Addict
Posts: 162
Joined: Thu Dec 31, 2009 8:51 am

Post by kster59 »

Another disadvantage of clocked inputs is that the set up time for a clocked input is also quite large it seems.

I've been experimenting with various ways to optimze timing.

PWM with varying duty cycle is easy and optimized for any resolution with frequency 100mhz/resolution which is pretty good.

I'm still thinking of clever way to do doing varying duty cycle which is different to pwm.

50% pwm with 8 bit resolution would be 11110000
while 50% duty cycle is 10101010

I have now thought of the following pseudo code:
<code>
phsa=count
output=0;
while(1){
for i=1:32{
{sum,carry}=ladd(sum,phsa}
carry <<= 1;
output|=carry;
}
myPort <: output;
}
</code>

this requires 3 + 1 (loop) instructions per bit and 2 instructions every 32bits.

This should work at 50mhz which isn't bad. Maybe it can work at 100mhz if there is some optimization.

Anyone else have any ideas?
User avatar
lilltroll
XCore Expert
Posts: 956
Joined: Fri Dec 11, 2009 3:53 am
Location: Sweden, Eskilstuna

Post by lilltroll »

I might be very confused here but, if you would like to have an optimal solution for an timevariant reference signal, (like music); shouldn't you think about Pulse-density modulation/sigma delta instead of the special case PWM?
http://en.wikipedia.org/wiki/Pulse-density_modulation

(The applet was nice)

Since you recalculate the output sequence all the time I read it as you do not want a "steady state" repeating PWM signal (or a semi-static analog DC value)!?

In another thread here I realized the benefit of PDM prior using PWM when using an timevariant reference signal as used in Direct Stream Digital.

Or is that code actual calculating a PDM with a 32 bit insignal?

To get me less confused, What is the effective analog bandwidth and ENOB (Effective Number of Bits) of the output signal that you want to achieve in the end?
Probably not the most confused programmer anymore on the XCORE forum.