How to properly change the TCPMW Counter or PWM period in a complex application

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

cross mob
GuGa_1322886
Level 4
Level 4
5 solutions authored 25 sign-ins First comment on KBA

Hi Everyone,

A few weeks ago I posted a message here complaining that Cy_TCPM_Counter_SetPeriod() is not behaving as documented. In my application I need to dynamically change the period of a counter anywhere from 1 to 400 Hz to generate a periodic interrupt that will trigger other events. The problem was that no mater what period I tried to set, the counter ignored it and remained running at the initial frequency when it was initialized. Frustrated I resorted to just de-initialize and reinitialize the counter with the new frequency.  A few weeks later the problem came back to bite me again when I needed to change the period of a timer in PMW mode, and posted a second message here.

After detailed code review and debugging, and stepping through the code, I finally found a few errors in my code, AND, understood how the API really has to be used. This is not clearly documented anywhere, at least in the places where I looked (this forum and others).

First, don't repeat my errors:

1. Pay attention to the type of parameters that each function expects.  For example,

Cy_TCPWM_TriggerStart_Single(Timer_1_HW, Timer_1_NUM);  // The second parameter must be the timer NUMBER
Cy_TCPWM_TriggerStart(Timer_1_HW, Timer_1_MASK);  // The second parameter must be the timer MASK (or the OR '|'  combined mask of several timers, if you want to start them all in sync)

2. Double check the parameters used in the PDL documentation. In one instance the example has the second parameter incorrect (it uses counter MASK where it should use counter NUMBER, twice!).  All xx_Single() versions of the functions work on a single timer and expect the timer number.  That error in the example led me to believe that xx_MASK and xx_NUMBER are interchangeable, when they are NOT.

3. You must STOP the counter to make changes to the period, duty cycle,  compare values, etc. There may be instances where this is not necessary, but better safe than sorry, in the PDL documentation the examples do not mention this, (at least for the functions that I used).

4. It seems that Cy_TCPWM_TriggerStopOrKill_Single() is not an atomic operation. You must test and make sure that the counter has stopped before continuing. This was the principal problem I had since the beginning.

So, here is the the safe method to alter counter parameters that I came up with:

uint32_t loops;   // this variable is for debugging purposes only

void Change_Timer_1_Frequency(uint32_t _new_frequency)
{
      uint32_t period = 0;

   /* validate frequency range */
   if (_new_frequency < MIN_FREQUENCY || _new_frequency > MAX_FREQUENCY)
        return;

    period = _Timer_1_clock / _new_frequency;

    loops = 0;

    Cy_TCPWM_TriggerStopOrKill_Single(Timer_1_HW, Timer_1_NUM);

    /* IMPORTANT: wait for the timer to stop before changing registers */
     while (Cy_TCPWM_Counter_GetStatus(Timer_1_HW, Timer_1_NUM) &      CY_TCPWM_COUNTER_STATUS_COUNTER_RUNNING )
         loops++;

    Cy_TCPWM_Counter_SetCounter(Timer_1_HW, Timer_1_NUM, 0);
    Cy_TCPWM_Counter_SetPeriod(Timer_1_HW, Timer_1_NUM, period);
    Cy_TCPWM_TriggerStart_Single(Timer_1_HW, Timer_1_NUM);
}

I introduced the variable Loops for testing purposes. In a simple, Hello World type of program when only this code is running Loops may be 0, most of the time. But as the program grows in complexity with interrupts and external events going on at the same time, that won't be the case.  In my application Loops ends up anywhere from 65 a to over 600 iterations before the counter stopped!

In summary, I thing that PDL is a good helper, but its documentation is minimal and the examples just snippets not really tested in an actual program.  As a former boss of mine used to say "Trust is good, check is better!"

 

 

0 Likes
1 Solution
rsiigod
Level 3
Level 3
Distributor - WPG(GC)
25 replies posted 50 sign-ins First solution authored

Thanks your information share~😀

View solution in original post

0 Likes
5 Replies
Ekta_N
Moderator
Moderator
Moderator
750 replies posted First like given 250 solutions authored

Hello @GuGa_1322886 

