This blog is to show how to use the external flash for storing the bonding information instead of using the internal flash. I would base it on the Nordic SDK 16.0 and use the nRF52840 DK as the baseline to show how it works. By using the external flash for bonding storage, it can save at least 2 pages space for the internal flash. I would use the QSPI on this demo in this blog.
nRF52840 DK Board
The nRF52840 DK Board has a 64MB external flash memory. The memory is a multi- I/O memory supporting both regular SPI and Quad SPI.

Peer Manager
The BLE Peer Manager can be used by applications to manage BLE security (encryption, pairing, and bonding). It uses flash storage to persistently store bonding information and GATT data for each peer device that it is bonded with. The Peer Manager handles the different BLE security procedures as required by the Bluetooth Specification, which makes it easy to create compliant applications.
https://infocenter.nordicsemi.com/topic/sdk_nrf5_v16.0.0/lib_peer_manager.html
The Peer Manager consists of the following modules:
Security Manager & Dispatcher
When the application or a connected peer device requests a secured link, the Security Manager & Dispatcher is responsible for handling the required procedure. It interfaces with the SoftDevice in creating the secured connection, stores and retrieves the exchanged keys, and manages the pairing procedure.
The module consists of two parts: the Security Manager and the Security Dispatcher. The Security Manager stores security parameters keep track of the current state and coordinate the pairing procedure. The Security Dispatcher interfaces with the SoftDevice and the flash to do the actual pairing.
LESC support (optional)
You can enable support for LESC pairing by setting “PM_LESC_ENABLED” in the sdk_config.h
file. In this mode, the Peer Manager handles internally all requests for Diffie-Hellman keys from the SoftDevice. When PM_LESC_ENABLED is true, it is necessary to call nrf_ble_lesc_request_handler function in the main context of the application. If there is any pending DH key request, the function will calculate the requested key and provide it to the SoftDevice.
Repeated pairing attempts protection (optional)
You can enable protection against repeated pairing attempts by setting “PM_RA_PROTECTION_ENABLED” in the sdk_config.h
file. In this mode, the Peer Manager uses the timing module to keep track of peer devices that failed at the pairing procedure. Future pairing attempts from these peer devices are rejected for a certain period of time. More detailed description of peer tracking policy can be found in Bluetooth Core Specification v5.0, Vol 3, Part H, Section 2.3.6.
ID Manager
The ID Manager keeps track of connected peers and identifies them based on different kinds of IDs: the static device address, master ID, Identity Resolving Key (IRK), IRK whitelist index, and peer ID. It detects if different IDs refer to the same peer and determines which of the connected peers are bonded. When a bonded device is connected, the application can ask for the connection handle associated with the peer ID (or the other way around).
In addition, the ID Manager creates and maintains whitelists.GATT Cache ManagerThe GATT Cache Manager (with its submodules GATT Server Cache Manager and GATT Client Cache Manager) has three main tasks:
- Store CCCD values: As required by the Bluetooth Specification, the GATT Server Cache Manager persistently stores the CCCD values for all bonded peers across connections.
- Distribute service changed indications: When the application notifies the Peer Manager that its database has changed, the GATT Server Cache Manager sends a service changed indication to all connected peers (and to all bonded peers when they reconnect).
- Store remote ATT databases in flash: If requested by the application, the GATT Client Cache Manager stores the remote database for all peers. This attribute caching is optional, but it reduces the required packet exchange and therefore conserves energy.
Peer Database
The Peer Database holds the stored data for all peer IDs. It provides functions to create unique peer IDs, write and read data for a specific peer ID, free a peer ID, and enumerate all existing peer IDs.
Peer Data Storage
The Peer Data Storage module is an interface between the Peer Database and the Flash Data Storage module. It is responsible for storing the peer data in flash memory. To do so, it uses the Flash Data Storage module and requires exclusive use of certain record keys and file IDs (see Restrictions on keys and IDs for more information). In addition, the Peer Data Storage module assigns peer IDs.
Operation on QSPI flash
Based on the blog (QSPI through nrfjprog), I need to erase all the external flash content.
Erase the entire QSPI flash
cmd> nrfjprog --qspieraseall
Erase the particular bonding page inside QSPI flash
The starting address of the bonding is 0x7FD000.

The starting address of the bonding is stored at 0x7FD000 and its end is at 0x80000. Thus, that address converted to QSPI by offset 0x1200 0000 is 0x127FD000.
nrfjprog --qspicustominit --erasepage 0x127FD000
Read the bonding information
You can read the bonding information from QSPI as the following command.
nrfjprog --qspicustominit --memrd 0x127FD000 --w 32 --n 0x100
How to get the address of bonding
You can also manually configure the starting and ending address of bonding. by changing FLASH_FDS_START_ADDR and FLASH_FDS_END_ADDR.

BLE Bonding on External flash

FSTORAGE_QSPI Module
In order to use the qspi flash instead of internal flash, it needs to modify the fds.c (module) particularly for handling the flash reading, writing and erasing. Because it is much easy and fast to load the data from internal flash bypassing address directly.
Thus, there is a new fds_external.c file from original fds.c (as below).

nrf_fstorage_qspi
By using the QSPI flash on the nRF52840 DK board, the flash initialization code is as

The API of the nrf_fstorage_qspi.c are
static ret_code_t read(nrf_fstorage_t const * p_fs, uint32_t src, void * p_dest, uint32_t len);
static ret_code_t write(nrf_fstorage_t const * p_fs,
uint32_t dest,
void const * p_src,
uint32_t len,
void * p_param);
static ret_code_t erase(nrf_fstorage_t const * p_fs,
uint32_t page_addr,
uint32_t len,
void * p_param);
It would have the new module name nrf_fstorage_qspi.

Inside the SDK_CONFIG.h, it adds the NRF_FSTORAGE_QSPI option.

If you have interested to know more, please send a message and give me details on your requirement.
Hey Jimmy,
your nrf52 articles here are very helpful. Thanks for that!
I’m currently trying to get the peer manager store the bonding data on an external SPI flash but am having hard time adapting fds to read from an external flash instead of accessing the internal storage by pointers.
Do you possibly have a reference implementation of the fds_external.c that you mention in your post?
Thank you,
Beny
LikeLike
Hi Beny,
Just sent the code to you by email.
This is just for the demo purpose.
Thanks
Jimmy
LikeLike
Amazing! Thank you.
Beny
LikeLike
Hi Jimmy
I’m interested at the implementation of your fds.c, I’m porting my BLE application into another processor, so pairing/bonding will be reimplemented, can you send me your demo code, thanks.
LikeLike