PWM Capability
Hi,
So I have just stumbled across the P8X32 processor due to this site infact:
http://hackaday.com/2011/03/28/open-source-wireless-mesh-networking-energy-meter/
I digress, I have a possible project in mind, a fan controller. I have read the datasheet but I'm not sure I'm reading it right. I need to know how many PWM channels I can get, is it two per core ? Also how fast frequency wise can I drive them (more important), and what sort of resolution are they (less important) ?
They are just going to be used to drive buck converters, I would like to keep the component footprints small so higher frequency PWM is preferred.
Thanks.
--
Cam
So I have just stumbled across the P8X32 processor due to this site infact:
http://hackaday.com/2011/03/28/open-source-wireless-mesh-networking-energy-meter/
I digress, I have a possible project in mind, a fan controller. I have read the datasheet but I'm not sure I'm reading it right. I need to know how many PWM channels I can get, is it two per core ? Also how fast frequency wise can I drive them (more important), and what sort of resolution are they (less important) ?
They are just going to be used to drive buck converters, I would like to keep the component footprints small so higher frequency PWM is preferred.
Thanks.
--
Cam
Comments
Browse the OBEX ?
This one looks useful ?
http://obex.parallax.com/objects/359/
PWMx8 : Provides up to 8 channels of fast PWM output per cog, generated by the Propeller's video circuitry.
From reading the code:
'' This Propeller object provides up to eight leading-edge-aligned PWM channels '' with resolution determined by a defined constant. The maximum PWM frequency '' is the lesser of: '' '' clkfreq / (13.5 * resolution) and 20.648881 / resolution MHz
So my reckoning using the max clock of 80Mhz
80,000,000/(13.5 * 256) = 23.148 Khz
20.648881 / 256 = 0.0865 Mhz = 86.5 Khz
So the lesser is 23.148Khz. Shame really liked the idea of 8 PWMs per core.
Cam.
Then you can use the counters and a little piece of code, which gives you 2 PWMs per COG. 200kHz is 400 clock-cycles, which is 100 PASM instructions, so a COG dedicated to PWM-generation can not do much more. Resolution would be 400.
Another possibility is to use 2 counters, 2 pins per PWM and an external XOR to have one PWM per COG. Difference is, that this would allow to run other code while the counters generate the PWM autonomously. Only changing the pulse width needs some caretaking code.
Expanding on this idea, it should be possible to have a shared (master) phase, from half a cog, and then other cogs can phase adjust from that, to allow 2 PWM per COG.
Sync could be from either the Master Pin, or via CNT, which everyone can read.
Then 8 channels would use be 4 1/2 COGs timers, and a whole chip could manage 15 channels..
So the master clock gives the frequency ? and the other clocks give the period ?
Very close, you do need an external XOR per chan, as mentioned in #4, but now one timer is fixed as a master Freq, and that never changes.
The other active COGs change the Phase, relative to that master, and that phase sets the PWM %, which can have any value 0..100%
Prop counters are really 32 bit adders.
There is no per-cycle maths needed, so you can resolve to what the timers can, (12.5ns), but to avoid jitter issues it would be best to stay Binary on the master clock. ( ie 312.500KHz @ 256 or 625.000 KHz @ 128 )
SW intervention is needed only on a change in duty cycle.
Prop 2 fixes that, with a true PWM mode, but a Prop 1 should allow full-range, 1 clk granularity on up to 15 channels (with an external HCT86 for every 4 chans )
So, if you need 2 different frequencies you need 2 masters ... removing counters from the PWM pool ;o)
The nice thing with the masters is, that these never need any maintenance, just fire them up!
16-2 => 14 PWM-channels @ 2 different frequencies
16-3 => 13 PWM-channels @ 3 different frequencies
though the 16 counters does have PDensityM (refered to as Duty), a high level is fixed at 12.5ns so only useful for RC circuits as it's to fast for much anything else.
Using external gates you could use a few NCO (square wave) in different syncs to create the Pulse-WIDTH-Modified signal.
Or simple use software as the Prop is fast and time-deterministic.
You can probably tolerate non phase aligned PWMs if you are driving FANs, so a variant of the example here (from page 7, NCO mode, Pin = PHSA[32] )
http://www.parallaxsemiconductor.com/an001
:loop ' AN001 rdlong value, par 'get an up to date pulse width from main memory waitcnt time, period 'wait until next period = 12.5ns granular neg phsa, value 'back up phsa so that it trips "value" cycles from now Pin = PHSA[32] jmp #:loop 'loop for next cycle
becomes something like
:loop ' expanded AN001 rdlong PwmVal_Ch1, RegAdrCh1 'get an up to date pulse width from main memory rdlong PwmVal_Ch2, RegAdrCh2 'get an up to date pulse width from main memory waitcnt time, period 'wait until next period = 12.5ns granular , and granular from here neg phsa, PwmVal_Ch1 'back up phsa so that it trips "value" cycles from now, Pin = PHSA[32] neg phsb, PwmVal_Ch2 'back up phsb so that it trips "value" cycles from now, Pin = PHSB[32] jmp #:loop 'loop for next cycle
This reloads the adder output once every Period, and the Pin is the Adder MSB, so the reload value needs to be close to a MSB edge.
For Period values of 256, we can say :
a) A reload value of less than -256, will give PHSx[32] always hi, so is 100% Hi
b) A reload value of less than 2^31-256, will give PHSx[32] always lo, so is 100% Lo
You can reload close to either edge, for PWM; the example above reloads near the falling edge
Within the reload range, the narrowest finite pulse width, will be a single clock Hi, or Low.
Loading -255 will be 255 clocks Hi, and overflow right before the WAITCNT+NEG reloads a new -265 == single clk low.
Loading -1 will output one clk-width high, and low for 255, until it reloads again.
This makes the rising edge aligned with the NEG opcode, thus PHSB will be 4 clks later than PHSA.
Both PHSx edges are also phased to the WAITCNT, so some housekeeping to do more than just CNT+Period, could start each COG with a more predictable phase between COGs
In some systems, like LED drivers, such edge miss-align is considered a benefit as it avoids large current steps.
On other designs, where you can expect a narrow-ish PWM range, (as in multi phase SMPS), then a deliberate coarse phase shift can reduce ripple current levels.
eg if you expect your FAN PWM to be ~25% when stable, you can phase 4 cogs to be each ~25% shifted for best current profiles over 8 fans.
The last post by "jmg" is still sinking in.
Meanwhile can someone further explain this:
I don't really follow, the shortest high level duration is fixed @ 12.5ns ??
That is a different timer mode, less suited to Power Drivers, as it has more edges. - so it is not classic PWM.
Again pardon my ignorance, for that's exactly what it is. I'm really just trying to evaluate if getting a development board is worth it for this project. Some final questions to condense my knowledge:
Thanks so far for all the help. I can see now why this isn't a straight up spec for these devices, they seem more like an FPGA multi core device than conventional processor like.
I'm not sure why you want different frequencies for Fan PWM ?
Yes. Yes, with external xor added case In Master mode case, it would be best to stick with binary divides of 80MHz. At least to start with.
Yes, for the external xor case.
Yes, which was why I suggested the modified AN001, which does not need XORs . (or any master )
I think you can tolerate phase not-aligned across PWM drives as they go to separate FANS, and in some cases that is a plus.
I noticed that upthread you said that 200 KHz is not achievable with AVRs and PICs; for your required resolution of 256 (128 ?), it certainly is. I'm not sure if you need a through-hole chip or if you need the PWM outputs to be phased; here's a list of the capabilities of several possibilities:
PWM MHz KHz @ 256 PWM outputs Phased outputs Package P8x32A (6.25 MHz Xtal) 100 391 14 14 DIP40 dsPIC33FJ16GS502* 960/40 3750/156 10 (8/2) 8/2 SPDIP28 dsPIC33FJ64GS406 960/40 3750/156 16 (12/4) 12/4 TQFP64 (0.5mm) dsPIC33FJ64MC202 80/40 313/156 12 (8/4) 4 SPDIP28 dsPIC33EP64MC202 140/70 547/273 10 (6/4) 10 SPDIP28 dsPIC33EP512MC506 140/70 547/273 24 (8/16) 8/16 TQFP64 (0.5mm) ATxmega64A4U 256 1000 18 0 TQFP44 (0.8mm) *If you need 12 phased outputs in a through-hole package, you can use multiple of these chips with the PWM synchronized to each other.
So, the propeller has the highest number of phased PWM outputs in a DIP package, which is one of the reasons I'm using it.
Hm... doesn't look like there is much room to speed up the PWM8 object, but I don't see anything restricting the code to the current PWM pattern. I.e. It should work just fine outputting a dithered sequence of 16 4-bit pulses instead of one 8-bit pulse.
Lawson
It's computer type fans I am after controlling so the two frequencies allow me to control 4 wire fans, which according to the specs need around 22-25Khz PWM signal and others can be two or three wire fans with my own buck convertor running faster to allow for small components.
Yes none aligned will be fine, I understood that about the modified AN001 example, I didn't grasp that it eliminated XOR's or a master clock. I really am green as grass when it comes to these propellor IC's, it isn't sinking in fast, I think I have established that what I am after is possible so a dev board should really be my next step. Much easier to learn by doing.
If you will permit me another dumb question. Do I deduce from your comment the original AN001 code you posted does need a master clock and an external XOR ?
Sorry I always forget about dsPICs due to not using them much/at all. I did know about the ATXmegas, I think I just got a little excited about this whole different and exciting sounding world that has obviously been around a while but slipped by me and opened my mouth early, sorry.
Are you talking about what is sometimes called Bit Angle Modulation (BAM) or Binary Code Modulation ?
Hmmm, ok thanks, you could be right in that I am probably being greedy with resolution, time for trying things out I think.
Here you can find a wave player object using real 8-bit NCO PWM @ about 300 kHz - maybe this can help
The Counter has 32 bit. So, the higher the frequency, the lower the resolution and vice versa.
8bit resolution gives you a frequency of 80_000_000 / 256 = 312500 Hz
Overclocking to 100_000_000 gives you 390625 Hz
7bit @ 80MHz allows 625000 Hz
The AN001 method allows you to vary frequencies by COG pair, but of course the housekeeping is a little more complex, as the loaded Time-offset, is in clocks, not %.
No, AN001 does not need XOR, in any form : it uses a skew on the Counter-Adder-edge, and a reload-every-cycle.
My modify was simply to add a second timer load for dual PWM & note the phase skew; The code flow is identical.
There are examples in OBEX based on AN001, so fire those up first.
Think that pretty much covers the PWM ability of these chips, we have had software PWM, hardware PWM (using XOR) and a mixture of both hardware and software in the example last talked about.
Thanks one and all !
Yes, because you are pushing the switching frequency up, some hardware support is needed.
There is a lot of code space spare, in the 1 Chan AN001, it has just 4 lines of active code, and in 2 Chan variant, that is just 6 lines.
At PWM rate of /128, and (nominal) 4 clks per opcode, or 8..23 for Hub access (RDLONG) you have a time budget of up to 32 base opcode times. ( at /256, this grows to 64 base opcode times)
You have plenty of code space per COG, and with care, you have enough time-space too, to implement the ideas mentioned like dithering.
Google Rate Multipliers to get an idea on bit-density ( ie when to do a dither), and in a Prop, you can naturally dither fractions to N/32, N/16, N/8, N/4 etc, and at 80Mhz, and /128, the dither frequency is 19.53125 KHz, so still inaudible. ( and of very low energy, as it is LSB dither).
N/32 dither would allow you to have 12 bit (averaged) PWM precision on a Switching Base of 625KHz
Dither itself would cost as low as 2 opcodes excluding the fetch of the Dither itself.
A ROR Dither,#1 gets a new LSB choice bit into CY, and then CY is used to load either PWM_Value, or PWM_Value+1 so that you load alternating choice of two PWM values. Over the 32 choices, this averages to give another 5 bits of PWM precision.
For less dither choices, simple repeat the pattern in the rotated dither register.
Loading (update) of the Dither value could need careful interleave of RDLONGs, but as it is a LSB decision, the phase of loading is likely to be tolerated. Certainly a motor will not notice any effect
AN001 also does not pass Period as a Parameter, so the most-flexible high precision PWM would pass 5 values :
Period (common to COG ) - Sets Period of PWM, in 12.5ns steps
Base_PWM_A - sets base HI time in 12.5ns steps
Base_PWM_B - sets base HI time in 12.5ns steps
Fract_PWM_A - sets N/32 fractional dither for ChA - 32 bits as pattern, 5 bits as index
Fract_PWM_B - sets N/32 fractional dither for ChB - 32 bits as pattern, 5 bits as index
Simple RDLONG of all of those will be time-costly, so an optimal design may move to pack or index them.
So a RDLONG loads data from the hubs memory and it costs 8 to 23 clock ticks ? which is 2 to 6 opcode ticks ?
Yes, see page 24 of Propeller Manual v1.2.
The worst case is 23 cy and a slot exists every 16, so a series of RDLONGs will be 23+16+16+16 , and of the following RDLONGS, each needs 8cy for itself, which means two 'free' Opcode slots are available (or it simply waits). 8+4+4 = 16
WAITCNT is clk-granular, so you cannot place RDLONG after WAITCNT
Those free opcode slots mean you might choose to pack parameter passing.
* Pack the Params, to reduce RDLONGs needed
* two opcodes are placed between RDLONGs, time-free.
* Pass Dither as an index, as this saves code in Main, and COG memory is 'free' on the PWM slaves.
It also allows param packing.
' Min Time: Host has to know the Array_32_Long_Ch1,Array_32_Long_Ch2 base values ' Host then adds the fraction 0..31, to those base values & passes 9 bits ' -or- lines to add PwmVal_Ch1,#Array_32_Long_Ch1 can be added here ' at a cost of +4 +4 (no carry into upper 16 bits) ; NOT INCLUDED - start-up code :loop ' expanded AN001 - Dual PWM and N/32 Dither and Full param rdlong PwmVal_Ch1, RegAdrCh1 '+23 8..23 Pack Time(16b) + Fraction(5b.IDX + 9b.Reg Base) ' optional add PwmVal_Ch1,#Array_32_Long_Ch1 movd RegAdr_ROR_A, PwmVal_Ch1 ' (4) patch index into Ch1 Dither table into ROR opcode ror PwmVal_Ch1,#16 ' (4) Upper 16 bits are PWM_Time rdlong PwmVal_Ch2, RegAdrCh2 '+16 ' optional add PwmVal_Ch2,#Array_32_Long_Ch2 movd RegAdr_ROR_B, PwmVal_Ch2 ' (4) patch index into Ch2 Dither table into ROR opcode ror PwmVal_Ch2,#16 ' (4) Upper 16 bits are PWM_Time rdlong PwmVal_Per, RegAdrCh2 '+16 waitcnt time, PwmVal_Per '+6 +nC wait until next period = 12.5ns granular , and granular from here :RegAdr_ROR_A ror RegAdr_ROR_A,#1 '+4 CY = dither decision,A '.. tbd code to load PHSA with PwmVal_Ch1, or PwmVal_Ch1+1, based on CY :RegAdr_ROR_A ror RegAdr_ROR_B,#1 '+4 CY = dither decision,B '.. tbd code to load PHSB with PwmVal_Ch2, or PwmVal_Ch2+1, based on CY jmp #:loop '+4 loop for next cycle ' = 73, at /128 have MAX of 128, 55 cy budget. ' { 81 with Add index offset in COG, 47 cy budget } :Array_32_Long_Ch1 ' 32 Long array, register Constant Patterns for Ch1, :Array_32_Long_Ch2 ' 32 Long array, register Constant Patterns for Ch2, ' These rotate in-place, for speed, so cannot share
and with one version of CY handling, and the index add done locally included, as we still have time-budget (I think ?)
' Min Time: Host has to know the Array_32_Long_Ch1,Array_32_Long_Ch2 base values ' Host then adds the fraction 0..31, to those base values & passes 9 bits ' -or- lines to add PwmVal_Ch1,#Array_32_Long_Ch1 can be added here (shown) ' at a cost of +4 +4 (no carry into upper 16 bits) :loop ' expanded AN001 rdlong PwmVal_Ch1, RegAdrCh1 '+23 8..23 Pack Time(16b) + Fraction(5b.IDX ) add PwmVal_Ch1,#Ary_32L_Ch1 ' (4) ' optional, if cycles tight ( Add 9b reg base ) movd RegAdr_ROR_A, PwmVal_Ch1 ' (4) free time pass index into A_Jitter table into opcode rdlong PwmVal_Ch2, RegAdrCh2 '+16 (8) <- keep MOD16 from prev RDLONG add PwmVal_Ch2,#Ary_32L_Ch2 ' (4) optional, if cycles tight ( Add 9b reg base ) movd RegAdr_ROR_B, PwmVal_Ch2 ' (4) free time pass index into A_Jitter table into opcode rdlong PwmVal_Per, RegAdrCh2 '+16 (8) <- keep MOD16 from prev RDLONG sar PwmVal_Ch2,#16 '+4 Upper 16 bits are PWM_Time, ror or sar sar PwmVal_Ch1,#16 '+4 Upper 16 bits are PWM_Time, ror or sar waitcnt time, PwmVal_Per '+6 +nC wait until next period = 12.5ns granular , and granular from here :RegAdr_ROR_A ror RegAdr_ROR_A,#1 '+4 CY = new dither decision, A addx PwmVal_Ch1,#0 '+4 Add 1 based on CY Dither, reloads every PWM neg PHSA, PwmVal_Ch1 '+4 back up phsa so that it trips "value" cycles from now :RegAdr_ROR_B ror RegAdr_ROR_B,#1 '+4 CY = new dither decision,B addx PwmVal_Ch2,#0 '+4 Add 1 based on CY Dither, reloads every PWM neg PHSB, PwmVal_Ch2 '+4 back up phsb so that it trips "value" cycles from now jmp #:loop '+4 loop for next cycle ' 97 with Add index offset in COG, 31 cy budget :Ary_32L_Ch1 ' 32 Long array, register Constant Dither Patterns for Ch1, :Ary_32L_Ch2 ' 32 Long array, register Constant Dither Patterns for Ch2, ' These registers rotate in-place, for speed, so cannot share, so simply use 2 tables.
There will now be a fixed and stable 12cy skew between PWM edges, Ch1 and Ch2
Size is 17 opcodes for loop, + 32L + 32L for patterns, and whatever Init code is needed.
Result should be 2 chans of 12 bits of average PWM, (7b+5b) with 625KHz Freq
Edit : option of sar for ror, to allow sign extend and the !MSB passes into the Pin, for values outside of period from a count boundary, the pin is static dc, H or L.
An idea of my own,,,, as the update of my PWM duty does not need to be every cycle things could be eased by alternating the between RDLONGing new Ch1 and Ch2 values, you think ? Although somewhat complicated by the dithering, but even then we could only refresh every 16th time.