Thank you for the detailed post on changing the TCPMW Counter or PWM period.

Could you please let me know where did you find the error in the PDL documentation so that I can create an internal ticket and get the error corrected?

In the MTB CAT1 Peripheral driver library, the Cy_TCPWM_TriggerReloadOrIndex_Single() function mentions that the second parameter should be the Counter instance number in the selected TCPWM.

Note that the Cy_TCPWM_TriggerReloadOrIndex() function accepts the second parameter as a bit field representing each counter in the TCPWM block. This is also shown with the help of a code snippet:

Ekta_0-1632121907992.png

 

Best Regards

Ekta

 

Hello @GuGa_1322886 

Thank you for the detailed post on changing the TCPMW Counter or PWM period.

Could you please let me know where did you find the error in the PDL documentation so that I can create an internal ticket and get the error corrected?

In the MTB CAT1 Peripheral driver library, the Cy_TCPWM_TriggerReloadOrIndex_Single() function mentions that the second parameter should be the Counter instance number in the selected TCPWM.

Note that the Cy_TCPWM_TriggerReloadOrIndex() function accepts the second parameter as a bit field representing each counter in the TCPWM block. This is also shown with the help of a code snippet:

Ekta_0-1632121907992.png

 

Best Regards

Ekta




 

Hello @GuGa_1322886 

Thank you for the detailed post on changing the TCPMW Counter or PWM period.

Could you please let me know where did you find the error in the PDL documentation so that I can create an internal ticket and get the error corrected?

In the MTB CAT1 Peripheral driver library, the Cy_TCPWM_TriggerReloadOrIndex_Single() function mentions that the second parameter should be the Counter instance number in the selected TCPWM.

Note that the Cy_TCPWM_TriggerReloadOrIndex() function accepts the second parameter as a bit field representing each counter in the TCPWM block. This is also shown with the help of a code snippet:

Ekta_0-1632121907992.png

 

Best Regards
Ekta

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

Hi Ekta,

That's correct in what  respects to Cy_TCPWM_TriggerReloadOrIndex() and its _Single() counterpart.

You will find the erroneous example in  Cy_TCPWM_PWM_SetCounter()

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
 
There are two problems with this code: First, is that the Stop and Trigger _Single() functions with the mask for Counter 0 are actually acting on Counter 1.
The second problem is, after correcting the first problem, that after issuing Cy_TCPWM_TriggerStopOrKill_Single(),  there is no guarantee that the counter has actually stopped when the code reaches Cy_TCPWM_PWM_SetCounter();
Obviously this code was never tested. There may be many more issues like this all over PDL, which makes it questionable to be used in mission critical applications. 
PSOC6 is a beauty  to work with, and I hope that PDL evolve to be a reliable API backed up by a rigorous test harness.
One other thing to suggest, if I may, is why are you still working in C, when you have C++ compiler which is more strict in parameter checking, and it was proven that it doesn't create any performance penalty.
 
rsiigod
Level 3
Level 3
Distributor - WPG(GC)
25 replies posted 50 sign-ins First solution authored

I had a similar problem and was lucky enough to see this explained!

Cy_TCPWM_TriggerReloadOrIndex_Single(TCPWM0, MY_TCPWM_PWM_MASK);         // Second parameter incorrect again in the MTB CAT1 Peripheral driver library release-v2.3.1.

SAC_Ade_Chen_0-1642581564583.png

 

0 Likes

Besides the incorrect parameter in the sample snippet, the most important issue not mention in the documentation is that there is no guarantee that the counter has stopped when the Cy_TCPWM_TriggerStopOrKill_Single() returns.

Make sure that you test for it before changing any register in the counter like this:

/* IMPORTANT: wait for the timer to stop before changing registers */
while (Cy_TCPWM_Counter_GetStatus(Timer_1_HW, Timer_1_NUM) & CY_TCPWM_COUNTER_STATUS_COUNTER_RUNNING );

After this change my code has been running reliably dynamically changing the frequency of the counter as needed.

 

rsiigod
Level 3
Level 3
Distributor - WPG(GC)
25 replies posted 50 sign-ins First solution authored

Thanks your information share~😀

0 Likes