Skip navigation
Home > All Places > ModusToolbox > Blog > 2019 > February
2019

On Windows 10, mbed build times are greatly affected by Windows Defender scan. To speed up the builds, follow the steps below. The steps basically add process and folder exclusions to Windows Defender.

 

  • Open Windows PowerShell:
    • Click Start
    • Type in PowerShell
    • Right Click Windows PowerShell in the search results and then click Run as Administrator
    • Run the following commands depending upon the toolchain that you are using

 

Add-MpPreference -ExclusionPath "C:\mbed-cli\" <-- replace with your mbed cli folder path

Add-MpPreference -ExclusionPath "D:\mbed\" <--  replace with your mbed working folder path

 

Add-MpPreference -ExclusionProcess "cmd.exe"

Add-MpPreference -ExclusionProcess "make.exe"

Add-MpPreference -ExclusionProcess "python.exe”

Add-MpPreference -ExclusionProcess "cc1.exe”

Add-MpPreference -ExclusionProcess "cc1plus.exe”

Add-MpPreference -ExclusionProcess "mbed.exe”

Add-MpPreference -ExclusionProcess "sh.exe”

Add-MpPreference -ExclusionProcess "as.exe”

 

Using GCC ARM ToolChain

 

Add-MpPreference -ExclusionPath "C:\Program Files (x86)\GNU Tools ARM Embedded\6 2017-q2-update\bin\" <-- change this to your GCC installation path

 

Add-MpPreference -ExclusionProcess "arm-none-eabi-gcc.exe"

Add-MpPreference -ExclusionProcess "arm-none-eabi-cpp.exe"

Add-MpPreference -ExclusionProcess "arm-none-eabi-g++.exe"

Add-MpPreference -ExclusionProcess "arm-none-eabi-objcopy.exe"

Add-MpPreference -ExclusionProcess "arm-none-eabi-as.exe"

Add-MpPreference -ExclusionProcess "arm-none-eabi-ar.exe"

 

Using GCC ARM ToolChain Provided with ModusToolbox

 

Add-MpPreference -ExclusionPath "C:\Users\snvn\ModusToolbox_1.1\tools\gcc-7.2.1-1.0\bin\" <-- change this to your ModusToolbox installation path

 

Add-MpPreference -ExclusionProcess "ModusToolbox.exe"

Add-MpPreference -ExclusionProcess "arm-none-eabi-gcc.exe"

Add-MpPreference -ExclusionProcess "arm-none-eabi-cpp.exe"

Add-MpPreference -ExclusionProcess "arm-none-eabi-g++.exe"

Add-MpPreference -ExclusionProcess "arm-none-eabi-objcopy.exe"

Add-MpPreference -ExclusionProcess "arm-none-eabi-as.exe"

Add-MpPreference -ExclusionProcess "arm-none-eabi-ar.exe"

 

Using IAR ToolChain

 

Add-MpPreference -ExclusionPath "C:\Program Files (x86)\IAR Systems\Embedded Workbench 7.8\arm" <-- change this to your IAR installation path

 

Add-MpPreference -ExclusionProcess "iccarm.exe”

Add-MpPreference -ExclusionProcess "iasmarm.exe”

Add-MpPreference -ExclusionProcess "ilinkarm.exe”

 

Using ARMCC ToolChain

 

Add-MpPreference -ExclusionPath "C:\Keil_v5\ARM" <-- change this to your ARMCC installation path

 

Add-MpPreference -ExclusionProcess "armar.exe”

Add-MpPreference -ExclusionProcess "armasm.exe”

Add-MpPreference -ExclusionProcess "armcc.exe”

Add-MpPreference -ExclusionProcess "armlink.exe”

 

  • By default, mbed cli uses parallel builds, that’s why you will notice that a simple mbed compile command spawns a lot of processes. That feature is not enabled by default in ModusToolbox 1.1 for mbed imports. Please follow the page below to enable it for each project. Set it to an optimal number as suggested by the IDE.

