nRF5 SDK  v14.2.0
Choose documentation:
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
SoftDevice Handler library
This information applies to the following SoftDevices: S132, S140

This library is used for enabling and disabling the SoftDevice and for propagating SoftDevice events to the application.

This library has the following functionalities.

  • Enabling the SoftDevice and its event interrupt.
  • Disabling the SoftDevice.
  • Receiving stack events from the SoftDevice and forwarding them to the parts of application that have registered themselves as observers.
  • Sending SoftDevice state events to applications and drivers.
  • Sending SoftDevice state requests which can be accepted or rejected by an observer.
  • This module works with SoftDevices that have BLE, ANT, or BLE and ANT functionalities.

The API exposed by this module can be found in <InstallFolder>\components\softdevice\common in the files nrf_sdh.h, nrf_sdh_soc.h, nrf_sdh_ant.h, and nrf_sdh_ble.h.

For API documentation of this module, see SoftDevice Handler.

Library components

The SoftDevice Handler library consists of four main modules.

  • nrf_sdh
  • nrf_sdh_soc
  • nrf_sdh_ant
  • nrf_sdh_ble

nrf_sdh component

This is the core of the SoftDevice Handler library. It handles requests for enabling or disabling the SoftDevice, and notifies about SoftDevice state changes (such as: about to be enabled, enabled, about to be disabled, disabled). This component also fetches stack events and dispatches them to the correct nrf_sdh_* library, where stands for soc, ant, or ble. To view and modify the configuration options of this module, refer to SoftDevice handler configuration.

nrf_sdh_soc component

This part of the library receives SoC events from the SoftDevice Handler library. Applications and other libraries can register with nrf_sdh_soc to receive these events by using the macro NRF_SDH_SOC_OBSERVER. To view and modify the configuration options of this module, refer to SoftDevice SoC event handler configuration.

nrf_sdh_ant component

This part of the library receives ANT events from the SoftDevice Handler library. Applications and other libraries can register with nrf_sdh_ant to receive these events by using the macro NRF_SDH_ANT_OBSERVER. To view and modify the configuration options of this module, refer to SoftDevice ANT event handler configuration.

nrf_sdh_ble component

This part of the library receives BLE events from the SoftDevice Handler library. Applications and other libraries can register with nrf_sdh_ble to receive these events by using the macro NRF_SDH_BLE_OBSERVER. To view and modify the configuration options of this module, refer to SoftDevice BLE event handler configuration.

Functions of the library components

The three nrf_sdh_* components have a similar role of sending a particular kind of stack events to their registered observers.

These three modules register with the core nrf_sdh module using macros defined in the SoftDevice Handler library, in a similar way as their observers register to them. The nrf_sdh core component then sends the specific stack events (SoC, ANT, or BLE) to the respective modules.

Apart from propagating stack events to the submodules, nrf_sdh sends two other types of messages.

  • SoftDevice state events:
    Four types of events are sent.
    • SoftDevice enabled,
    • SoftDevice about to be enabled (Enable prepare),
    • SoftDevice disabled,
    • SoftDevice about to be disabled (Disable prepare). Applications or drivers can listen to these events and perform any necessary operations based on these notifications.
  • SoftDevice state requests.
    Two types of requests are sent.

    • Enable request,
    • Disable request. State requests differ from state events in that they are not a notification about the SoftDevice state change, but a request to change this state. A part of the application that receives this request can either accept or reject it, in which case the state change is aborted. An example use case of this functionality is preventing one part of the application from disabling the SoftDevice, when another part of the application has a pending operation that is being executed (such as a flash write operation). See Delaying a SoftDevice's state change.

    In certain situations, an observer might also want to delay or prevent enabling of the SoftDevice. For example, when a library is accessing the NVMC registers to write to flash, enabling the SoftDevice will result in an assert the next time when the library attempts to access these registers, because they are protected when the SoftDevice is enabled.

Registering with the nrf_sdh_* components

Each part of an application that wants to receive events from the different components of the SoftDevice Handler library must register itself by using the macros specified in the Library components section. The registering happens at compile time, even though the events themselves are sent out to the observers at runtime. A registered part of an application becomes an observer.

Observers and their priorities

An observer is essentially a piece of code that listens for events. It consists of a handler function and its associated parameter - a context. Every handler function has a certain priority.

The priority of a handler is a number that specifies the order in which it receives events with respect to other handlers (of the same type of events - BLE, ANT, or SoC). Priority zero is the highest. It is possible to have several observers registered with the same priority, but then the order in which they receive events depends on the order in which the linker encounters the handlers during the linking process. Therefore, to be certain that a particular observer is the first one to receive an event, you must assign higher priority to it.

Example: Module A registers itself an observer of BLE events with (a handler) priority 1. Then, module B registers itself as an observer of BLE events with a handler of priority 0. When an event is pulled from the stack by the SoftDevice Handler library, it is dispatched to nrf_sdh_ble, and from there, it is first sent to all observers that have a handler with priority zero, and then to all observers with a handler with priority 1. In consequence, module B receives the event before module A.

These priorities may be crucial when the modules depend on each other. In the example above, module A might depend on module B to be up to date when module A is processing an event from the stack. For that to happen, module B must receive the event first.

Priorities can be configured by passing a parameter to the macro.

