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.4. Advertise the SUOTA Service
If you want to use the Dialog SUOTA application then your device must advertise the SUOTA service. To do this, change the definition of the USER_ADVERTISE_DATA
macro, found in the user_config.h file to the following:
#define USER_ADVERTISE_DATA ("\x03"\
ADV_TYPE_COMPLETE_LIST_16BIT_SERVICE_IDS\
ADV_UUID_SUOTAR_SERVICE)
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.