PWM period and duty cycle configuration

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
KeLa_4479261
Level 4
Level 4
First like received

Good Evening:

I have been using the PWM component with the PSOC 5LP prototyping kit.  I put in the period and then based on the number of cycles at 64Mhz, I put in the duty cycle.  My system is running at 64Mhz with an XTAL crystal.  After adjusting the period to compensate for GPIO toggles and overhead processing I remove the development kit from the USB and attach it to my breadboard.

I have not been able to determine why the duty cycle, which appeared good on the PC, is off by usually 4 microseconds. At 64Mhz that is a lot of cycles that have either been added or subtracted from the PWM component.  The period stays constant by the Duty changes considerably.

Does anyone know how to correct this issue.

Please and thanks,

Ken.

0 Likes
1 Solution

Hello Motoo:

That is good news, I didn't think I was the only user that had tried to do this.

I think we should mark this as the correct answer and close.

Regards,

Ken L.

View solution in original post

24 Replies
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

> My system is running at 64Mhz with an XTAL crystal. 

I hope that you are using 8MHz XTAL and PLL to 64MHz, correct?

As in the datasheet of CY8C58LP, the external crystal supposed to be between 4MHz and 25MHz.

000-Datasheet.JPG

Now assuming that your XTAL is fine.

Then another thing I have often encountered is the period.

If we need a 1000 cycle period, we must set the period to 999 (0 - 999 = 1000).

So I wonder if the difference you are seeing is because of this.

> The period stays constant by the Duty changes considerably.

This does not sound right.

But I have no idea except the off by one problem above.

BTW, just another thing I often care about is...

Are you stopping PWM before you set period?

We can change duty during the PWM is running

but changing period requires PWM to be stopped.

So Please stop the PWM, write period (and/or duty), then enable (or restart) the PWM.

moto

0 Likes

Hello Motoo:

The XTAL cyrstal is 8Mhz as per the allowed crystals.

I am enclosing a snapshot of the PWM_1 block from the design, I am also using the one shot multi tirgger mode as you showed me before.

The pwm output is driving the GPIO pin.  (picture included).

The same PWM_1 block gives me two different duty cycles.

When I am connected to the PC and capture the analog signal from LED1, duty cycle is:12.0203%

When I am connected to my target and capture the analog signal form LED1, duty cycle is: 12.7310%

Your help is appreciated, thanks,

Ken.PWM_1_configure_page.JPGPWM_1_advanced_page.JPGCapture_wiring_block.JPG

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ken-san,

So you trigger PWM_1 from PSoC firmware via Control_Reg_1_Write(), right?

Then time from isr_1 to Control_Reg_1_Write() may not be exactly same.

To test it, can you connect a GPIO output from Control_Reg_1?

I think that the duty after the trigger would be the same,

but delay after the period to the next trigger may be different.

Although I can not remember why PWM_1 needed to be triggered by the Control_Reg_1,

if you can, could you also try to configure PWM_1 mode as continuous and measure if the period/duty changes?

If the duty and period will be stable when configured as continuous mode, then the clock and PWM will be fine.

(Which I hope so)

Best Regards,

14-Mar-2020

Motoo Tanaka

0 Likes

Helllo Motoo:

PWM_1 and PWM_2 are used to make a custom waveform that in turns on lights.  The timings, hence the XTAL 8Mhz, are so that I can get these times to the decimal microsecond.

I am using two different blocks to generate the following:

1) 60.5 + 432.2

2) 62.7 + 429.7

All times need to be accurate to the first decimal microsecond.  Because of this I use the control register to trigger the PWM as required.

Would a timer be a better option?

Please and thanks,
Ken.

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ken-san,

If you set PWM mode Continuous, each of PWMs will generate accurate wave.

But if you use the interrupt to notice the firmware and firmware isr will take care of re-starting that PWM,

there will be some time delay.

