Skip navigation
Home > All Places > ModusToolbox > Blog > 2019 > June
2019
MarkS_11

Task-Aware Debugging

Posted by MarkS_11 Jun 30, 2019

I was asked a question last week about multi-threaded debugging in ModusToolbox IDE. I knew there was a plug-in for that but I had never used it. So I gave it a shot and here's how you can enable task-aware debugging with the FreeRTOS kernel.

It turns out that you do not need to install the plug-in - it is already there - but you do need to enable it for OpenOCD. I did that in the file ModusToolbox_1.1\tools\openocd-2.1\scripts\target\psoc6_common.cfg. On line 228, make this addition:

${TARGET}.cm4 configure -rtos auto -work-area-phys $_WORKAREAADDR_CM4 -work-area-size $_WORKAREASIZE_CM4 -work-area-backup 0

The "auto" is taken to mean FreeRTOS by the plug-in, but you can use "-rtos FreeRTOS" if you prefer to be explicit. Next you need to define a variable that the plug-in expects (but is no longer defined in FreeRTOS). I just created a global variable like this:

static volatile int uxTopUsedPriority;

Then, in main(), I use the variable to make sure it is not optimized out by the compiler.

uxTopUsedPriority = configMAX_PRIORITIES - 1;

All I needed now was a project to debug. I created one from the PioneerKitAppFreeRTOS template and made a copy of the "blinky" task (so my application would be slightly more interesting). When I run the debugger and hit a breakpoint in one of the blinky functions, the Quick Panel Debug tab looks like this, which make it really easy to figure out what tasks are in the application and which one I am debugging.

Task-Aware Debugging in ModusToolbox IDE

Now, there is one slight problem with this... it breaks debugging of non-FreeRTOS applications! Oopsy! The reason for this is that the "-rtos auto" causes the debugger to look for missing symbols when you are not using the RTOS. I fixed this by making copies of psoc6.cfg and psoc6_common.cfg (called psoc6_rtos.cfg and psoc6_common_rtos.cfg) in the target folder above. In the psoc6_rtos.cfg file I edited the last line as follows:

source [find target/psoc6_common_rtos.cfg]

In the original psoc6_common.cfg file I backed out the edit so that this file is returned to its original state. Now I have RTOS and non-RTOS config files and, by default, the launch configuration for your projects will use the non-RTOS setup. However, when I do want to enable multi-thread debugging, I just edit the Debug Configuration (Run menu) to use the new files instead. In the Debugger tab there is a filed for Config Options and I change the config file as follows:

-c "source [find target/psoc6.cfg]"

Here is an image of that change.

Enabling Task-aware Debugging in ModusToolbox IDE

 

I am really happy with this discovery. I can now enable task-aware debugging for a project in just a couple of minutes. I think it will become a standard practice for me. I hope you like it too! I attached my config files so you can just copy them into the target folder, rather than edit the installed IDE files (remember to log into cypress.com to access them).

MarkS_11

PWM Clock Sharing

Posted by MarkS_11 Jun 27, 2019

Did you finish your homework? Well, here's an answer anyway. A couple of days ago I used a pair of TCPWM blocks to toggle the brightness of an LED at 1Hz. In that project I used an 8-bit clock divider as the source for the LED-driving PWM and a 16-bit divider for the slower toggling Timer-Counter. It worked just fine but it felt a little decadent to use two clock dividers when I am using a PSoC, which has the on-chip flexibility to use one divider for both TCPWM blocks. How to do that?

Well, the easy part is changing the clock source selection in the Timer-Counter using the Device Configurator. Open the project in ModusToolbox IDE and open the Configurator. In the SWAP_CTR block change the Clock Signal to "8 bit Divider 2 clk", which is marked in the drop-down as "USED" as a reminder that another resource is using that source (obviously... this is why we are choosing it!).

Choosing a USED PSoC clock divider

Next, there is a little mathematics, which is the English word for math. The frequency of the TCPWM output is a function of the peripheral clock (CLK_PERI) frequency, the 8 but divider value, the TCPWM prescaler (which I hinted at last time) and the period of the TCPWM counter. In pseudo-math (note that it is OK to call it math when it's "pseudo" - just because I am a snob does not mean I am not really lazy as well) it looks like this.

CLK_PERI

LED frequency =  ----------------------------

divider * prescaler * period

Some of these are known (or the pseudo-math becomes "guessing"). The LED toggling frequency needs to be 1Hz. The CLK_PERI is set to 72MHz in the Platform tab of the Device Configurator and let's leave that alone. For the others, the divider is a 8-bit value, the period is 16-bit, and the prescaler is 1, 2, 4, 8, 16, 32, 164 or 128. The trick we have to perform is to divide and prescale CLK_PERI such that the TCPWM period gives us 1Hz, preferably with a nice round number. Here is the rearranged formula.

divider * prescaler * period = 72000000

The 16-bit TCPWM has a maximum period of 65535, which means the clock must be divided by at least 1098.6, which cannot be done in a 8-bit divider. So we need to use a prescaler value of at least 8. Beyond that there are gabillions of combinations that satisfy the equation. After a little trial and error, well mostly error, I figured out that a divider of 225 works really nicely because it creates a frequency of 320kHz. Precaling that by 32 gives me a TCPWM frequency of 10kHz and a period of 10000 gives me a 1Hz output. Et voila!

Dividing and prescaling the PSoC clock frequency

Here is the set up for the Timer-Counter, with the prescaler and period set as above.

Setting the PSoC Timer-Counter period and prescaler

Note that the LED_PWM just has to toggle fast enough not to flicker so I set the period to 1000 and the compare values to 500 and 50 for the 50% and 5% intensity values.

I had quite a lot of fun doing this project. I think it nicely illustrates how flexible PSoC is and how wretched my mathematics skills are. I've attached a template that you can use to replicate the design - to use it, open the New Application dialog and "Import..." the modus.mk file to create a copy of the project.

Importing the project template into ModusToolbox IDE

Note: you must be logged into cypress.com to see the attachment (PWM_Swap.zip).

MarkS_11

Internally-Triggered PSoC PWM

Posted by MarkS_11 Jun 26, 2019

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.