Assigning priorities

It is not recommended to modify the priority of any library that is included in this SDK. This may lead to unexpected problems.

However, when you create a new custom library and want to register it as an observer, you will have to assign a priority to it. In such case, a best practice is to assign to it a priority level that is lower than that for all other libraries. In this way, the rest of the application will have received the event and updated its state by the time the event reaches your library.

The default priority levels are already used by the existing libraries, so you must allocate an additional priority level and assign it to your library. To increase the number of priority levels, open the sdk_config file and look for the NRF_SDH_*_PRIO_LEVELS configuration parameter (* represents the type of events - SoC, BLE, or ANT). The value of this parameter is the number of available priority levels and the lowest priority is that number minus one. Change the value to a higher number and then assign the newly created priority level to your custom library.

It might also happen that you will need your piece of code to run before that of other libraries. In such case, check what priority is used in that library, by opening its header or .c file. Do not modify the priority of this library, but try to adjust the priority of yours. If this solution does not fit your requirements, you can increase the total number of priority levels.

Example of enabling the SoftDevice and BLE stack

The following code snippet demonstrates how to enable the SoftDevice and the BLE stack.

ret_code_t err_code;
// Attempt to enable the SoftDevice
APP_ERROR_CHECK(err_code);
{
// The status change has been deferred. Handle as appropriate.
}
// Fetch the start address of the application RAM.
uint32_t ram_start = 0;
err_code = nrf_sdh_ble_app_ram_start_get(&ram_start);
APP_ERROR_CHECK(err_code);
// Enable BLE stack.
err_code = nrf_sdh_ble_enable(&ram_start);
APP_ERROR_CHECK(err_code);

Example of registering a BLE observer

The following code snippet demonstrates how to register a piece of code as an observer of BLE events.

#include "nrf_sdh_ble.h"
void ble_evt_handler(ble_evt_t const * p_evt, void * p_context)
{
// process events here
}
NRF_SDH_BLE_OBSERVER(m_ble_observer, OBSERVER_PRIO, ble_evt_handler, NULL);
  • m_ble_observer: This is the name of the observer. It must be unique across the .c file.
  • OBSERVER_PRIO: This is the priority of the observer’s handler.
  • ble_evt_handler: This is the actual function that will receive the events.
  • NULL: This parameter is optional and will be passed as a parameter to the function (together with the event itself, which is always passed).

Example of registering a SoftDevice state observer

The following code snippet demonstrates how to register a piece of code as an observer of SoftDevice state change events.

#include "nrf_sdh.h"
void state_evt_handler(nrf_sdh_state_evt_t state, void * p_context);
{
switch (state)
{
// SoftDevice is about to be enabled.
break;
// SoftDevice is now enabled.
break;
// SoftDevice is about to be disabled.
break;
// SoftDevice is now disabled.
break;
}
}
NRF_SDH_STATE_OBSERVER(m_state_observer, OBSERVER_PRIO) =
{
.handler = state_evt_handler,
.p_context = NULL
};
  • m_state_observer: This is the name of the observer. It must be unique across the .c file.
  • OBSERVER_PRIO: This is the priority of the observer’s handler.
  • state_evt_handler: This is the actual function that will receive the events.
  • NULL: This parameter is optional and will be passed as a parameter to the function (together with the event itself, which is always passed).

Delaying a SoftDevice's state change

Using the SoftDevice state requests, a part of an application can prevent the SoftDevice from being disabled or enabled. For example, if the flash library has scheduled some flash operations, disabling the SoftDevice will make them fail. In such case, the flash library can use a dedicated macro to register itself with nrf_sdh as a request observer. When another part of the application issues the nrf_sdh_disable_request() call, the flash library event handler for the SoftDevice Handler library will respond to this request.

The handler can then return FALSE and abort the state change.

If it does so, it must afterwards call nrf_sdh_continue() when the SoftDevice can be disabled safely, for example after the flash operation has been completed. This call restarts the state change procedure, which involves resending state requests to all observers. If they all return TRUE, then the SoftDevice begins to change its state and the state events are sent.

Example of registering a SoftDevice state request observer

The following code snippet demonstrates how to register a state request observer.

#include "nrf_sdh.h"
// This functions must return either 'true' or 'false'.
// If you return false, be sure to call nrf_sdh_continue()
// when ready for the SoftDevice to change state.
bool request_evt_handler(nrf_sdh_req_evt_t request, void * p_context);
{
switch (request)
{
break;
break;
}
}
NRF_SDH_REQUEST_OBSERVER(m_req_observer, OBSERVER_PRIO) =
{
.handler = request_evt_handler,
.p_context = NULL
};


Message sequence chart for SoftDevice state requests

In the MSC below, a library requests to disable the SoftDevice. A request observer defers the state change to complete pending operations, and then it restarts the procedure. Finally, the SoftDevice changes its state and the library (a state observer) is notified of this.

msc_inline_mscgraph_1


Preprocessor defines

This module uses conditional compilation. It expects certain preprocessor macros to be defined as follows.

  • SOFTDEVICE_PRESENT must be defined when the SoftDevice is present. It might not need to be defined for specific use cases, such as serialization.
  • ANT_LICENSE_KEY must be defined if an ANT license must be provided to the SoftDevice.