(In other words, using isr and control_reg, "Exact" timing will be very difficult or impossible)

From the schematic I understand that you have 2 kinds of PWM to drive a LED.

I hope that only 1 of 2 is active at any time, correct?

Then what is the timing of changing the PWM source from PWM_1 to PWM_2 and so on?

Best Regards,

15-Mar-2020

Motoo Tanaka

0 Likes

Hello Motoo:

I am including a snippet of code that I use to build the Waveform pulse.  I toggle between two modes.  PWM_1 and PWM_2 as required.  The GPIO signal out is or'd and only one of the PWM blocks is activate at one time.

        for(cycles=0; cycles<5; cycles++)

        {

            for (signals=0; signals<SIGNALS-1; signals++)

            {

                if (TABLES[table].on.waveform[signals] == ON)       

                {

                    //trigger_ON_waveform();        PWM_1          

                    trigger_for_ON();

                    flag_on = 0;

                    while(!flag_on);

                }

                else

                {

                    //trigger_OFF_waveform();            PWM_2       

                    trigger_for_OFF();

                    flag_off = 0;

                    while(!flag_off);

                }

            }

            //trigger_EOS_waveform;            PWM_2

            trigger_for_OFF();

            flag_off = 0;

            while(!flag_off);

            CyDelayUs(2970);               // interpulse delay.

        }

I am looking for suggestions on generate some waveforms that require accuracy to the hundreds of nano-seconds.

Any ideas?

Please and thanks,

ken.

0 Likes
lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ken-san,

If the most important requirement is the accuracy of the PWM

probably using Control_Reg and ISR to control the PWM is not a good idea.

I just made a sample, which is, with each time the sw is pushed

the program change the PWM mode between set1 and set2.

So that there is a some delay between swithing set1 and set2,

each waveform must be accurate.

I wonder if you can allow this delay between switching the mode (set1 and set2).

If you can not, may be we can have 2 set of PWMs and logically enable and disable the output.

(but each PWMs will run in continuous mode)

schematic

011-Schematic.JPG

pins

012-pins.JPG

mian.c

====================

#include "project.h"

#include "stdio.h"

volatile int sw_pushed = 0 ;

CY_ISR(sw_pushed_isr)

{

    isr_sw_ClearPending() ;

    sw_pushed = 1 ;

}

#define STR_LEN 64

char str[STR_LEN+1] ;

void print(char *str)

{

    UART_PutString(str) ;

}

void cls(void)

{

    print("\033c") ; /* reset */

    CyDelay(20) ;

    print("\033[2J") ; /* clear screen */

    CyDelay(20) ;

}

void splash(void)

{

    cls() ;

    print("PWM Test ") ;

    snprintf(str, STR_LEN, "(%s %s)\n\r", __DATE__, __TIME__) ;

    print(str) ;

}

void init_hardware(void)

{

    CyGlobalIntEnable; /* Enable global interrupts. */  

    UART_Start() ;

    splash() ;

  

    PWM_1_Start() ;

  

    isr_sw_StartEx(sw_pushed_isr) ;

}

void set_pwm(uint16_t period, uint16_t compare)

{

    PWM_1_Stop() ;

    PWM_1_WritePeriod(period) ;

    PWM_1_WriteCompare(compare) ;

    PWM_1_Enable() ;

}

int main(void)

{

    int mode = 0 ;

  

    init_hardware() ;

    for(;;)

    {

        if (sw_pushed) {

            if (mode == 0) {

                set_pwm(4322+605, 605) ;

                mode = 1 ;

                print("Mode: 1\n\r") ;

            } else {

                set_pwm(4297+627, 527) ;

                mode = 0 ;

                print("Mode: 0\n\r") ;

            }

            sw_pushed = 0 ;

        }

    }

}

====================

Tera Term log

010-TeraTerm-log.JPG

Best Regards,

15-Mar-2020

Motoo Tanaka

