27. SUOTA Application Development

In this section we will describe the process of adding SUOTA capability to an example application. To do this, we will use the empty_peripheral_template example, provided within the DA145xx SDK, and describe the required modifications.

27.1. Empty Peripheral Template Modifications

Using the Keil IDE, open the empty_peripheral_template example application located in the \projects\target_apps\template folder of the SDK and then make the modifications detailed in the following sections.

..note :: It is recommended you use a clean version of the empty_peripheral_template example application, not the version that was previously modified as part of this tutorial. One way to achieve this is to simply downloaded SDK6 again.

27.1.1. Setting the Application Version Number

Create a file called user_version.h, containing the following, and place it in the \projects\target_apps\template\empty_peripheral_template\src folder:

#define SDK_VERSION "1.0.0"

Next, we will use this version number to populate the software version number characteristic in the Device Information Service. To do this we first need to add the following callback function to the user_empty_peripheral_template.c file:

#include "user_version.h"
void user_app_on_db_init_complete(void)
{
    /* Set application version number in DISS */
    char sw_version[] = SDK_VERSION;
    struct diss_set_value_req *req = KE_MSG_ALLOC_DYN(DISS_SET_VALUE_REQ,
                                                      prf_get_task_from_id(TASK_ID_DISS),
                                                      TASK_APP,
                                                      diss_set_value_req,
                                                      sizeof(sw_version));
    req->value = DIS_SW_REV_STR_CHAR;
    req->length = sizeof(sw_version);
    memcpy(req->data, sw_version, sizeof(sw_version));
    ke_msg_send(req);

    default_app_on_db_init_complete();
}

Next add a prototype for this function to the user_empty_peripheral_template.h file as follows:

void user_app_on_db_init_complete(void);

Finally, redirect the stack to call this application defined callback by changing the definition of the app_on_db_init_complete pointer in the user_callbackconfig.h file to the following:

.app_on_db_init_complete            = user_app_on_db_init_complete,

27.1.2. Configure the SUOTA Service

The source files that implement the SUOTA service are already present in the empty_peripheral_template example. All that is needed is to enable them by adding the following to the user_profiles_config.h file:

#define CFG_PRF_SUOTAR

Next define the type of non-volatile memory to be used, in this case we will use SPI flash. To do this, change the definition of the CFG_SPI_FLASH_ENABLE macro in the file da1458x_config_basic.h to the following:

#define CFG_SPI_FLASH_ENABLE

Increase the amount of memory available for the storage of retained data by updating the CFG_RET_DATA_SIZE macro in the da145x_config_advanced.h file as follows:

#define CFG_RET_DATA_SIZE    (2200)

Also increase the database heap size by uncommenting the DB_HEAP_SZ macro in the same file and changing the value as follows:

#define DB_HEAP_SZ           (2048)

27.1.3. Application Callbacks

Add the following callback function to the user_empty_peripheral_template.c file to handle events from the SUOTA service:

void on_suotar_status_change(const uint8_t suotar_event)
{
#if (!SUOTAR_SPI_DISABLE)
    uint8_t dev_id;

    // Release Flash from power down
    spi_flash_release_from_power_down();

    // Try to auto-detect the device
    spi_flash_auto_detect(&dev_id);

    if (suotar_event == SUOTAR_END)
    {
        // Power down SPI Flash
        spi_flash_power_down();
    }
#endif
}

Add the following code to the end of the user_app_on_disconnect function that is found in the user_empty_peripheral_template.c file:

// Issue a platform reset when it is requested by the suotar procedure
if (suota_state.reboot_requested)
{
    // Reboot request will be served
    suota_state.reboot_requested = 0;

    // Platform reset
    platform_reset(RESET_AFTER_SUOTA_UPDATE);
}

27.1.5. Setup the SPI Flash Interface

Next the SPI interface has to be configured so it can be used to communicate with the external flash memory. To do this, first add the following to the user_periph_setup.h file:

// Define SPI Pads
#if defined (__DA14531__)
    #define SPI_EN_PORT             GPIO_PORT_0
    #define SPI_EN_PIN              GPIO_PIN_1

    #define SPI_CLK_PORT            GPIO_PORT_0
    #define SPI_CLK_PIN             GPIO_PIN_4

    #define SPI_DO_PORT             GPIO_PORT_0
    #define SPI_DO_PIN              GPIO_PIN_0

    #define SPI_DI_PORT             GPIO_PORT_0
    #define SPI_DI_PIN              GPIO_PIN_3

