5. User Guides
5.1. The BLE Framework
Figure 40 depicts the general architectural scheme used.

Figure 40 BLE Framework architecture
Using a top-down approach, the layers that build-up the BLE Framework functionality can be identified as the following:
The BLE service framework provides BLE services that the application can use “out-of-the-box”, using simple initialization functions and callbacks for the various BLE service events (like the change of an alert level attribute). The functionality of the BLE service framework is built on top of the Dialog BLE API library. The BLE service API header files can be found under
<SDK_ROOT_PATH>/sdk/interfaces/ble/services/include
. The BLE service callbacks are executed by the application task that uses the BLE service framework.The Dialog BLE API is a set of functions that can be used to initiate BLE operations or respond to BLE events. The API header files can be found under the path
<SDK_ROOT_PATH>/sdk/interfaces/ble/api/include
. The API functions can be used to send messages (commands or replies to events) to the BLE manager, either by directly calling the BLE manager command handler or by using queues between the application task and the BLE manager task. The BLE API is called in the context of the application.The BLE manager provides the interface to the BLE functionality of the chip. Application tasks that are based on BLE functionality use the Dialog BLE API to interface with the BLE manager. The BLE manager is a task that stands between the application and the BLE adapter. It uses the BLE adapter to interface with the BLE Stack. The BLE manager uses a Generic Transport Layer (GTL) interface to communicate with the BLE adapter through a command and event queue. The BLE manager is a separate system task.
The BLE adapter is the system task that provides the interface to the BLE Stack. It runs the BLE Stack internal scheduler, receives the commands or the replies to events from the BLE manager, and passes BLE events to the BLE manager. BLE core functionality is implemented by the BLE adapter task.
The BLE Stack is the software stack that implements both the BLE Host and the Low Energy (LE) controller (Link Layer). The BLE Host includes the Logical Link Control and Adaptation Protocol (L2CAP), the Security Manager Protocol (SMP), the Attribute Protocol (ATT), the Generic Attribute Profile (GATT) and the Generic Access Profile (GAP). The BLE Stack API header files, for the DA1470x, can be found under
<SDK_ROOT_PATH>/sdk/interfaces/ble/stack/<device_family>/include
. The BLE Stack software is run under the BLE adapter’s task context.
5.1.1. Developing BLE Applications
One of the main goals of the SmartSnippets™ DA1470x SDK is to simplify the development of BLE applications and achieve a fast time to market. The SmartSnippets™ DA1470x SDK separates the application logic from the BLE Stack implementation and provides a clean and powerful API to interact with the BLE capabilities of the device. The BLE API provides an easy way to configure the BLE device, start air operations and set up the attribute database of a GATT server. The BLE service API provides access to predefined Bluetooth SIG profiles with the definition of only a few callback functions.
The Proximity Reporter (pxp_reporter
) application is the most typical of the BLE applications that are included in the SmartSnippets™ DA1470x SDK. It is a complete and solid example of a BLE application developed on top of the SmartSnippets™ DA1470x SDK. It uses both the Dialog BLE API and the BLE service framework to implement the functionality of a BLE profile.
However, it may not be the simplest example or the best starting point to become familiar with the development of a BLE application from scratch. Instead, there are BLE projects specifically created to serve as starting points for specific BLE applications such as beacons (ble_adv_demo
) or specific roles such as a generic peripheral (ble_peripheral
) or central (ble_central
) device.
The goal of this section is to introduce the various options and examples that exist in the SmartSnippets™ DA1470x SDK which can be used as building blocks for many applications. After a short introduction on where the API header files can be found, each section describes the functionality they implement along with guidance on how they differ from each other. This information is essential when developing a BLE application from scratch.
5.1.2. The BLE API header files
5.1.2.1. Dialog BLE API
All demos and services API can be found in [Ref_04].
In most projects these API header files are symbolically linked to files located in
<SDK_ROOT_PATH>/sdk/interfaces/ble/api/include
.
The API functions are declared across several header files depending on their functionality:
Common API (ble_common.h): Functions used for operations, not specific to a specific BLE device software component. For example:
Function |
Description |
---|---|
ble_register_app() |
Register the application to the BLE Framework so that it can receive BLE event notifications. |
ble_enable() |
Enable the BLE module. |
ble_reset() |
Reset the BLE module. |
ble_central_start() |
Start the device as a BLE central. This is actually a helper function, since it uses API calls ble_enable() and ble_gap_role_set(). |
ble_peripheral_start() |
Start the device as a BLE peripheral. This is also a helper function that uses ble_enable() and ble_gap_role_set(). |
ble_get_event() |
Get a BLE event from the BLE event queue. |
ble_has_event() |
Check if there is an event pending at the BLE event queue. |
ble_handle_event_default() |
Used to define handling of events that are not handled by the added services or the application defined handlers. |
ble_read_tx_power() |
Read controller TX power |
ble_address_to_string() |
Convert bd_address to string |
ble_address_from_string() |
Convert string to bd_address |
ble_set_fem_voltage_trim() |
Set FEM Voltage GPIO values for a specific channel |
GAP & L2CAP APIs (ble_gap.h/ble_l2cap.h): Covers a wide range of operations, like
Device parameters configuration: device role, MTU size, device name exposed in the GAP service attribute, etc.
Air operations: Advertise, scan, connect, respond to connection requests, initiate or respond to connection parameters update, etc.
Security operations: Initiate and respond to a pairing or bonding procedure, set the security level, unpair, etc.
Function |
Description |
---|---|
BLE device configuration |
|
ble_gap_role_get() |
Get the GAP role currently set. |
ble_gap_role_set() |
Set the device GAP roles (central, peripheral, observer, broadcaster). |
ble_gap_mtu_size_get() |
Get the MTU size currently set. |
ble_gap_mtu_size_set() |
Set the MTU size to be used in MTU exchange transactions. |
ble_gap_channel_map_get() |
Get the currently set channel map of the device (the device has to be configured as central). |
ble_gap_channel_map_set() |
Set the channel map of the device (device has to be configured as central). |
ble_gap_address_get() |
Get the currently used BD address of the device. |
ble_gap_address_set() |
Set the BD address of the device. |
ble_gap_device_name_get() |
Get the device name used in the respective attribute of GAP service. |
ble_gap_device_name_set() |
Set the device name used in the respective attribute of GAP service. |
ble_gap_appearance_get() |
Get the appearance used in the respective attribute of GAP service. |
ble_gap_appearance_set() |
Set the appearance used in the respective attribute of GAP service. |
ble_gap_per_pref_conn_params_get() |
Get the peripheral preferred connection parameters used in the respective attribute of GAP service. |
ble_gap_per_pref_conn_params_set() |
Set the peripheral preferred connection parameters used in the respective attribute of GAP service. |
ble_gap_get_io_cap() |
Get the I/O capabilities of the device. |
ble_gap_set_io_cap() |
Set the I/O capabilities of the device (combined with the peer’s I/O capabilities, this will determine which pairing algorithm will be used). |
ble_gap_data_length_set() |
Set the data length to be used for transmission on new connections. |
Advertising |
|
ble_gap_adv_start() |
Start advertising. |
ble_gap_adv_stop() |
Stop advertising. |
ble_gap_adv_data_set() |
Set the Advertising Data and Scan Response Data used. |
ble_gap_adv_ad_struct_set() |
Set Advertising Data and Scan Response Data using |
ble_gap_adv_data_get() |
Get currently used Advertising Data and Scan Response Data. |
ble_gap_adv_intv_get() |
Get the currently set advertising interval. |
ble_gap_adv_intv_set() |
Set the advertising interval (has to be done prior to ble_gap_adv_start()). |
ble_gap_adv_chnl_map_get() |
Get the advertising channel map currently set. |
ble_gap_adv_chnl_map_set() |
Set the advertising channel map (has to be done prior to ble_gap_adv_start()). |
ble_gap_adv_mode_get() |
Get the discoverability mode used for advertising. |
ble_gap_adv_mode_set() |
Set the discoverability mode used for advertising (has to be done prior to ble_gap_adv_start()). |
ble_gap_adv_set_permutation() |
Set the order of the primary advertising channels (Bluetooth Core v5.1 or later) |
ble_gap_adv_filt_policy_get() |
Get the filtering policy used for advertising. |
ble_gap_adv_filt_policy_set() |
Set the filtering policy used for advertising. |
ble_gap_adv_direct_address_get() |
Get the peer address used for directed advertising. |
ble_gap_adv_direct_address_set() |
Set the peer address used for directed advertising (has to be done prior to ble_gap_adv_start()). |
Scanning |
|
ble_gap_scan_start() |
Start scanning for devices. |
ble_gap_scan_stop() |
Stop scanning for devices. |
Connection management |
|
ble_gap_scan_params_get() |
Get the scan parameters used for future connections. |
ble_gap_scan_params_set() |
Set the scan parameters used for future connections. |
ble_gap_connect() |
Initiate a direct connection to a device. |
ble_gap_connect_ce() |
Initiate a direct connection with an application-defined minimum and maximum connection event length |
ble_gap_connect_cancel() |
Cancel an initiated connection procedure. |
ble_gap_disconnect() |
Initiate a disconnection procedure on an established link. |
ble_gap_peer_version_get() |
Get peer’s version on an established connection. |
ble_gap_peer_features_get() |
Get peer’s features on an established connection. |
ble_gap_conn_rssi_get() |
Get the RSSI of a connection. |
ble_gap_conn_param_update() |
Initiate a connection parameter update or update request procedure (depending on the role set and peer’s supported features). |
ble_gap_conn_param_update_reply() |
Reply to a connection parameter update request. |
ble_gap_data_length_set() |
Set the data length used for transmission on a specified connection. |
ble_gap_phy_get() |
Get the transmitter and receiver PHY (default preferred or for a specified connection). |
ble_gap_phy_set() |
Set PHY used for RX and TX (default or for a given connection). |
Security |
|
ble_gap_pair() |
Start pairing. |
ble_gap_pair_reply() |
Respond to a pairing request. |
ble_gap_passkey_reply() |
Respond to a passkey request. |
ble_gap_numeric_reply() |
Respond to a numeric comparison request (LE Secure Connections only). |
ble_gap_get_sec_level() |
Get link security level. |
ble_gap_set_sec_level() |
Set link security level. |
ble_gap_unpair() |
Unpair device (will also remove the related bond data from BLE storage). |
ble_gap_address_resolve() |
Resolve a BD address using the set of IRKs stored in BLE storage. |
Power Control |
|
ble_gap_local_tx_power_get() |
Get the current and maximum transmit power levels of the local device, on the ACL connection, for the indicated PHY. |
ble_gap_remote_tx_power_get() |
Get the TX power level used by the remote device, on the ACL connection, for the indicated PHY. |
ble_gap_path_loss_report_params_set() |
Set the path loss threshold reporting parameters. |
ble_gap_path_loss_report_en() |
Enable or disable path loss reporting. |
ble_gap_tx_power_report_en() |
Enable or disable reporting of TX power level in the local and remote device for the ACL connection. |
ble_gap_rf_path_compensation_set() |
Indicate the RF path gain or loss between the RF transceiver and the antenna contributed by intermediate components. |
Helper functions |
|
ble_gap_get_connected() |
Get list of connected devices. |
ble_gap_get_bonded() |
Get list of bonded devices. |
ble_gap_get_devices() |
Return list of known devices based on filter. |
ble_gap_get_device_by_addr() |
Get the device object, given the device address. |
ble_gap_get_device_by_conn_idx() |
Get device object, given the connection index. |
ble_gap_is_bonded() |
Get bond state of device (by connection index). |
ble_gap_is_addr_bonded() |
Get bond state of device (by address). |
Expert functions |
|
ble_gap_skip_peripheral_latency() |
Controls the peripheral skip latency feature for a connection. Can only be enabled for peripheral (slave) connections. If enabled, the device ignores the peripheral (slave) latency of connection parameters and listens on all possible connection events. By doing so, this feature decreases the latency of the data exchange, but may increase the power consumed due to increased listening. If disabled, the device usually listens only on the connection event that meets the peripheral (slave) latency of connection parameters. Additionally, it will listen on all possible connection events only when at least one of the below condition is met:
When a connection is established, the peripheral skip latency feature is disabled by default. |
GATT server API (ble_gatts.h): Set up the attribute database, set attribute values, notify/indicate characteristic values, initiate MTU exchanges, respond to write and read requests, etc.
Function |
Description |
---|---|
ble_gatts_add_service() |
Add a new GATT service to the ATT database. Subsequent calls to ble_gatts_add_include(), ble_gatts_add_characteristic() and ble_gatts_add_descriptor() will add attributes to the service added by this call. |
ble_gatts_add_include() |
Add an included service declaration to the service added by ble_gatts_add_service(). |
ble_gatts_add_characteristic() |
Add a characteristic declaration to the service added by ble_gatts_add_service(). |
ble_gatts_add_descriptor() |
Add a descriptor declaration to the service added by ble_gatts_add_service(). |
ble_gatts_register_service() |
Add to the ATT database all attributes previously added to the service. |
ble_gatts_enable_service() |
Enable service in database. |
ble_gatts_disable_service() |
Disable service in database. |
ble_gatts_get_characteristic_prop() |
Read current characteristic properties and permissions. |
ble_gatts_set_characteristic_prop() |
Set characteristic properties and permissions. |
ble_gatts_get_value() |
Get attribute value. |
ble_gatts_set_value() |
Set attribute value. |
ble_gatts_read_cfm() |
Confirmation response to an attribute read request. |
ble_gatts_write_cfm() |
Confirmation response to an attribute write request. |
ble_gatts_prepare_write_cfm() |
Confirmation response to an attribute prepare write request. |
ble_gatts_send_event() |
Send a characteristic value notification or indication. |
ble_gatts_service_changed_ind() |
Send indication of the Service Changed Characteristic. |
ble_gatts_get_num_attr() |
Calculate the number of attributes required for a service. |
GATT client API (ble_gattc.h): Used by a device configured as a GATT client to discover the services, characteristics, etc. of a peer device, read or write its attributes, initiate MTU exchanges, confirm the reception of indications, etc.
Function |
Description |
---|---|
ble_gattc_browse() |
Browse services on a remote GATT server. |
ble_gattc_browse_range() |
Browse services on remote GATT server in a given range. |
ble_gattc_discover_svc() |
Discover services on a remote GATT server. |
ble_gattc_discover_include() |
Discover included services on a remote GATT server. |
ble_gattc_discover_char() |
Discover characteristics on a remote GATT server. |
ble_gattc_discover_desc() |
Discover descriptors on a remote GATT server. |
ble_gattc_read() |
Read a characteristic value or a characteristic descriptor from the remote GATT server, depending on the attribute handle. |
ble_gattc_write() |
Write a characteristic value or a characteristic descriptor to the remote GATT server, depending on the attribute handle. |
ble_gattc_write_no_resp() |
Write attribute to remote GATT server without response. |
ble_gattc_write_prepare() |
Prepare long/reliable write to remote GATT server. |
ble_gattc_write_execute() |
Execute long/reliable write to remote GATT server. |
ble_gattc_indication_cfm() |
Send confirmation for received indication. |
ble_gattc_get_mtu() |
Get current TX MTU of peer. |
ble_gattc_exchange_mtu() |
Exchange MTU. |
Note
Several GAP configuration functions must be called before the attribute database is created, because modifying the device’s configuration can clear the attribute database created up to that point. This is noted in the Doxygen headers of the configuration functions that can have this effect.
5.1.2.2. Dialog BLE service API
The BLE service API header files are in
<SDK_ROOT_PATH>/sdk/interfaces/ble/services/include
.
The services-specific API, callback function prototypes and definitions are included in each service’s header file. The services implemented are the following:
Header file |
Description |
---|---|
bas.h |
Battery Service |
bcs.h |
Body Composition Service |
ble_service.h |
Services handling routines API |
bls.h |
Blood Pressure Service |
bms.h |
Bond Management Service |
cts.h |
Current Time Service |
dis.h |
Device Information Service |
dlg_debug.h |
Dialog Debug Service |
dlg_suota.h |
Dialog SUOTA Service |
hids.h |
Human Interface Device Service |
hrs.h |
Heart Rate Service |
ias.h |
Immediate Alert Service |
lls.h |
Link Loss Service |
scps.h |
Scan Parameter Service |
sps.h |
Serial Port Service |
svc_defines.h |
Common definitions for all services |
svc_types.h |
Common characteristic common |
tps.h |
Tx Power Service |
uds.h |
User Data Service |
wss.h |
Weight Scale Service |
All services have an initialization function defined. This function is called with arguments that vary for different services.
The most common argument is a pointer to one or more callback functions that should be called upon a service-specific event. For example, the prototype for the initialization function of the Immediate Alert Service (ias.h) is the following:
ble_service_t *ias_init(ias_alert_level_cb_t alert_level_cb)
Function ias_init()
has only one argument. It is a pointer to the callback function that will be called when a peer device has modified the value of the Immediate Alert Level characteristic. This callback function is part of the user application code and should provide the application handling required for the change to the Immediate Alert Level.
The return value from all initialization functions is the created service’s handle which is used to reference the service in the application. To understand how the service interacts with the BLE Framework it is useful to know what this handle represents. The handle is a pointer to a generic structure (ble_service_t
) that defines how the service should interact with the framework. Within each service there is an internal service definition (XXX_service_t
) as shown in Figure 41. This contains the generic service structure plus a set of handles, one for each GATT characteristic that the service implements. This XXX_service_t
structure is populated by XXX_init()
for that service. The start_h
and end_h
handles will contain the start and end handles of the attributes for this service within the overall GATT table provided by the GATT server. So, when a GATT client requests a Service Discovery from the server these represent the start and end handles that the client would use to access service XXX
.

Figure 41 Structure of a service handle
The set of optional callbacks allow each service to specify if it wants to do some specific handling on a certain event received by the BLE Framework. If the service wants to be informed when another BLE device has connected to this device then it can define its own handle_connected_evt()
function and plug it into the connected_evt
callback. Each service declares its handle_connected_evt()
function as static in xxx.c
and by convention in the SmartSnippets™ SDK they all share the same function names in each service.
As each service is initialized and thus added to the BLE services framework with ble_service_add()
, its generic services structure is added to a structure of supported services as shown in Figure 42.