P.S. Oops, I forgot to attach the project

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ken-san,

I was busy writing the sample and missed the last comment.

It seems that you have exact sequence of Frequency Mode.

Since it's after 1:00 AM here, let me read and think about it tomorrow.

But meantime, please try my sample

and think if you can just re-configure the PWM

or you need a couple of PWMs to be switched.

Best Regards,

15-Mar-2020

Motoo Tanaka

0 Likes
lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ken-san,

So I assumed that the change(s) of duty was caused by the various delay of the firmware.

Although there should be ways of doing it only by hardware,

doing it with hardware and ISR seems to be easier and simpler.

So I did the following sample.

Could you test if the duty is more stable or not?

schematic

020-schematic-1.JPG

pins

022-pins.JPG

main.c

Note: To simplify the hardware and isr, I assumed that the last signal is included in the last index of waveform.

=================

#include "project.h"

#include "stdio.h"

#define NUM_CYCLES  5

#define NUM_SIGNALS 5

#define ON_VALUE    1u

#define OFF_VALUE   2u

// for Next_Signal Control_Register

#define FW_TRIG_BIT 0x01

#define ON_BIT      0x02

#define OFF_BIT     0x04

volatile int signal_index = 0 ;

//TABLES[table].on.waveform[signals]

int waveform[NUM_SIGNALS] = { ON_VALUE, OFF_VALUE, ON_VALUE, OFF_VALUE, OFF_VALUE } ; /* test data */

volatile int cycle_busy = 1 ;

CY_ISR(pwm_on_isr)

{

    PWM_1_ReadStatusRegister() ;

    signal_index++ ;

    if (signal_index >= NUM_SIGNALS) {

        cycle_busy = 0 ;

    } else {

        if (waveform[signal_index] == ON_VALUE) {

            Next_Signal_Write(ON_BIT | FW_TRIG_BIT) ;

            Next_Signal_Write(0x00) ;

        } else {

            Next_Signal_Write(OFF_BIT | FW_TRIG_BIT) ;

            Next_Signal_Write(0x00) ;

        }

    }

}

CY_ISR(pwm_off_isr)

{

    PWM_2_ReadStatusRegister() ;

    signal_index++ ;

    if (signal_index >= NUM_SIGNALS) {

        cycle_busy = 0 ;

    } else {

        if (waveform[signal_index] == ON_VALUE) {

            Next_Signal_Write(ON_BIT | FW_TRIG_BIT) ;

            Next_Signal_Write(0x00) ;

        } else {

            Next_Signal_Write(OFF_BIT | FW_TRIG_BIT) ;

            Next_Signal_Write(0x00) ;

        }

    }   

}

#define STR_LEN 64

char str[STR_LEN+1] ;

void print(char *str)

{

    UART_PutString(str) ;

}

void cls(void)

{

    print("\033c") ; /* reset */

    CyDelay(20) ;

    print("\033[2J") ; /* clear screen */

    CyDelay(20) ;

}

void splash(void)

{

    cls() ;

    print("PWM Test ") ;

    snprintf(str, STR_LEN, "(%s %s)\n\r", __DATE__, __TIME__) ;

    print(str) ;

}

void init_hardware(void)

{

    CyGlobalIntEnable; /* Enable global interrupts. */   

    UART_Start() ;

    splash() ;

   

    PWM_1_Start() ;

    PWM_2_Start() ;

   

    isr_1_StartEx(pwm_on_isr) ;

    isr_2_StartEx(pwm_off_isr) ;

}

void set_pwm(uint16_t period, uint16_t compare)

{

    PWM_1_Stop() ;

    PWM_1_WritePeriod(period) ;

    PWM_1_WriteCompare(compare) ;

    PWM_1_Enable() ;

}

int main(void)