How do I enable parallel builds within Eclipse?

 

  • Further, Eclipse/ModusToolbox 1.1, by default, tries to refresh the workspace and index the files as the build progresses. This will slow down your machine. Follow the page below to stop that from happening:

Eclipse stop background refresh

 

  • If your mbed working folder is inside your corporate OneDrive sync folder, pause the OneDrive sync. The sync process hogs CPU and will slow down the build time further

 

  • Build speed could be further increased by using a pre-built static library of the mbed-os. Thanks to my colleague K Aadhi Vairava Sundaram for this tip.
    • Import required example:
    • Git clone mbed-os separately
      • use mbed import <commit code> from above
      • change directory to mbed-os
      • Run mbed compile -m <your compile target> -t <your toolchain> --library
    • copy generated library to example folder
      • copy BUILD/libraries/mbed-os/*  ../mbed-os-example-blinky/mbed-os
    • Build example
      • mbed compile --toolchain <your toolchain> --target <your compile target>

 

Note that although mbed-os library can be built for multiple targets and multiple toolchains, only one library at time can be copied to example folder.

Last time I blogged (is that really a word? what a strange world this is) I wrote about the ModusToolbox device configurator. We set up a pair of pins, one called "LED" and the other "SW" (because, it turns out that I am too lazy to write "Switch" or "Button"). Anyway, I mentioned that the configurator generates the setup code for the pins and that I would explain how that works. So here goes...

At the top of main() there is a function call to "init_cycfg_all()" and that's where the pins get initialized. That function lives in a file called cycfg.c which you can find in the GeneratedSource folder.

You can see here that this folder is actually a link (see the tiny arrow on the folder icon) to the configuration project. That project is the one that contains the design.modus file, which contains all the selections and information from the configurators, and the source files generated when I saved my configuration.

Also in this folder are the configuration files for connectivity, which means the signals that are connected inside the PSoC, platform, which is the power, debug and clock setup code, and pins, which we are most interested in.

 

First, here is the meat of cycfg.c, showing the init_cycfg_all() function.

 

#include "cycfg.h"

void init_cycfg_all()
{
    init_cycfg_pins();
    init_cycfg_platform();
    init_cycfg_connectivity();
}

 

I am sure you'll be flabbergasted to learn that init_cycfg_pins() is in cycfg_pins.c - this is pretty simple stuff (by design - so you can dig in and really understand what the tools are doing for you). What does that function look like? Well, this...

 

void init_cycfg_pins(void)
{
    Cy_GPIO_Pin_Init(SW_PORT, SW_PIN, &SW_config);
    Cy_GPIO_Pin_Init(LED_PORT, LED_PIN, &LED_config);
    Cy_GPIO_Pin_Init(SWO_PORT, SWO_PIN, &SWO_config);
    Cy_GPIO_Pin_Init(SWDIO_PORT, SWDIO_PIN, &SWDIO_config);
    Cy_GPIO_Pin_Init(SWCLK_PORT, SWCLK_PIN, &SWCLK_config);
}

 

The pins are set up with a call to a PDL function that accepts the port address, pin number within the port, and a pointer to a configuration struct. There are five calls - one for LED, one for SW, and three for debugging - SWCLK, SWDIO, and SWO. It's all simple enough, but where do those arguments come from? The ports and pins are defines and they come from cycfg_pins.h. Here is a snippet for SW, the input pin.

 

#define SW_PORT GPIO_PRT0
#define SW_PIN 4U
#define SW_NUM 4U
#define SW_DRIVEMODE CY_GPIO_DM_PULLUP
#define SW_INIT_DRIVESTATE 1
#ifndef ioss_0_port_0_pin_4_HSIOM
#define ioss_0_port_0_pin_4_HSIOM HSIOM_SEL_GPIO
#endif
#define SW_HSIOM ioss_0_port_0_pin_4_HSIOM
#define SW_IRQ ioss_interrupts_gpio_0_IRQn

 

 

I'll go through all of those defines in order. SW_PORT is the crucial macro, it maps to a pointer to the port 0 hardware:

#define GPIO_PRT0                               ((GPIO_PRT_Type*) &GPIO->PRT[0])

I am glad that's generated for me!

Next there is SW_PIN, the pin number. SW_NUM is always the same as the PIN macro - the "NUM" style is used for lots of peripherals (which I'll show you next time) and so we generate two macros, in case you are lazy like me and always want to use one style. it's your choice.

After that we have the drive mode - PULLUP creates an active low button. DRIVESTATE is the initial pin value (so pin reads get the right result before the button is pressed). Then there are some definitions of internal connections, which are unused for software-driven GPIOs. Lastly, there is the IRQ definition, which is useful when you trigger interrupts from the pin and REALLY useful when you trigger interrupts on multiple pins in the same port (another subject for another day).

All that explains where the first two arguments come from. The last one is a C variable and it is back in the cycfg_pins.c file. Here is the configuration struct for SW.

 

const cy_stc_gpio_pin_config_t SW_config =
{
    .outVal = 1,
    .driveMode = CY_GPIO_DM_PULLUP,
    .hsiom = SW_HSIOM,
    .intEdge = CY_GPIO_INTR_DISABLE,
    .intMask = 0UL,
    .vtrip = CY_GPIO_VTRIP_CMOS,
    .slewRate = CY_GPIO_SLEW_FAST,
    .driveSel = CY_GPIO_DRIVE_FULL,
    .vregEn = 0UL,
    .ibufMode = 0UL,
    .vtripSel = 0UL,
    .vrefSel = 0UL,
    .vohSel = 0UL,
};

 

I will not bore you explaining all of that but a few things are intreesting. Firstly, the struct is const. That means it is stored in flash and not copied into RAM on startup. This saves memory but, if you want to change the struct at run-time, you can turn off the const from the device configurator, by un-checking "Store Config in Flash". Note that this option saves RAM, but not flash, because the INITDATA always has to be kept in non-volatile memory.

 

 

Back in the C code you can see that the pin initialization, like the PULLUP and interrupt settings, is all contained in the struct. The end result of all this code is that device initialization is an automated function with PSoC in ModusToolbox but, if you need to over-ride these choices for any reason, it is really easy to make a copy of the generated code, edit it and simply call your new function and not the generated one!

Next time, I'll take you a little deeper into the configurators and set up some more interesting peripherals, and then, if all goes well, I'll introduce you to the wonders of the CapSense configurator and its pal, the CapSense tuner!

I promised to tell you about the FreeRTOS templates and, now is the time, so, here we go... In the last post I used the PioneerKitApp template. This time I'll try PioneerKitAppFreeRTOS. It's a bit of a mouthful.

PioneerKitAppFreeRTOS starter template

When the project is created it looks identical to last week's project except this one includes the FreeRTOS code and has an example FreeRTOSConfig.h in your source folder.

ModusToolbox application with Amazon FreeRTOS middleware

You will see that the code in main.c is quite different from the other template. First of all, in main(), it creates a task and starts the FreeRTOS scheduler.

    BaseType_t retval;      retval = xTaskCreate( blinky, BLINKY_NAME, BLINKY_STACK_SIZE, NULL, BLINKY_PRIORITY, NULL );     if( pdPASS == retval )     {         vTaskStartScheduler();     }

 

Notice how the code checks the return code from the create call because that is the easiest way to avoid out of memory start-up problems in the OS. I had no problems because I configured ooooooodles of RAM for my tasks, and I only have one! The task itself is implemented in a function called blinky() and has the following parameters.

/* Defines for a simple task */ #define BLINKY_NAME         ("Blinky") #define BLINKY_STACK_SIZE   (1024) #define BLINKY_PRIORITY     (5) #define BLINKY_DELAY_TICKS  (500)

 

