Back in February I committed to write about peripheral setup using the ModusToolbox device configurator tool. I have not done very well! We have had another release since then - ModusToolbox 1.1 - and I have been buried in planning for the 2.0 release and training our internal teams. But that is real work and I've had enough of it. It's time to get back to writing about PSoC and developing cool applications.
But first a word or two about a change in the project structure for PSoC applications between the 1.0 and 1.1 releases. In 1.0 we created five projects for a single application; one each for the CM4 and CM0+ cores, one each for the PDL libraries for each core, and one for the configuration. Early feedback was that this was confusing and difficult to manage and so we re-jigged things in the 1.1 software. We also found that most engineers do not use the CM0+ core in real applications because they prefer the faster (more power efficient) CM4 and the simplicity of single-core development. As a result, by default, we now only create the CM4 project and leave the CM0+ to the experts. That removed two of the required projects, which was a good start. Next, we realized that there is no longer any point having a separate configuration project, so we moved the design.modus file into the CM4 project and put all the generated source into a local folder, instead of a link to another project. The result is a major usability improvement. You get the application project, where you do all the work, and a library project, which you typically never edit - just build once and link. Another benefit of this is that the library project can be linked to any number of a applications. The first application you create in a new workspace will have a library project but, if you then create more applications they default to sharing the original library. I find this really helps me manage projects and I only rarely have to change, or create new, workspaces.
In previous posts I have written about creating applications, starter templates for single-threaded and FreeRTOS applications, and doing some basic things like twiddling LEDs and reacting to button presses. Note that in the post about <here all the generated code goes I wrote about the GeneratedSource folder being in the configuration project. Obviously, if you are using ModusToolbox 1.1 that folder is now in the main application project.
OK, that's enough housekeeping about releases. We've all got v1.1 right! Good, let's modify a start project to set up a peripheral. You all know how to do this by now (check out the links above if you need a reminder) so create a new application using the PioneerKitApp starter template. This template has the buttons and LEDs configured for us so we do not need to repeat all that work.
Open the device configurator (click on "Configure Device" in the Quick Panel or double-click on design.modus) and look for KIT_LED1 in the Pins tab. Click on the Digital Output internal connection and choose the 16-bit TCPWM.
Now click on the chain button to jump to the TCPWM.
Note that it is in block 1, which contains 24 16-bit TCPWMs (block 0 are the 32-bit TCPWMs - that's overkill for this application). Click on the checkbox to enable the peripheral and give it a name - "LED1_PWM".
Notice the configuration panel that appeared when you enabled the peripheral. It has set the Period to the maximum value (32768) and the Compare to 50% of that (16384). Those are good starting conditions - the LED will "glow" at 50% of maximum intensity. You can also see that the PWM_n signal is connected to KIT_LED1, which is a useful confirmation.
The last edit is to set up a source clock, which follows a very similar path to what we just did. Click on the Clock Signal, under Inputs, and choose "8 bit Divider 2 clk". In the pull-down you will see all the other clocks that are set up by the template. You could reuse one of those if you wish to share the resources, but this is a simple application that will only use a fraction of available resources so I am keeping things simple. Click on the chain to jump to the clocks tab. Notice that the clock is enabled for you, with a divider of 1, giving a 72MHz frequency for the counter. That's plenty o' Hertz! Save the changes and exit the configurator.
In the GeneratedSource/cycfg_peripherals.h file you will find these new defines that we will use to start the PWM.
#define LED1_PWM_NUM 14UL
#define LED1_PWM_MASK (1UL << 14)
In cycfg_peripherals.c there is a configuration struct. All those PWM options are really impressive but I am glad I do not have to set them up by hand!
const cy_stc_tcpwm_pwm_config_t LED1_PWM_config =
.pwmMode = CY_TCPWM_PWM_MODE_PWM,
.clockPrescaler = CY_TCPWM_PWM_PRESCALER_DIVBY_1,
.pwmAlignment = CY_TCPWM_PWM_LEFT_ALIGN,
.deadTimeClocks = 0,
.runMode = CY_TCPWM_PWM_CONTINUOUS,
.period0 = 32768,
.period1 = 32768,
.enablePeriodSwap = false,
.compare0 = 16384,
.compare1 = 16384,
.enableCompareSwap = false,
.interruptSources = CY_TCPWM_INT_NONE,
.invertPWMOut = CY_TCPWM_PWM_INVERT_DISABLE,
.invertPWMOutN = CY_TCPWM_PWM_INVERT_DISABLE,
.killMode = CY_TCPWM_PWM_STOP_ON_KILL,
.swapInputMode = LED1_PWM_INPUT_DISABLED & 0x3U,
.swapInput = CY_TCPWM_INPUT_0,
.reloadInputMode = LED1_PWM_INPUT_DISABLED & 0x3U,
.reloadInput = CY_TCPWM_INPUT_0,
.startInputMode = LED1_PWM_INPUT_DISABLED & 0x3U,
.startInput = CY_TCPWM_INPUT_0,
.killInputMode = LED1_PWM_INPUT_DISABLED & 0x3U,
.killInput = CY_TCPWM_INPUT_0,
.countInputMode = LED1_PWM_INPUT_DISABLED & 0x3U,
.countInput = CY_TCPWM_INPUT_1,
Add these lines to your main() function (in Source/main.c) before the for loop.
Cy_TCPWM_PWM_Init( LED1_PWM_HW, LED1_PWM_NUM, &LED1_PWM_config );
Cy_TCPWM_TriggerStart( LED1_PWM_HW, LED1_PWM_MASK );
Cy_TCPWM_PWM_Enable( LED1_PWM_HW, LED1_PWM_NUM );
Notice that the trigger function works on all 24 TCPWMs in the block, and so we pass a MASK argument, not the individual TCPWM number. LED1_PWM_MASK is just a 1 shifted left by LED1_PWM_NUM and so you can see how to control and synchronize the triggering of individual TCPWMs by ORing the masks.
When you build and program the application into your Pioneer kit the LED should glow quite brightly. This is because a 50% duty cycle is almost as bright as 100% to our eyes. Even your young ones! A household mirror reflects about 50% of the light that hits it and your reflection does not appear especially dim! So play with the duty cycle to reduce the brightness. You can do this by editing the compare value in the configurator or by calling the Cy_TCPWM_PWM_SetCompare0() function to change it your C code (use the Compare0 function because the PWM supports two period and compare values, which we will play with next time). If you have never done this before I think you will be surprised at how low the duty cycle has to be in order to really dim the LED.