Figure 42 Structure of supported services
Now that the main internal services structure has been explained, it is easier to follow how the service initialization defines how the service operates.
Within the BLE service framework, the main event handler is ble_service_handle_event()
which is shown below.
bool ble_service_handle_event(const ble_evt_hdr_t *evt)
{
switch (evt->evt_code) {
case BLE_EVT_GAP_CONNECTED:
connected_evt((const ble_evt_gap_connected_t *) evt);
return false; // make it "not handled" so app can handle
case BLE_EVT_GAP_DISCONNECTED:
disconnected_evt((const ble_evt_gap_disconnected_t *) evt);
return false; // make it "not handled" so app can handle
case BLE_EVT_GATTS_READ_REQ:
return read_req((const ble_evt_gatts_read_req_t *) evt);
case BLE_EVT_GATTS_WRITE_REQ:
return write_req((const ble_evt_gatts_write_req_t *) evt);
case BLE_EVT_GATTS_PREPARE_WRITE_REQ:
return prepare_write_req((const ble_evt_gatts_prepare_write_req_t *) evt);
case BLE_EVT_GATTS_EVENT_SENT:
return event_sent((const ble_evt_gatts_event_sent_t *) evt);
}
return false;
}
Each of these sub-handlers inside ble_service_handle_event()
search throughout the added services to find one that has defined a behavior for this event. There are two types of events that are handled differently.
5.1.2.3. Connection Orientated Events
The connection and disconnection events are potentially of interest to all registered services, so all services can be informed. The cleanup on shutdown is handled in the same way.
For example a connection event will call the BLE service’s statically defined connected_evt()
function (sdk/interfaces/ble/services/src/ble_service.c
). This will go through all the services registered to the BLE service framework to check for services that have registered to connection events during the service initialization. For each such service the connected_evt
callback will be called.
5.1.2.4. Attribute Orientated Events
These are events that have to do with a given attribute handle. As each attribute is related to a unique service the first step in each of these handlers is to identify which service the attribute belongs to.
For example a write request on a specific attribute will call the BLE service’s statically defined write_req()
function (sdk/interfaces/ble/services/src/ble_service.c
) as shown below. This will first identify which service owns that attribute with find_service_by_handle()
. Then if it has a write_req callback defined it executes the callback.
static bool write_req(const ble_evt_gatts_write_req_t *evt)
{
ble_service_t *svc = find_service_by_handle(evt->handle);
if (!svc) {
return false;
}
if (svc->write_req) {
svc->write_req(svc, evt);
}
return true;
}
An example of this flow is the Write No Response procedure that can be applied to the Immediate Alert Level characteristic of the Immediate Alert Service. When a GATT client requests a write to that characteristic it will trigger the write_req()
sub-handler under ble_service_handle_event()
.
The write_req()
sub-handler will use find_service_by_handle()
to see if any of the added services are registered for that characteristic. It will match it with the Immediate Alert Service (IAS) and as the IAS has registered a Write Request handler the IAS handle_write_req()
will be called (sdk/interfaces/ble/services/src/ias.c
).
static void handle_write_req(ble_service_t *svc, const ble_evt_gatts_write_req_t *evt)
{
ia_service_t *ias = (ia_service_t *) svc;
att_error_t err = ATT_ERROR_OK;
if (evt->length == 1) {
uint8_t level;
/*
* This is write-only characteristic so we don't need to store value written
* anywhere, can just handle it here and reply.
*/
level = get_u8(evt->value);
if (level > 2) {
err = ATT_ERROR_APPLICATION_ERROR;
} else if (ias->cb) {
ias->cb(evt->conn_idx, level);
}
}
ble_gatts_write_cfm(evt->conn_idx, evt->handle, err);
}
By calling ias->cb()
function, this handler actually calls the application supplied callback function passed as an argument when ias_init()
was called by the application. Finally, it sends a Write Confirmation to update the value at the attribute database maintained in the BLE Stack.
This is only an example of the way the BLE service framework translates BLE events to BLE service events. Different events in different services can have different levels of complexity, but most of the times this complexity is contained within the BLE service API. The aim is that the application only needs to call the service’s initialization function and define the appropriate callback functions to handle all service’s events.
In addition, some services define additional service-specific API calls. For instance, the Heart Rate Service implementation defines an API to trigger notifications of the Heart Rate Measurement characteristic, using functions hrs_notify_measurement()
and hrs_notify_measurement_all()
(the first is used to notify the characteristic to a specified peer, while the second is used to notify the characteristic to all subscribed peers). Some services also define some internal helper functions that are used to manipulate characteristic values, and some services require attribute initial values as arguments of the initialization function.
The BLE service API adds another layer to the general BLE API. Together the BLE adapter, BLE manager, BLE API library and BLE service framework results in the BLE Framework.
The BLE services API provides an off the shelf solution to implement an application using many of the common adopted BLE services. The underlying BLE API and GATT server API can be used to create other adopted services or even custom services using the BLE services as a template.
The following sections will provide an overview of a generic application and then will describe in detail several of the example BLE projects included in the SmartSnippets™ DA1470x SDK:
ble_peripheral |
A project that can be used as a template for developing BLE peripheral applications. The project includes some of the services implemented under the BLE service framework. |
ble_suota_client |
This application is a SUOTA 1.2 client implementation and allows to update SUOTA-enabled devices over the air, using a simple serial console interface. |
ams |
Apple Media Service demo application |
ancs |
This application is a sample implementation for Apple Notification Center Service (ANCS) client. It supports all the features of the Notification Consumer (NC) role provided by this service. |
blp_sensor |
This application is a sample implementation of the Blood Pressure Sensor as defined in the Blood Pressure Profile Specification. |
bms |
This application is an implementation of the Bond Management Service, as defined by the Bluetooth Special Interest Group. |
cscp_collector |
This application is a sample implementation of Cycling Speed And Cadence collector as defined by CSCP specification 1.0. All features are supported (i.e. both mandatory and optional features). |
hogp_device |
This application is a sample implementation of a HOGP Device. |
hogp_host |
This application is a sample implementation of the HOGP Host of the HID Over GATT Profile, as defined by the Bluetooth Special Interest Group. |
hrp_collector |
This application is a sample implementation of Heart Rate collector as defined by HRP specification 1.0. All features are supported (i.e. both mandatory and optional features). |
hrp_sensor |
This application is a sample implementation of a Heart Rate Sensor of the Heart Rate Profile, as defined by the Bluetooth Special Interest Group. |
wsp_weightscale |
This application is a sample implementation of Weight Scale role of the Weight Scale Profile specification, as defined by the Bluetooth Special Interest Group. |
ble_cli |
This application provides a Command Line Interface handling functions and events from the SDK APIs. |
ble_external_host |
The application purpose is the use of transport layer on DA1470x platform by an external user. For example a different BLE Host stack may be used with BLE Controller on DA146xx targets. |
ble_multi_link |
This application allows connecting to many devices by writing their addresses to characteristic. |
pxp_reporter |
This application is a sample implementation of Proximity Reporter of the Proximity Profile, as defined by the Bluetooth Special Interest Group. |
ble_central |
This application is an example of BLE central role device implementation. It connects to a remote device, searches for services, characteristic and descriptors. |
ble_usbhid_device |
This application implements a HoGP Device compliant with HoGP specification and can be used with any BLE device supporting HoGP Host (either Boot Host or Report Host), e.g. PC or a smartphone. |
ble_usbhid_dongle |
This application implements parts of HoGP Report Host role which are necessary to work with HoGP Device implementation of ble_usbhid_device. Its purpose is to act as a bridge between HoGP Device and non-BLE HID host supporting USB HID host. It is a plug & play solution which works only with specific, preconfigured HoGP Device. |
5.1.2.5. Configuring the project
In each project the BLE Framework and BSP are configured via a set of custom config files that set the defines and macros used in that project. These files are found in the config
directory of each project.
In the case of the ble_adv project these file are config/custom_config_oqspi.h
and config/custom_config_ram.h
(for OQSPI and RAM build configurations respectively).
Any definitions set in these file will override the default SDK values which are found in the following files:
sdk/bsp/config/bsp_defaults.h
sdk/bsp/config/bsp_defaults_da1470x.h
sdk/bsp/config/bsp_memory_defaults.h
sdk/middleware/config/middleware_defaults.h
sdk/interfaces/ble/config/ble_config.h
sdk/free_rtos/include/FreeRTOSConfig.h
5.1.2.6. BLE application structure
All the BLE application projects in the SmartSnippets™ DA1470x SDK have a similar structure. This involves several FreeRTOS tasks at different priority levels to ensure that the overall system’s real time requirements are met.
The BLE application is implemented in a FreeRTOS task that is created by the system_init()
function. system_init()
runs at the highest priority at startup and is also responsible for starting the BLE manager and BLE adapter tasks.
The application task has the following flow:
Device initialization and configuration: Start-up BLE, setting device role, device name, appearance and other device parameters.
Attribute database creation (GATT server devices): Creation of services and attributes using the BLE service framework. This must be done after (1) to prevent deletion of the attribute database.
Air operation configuration and initiation: BLE peripheral devices usually end-up advertising and BLE central devices end-up scanning and/or attempting to connect to another device.
The infinite
for(;;)
loop, which is the application’s event handling loop. The application has been set-up as desired and now it is waiting for BLE events to respond to, like connections or write requests.The BLE adapter (
ad_ble_task
) must have a higher priority than the application task(s) because it runs the BLE Host stack scheduler and it handles time critical tasks.Most of the BLE applications use the FreeRTOS task notifications mechanism to block.
ble_adv_demo
is the simplest application and is the only project that does not use this mechanism. Instead, it just blocks on the BLE manager’s event queue.In addition to the BLE-related functionality most projects also use other system modules, like software timers or sensors. In this case, the application usually defines callback functions to be triggered on these system events or interrupts. These callback functions should use task notifications to unblock the application task which can then handle the event or interrupt in the application task’s context (under its
for(;;)
loop).
Warning
Calling a BLE API function inside a callback function triggered on a timer expiry will execute the BLE API function in the timer’s task context. Calling other functions in the callback functions can also have implications on real time performance or in corrupting the small stack used by the timer task.
5.1.3. BLE Security
The Bluetooth specification defines the security options for device authentication and link encryption. These aspects of security are handled seamlessly by the BLE Framework. The API in Table 41 is able to set-up security, initiate pairing, do a security request or set-up encryption using previously exchanged keys. Most details of the procedures will be handled internally by the BLE Framework and the application will be notified only if intervention is needed or when the procedure is completed. These options will be described in detail in Section 5.1.3.2 and Section 5.1.3.5.
The generation and storage of the security keys and other bonding information is also handled by the BLE Framework. Persistent storage can also be used to enable storage of the security keys and bonding data info in the flash. The BLE Framework can then retrieve this information after a power cycle and use it to restore connections with previously bonded devices. This is described in Section 5.1.5.
5.1.3.1. LE Secure
LE Secure Connections pairing is supported and enabled by default by the SDK using the API described in Section 5.1.3.2, LE Secure Connections pairing will be used if the connected peer supports the feature without the need for the application to specifically request it. If the combination of the devices’ capabilities result in a numeric comparison pairing algorithm (introduced and used for the LE Secure Connections pairing), the application will be notified of a numeric comparison request during pairing by the reception of a BLE_EVT_GAP_NUMERIC_REQUEST
event and should respond using ble_gap_numeric_reply()
function.
If the application needs to use only LE Legacy Pairing and disable LE Secure Connections support in the SDK, it should define dg_configBLE_SECURE_CONNECTIONS
macro to 0 in the application config file.
5.1.3.2. Functions
Table 41 summarizes the API functions that can be used by the application to set-up security features.
API call |
Description |
---|---|
ble_gap_pair() |
Initiate a pairing or bonding procedure with a connected peer. Depending on whether the device is master or slave on the connection, this call will result either in a pairing or a security request respectively. |
ble_gap_pair_reply() |
Reply to a received |
ble_gap_passkey_reply() |
Reply to a received |
ble_gap_numeric_reply() |
Reply to a received |
ble_gap_set_sec_level() |
Set the security level for a connection. If the device is already bonded, the existing Long Term Key (LTK) will be used to set-up encryption. If the device is not bonded, a pairing or a security request will be triggered (depending on whether the device is master or slave on the connection) with the bond flag set to false. |
ble_gap_get_sec_level() |
Get the security level currently established on a connection. |
ble_gap_unpair() |
Unpair a previously paired or bonded device. This will also remove security keys and bonding data info currently present in BLE storage. |
5.1.3.3. Events
Table 42 describes the BLE events related to security that may be received by the application and the proper API functions to respond to them.
Event |
Argument |
Description |
---|---|---|
BLE_EVT_GAP_PAIR_REQ |
ble_evt_gap_pair_req_t |
Pairing request received by a connected peer. Member |
BLE_EVT_GAP_PAIR_COMPLETED |
ble_evt_gap_pair_completed_t |
A previously requested pairing procedure has been completed. Member |
BLE_EVT_GAP_SECURITY_REQUEST |
ble_evt_gap_security_request_t |
Security request received by a connected peripheral peer. Members |
BLE_EVT_GAP_PASSKEY_NOTIFY |
ble_evt_gap_passkey_notify_t |
A passkey has been generated during a pairing procedure. This event will be received if the application has display capability. Member |
BLE_EVT_GAP_PASSKEY_REQUEST |
ble_evt_gap_passkey_request_t |
A passkey has been requested during a pairing procedure. This event will be received if the application has keyboard capability. The application should use |
BLE_EVT_GAP_NUMERIC_REQUEST |
ble_evt_gap_numeric_request_t |
A numeric comparison has been requested during a pairing procedure. This event will be received if the application has keyboard or Yes/No and display capability. The application should use |
BLE_EVT_GAP_SEC_LEVEL_CHANGED |
ble_evt_gap_sec_level_changed_t |
The security level has been changed on an established link. Member |
BLE_EVT_GAP_SET_SEC_LEVEL_FAILED |
ble_evt_gap_set_sec_level_failed_t |
Setting the security level on an established link using |
5.1.3.4. Macros
Table 43 contains the configuration macros related to BLE security.
Macro |
Default |
Description |
---|---|---|
dg_configBLE_SECURE_CONNECTIONS |
1 |
Set to 1 to use LE Secure Connections pairing if the peer supports the feature or to 0 to always use LE Legacy Pairing. |
dg_configBLE_PAIR_INIT_KEY_DIST |
GAP_KDIST_ENCKEY | GAP_KDIST_IDKEY | GAP_KDIST_SIGNKEY |
Set the security keys requested to be distributed by the pairing initiator during a pairing feature exchange procedure. |
dg_configBLE_PAIR_RESP_KEY_DIST |
GAP_KDIST_ENCKEY | GAP_KDIST_IDKEY | GAP_KDIST_SIGNKEY |
Set the security keys requested to be distributed by the pairing responder during a pairing feature exchange procedure. |
5.1.3.5. Message Sequence Charts (MSCs)
5.1.3.6. Central

Figure 43 Central Pairing Just Works

Figure 44 Central Bonding Just Works

Figure 45 Central Bonding Passkey Entry (Central Display)

Figure 46 Central Bonding Passkey Entry (Peripheral Display)

Figure 47 Central Bonding Numeric Comparison (Secure Connections Only)
5.1.3.7. Peripheral

Figure 48 Peripheral Pairing Just Works

Figure 49 Peripheral Bonding Just Works

Figure 50 Peripheral Bonding Passkey Entry (Peripheral Display)

Figure 51 Peripheral Bonding Passkey Entry (Central Display)

Figure 52 Peripheral Bonding Numeric Comparison (Secure Connections Only)
5.1.4. BLE Power Control
The Bluetooth specification defines the way by which a device can request a remote device to make a specified change in its TX power level on a given PHY. The response of the peer device contains a value that indicates an acceptable reduction in the power level that allows the local device to further reduce its transmit power level to the minimum level possible. The local and remote devices can also share their current transmit power levels. This way they can also calculate the link path loss between them.
This 5.2 BLE Feature is supported and enabled by default by the SDK using the API described in Section 5.1.4.2
5.1.4.1. RSSI Golden Range
The BLE specification defines that the radio receiver can have an RSSI Golden Range that it prefers the incoming signal to remain within. This functionality is supported and so the local controller adjusts automatically the peer’s TX power to bring RSSI to its preferred value inside this Golden Range.
The local device continuously monitors the RSSI and if it is below the minimum or above maximum acceptable values of the RSSI
it uses the Power Control Request procedure to request an increase or decrease the TX power level of the peer device. This procedure is done automatically by the local device. The application
can be informed about the changes in the local and the remote TX power levels by using the ble_gap_tx_power_report_en()
command. The changes in the TX power level are then reported to the application by the
BLE_EVT_GAP_TX_PWR_REPORT
event.
The default values of the RSSI Golden Range can be seen in Table 46.
5.1.4.2. Functions
Table 44 summarizes the API functions that can be used by the application to use the LE Power Control feature.
API call |
Description |
---|---|
ble_gap_local_tx_power_get() |
Get the current and maximum transmit power levels of the local device, on the ACL connection, for the indicated PHY. |
ble_gap_remote_tx_power_get() |
Get the TX power level used by the remote device, on the ACL connection for the indicated PHY. |
ble_gap_path_loss_report_params_set() |
Set the path loss threshold reporting parameters |
ble_gap_path_loss_report_en() |
Enable or disable path loss reporting. |
ble_gap_tx_power_report_en() |
Enable or disable reporting of TX power level in the local and remote device for the ACL connection. |
ble_gap_rf_path_compensation_set() |
Indicate the RF path gain or loss between the RF transceiver and the antenna contributed by intermediate components. |
5.1.4.3. Events
Table 45 describes the BLE events related to LE Power Control that may be received by the application.
Event |
Argument |
Description |
---|---|---|
BLE_EVT_GAP_LOCAL_TX_PWR |
ble_evt_gap_local_tx_pwr_t |
Reading of the local transmit power has been completed. Member |
BLE_EVT_GAP_TX_PWR_REPORT |
ble_evt_gap_tx_pwr_report_t |
Reports that the local or remote transmit power has changed or that a |
BLE_EVT_GAP_PATH_LOSS_THRES |
ble_evt_gap_path_loss_thres_t |
Reports a path loss threshold crossing. Member |
5.1.4.4. Macros
Table 46 contains the configuration macros related to BLE Power Control.
Macro |
Default |
Description |
---|---|---|
dg_configBLE_GOLDEN_RANGE_LOW |
-70 |
Sets the lower RSSI value (dBm) of the Golden Range. |
dg_configBLE_GOLDEN_RANGE_UP |
-40 |
Sets the upper RSSI value (dBm) of the Golden Range. |
dg_configBLE_GOLDEN_RANGE_PREF |
-55 |
Sets the preferred RSSI value (dBm) inside the Golden Range (dBm). |
dg_configBLE_PCLE_MIN_TX_PWR_IDX |
GAP_TX_POWER_MINUS_26_dBm |
Sets the Minimum TX Power index used in LE Power Control (dBm). The available TX power levels can be seen in Table 47 |
dg_configBLE_PCLE_MAX_TX_PWR_IDX |
GAP_TX_POWER_MAX |
Sets the Maximum TX Power index used in LE Power Control (dBm). The available TX power levels can be seen in Table 47 |
5.1.4.5. Supported TX Power Levels
Table 47 lists the available TX power levels. The corresponding gap_tx_power_t
enumeration is located in the sdk/ble/api/include/ble_gap.h
.
Tx Power level |
Corresponding index |
---|---|
GAP_TX_POWER_MAX |
17 |
GAP_TX_POWER_6_dBm |
17 |
GAP_TX_POWER_5_dBm |
16 |
GAP_TX_POWER_4_5_dBm |
15 |
GAP_TX_POWER_4_dBm |
14 |
GAP_TX_POWER_3_dBm |
13 |
GAP_TX_POWER_2_dBm |
12 |
GAP_TX_POWER_1_5_dBm |
11 |
GAP_TX_POWER_0_dBm |
10 |
GAP_TX_POWER_MINUS_1_dBm |
9 |
GAP_TX_POWER_MINUS_2_dBm |
8 |
GAP_TX_POWER_MINUS_3_dBm |
7 |
GAP_TX_POWER_MINUS_6_dBm |
6 |
GAP_TX_POWER_MINUS_8_dBm |
5 |
GAP_TX_POWER_MINUS_12_dBm |
4 |
GAP_TX_POWER_MINUS_18_dBm |
3 |
GAP_TX_POWER_MINUS_22_dBm |
2 |
GAP_TX_POWER_MINUS_26_dBm |
1 |
GAP_TX_POWER_MINUS_50_dBm |
0 |
GAP_TX_POWER_MIN |
0 |
5.1.5. BLE Storage
BLE Storage is the module that implements storage functionality for information related to connected and bonded peers, like security keys, CCC descriptors configuration and application-defined values. BLE Storage can maintain the list of connected and bonded devices both in RAM and in persistent storage (for example, in the flash). By default, devices are managed in RAM only and persistent storage must be explicitly enabled in the application’s configuration by defining macro CONFIG_BLE_STORAGE
. Device data is then stored using Non-Volatile Memory Storage (NVMS) on the generic partition (see Section 4.2 for details).
Two kinds of data are stored:
Device pairing data (exchanged keys and related information).
Application-defined data managed using the BLE storage API (only the values with the ‘persistent’ flag set are stored in flash, for example CCC descriptor values).
Persistent storage can be enabled by the application by adding the following entries in the application’s custom configuration file:
// enable BLE persistent storage
#define CONFIG_BLE_STORAGE
// enable Flash and NVMS adapters with VES (required by BLE persistent storage)
#define dg_configFLASH_ADAPTER 1
#define dg_configNVMS_ADAPTER 1
#define dg_configNVMS_VES 1
The maximum number of bonded devices can be set using the defaultBLE_MAX_BONDED
macro (defined to 8 by default). If the application attempts to bond more devices than its allowed, an error will be returned. This error should be handled by the application. It can then either unpair one of the currently bonded devices (using ble_gap_unpair()
API function) or perform pairing without bonding.
Technical details on the BLE Storage implementation can be found in the following readme file:
<SDK_ROOT_PATH>/sdk/interfaces/ble/README.md
5.1.6. Logical Link Control and Adaptation Layer Protocol
The Logical Link Control and Adaptation Layer Protocol, referred to as L2CAP provides connection-oriented and connectionless data services to upper layer protocols with protocol multiplexing capability and segmentation and reassembly operation. As referred in [Ref_02], L2CAP permits higher level protocols and applications to transmit and receive upper layer data packets (L2CAP Service Data Units, SDU) up to 64 kilobytes in length. L2CAP also permits per-channel flow control and retransmission.
The L2CAP layer provides logical channels, named L2CAP channels, which are multiplexed over one or more logical links. Each one of the endpoints of an L2CAP channel is referred to by a channel identifier (CID).
L2CAP channels may operate in one of five different modes as selected for each L2CAP channel. The modes are:
Basic L2CAP Mode (equivalent to L2CAP specification in Bluetooth v1.1)
Flow Control Mode
Retransmission Mode
Enhanced Retransmission Mode
Streaming Mode
LE Credit Based Flow Control Mode
The null CID (0x0000
) is never used as destination endpoint. Identifiers
from 0x0001
to 0x003F
are reserved for specific L2CAP functions. These
channels are referred to as Fixed Channels.
CID 0x0004
is used by the ATT, CID 0x0006
is used by the SMP while CID
is used by the signaling channel.
As referred above, the connection-oriented data channels represent a connection between two devices, where a CID, combined with the logical link, identifies each endpoint of the channel.
Figure 53 illustrates the format of the L2CAP PDU in basic mode.