The two NULL arguments are unused options you can use when creating tasks - the first is a pointer to to an argument that gets passed into blinky() and the second is a pointer for the OS to return the created task's ID. I'll go into more detail on that later.

And here is the blinky() code. It's very complex (not) - toggle the pin and wait for a while!

void blinky( void * arg ) { (void)arg; for(;;) { /* Toggle the user LED periodically */ Cy_GPIO_Inv( KIT_LED1_PORT, KIT_LED1_PIN ); vTaskDelay( BLINKY_DELAY_TICKS ); } }

 

You can build and program the application to verify the OS running - the orange LED blinks at 1Hz (BLINKY_DELAY_TICKS is 500, and each tick is 1ms, so the LED is off for 500ms and on for 500ms). Let's extend that a little by blinking another LED. First, let's make a copy of blinky() and change the LED and frequency.

void blinky2( void * arg ) { (void)arg; for(;;) { /* Toggle the user LED periodically */ Cy_GPIO_Inv( KIT_LED2_PORT, KIT_LED2_PIN ); vTaskDelay( BLINKY_DELAY_TICKS / 3 ); } } 

 

Then create the new task with a new name.

#define BLINKY2_NAME        ("Blinky2")  retval = xTaskCreate( blinky, BLINKY_NAME, BLINKY_STACK_SIZE, NULL, BLINKY_PRIORITY, NULL ); if( pdPASS == retval ) retval = xTaskCreate( blinky2, BLINKY2_NAME, BLINKY_STACK_SIZE, NULL, BLINKY_PRIORITY, NULL );

 

