Shop OBEX P1 Docs P2 Docs Learn Events
Is there a technique for generating 16bit ~20KHz PWM — Parallax Forums

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

on the Prop?

I don't necessarily need it, just wondering.
«1

Comments

  • evanhevanh Posts: 15,126
    Probably yes. Given that full PWM requires in-sync Cog instructions anyway and spends most of it's time waiting on the next modulation cycle, then it shouldn't be impossible to add a dither into the mix.
  • ErNaErNa Posts: 1,738
    definitely no. 65k*20K = 1.2 Gig
  • evanhevanh Posts: 15,126
    edited 2016-04-27 10:27
    I just had a look at Ahle's SIDcog.spin in the OBEX and was blown away by how much processing he is getting done between samples. It all runs on a single Cog. It's a huge piece of code processing all the various synth features and even threw in a bunch of filters for good measure.

    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.
  • ErNa wrote: »
    definitely no. 65k*20K = 1.2 Gig
    It depends on how fast you need it to respond to duty cycle changes. If you need it to change at the PWM rate (i.e., 20 kHz), then definitely no.

    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
    
  • ErNaErNa Posts: 1,738
    edited 2016-04-27 18:13
    Don't know, where I'm wrong? 20KHz means: 50µs lasts one PWM-Cycle. 64K possible transitional points means approx. 1 ns time resolution.
  • Heater.Heater. Posts: 21,230
    ErNa,

    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.



  • altosackaltosack Posts: 132
    edited 2016-04-27 20:01
    No, your math is not incorrect; it just may be incomplete, depending on the application. If you're wanting it for audio, or another application where it's likely that you change the duty cycle at every PWM cycle, then yes, you can never get more than PWM rate X resolution = hardware clock.

    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.
  • ErNaErNa Posts: 1,738
    You can not have milk and meat. Sure, PWM duty cycle is high/all pulses. So if you count 64k pulses and change number of high by one, you have a 16Bit resolution. Now you can distribute the high/low states into multiple segments, you call 20 kHz PWM. This is a trick that can be applied if your requirement is not precisely 16 Bit resolution at 20 KHz. But this is not the way to determine prime numbers
  • jmgjmg Posts: 15,140
    altosack wrote: »
    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.
    Yes, this sub-frame dithering gives an apparent higher resolution, with both coarse and fine settings, and the fine setting then averages over the frame period.

    altosack wrote: »
    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.
    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.
    altosack wrote: »
    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.

    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.
    altosack wrote: »
    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.
    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.

  • tonyp12tonyp12 Posts: 1,950
    edited 2016-04-27 20:44
    Yes, modulation bit register/var.
    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

  • Mark_TMark_T Posts: 1,981
    edited 2016-04-27 22:24
    Are you looking for noise-shaping techniques as used in audio DACs? You can use 8 bit PWM at 192kHz
    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
  • evanhevanh Posts: 15,126
    Thanks Mark,
    I'm seeing SIDcog's CTRA/B twiddling in there. Hopefully I'll spend some time reading it carefully.
  • jmgjmg Posts: 15,140
    tonyp12 wrote: »
    Yes, modulation bit register/var.
    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

    Did you mean that is in the current P2 DOCs somewhere ?

  • tonyp12tonyp12 Posts: 1,950
    edited 2016-04-28 01:26
    No, but that is how many other mcu get numbers that are in between.
    Like msp430 baudrate settings
    What I meant you would have to do this Modulation in software on a Prop.
  • jmgjmg Posts: 15,140
    tonyp12 wrote: »
    No, but that is how many other mcu get numbers that are in between.
    Like msp430 baudrate settings

    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.

  • If you heterodyne a control signal of 1kHz at a 16-bit resolution with a fixed 20kHz, the resulting PWM will be "up converted" to 20Khz with frequency components of 1kHz, 19kHz, 20kHz, and 21kHz. The caveat is that the control signal AND the fixed 20kHz should be a sine wave. Doing something like this, that 1.3GHz becomes a substantially lower number to work with. ... but all of this depends on the application.

    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.

    PWM.png
  • jmgjmg Posts: 15,140
    Another method for going 'above the clock' resolution, is to mix analog and digital.
    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
  • ErNaErNa Posts: 1,738
    I find we are deep in the sea of digital signal processing. Nice. And we come to the question: what is important in numbers? The numbers itself, that are positioned like fence posts or the fence in between. A solution is to build palisades. But building palisades from whatever numbers always leaves space for another number in between.
  • altosack wrote: »

    In my case, the update cycle is 10/second,

    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:
  • altosackaltosack Posts: 132
    edited 2016-04-28 14:29
    Mickster wrote: »
    altosack wrote: »

    In my case, the update cycle is 10/second,

    So, in a nutshell, you are stuck with this slow update cycle and therefore need to create your own dither
    No, not at all: I chose that update cycle because it is (more than) sufficient for the stability of my controllers. There is a lot of computationally intensive floating point during each update cycle in the main thread, not all of it directly related to setting the next duty cycle(s); it couldn't go too much faster, anyway. The dithering is so it doesn't hunt within that sphere of stability.

    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 !).

  • evanhevanh Posts: 15,126
    edited 2016-04-28 14:46
    Mickster,
    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.
  • No, I fully understand what's being said.

    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.
  • evanhevanh Posts: 15,126
    Umm, I assume you are referring to Derivative oscillations?
  • Let's say, for example, that I have a system that's calibrated such that 100% duty cycle of the 12bit PWM resulted in a motor speed of 4095 RPM. In an open-loop system, the minimum possible non-zero speed would be 1 RPM. Closing the loop and having the PID fed commands by a velocity profiler, pretty much any speed lower than 1 RPM can be achieved as the PID will dither bit 1 accordingly. Of course, this applies across the entire speed range.
  • ErNaErNa Posts: 1,738
    OK, if the drive system is hypothetical, this may apply. Any real system will suffer from so many obstacles, stick slip friction, motor cogging, resolution of encoders, commutation on power stage etc will bring so much uncertainty, you will never touch the pwm of the set value ;-)
  • MicksterMickster Posts: 2,588
    edited 2016-04-28 16:58
    Not hypothetical although the example above is intended to show how speed resolution is not dictated by DAC/PWM resolution.

    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.
  • evanhevanh Posts: 15,126
    Mickster wrote: »
    Let's say, for example, that I have a system that's calibrated such that 100% duty cycle of the 12bit PWM resulted in a motor speed of 4095 RPM. In an open-loop system, the minimum possible non-zero speed would be 1 RPM. Closing the loop and having the PID fed commands by a velocity profiler, pretty much any speed lower than 1 RPM can be achieved as the PID will dither bit 1 accordingly. Of course, this applies across the entire speed range.

    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.
  • It's simply a method whereby position increments are added to the PID command in a time sliced (1KHz in my case) fashion (v=dp/dt).
    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.
  • ErNaErNa Posts: 1,738
    Did you make any assumptions about the precision of your position increments? I do not see a way to measure position to such a grade of precision that your PWM requirements are justified. In other words: creating PWM duty cycle is fully in your hands, nothing else is.
  • I am modelling my Prop development on a high performance motion controller that I currently purchase for machine retrofitting. It actually does have 16bit PWM and can handle 15M quad encoder counts/sec. I just want to see how close I can get, using the Prop....just for giggles, mind.
Sign In or Register to comment.