I got "back on the blog" about a week ago and wrote about configuring PWMs and hooking them up to LEDs. It was simple stuff but hopefully got us all up to speed on configuring peripherals and connecting them to pins. Today I want to extend that learning a little bit and use one TCPWM block to control another. Wooooooo! Scary stuff! Not really.

I also mentioned last time that the TCPWM supports two compare values, which is why I suggested you play with the duty cycle using the SetCompare0() function. Yes, there is also a SetCompare1(). Genius! So I propose to set up the PWM with two compare values, giving us a bright and a dim LED. Then I will periodically switch between the two values to prove that it all works. As with cat-skinning, there are many ways to do this. Here are three that come to mind (all of them assume you have set up a 16-bit PWM, called LED1_PWM, to drive an LED as in my previous blog).

  1. Modify the compare value from firmware
  2. Set up two compare values and switch between them from firmware
  3. Set up two compare values and use another TCPWM to switch between them

Let's walk through all three methods, starting with the firmware. Remember that LED1_PWM has a period of 32767 and a compare value of 16384, and that it is turned on with the following code in main().

 

Cy_TCPWM_PWM_Init(     LED1_PWM_HW, LED1_PWM_NUM, &LED1_PWM_config );

Cy_TCPWM_PWM_Enable(   LED1_PWM_HW, LED1_PWM_NUM );

Cy_TCPWM_TriggerStart( LED1_PWM_HW, LED1_PWM_MASK );

 

That gives us a fairly bright LED (actually 50% of maximum) and so, in the main loop, add these two lines of code.

 

for(;;)

{

Cy_TCPWM_PWM_SetCompare0( LED1_PWM_HW, LED1_PWM_NUM, 1638 ); // 5%

Cy_SysLib_Delay( 500 );

Cy_TCPWM_PWM_SetCompare0( LED1_PWM_HW, LED1_PWM_NUM, 16384 ); // 50%

Cy_SysLib_Delay( 500 );

}

 

Program that code and you will see the LED switch between 50% and 5% brightness. But, let's face it, that is a fairly ugly solution because we are just jamming values into the PWM and spinning the CPU.Let's move onto the second method. Open up the Device Configurator and navigate to the setup for LED1_PWM. Check the Enable Compare Swap box, which causes the Compare 1 parameter to become editable, and set it to 1638.

Setting two compare values in the PSoC TCPWM

Save that change and go back to ModusToolbox IDE. Change the C code in the loop as follows.

 

for(;;)

{

Cy_TCPWM_TriggerCaptureOrSwap( LED1_PWM_HW, LED1_PWM_MASK );

Cy_SysLib_Delay( 500 );

}

 

This code causes the PWM to swap the compare value between the choices you made in the Configurator. It's a little neater than the first choice but I am still troubled by the spin loop.Let's just do it all in hardware. Go back to the Device Configurator and find the Swap Input parameter in the Inputs section. Choose Rising Edge from the drop-down menu. This selection enables the Swap Signal parameter. Use that drop-down to choose another TCPWM block - Counter 15 is a good choice because it is adjacent to the one we are already using (and so is easy to find - there is no technical advantage to a particular 16-bit TCPWM choice). Note that there are three connectivity options on the block - overflow, underflow and cc_match/capture/compare. I chose underflow so I can trigger the swap with a down-counter. All this sets up LED1_PWM to switch between Compare 0 and Compare 1 whenever there is a rising edge on the TCPWM #15 underflow signal.

Setting up the PSoC TCPWM compare value swap signal

The next step is to set up the new TCPWM block. I chose the Timer-Counter personality - I do not need it to be a PWM because I just want the terminal condition to cause the trigger - and called it SWAP_CTR.

Choosing the Timer-Counter personality for the PSoC TCPWM

In the parameters, set the Count Direction to Down so it will underflow at the end of each cycle.

Setting the direction and period of the PSoC Timer-Counter

I want a 500ms period and so this means I need a pretty slow clock and I cannot do that with an 8-bit source divider. For the Clock Signal choose 16-bit Divider 1, jump the Peripheral-Clocks tab, and set the divider on that clock to 36000 (which give a 2kHz signal from the 72MHz source).

Setting the 16-bit divider on the PSoC Timer-Counter peripheral clock

Back in the TCPWM set the period to 1000 (shown above) and we have the desired triggering rate. Save the edits and return to the ModusToolbox IDE.First of all, remove ALL the code from the for(;;) loop - this is hardware only! Then enable the new counter as follows.

 

Cy_TCPWM_PWM_Init(       LED1_PWM_HW, LED1_PWM_NUM, &LED1_PWM_config );

Cy_TCPWM_PWM_Enable(     LED1_PWM_HW, LED1_PWM_NUM );

Cy_TCPWM_Counter_Init(   SWAP_CTR_HW, SWAP_CTR_NUM, &SWAP_CTR_config );

Cy_TCPWM_Counter_Enable( SWAP_CTR_HW, SWAP_CTR_NUM );

Cy_TCPWM_TriggerStart(   LED1_PWM_HW, LED1_PWM_MASK | SWAP_CTR_MASK );

 

There are three things to note about this code.

  1. The APIs are slightly different - i.e. Cy_TCPWM_Counter_Init intead of Cy_TCPWM_PWM_Init - but the arguments are the same.
  2. There is only one call to the TriggerStart() function because both counters are in the same block (i.e. LED1_PWM_HW == SWAP_CTR_HW).
  3. TriggerStart() acts upon the ORed masks of the two TCPWM blocks.

Programming this should cause the LED to swap states automatically without any SW intervention. I think you'd agree that it is a cleaner, more efficient solution. The only remaining problem I have with this is that I am being a little greedy with the clocks. I'd really like to use just one 8-bit clock source, as opposed to one 8- and one 16-bit divider. So I will leave you a little homework... try to change the settings in the Device Configurator to use just one clock for the TCPWMs. I'll post a solution in a day or two (assuming I can actually get it to work!).

Hint - there is a Clock Prescaler in the TCPWM blocks which can be used to slow down the input clock speed.