- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
Solved! Go to Solution.
- Labels:
-
PSoC 5LP
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
pins
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
Best Regards,
15-Mar-2020
Motoo Tanaka
P.S. Oops, I forgot to attach the project
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
pins
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello Motoo:
Testing just commenced.
Thank you,
Ken.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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>
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
"Engineering is an Art. The Art of Compromise."
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
"Engineering is an Art. The Art of Compromise."