Can't change a PWM period on the fly with Cy_TCPWM_PWM_SetPeriod0()

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

cross mob
lock attach
Attachments are accessible only for community members.
GuGa_1322886
Level 4
Level 4
5 solutions authored 25 sign-ins First comment on KBA

Hi,

A few weeks ago I reported a similar problem in my application with TCPWM Timers. Unfortunately, in that opportunity we couldn't reproduce the problem using a simplified Hello World example. Now I have the same problem with TCPWM in PWM mode. Like before, the only sure way to change its period is by deinint the PWM and reinit it with the new period.

But this time I could reproduce the problem in the Hello World example, which I uploaded here.  For what I could pick up from the PDL documentation and other comments this should  be the correct way to change the PWM period, but it does nothing:

/* validate frequency ranges */
if (_timer_frequency < MIN_FREQUENCY || _timer_frequency > MAX_FREQUENCY)
return;

Cy_TCPWM_TriggerStopOrKill(PWM_1_HW, PWM_1_NUM);

Cy_TCPWM_PWM_SetCounter(PWM_1_HW, PWM_1_MASK, 0);

uint32_t new_period = _timer_clock / _timer_frequency;

Cy_TCPWM_PWM_SetPeriod0(PWM_1_HW, PWM_1_MASK, new_period);
Cy_TCPWM_PWM_SetPeriod1(PWM_1_HW, PWM_1_MASK, new_period);  // just in case!
Cy_TCPWM_TriggerReloadOrIndex_Single(PWM_1_HW, PWM_1_NUM);

the clock frequency is 10 KHz and the PWM range needed is 1 to 400 Hz. Nothing spectacular.

uncommenting //#define USE_REINIT_MODE at line 200 of main.c you can select the code above or the full reinit mode which actually works.

I left there the older Timer test routines, but the timer init is commented out in main(). The loop reads the debug terminal awaiting single key commands. P should change the PWM from 1 to 10 Hz that toggles the LED accordingly.

The PDL documentation should have complete examples of these functions A to Z. Not just a snippet with a line of comment.

0 Likes
1 Solution
GuGa_1322886
Level 4
Level 4
5 solutions authored 25 sign-ins First comment on KBA

Hi ARH,

UPDATE

I reviewed  my code once again, cross check with the API, and found what the error was in my code,  induced by errors in the API example (!!!).

Here is the sample code for  Cy_TCPWM_PWM_SetCounter()

/* Scenario: there is a need to set the counter value for
* the first (index = 0) counter of the TCPWM0 block
*/
#define MY_TCPWM_PWM_NUM (0UL)
#define MY_TCPWM_PWM_MASK (1UL << MY_TCPWM_PWM_NUM)
#define MY_TCPWM_PWM_VAL (100UL)
 
Cy_TCPWM_TriggerStopOrKill_Single(TCPWM0, MY_TCPWM_PWM_MASK);                   //  Second parameter incorrect
Cy_TCPWM_PWM_SetCounter(TCPWM0, MY_TCPWM_PWM_NUM, MY_TCPWM_PWM_VAL);
Cy_TCPWM_TriggerReloadOrIndex_Single(TCPWM0, MY_TCPWM_PWM_MASK);         // Second parameter incorrect again
 
The problem with this example, that obviously was never tested, is that the xxx_Single() functions expect the counter NUMBER , NOT the MASK in the second parameter.   I am at fault too because I copied blindly the example in my code without checking the actual function parameters, it also led me to believe that COUNTER_NUM and COUNTER_MASK are freely interchangeable, which are not.  I learned my lesson here.
 
So, with that clarified this code should work as expected, right?
 
Cy_TCPWM_TriggerStopOrKill_Single(PWM_1_HW, PWM_1_NUM);
Cy_TCPWM_PWM_SetCounter(PWM_1_HW, PWM_1_NUM, 0);
Cy_TCPWM_PWM_SetPeriod0(PWM_1_HW, PWM_1_NUM, new_period);
Cy_TCPWM_TriggerReloadOrIndex_Single(PWM_1_HW, PWM_1_NUM);
 
WRONG!
Still doesn't work. After some debugging I noticed that stepping throw the code, it works, but not if I let it run freely.
What's the problem?
Answer: You have to wait for the counter to stop!
 
Here is the correct, and tested, way to change the period (or duty cycle) of a PWM
 