Figure 53 L2CAP PDU format in Basic L2CAP mode on COC
Summarizing:
L2CAP implementations transfer data between upper layer protocols and the lower layer protocol.
L2CAP maps channels to Controller logical links, which in turn run over Controller physical links. All logical links going between a local Controller and remote Controller run over a single physical link.
L2CAP is packet-based but follows a communication model based on channels. A channel represents a data flow between L2CAP entities in remote devices. Channels may be connection-oriented or connectionless. Fixed channels other than the L2CAP connectionless channel (CID
0x0002
) and the two L2CAP signaling channels (CIDs0x0001
and0x0005
) are considered connection-oriented. All channels with dynamically assigned CIDs are connection-oriented.
5.1.6.1. Credit-Based Flow Control
The Credit-Based Flow Control is an L2CAP mode of operation that when used, allows both devices involved in the LE connection to have complete control over how many packets the peer device is allowed to send. This is achieved by the use of credits that represent the absolute maximum number of LE frames that the device is willing to accept at a particular moment. The sending entity may send only as many LE-frames as it has credits. If the credit count reaches zero the transmission must stop. If more frames are sent the connection will be closed.
5.1.6.2. Functions
To establish a LE-Credit Based L2CAP connection, the initiator should send a LE Credit-Based connection request, specifying parameters like the Protocol Service Multiplexer (PSM), Maximum Transmission Unit (MTU), Maximum Payload Size (MPS), and the initial number of credits that the remote peer has to send data. The responding device should respond with a LE Credit-Based Connection Response specifying its own MTU, MPS and initial credits value. PSM is 2-byte odd number that can be used to support multiple implementations of a protocol. The valid range for PSM is between 0x80 - 0xFF
. The fixed SIG assigned PSM values are between 0x00
and 0x7F
. MTU represents the maximum size of data that the service above L2CAP can send to the remote peer (Maximum size of an SDU – Service Data Unit). MPS is the maximum payload size that an L2CAP entity can receive from the lower layer, and it is equivalent to the maximum PDU payload size. Each MPS corresponds to one credit. One SDU (of size MTU) can be fragmented to one or more PDUs (of size MPS). If the SDU length field value exceeds the receiver’s MTU, the receiver shall disconnect the channel. If the payload length of any LE-frame exceeds the receiver’s MPS, the receiver shall disconnect the channel. If the sum of the payload lengths for the LE-frames exceeds the specified SDU length, the receiver shall disconnect the channel. After the LE Credit-Based connection request and response frames are received or exchanged, the two entities agree to use the minimum values of MTU and MPS.
As an example, consider two devices that use the following values during the Credit-Based connection request/response procedure:
Device A Connection Request |
Device B Connection Response |
|
---|---|---|
PSM |
0x80 |
- (Field not available on Connection Response) |
MTU |
100 |
250 |
MPS |
50 |
23 |
Initial Credits |
10 |
20 |
In this scenario, device B can receive PDUs of size at most 23, and SDUs of size at most 250. On the other hand, device A can receive PDUs of size at most 100, and SDUs of size at most 50. Device A can send 20 PDUs to device B, and should stop until device B updates the available credits. This update could be performed any time during the connection. If device A was to transmit an SDU of size 100 bytes (MTU), it would be fragmented to 5 PDUs of data sizes 21, 23, 23, 23 and 10 respectively. This transmission would consume 5 credits (one credit for every PDU that could be received from device B). Note that the usable payload size of the first PDU is 2 bytes less than the value of MPS as the first LE-frame contains a 2-byte field specifying the total length of the SDU. This is true only for the first frame. In the same manner, the transmission of a 23-byte SDU would require two credits and two PDUs of size 21 and 2 bytes respectively whereas the transmission of a 21-byte SDU would require just one PDU. Maximum SDU length (MTU) can be specified using the ble_gap_mtu_size_set()
API call.
API call |
Description |
---|---|
ble_l2cap_connect() |
Create a l2cap connection oriented channel with remote peer. Connection establishment will be signaled using |
ble_l2cap_listen() |
Create a connection oriented channel listening for incoming connections. Incoming connection will be signaled using |
ble_l2cap_listen_defer_setup() |
Create a connection oriented channel listening for incoming connections. Incoming connection will be signaled using |
ble_l2cap_connection_cfm() |
Accept or reject incoming connection. Accepted connection will be signaled using |
ble_l2cap_stop_listen() |
Stop listening for incoming connections. |
ble_l2cap_disconnect() |
Disconnect an established L2CAP channel. |
ble_l2cap_send() |
Send data on channel, response code is signaled using the |
ble_l2cap_add_credits() |
Provide additional credits to remote peer. |
5.1.6.3. Events
Event |
Argument |
Description |
---|---|---|
BLE_EVT_L2CAP_CONNECTED |
ble_evt_l2cap_connected_t |
Channel connected. Members |
BLE_EVT_L2CAP_CONNECTION_FAILED |
ble_evt_l2cap_connection_failed_t |
Channel connection failed. Member |
BLE_EVT_L2CAP_CONNECTION_REQ |
ble_evt_l2cap_connection_req_t |
Request connection. Members refer to connection parameters e.g. |
BLE_EVT_L2CAP_DISCONNECTED |
ble_evt_l2cap_disconnected_t |
Channel disconnected. Member |
BLE_EVT_L2CAP_SENT |
ble_evt_l2cap_sent_t |
Data sent on channel. |
BLE_EVT_L2CAP_REMOTE_CREDITS_CHANGED |
ble_evt_l2cap_credit_changed_t |
Available remote credits changed on channel. |
BLE_EVT_L2CAP_DATA_IND |
ble_evt_l2cap_data_ind_t |
Data received on channel. |
5.1.7. LE Data Packet Length Extension
For Bluetooth Core versions 4.0 and 4.1 the maximum Packet Data Unit (PDU) size was 27 octets. Bluetooth Core version 4.2 introduced an important enhancement, namely LE Data Packet Length Extension, which allows for the PDU size to be anywhere between 27 and 251 octets. This means that, for example, the L2CAP layer can now fill up to 247 octets of higher layer data packets in every L2CAP PDU compared to 21 octets with previous Bluetooth Core versions. This significant increase (more than 10 times) in the number of octets of user data sent per packet allows devices to transfer data up to 2.5 times faster than with previous versions. This will be of great benefit to applications that might require transferring large amounts of data such as Over-the-Air (OTA) firmware updates or downloading large data logs from sensors.
For the default PDU size to be extended on an established connection, the Data Length Update procedure must be performed. According to this control procedure, the LL_LENGTH_REQ
and LL_LENGTH_RSP
PDUs must be exchanged by the connected devices so that each is notified of its peer device’s capabilities. Each device uses these PDUs to report its maximum receive data channel and maximum transmit data channel PDU payload length and PDU time. After this update procedure, the PDU size for each direction of the connection’s data path is set by both controllers to the minimum of the values exchanged.
The DA1470x supports the LE Data Length Extension feature, so the values for the Receive and Transmit Data Length are set by default to the maximum allowed, which is 251 octets. The DA1470x controller when configured as a BLE central device will initiate a Data Length Update upon a new connection if the peer device’s controller supports this feature. The BLE Manager will use the values defined by dg_configBLE_DATA_LENGTH_RX_MAX
and dg_configBLE_DATA_LENGTH_TX_MAX
macros for this initial Data Length Update negotiation.
5.1.7.1. Functions
Function |
Description |
---|---|
ble_gap_data_length_set() |
Set the maximum Transmit data length and time for an existing connection or the preferred Transmit data length for future connections (that is, the Transmit data length to be used in future data length update negotiations). Connection data length change will be signaled using |
5.1.7.2. Macros
Macro |
Default |
Description |
---|---|---|
dg_configBLE_DATA_LENGTH_RX_MAX |
251 |
Set the maximum Receive Data Channel PDU Payload Length. Unless |
dg_configBLE_DATA_LENGTH_TX_MAX |
251 |
Set the maximum Transmit Data Channel PDU Payload Length. Unless |
5.1.7.3. Events
Event |
Argument |
Description |
---|---|---|
BLE_EVT_GAP_DATA_LENGTH_CHANGED |
ble_evt_gap_data_length_changed_t |
Data Length changed for specified connection. Members |
BLE_EVT_GAP_DATA_LENGTH_SET_FAILED |
ble_evt_gap_data_length_set_failed_t |
Data Length Set operation failed. Member |
5.1.8. 2M PHY
Bluetooth Core versions up to 4.2 supported only the mandatory bit rate of 1 megabit per second (Mb/s), which is referred to as the LE 1M PHY. Bluetooth Core version 5.2, which is implemented in Dialog DA1470x product family, also supports the optional bit rate of 2 Mb/s, which is referred to as the LE 2M PHY. This feature is implemented by the LE controller and it can be configured using the API described in Section 5.1.8.1 and Section 5.1.8.2, although additional configuration is not mandatory; the LE controller will handle PHY update procedures seamlessly and will inform the application of any changes in the PHY configuration regardless of which side initiated the PHY update procedure.
5.1.8.1. Functions
Function |
Description |
---|---|
ble_gap_phy_get() |
Get the transmitter and receiver PHY preferences set for an existing connection or for all future connections. The changes in PHY configuration for a given connection will be signaled using |
ble_gap_phy_set() |
Set the transmitter and receiver PHY preferences for an existing connection or for all future connections. Completion of the PHY set operation will be signaled using |
5.1.8.2. Events
Event |
Argument |
Description |
---|---|---|
BLE_EVT_GAP_PHY_CHANGED |
ble_evt_gap_phy_changed_t |
PHY configuration changed for the specified connection. Members |
BLE_EVT_GAP_PHY_SET_COMPLETED |
ble_evt_gap_phy_set_completed_t |
PHY set operation has completed. This will be received following a ble_gap_phy_set() call. Member |
5.1.9. NVPARAM fields
Table 56 shows the Non-Volatile memory parameters which can be found in <SDK_ROOT_PATH>/sdk/middleware/adapters/include/platform_nvparam.h
Tag |
Offset |
Length |
---|---|---|
NVPARAM_BLE_PLATFORM_BD_ADDRESS |
0x01 |
6 bytes array |
NVPARAM_BLE_PLATFORM_LPCLK_DRIFT |
0x07 |
16bit value |
NVPARAM_BLE_PLATFORM_EXT_WAKEUP_TIME |
0x0D |
16bit value |
NVPARAM_BLE_PLATFORM_OSC_WAKEUP_TIME |
0x0E |
16bit value |
NVPARAM_BLE_PLATFORM_RM_WAKEUP_TIME |
0x0F |
16bit value |
NVPARAM_BLE_PLATFORM_SLEEP_ENABLE |
0x11 |
1 byte |
NVPARAM_BLE_PLATFORM_EXT_WAKEUP_ENABLE |
0x12 |
1 byte |
NVPARAM_BLE_PLATFORM_BLE_CA_TIMER_DUR |
0x40 |
16bit value |
NVPARAM_BLE_PLATFORM_BLE_CRA_TIMER_DUR |
0x41 |
1 byte |
NVPARAM_BLE_PLATFORM_BLE_CA_MIN_RSSI |
0x42 |
1 byte |
NVPARAM_BLE_PLATFORM_BLE_CA_NB_PKT |
0x43 |
16bit value |
NVPARAM_BLE_PLATFORM_BLE_CA_NB_BAD_PKT |
0x44 |
16bit value |
NVPARAM_BLE_PLATFORM_IRK |
0x80 |
16 bytes array |
5.1.10. Considerations on BLE Task Priorities
The BLE Software in the SDK consists of three modules:
BLE manager: Provides the interface to the BLE functionality of the chip. The application task uses the BLE API to interface with the BLE manager. The BLE manager is a task that stands between the application and the BLE adapter. It uses the BLE adapter to interface with the BLE Stack.
BLE adapter: The system task that provides the interface to the BLE Stack. It runs the BLE Stack internal scheduler, receives the commands or the replies to events from the BLE manager, and passes BLE Stack events to the BLE manager. BLE core functionality is implemented by the BLE adapter task.
BLE Stack: The software stack that implements the Link Layer and the BLE Host stack, specifically the Logical Link Control and Adaptation Protocol (L2CAP), the Security Manager Protocol (SMP), the Attribute Protocol (ATT), the Generic Attribute Profile (GATT) and the Generic Access Profile (GAP).
Note
The BLE Stack software is run under the BLE adapter’s task context, which instantiates and initializes the stack.
The two BLE system tasks have by default a higher priority than application tasks in the SDK.
Note
Application developers should always make sure BLE adapter and BLE manager tasks always have a higher priority than application tasks.
The BLE adapter runs the BLE Stack scheduler, which dispatches all the messages between the BLE Stack’s different layers and calls the appropriate handlers. For example, when an application uses API ble_gatts_send_event()
to send a GATT notification, this will result in a propagation of messages between the BLE manager, the BLE adapter and several BLE Stack’s internal layers until it reaches a transmission buffer and, eventually, the air.
5.1.11. BLE tasks timing requirements
In DA1470x the LE controller runs independently from the application. However, the BLE system tasks (BLE adapter, BLE manager) still need to run regularly to make sure BLE data are transmitted and handled in a timely manner. When the application is not making any BLE API calls and no data are exchanged with the peer, the BLE adapter will typically block until it is needed to either retrieve a message from or send a command to the BLE Host.
In some scenarios the BLE manager and the BLE adapter will communicate with messages without notifying the application. For example, upon connection with a peer that uses a resolvable private address, the BLE manager will attempt to resolve it using known devices IRKs. In this case the BLE manager and BLE adapter will have more running slots.
There are also other cases when the BLE framework will require a reply from the application when, for example, a pair request or a write request is received from the peer. Again, in these cases the BLE adapter and BLE manager will have to run more times in a period between two connection events.
5.1.12. Attribute operations
As the Attribute protocol is strict when an attribute request such as a read or a write request is received, the BLE Stack’s GATT layer will switch to a busy state for as long as the request is not completed/handled. In the case of a write request or a read request of an attribute whose value is to be provided by the application, then the application will have to confirm these operations using ble_gatts_read_cfm()
or ble_gatts_write_cfm()
respectively (after receiving BLE_EVT_GATTS_READ_REQ
or BLE_EVT_GATTS_WRITE_REQ
). In this case, other GATT operations, such as notification sending, will be queued until this request is confirmed.
5.1.13. BLE Application Examples
5.1.13.1. Advertising Application
The simplest BLE project in the SmartSnippets™ DA1470x SDK is ble_adv
demo which is found in the folder <SDK_ROOT_PATH>/projects/dk_apps/demos/ble_adv
. The application starts the device as a peripheral, sets the device name and advertising data and starts advertising. Code 40 is an extract from main.c.
// Start BLE module as a peripheral device
ble_peripheral_start();
// Set device name
ble_gap_device_name_set("Dialog ADV Demo", ATT_PERM_READ);
// Set advertising data
ble_gap_adv_data_set(sizeof(adv_data), adv_data, 0, NULL);
// Start advertising
ble_gap_adv_start(GAP_CONN_MODE_UNDIRECTED);
No BLE service is added, and the ones exposed are just GAP and GATT
services. The infinite loop that implements the lifetime behavior of the
application uses just ble_get_event(true)
to block indefinitely on the
BLE manager’s event queue. As soon as an event is posted there, the task
unblocks and handles it using a switch
case. Code 41 is located in
main.c.
for (;;) {
ble_evt_hdr_t *hdr;
/* notify watchdog on each loop */
sys_watchdog_notify(wdog_id);
/* suspend watchdog while blocking on ble_get_event() */
sys_watchdog_suspend(wdog_id);
/*
* Wait for a BLE event - this task will block
* indefinitely until something is received.
*/
hdr = ble_get_event(true);
/* resume watchdog */
sys_watchdog_notify_and_resume(wdog_id);
if (!hdr) {
continue;
}
switch (hdr->evt_code) {
case BLE_EVT_GAP_CONNECTED:
handle_evt_gap_connected((ble_evt_gap_connected_t *) hdr);
break;
case BLE_EVT_GAP_DISCONNECTED:
handle_evt_gap_disconnected((ble_evt_gap_disconnected_t *) hdr);
break;
case BLE_EVT_GAP_PAIR_REQ:
{
ble_evt_gap_pair_req_t *evt = (ble_evt_gap_pair_req_t *) hdr;
ble_gap_pair_reply(evt->conn_idx, true, evt->bond);
break;
}
default:
ble_handle_event_default(hdr);
break;
}
// Free event buffer
OS_FREE(hdr);
}
Since the BLE service framework is not used, the only events handled by the application are the three events handled by the switch
case: connection, disconnection and pair request. This makes sense for this application as its only purpose is to start a connectable advertising, restart it in case of a disconnection and respond to pair requests from devices that require pairing/bonding upon connection.
Running this project will result in an advertising BLE peripheral device exposing GAP and GATT services. GAP service attributes can be read using any standard BLE central device.
The ble_peripheral
project is a good starting point for developing BLE peripheral applications. It is found in folder <SDK_ROOT_PATH>/projects/dk_apps/features/ble_peripheral
. Unlike other example projects, it does not implement a specific profile, but instead exposes several BLE services via a GATT server.
The application’s initialization is similar to other projects that implement BLE peripheral applications. It uses the BLE service framework to instantiate several BLE services:
Battery Service (multiple instance)
Current Time Service
Device Information Service
Scan Parameter Service
Dialog Debug Service
Custom User Service
In addition to Bluetooth SIG-adopted services, ble_peripheral
project instantiates two more services, Dialog Debug Service and a custom user service.
The Dialog Debug Service can be used to interact with the services that the application exposes using a Control Point characteristic to write commands and receive notifications from. A detailed description of the ways to interact with the Dialog Debug Service is included in the readme.md file inside the project’s folder.
The custom user service does not define any specific functionality other than using 128-bit UUIDs for services, characteristics and descriptors. This custom service, referred to as myservice
in the project source code, is an example of implementing a custom service using BLE API calls to create its attribute database. No specific functionality is defined when one of these attributes is read or written. More details on how to create and use custom services will be given in Section 5.1.16.
After the attribute database is created, the device will end-up advertising and it will wait for a connection event.
The ble_peripheral
project uses the BLE service framework to handle service events, the application also defines handlers for connection, advertising completion and pair request events. The ble_peripheral
project stands in terms of its completeness somewhere between the ble_adv
demo and an application implementing a full BLE profile like the pxp_reporter
.
The services the project will expose can be configured using the file config/ble_peripheral_config.h
.
5.1.14. BLE profile projects
In addition to the projects described in the previous sections, there are several application projects that implement BLE profiles. These projects are more complex and provide a full implementation of BLE applications. As such they provide a good reference on how to combine the BLE functionality with several OS mechanisms, GPIO handling and interfacing with external sensors.
The implemented profiles are located under <SDK_ROOT_PATH>/projects/dk_apps/ble_profiles
and listed below:
Apple Media Service (
AMS
) - Client roleHID over GATT Profile (
HOGP
) – Device role (hogp_device
)HID over GATT Profile (
HOGP
) – Host role (hogp_host
)Heart Rate Profile – Sensor role (
hrp_sensor
)Heart Rate Profile – Collector role (
hrp_collector
)Proximity Profile – Reporter role (
pxp_reporter
) located under<SDK_ROOT_PATH>/projects/dk_apps/demos
Weight Scale Profile – Weight Scale role (
wsp_weightscale
)Apple Notification Center Service (
ANCS
) - Notification Consumer (NC
) role (ancs
)Blood Pressure Profile (
BLP
) – Blood Pressure Sensor role (blp_sensor
)Bond Management Service (
BMS
)Cycling Speed And Cadence collector (
CSCP
)
5.1.15. Using adopted BLE services
Table 57 summarizes the API header files of the BLE services implemented by the SmartSnippets™ DA1470x SDK. These files can be found under <SDK_ROOT_PATH>/sdk/interfaces/ble/services/include
. The developer can use these APIs to add these services to another project.
File name |
Description |
---|---|
ble_service.h |
BLE service framework API:
|
bls.h |
Blood Pressure Service – BLS |
bas.h |
Battery Service – BAS |
bcs.h |
Body Composition Service – BCS |
bms.h |
Bond Management Service – BMS |
cts.h |
Current Time Service – CTS |
dis.h |
Device Information Service – DIS |
dlg_debug.h |
Dialog Debug Service |
dlg_suota.h |
Dialog SUOTA Service |
hids.h |
Human Interface Device Service – HID |
hrs.h |
Heart Rate Service – HRS |
ias.h |
Immediate Alert Service – IAS |
lls.h |
Link Loss Service – LLS |
scps.h |
Scan Parameters Service – ScPS |
sps.h |
Serial Port Service – SPS |
tps.h |
Tx Power Service – TPS |
uds.h |
User Data Service – UDS |
wss.h |
Weight Scale Service – WSS |
5.1.16. Adding a custom service
The following code segments provide an overview of the initialization required to create a new custom service called XXX
. It requires the files xxx.c
and xxx.h
to be created. A good example to base these on is the dlg_mls
service in the Multi-Link demo. This provides a single write only characteristic in the service.
Each service needs a structure containing both the generic ble_service_t
structure and any callbacks and characteristic handles required by the service. In the example below for service XXX
there is one callback and one characteristic defined.
typedef struct {
ble_service_t svc; // Core BLE service structure
xxx_cb_t cb; // Callback provided by app to xxx
// service to process an event
uint16_t xxx_char1_val_h; // Declare handle for each characteristic
// that can be read or written
} xxx_service_t;
The requirements of the initialization function xxx_init()
are illustrated below. They key information here is the comments which are explaining what each line is doing.
xxx_service_t* xxx_init(callback1){
// Allocate and initialise xxx_service_t structure
// Define any callback functions required by the service, write only in this case
xxx->svc.write_req = <this services write request handler>
// Create primary service UUID with either 16 or 128 bit value
uuid=ble_uuid_from_string() or uuid=ble_uuid_create16()
// add PRIMARY service with X attributes
num_attrs=X
ble_gatts_add_service(&uuid, GATT_SERVICE_PRIMARY, num_attrs)
// Create characteristic 1 for this service and allocate handle for it in GATT table
ble_gatts_add_characteristic(&uuid, GATT property, ATT permissions, size,0, NULL, &xxx->xxx_char1_h)
// Set start_h and pass in null terminated variable length list of all characteristic handles in the service
ble_gatts_register_service(&xxx->svc.start_h, &xxx->xxx_char1_h,0);
// Calculate end handle for service based on number of attributes in service
xxx->svc.end_h= xxx->svc.start_h + num_attrs;
// add the passed in callback function to service structure
xxx->xxx_cb1=callback1;
// add newly created service to ble framework
ble_service_add(&xxx->svc);
// and return handle for the service to the application
return &xxx->svc
}
5.1.17. Extending BLE functionality
The Dialog BLE API can be used to develop BLE applications. The API header files are located in folder <SDK_ROOT_PATH>/sdk/interfaces/ble/api/include
. They are documented in [Ref_04] and are summarized in Table 58.
File name |
Description |
---|---|
ble_att.h |
Attribute Protocol API: Mostly definitions. |
ble_attribdb.h |
Helper to manage complex attributes database. |
ble_bufops.h |
Helpers to put and get data from BLE buffers. |
ble_common.h |
Common API: Functions used for operations not specific to a certain BLE Host software component |
ble_gap.h |
GAP API:
|
ble_gatt.h |
Common definitions for GATT API |
ble_gattc.h |
GATT client API:
|
ble_gattc_util.h |
GATT client utilities |
ble_gatts.h |
GATT server API:
|
ble_l2cap |
BLE L2CAP API. |
ble_storage.h |
BLE persistent storage API. |
ble_uuid.h |
BLE UUID declarations and helper functions. |
5.2. Software Upgrade Over The Air (SUOTA)
5.2.1. Introduction
The SmartSnippets™ DA1470x SDK allows the user to update the software of the device wirelessly, using the Software Upgrade Over The Air (SUOTA) procedure.
When an update procedure is initiated from an Android or iOS device, a new firmware image is first transferred to the Flash memory and then the device reboots to complete the update. The final verification and activation of the new image is performed by the ROM boot loader on the next reboot. For more information see Section 5.3.1.
The SUOTA GATT server runs on the DA1470x device and the GATT client on the Android or iOS device running the SUOTA application.
5.2.2. SUOTA service description
This section gives a brief description of the SUOTA service, responsible for performing software upgrades over BLE. A detailed service characteristic description is given on Table 59.
Characteris tic |
SUOTA Version (SUOTA_VERS ION definition) |
Access |
Size |
Description |
---|---|---|---|---|
MEM_DEV |
since version v1.3 |
READ WRITE |
4 |
Using this characteristic the GATT client is able to send commands to the SUOTA service. Some of the most commonly used commands are the following:
|
GPIO MAP |
READ WRITE |
4 |
Used to specify GPIO map of external FLASH device. Currently not applicable. |
|
MEM_INFO |
READ |
4 |
Stores the total number of bytes received until now. |
|
PATCH_LEN |
since version v1.3 |
READ WRITE |
2 |
Specifies the number of bytes that when received, will trigger a notification back to the GATT client. This is meant to be used for flow control. The exact value is set by the GATT client during SUOTA. The notification is generated from the “STATUS” characteristic |
PATCH_DATA |
since version v1.3 |
READ WRITE WRITE_NO_RE SP |
Exact size is specified by PATCH_DATA CHAR_SIZE |
This is the endpoint to which SUOTA image data are sent. The exact size is specified by the “PATCH_DATA CHAR_SIZE” characteristic, and different values (23 – 509) can be used depending on the throughput requirements |
STATUS |
since version v1.3 |
READ NOTIFY |
1 |
This characteristic is used to notify the GATT client of the status of the SUOTA process. Status notifications are sent to indicate error conditions (for example bad command, or CRC) or to allow flow control during SUOTA process. |
L2CAP_PSM |
since version v1.3 |
READ |
2 |
This is an optional characteristic that, if exists, indicates that the SUOTA service supports both SUOTA over GATT and SUOTA over L2CAP CoC. The value indicates the dynamic L2CAP channel on which the SUOTA service is listening for connections. The absence of this characteristic indicates that only SUOTA over GATT is supported. |
VERSION |
since version v1.3 |
READ |
1 |
Indicates the version of the SUOTA service. The value is retrieved from the “SUOTA_VERSION” definition. |
MTU |
since version v1.3 |
READ |
2 |
Stores the current value of the MTU, which is going to be either 23 (default), or a bigger value, if MTU exchange took place. This value can be used by the GATT client to retrieve the MTU size (if such API is not available on its side) and write with optimal rate to the “PATCH_DATA” characteristic. |
PATCH_DATA CHAR_SIZE |
since version v1.3 |
READ |
2 |
Specifies the size of the “PATCH_DATA” characteristic. |
CCC |
READ WRITE |
1 |
Client Characteristic Configuration Allows the GATT client to enable notifications from the “STATUS” source. |
Once the SUOTA service is discovered on the remote device and the GATT client
has enabled notifications by writing the CCC characteristic, the SUOTA
procedure can be started by issuing the SPOTAR_IMG_SPI_FLASH
command.
The write command executes successfully only if:
No more than one device is currently connected to the SUOTA enabled device
The application hosted in the SUOTA enabled device allows the upgrade to take place
There is enough memory to allocate the internal working buffers
If any of the above restrictions is violated, then command fails and an
error notification is sent back to the GATT client (status SUOTA_SRV_EXIT
).
After a successful command execution the service is able to receive data
either over GATT or L2CAP CoC layer (if the L2CAP_PSM
characteristic is
available).
The GATT client can use the value of the characteristic MTU
to perform ATT
write commands to the characteristic PATCH_DATA
with optimal size if
the GATT Client (mobile phone) has no API to find the optimal packet size.
It is also possible for the GATT client to retrieve the size of the PATCH_DATA
characteristic by reading the PATCH_DATA_CHAR_SIZE
characteristic.
Following this, the GATT client should specify the value of the patch_len
variable by writing the PATCH_LEN
characteristic. PATCH_LEN
specifies
the number of bytes that once received, will trigger a notification back to
the GATT client. This kind of flow control could be used to
avoid flooding the SUOTA enabled device with too much image data. The
bigger the value, the better the throughput, since notifications are
going to be generated less frequently and therefore the number of missed
connection events (where flow has stopped waiting for the notification)
is decreased.
For example, if patch_len
is set to 500 bytes, notifications will
be sent to the GATT client when byte ranges 1-500, 501-1000, 1001 – 1500 etc.
are received. Following the Bluetooth low energy specification, the
maximum number of bytes that can be written to the PATCH_DATA
characteristic with a single ATT write command is the minimum of MTU – 3
and the size of the PATCH_DATA
characteristic.
When the whole image has been sent, the GATT client should issue the
SPOTAR_IMG_END
command to indicate this to the SUOTA service. The
service is going to perform some sanity checks to verify that image
transfer took place without errors, and then it is going to generate the
appropriate status notification (SUOTA_CMP_OK
on success,
SUOTA_APP_ERROR
or SUOTA_CRC_ERR
on error).
Finally, the GATT client could issue an SPOTAR_REBOOT
command to force a
device reboot. This step is optional, but it is highly recommended.
Note
The PATCH_DATA
, PATCH_DATA_CHAR_SIZE
and PATCH_LEN
characteristics
are only relevant when SUOTA over GATT is taking place. When L2CAP
CoC are used, a connection should be established to the L2CAP_PSM
channel via L2CAP CoC and the flow is controlled using L2CAP credits.
SUOTA service assigns enough credits to ensure that flow won’t stop
during the upgrade. Notifications relevant to the PATCH_LEN
characteristic are not sent during image transfer, but all other
notifications are still valid.
5.2.3. SUOTA Flow
Figure 54 and Figure 55 illustrate the abstract FW update steps. Figure 56 illustrates in detail the flow diagram of the SUOTA.
The final verification and activation of the new image is performed by the ROM boot loader on the next reboot.
Figure Figure 75 illustrates the booting sequence that shows at which step the acceptance/rejection of a FW image happens.