#elif !defined (__DA14586__)
    #define SPI_EN_PORT             GPIO_PORT_0
    #define SPI_EN_PIN              GPIO_PIN_3

    #define SPI_CLK_PORT            GPIO_PORT_0
    #define SPI_CLK_PIN             GPIO_PIN_0

    #define SPI_DO_PORT             GPIO_PORT_0
    #define SPI_DO_PIN              GPIO_PIN_6

    #define SPI_DI_PORT             GPIO_PORT_0
    #define SPI_DI_PIN              GPIO_PIN_5
#endif

// Define SPI Configuration
#define SPI_MS_MODE                 SPI_MS_MODE_MASTER
#define SPI_CP_MODE                 SPI_CP_MODE_0
#define SPI_WSZ                     SPI_MODE_8BIT
#define SPI_CS                      SPI_CS_0

#if defined (__DA14531__)
    #define SPI_SPEED_MODE          SPI_SPEED_MODE_4MHz
    #define SPI_EDGE_CAPTURE        SPI_MASTER_EDGE_CAPTURE
#else // (DA14585, DA14586)
    #define SPI_SPEED_MODE          SPI_SPEED_MODE_4MHz
#endif

#if !defined (__DA14586__)
    #define SPI_FLASH_DEV_SIZE      (256 * 1024)
#endif

Next, add the following to the user_periph_setup.c file:

#if defined (CFG_SPI_FLASH_ENABLE)
    #include "spi.h"
    #include "spi_flash.h"
#endif

#if defined (CFG_SPI_FLASH_ENABLE)
    // Configuration struct for SPI
    static const spi_cfg_t spi_cfg = {
    .spi_ms = SPI_MS_MODE,
    .spi_cp = SPI_CP_MODE,
    .spi_speed = SPI_SPEED_MODE,
    .spi_wsz = SPI_WSZ,
    .spi_cs = SPI_CS,
    .cs_pad.port = SPI_EN_PORT,
    .cs_pad.pin = SPI_EN_PIN,
    #if defined (__DA14531__)
        .spi_capture = SPI_EDGE_CAPTURE,
    #endif
    };

    // Configuration struct for SPI FLASH
    static const spi_flash_cfg_t spi_flash_cfg = {
        .chip_size = SPI_FLASH_DEV_SIZE,
    };
#endif

Then in the GPIO_reservations function add the following:

#if defined (CFG_SPI_FLASH_ENABLE) && !defined (__DA14586__)
    // SPI Flash
    RESERVE_GPIO(SPI_EN, SPI_EN_PORT, SPI_EN_PIN, PID_SPI_EN);
    RESERVE_GPIO(SPI_CLK, SPI_CLK_PORT, SPI_CLK_PIN, PID_SPI_CLK);
    RESERVE_GPIO(SPI_DO, SPI_DO_PORT, SPI_DO_PIN, PID_SPI_DO);
    RESERVE_GPIO(SPI_DI, SPI_DI_PORT, SPI_DI_PIN, PID_SPI_DI);
#endif

Next, add the following to the set_pad_functions function:

#if defined (CFG_SPI_FLASH_ENABLE)
    // SPI Flash
    GPIO_ConfigurePin(SPI_EN_PORT, SPI_EN_PIN, OUTPUT, PID_SPI_EN, true);
    GPIO_ConfigurePin(SPI_CLK_PORT, SPI_CLK_PIN, OUTPUT, PID_SPI_CLK, false);
    GPIO_ConfigurePin(SPI_DO_PORT, SPI_DO_PIN, OUTPUT, PID_SPI_DO, false);
    GPIO_ConfigurePin(SPI_DI_PORT, SPI_DI_PIN, INPUT, PID_SPI_DI, false);
#endif

Finally, in the periph_init function, add the GPIO_Disable_HW_Reset function call as shown below:

#if defined (__DA14531__)
    // Disable HW Reset functionality of P0_0
    GPIO_Disable_HW_Reset();

    // In Boost mode enable the DCDC converter to supply VBAT_HIGH for the used GPIOs
    syscntl_dcdc_turn_on_in_boost(SYSCNTL_DCDC_LEVEL_3V0);
#else
    // Power up peripherals' power domain
    SetBits16(PMU_CTRL_REG, PERIPH_SLEEP, 0);
    while (!(GetWord16(SYS_STAT_REG) & PER_IS_UP));
    SetBits16(CLK_16M_REG, XTAL16_BIAS_SH_ENABLE, 1);
#endif

Then add the following, just before the call to the set_pad_functions function:

#if defined (CFG_SPI_FLASH_ENABLE)
    // Configure SPI Flash environment
    spi_flash_configure_env(&spi_flash_cfg);

    // Initialize SPI
    spi_initialize(&spi_cfg);
#endif

27.1.6. Building Images & Testing

Once all the above modifications are complete, and the empty_peripheral_template project successfully compiles, follow the instructions in the SUOTA Demonstration section to create SUOTA images and perform a software update over-the-air.