When I program the application the red LED blinks three times faster than the orange one.

I do not know about you but that copied code in blinky() and blinky2() drives me a bit nutty. Its the same code! I want to reuse it - for all the LEDs. The functions only differ in the pin they toggle and the time they go to sleep. Let's use that argument pointer I talked about above. First I create a struct to contain the arguments and five variables for each of the LEDs on the kit (red, orange, and the RGB).

struct led_t { GPIO_PRT_Type* port; unsigned int   pin; int            rate; };  const struct led_t LED1 = { KIT_LED1_PORT,  KIT_LED1_PIN,  300 }; const struct led_t LED2 = { KIT_LED2_PORT,  KIT_LED2_PIN,  600 }; const struct led_t LEDR = { KIT_RGB_R_PORT, KIT_RGB_R_PIN, 100 }; const struct led_t LEDG = { KIT_RGB_G_PORT, KIT_RGB_G_PIN, 150 }; const struct led_t LEDB = { KIT_RGB_B_PORT, KIT_RGB_B_PIN, 750 };

 

Then create more tasks and give each call the argument pointer.

    retval = xTaskCreate( blinky, "LED 1", BLINKY_STACK_SIZE, (void *)&LED1, BLINKY_PRIORITY, NULL ); if( pdPASS == retval ) retval = xTaskCreate( blinky, "LED 2", BLINKY_STACK_SIZE, (void *)&LED2, BLINKY_PRIORITY, NULL ); if( pdPASS == retval ) retval = xTaskCreate( blinky, "LED R", BLINKY_STACK_SIZE, (void *)&LEDR, BLINKY_PRIORITY, NULL ); if( pdPASS == retval ) retval = xTaskCreate( blinky, "LED G", BLINKY_STACK_SIZE, (void *)&LEDG, BLINKY_PRIORITY, NULL ); if( pdPASS == retval ) retval = xTaskCreate( blinky, "LED B", BLINKY_STACK_SIZE, (void *)&LEDB, BLINKY_PRIORITY, NULL );

 

Lastly, I deleted blinky2() and modified blinky() to use the argument.