Figure 54 Flash layout after initial programming during production

Figure 55 Flash image layout after SUOTA image written in the destination partition

Figure 56 Flash image layout after reboot and activation of the new image
5.2.4. Performing SUOTA upgrade using a mobile phone
Note
The following procedure applies when using Android or iOS devices
5.2.4.1. Preparing the SUOTA enabled device
Build the pxp_reporter application using the
DA1470x-00-Release_OQSPI_SUOTA
configuration.Erase the Flash memory of DA1470x using the
erase_oqspi_jtag
script.Download the pxp_reporter binary to DA1470x using the program_oqspi_jtag script.
Press the B1 (Reset) button on the DK daughter board. PXP Reporter application should start running.
5.2.4.2. Preparing the SUOTA image
A SUOTA image is a binary file with a proper header that can be sent to a target device from an Android or iOS device.
To prepare a SUOTA image using PXP Reporter demo application, perform the following steps:
Import the following two projects into SmartSnippets™ Studio from these locations.
python_scripts:
<SDK_ROOT_PATH>\utilities
pxp_reporter:
<SDK_ROOT_PATH>\projects\dk_apps\demos
Build the pxp_reporter application using the
DA1470x-00-Release_OQSPI_SUOTA
configuration. A new image namedpxp_reporter.img
is also created underDA1470x-00-Release_OQSPI_SUOTA
folder.
5.2.4.3. Perform the SUOTA using the mobile application
Download the Dialog SUOTA application from Google PlayStore or Apple App Store.
Copy
pxp_reporter.img
to an Android phone or tablet or to an iOS device and placed into the SUOTA folder. The folder is automatically created, if it does not exist, on the device by running the “Dialog Suota” application. On Android it is located at the root directory of the “Internal Storage” drive.Launch the Dialog SUOTA application on the Android phone and select the DA1470x device you want to update.

Figure 57 Device selection
Select Update device.

Figure 58 Update device
Select the appropriate image file – this is a list of the files in the SUOTA directory.

Figure 59 Image file
Touch Send to device on the touchscreen

Figure 60 Parameter settings for SPI

Figure 61 Parameter settings for I2C
Wait until the process is completed. When the image is uploaded, a dialog box pops up asking for a device reboot. Select OK.

Figure 62 Uploading the image file

Figure 63 Reboot device
Press Close to return to the main menu.

Figure 64 When file upload is finished, press “Close”
5.2.5. Performing SUOTA upgrade using two DA1470x devices
This section describes the procedure for performing SUOTA using two DA1470x devices.
One acting as the BLE central. It performs as the SUOTA image transmitter running
ble_suota_client
application. The SUOTA image will be stored in the NVMS_BIN_PART NVMS partition.One acting as the BLE peripheral. It performs as the SUOTA image receiver, running
pxp_reporter
application.
Using this setup, it is possible to test both SUOTA methods (over GATT and over L2CAP Connection-Oriented Channels) without using any phone. The image to be transferred is stored in the NVMS_BIN_PART partition in the Flash memory of the BLE central device.
5.2.5.1. Preparing the SUOTA enabled device pxp_reporter
Follow the steps described in Section 5.2.4.1
As soon as program_oqspi_jtag
is executed, a new window pops up asking to choose
which device is the target; select the appropriate device.

Figure 65 Selecting target device
5.2.5.2. Preparing the SUOTA update image
See Section 5.2.4.2
5.2.5.3. Preparing the ble_suota_client
device
5.2.5.4. Building the ble_suota_client
application
Import the following projects using SmartSnippets™ Studio from the following locations:
python_scripts:
<SDK_ROOT_PATH>\utilities
ble_suota_client:
<SDK_ROOT_PATH>\projects\dk_apps\features
and select the following build configuration for the ble_suota_client project:
ble_suota_client in
DA1470x-00-Release_OQSPI
configuration.
To build and install the ble_suota_client
, follow the procedure below:
Build the project ble_suota_client by executing
DA1470x-00-Release_OQSPI
Erase the flash memory of the device by executing the
erase_oqspi_jtag
script.Execute the
program_oqspi_jtag
script to program the OQSPI Flash memory.
As soon as the script is executed, a new window pops up asking to choose which of the DKs is the target; select the appropriate device.