{

    int cycles = 0 ;

   

    init_hardware() ;

    for(;;) {

        for (cycles = 0 ; cycles < NUM_CYCLES ; cycles++ ) {

            signal_index = 0 ;

            cycle_busy = 1 ;

            /* firmware takes care of the first signal */

            if (waveform[signal_index] == ON_VALUE) {

                Next_Signal_Write(ON_BIT |  FW_TRIG_BIT) ;

            } else {

                Next_Signal_Write(OFF_BIT | FW_TRIG_BIT) ;

            }

            Next_Signal_Write(0x00) ;

            /* signal_index 2 to the last will be taken care of by hardware */

            while(cycle_busy) ;

            CyDelayUs(2970) ;

        }

    }

}

=================

Best Regards,

15-Mar-2020

Motoo Tanaka

0 Likes

Hello Motoo:

Testing just commenced.

Thank you,

Ken.

Hello Motoo:

I have tested your archive example and the hardware solution provides repeatable results.

Testing on PC with prototyping kit, tweaked the PWM to 60.3 microseconds ON + 432.2 microseconds OFF.  I processed 30 million samples and found that the drift was restricted to 500 nanoseconds.  PERFECT.

I then took the prototype board and plugged it into the working customer target.  I processed 30 million samples and found that the drift was restricted to 500 nanoseconds.

PROBLEM:  The PWM block is now providing me with 56.7 microsecond ON + 435.7 microsecond OFF.

The period is is identical in both the samples from the PC and the customer's target. 

Do you have any idea why the ON / OFF times have drifted this far?

Please and thanks.

Ken.

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ken-san,

When we run the board with PC, especially with debugger,

there is/are communication between the board and PC

which affect the time of the main loop in the main() function.

But this time all the necessary handling is done in ISRs

so the timing is not (or should not be) affected by other

activity in the program.

> PROBLEM:  The PWM block is now providing me with 56.7 microsecond ON + 435.7 microsecond OFF.

As I wrote above may be some uart communication and/or debug communication

between PC and Board is causing this difference.

I wonder if the numbers shown in the standalone configuration is closer to the calculated value?

(If so you need to adjust the PWM period/compare to fit with the standalone configuration.)

Best Regards,

16-Mar-2020

Motoo Tanaka

0 Likes

Hello Motoo:

I will adjust my duty cycle for the final target.

Thank you,

Ken.

On Sun, Mar 15, 2020 at 8:42 PM Motoo Tanaka <community-manager@cypress.com>

Hello Motoo:

From your last comment about customizing the compare for the PWM duty cycle.  I thought I would write a small text fixture to test some varying Periods / duty cycles.  I could then use some simple math to determine what I needed to program in on the TopDesign.cysch screen.

I have made use of your hardware implementation, which works, to allow me to trigger the first PWM block.

This is the snippet of code I am using to generate a batch of 10 pulses with varying periods and duty cycles.

    for(period = 4800; period < 32000; period+=4800)

    {

        for(compare=1920; compare<4752; compare+=960)

        {

            PWM_1_Stop() ;

            PWM_1_ClearFIFO();

            PWM_1_WritePeriod(period) ;

            PWM_1_WriteCompare(compare) ;

           

            PWM_2_Stop() ;

            PWM_2_ClearFIFO();

            PWM_2_WritePeriod(period) ;

            PWM_2_WriteCompare(compare) ;

           

            PWM_1_Enable() ;

            PWM_2_Enable() ;           

           

               // this is your code for the hardware trigger.

               // I have set signal_index to 10 to get some extra cycles that I can measure.

                signal_index = 0 ;

                cycle_busy = 1 ;

                /* firmware takes care of the first signal */

                if (waveform[signal_index] == ON_VALUE)

                {

                    Next_Signal_Write(ON_BIT |  FW_TRIG_BIT) ;

                }

                else

                {

                    Next_Signal_Write(OFF_BIT | FW_TRIG_BIT) ;

                }

                Next_Signal_Write(0x00) ;

                /* signal_index 2 to the last will be taken care of by hardware */

                while(cycle_busy) ;

                CyDelayUs(2970) ;

         }

    }

