Can someone please explain propeller PLL-based PWM theory to me?
Wossname
Posts: 174
I know the general idea of PWM - a means to approximate an analog signal by toggling an output pin in a carefully governed pattern.
But I am finding it difficult to understand how to correctly write some PASM code to make a Cog generate a variable duty cycle PWM square wave using the built-in PLL counter system.
I've had a good read through of the relevant sections in both the Propeller manual and the datasheet, which both give lengthy descriptions of what all the registers DO, but I'm damned if I can find a real-world example of how to use them in a practical way.
For example, how do you make the Cog output a square wave of 35% duty cycle at 128KHz (rising edge to rising edge)? And then how do you change the duty cycle to 71%? These numbers are arbitrary of course but I can find no clues in the docs to suggest how this is done.
I don't want code, I just need a layman's terms explanation of how to set up the Prop to do this. My brain is starting to hurt and I'm getting datasheet blindness. Is a kind soul willing to write a few paragraphs to help me out or does anyone know a novice-level tutorial that covers all the salient topics?
I've gotten pretty far through the feature set of the Propeller up to now and this is one of the last obscure features (apart from VGA output!!!) I want to understand, and it seems to me that the manual/datasheet could REALLY use a decent set of code examples and a better procedural plain-text description of how to really use the PLL and (in particular) PWM capabilities of the Prop platform.
Any and all help gratefully received, thanks for reading.
Adam.
But I am finding it difficult to understand how to correctly write some PASM code to make a Cog generate a variable duty cycle PWM square wave using the built-in PLL counter system.
I've had a good read through of the relevant sections in both the Propeller manual and the datasheet, which both give lengthy descriptions of what all the registers DO, but I'm damned if I can find a real-world example of how to use them in a practical way.
For example, how do you make the Cog output a square wave of 35% duty cycle at 128KHz (rising edge to rising edge)? And then how do you change the duty cycle to 71%? These numbers are arbitrary of course but I can find no clues in the docs to suggest how this is done.
I don't want code, I just need a layman's terms explanation of how to set up the Prop to do this. My brain is starting to hurt and I'm getting datasheet blindness. Is a kind soul willing to write a few paragraphs to help me out or does anyone know a novice-level tutorial that covers all the salient topics?
I've gotten pretty far through the feature set of the Propeller up to now and this is one of the last obscure features (apart from VGA output!!!) I want to understand, and it seems to me that the manual/datasheet could REALLY use a decent set of code examples and a better procedural plain-text description of how to really use the PLL and (in particular) PWM capabilities of the Prop platform.
Any and all help gratefully received, thanks for reading.
Adam.
Comments
The point is that there is no PWM-mode for the counters. For PWM you also need the COG to do some stuff. It is waiting for one period (1/125kHz in your case) and after that has to update the PHSA register.
With 0% duty you'd choose a value for PHSA with which the counter does not reach $8000_0000 in your period - that value of course depends on the FREQA and PLL settings. With 100% you'd directly set PHSA to $8000_0000.
With the PLL setup and FREQA you can influence the resolution of your PWM.
Hope this helps at least a bit ;o)
here:
http://forums.parallax.com/showthread.php?89958-Propeller-Education-Kit-Labs-Tools-and-Applications
you can download the "Propeller Education Kit Labs" and starting page 121 you will find some info and examples how to use the counters. To get PWM with fixed duty cycle you will need a cog with some code in addition to a counter, see page 157.
Best regards Christof
So I'd have to pick a value for FRQA that over repeated additions, causes that carry bit to toggle at the duty cycle I want?
Is there actually any benefit to using the PLL counter functionality to generate variable duty cycle PWM, when I'd have to make the Cog do such a lot of supporting math? Wouldn't it just be more sensible to do something like...
(pseudocode...)
[PHP]:loop
set pin high
sleep for (35/100) * pwm_frequency //35% duty cycle
set pin low
sleep for (65/100) * pwm_frequency
jmp loop[/PHP]
http://www.parallaxsemiconductor.com/an001
Sigma Delta Analog to Digital Conversion
http://www.parallaxsemiconductor.com/an008
Propeller Education Kit Labs Tools and Applications
http://forums.parallax.com/showthread.php?89958-Propeller-Education-Kit-Labs-Tools-and-Applications
Find the "Transmit Square Wave Frequencies" section.
In the end your approach also eats up a whole COG as a resource, so why not do it with a counter having 12,5ns accuracy?
Actually the code is pretty easy:
1. setup the counter and calculate the clock-ticks once that you have to wait to match your PWM frequency
2. optionally do some other tasks here
3. fetch the duty cycle from HUB-RAM and calculate the next value to be moved to PHSA in 5.
4. waitcnt ' this will also calculate the next cnt-value to wait for
5. update PHSA
6. goto 2
For 3.: 80_000_000 / 125 000 gives you the range you have to subtract from $8000_0000. (0-640 in this case) So, 0 means 100% duty and 640 means 0% duty.
The range itself is the value you have to use in the waitcnt to add up.
A range of 640 means that at 125kHz you have a resolution of a little bit more than 8 bits.
I'll do some experimenting this evening and reappear with my results and code if successful.
My code below is not even remotely sensible and it doesn't work at all. I get random patterns out of it. The hardware I'm using to try this is "known good", so no worries on that score.
Please can someone have a look at this code and tell me how to make it work. I can't even work out the blasted PWM frequency for this thing and I'm beginning to lose my mind with frustration.
I just want it to output ANY duty cycle that isn't 50%.
[PHP]CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
PUB Main
cognew(@PWM_SETUP, 0)
DAT
org 0
PWM_SETUP mov dira, #0
or dira, LED
mov FRQA, #256
mov PHSA, #0
mov CTRA, CTRMODE_VAL 'starts the PLL counter *now*'
mov Time, cnt
add Time, #9
:loop waitcnt Time, Delay
mov PHSA, #0
jmp #:loop
Delay long $8000_0000 / 256
LED long |< 0
CTRMODE_VAL long %0_00100_000_00000000_000000_000_000000
Time res 1[/PHP]
Going rapidly insane.
You have to call it with an address pointing to a variable that holds the duty.
- SPIN arithmetic is signed, meaning $8000_0000 / 256 is not what you really want, try $8000_0000 / -256 or $8000_0000 >> 8
- Delay is setup for a frequency of N Hz, your counter for N/2 Hz, so the output gets reset before it can even change to 1
Regarding frequency calculations check the latest counter AppNote. For NCOs it comes down to frqx/232*clkfreq (page 6). In your example we end up with 256/232*80M = 4.76Hz. Have a look at this and see if you can make it work for you:Andy
Edit: You can also do a PWM in Spin only. This makes it much simpler to pass the values (just use global variables):
First of all I'd move the phsa:=-pulswidth directly behind the waitcnt and maybe let the "-" operation been done by the code that sets pulswidth. My expectation is that due to the many SPIN-operations you have in between waitcnt and and setting the phsa (repeat, -pulswith, :=) you introduce some unlinearity in the low duty-cycle range. At least with the fact that the SPIN-COG always has to synchronize with the HUB-timing you will loose some precision, right?
And of course the SPIN is more limited in the max. PWM-frequency. I'm not sure if the frequency of 128kHz can be achieved by SPIN.
For sure you can not reach 128kHz PWM frequency, I would not go over 20kHz with Spin.
The ideal PWM frequency depends on the application, and the resolution you want.
Andy
I'll put my findings on my blog when I get all this working, I bet there are others out there who are having similar trouble as I did.
It seems obvious now, I didn't even get close to this from the info in the datasheet/manual. Perhaps it's an area that would benefit from some clarification for slow-witted folk like myself.
Anyway, I'm going to adapt this code to generate audio waveforms through an op-amp into a speaker!