Figure 66 Selecting the target device
5.2.5.5. Storing the SUOTA update image in the ble_suota_client
device
Note
The SUOTA update image was created in step Preparing the SUOTA update image
Use cli_programmer
to download the binary pxp_reporter.img to the device.
> <SDK_ROOT_PATH>\binaries\cli_programmer.exe <SERIAL_PORT> write_oqspi 0x300000 <SDK_ROOT_PATH>\projects\dk_apps\demos\pxp_reporter\DA1470x-Debug_OQSPI_SUOTA\pxp_reporter.img
5.2.5.6. Performing the software upgrade procedure
When the previous procedure has finished, the two DA1470x devices are ready to communicate. To perform SUOTA on the PXP Reporter device, the following steps should be followed:
A serial terminal is needed to connect to the
ble_suota_client
device and perform the SUOTA. In the example below, “Real Term” is used for this purpose. Configure the serial terminal as follows:

Figure 67 Configuring the serial port
Connect to the serial port and press the
B1
RESET button.

Figure 68 Connecting to the serial port
Information regarding the image stored in the NVMS_BIN_PART partition are displayed during boot
In the serial terminal of the
ble_suota_client
device, give the following command:> scan start

Figure 69 Scanning for available devices
The ble_suota_client
device starts scanning for available
devices immediately (Figure 69). In this in example, from the devices
listed Figure 69, the pxp_reporter
device, is the
device with sequence number [01]
.
As soon as the
pxp_reporter
device is found, the scanning operation can be stopped with the following command:> scan stop
A connection to the
pxp_reporter
device can be initiated with the command:> connect 1
The first argument of the “connect” command refers to the device index on the scan result list. Once a connection is established, the application automatically queries the remote device for available services and device information. The characteristic values of the Device Information Service (DIS) are read. The following output is printed on the terminal:

Figure 70 Connecting to loader device
The presence of “L2CAP PSM” indicates that the remote device supports SUOTA and over-L2CAP COC.
To update a device supporting L2CAP COC over L2CAP, issue the update command. To update the same device over GATT, issue the update gatt command. If the remote device does not support L2CAP COC (“L2CAP PSM” is not displayed), both update and update gatt commands begin SUOTA over GATT.
> update gatt

Figure 71 Updating with new image the loader device
After the image transfer has been completed, the remote device disconnects and reboot as shown in Figure 72.

Figure 72 Transfer complete
When the pxp_reporter
device reboots, execution of the new image begins.
The software upgrade has finished. Now the pxp_reporter
device should start advertising as <Dialog PX Reporter>
. To
verify that, perfom the scan operation again by issuing the scan start
command.
> scan start

Figure 73 Verifying that loader is running PX Reporter
5.3. Booting
The Boot ROM code is executed by SYSCPU
(Cortex-M33) after a POR or HW Reset. As an additional capability it can be executed after wakeup when the RESET_ON_WAKEUP
feature is configured.
Four different booting flavors are supported:
Boot from cached OQSPI FLASH without any security features, configuration script (CS) in OTP
Boot from cached OQSPI FLASH without any security features, configuration script (CS) in FLASH
Boot from cached OQSPI FLASH with security features, configuration script (CS) in OTP
Boot from UART without FLASH or security features
In case neither Flash nor UART is available, the booter remains in an infinent loop.
The Configuration Script (CS) is a table of up to 256 32-bit entries. System can boot without CS but if it exists it is stored either in Flash or OTP. The booter expects CS to start at
address 0x00000000
of flash or at 0x00000C00
of OTP. It contains information related to booter, like Product header location, the development mode flag, entries which can be used by SDK
or application software to apply some values to specific registers (address value pair) or to use them as trim values for a subsystem. The address value pair entry in CS is a way to enable the
secure features of the booter. For extra protection in secure boot mode CS is stored to OTP in order to be difficult to change the security related entries.
For more info about CS valid entries, please refer to the DA1470x datasheet chapter “System Overview”, paragraph “OTP”, section “Configuration Script”.
5.3.1. Boot flow
The boot flow is divided in five separate phases
Initialization: The booter takes care of configuring power domains and peripheral clocks. The boot code resets the OTP (One Time Programmable) - and Flash memory in order to be able to access them. It enables the Development Mode by default. In Development Mode the booter enables the debugger and it is also possible to update the devices firmware by UART. Development mode can be deactivated adding the corresponding entry in CS.
Run Configuration Script: The booter searches for the CS in the beginning of Flash or address
0x00000C00
of OTP. If CS is found it starts parsing it and applies its directives, otherwise continues to next state.Retrieve application code: This phase depends on Development mode.
Development mode enabled
The booter will enable debugger and try to boot form UART. If it fails to retrieve an application from UART it will try to locate a valid product header. If it finds a product header will continue to next phase otherwise will try to boot from UART again. In case there is no valid product header the booter will enter an infinite loop. This loop is active until it boots form UART or a debugger is connected.Development mode disabled
In this case both debugger and booting from UART feature are disabled. The booter tries to locate and parse the Product and image headers from FLASH. If these headers are invalid, HW Reset is performed.Device administration: The booter will first check if there is any pending update. In case of update it will validate the new firmware image and if the validation succeeds the booter will accept the update. Otherwise the booter will reject the update, trigger a HW Reset and boot the old image. In case there is no pending update the booter will validate the active (old) image. If validation fails the booter will trigger again a HW reset. Image validation depends on secure mode. If the image is no secure then the booter checks for ‘Qq’ in the beginning of the image header. If FW image is secure then booter has to do the below additional checks.
Checks if image header contains the security section (
0xAA22
).Checks if image header contains the administration section (
0xAA44
)Checks if is able to extract public keys, encryption keys, signature verification value and NONCE.
Checks if the public key used for the signature verification is not revoked.
Checks if application encryption key is not revoked.
Checks if signature value calculated using
Ed25519
algorithm is the correct one.
In case any of the above checks fail, then the booter will trigger a HW reset.
Load image: This is the final booting phase in which the actual FW image is loaded. This phase is divided in the following steps:
Flash OQSPI controller configuration retrieving information from the
Product header
.In case the secure feature is enabled, the decryption key is fetched through DMA to OQSPI controller. OQSPI controller supports image decryption on the fly.
Interrupt Vector Table (IVT) is copied to RAM.
Cache controller is configured to point to the beginning of IVT.
OQSPI FLASH address is remapped to address 0x0.
SW_RESET is triggered.
If any error occurs during this phase a HW reset will be triggered by the booter.
For more details please refer to Figure 75.

Figure 75 BootROM flow
5.3.2. FLASH Layout
The bootROM expects at least three different section in FLASH to be able to boot.
These are shown in : Figure 77.

Figure 77 FLASH layout
Configuration Script is an optional region.
Product header starts with “Pp” and contains information about the address of the active and updated FW image, OQSPI controller and the Flash device setup
and the CRC code used to verify Product header integrity. There are two Product Headers, Primary and Backup which are identical. The reason is that we want to protect
system from a possible Product header corruption. The booter first does a CRC check of the primary header and if is successful it parses it, otherwise does a CRC check
of the backup header. In case both headers are corrupted the booter continues as there is no Product Header
and behaves as described above in boot flow
chapter
in paragraph Retrieve application code
. If only primary is corrupted, the booter copies backup header to primary header. If active and update FW image addresses are
different, the booter will trigger the update procedure. An update image is considered valid when all the checks described in Device Administration phase are successful.
An update is considered successful when the booter executes successfully all the boot flow phases described above.
Product Header fields:
Flash Programmed identifier
“Pp” Product header identifierActive FW Image Address
address in Flash of the active FW Image headerUpdate FW Image Address
address in Flash of the update FW Image headerFlash BURSTCMDA reg value
value written in the OQSPIC_BURSTCMDA_REG of the OQSPI controllerFlash BURSTCMDB reg value
value written in the OQSPIC_BURSTCMDB_REG of the OQSPI controllerFlash CTRLMODE reg value
value written in the OQSPIC_CTRLMODE_REG of the OQSPI controller0xAA11
Flash config section identifierLength of Flash Config Section
size of Flash config sectionFlash Write Config reg Command sequence
commands for OQSPI controller
Image header region starts with “Qq” and is divided into three sections, common, security and administration.
Common section fields:
Image identifier
(“Qq”) Image header identifiersize
the FW image sizeCRC
CRC value for image integrity checkTimestamp
number of seconds passed since epoch (1/1/1970)IVT
offset from the beginning of theImage header
to the start of the IVT.
Security section fields:
0xAA22
security section identifierLength of security section
size of security sectionIndex to ECC key
signature key indexIndex to Sym. key
FW decryption keyNONCE
used in FW decryption algorithmLength of security section
size in bytes of the signature valueSignature
signature value used for signature verification
Administration sections fields:
0xAA44
administration section identifierLength of administration section
size of administration section0xAA55
key revocation section identifierLength of the key revocation record
size of key revocation recordKey Type
type of the key(eg ECC, Sym., User data key)Key Index
key indexnext key records if are available
For security reasons the key revocation status is stored in a specific area in OTP per key type.
IVT and executable is the section where the IVT and application are stored. In case the boot is secure this section is encrypted.
5.3.3. OTP Layout
The OTP consists of several different segments. None of these segments needed in case of no secure boot. In the below table there is a description of the segments.
Table 60 OTP Layout Segment
Bytes
Description
OTP Address
1
1024
Configuration Script
~100 registers write operations
0x00000C00
2
256
O/QSPI FW Decryption Keys Area – Payload
write/read protected when secure mode enabled in CS
Secure mode connects those (8 * 256-bits) keys to O/QSPI Controller
0x00000B00
3
256
User Data Encryption Keys – Payload
Write/Read protected when secure mode enabled in CS.
Secure mode connects those (8 * 256-bits) keys to AES engine
0x00000A00
4
32
O/QSPI FW Decryption Keys Area – Index
8 entries for 8 256-bit keys
0x000009E0
5
32
User Data Encryption Keys – Index
8 entries for 8 256-bit keys
0x000009C0
6
256
Signature Keys Area – Payload
0x000008C0
7
32
Signature Keys Area – Index
0x000008A0
8
2208
Customer Application Area (Secondary bootloader, binaries, …)
0x00000000
5.4. Build configurations and startup procedure
This section provides a brief description of the supported project build configurations and the startup procedure of the SmartSnippets™ DA1470x SDK.
5.4.1. Build configurations
SmartSnippets™ DA1470x SDK projects by default support the following two types of build configurations:
Build Configuration |
Location of code and read-only data |
Cache enabled |
Description |
---|---|---|---|
RAM |
RAM |
No |
Program is loaded directly to RAM |
OQSPI |
OQSPI Flash |
Yes |
Program runs in place from OQSPI Flash |
5.4.1.1. OQSPI build configuration
In this mode, the code will be placed in Flash whereas variables will be placed in RAM. It is also possible to move specific
functions to RAM using the __RETAINED_CODE
macro. This could be useful when a particular function implements time critical functionality.
A program built for Flash cached mode is written into OQSPI Flash memory with the CLI programmer tool. Along with the application image, OQSPI Flash
shall be programmed with valid Product and Image Headers too. Upon hard reset, the booter reads these headers to set up the external Flash memory
and prepare the application image before jumping to it.
After hard reset, ROM is remapped to address 0x0 and the booter starts executing. At first, the booter will read the Product and Image headers
(located to Flash) to verify that a valid firmware image is programmed to Flash. It will also program the OQSPI Flash controller according to the contents of the relevant fields in the
Product Header to enable OQSPI Auto mode and execution in place (XiP). Before starting executing the programmed firmware image, the booter sets up the cache controller and copies
application’s IVT (Interrupt Vector Table) from Flash to RAM. SYS_CTRL_REG[REMAP_INTVECT]
is enabled so that the virtual addresses 0 - 0x1FF are mapped to the beginning of RAM,
i.e. memory area 0x00000000 – 0x000001FF
is mapped to memory area 0x20000000 – 0x200001FF
.
This ensures that the IVT is always located in RAM for quick access. SYS_CTRL_REG[REMAP_ADR0]
is set to 2 (Flash is remapped to virtual address 0x0)
and a software reset is applied. CPU fetches the location of Reset_Handler
from RAM (since SYS_CTRL_REG[REMAP_INTVECT]
is enabled), and the Reset_Handler
starts executing
from Flash.
A program built with the OQSPI build configuration can be executed using the OQSPI_DA1470x
debug configuration using the SmartSnippets™ Studio. It is also possible to execute
the image by pressing the Reset button on the development kit, after programming the Flash memory.
5.4.1.2. RAM build configuration
In this mode, both code and data are placed in RAM.
A program built with the RAM build configuration can be loaded to RAM either directly using the ELF file and J-Link debugger or be first converted to a raw binary and then written to RAM with the CLI programmer tool.
RAM mode is used only for debugging purposes as it avoids the step of programming OQSPI Flash.
After loading the program, SYS_CTRL_REG[REMAP_ADR0]
must be configured so that RAM is mapped to address 0x00000000
.
After a soft reset is issued, the written program starts execution.
The RAM mode does not rely on the boot loader. Therefore, the Product and Image headers in OQSPI Flash do not affect firmware execution.
A program built with the RAM build configuration can be executed using the RAM_DA1470x
debug configuration using the SmartSnippets™ Studio. It is also possible to load and execute the image using the CLI programmer tool. In both cases SYS_CTRL_REG[REMAP_ADR0]
is configured appropriately by the corresponding tool.
5.4.2. Startup procedure
Startup code is the part of the program that runs after reset and before entering main()
.
Briefly, it consists of the following steps, as shown in Figure 79 (please consult the startup code within the SmartSnippets™ DA1470x SDK for details):

Figure 79 Startup procedure
Reset_Handler
insdk\bsp\startup\startup_da1470x.S
starts execution.SystemInitPre()
insdk\bsp\startup\system_da1470x.c
is called, to do the following:Enable debugger (if the corresponding macro,
dg_configENABLE_DEBUGGER
, is enabled).Set dividers of the AMBA High Speed Bus and Peripheral Bus.
Check IC version compatibility with SW.
Check alignment of copy and zero tables.
Bring the pad latches, the memory controller, the PDC and the peripherals’ clocks to a well known initial state.
Note
No variable initialization should take place here, since copy and zero tables have not been initialized yet and any modifications to variables will be discarded. For the same reason, functions that initialize or are using initialized variables should not be called from here.
Reset_Handler
insdk\bsp\startup\startup_da1470x.S
resumes execution.Copy code and data to RAM according to the
.copy.table
section.Initialize certain memory areas to zero according to the
.zero.table
section.
SystemInit()
insdk\bsp\startup\system_da1470.c
is called, simply handling the FPU._start()
is called which in turn calls__libc_init_array()
.__libc_init_array()
in turn callsda1470x_SystemInit()
, with the latter doing the following. In the end,_start()
callsmain()
.Initialize the
.bss
section to zero.Initialize TRNG/DRBG conditionally.
Configure interrupt priorities.
Configure the QSPI Flash and Cache. This is done to fine-tune the configuration applied by the booter code for the particular Flash model.
Read Trim and Calibration Section (TCS) values from One-Time Programmable (OTP) memory.
Configure the power domain controller (PDC).
Bring power domains to a well known initial state.
Activate BOD protection.
5.5. Memory Layout
This section gives a brief description on the available program and data memories.
Information on the memory layout is given in Figure 81 and in Table 63 below.