bool Set_PWM_1_Frequency(uint32_t  _new_frequency)
{
/* validate frequency ranges */
if (_new_frequency< MIN_FREQUENCY || _new_frequency> MAX_FREQUENCY)
return true;

uint32_t new_period = _timer_clock / _new_frequency;

Cy_TCPWM_TriggerStopOrKill_Single(PWM_1_HW, PWM_1_NUM);

/* IMPORTANT: Wait until the counter has stopped before changing its parameters */
while ( Cy_TCPWM_PWM_GetStatus(PWM_1_HW, PWM_1_NUM) & CY_TCPWM_PWM_STATUS_COUNTER_RUNNING )
;

Cy_TCPWM_PWM_SetCounter(PWM_1_HW, PWM_1_NUM, 0);
Cy_TCPWM_PWM_SetPeriod0(PWM_1_HW, PWM_1_NUM, new_period);
Cy_TCPWM_TriggerReloadOrIndex_Single(PWM_1_HW, PWM_1_NUM);


return false;
}

Conclusion, a set of full blown examples known to work is not a bad idea after all.

View solution in original post

2 Replies
ARH
Level 3
Level 3
10 replies posted 5 replies posted 5 sign-ins

I read your post several times and I am not sure that I understand.

You for sure can change the period of a TCPWM on the fly.  The only thing that you need to be careful of is if you change the period to be less than the counter if you are counting up you will endup with really weird behavior (which I bet is what is happening)

 

As far as the documentation goes.... yes there is always improvements to be made.  I will say that my personal bias was always to have SIMPLE code snippets that show one specific thing rather than more complete examples in the documentation which take more time to study.  The strategy was always to make more complicated examples as code example.

 

The documentation tells you to stop the counter... then change the period... then restart .. this is obviously the safest thing to do... e..g. in a motor control example. Im 99% sure that in your chase you can just change it.

 

I would for sure make sure that you are down counting... as im almost sure that was the problem.


ARH

0 Likes
GuGa_1322886
Level 4
Level 4
5 solutions authored 25 sign-ins First comment on KBA

Hi ARH,

UPDATE

I reviewed  my code once again, cross check with the API, and found what the error was in my code,  induced by errors in the API example (!!!).

Here is the sample code for  Cy_TCPWM_PWM_SetCounter()

/* Scenario: there is a need to set the counter value for
* the first (index = 0) counter of the TCPWM0 block
*/
#define MY_TCPWM_PWM_NUM (0UL)
#define MY_TCPWM_PWM_MASK (1UL << MY_TCPWM_PWM_NUM)
#define MY_TCPWM_PWM_VAL (100UL)
 
Cy_TCPWM_TriggerStopOrKill_Single(TCPWM0, MY_TCPWM_PWM_MASK);                   //  Second parameter incorrect
Cy_TCPWM_PWM_SetCounter(TCPWM0, MY_TCPWM_PWM_NUM, MY_TCPWM_PWM_VAL);
Cy_TCPWM_TriggerReloadOrIndex_Single(TCPWM0, MY_TCPWM_PWM_MASK);         // Second parameter incorrect again
 
The problem with this example, that obviously was never tested, is that the xxx_Single() functions expect the counter NUMBER , NOT the MASK in the second parameter.   I am at fault too because I copied blindly the example in my code without checking the actual function parameters, it also led me to believe that COUNTER_NUM and COUNTER_MASK are freely interchangeable, which are not.  I learned my lesson here.
 
So, with that clarified this code should work as expected, right?
 
Cy_TCPWM_TriggerStopOrKill_Single(PWM_1_HW, PWM_1_NUM);
Cy_TCPWM_PWM_SetCounter(PWM_1_HW, PWM_1_NUM, 0);
Cy_TCPWM_PWM_SetPeriod0(PWM_1_HW, PWM_1_NUM, new_period);
Cy_TCPWM_TriggerReloadOrIndex_Single(PWM_1_HW, PWM_1_NUM);
 
WRONG!
Still doesn't work. After some debugging I noticed that stepping throw the code, it works, but not if I let it run freely.
What's the problem?
Answer: You have to wait for the counter to stop!
 
Here is the correct, and tested, way to change the period (or duty cycle) of a PWM
 

bool Set_PWM_1_Frequency(uint32_t  _new_frequency)
{
/* validate frequency ranges */
if (_new_frequency< MIN_FREQUENCY || _new_frequency> MAX_FREQUENCY)
return true;

uint32_t new_period = _timer_clock / _new_frequency;

Cy_TCPWM_TriggerStopOrKill_Single(PWM_1_HW, PWM_1_NUM);

/* IMPORTANT: Wait until the counter has stopped before changing its parameters */
while ( Cy_TCPWM_PWM_GetStatus(PWM_1_HW, PWM_1_NUM) & CY_TCPWM_PWM_STATUS_COUNTER_RUNNING )
;

Cy_TCPWM_PWM_SetCounter(PWM_1_HW, PWM_1_NUM, 0);
Cy_TCPWM_PWM_SetPeriod0(PWM_1_HW, PWM_1_NUM, new_period);
Cy_TCPWM_TriggerReloadOrIndex_Single(PWM_1_HW, PWM_1_NUM);


return false;
}

Conclusion, a set of full blown examples known to work is not a bad idea after all.