Set Up a Basic DFU Application in ModusToolbox 2.0 for PSoC 6 MCU Device – KBA229130

Version 6

    Author: WangS_81           Version: **

     

    Translation - Japanese: ModusToolbox 2.0 を使用した PSoC 6 MCU の基本的な DFU アプリケーションの設定方法 - Community Translated (JA)

     

    This article assumes that you are familiar with using ModusToolbox™ and device firmware update (DFU).

    The following steps are to set up and build basic DFU loader and loadable applications for PSoC® 6 MCU in ModusToolbox 2.0. The target board is the CY8CKIT-062-WIFI-BT DVK, and communication protocol is I2C. See the kit user guide for details. See AN213924 - PSoC 6 MCU Device Firmware Update Software Development Kit Guide for details on the device firmware update process.

    Note: Ensure that the DVK has the KitProg3. See Chapter 6 of KitProg3 User Guide for information on updating KitProg2 to KitProg3 if your kit is running KitProg2.

    Step 1: Prepare the Projects

    1. Create a project for CY8CKIT-WIFI-BT using the “Hello_World” application, and name it “DFU_App0_I2C”. This is used as the DFU loader application.
    2. Create a project for the DFU loadable application in the same way and name it “DFU_App1_Hello_World”.
    3. Include the DFU middleware into each project through ModusToolbox Library Manager. To include the library, right-click the target project, and navigate to ModusToolbox > Library Manager. You can also download it from GitHub and copy it to the project manually.
    4. Include a DFU header in main.c of each project to get access to the DFU library API.

                  include "cycfg.h"
    include "cy_dfu.h"

     

    Step 2: Set up the Loader application (DFU_App0_I2C)

    1. Copy the configuration files dfu_user.c and dfu_user.h from the libs\dfu\config directory and put them in the project directory.
    2. Copy the transport files from the \config directory and put them in the project directory. In this case, I2C requires transport_i2c.h and transport_i2c.c.
    3. Copy the app0 linker script files from libs\dfu\linker_scripts\TOOLCHAIN_<COMPILER>\ in the project root. For the GCC ARM compiler, copy libs\dfu\linker_scripts\TOOLCHAIN_GCC_ARM\dfu_cm4_app0.ld.
    4. Configure the serial communication block (SCB) with I2C communication using ModusToolbox Device Configurator. For CY8CKIT-062-WIFI-BT, use SCB3, which is connected to the KitProg3 interface. See Figure 1 for specific settings of SCB3.

    Figure 1

    Step 3:  Update Loader DFU_App0_I2C main.c

    1. Clear the original code in main.c and keep only an empty for loop in main().

    2. Add the following code to include a      DFU reset handler in main.c to      start the appropriate application after a reset.

    /*******************************************************************************

    * Function Name: Cy_OnResetUser

    ********************************************************************************

    *

    *  This function is called at the start of Reset_Handler().

    *  DFU requires it to call Cy_DFU_OnResetApp0() in app#0.

    *

    *******************************************************************************/

    void Cy_OnResetUser(void)

    {

        Cy_DFU_OnResetApp0();

    }

    3. Add the following code to main.c to initialize the variables      and call the DFU initialization function in main().

    /*

    * Used to count seconds

    */

    uint32_t count = 0;

    /* Status codes for DFU API */

    cy_en_dfu_status_t status;

    /*

    * DFU state, one of the:

    * - CY_DFU_STATE_NONE

    * - CY_DFU_STATE_UPDATING

    * - CY_DFU_STATE_FINISHED

    * - CY_DFU_STATE_FAILED

    */

    uint32_t state;

    /* Timeout for Cy_DFU_Continue(), in milliseconds */

    const uint32_t paramsTimeout = 20u;

     

    /* Buffer to store DFU commands */

    CY_ALIGN(4) static uint8_t buffer[CY_DFU_SIZEOF_DATA_BUFFER];

    /* Buffer for DFU data packets for transport API */

    CY_ALIGN(4) static uint8_t packet[CY_DFU_SIZEOF_CMD_BUFFER ];

    /* DFU params, used to configure DFU */

    cy_stc_dfu_params_t dfuParams;

    /* Initialize dfuParams structure */

    dfuParams.timeout          = paramsTimeout;

    dfuParams.dataBuffer       = &buffer[0];

    dfuParams.packetBuffer     = &packet[0];

    status = Cy_DFU_Init(&state, &dfuParams);

    /* Set up the device based on configurator selections */

    init_cycfg_all();

    /* enable interrupts */

    __enable_irq();

     

    /* Initialize DFU communication */

     

    Cy_DFU_TransportStart();

    4. Add the following code to main.c to initialize the DFU      transport layer in main().

    /* Initialize DFU communication */

    Cy_DFU_TransportStart();

    5. Add the following code to main.c to update the main loop with      Host Common/Response protocol processing.

    status = Cy_DFU_Continue(&state, &dfuParams);

    ++count;

    if (state == CY_DFU_STATE_FINISHED)

    {

    /* Finished loading the application image */

    /* Validate DFU application, if it is valid then switch to it */

    status = Cy_DFU_ValidateApp(1u, &dfuParams);

    if (status == CY_DFU_SUCCESS)

    {

    Cy_DFU_TransportStop();

    Cy_DFU_ExecuteApp(1u);

    }

    else if (status == CY_DFU_ERROR_VERIFY)

    {

    /*

    * Restarts loading, an alternatives are to Halt MCU here

    * or switch to the other app if it is valid.

    * Error code may be handled here, i.e. print to debug UART.

    */

    status = Cy_DFU_Init(&state, &dfuParams);

    Cy_DFU_TransportReset();

    }

    }

    else if (state == CY_DFU_STATE_FAILED)

    {

    /* An error has happened during the loading process */

    /* Handle it here */

    /* In this Code Example just restart loading process */

    status = Cy_DFU_Init(&state, &dfuParams);

    Cy_DFU_TransportReset();

    }

    else if (state == CY_DFU_STATE_UPDATING)

    {

    uint32_t passed5seconds = (count >= (5000ul/paramsTimeout)) ? 1u : 0u;

    /*

    * if no command has been received during 5 seconds when the loading

    * has started then restart loading.

    */

    if (status == CY_DFU_SUCCESS)

    {

    count = 0u;

    }

    else if (status == CY_DFU_ERROR_TIMEOUT)

    {

    if (passed5seconds != 0u)

    {

    count = 0u;

    Cy_DFU_Init(&state, &dfuParams);

    Cy_DFU_TransportReset();

    }

    }

    else

    {

    count = 0u;

    /* Delay because Transport still may be sending error response to a host */

    Cy_SysLib_Delay(paramsTimeout);

    Cy_DFU_Init(&state, &dfuParams);

    Cy_DFU_TransportReset();

    }

     

    }

    6. Update the main loop with a routine to      switch to the loaded DFU_App1_Hello_World application. For example, switching      the applications by pressing the button SW2 on CY8CKIT-062-WIFI-BT. This      pin is conneted to SW2 on the board.

     

    a.    Enable Pin P0[4] in Device Configurator with the name “PIN_SW2”.

     

    b.    Set/check pin configurator with the specific settings as shown in Figure 2:

    Figure 2

     

    c. Add the following routine in the main loop:

    /* If Button clicked - Switch to App1 if it is valid */

    if (Cy_GPIO_Read(PIN_SW2_PORT, PIN_SW2_PIN) == 0u)

    {

    /* 50 ms delay for button debounce on button press */

    Cy_SysLib_Delay(50u);

    if (Cy_GPIO_Read(PIN_SW2_PORT, PIN_SW2_PIN) == 0u)

    {

    while (Cy_GPIO_Read(PIN_SW2_PORT, PIN_SW2_PIN) == 0u)

    {   /* 50 ms delay for button debounce on button release */

    Cy_SysLib_Delay(50u);

    }

    /* Validate and switch to App1 */

    status = Cy_DFU_ValidateApp(1u, &dfuParams);

    if (status == CY_DFU_SUCCESS)

    {

    Cy_DFU_TransportStop();

    Cy_DFU_ExecuteApp(1u);

    }

    }

     

    }

     

    Step 4: Build and Program Loader DFU_App0_I2C

    1. Update the project makefile to use the DFU      linker script for the appropriate toolchain dfu_cm4_app0. Set LINKER_SCRIPT to the linker script copied to the project      root.

    LINKER_SCRIPT=dfu_cm4_app0.ld

    1. Add a post-build      step to sign the ELF file or sign it manually after the build:

    <MCUELFTOOL> –sign <app>.elf –output <app_signed>.elf –hex <app_signed>.hex.

    For macOS/Linux platform:

    POSTBUILD=$(CY_MCUELFTOOL_DIR)/bin/cymcuelftool --sign $(CY_CONFIG_DIR)/$(APPNAME).elf --hex $(CY_CONFIG_DIR)/$(APPNAME).hex

    For Windows platform:

    POSTBUILD="$(CY_MCUELFTOOL_DIR)/bin/cymcuelftool.exe" --sign $(CY_CONFIG_DIR)/$(APPNAME).elf --hex $(CY_CONFIG_DIR)/$(APPNAME).hex

    1. Build and program the device. 

    Step 5: Set up Loadable DFU_App1_Hello_World

    1. Copy the configuration file dfu_user.h from the libs\dfu\config directory and put it      near main.c in the project root.

    NOTE: Do not copy dfu_user.c to avoid duplication of the metadata structures.

    1. Copy the app1 linker script files from \libs\dfu\linker_scripts\TOOLCHAIN_<COMPILER>\      in the project root. For GCC ARM compiler, copy \libs\dfu\linker_scripts\TOOLCHAIN_GCC_ARM\dfu_cm4_app1.ld.

     

    Step 6: Modify Loadable DFU_App1_Hello_World main.c

    1. Update the main.c      file with adding the .cy_app_signature section. The      existing code remains untouched.

    CY_SECTION(".cy_app_signature") __USED static const uint32_t cy_dfu_appSignature[1];

    1. Initialize the peripheral in the main function:

    init_cycfg_all();

    1. Update the main loop with a routine to switch      to the loader DFU_App0_I2C application to load      another application. For example, switching the applications by pressing      the button SW2 on CY8CKIT-062-WIFI-BT.

    a.    Enable Pin P0[4] in Device Configurator with the name “PIN_SW2”.

    b.    Set/check pin configuration with the same settings of P0[4] in App0.

    c.    Add the following routine in the main loop.

    /* If Button clicked and switch to App0 */

    if (Cy_GPIO_Read(PIN_SW2_PORT, PIN_SW2_PIN) == 0u)

    {

    /* 50 ms delay for button debounce on button press */

    Cy_SysLib_Delay(50u);

    if (Cy_GPIO_Read(PIN_SW2_PORT, PIN_SW2_PIN) == 0u)

    {

    while (Cy_GPIO_Read(PIN_SW2_PORT, PIN_SW2_PIN) == 0u)

    {   /* 50 ms delay for button debounce on button release */

    Cy_SysLib_Delay(50u);

    }

    Cy_DFU_ExecuteApp(0u);

    }

     

    }


    Step 7: Build and Program Patch

     

    1. Update the project makefile to use the DFU      linker script for the appropriate toolchain dfu_cm4_app1. Set the LINKER_SCRIPT variable with the path to the copied linker      script.

    LINKER_SCRIPT=dfu_cm4_app1.ld

    1. Add the post-build step to run the CyMCUElfTool      to generate a patch file in the *.cyacd2 format (see CyMCUElfTool User Guide).

    For macOS/Linux platform:

    POSTBUILD=$(CY_MCUELFTOOL_DIR)/bin/cymcuelftool --sign $(CY_CONFIG_DIR)/$(APPNAME).elf CRC --output $(APPNAME)_crc.elf && \ $(CY_MCUELFTOOL_DIR)/bin/cymcuelftool -P $(APPNAME)_crc.elf --output $(APPNAME)_crc.cyacd2

    For Windows platform:

    POSTBUILD="$(CY_MCUELFTOOL_DIR)/bin/cymcuelftool.exe" --sign $(CY_CONFIG_DIR)/$(APPNAME).elf CRC --output $(APPNAME)_crc.elf && \ "$(CY_MCUELFTOOL_DIR)/bin/cymcuelftool.exe" -P $(APPNAME)_crc.elf --output $(APPNAME)_crc.cyacd2

    1. Build the project.
    2. Open the Device Firmware Update Tool (right-click the target project in ModusToolbox, and then      navigate to ModusToolbox > Device Firmware Update Tool). Connect to the device. Select the generated      .cyacd2 file in the project root      and program it to the device.

    Click Help > View Help and open the ModusToolbox Device Firmware Update Host Tool document for details to use this tool.

    Figure 3

     

    The ‘DFU_App1_Hello_World’ application should start after a successful program. The red LED will blink with default settings of the Hello_World application. Press the SW2 on board to switch between App0 and App1.