Figure 81 System Memory
Memory Device |
Start Address | End Address |
Size (bytes) |
Size (KiB) |
|
---|---|---|---|---|
Remapped Devices |
0x00000000 |
0x08000000 |
0x08000000 |
131072 |
ROM |
0x0F020000 |
0x0F030000 |
0x00010000 |
64 |
OTP |
0x10080000 |
0x10090000 |
0x00010000 |
64 |
RAM (Code Interface) |
0x10000000 |
0x10050000 |
0x00050000 |
320 |
RAM (System Interface) |
0x20000000 |
0x20180000 |
0x00180000 |
1536 |
OQSPI FLASH (Code Interface, cached) |
0x18000000 |
0x20000000 |
0x00800000 |
131072 |
OQSPI FLASH (System Interface, uncached) |
0x38000000 |
0x40000000 |
0x00800000 |
131072 |
QSPI FLASH (System Interface, cached) |
0x28000000 |
0x30000000 |
0x00800000 |
131072 |
QSPI FLASH (System Interface, uncached) |
0x48000000 |
0x50000000 |
0x00800000 |
131072 |
5.5.1. ROM
The 64KiB ROM is mapped to address 0x0F020000
and is only used for executing the booter code. This is the first piece
of code that will be executed after a HW reset, before jumping to the application.
5.5.2. OTP
The 64KiB one-time programmable (OTP) memory is accessible at address 0x10080000
and its main purpose is to store information
(Product Header and Configuration Script) that will be used by the booter before jumping to the application.
5.5.3. System RAM
The RAM (up to 1536KiB) is mapped to addresses 0x10000000
(Code Interface) and 0x20000000
(System Interface).
Accesses to RAM are never cached. In BLE projects, some memory is used by the CMAC CPU.
In SNC projects, some memory is used by the SNC CPU.
The system RAM comprises 14 RAM cells (4-256KiB in size), which can be indpendently configured to
not retain their content during sleep, to decrease power consumption. It is also possible for a RAM cell to be completely disabled
in case it is not needed by the application.
5.5.4. External OQSPI Flash
The external FLASH memory (up to 128MiB) is mapped to addresses 0x18000000
(Code Interface) and 0x38000000
(System Interface).
Accesses to the external FLASH memory are only cached when performed through the Code Interface (or the Remapped Region) and are performed transparently
thanks to the use of the internal OQSPI FLASH controller. The BLE Host, the SNC and the M33 application code are located here.
5.5.6. External QSPI Flash/PSRAM
The external FLASH memories (up to 128MiB, each) are mapped to addresses 0x28000000
(external Flash System Interface) and
0x48000000
(external PSRAM System Interface).
An 8 kB data cache with write-back capabilities, is integrated into one of the two QSPI controllers
for increased read/write performance for use with an external PSRAM only.
5.5.7. Remapped Region
Depending on the value of SYS_CTRL_REG[REMAP_ADR0]
register field, accesses to the address region [0x0, 0x07FFFFFF]
can be mapped to
The ROM (
0x0F020000
)The system RAM (
0x10000000
)The External OQSPI Flash address range (
0x18000000
)
When SYS_CTRL_REG[REMAP_INTVECT]
is set to 1, the address range [0x0, 0x1FF]
is mapped to System RAM
[0x10000000, 0x100001FF]
address range, allowing to have the interrupt vector table in RAM while executing from FLASH.
When FLASH is remapped to address 0x0 (SYS_CTRL_REG[REMAP_ADR0]
is set to 2) and the cache is enabled, the effective FLASH address for an
address in the range [0x0, 0x07FFFFFF] used by the CPU or the Debugger depends on the value of CACHE_FLASH_REG
:
FLASH_REGION_BASE (
CACHE_FLASH_REG[31:16]
) defines the FLASH region base.FLASH_REGION_OFFSET (
CACHE_FLASH_REG[15:4]
) defines the offset for each subregion.FLASH_REGION_SIZE (
CACHE_FLASH_REG[3:0]
) defines the FLASH region size.
The effective FLASH address for a remapped address can be calculated as follows:
(CACHE_FLASH_REG[FLASH_REGION_SIZE] << 16) + (CACHE_FLASH_REG[FLASH_REGION_OFFSET] << 2) + ADDR
This enables an application image compiled to execute from address 0x0, to be placed in different FLASH locations to allow firmware update.
Consider for example the case where CACHE_FLASH_REG
has the value 0x18009001
:
FLASH_REGION_BASE
is0x1800
.FLASH_REGION_OFFSET
is0x900
. Since this field is in 32-bit words, the actual offset in bytes is0x2400
.FLASH_REGION_SIZE
is0x1
. This value corresponds to a flash region size of 0.5MiB (0x80000
).
Any CPU or Debugger access to address 0x1000
will be remapped to FLASH address 0x18002400
+ 0x1000
.
Since CACHE_FLASH_REG[FLASH_REGION_BASE]
is 0x1
(0.5 MiB), the valid address range that the CPU is allowed to access
through the remapped region will be [0x0, 0x7FFFF - 0x2400]
, which translates to the [0x18002400, 0x1807FFFF]
FLASH range. Trying to access
an address outside this region will give undefined results. The same restrictions apply also to the code memory range, e.g. the
CPU will be able to access only addresses [0x18000000, 0x1807FFFF]
using the code memory range, 0x18000000
to 0x187FFFFF
. Note though, that the
address range [0x18000000, 0x180023FF]
will be accessed uncached. However, the debugger can access the whole code memory range,
0x18000000
to 0x187FFFFF
, regardless of the CACHE_FLASH_REG
configuration.
The CACHE_FLASH_REG
is configured to the proper value during booter execution, depending on the product and application FW headers.
Both the CPU and the debugger are able to access the whole FLASH address range without restrictions using the uncached system memory range,
0x38000000
to 0x387FFFFF
.
5.6. Non-Volatile Memory Storage (NVMS)
SmartSnippets™ DA1470x SDK defines a software layer for Non-Volatile Memory Storage management. It is essential for the user to have a clear understanding of the requirements for the following elements that could use non-volatile storage:
System Parameters that need to be stored in NVM (e.g. device address)
Firmware upgrade (dual images)
Application specific binaries (e.g. pre-recorded audio messages)
Logging (e.g. event logs)
Application data (e.g. sensor values, statistics, authentication keys)
For each storage type, a corresponding dedicated region is allocated in the Flash partition table. Each region is identified by a partition ID. When the NVMS Adapter performs read/write accesses from/to storage, it uses the partition ID and an offset. Additional details can be found in the NVMS Adapter The NVMS Adapter
.
SmartSnippets™ DA1470x SDK defines the following Flash partitions (in a non-SUOTA build) to manage storage:
(PRODUCT_HEADER) Product header
(PARTITION_TABLE) Partition table
(FW) Firmware Image Region
(PARAMS) Parameters Region
(BIN) Binaries Region
(LOG) Logging of events or values
(GENERIC) Generic data Region, Statistics etc.
The exact memory mapping depends on the actual Flash device (i.e. size, sector size) used on the board.
This mapping needs to be defined at compile-time in the file <SDK_ROOT_PATH>/bsp/config/8M/partition_table.h
(or <SDK_ROOT_PATH>/bsp/config/8M/suota/partition_table.h
when SUOTA is used).
A default partition table is provided with SmartSnippets™ DA1470x SDK, which fits in the DK OQSPI Flash (8MiB with sectors of 4KiB), the actual definition of which is shown in Code 46:
PARTITION2( NVMS_PRODUCT_HEADER_PART , 0 )
PARTITION2( NVMS_PARTITION_TABLE , PARTITION_FLAG_READ_ONLY )
PARTITION2( NVMS_FIRMWARE_PART , 0 )
PARTITION2( NVMS_GENERIC_PART , PARTITION_FLAG_VES )
PARTITION2( NVMS_LOG_PART , 0 )
PARTITION2( NVMS_BIN_PART , 0 )
PARTITION2( NVMS_PARAM_PART , 0 )
When SUOTA is implemented, an additional partition is required so that both the current and the updated image can be stored, as shown in Code 47:
PARTITION2( NVMS_PRODUCT_HEADER_PART , 0 )
PARTITION2( NVMS_PARTITION_TABLE , PARTITION_FLAG_READ_ONLY )
PARTITION2( NVMS_FW_EXEC_PART , 0 )
PARTITION2( NVMS_GENERIC_PART , PARTITION_FLAG_VES )
PARTITION2( NVMS_FW_UPDATE_PART , 0 )
PARTITION2( NVMS_LOG_PART , 0 )
PARTITION2( NVMS_BIN_PART , 0 )
PARTITION2( NVMS_PARAM_PART , 0 )
5.7. XiP (OQSPI) FLASH Support
This section describes the XiP flash memory support in SmartSnippets™ DA1470x SDK and provides a guideline on how to add support for new flash memories. The XiP flash memory is connected to the OQSPI controller (OQSPIC), which supports both OCTA and QUAD bus flash memories.
5.7.1. API description
The OQSPI flash memory functionality is implemented by the oqspi_automode.{h c}
API.
The file tree of the API is shown below:
sdk
│
└───memory
| | README OQSPI.md
│ │
│ └───include
│ │ oqspi_automode.h
│ │ oqspi_common.h
│ │ oqspi_<manufacturer>_<bus_mode>.h
│ │ oqspi_<manufacturer_prefix><memory_name>.h
│ │
│ src
│ │ oqspi_automode.c
where:
<manufacturer>: The name of the manufacturer, e.g. Macronix, Winbond, Adesto, Gigadevice.
<bus_mode>: The bus mode of the memory, e.g. quad, octa.
<manufacturer_prefix>: A prefix that determines the memory manufacturer, e.g. mx (Macronix), w (Winbond), at (Adesto), gd (Gigadevice).
<memory_name>: The OQSPI flash memory name.
The oqspi_common.h contains definitions of common macros and structures, whereas the header files oqspi_<manufacturer>_<bus_mode>.h contain all common code used by a particular group of memories, e.g. the oqspi_macronix_octa.h contains common code for octa bus Macronix flash memories. The oqspi_<manufacturer_prefix><memory_name>.h are the flash memories drivers, which contain all the neccessary settings and callback functions, in order for the API to properly configure the OQSPIC for that memory.
5.7.2. Supported flash memories
The SDK supports the next flash memories:
Macronix MX25U6432, 64MBit (QUAD)
Winbond W25Q64JWIM, 64MBit (QUAD)
The default XiP flash memory is the Macronix MX25U6432, which is mounted on the ProDK daughterboard.
5.7.2.1. Select OQSPI flash memory
The OQSPI flash memory is selected by defining the macros shown in Table 64.
OQSPI flash memory build macros |
Description |
---|---|
dg_configOQSPI_FLASH_HEADER_FILE |
This macro must be defined as a string and determines the name of the OQSPI flash driver, for which the
application is built for, e.g. “oqspi_mx25u6432.h”, “oqspi_w25q64jwim.h” etc. It must be either
one of the oqspi_<flash_memory>.h header files, found in |
dg_configOQSPI_FLASH_CONFIG |
This macro defines the oqspi_flash_config_t structure of the OQSPI flash driver, for which the application is built for, e.g. oqspi_mx25u6432_cfg, oqspi_w25q64jwim_cfg etc |
In order to change the OQSPI flash memory of an application, define
the aforementioned macros in config/custom_config_xxx.h
(see
the example Code 48.).
#define dg_configOQSPI_FLASH_HEADER_FILE "oqspi_w25q64jwim.h"
#define dg_configOQSPI_FLASH_CONFIG oqspi_w25q64jwim_cfg
5.7.3. Build configuration options
The SmartSnippets™ DA1470x SDK has two build configuration macros (see Table 65), which determine whether the OQSPI flash memory is pre-configured at compile time or autodetected at runtime, and if the former is selected, whether its JEDEC ID is verified or not.
dg_configOQSPI_FLASH_AUTODETECT |
dg_configOQSPI_FLASH_CONFIG_VERIFY |
|
---|---|---|
0 |
0 |
Default option. The OQSPI flash memory is pre-configured at compile time, yet its JEDEC ID is NOT verified. |
0 |
1 |
The OQSPI flash memory is pre-configured at compile time, and its JEDEC ID is verified (NOT recommended for production builds). |
1 |
0 |
The OQSPI flash memory is autodetected at runtime (NOT recommended for production builds). |
1 |
1 |
Not valid build configuration. When the OQSPI flash is autodetected atruntime the JEDEC ID is verified, hence the dg_configOQSPI_FLASH_CONFIG_VERIFY is meaningless. |
5.7.3.1. Pre-configured at compile time
When the XiP flash memory is pre-configured at compile time, only the flash driver of the selected memory is compiled, thus a binary file with smaller build size is generated. For this reason, this option is suitable for production builds.
5.7.3.2. Autodetected at runtime
When the XiP flash memory is autodetected at rutime, the JEDEC ID of the connected flash memory is read and compared with the JEDEC ID of all supported memories. If the JEDEC ID matches, the corresponding flash driver is used in order to configure the OQSPIC properly. If not, the default OQSPI flash driver, determined by the macros shown in Table 64 will be used, and most likely the OQSPIC will not be configured properly.
Since the flash driver is detected at runtime, all OQSPI flash drivers
must be included to the binary and this increases its size by a few
KBytes. Therefore, this option is NOT recommended for production
builds and it is mainly used by the flashing tools (uartboot
,
cli_programmer
, segger_flash_loader
etc).
Note
The default pre-configured OQSPI flash memory is the recommended option for production builds, since the autodetect option increases by a few KBytes, both the build size of the binary and the retained RAM usage.
The default values of these macros are defined in sdk/config/bsp_defaults_da1470x.h
.
5.7.4. OQSPI flash driver
The OQSPI memory drivers are header files located in sdk/bsp/memory
,
which contain an instance of the OQSPI flash configuration structure
oqspi_flash_config_t
, as well as a set of callback and helper
functions, neccessary to configure the OQSPIC properly for a particular
memory. The oqspi_flash_config_t
is defined in oqspi_common.h
.
oqspi_flash_config_t field |
Description |
---|---|
jedec.manufacturer_id |
The JEDEC manufacturer ID. |
jedec.type |
The JEDEC device type. |
jedec.density |
The JEDEC device density. |
jedec.density_mask |
The JEDEC device density mask, which is used to mask the device density reading, if needed. If not, must be set 0xFF. |
size_mbits |
The memory size in MBits. |
address_size |
The address size of the commands, which can be either 24bits or 32bits length. |
clk_mode |
Pointer to the flash-specific initialization function. |
opcode_len |
The length of the command opcode, which can be either 1 byte or 2 bytes length. If 2 bytes length is used, the second byte of the opcode equals to the inverted value of the first one. |
read_instr_cfg.opcode_bus_mode |
The bus mode of the opcode phase of the reading command. |
read_instr_cfg.addr_bus_mode |
The bus mode of the address phase of the reading command. |
read_instr_cfg.extra_byte_bus_mode |
The bus mode of the extra byte phase of the reading command. This phase is used in order to determine whether the next reading command will be in continuous mode of operation or not. When the continuous mode of operation is enabled, the opcode is sent only on the first reading and is omitted on all subsequent reading commands. |
read_instr_cfg.dummy_bus_mode |
The bus mode of the dummy bytes phase of the reading command. |
read_instr_cfg.data_bus_mode |
The bus mode of the data phase of the reading command. |
read_instr_cfg.continuous_mode |
Enable/disable the continuous mode of operation. If enabled, the read_instr_cfg.extra_byte_cfg must be enabled as well, and the read_instr_cfg.extra_byte_value must be set according to the datasheet of the memory in order to maintain the continuous mode of operation. |
read_instr_cfg.extra_byte_cfg |
Enable/disable the extra byte phase. |
read_instr_cfg.extra_byte_half_cfg |
Enable/disable transmitting only the high nibble of the extra byte, meaning that the output is switched to high-z during the transmission of the low nibble. Not applicable in octa bus mode, where it must always remain disabled. This setting is very rarely enabled in general. |
read_instr_cfg.opcode |
The opcode of the reading command. |
read_instr_cfg.extra_byte_value |
The value of the byte sent during the extra byte phase provided that the read_instr_cfg.extra_byte_cfg is enabled. If the continuous mode of operation is enabled, set this field according to the memory. If disabled, is usually set to 0xFF yet many other values could serve the same scope. |
read_instr_cfg.cs_high_delay_nsec |
The minimum required delay (nsec) that the CS signal has to stay at idle state between two consecutive read commands. |
erase_instr_cfg.opcode_bus_mode |
The bus mode of the opcode phase of the erasing command. |
erase_instr_cfg.addr_bus_mode |
The bus mode of the address phase of the erasing command. |
erase_instr_cfg.hclk_cycles |
The delay in terms of AMBA hclk clock cycles without flash memory reading requests before performing an erase or erase resume command. |
erase_instr_cfg.opcode |
The opcode of the erasing command. |
erase_instr_cfg.cs_high_delay_nsec |
The minimum required delay (nsec) that the CS signal has to stay at idle state between a Write Enable, Erase, Erase Suspend or Erase Resume command and the next consecutive command. |
read_status_instr_cfg.opcode_bus_mode |
The bus mode of the opcode phase of the read status register command. |
read_status_instr_cfg.receive_bus_mode |
The bus mode of the receive phase of the read status register command. |
read_status_instr_cfg.dummy_bus_mode |
The bus mode of the dummy bus phase of the read status register command. |
read_status_instr_cfg.dummy_value |
The value that is transferred on the OQSPI bus during the dummy cycles phase. In fact, the value of the dummy bytes is out of interest. However, there are some octa flash memories (e.g. Macronix) which require an address phase, during which the transmitted data must equal to 0x00. Since the OQSPIC does not support a dedicated address phase for the read status register command, this option allows forcing the dummy bytes value to 0x00 achieving the same result. |
read_status_instr_cfg.busy_level |
The level where the busy bit (low or high) is considered as busy. |
read_status_instr_cfg.busy_pos |
The position of the busy bit in the status register. |
read_status_instr_cfg.dummy_bytes |
The number of the dummy bytes of the read status register command. It should not been confused with the dummy bytes of read command. |
read_status_instr_cfg.opcode |
The opcode of the read status command. |
read_status_instr_cfg.delay_nsec |
The minimum delay in nsec between the read status register command and the previous erase command. Usually is not needed, thus is set 0. |
write_enable_instr_cfg.opcode_bus_mode |
The bus mode of the opcode phase of the write enable command. |
write_enable_instr_cfg.opcode |
The opcode of write enable command. |
page_program_instr_cfg.opcode_bus_mode |
The bus mode of the opcode phase of the page program command. |
page_program_instr_cfg.addr_bus_mode |
The bus mode of the address phase of the page program command. |
page_program_instr_cfg.data_bus_mode |
The bus mode of the data phase of the page program command. |
page_program_instr_cfg.opcode |
The opcode of page program command. |
suspend_resume_instr_cfg.suspend_bus_mode |
The bus mode of the opcode phase of the suspend command. |
suspend_resume_instr_cfg.resume_bus_mode |
The bus mode of the opcode phase of the resume command. |
suspend_resume_instr_cfg.suspend_opcode |
The opcode of the suspend command. |
suspend_resume_instr_cfg.resume_opcode |
The opcode of the resume command. |
suspend_resume_instr_cfg.suspend_latency_usec |
The minimum required latency in usec to suspend an erase operation. The next consecutive erase resume command cannot be issued before this delay has elapsed. |
suspend_resume_instr_cfg.resume_latency_usec |
The minimum required latency in usec to resume an erase operation. Once the resume command is issued, the currently suspended erase operation resumes within this time. |
suspend_resume_instr_cfg.res_sus_latency_usec |
The minimum required latency in usec between an erase resume and the next consequent erase suspend command. |
exit_continuous_mode_instr_cfg.opcode_bus_mode |
The bus mode of the opcode phase of the exit from continuous mode command. |
exit_continuous_mode_instr_cfg.sequence_len |
The number of the bytes needed to be shifted out, in order for the flash memory to get out from continuous mode. Once the memory is switched to continuous mode, the value of the extra byte determines whether the next read command will be sent in continuous mode or not, in other words with or without opcode. The extra byte is sent immediately after the address phase, which means that the the number of the bytes must be at least 4 or 5, for 24 bit and 32 bit address size respectively. Any higher amount of bytes work fine as well, yet in will cause a redundant overhead. |
exit_continuous_mode_instr_cfg.disable_second_half |
Disable the output during the second half of the sequence. Not applicable in octa bus mode. |
exit_continuous_mode_instr_cfg.opcode |
The opcode of the exit from continuous mode command. |
delay.reset_usec |
The minimum required delay in usec after a reset sequence. |
delay.power_down_usec |
The minimum required delay between an enter power down sequence and the moment the memory enters the power down mode. |
delay.release_power_down_usec |
The minimum required delay between a release power down sequence and the moment the memory exits from power down mode |
delay.power_up_usec |
The minimum required power up delay. |
callback.initialize_cb |
Flash memory initialization callback function which is called at system startup. |
callback.sys_clk_cfg_cb |
This function is called every time the system clock changes in order to re-configure the OQSPIC. |
callback.exit_opi_qpi_cb |
Callback function which exits the flash memory from OPI/QPI mode for octa/quad flash memories respectively. |
callback.get_dummy_bytes_cb |
Callback function which returns the number οf the dummy bytes for any given system clock. |
callback.is_suspended_cb |
Callback function which checks whether the flash erase operation is suspended. |
callback.is_busy_cb |
Callback function which checks whether the flash memory is busy. |
callback.read_status_reg_cb |
Callback function to read the status register of the memory. |
callback.write_status_reg_cb |
Callback function to write the status register of the memory. |
5.7.5. Add support for a new OQSPI flash memory
The SDK provides support for a specific set of Octa and Quad bus
flash memories. Each of those memories has a dedicated OQSPI flash
driver. In order to add support for a new memory, a new driver must
be implemented. It is recommended to use as a starting point either
the header file of an already existing driver or the template header
file oqspi_xxx.h
. If the new memory belongs to an already supported
group of memories, the macros and the functions from
oqspi_<manufacturer>_<bus_mode>.h
files can be used in order to
eliminate the development effort.
Copy and rename either an existing driver header file or the template header file
oqspi_xxx.h
. It is recommended to follow the naming patternoqspi_<manufacturer_prefix><memory_name>.h
.Rename the prefixes of all functions and variables accordingly. Keep in mind, that all drivers reside in the same name-space, therefore the names of all functions and variables must be unique.
Set all JEDEC ID fields (ID, type and density) according to the datasheet.
jedec.manufacturer_id
jedec.type
jedec.density
Set the size of the memory in MBits, the address size and the opcode length:
size_mbits
address_size
opcode_len
Set the opcode and the bus mode of all phases of the following commands (instructions) according to the datasheet:
- READ command
read_instr_cfg.opcode
read_instr_cfg.opcode_bus_mode
read_instr_cfg.addr_bus_mode
read_instr_cfg.extra_byte_bus_mode
read_instr_cfg.dummy_bus_mode
read_instr_cfg.data_bus_mode
- ERASE SECTOR command
erase_instr_cfg.opcode
erase_instr_cfg.opcode_bus_mode
erase_instr_cfg.addr_bus_mode
- READ STATUS command
read_status_instr_cfg.opcode
read_status_instr_cfg.opcode_bus_mode
read_status_instr_cfg.receive_bus_mode
read_status_instr_cfg.dummy_bus_mode
- WRITE ENABLE command
write_enable_instr_cfg.opcode
write_enable_instr_cfg.opcode_bus_mode
- PAGE PROGRAM command
page_program_instr_cfg.opcode
page_program_instr_cfg.opcode_bus_mode
page_program_instr_cfg.addr_bus_mode
page_program_instr_cfg.data_bus_mode
- ERASE SUSPEND & ERASE RESUME commands
suspend_resume_instr_cfg.suspend_opcode
suspend_resume_instr_cfg.resume_opcode
suspend_resume_instr_cfg.suspend_bus_mode
suspend_resume_instr_cfg.resume_bus_mode
Set the next timing parameters according to the datasheet:
read_instr_cfg.cs_idle_delay_nsec
erase_instr_cfg.cs_idle_delay_nsec
suspend_resume_instr_cfg.suspend_latency_usec
suspend_resume_instr_cfg.resume_latency_usec
suspend_resume_instr_cfg.res_sus_latency_usec
delay.reset_usec
delay.power_down_usec
delay.release_power_down_usec
delay.power_up_usec
The name of the timing parameters vary between different manufacturers, even between different memory variants of the same manufacturers. The next table represents the names of these parameters according to the datasheets of the supported memories, and can be used as a reference point.
manufacturer
Read cs_idle_delay
Erase cs_idle_delay
suspend_latency
resume_latency
res_sus_latency
reset
power_down
release_power_down
power_up
Macronix
tSHSL (read)
tSHSL (erase)
tESL
(*)
tERS
tREADY2 (4KB Sector Erase)
tDP
tRES1
tVSL
Winbond
tSHSL1
tSHSL2
tSUS
(*)
tSUS
tRST
tDP
tRES1
tVSL
Gigadevice
tSHSL
tSHSL
tSUS
(*)
tRS
tRST_E
tDP
tRES1
tVSL
Adesto
tCSH
tCSH
tSUS
(*)/tRES
tSUS
tRST/tSWRST
tDP/tEDPD
tRES1/tRDPD
tVSL/tVCSL
(*) The
res_sus_latency
is usually not mentioned in the AC characteristics table of the datasheet. Please, read carefully the description of the erase suspend/resume commands for more information.
Set properly the next settings regarding the continuous mode of operation. The continuous mode is also named by some memory manufacturers as performance enhanced mode or burst mode. When this feature is enabled, the opcode is sent only once, during the first read command, and then all subsequent read commands are issued without it. The OQSPI flash memory enters and stays in continuous mode, as long as the value of the extra byte phase maintains a specific pattern, which is memory dependant. In order to get the memory out from continuous mode this pattern must be violated (the value 0xFF is usually used, which works for all supported flash memories).
read_instr_cfg.continuous_mode
read_instr_cfg.extra_byte_cfg
read_instr_cfg.extra_byte_half_cfg
read_instr_cfg.extra_byte_value
exit_continuous_mode_instr_cfg.opcode_bus_mode
exit_continuous_mode_instr_cfg.sequence_len
exit_continuous_mode_instr_cfg.disable_second_half
Implement and set the next callback functions. If the new memory belongs to an already supported group of memories consider using the already implemented callback and helper functions from oqspi_<manufacturer>_<bus_mode>.h. Even if the callbacks of the new flash driver must be diffentiated, parts of the aforementioned functions could be used to eliminate the effort.
callback.initialize_cb: Make sure that all needed register fields are set properly. Refer to the datasheet for more information. Typical configurations set by this function: Bus Mode, Dummy Bytes, Address Size, Slew Rate, Current Strength etc.
callback.sys_clk_cfg_cb: Reconfigure the dummy bytes and any other parameter of the flash memory based on the new system clock frequency.
callback.exit_opi_qpi_cb: Issue the right sequence in order to exit the memory from OPI/QPI mode.
callback.get_dummy_bytes_cb: Return the dummy bytes for any given system clock. This function is used by initialize_cb() and sys_clk_cfg_cb() in order to set the dummy bytes properly.
callback.is_suspended_cb: Implement the right sequence in order to check whether an Erase or Page Program operation is suspended.
callback.is_busy_cb: Implement the right sequence in order to check whether the memory is busy or not.
callback.read_status_reg_cb: Implement the right sequence in order to read the status register.
callback.write_status_reg_cb: Implement the right sequence in order to write the status register.
5.8. QSPI FLASH Support
This section describes the Storage (QSPI) Flash support in SmartSnippets™ DA1470x SDK and provides a guideline on how to add support for new QSPI flash memories. The stotage flash memory is connected to the QSPI controller (QSPIC), which supports QUAD bus flash memories. Below are listed the flash memories supported by the SDK:
Adesto: AT25SL128, 128MBit
Winbond: W25Q256FW, 256Mbit
All these flash memories have been tested with the SDK release under all build configurations options. The default device is the Adesto AT25SL128, which is mounted on the ProDK motherboard. Another memory can be selected by changing the macros shown in Section 5.8.4.
The Section 5.8.7 describes how to add support for other flash memories, which have the same boot sequence with the aforementioned supported devices.
5.8.1. Build configuration options
The SmartSnippets™ DA1470x SDK supports two discrete build configuration options, in terms of whether the QSPI flash memory is pre-configured at compile time or autodetected at runtime.
Note
The pre-configured QSPI flash configuration is the default and recommended option for production builds, since the autodetect option increases both the build size of the binary, as well as the retained RAM memory usage by a few KBytes.
5.8.2. Pre-configured at compile time
When the QSPI flash memory is pre-configured at compile time, only the corresponding flash driver of the selected memory is compiled, thus a binary file with lower build size is generated. For this reason, this option is suitable for production builds.
5.8.3. Autodetected at runtime
When the QSPI flash memory is autodetected at rutime, the Jedec ID of the connected flash memory is read and in turns compared with the IDs of the memories officially supported by the
SmartSnippets™ DA1470x SDK. If the Jedec ID matches, the corresponding flash driver is used in order to configure the QSPI controller properly. If not, the default flash driver,
determined by the macros dg_configFLASH_CONFIG
and dg_configFLASH_HEADER_FILE
, will be used and most likely the QSPI controller will not be configured properly. Since the flash
driver is determined at runtime, the flash drivers of all supported memories need to be included to the binary and this is why its size is increased by a few KBytes. This option is NOT
recommended for production builds and it is mainly used by the flashing tools, e.g. uartboot
and cli_programmer
.
5.8.4. Build configuration macros
The Flash subsystem is configured using the macros shown in Table 68, which must be defined in the
config/custom_config_xxx.h
file of the project:
Table 68 DA1470X QSPI flash memory build configuration macros qspi_flash_config_t field
Description
dg_configFLASH_AUTODETECT
Set this macro, to enable autodetecting the flash memory at runtime (NOT recommended for
production builds
). Its default value is 0, i.e. flash is pre-configured at compile.dg_configFLASH_HEADER_FILE
This macro must be defined as a string that denotes the header file of the specific flash , drivere.g. qspi_at25sl128.h, qspi_w25q256jw.h. This header file must be either one of the qspi_<part_nr>.h header files found in
<SDK_ROOT_PATH>/sdk/bsp/memory/include
, or a header file under the project’s folder, as long as this path is in the compiler’s include search path (see Section 5.8.7 about adding support for new flash devices).dg_configFLASH_CONFIG
This macro defines the qspi_flash_config_t structure of the pre-defined flash driver, e.g. flash_at25sl128_config, flash_w25q256jw_config.
The default values of the build configuration macros are defined in sdk/config/bsp_defaults.h
.
5.8.5. Code Structure
The QSPI flash access functionality is implemented in qspi_automode.{h c}
, whereas all common macros and functions used by all memories are defined in qspi_common.h
.
Memory specific code is defined in the flash driver header files named as qspi_<flash memory>.h
, while code used by all memories of the same manufacturer is located in header files named as qspi_<flash manufacturer>.h
.
The code in qspi_automode.c
calls memory-specific callback functions and makes use of memory-specific values to properly initialize the selected flash.
Each flash driver header file provides an instance of the structure qspi_flash_config_t
, which contains all the memory-specific callback function pointers and variables.
5.8.6. The QSPI flash configuration structure
Each driver header file must provide its own instance of qspi_flash_config_t
. Please note that this instance must be named with a unique name, like flash_<device name>_config
, since all the device header files are included in the qspi_automode.c
file. Therefore, there is a single global namespace. Moreover, it is highly recommended the struct instance to be declared as const, so that the compiler can optimize it properly.
The qspi_flash_config_t
structure, as shown in Table 69, has the following fields (see <SDK_ROOT_PATH>/sdk/memory/include qspi_common.h
for more information):
qspi_flash_config_t field |
Description |
---|---|
initialize |
Pointer to the flash-specific initialization function. |
is_suspended |
Pointer to a flash-specific function that checks if flash is in erase/program suspend state. |
sys_clk_cfg |
Pointer to a flash-specific function that performs Flash configuration when system clock is changed (e.g. change dummy bytes or |
get_dummy_bytes |
Pointer to a flash-specific function that returns the number of dummy bytes currently needed (it may change when the clock changes). |
manufacturer_id |
The Flash JEDEC vendor ID ( |
device_type |
The Flash JEDEC device type ( |
device_density |
The Flash JEDEC device density ( |
erase_opcode |
The Flash erase opcode. |
erase_suspend_opcode |
The Flash erase suspend opcode. |
erase_resume_opcode |
The Flash erase resume opcode. |
page_program_opcode |
The Flash page program opcode. For PSRAM memories this is the write opcode. |
page_qpi_program_opcode |
The Flash QPI page program opcode to use. |
quad_page_program_address |
If true, the address will be transmitted in |
read_erase_progress_opcode |
The opcode to use to check if erase is in progress (Usually the Read Status Reg opcode |
enter_qpi_opcode |
The Flash opcode for entering QPI mode. |
erase_in_progress_bit |
The bit to check when reading the erase progress. |
erase_in_progress_bit_high_level |
The active state ( |
send_once |
If set to 1, the “Performance mode” (or “burst”, or “continuous”; differs per vendor) will be used for read accesses. In this mode, the read opcode is only sent once, and subsequent accesses only transfer the address. |
extra_byte |
The extra byte to transmit, when in “Performance mode” ( |
address_size |
Whether the flash works in 24- or 32-bit addressing mode. |
memory_size |
Maximum capacity, memory size of selected device in Mbits. |
break_seq_size |
Whether the break sequence, that puts the flash out of the continuous mode, is one or two bytes long (the break byte is |
ucode_wakeup |
The QSPIC microcode to use to set up the flash on wakeup. This is automatically used by the QSPI Controller after wakeup, and before CPU starts code execution. This is different, based on whether the flash was active, in deep power down or off while the system was sleeping. |
power_down_delay |
The maximum time required in usec, after the Power Down command, in order for the flash memory to enter into the deep power down mode. |
release_power_down_delay |
The maximum time required in usec, after the Release Power Down command, in order for the flash memory to exit from the deep power down mode. |
power_up_delay |
The maximum time required in usec, to power up the flash memory. |
suspend_delay_us |
The minimum time required in usec, between an erase/program suspend command and the moment when the memory is ready to accept the next consecutive command. |
resume_delay_us |
The minimum time required in usec between an erase/program resume command and the moment when the memory is ready to accept the next consecutive command. |
reset_delay_us |
The minimum time required in usec, between a reset command and the moment when the memory is ready to accept the next consecutive command. |
read_cs_idle_delay_ns |
The minimum time required in nsec, that the CS signal has to stay in ‘Idle’ state between two consecutive read commands. Also referred as “read CS deselect time”. |
erase_cs_idle_delay_ns |
The minimum time required in nsec that the CS signal has to stay in ‘Idle’ state between a write enable, erase, erase suspend or erase resume command and the next command. Also referred as “erase/write CS deselect time”. |
is_ram |
True if device is RAM, false if device is Flash. |
qpi_mode |
True if the device operates in QPI bus mode. Applicable to PSRAM memories only (QSPIC2). |
burst_len |
The length of the wrapping burst that the external memory device is capable to implement. Applicable to PSRAM memories only (QSPIC2). |
cs_active_time_max_us |
The maximum time in usec, that the QSPIC2 CS signal is allowed to stay active. Applicable to PSRAM memories only (QSPIC2). |
5.8.7. Adding support for a new QSPI flash memory
The SmartSnippets™ DA1470x SDK driver subsystem currently supports a specific set of QSPI flash memories. It provides, however, the capability to add support for other flash memories as well.
Each flash memory driver must have its own header file that should be named qspi_<flash memory>.h
.
The programmer can either use the qspi_XXX_template.h
, or start with an existing driver file.
Warning
The new flash driver file should be placed inside the project’s path, in a folder that is in the compiler’s include search path (an obvious choice is the config
folder, but others can be used as well). This is recommended so that potential SDK upgrades will not interfere with the project-specific flash driver implementation.
Note
As already mentioned before, common code used by all memories of the same manufacturer is located in header files named as qspi_<flash manufacturer>.h
, e.g. qspi_macronix.h
, qspi_winbond.h
etc. If functions implemented in these files are used by a custom flash driver, the corresponding header file must be included.
The following steps are usually needed to create a new flash driver:
Copy and rename the template header file, or an existing flash driver file.
Rename all functions and variables of the flash driver appropriately. It is important to remember that all drivers reside in the same namespace, thus all function and variable names must be unique.
Define the proper
JEDEC ID
values for the Manufacturer code, the device type and the device density.Set the opcodes for
fast read
,page program
,sector erase
,erase suspend
,erase resume
andread status register
commands according to the manufacturer’s datasheet.
Rename the
qspi_flash_config_t
struct toflash_<flash memory name>_config
, and initialize all members properly. It is higly recommended to declare this struct asconst
.Implement the function
flash_<flash memory name>_initialize()
, if needed, in order to write some special QSPI configuration registers fields, e.g. enable the QUAD mode, set the dummy bytes, set the current strength of the QSPI IOs etc.Implement the function
flash_<flash memory name>_sys_clock_cfg()
, if needed, in order to re-configure the flash parameters properly, whenever the system (and hence the QSPI) clock changes. This can include modifying the dummy bytes, changing the QSPI clock divider due to max frequency limitation etc.Implement the function
is_suspended()
, if needed, so that it returns true, if a sector erase or page program command is suspended.If the memory supports continuous mode of operation (also referred as performance mode or burst mode), make sure that the
send_once
is set to 1, and theextra_byte
is set properly, in order to keep the flash memory working in this mode. Refer to the manufacturer datasheet, and more specifically to the fast read command section for more details. The value of the byte, which is sent after the address phase (extra byte phase), determines whether the memory expects the next consecutive read command in continuous mode of operation or not, in other words whether the opcode will be send in the next read command or will be omitted.
Set
page_program_opcode
,erase_opcode
,break_seq_size
(this should also take into consideration whether the device will be working in Continuous Read mode as well) andaddress_size
.If the address, during write, will be provided in QUAD mode, set
quad_page_program_address
to true.
Note
The SmartSnippets™ DA1470x SDK supports reading in QUAD I/O mode (where the address and data phases are transferred in QUAD bus mode, whereas the opcode in SINGLE bus mode).
5.8.8. Enabling the SDK tools to support a new QSPI flash memory
In order for the SDK tools to support a new QSPI flash memory the following steps are required:
Add support for the new flash memory, as described in paragraph Section 5.8.7.
Rebuild the
DA1470x-00_Release
of theuartboot
application with the new QSPI flash driver included. Theuartboot
is the secondary bootloader used by SDK flash programming tools.Rebuild the
DA1470X_Release_static_linux
orDA1470X_Release_static_win32
of thecli_programmer
application, depending on whether you work in linux or in windows OS.
Note
The SmartSnippets™ Toolbox supports only the officialy supported QSPI flash memories mentioned in the beginning . Therefore, it is not recommended to be used with new flash memories without following first all the aforementioned steps.
5.10. Sensor Node Controller Reference Manual
5.10.1. Introduction
DA1470x chip family is a preferable solution for wearable designs, since it comprises components that can support operations required by such applications. One primary operation is the interface and communication with peripheral devices. For this purpose, the chip features a dedicated HW block named Sensor Node Controller (SNC) performing such operations on the external peripheral devices that are attached to the DA1470x chip. There are two major advantages when utilizing SNC in a DA1470x application:
Power is saved when data are fetched from sensors using SNC
SNC offloads the System CPU (M33) executing low to medium complexity algorithms for post-processing data purposes.
For instance, SNC can execute algorithms for gesture recognition or sleep quality monitor on data collected by an accelerometer sensor.
SNC block consists of an ARM Cortex M0+ processor and the necessary required HW blocks:
AHB & APB interfaces used to directly access the rest of the system (RAM, Register file, Peripheral Interfaces) and a dedicated Watchdog Timer.
Two RAM cells, RAM1 and RAM2, dedicated for SNC code and data storage, each having a 32KB size, large enough for supporting complex applications.
2-pin JTAG/SWD interface with which has access on RAM13 cell which can be used as ARM Micro Trace Buffer (MTB).
A list of external interrupts including interrupt lines from SYS(M33)/CMAC, HW Timers, GPIO and Serial Interfaces.
It resides in the same power domain with Serial Interfaces and GPADC (PD_SNC), thus the system only needs to power up PD_SNC to use peripheral devices, assisting to SNC autonomous functionality.
It supports communication with peripheral devices, using popular serial communication protocols (I2C, SPI, UART). The SNC block operates with a clock up to 32Mhz frequency, which runs independently from system clock,
being able to perform post-processing operations on the data acquired from the peripherals with the lowest cost on power performance. SNC is running its firmware autonomously,
thus its operations do not require extra processing time in M33 context, saving not only power but also resources management (MIPS) and memory accesses. SNC can communicate with the other two masters (CM33/CMAC)
using corresponding interrupt signals and a shared memory space. A 128KB sized RAM cell RAM8
is used to exchange data with the other CPU cores (masters) of the system. With a shared data access mechanism, masters
can exchange application or configuration data in an asynchronous way, increasing efficiency and reducing system active time.
5.10.2. Restrictions in SNC programming
SNC can be programmed to use Dialog CoRoutines
. Dialog CoRoutines is a single stack OS and this causes the below restrictions.
The stack of a co-routine is not maintained when the co-routine blocks and a new co-routine will be in the running state. This means that most probably the variables that are allocated in stack will lose their values. Variables that have to maintain their values after a blocking call should be declared as static.
An API call that causes a co-routine to block can be made by the co-routine itself and not from within a calling function inside the co-routine. Due to this restriction, asynchronous (non-blocking) function calls should only be utilized when the SNC uses the adapter middleware.
A blocking call cannot be made from within a switch statement.
5.10.3. Usage
The SmartSnippets™ DA1470x SDK supports SNC by providing a framework with all the necessary software modules to develop an SNC application. There are two reference SmartSnippets™ Studio projects,
os_app_retarget
project for M33 and os_snc_retarget
for SNC application development respectively. These projects can be imported in SmartSnippets Environment as shown in the figure below.
python_scripts
project must also be imported, to enable operation launchers, e.g. program/erase flash.

Figure 93 SNC reference projects
These two projects are used to demonstrate the steps required for the SNC to be utilized in a DA1470x application. In this example SNC wakes up periodically to increase the value of a variable on the shared space and store current timestamp. Then notifies M33 which by turn reads the variable and prints its value along with the timestamp. This example can be used as a template, so users can build on and extend existing projects to meet their own needs, gradually adding necessary code for their application design.
5.10.3.1. SNC programming
The SNC firmware runs over a single stack OS, the Dialog CoRoutines
, developed to exploit the SNC’s low power consumption advantages. Among others, Dialog CoRoutines support multiple tasks, timers, mutexes,
message queues etc. SNC Framework supports the middleware layer adapters and low-level drivers for the supported peripherals. ARM Cortex-M0+ specific code is provided including assembly modules
for low level processor operations, such as startup and sleep mechanism, CPU interrupt handling and SNC initialization process. File structure and compile configuration for an SNC project
is similar to M33 project. Macro definitions and preprocessor directives are used to apply SNC-project specific compilation settings, as in M33 projects.
In an SNC project, necessary code should be added for both peripheral initialization, data acquisition and post-processing operations on the acquired data. A peripheral initialization code could consist of
peripheral device drivers and dedicated tasks to perform serial interface operations using adapter APIs and a post processing part with code related to algorithm implementation and filters.
The SNC specific custom configuration file, custom_config_snc.h
, has project specific configuration settings. These settings could be macros to enable adapters and low-level drivers
for the peripherals included in the design and application specific settings like heap and stack size.
SNC programming is very similar to M33, a main function is used to initialize the system, and application specific settings are defined in the custom configuration header file. But there are some key differences that user must be aware of.
Its firmware runs over a different OS and user must take into consideration Dialog CoRoutines programming guidelines to develop an application.
SNC code does not proceed to any system configuration operations. M33 is responsible for this.
SNC is responsible for allocating memory in shared data address space, and for defining the corresponding structures for data storage and synchronization between masters. As soon as initialization is finished, the SNC allocates the application’s necessary shared memory and stores its address and initialized data in a predefined structure shared with M33. This structure format is application specific and must be defined in a way so any data type (e.g. acceleration data or location) can be distinguished easily in both SNC and M33.
In a typical application, the SNC creates a task to initialize the peripheral devices and then waits for any event that would trigger a data acquisition process. An SNC triggering event could be a periodic timer expiration,
or an interrupt signal driven from a full FIFO in sensor side, or an interrupt from another master. The post-processing algorithms have been initialized and are ready to be fed with the data acquired.
Until a triggering event the SNC is in sleep mode, saving power. Upon a triggering event, with the help of the adapters the SNC can read data from the peripherals, and the corresponding tasks are responsible for the next actions.
In case M33 is to be notified because either an event has occurred (e.g. wrist movement), or data have been acquired (e.g. GPS location data), the SNC stores this data in shared memory space allocated during its initialization and then sends
an SNC2SYS interrupt to the M33. In order for the SNC to modify the shared memory it must first acquire access in memory, using a semaphore-like mechanism, and then to proceed with the shared data modification. In this example, the shared data is a two elements array,
with the timestamp and an increasing value variable as elements. After the modification the M33 is notified about the change by calling the corresponding interrupt trigger function. The below code snippet, taken from os_snc_retarget
project,
shows an SNC task accessing shared data and notifying the M33 afterwards.
In order to compile os_snc_retarget
use the build targets DA1470x-00-SNC-Debug
or DA1470x-00-SNC-Release
to produce the SNC image for a debug- or release-build respectively.
After a successful compilation the resulting output header file snc_fw_embed.h
is placed in `DA1470x-00-SNC-Release/snc_files
(or Debug
). This header file contains the SNC firmware with the address of the shared symbols and is
ready to be integrated in M33 project.
5.10.3.2. M33 programming
os_app_retarget
has a linked folder interface
linked to the interface folder of os_snc_retarget
. This folder contains the files snc_shared_space.c
and snc_shared_space.h
, making M33 aware of the share memory structure.
Additionally, the os_app_retarget
project incorporates the file snc_fw_embed.h
containing the SNC firmware in its compiled modules, integrating the SNC firmware in the M33 application image, along with instructions to access the shared memory.

Figure 94 SNC reference projects file structure
After boot, M33 proceeds with the system initialization, configuring power, clock etc. Then it copies the SNC image to the corresponding RAM address and starts SNC firmware execution. It then waits until the SNC has been initialized and configured the shared memory space, before continuing code execution. In the code snippet below M33 registers a callback function to be executed when an interrupt from SNC is received. Then M33 starts SNC, it first disables SNC then copies SNC image to RAM and then resets SNC. After SNC is started, M33 waits until SNC has configured share memory space by checking corresponding status flag. When SNC sets this flag, M33 acquires shared memory address and proceeds to code execution.
/* Register callback for SNC2SYS IRQ. */
snc_register_snc2sys_int(test_snc_cb);
/* Initialize and start SNC. */
snc_freeze();
snc_init();
snc_start();
/* Wait for SNC to finish start-up process. */
while (!snc_is_ready());
/* Wait for SNC to initialize application shared space. */
while (!app_shared_space_ctrl_is_ready());
/* Get the address of application shared data. */
app_shared_data_ptr = snc_get_shared_space_addr(SNC_SHARED_SPACE_APP(APP_SHARED_SPACE_DATA));
To exchange data from now on, each master can notify other by using corresponding interrupts, the SNC2SYS
or SYS2SNC
, as soon as they have stored appropriate data on the shared memory space. M33 registered callback function test_snc_cb
is executed when SNC triggers an SNC2SYS
interrupt.
Depending on the shared data information that is read after interrupt is fired, it can either perform a post-processing operation like calculate distance using coordinates from GPS location data or to perform an action, like turn on LCD display after a hand movement.
In the below code snippet, a simple example of M33 accessing shared memory data is shown. At first, a task is notified by SNC interrupt callback and after acquiring access in shared memory data with app_semph_take()
it reads shared data and prints the values in the output console
and finally releases access on shared memory.
/*
* Callback function for SNC2SYS IRQ.
*/
void test_snc_cb(void) {
OS_TASK_NOTIFY_FROM_ISR(task_h, TASK_SNC_NOTIF, OS_NOTIFY_SET_BITS);
}
/**
* @brief Template task prints a message every time a notification is received from SNC
*/
static OS_TASK_FUNCTION(prvTemplateTask, pvParameters)
{
OS_TASK_BEGIN();
uint32_t notif;
for ( ;; ) {
OS_TASK_NOTIFY_WAIT(0x0, OS_TASK_NOTIFY_ALL_BITS, ¬if, OS_TASK_NOTIFY_FOREVER);
/* Check whether a notification has been received from SNC. */
if (notif & TASK_SNC_NOTIF) {
/* Acquire exclusive access on shared data. */
app_semph_take();
printf("@%lu\tSNC->SYSCPU (%lu)\r\n", app_shared_data_ptr->buffer[0],
app_shared_data_ptr->buffer[1]);
/* Release exclusive access on shared data. */
app_semph_give();
}
}
OS_TASK_END();
}
5.11. The USB Framework
5.11.1. Introduction
This section describes the USB support in the SmartSnippets™ DA1470x SDK and the necessary steps to add USB support to a user application. The USB port can be used as a charging port and/or a USB data interface to a host machine.
5.11.2. SYS_USB
In order to enable the USB port, the user must first add sys_usb
to the application, and set
the relative configuration macro.
#include "sys_usb.h"
.
.
sys_usb_init();
#define dg_configUSE_SYS_USB (1)
The following diagram illustrates the sys_usb
initialization procedure, when the device
is configured to use both the USB charger and the USB stack.
Please note that any one of the two features can be used independent of each other.

Figure 95 DA1470x SYS_USB initialization
5.11.3. The USB Charger
For information on how to use the USB charger of the DA1470x please refer to Section 4.1.3.
5.11.4. USB Data Connection
To enable the device’s communication with a connected host via
the USB port, the macro dg_configUSE_USB_ENUMERATION
must be set.
#define dg_configUSE_USB_ENUMERATION (1)
5.11.4.1. The SDK USB Stack
The SmartSnippets™ DA1470x SDK includes a complete USB stack implementation, based on the emUSB stack, version 3.36.1, provided by SEGGER.
To add the USB stack to a user project, link the library object usb_lib_da1470x to the application. The necessary header files are located under <SDK_ROOT_PATH>/sdk/interfaces/usb/include
.
In order to configure and use the USB stack, please refer to the SEGGER emUSB user manual, or check the example applications provided with the SDK, under
<SDK_ROOT_PATH>/projects/dk_apps/features/usb_cdc_vmsd
and
<SDK_ROOT_PATH/projects/dk_apps/features/usb_cdc
.
5.11.4.2. Using a different USB Stack
A different USB stack than the one provided with the SDK may be used. In that case, please check Section 5.11.4.3 to see which callback functions are necessary to be implemented by the user-provided USB Stack.
5.11.4.3. Callback Functions / Hooks
The application may implement the following hook functions in order to be notified about USB events
Function |
Description |
---|---|
sys_usb_ext_hook_attach() |
This function is called when the device is physically attached to the port. In case the USB Charger is used by the application, then this function is called after the Charger has finished with the port detection procedure. |
sys_usb_ext_hook_begin_enumeration() |
This function is called when the USB Host has signaled the device that it must begin the enumeration procedure. |
sys_usb_ext_hook_detach |
This function is called when the device has been physically disconnected from the USB port |
In case the application is not using the USB stack provided by the SDK, then it needs to implement the following callback functions.
Function |
Description |
---|---|
hw_usb_ep_rx_read_by_driver() |
This function will indicate if the USB driver should read the Rx data or if the Rx data is read by something else. |
hw_usb_ep_get_rx_buffer() |
This function will be called in order to get a buffer to store the data received. |
hw_usb_ep_rx_done() |
This function will be called when a receive operation has been completed. |
hw_usb_ep_tx_done() |
This function will be called when a transmit operation has been completed. |
hw_usb_ep_nak() |
This function will be called when a NAK has been received for an endpoint. |
hw_usb_bus_event() |
This function will be called to indicate bus events. |
hw_usb_bus_frame() |
This function will be called with the current USB frame number. |
5.11.5. USB Events
5.11.5.1. VBUS Attach
This event signals that the device has been physically attached to a USB port.
Upon receiving the interrupt,
sys_usb
sets the device power mode to ‘Active’, starts using the PLL clock and calls the applicationsys_usb_ext_hook_attach()
hook, if it is implemented.
sys_usb
then enables the USB pads and programs the USB interrupts.If the application is configured with charger support, the charger subsystem is notified.
Afterwards, the USB low level driver is initialized and the device is attached to the USB BUS.
Finally, application hook
sys_usb_ext_hook_begin_enumeration()
is called, if it is implemented.
At this point the USB enumeration must begin.
The following diagram illustrates the USB attach procedure.

Figure 97 DA1470x USB VBUS Attach
5.11.5.2. VBUS Detach
This event signals that the device has been physically detached from the USB port.
The USB pads are disabled
sys_usb
restores the power mode that was used before the device was attached to the USB port.If the application is configured with charger support, the charger subsystem is notified.
The device is detached from the USB BUS.
sys_usb
stops using the PLL clock.Finally, application hook
sys_usb_ext_hook_detach()
is called, if it is implemented.
The following diagram illustrates the USB detach procedure.

Figure 99 DA1470x USB VBUS Detach
5.11.5.3. USB Reset / Suspend / Resume
USB Reset
This interrupt is deployed prior to the enumeration procedure in order to ascertain that the device is in the correct state. It may occur at any time in case of device unexpected/improper response or behavior.
sys_usb
switches the device power mode to ‘Active’.
The USB interrupts are programmed accordingly.
The USB stack is notified of this event via callback
hw_usb_bus_event()
, with eventUBE_RESET
.
Please note that the application is not directly notified by sys_usb
, but instead via the USB stack.
USB Suspend
The USB Suspend interrupt indicates that the USB-Host requested the device to enter SUSPEND state. In this state, the USB device must consume no more than 2.5mA on USB bus and should not attempt to perform any data exchange transactions with the USB-Host.
If the application is configured with charger support, the charger subsystem is notified.
The USB stack is notified of this event via callback
hw_usb_bus_event()
, with eventUBE_SUSPEND
.The USB interrupts are programmed accordingly.
sys_usb
stops using the PLL clock.
Please note that the application is not directly notified by sys_usb
, but instead via the USB stack.
USB Resume
The USB Resume interrupt indicates that the USB-Host requested that the device resumed operation fron the SUSPEND state.
The USB stack is notified of this event via callback
hw_usb_bus_event()
, with eventUBE_RESUME
.The USB interrupts are programmed accordingly.
If the application is configured with charger support, the charger subsystem is notified.
Please note that the application is not directly notified by sys_usb
, but instead via the USB stack.
The following diagram illustrates the USB interrupt handling procedure.

Figure 101 DA1470x USB Interrupt Handling
5.11.6. USB Suspend Modes
In response to a suspend signal from the USB host, the application may switch to an ‘Idle’
power state, so as to decrease its power consumption. Whether the latter will happen or
not is controlled by the configuration parameter dg_configUSB_SUSPEND_MODE
.
Currently for the DA1470x two suspend modes are supported:
USB_SUSPEND_MODE_NONE
indicates that the suspend signal will have no effect on the
system’s power state.
USB_SUSPEND_MODE_IDLE
indicates that the suspend signal will cause the system’s power
state to switch to ‘Idle’. (NOTE: This is the suggested mode in order for the USB-Host to
be able to meet the suspended mode power consumption as per the USB specification.)
5.11.7. USB Power Consumption Considerations
In order for a device to be USB-IF certified, among other parameters, it needs to exhibit a low current draw from the USB host, when the host is suspended. The USB framework attempts to keep the power consumption to a minimum when the device is connected to a suspended host. Nonetheless, it is equally essential for the user application as well to go into a power-saving mode when the host is suspended.
5.13. Using SEGGER Open Flashloader
5.13.1. Automatic flashing of the application image via SEGGER J-Link
As part of the development process, it sometimes may be more convenient to have the application image automatically flashed onto the DA1470x whenever the code is modified and needs to be debugged or tested. This can be achieved by setting up SmartSnippets™ Studio to automatically build and/or flash the application binary whenever a debugging session is started.
For more info refer to Open Flashloader wiki page
Note
This feature is experimental and is not fully supported. If the
following instructions do not produce the expected results, please
use cli_programmer
to flash images onto the DA1470x.
Note
This option is only provided for software development purposes and must not be used for production purposes.
Note
SEGGER J-Link software version 6.20 or later is required.
5.13.2. Toolset preparation
Import the project
segger_flash_loader
located atsdk10/sdk/bsp/system/loaders
into workspace.Build the project using both
DA1470x-00_OQFLASH_Release
andDA1470x-00_QFLASH_Release
configurations.Make sure that the corresponding segger_flash_loader.elf files have been built under the path:
OFLASH: sdk10/binaries/da1470x/oflash/segger_flash_loader.elf
QFLASH: sdk10/binaries/da1470x/qflash/segger_flash_loader.elf
Make sure that an appropriate J-Link version is installed and selected in SmartSnippets™ Studio.
Make sure that the valid XML tags and attributes have been added to then
JLinkDevices.xml
so that the SEGGER tools can use thesegger_flash_loader.elf
properly. The XML tags and attributes can be found under:sdk10/config/segger/DA1470x/JLinkDevices.xml
Note
Only steps 1-2 are necessary for the user to setup the SEGGER flash loader. Steps 3-5 are only necessary to validate that everything has been configured properly.
For easier maintenance the SDK integrates the JLinkDevices.xml
using the J-Link
command
-JLinkDevicesXMLPath <path to JLinkDevices.xml>
according to SEGGER’s suggestion
This command allows a SmartSnippets™ launcher to flash a device by making use
of segger_flash_loader.elf
functionality.
The OQFLASH: OQSPI_DA1470x_segger_flasher
launcher can be used, in order
to flash and debug an application built for a non-volatile memory.
5.13.3. Setting up SEGGER J-Link tools
An alternative to flash the non-volatile memories is by using the SEGGER J-Link
Tools. To make that possible you need to follow the next steps, assuming that
the SEGGER J-Link is installed under the path C:\Program Files (x86)\SEGGER\JLink_V620
:
Copy the corresponding segger_flash_loader.elf
files under the following paths:
OQFLASH: C:\Program Files (x86)\SEGGER\JLink_V620\Devices\Dialog\da1470x\oflash\segger_flash_loader.elf
QFLASH: C:\Program Files (x86)\SEGGER\JLink_V620\Devices\Dialog\da1470x\qflash\segger_flash_loader.elf
Update the C:\Program Files (x86)\SEGGER\JLink_V620\JLinkDevices.xml
, by
adding the following between <DataBase>
and </DataBase>
<!-- -->
<!-- DIALOG (DA1470x)-->
<!-- -->
<Device>
<ChipInfo Vendor="Dialog Semiconductor" Name="DA1470x" WorkRAMAddr="0x10010000" WorkRAMSize="0x00040000" Core="JLINK_CORE_CORTEX_M33" />
<FlashBankInfo Name="Code Flash" BaseAddr="0x38000000" MaxSize="0x8000000" Loader="Devices/Dialog/da1470x/oflash/segger_flash_loader.elf" LoaderType="FLASH_ALGO_TYPE_OPEN" />
<FlashBankInfo Name="QSPIC1 (Storage Flash)" BaseAddr="0x48000000" MaxSize="0x8000000" Loader="Devices/Dialog/da1470x/qflash/segger_flash_loader.elf" LoaderType="FLASH_ALGO_TYPE_OPEN" />
</Device>
To verify quickly whether the setup configuration is valid you can flash a
binary to a device using the JFlashLite.exe
:
Run
JFlashLite.exe
Choose Device DA1470x and press OK
Choose the binary file and set the
Prog. addr. (bin file only)
with the physical address of the memory you want to program, and press the buttonProgram Device
Note
for DA1470x, the memory physical addresses are
0x38000000
for OQSPI and 0x48000000
for QSPI.
5.13.4. Project preparation
In order to build an application capable to be flashed with Segger Flash Loader
it is necessary to add the following entry in the custom_config_qspi.h
file of
the project.
#define dg_configUSE_SEGGER_FLASH_LOADER ( 1 )
Keep in mind that the linker script is only applicable for the default memory of each device. If it is necessary to build an application for another memory, the linker script must be modified so that the proper product headers etc are included in the binary file.