Is there a technique for generating 16bit ~20KHz PWM

in Propeller 1
on the Prop?
I don't necessarily need it, just wondering.
I don't necessarily need it, just wondering.
Comments
He's getting about 11.5 bits of resolution, 80MHz/31kHz, without dithering but for just a basic sample player it wouldn't be hard to notch on another 4-bits worth with dithering.
EDIT: He doesn't seem to be doing an ordinary PWM but is staying in sync exactly the same way as the typical PWM examples.
However, if you are OK with, say, 1 ms of latency, it can be done. Below is an object that I use in propgcc that is capable of 16 bits at 390 kHz, at a 100 MHz clock; its latency is < 1 ms. It's a little overcommented, and it's complicated by being able to choose 1 or 2 PWM outputs and software stepping or not, but the table in the header section lets you know what's possible.
/******************************************************************************* Module: PWM2, v.0.1, 16 July 2012 Target: Parallax Propeller P8X32A, prop-gcc, memory model = cog Purpose: System Clock Speed PWM driver with (2) outputs and software stepping to increase the effective PWM resolution to 25 GHz / 40 ps @ 100 MHz ******************************************************************************** On startup, PAR is a pointer to the PWM mailbox: struct PWMmbx { uint32_t CycleStart, uint16_t *dutyA_ptr; uint16_t *dutyB_ptr; uint8_t pinA; uint8_t pinB; } For 2 outputs, #define PWM_B; if you don't, the code will be a little smaller. If you #define PWM_STEP, software stepping of the remaining bits of the 16-bit duty cycle is implemented to increase the duty cycle resolution while still having a high PWM rate (short PWM period, set by what I call the PWM native resolution). For this to work, both the PWM duty cycle resolution and the PWM native resolution should be a power of 2. Note that the response time to duty cycle changes increases as the effective resolution of software stepping increases, as follows (NATIVE_PWM_RES = 256): 80 MHz SysClk/313 kHz PWM rate 100 MHz SysClk/390 kHz PWM rate STEPPING_BITS EFFECTIVE_RES RESPONSE_TIME EFFECTIVE_RES RESPONSE_TIME 0 80 MHz / 13 ns 3.2 us 100 MHz / 10 ns 2.6 us 1 160 MHz / 6.3 ns 6.4 us 200 MHz / 5 ns 5.1 us 2 320 MHz / 3.1 ns 12.8 us 400 MHz / 2.5 ns 10.2 us 3 640 MHz / 1.6 ns 25.6 us 800 MHz / 1.3 ns 20.5 us 4 1.3 GHz / 781 ps 51.2 us 1.6 GHz / 625 ps 41.0 us 5 2.6 GHz / 390 ps 102 us 3.2 GHz / 313 ps 81.9 us 6 5.1 GHz / 195 ps 205 us 6.4 GHz / 156 ps 164 us 7 10 GHz / 98 ps 410 us 13 GHz / 78 ps 328 us 8 20 GHz / 49 ps 819 us 25 GHz / 39 ps 655 us The following symbols should be defined in a header file called by 'main.h': PWM_RES // #ifdef PWM_STEP, it should be a power of 2 PWM_B (optional) PWM_STEP (optional) #ifdef PWM_STEP PWM_NATIVE_RES // SysClocks (min = 256 --> 390 kHz @ 100MHz) PWM_STEP_BITS // = log2(PWM_RES) - log2(NATIVE_RES) #endif // e.g., if PWM_RES = 65536 and NATIVE_RES = 256, // PWM_STEP_BITS = log2(65536) - log2(256) = 8 *******************************************************************************/ #define __ASSEMBLY__ #include "main.h" #define PeriodStart_ptr PAR #define Mbx_ptr PAR #define CTR_PWM_MODE %0_00100_000 #define OFFSET_DUTYA_PTR 4 #define OFFSET_DUTYB_PTR 8 #define OFFSET_PINA 12 #define OFFSET_PINB 13 .org 0 .pasm PWM_COG pwm_driver: rdlong periodStart, PeriodStart_ptr mov dutyA_ptr_ptr, Mbx_ptr add dutyA_ptr_ptr, #OFFSET_DUTYA_PTR rdlong dutyA_ptr, dutyA_ptr_ptr mov ptr1, Mbx_ptr add ptr1, #OFFSET_PINA // point to PWMMBOX.pinA rdbyte pinA, ptr1 movs CTRA, pinA mov r1, #1 shl r1, pinA or DIRA, r1 // Set data direction register movi CTRA, #CTR_PWM_MODE mov FRQA, #1 // Set Frequency to System Clock #ifdef PWM_B mov dutyB_ptr_ptr, Mbx_ptr add dutyB_ptr_ptr, #OFFSET_DUTYB_PTR // ptr to PWMmbx.*dutyB rdlong dutyB_ptr, dutyB_ptr_ptr add ptr1, #(OFFSET_PINB - OFFSET_PINA) // ptr to PWMmbx.pinB rdbyte pinB, ptr1 movs CTRB, pinB mov r1, #1 shl r1, pinB or DIRA, r1 movi CTRB, #CTR_PWM_MODE mov FRQB, #1 #endif #ifdef PWM_B // system counter add periodStart, halfPeriod #else add periodStart, period #endif pwm_loop: #ifdef PWM_STEP rdword unsteppedDutyA, dutyA_ptr mov carryA, unsteppedDutyA shr unsteppedDutyA, #PWM_STEP_BITS shl carryA, #(32 - PWM_STEP_BITS) mov stepCtr, stepCount #else rdword dutyA, dutyA_ptr #endif #ifdef PWM_B #ifdef PWM_STEP rdword unsteppedDutyB, dutyB_ptr mov carryB, unsteppedDutyB shr unsteppedDutyB, #PWM_STEP_BITS shl carryB, #(32 - PWM_STEP_BITS) #else rdword dutyB, dutyB_ptr #endif #endif #ifdef PWM_STEP native_pwm_loop: mov dutyA, unsteppedDutyA add accumulatorA, carryA wc if_c add dutyA, #1 #endif cmp dutyA, #0 wz // check for 0 #ifdef PWM_B waitcnt periodStart, halfPeriod // Start PWMB out of phase #else // from PWMA to reduce noise waitcnt periodStart, period #endif if_z mov PHSA, #0 // Make sure it stays off if_nz neg PHSA, dutyA #ifdef PWM_B #ifdef PWM_STEP mov dutyB, unsteppedDutyB add accumulatorB, carryB wc if_c add dutyB, #1 #endif cmp dutyB, #0 wz waitcnt periodStart, halfPeriod if_z mov PHSB, #0 // Make sure it stays off if_nz neg PHSB, dutyB #endif #ifdef PWM_STEP djnz stepCtr, #native_pwm_loop #endif jmp #pwm_loop #ifdef PWM_B #ifdef PWM_STEP halfPeriod: .long PWM_NATIVE_RES / 2 #else halfPeriod: .long PWM_RES / 2 #endif #else #ifdef PWM_STEP period: .long PWM_NATIVE_RES #else period: .long PWM_RES #endif #endif #ifdef PWM_STEP stepCount: .long 1 << PWM_STEP_BITS #endif periodStart: .res 1 dutyA_ptr_ptr: dutyA_ptr: .res 1 dutyB_ptr_ptr: dutyB_ptr: .res 1 dutyA: .res 1 dutyB: .res 1 pinA: .res 1 pinB: .res 1 stepCtr: .res 1 unsteppedDutyA: .res 1 accumulatorA: .res 1 carryA: .res 1 unsteppedDutyB: .res 1 accumulatorB: .res 1 carryB: .res 1 r1: .res 1 ptr1: .res 1
Yeah. I did not get it either. PWM at 20KHz to 12 bit resolution looks like 1.3GHz to me. 0.76ns resolution.
How you do that on a Prop with an 80MHz or 100MHz clock, 12ns to 10ns resolution, I don't understand.
I put the same question to a field service engineer from MPS who gave me a Class D stereo audio amplifier evaluation board, 16 bits at 44.1KHz. I did not get an answer that I understood.
However, if you don't need to change the duty cycle rapidly, such as in SMPS (I use this for energy conversion in solar & wind controllers and battery chargers), you can dither the duty cycle by twiddling the hardware LSB. The effective duty cycle is then averaged for however many cycles you dither it for. In my case, I use 8-bit hardware resolution, and average it over 256 cycles to get a total of 16 bits of effective resolution. So, 100 MHz / 256 = 390 kHz PWM rate, but I need to let it average for 256 cycles, so I can only change the duty cycle every 390 kHz / 256 ==> 0.655 ms.
Why not just use 16-bit hardware resolution ? Well, the numbers give us 100 MHz / 65536 = 1.53 kHz, which is not only in the audible range, but extremely inefficient for power conversion; the inductors and capacitors would be huge. In the case of power conversion, the inductors and capacitors let you get away with varying the duty cycle by one bit each PWM cycle; it may hunt a bit at a high frequency within the circuit, but the correct current is transferred each averaged cycle, and yes, that accuracy does matter for certain applications.
In my case, the update cycle is 10/second, and if I didn't do the dithering, you would see the voltage moving around a bit (I like to display it at 0.01 V resolution; at 0.1 V, it wouldn't matter), instead of being rock solid. Also, the PID tuning would take more time; this is a pretty simple way to circumvent that.
The prop is ideal for doing things like this, especially since you have to use software for PWM anyway; there's plenty of time in a PWM cycle to do a little bit twiddling, even at 390 kHz. In a microcontroller with hardware PWM, it's not possible unless you put the dithering algorithm in an interrupt set to fire at the PWM rate, which can easily use 30% of your CPU time at 390 kHz, and then what do you do if you want to run three SMPSs from one chip like I do with the prop ? To get high PWM rates and resolution for SMPS, MicroChip runs their dsPIC GS chip PWM at 1 GHz; TI uses something called microstepping, which is rather a black box that few understand, and their chips and tools are not really accessible to a hobbyist. The way I read it, is that they have the market, so there's not a big push to put hardware dithering (or some other competing solution) into other microcontrollers.
When we were talking about PWM for the P2 awhile back, I brought it up, but it was lost in the discussion, and no one else seemed to be interested. Curiously, I may have to keep using the P1 for this reason, but I bet there will be some trick in the P2's PWM that will allow it to work. Time will tell.
Well, you can change the coarse resolution at 390KHz, but the fine resolution has a frame-period,
The dither-modulation can vary.
eg A simple compare, or rate-multiplier modulated LSB.
An advantage of rate-multiplier is the fine resolution modulation noise spectrum is pushed higher, but it does take a few lines of code to implement.
Still another approach to 'more resolution', is to allow frequency to vary, and use the fractional ratio Duty = hiT/(HiT+LoT)
eg consider a 100 period 50% setting, if you change the Period to 101, PWM is 49.504%, or 99 can give 50.505%
This has no sub-harmonic frame effects, but it does frequency modulate the main PWM.
More vendors are moving to allow higher clocks for their Timers, and the rush to 'single clock' MCUs has resulted in some compromises.
Flash speed is now low, compared with what silicon can run at, but MCU clocks used to have an overall ceiling set by the Core opcode fetch.
The P2 is better optimized, it has 2 Clks per opcode, but can run timers faster as a result, with 100MHz+ targeted.
You should still be able to SW dither P2 PWMs just fine, and you can also use HiT and LoT modulation.
There was talk about feeding a NCO-Duty mode signal into an adjacent PinCell PWM LSB, but I'm not sure where that got to.
If a bit is set it uses the pwm value next step up, so you keep alternating between two coarser (8bit) pwm values
So to get 50.25% you set Mod to: %00100010
and get (nearly) 24bit audio. Its indistinguishable from magic, but requires a DSP for higher order filters for
best performance - however there is a double-integration filter that gives you most of the advantage
of noise-shaping from even lowly processors.
Perhaps this thread is relevant? forums.parallax.com/discussion/148280/prop-sound-quality-question
I'm seeing SIDcog's CTRA/B twiddling in there. Hopefully I'll spend some time reading it carefully.
Did you mean that is in the current P2 DOCs somewhere ?
Like msp430 baudrate settings
What I meant you would have to do this Modulation in software on a Prop.
Ah OK.
All I can see in P2 DOCs is a mention of dither only on DAC modes, but no dither choices on digital PWM modes ?
You could always update the setpoint on every PWM period, via SW to do normal software dither.
Note: The Simulator only goes to 25kHz, but in order to "see" the wave patterns I used 100Hz and 2kHz for my control and fixed frequency in the attached image.
This may be possible in P2, where you need a PWM feeding a CAP + Current source, and a Comparator + DAC - coarse time is set by PWM, and the fine time is the ramp portion chosen by the DAC.
A 15ns linear ramp, needs only 4b of DAC to break 1ns LSB.
I think the P2 has all of these, in the expected final form :
* PWM
* Current source option
* Comparator
* DAC
So, in a nutshell, you are stuck with this slow update cycle and therefore need to create your own dither?
Chalk another one up for the awesome Propeller, eh?
:cool:
An analogy might be a racing car that's being driven below its limit, but the steering wheel jiggles. The dithering gets rid of the jiggle, which is mostly irrelevant to the stability of the car, but is just unsightly, and slightly distracting to the driver (the PID, in this case).
I think I should bow out of this thread because everyone else is talking about what seems to be audio applications, and we are talking past each other. You didn't specify what the application was, so I answered your question with "Yes !" and presented my application where I attain 390 kHz and effective 16-bit resolution on the prop with the caveat that it is not applicable to all PWM cases (or, apparently, anybody's !).
What's being said about dithering is it introduces a dynamic precision. Where the lower frequencies will gain a higher resolution than the higher frequencies can achieve.
So, although a 10kHz tone from 20kHz 8-bit DAC will never be better than 8-bit, a simultaneous 5kHz tone on the same DAC can be 9-bit effective ... I think.
If you are wanting to do, say, servo control then the typical profile frequencies are very often much much lower than the sample rate.
In my case, I also have a PID but it's running at like 8KHz so although I might only have a 12bit resolution PWM, I am not limited to 12bits of speed resolution (motor), thanks to the naturally occurring dither created by the PID.
In many, if not most cases, industrial servo-drives/motors have their own internal control loops (velocity/torque). The command signal, in these cases, is merely a velocity command reference.
Ah, it's not the PID nor even the whole servo loop. Rather it's the method of profile generation, there is a good dither that comes from a lossless profile generator. That doesn't always make it to the DAC but, agreed, there is something there.
To achieve an axis velocity of 1m/s, the PID command is incremented by 1mm at 1ms intervals. From there the PID does it's thing which might involve wagging a bit (dithering) from one update to the next to maintain that velocity.