void blinky( void * arg ) { struct led_t *led = (struct led_t *)arg; for(;;) { /* Toggle the user LED periodically */ Cy_GPIO_Inv( led->port, led->pin ); vTaskDelay( led->rate ); } }

 

Now, when I program the kit, it twinkles like the lights in my Christmas tree (that I already miss terribly) and I did not have to write 5 almost-identical tasks to make it work. I attached the source code to this article for you, in case you want to play with the LEDs (note: I had to change the file extension to "txt" because, apparently, "c" files are dangerous!!!).

The point of all this is not really to create a Christmas tree replacement. Hopefully it's obvious that the template is a really nice way to get started on a multi-tasking application because it configures all the basic peripherals on the board, sets up the OS, and gives you a stub task (blinky) that you can modify and copy to make your own tasks (that do a little more than blink LEDs). Happy tasking!

I have recently shown you two of the most helpful starter templates in ModusToolbox - PioneerKitApp and PioneerKitAppFreeRTOS. Just as an FYI, if you have the PSoC 6 prototyping kit instead, there are equivalent templates for that kit called ProtoKitApp and ProtoKitAppFreeRTOS. All of these templates set up some kit basics for you so that you can jump straight into writing your application code. Essential peripherals like clocks, debugging, LED and button pins, CapSense, plus the I2C and UART bridge peripherals are all pre-configured in the design.modus file. If you do not like our configuration, you are very welcome to launch the configurators and modify the setup the way you need it. How should you go about that?

Well, the short answer is to just double-click on the design.modus file and have a good time! But, since I am feeling a little more helpful that that today, a walk-through of how to set up peripherals from scratch might be good. I'll start with a new project and will use (yet) another project template - EmptyPSoC6App. This template sets up the barest of bare minima - just the clocks and debug.

In the EmptyPSoC6App_config project, find and double-click on design-modus to open the device configurator. There are five panels in the GUI - Peripherals, Pins, Platform, Peripheral Clocks, and DMA. Switch to the Platform tab and you will see the very minimal setup from the template.

 

MTconfigs1.png

In the Resource panel (left) you will see Debug, Power and System Clocks. If you just select each of them in turn you will see, in the Parameters panel (right), that SWD and SWO are enabled for debugging, the power supply is set to 3.3V, and the PLL is configured to generate a frequency of 144MHz. For your early projects there is probably no reason to change any of these values so I'll move on to configuring an LED and button.

 

Switch over to the Pins tab and open the tree display to see the pins in Port 13. Click on the checkbox next to pin 7 to start setting up the pin. Conveniently, this pin is attached to an LED on both of our Pioneer kits and prototyping kit so this should work on whatever board you have. Start by giving the pin a name (or alias if you want to be fancy) - I am using "LED". Then set the Drive Mode of the pin to "Strong Drive. Iinput buffer off" using the drop-down menu in the parameters panel.

 

MTconfigs2.png

 

Next repeat that basic sequence for Port 0. Pin 5 is connected to a push button (again, thanks to my pal Ronak who designs all these boards, it is the same pin on all kits) with I am calling "SW". To configure the input choose "Resistive Pull-Up. Input buffer on" drive mode so that my active-low button reads 1 when up and 0 when pressed. Save the edits and switch back to the IDE.

MTconfigs3.png

In the EmptyPSoC6App_mainapp project, open the Source folder and double-click on main.c to open it in the editor and write the following code in the infinite loop.

 

state = Cy_GPIO_Read( SW_PORT, SW_PIN );

Cy_GPIO_Write( LED_PORT, LED_PIN, state );

 

That is all we need to prove that our configuration works. In the IDE Quick Panel click on "EmptyPSoC6App Program (KitProg3)" and, if you did everything right, it will build the application and program the kit. When you press and hold the button, the LED will turn on...

 

So, there are the basics for device configuration with ModusToolbox. Next time I'll show you the firmware that makes all this work and get a little more fancy with some PWM tricks and fun with the serial communication blocks.