I would expect to get a period of 100 microseconds, with a duty of 40 microseconds.  I am getting 2 waveforms I cannot explain and 8 good ones, See picture.

Capture_10_Peaks.JPG

I cannot find any other commands in the API section of the datasheet that will allow me to perform any other clear of the parameters.

Your help is appreciated, thanks,

Ken.

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ken-san,

I think that those first 2 pwm were triggered by

PWM_1_Enable()

PWM_2_Enable()

Sine PWMs are configures as one shot,

probably you don't need to call

PWM_1_Stop()

PWM_1_clearFIFO()

So could you try

=============

    for(period = 4800; period < 32000; period+=4800)

    {

         PWM_1_WritePeriod(period) ;

         PWM_2_WritePeriod(period) ;

        for(compare=1920; compare<4752; compare+=960)

        {

            PWM_1_WriteCompare(compare) ;

            PWM_2_WriteCompare(compare) ;

               // this is your code for the hardware trigger.

               // I have set signal_index to 10 to get some extra cycles that I can measure.

                signal_index = 0 ;

                cycle_busy = 1 ;

               

                /* firmware takes care of the first signal */

                if (waveform[signal_index] == ON_VALUE)

                {

                    Next_Signal_Write(ON_BIT |  FW_TRIG_BIT) ;

                }

                else

                {

                    Next_Signal_Write(OFF_BIT | FW_TRIG_BIT) ;

                }

                Next_Signal_Write(0x00) ;

                /* signal_index 2 to the last will be taken care of by hardware */

                while(cycle_busy) ;

                CyDelayUs(2970) ;

         }

    }

=============

Best Regards,

17-Mar-2020

Motoo Tanaka

0 Likes

Hello Motoo:

Gave this a try, same logic analyzer capture.

What I checked was that the Period for both the PWM 1 and 2 for the first two pulses matches the values placed in the TopDesign.cysch screen.  The writing of the PERIOD, 4800,  does not appear to have worked the first time through.

Regards,

Ken.

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ken-san,

> What I checked was that the Period for both the PWM 1 and 2

> for the first two pulses matches the values placed in the TopDesign.cysch screen.

That's right, I was also thinking about it, but somehow failed to take care of it.

If we want to avoid the effect of the value entered in the TopDesign.cysch, aka schematic,

we could set the compare to 0 in the schematic.

Then another thing I noticed is since you are changing the period this time,

PWM should be stopped before PWM_x_WritePeriod().

But for each loops you have CyDelay(2970), and PWMs are one shot,

I think that it makes the same result with PWM_x_Stop().

So how about trying

(1) Set period to the first  value (4800)

      and compare to 0 in the schematic.

  I hope this will prevent generating the first two pulse.

(2) add

PWM_1_Stop() and PWM_2_Stop()

before the line

    for(period = 4800; period < 32000; period+=4800)

=============

// In the schematic

// set period of both PWMs to 4800 // <= New

// set compare of both PWMs to 0 // <= New

    PWM_1_Stop() ; // <= New

    PWM_2_Stop() ; // <= New

    for(period = 4800; period < 32000; period+=4800)

    {

         PWM_1_WritePeriod(period) ;

         PWM_2_WritePeriod(period) ;

        for(compare=1920; compare<4752; compare+=960)

        {

            PWM_1_WriteCompare(compare) ;

            PWM_2_WriteCompare(compare) ;

               // this is your code for the hardware trigger.

               // I have set signal_index to 10 to get some extra cycles that I can measure.

                signal_index = 0 ;

                cycle_busy = 1 ;

              

                /* firmware takes care of the first signal */

                if (waveform[signal_index] == ON_VALUE)

                {

                    Next_Signal_Write(ON_BIT |  FW_TRIG_BIT) ;

                }

                else

                {

                    Next_Signal_Write(OFF_BIT | FW_TRIG_BIT) ;

                }

                Next_Signal_Write(0x00) ;

                /* signal_index 2 to the last will be taken care of by hardware */

                while(cycle_busy) ;

                CyDelayUs(2970) ;

         }

    }

=============

Best Regards,

17-Mar-2020

Motoo Tanaka

0 Likes

Hello Motoo:

Tested and discovered the following.

1) Left init_hardware() the same

2) In the schematic changed Period to 4800 and compare to zero

3) Note: When I used PWM_1_Stop() and PWM_2_Stop() the hardware did not generate a waveform.  I took them out and started getting waveforms being produced.

4) The first "10" periods and duty cycles were perfect.  The second loop through produced the same problem as shown above in the logic analyzer capture.

5) Spent some time surfing on the net and came across a contribution page on the Cypress Developer Community from Mar 3, 2011. 

Dynamically Changing the Period Value of Timers, Counters, and PWMs in PSoC® 3/4/5LP – KBA88172

6) Tried this and it works buy setting the PWM_WriteCounter( 0 ), after adjusting the new period dynamically.

Regards,

Ken.

MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ken-san,

Congratulations!

I'm happy hearing that you solved the problem.

I will read and study that KBA, too!

> 6) Tried this and it works buy setting the PWM_WriteCounter( 0 ), after adjusting the new period dynamically.

I often did this, but I forgot doing this this time...

Best Regards,

18-Mar-2020

Motoo Tanaka

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ken-san,

FYI, I just translated KBA88172 into Japanese and posted to

Dynamically Changing the Period Value of Timers, Counters, and PWMs in PSoC 3/4/5LP - KBA88172

Best Regards,

18-Mar-2020

Motoo Tanaka

0 Likes

Hello Motoo:

That is good news, I didn't think I was the only user that had tried to do this.

I think we should mark this as the correct answer and close.

Regards,

Ken L.

Len_CONSULTRON
Level 9
Level 9
Beta tester 500 solutions authored 1000 replies posted

Ken,

I've been trying to follow this discussion.   I hope I didn't misunderstand it.

It looks from the information you have shared with this forum that you are SW triggering each PWM cycle.  If this is true, you might be exhibiting a SW latency issue that varies with PWM period.  This variance can be because you are processing interrupts (such as communication) and they occur asynchronously.  Due to this asynchronicity the latency can be erratic and not always predictable.

Is there a way to modify your design to eliminate the external triggering of the PWMs?   Even if you were to temporarily change the PWMs to continuous mode just to verify that the XTAL clock source is NOT the root cause of the problem of duty-cycle variance.

Len

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

Good Morning:

With the example from Motoo's note I have been able to reproduce my waveform exactly.  The microsecond accuracy is repeatable.

As he suggested, trigger the waveform in software, and let the hardware block do the rest.  I do have to adjust the duty cycle slightly but with the hardware version I can reproduce a waveform that is useable. 

I have put this into continuous mode and my XTAL 8Mhz, 30ppm crystal is producing timings to the nanoseconds.

Testing is still ongoing and I am getting closer to a solution.

Regards,

Ken.

0 Likes

Ken,

I'm glad to hear you have a better handle on your issue.

Being a trained HW engineer with SW experience, I'm appreciative to Cypress for creating a device (PSoC-series) that has user-programmable HW routing both for digital and analog.   If possible, I try to create HW state-machine designs that only rely on SW to start or stop.  This allows the maximum available performance achievable with minimal SW interference from other HW or SW events (such as interrupts) while also maximizing CPU utilization.

This is particularly crucial if your system requirements have a fast throughput or tight timing (ie. usecs or nsecs).

It take a bit more work but I have used the PSoC5 DMA HW to provide very high-speed, reliable HW state-machines.

Len

Len
"Engineering is an Art. The Art of Compromise."
0 Likes