An5289 How To Build Wireless Applications With stm32wb Mcus Stmicroelectronics
An5289 How To Build Wireless Applications With stm32wb Mcus Stmicroelectronics
Application note
How to build wireless applications with STM32WB MCUs
Introduction
This document guides designers through the steps required to build specific Bluetooth® Low
Energy or 802.15.4 applications based on STM32WB series microcontrollers. It groups
together the most important information, and lists the aspects to be addressed.
To fully benefit from the information in this document and to develop an application, the user
must be familiar with STM32 microcontrollers, Bluetooth® Low Energy technology, 802.15.4
OpenThread protocol, Zigbee® protocol, and 802.15.4 MAC layer. It must then master
system services, such as low power management and task sequencing.
Contents
1 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3 Software overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.1 Supported stacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2 BLE application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.3 Building a BLE application on top of the HCI layer interface . . . . . . . . . . 16
3.4 Thread application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.5 MAC 802_15_4 application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.6 BLE and Thread application in concurrency . . . . . . . . . . . . . . . . . . . . . . . 17
4.8.2 SRAM2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.9 FreeRTOS low power . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.10 Device information table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.11 ECCD error management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
5 System initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
5.1 General concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
5.2 CPU2 startup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6 PLL management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
6.1 How to switch the system clock between HSE and PLL . . . . . . . . . . . . . 48
6.1.1 Case 1: Before CPU2 is started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
6.1.2 Case 2: CPU2 is started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
10 Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
10.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
10.2 How to start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
10.3 Thread configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
14 Annexes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
14.1 Detailed flow of the device initialization . . . . . . . . . . . . . . . . . . . . . . . . . 133
14.2 Mailbox interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
14.2.1 Interface API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
14.2.2 Detailed interface behavior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
14.3 Mailbox interface - Extended . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
14.3.1 Interface API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
14.3.2 Detailed interface and behavior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
14.4 ACI interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
14.4.1 Detailed interface and behavior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
14.5 Vendor specific HCI commands for controller . . . . . . . . . . . . . . . . . . . . 153
14.6 STM32WB system commands and events . . . . . . . . . . . . . . . . . . . . . . 155
14.6.1 Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
14.6.2 Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
14.7 BLE - Set 2 Mbps link . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
14.8 BLE - Connection update procedure . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
14.9 BLE - Link layer data packet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
14.10 Thread overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
14.10.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
14.10.2 Main characteristics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
14.10.3 Layers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
14.10.4 Mesh topology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
14.10.5 Thread configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
15 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
List of tables
List of figures
Figure 49. FUOTA server flash memory mapping after CPU1 binary transfer. . . . . . . . . . . . . . . . . . 121
Figure 50. FUOTA server flash memory mapping after CPU2 binary transfer. . . . . . . . . . . . . . . . . . 121
Figure 51. Thread FUOTA protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
Figure 52. FUOTA startup procedure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
Figure 53. Update procedure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
Figure 54. MAC 802.15.4 software architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
Figure 55. MAC API dedicated to application core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
Figure 56. Option bytes configuration for MAC 802.15.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
Figure 57. MAC 802.15.4 simple application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
Figure 58. MAC 802.15.4 applications - Directory structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
Figure 59. Coordinator start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
Figure 60. Node start, requesting association, and data send. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
Figure 61. Coordinator receiving association request and data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
Figure 62. MAC 802.15.4 layer abstraction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
Figure 63. Traces on MAC 802.15.4 application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
Figure 64. System initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
Figure 65. System ready event notification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Figure 66. BLE initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
Figure 67. Transport layer initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Figure 68. BLE channel initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
Figure 69. BLE command sent by the mailbox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Figure 70. ACL data sent by the mailbox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Figure 71. System command sent by the mailbox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
Figure 72. BLE and system user event received by the mailbox . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
Figure 73. System transport layer initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
Figure 74. System command sent by the system transport layer . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
Figure 75. System user event reception flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
Figure 76. shci_resume_flow() usage example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
Figure 77. BLE transport layer initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
Figure 78. ACI command flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
Figure 79. BLE user event receive flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
Figure 80. hci_resume_flow() usage example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
Figure 81. 2 Mbps set-up flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
Figure 82. Master initiates the connection update with HCI command . . . . . . . . . . . . . . . . . . . . . . . 160
Figure 83. Slave initiates the connection update with L2CAP command. . . . . . . . . . . . . . . . . . . . . . 160
Figure 84. Data packet breakdown . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
Figure 85. Application GATT data format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
Figure 86. Thread protocol letters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
Figure 87. 6LoWPAN packet fragmentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Figure 88. Thread network topology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
Figure 89. Link with the external world . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
Figure 90. Thread device roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
1 References
3 Software overview
stm32wb5x_BLE_HCI_AdvScan_fw.bin
stm32wb5x_BLE_HCILayer_fw.bin
BLE stm32wb5x_BLE_LLD_fw.bin
stm32wb5x_BLE_Stack_full_fw.bin
stm32wb5x_BLE_Stack_light_fw.bin
stm32wb5x_Thread_FTD_fw.bin
Thread
stm32wb5x_Thread_MTD_fw.bin
stm32wb5x_BLE_Thread_dynamic_fw.bin
BLE and Thread
stm32wb5x_BLE_Thread_static_fw.bin
BLE and MAC 802_15_4 stm32wb5x_BLE_Mac_802_15_4_fw.bin
stm32wb5x_BLE_Zigbee_FFD_dynamic_fw.bin
stm32wb5x_BLE_Zigbee_FFD_static_fw.bin
stm32wb5x_BLE_Zigbee_RFD_dynamic_fw.bin
BLE and Zigbee
stm32wb5x_BLE_Zigbee_RFD_static_fw.bin
stm32wb5x_Zigbee_FFD_fw.bin
stm32wb5x_Zigbee_RFD_fw.bin
stm32wb5x_Mac_802_15_4_fw.bin
MAC 802_15_4
stm32wb5x_Phy_802_15_4_fw.bin
a. Arm is a registered trademark of Arm Limited (or its subsidiaries) in the US and/or elsewhere.
Application
Infrastructure
Profiles Services
CPU1
M0 firmware
L2CAP Routing
CPU2
MS52414V2
Application M0 firmware
Profiles Services
BT MAC
L2CAP
HCL layer
HostCtl interface
BLE radio
CPU1 CPU2
MS52413V1
Application
CPU1
Profiles User code Services
ACI - HCI
SRAM2 IPCC
GAP GATT
SMP ATT
BLE stack
L2CAP
CPU2
HCI
Radio PHY
MS51765V1
of the transport layer responsible for building the system command buffer and to manage
the system asynchronous event.
The BLE_TransparentMode project can be used as an example to build an application on
top of a BLE HCI layer co-processor using the mailbox as described in Section 12.2:
Thread_Coap_DataTransfer.
Flash SRAM1
FLASH SRAM1
_END_ADDR _END_ADDR
Secure
Available in linker file
SFSA
SRAM1_BASE
Free but not available +4
in default linker file Not available in
End of default linker file
User app
SRAM2a SRAM2b
SRAM2A SRAM2B
_END_ADDR _END_ADDR
Secure Secure
SBRSA SNBRSA
The flash, SRAM2a and SRAM2b memories contain a secure section, which cannot be read
nor written by CPU1. The secure start address for each memory can be read from the option
byte, indicated in blue in Figure 4:
• SFSA for the flash memory
• SBRSA for the SRAM2a (retained in Standby)
• SNBRSA for the SRAM2b.
These option bytes are only written by the FUS running on CPU2. This is done on each
CPU2 update installed by the FUS.
The user application must take into account that the available memory can vary between
different versions of the RF stack. The available space for the user application can be
obtained from the release notes for STM32WB coprocessor wireless binaries. The install
address for the RF stack is also the boundary address for the user flash memory area.
Ensure that some margin is included in CPU2 domain to support updates during the product
lifetime.
The boundary granularity is 4 Kbytes for the flash memory and 1 Kbyte for SRAM2a and
SRAM2b.
The linker file is identical to all delivered BLE/Thread applications (except for
BLE_Thread_Static, BLE_HeartRate_ota and BLE_p2pServer_ota). The available
memories are chosen to fit all provided applications. For applications like BLE, where CPU2
memory requirements are smaller, it is possible to update the linker file to allocate more
memory to the application.
To optimize the available memory for a dedicated application, the linker file must be updated
inline with the following guidelines:
• Flash memory: the end of available memory address can be moved up to the SFSA
address. When a CPU2 update is required, there must be enough free memory just
below the secure memory to upload a new encrypted CPU2 FW update. The size of the
memory required depends on CPU2 FW to be updated (BLE, Thread or concurrent
BLE/Thread), see [1].
• SRAM1: the first unavailable 32 bits in the linker file are only required for the BLE_OTA
application. For all other applications, the start address can be moved from
SRAM1_BASE + 4 to SRAM1_BASE.
• SRAM2a: the end of available memory address can be moved up to the SBRSA
address. When CPU2 update support required, there must be some free sectors just
below the secure memory to support new CPU2 FW updates requiring more sectors to
be secure.
• SRAM2b: The SRAM2b is not part of the linker file because it is all secure for any FW
CPU2 supporting the Thread protocol. For BLE only applications, the linker file can be
updated with a new section to map RW data into the SRAM2B from SRAM2B_BASE
up to the SNBRSA address. When CPU2 update support required, there must be some
free sectors just below the secure memory to support new CPU2 FW updates requiring
more sectors to be secure.
STOP2 is the deepest low power mode supported when RF is active. When the user
application must enter Standby mode, it must first stop all RF activities, and fully reinitialize
CPU2 when coming out of Standby mode. The user application can use the full non secure
SRAM2a to store its own content (that needs to be retained in Standby mode).
Table 2. Semaphores
Semaphore Purpose
If the application needs to use semaphores for inter task control, it is recommended to start
using Sem31 downwards to be compatible with future wireless firmware updates on CPU1,
where new features requiring additional semaphores can be added.
Sem0 is used to share the RNG between the two CPUs. The semaphore is taken by the
CPU2 for an interval depending upon the number to be generated and upon the RNG
source clock speed. To relax the latency to get these numbers, it is recommended to
generate at startup a pool of numbers and fill the pool in a low priority task when some of
them are retrieved by the application to keep it full. The usage of Sem0 is shown in Figure 8.
Sem 0 can be used in the USB use case too. When the USB is not used anymore and
needs to be switched off by the application, Sem 0 must be taken before switching off the
CLK48 clock. This is required because USB and RNG share the same clock, and CPU2
could use RNG at the same time when CPU1 needs to switch off the USB (see Figure 9).
Sem1 is used to share the PKA IP between the two CPUs.
Sem2 is used to share between the two CPUs the capability to write/erase data in FLASH.
Sem2 must be taken before starting more than a single write/erase procedure, and released
when they are completed. The semaphore must be taken/released to surround a couple of
write/erase procedure. The semaphore is taken by the CPU2 for an interval depending upon
the number of data to be written in the flash memory and upon the number of sectors to
erase. BLE stack writes to flash memory the pairing information (when bonding is enabled)
and the GATT attribute cache.
Sem3 is used for the low power management. It must not be locked for more than 500 µs by
the CPU1 when there is BLE RF activity. The algorithm is detailed in Figure 6 and Figure 7.
Sem4 is used to handle race condition on the switch of the system clock when a CPU exits
low power mode while the other one enter low power mode. The algorithm is detailed in
Figure 6 and Figure 7.
Sem3 and Sem4 are used in the examples to enter/exit Stop mode.
The user must ensure that the algorithms shown in Figure 6 and Figure 7 are executed
before and after wake-up from Stop mode. These routines (see Figure 5) are usually
implemented inside the IDLE task of sequencer or RTOS. The implementation takes
advantage of the fact that when WFI is called from critical section, the MCU wakes up upon
interrupt request, but instead of executing ISR it continues to execute the next instruction
after WFI. Only after exiting the critical section the ISR is executed.
PWR_Enter PWR_Exit
Thread zzz ISR Thread
StopMode() StopMode()
IRQx
MS53131V1
Enter
ENTER_CRITICAL_SECTION
If Sem4 is busy CPU2 has not switched
the clock to HSI.
Poll Sem3 until granted It is either in Stop mode, or polling Sem3
to exit Stop mode (in this case, the clock
is switched to HSI and switched back to
Get Sem4
another clock by the CPU2)
No
Sem4 granted ?
Set HSION
Wait for HSIRDY
Set SW to HSI
Wait for SWS to report HSI
Set SMPSSEL to HSI
Release Sem3
Enter CStop
EXIT_CRITICAL_SECTION
Exit
MS53122V1
Enter
Release Sem4
ENTER_CRITICAL_SECTION
Yes
Clock configuration
Release Sem3
EXIT_CRITICAL_SECTION
Exit
MS53123V1
Sem5 is used to control the RNG/USB CLK48 source clock. The CPU2 updates or switches
off the clock only when the RNG IP (Sem0) is used.
To avoid a race condition with the CPU2, when the CPU1 needs to switch off the clock it
must always first get Sem0, even if not using the RNG IP. This mechanism is shown in the
BLE P-NUCLEO-WB55.USB dongle examples (see Section 8.1.3 and Figure 9). This does
not impact the CPU2.
Sem5 must be taken before changing RNG, ADC, CLK48, SAI1, LPTIM1, LPTIM2, I2C1,
I2C3, LPUART1, and USART1 source clock on CPU1 (RCC_CCIPR register), to avoid a
race condition with the CPU2 when it configures CLK48 source clock.
RNG entry
RNG process
Release Sem0
Exit
MS53124V1
Note: Sem5 is not taken because the CPU2 does not take it without taking first Sem0. This
algorithm can be updated to take Sem5 before configuring the RNG clock source.
USB entry
Switch on USB IP
USB process
Get Sem0
Release Sem0
Release Sem5
Exit
MS53125V1
The USB and RNG IPs share the same source clock. Before switching off the clock, the
USB driver must first check whether the CPU2 requires the clock or not. To avoid a race
condition with the CPU2, the CPU1 must first get Sem0 (RNG semaphore, CPU2 does not
use USB) before switching off the clock.
If Sem0 is busy, the CPU1 must wait for Sem0 to be free to switch off the clock. This is
required because there can be a race condition when CPU1 releases the USB and CPU2
releases the RNG at the same time, leading to the oscillator to be kept on.
Sem6 is used to protect the CPU1 timing versus write/erase operations requested by the
CPU2. The CPU1 shall get Sem6 to prevent the CPU2 or other CPU1 processes to either
write or erase data in flash memory. There is no time limit on how long the CPU1 can keep
the semaphore, but, as long as the semaphore is taken, the CPU2 is unable to write either
the pairing or client descriptor information in the memory.
CPU1 must release Sem6 only if it can afford being stalled for the time required to finish the
write or erase operation.
The CPU2 implements the algorithm described in Figure 10, similarly to the CPU1. Before
writing or erasing data in flash memory, it tries to get Sem6 and, if successful, writes/erases
data and releases the semaphore. When the CPU1 needs to protect its timing, it polls Sem6
until it gets it.
Sem7 is used to protect the CPU2 timing versus write/erase flash memory operation
requested by CPU1. The CPU1 must get Sem7 before writing or erasing. Sem7 must be
taken and released for each single write or erase operation, but for not more than 0.5 ms in
addition to the write/erase timing. To comply with this requirement the code must be
executed in the critical section. The algorithm is described in Figure 10.
Sem8 is used to ensure that CPU2 does not update the BLE persistent data in SRAM2
while CPU1 reads them.
The CPU2 can be configured to store the BLE persistent data either in the internal NVM
storage on CPU2 or in the SRAM2 buffer provided by the user application. This can be
configured with the system command SHCI_C2_Config() when the CPU2 is requested to
store persistent data in SRAM2, so it can write data in this buffer when needed. To read
consistent data with the CPU1 from the SRAM2 buffer, the flow must be:
1. CPU1 takes Sem8
2. CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these
data into an NVM managed by CPU1)
3. CPU1 releases Sem8
There is no timing constraint on how long this semaphore can be kept.
Sem9 is used to ensure that CPU2 does not update the Thread persistent data in SRAM2
while CPU1 reads them.
The CPU2 can be configured to store the Thread persistent data either in the internal NVM
storage on CPU2 or in the SRAM2 buffer provided by the user application. This can be
configured with the system command SHCI_C2_Config() when the CPU2 is requested to
store persistent data in SRAM2, so it can write data in this buffer when needed. To read
consistent data with the CPU1 from the SRAM2 buffer, the flow must be:
1. CPU1 takes Sem9
2. CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these
data into an NVM managed by CPU1)
3. CPU1 releases Sem9
There is no timing constraint on how long this semaphore can be kept.
4.4 Sequencer
The sequencer executes the registered functions one by one. It has the following features:
• supports up to 32 functions
• requests functions to be executed
• enables / disables the execution of a function
• provides a blocking interface based on the reception of an event.
The sequencer provides a simple background scheduling function. It provides a hook to
implement a secure way Low-power mode (no event loss) when the sequencer does not
have any pending tasks to be executed. It also provides an efficient mechanism for the
application to wait for a specific event before moving forward. When the sequencer is
waiting for a specific event, it provides a hook where the application can either enter
low-power mode, or execute some other code.
4.4.1 Implementation
To use the sequencer, the application must:
• set the number of maximum of supported functions (this is done by defining a value for
UTIL_SEQ_CONF_TASK_NBR)
• register a function to be supported by the sequencer with UTIL_SEQ_RegTask()
• start the sequencer by calling UTIL_SEQ_Run() to run a background while loop
• call UTIL_SEQ_SetTask() when a function needs to be executed.
4.4.2 Interface
void UTIL_SEQ_Idle( void ); Called (in critical section - PRIMASK) when there is nothing to execute.
void UTIL_SEQ_Run( Requests the sequencer to execute functions that are pending and
UTIL_SEQ_bm_t mask_bm ) enabled in the mask mask_bm.
void
UTIL_SEQ_RegTask(UTIL_SEQ_bm Registers a function (task) associated with a signal (task_id_bm) in the
_t task_id_bm, uint32_t sequencer. The task_id_bm must have a single bit set.
flags, void (*task)( void ))
Requests the function associated with the task_id_bm to be executed.
void UTIL_SEQ_SetTask( The task_prio is evaluated by the sequencer only when a function has
UTIL_SEQ_bm_t task_id_bm, finished. If several functions are pending at any one time, the one with the
highest priority (0) is executed.
void UTIL_SEQ_PauseTask( Disables the sequencer to execute the function associated with
UTIL_SEQ_bm_t task_id_bm ) task_id_bm.
void UTIL_SEQ_ResumeTask( Enables the sequencer to execute the function associated with
UTIL_SEQ_bm_t task_id_bm ) task_id_bm.
void UTIL_SEQ_WaitEvt( Requests the sequencer to wait for a specific event evt_id_bm and does
UTIL_SEQ_bm_t evt_id_bm ) not return until the event is set with UTIL_SEQ_SetEvt().
void UTIL_SEQ_SetEvt( Notifies the sequencer that the event evt_id_bm occurred (the event must
UTIL_SEQ_bm_t evt_id_bm ) have been first requested).
void
UTIL_SEQ_EvtIdle(UTIL_SEQ_bm
Called while the sequencer is waiting for a specific event.
_t task_id_bm, UTIL_SEQ_bm_t
evt_waited_bm)
void UTIL_SEQ_ClrEvt(
Clears the pending event.
UTIL_SEQ_bm_t evt_id_bm )
UTIL_SEQ_bm_t
Returns the evt_id_bm of the pending event.
UTIL_SEQ_IsEvtPend( void )
if(task_id1)
{
task_id1 = 0;
Fct1();
}
if (task_id2)
{
task_id2= 0;
Fct2();
}
__disable_irq();
If (! (task_id1|| task_id2))
{
UTIL_SEQ_Idle();
}
__enable_irq();
}
number of API calls before the function is executed, the sequencer runs the associated
function only once.
4.5.1 Implementation
To use the timer server, the application must:
• Configure the RTC IP. When the calendar is required in the application, the RTC
configuration must be compatible with the calendar settings requirement. When the
calendar is not used, the RTC can be optimized for a Timer Server usage only.
• Initialize the timer server with HW_TS_Init().
• Implement HW_TS_RTC_Int_AppNot() (optional). When not implemented, the timer
callback is called in the RTC interrupt handler context.
• Create a virtual timer with HW_TS_Create().
• Use the virtual timer with HW_TS_Stop(), HW_TS_Start().
• Delete the virtual when not needed using HW_TS_Delete().
4.5.2 Interface
void HW_TS_Init(
HW_TS_InitMode_t TimerInitMode, Initializes the timer server.
RTC_HandleTypeDef *hrtc);
HW_TS_ReturnStatus_t HW_TS_Create(
uint32_t TimerProcessID,
uint8_t *pTimerId, Creates a virtual timer.
HW_TS_Mode_t TimerMode,
HW_TS_pTimerCb_t pTimerCallBack)
void HW_TS_Stop(uint8_t TimerID) Stops a virtual timer.
void HW_TS_Start(
uint8_t TimerID, Starts a virtual timer.
uint32_t timeout_ticks)
void HW_TS_Delete(uint8_t TimerID Deletes a virtual timer.
Timer server handler to be called from the RTC interrupt
void HW_TS_RTC_Wakeup_Handler(void);
handler.
uint16_t Returns the number of ticks to count before the next
HW_TS_RTC_ReadLeftTicksToCount(void); interrupt.
void HW_TS_RTC_Int_AppNot(
uint32_t TimerProcessID,
Reports to the application that a virtual timer has expired.
uint8_t TimerID,
HW_TS_pTimerCb_t pTimerCallBack)
Reports to the application that the number of ticks before
void HW_TS_RTC_CountUpdated_AppNot(void)
the next interrupt has been updated by the timer server.
pTimerId
This is the id returned by the timer server to the caller that needs to be used to
Stop/Start/Delete the created timer.
TimerMode
The timer mode can be either in single shot or repeated mode. When in single shot mode,
the timer is stopped when the timeout is reached. Inrepeated mode, it is restarted with the
same previously programmed value at each timeout. This mode is fixed when the timer is
created. To change the mode, the timer must be deleted and a new one must be created.
Note that in this case the new allocated pTimerId can be different.
pTimerCallBack
User callback on timeout.
TimerProcessID
This is defined by the user and is expected to be used in HW_TS_RTC_Int_AppNot(). When
the timer is created, only the caller knows the Id that has been allocated. The
TimerProcessID is returned in the HW_TS_RTC_Int_AppNot() with the pTimerCallBack so
that relevant decision can be done when implementing HW_TS_RTC_Int_AppNot().
void HW_TS_RTC_Wakeup_Handler(void)
This interrupt handler must be called by the application in the RTC interrupt handler. This
handler clears all required status flag in the RTC and EXTI peripherals.
uint16_t HW_TS_RTC_ReadLeftTicksToCount(void)
This API returns the number of ticks left to be counted before an interrupt is generated by
the timer server. It can be used when the system needs to enter Low-power mode and
decide which Low-power mode to apply, depending on when the next wake-up is expected.
When the timer is disabled (no timer in the list), it returns 0xFFFF.
void HW_TS_RTC_CountUpdated_AppNot(void):
This API must be implemented by the user application.
This API notifies the application that the counter has been updated. This is expected to be
used along with the HW_TS_RTC_ReadLeftTicksToCount () API. The counter can have
been updated since the last call of HW_TS_RTC_ReadLeftTicksToCount () and before
entering Low-power mode. This notification provides the application a way to solve the race
condition to reevaluate the counter value before entering Low-power mode
device to enter in low-power mode. Otherwise, it is impossible to go lower than Stop 0 mode
until CPU2 starts, because of the reset value of the CPU2 low-power mode selection.
4.6.1 Implementation
The low power manager can handle up to 32 users with different low-power mode requests.
To use the low power manager, the application must:
• create a user Id
• call either UTIL_LPM_SetOffMode() or UTIL_LPM_SetStopMode() at any time with the
defined user Id to set the requested Low-power mode
• call void UTIL_LPM_EnterLowPower() in background.
4.6.2 Interface
Enter
HAL_FLASH_Unlock()
SHCI_C2_FLASH_EraseActivity
(ERASE_ACTIVITY_ON) Only to erase a sector
Yes
PESD bit set?
No
ENTER_CRITICAL_SECTION
EXIT_CRITICAL_SECTION
Yes
CFG_BSY bit set?
No
Yes
Another Write/Erase?
No
SHCI_C2_FLASH_EraseActivity
(ERASE_ACTIVITY_OFF)
HAL_FLASH_Lock()
Release Sem2
Exit
MS53552V2
By default, CPU2 uses the PESD bit mechanism (from FLASH_SR register) to protect its
BLE timing and not Sem7. The algorithm is still valid although checking Sem7 is useless.
The drawback is that if the PESD bit is set by CPU2 at the same time when CPU1 starts a
write or erase operation, CPU1 can fetch code but cannot read literals from the memory,
even if the code to be executed requires this action. It is difficult to control whether CPU1
will be stalled or not when the PESD mechanism is used. Additionally, there is no interrupt
signal on PESD bit release by CPU2, so asynchronous software flow is not possible.
Figure 11. CPU1 and flash memory operation versus PESD bit
PESD
Flash erase/write
requested by CPU1 Flash suspended Flash erase/write
CPU1 execution
Can fetch code but
Run Stalled Run
cannot write Flash nor read literals
MS53539V1
The CPU2 use of PESD or Sem7 mechanism to protect the BLE timing is configurable by
CPU1 with the system command SHCI_C2_SetFlashActivityControl(). Although it can be
sent at any time, it is recommended to send it during the initialization phase.
By default, CPU2 protects its timing versus write operations requested by CPU1. When
CPU1 starts an erase operation, it must first send the system command
SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON). When CPU1 sends the system
command SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON), CPU2 immediately
takes Sem7 until the next radio event, preventing any flash erase or write operations during
this period. When it does not expect to request erase operations anymore, it must send the
system command SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF). These
commands do not need to be sent for each single erase operation. It is recommended to
enable the protection before requesting the first erase operation, and to send the disable
protection after the last erase operation has been performed.
Write is always possible, but when the NVM is full on CPU2, a write request may need an
erase. In this case, data are not written until the erase is executed.
When the CPU2 needs to protect its timing versus erase (either because it has been notified
by CPU1 with the command SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON) or
because internally it needs to erase some sectors), any memory operation is forbidden
25 ms before the radio activity until the end of it. To execute the erase operation when BLE
is running, the application must ensure that there is a radio idle time longer than 25 ms.
• BLE advertising: the advertising interval must be longer than 25 ms + advertising
packet length to be able to execute flash memory erase operation.
• BLE connected: the connection interval must be longer than 25 ms + packet length, to
be able to execute flash memory erase operation.
• Data throughput use case: When data streaming packet is sent, the radio is kept active
to send as much data as possible between two connection intervals. Therefore, the
radio can not be in idle long enough to fit an erase operation. When the device is
master, it can reduce the Connection Event Length parameter (with either the
aci_gap_create_connection() or aci_gap_start_connection_update() command) to
prevent the device filling completely the interval between two connection intervals.
When the device is slave, it must request the master to increase the connection interval
so that the data to be sent fit only part of the interval between two connections events.
CPU2 needs to write data in flash memory when:
• after the pairing phase, only if bonding is enabled (first pairing or if the pairing is
requested with the force_rebond parameter), to store the security information
• after a disconnection to store the GATT database, if the device has been previously
bonded
• when aci_gatt_store_db command is called to store the GATT database for all active
connections, if the device has been previously bonded
• when aci_gap_clear_security_db command is called to clear the bonding table (write to
invalidate one or several records, security and GATT information)
• when aci_gap_remove_bonded_device command is called to remove a specified
device from bonding table (write to invalidate the record, security and GATT information
related to the specified device)
4.8.1 GPIO
It is possible to output on GPIOs most of the real time activity of CPU2 such as background
tasks, interrupt handlers and BLE IP Core signals. Assignment of a signal to particular GPIO
is fully configurable from the CPU1 side except for the BLE IP Core signals as they are
driven by HW. Therefore, the BLE IP Core GPIOs must be enabled only if not used by the
application.The full configuration is made in the file app_debug.c located in \Core\Src for
each application.
HW signals
The aRfConfigList[] table holds the list of GPIO driven by the hardware according to the
radio activity. There are four parameters for each signal to monitor:
{ GPIOA, LL_GPIO_PIN_9, 0, 0}, /* DTB13 - Tx/Rx Start */
The first two parameters define the GPIO used (in this example, PA9 is used to output
DTB13). These two parameters cannot be modified. To monitor the signal, the associated
GPIO must be available on the board.
The third parameter is used to enable (1) or disable (0) the signal. All signals are set to 0 by
default.
The fourth parameter is unused, keep it at 0.
To monitor a signal on the associated GPIO, the third parameter must be set to 1, and the
BLE_DTB_CFG compiler switch at the top of the file must be set to 7.
The most useful signal is DTB13, which shapes all radio activity.
SW signals
The aGpioConfigList [] table holds the list of GPIO that are driven by the software. There are
four parameters for each signal to monitor:
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_ISR - Set on Entry / Reset on Exit */
The first two parameters define the GPIO used to output the signal. These are fully
configurable. The user can select any GPIO unused in the application.
The third parameter is used to enable (1) or disable (0) the signal. All signals are set to 0 by
default.
The fourth parameters is unused and must be kept to 0.
To monitor one signal on the associated GPIO, the third parameter must be set to 1.
4.8.2 SRAM2
Hardfault
When the CPU2 enters the hardfault interrupt handler, it can output different information
before running an infinite loop.
It can set a GPIO if enabled in app_debug.c - aGpioConfigList [].
It writes in SRAM2A the following data:
@SRAM2A_BASE: 0x1170FD0F Keyword that identifies a hardfault issue
@SRAM2A_BASE + 4 Program counter value that generated the hardfault
@SRAM2A_BASE + 8 Link register value when the instruction that generated
the hardfault has been executed
@SRAM2A_BASE + 12 Stack pointer value when the instruction that generated
the hardfault has been executed
Security attack
When the buffers provided to the CPU2 to exchange data through the mailbox are not in the
unsecure SRAM, the CPU2 enters an infinite loop and writes the keyword 0x3DE96F61 at
address SRAM2A_BASE.
BLE
The number of functions to be called in the background depends upon the application,
which also determines if each function is called from a dedicated or a single common task.
The BLE architecture supports any combination.
Whatever the BLE application, there must be at least two functions to be called in a task:
• hci_user_evt_proc(): when hci_notify_asynch_evt() is called from the middleware,
this function must be called in the background. hci_user_evt_proc() must not be called
inside hci_notify_asynch_evt() as it can be called from the IPCC interrupt context.
There is no timing constraint between the time hci_notify_asynch_evt() is called from
the middleware and the time when hci_user_evt_proc() is called in the background.
However, in some data throughput use cases, the performance is better when the time
is short enough to read the events at the same rate they are notified. When several
hci_notify_asynch_evt() are received, the hci_user_evt_proc() function needs to be
called only once from the background. It does not hurt to call several times
hci_user_evt_proc() from the background whereas there was only one or no
notification with hci_notify_asynch_evt().
• shci_user_evt_proc(): the requirement is the same as for hci_user_evt_proc() with
the associated notification shci_notify_asynch_evt(). Note that there is no currently
data throughput on this system channel.
As long as it is not possible to send a BLE command while there is already one pending, or
a system command while there is already one pending, the middleware provides hook so
that the application can implement a semaphore mechanism.
When hci_cmd_resp_wait() is called from the middleware, a semaphore must be taken and
released on reception of hci_cmd_resp_release(). The application must not return from
hci_cmd_resp_wait() until the semaphore is released.
Another semaphore must be used to handle the same mechanism on the system channel
with shci_cmd_resp_wait()/shci_cmd_resp_release().
The DIT has a different mapping when filled by the FUS (see [6]) or by the wireless
firmware.
The system command SHCI_GetWirelessFwInfo() can decode the two DIT mappings.
The DIT address can be found at the start of SRAM2 (+ IPCCDBA offset - user option byte).
Unless modified by the user, IPCCDBA is always set to 0, hence the DIT address can be
found at the first address of SRAM2A.
10
11
9
8
7
6
5
4
3
2
1
0
SRAM2b SRAM2a Flash memory
Memory Reserved
(no. of 1 KB sectors) (no. of 1 KB sectors) (no. of 4 KB sectors)
size
31
30
29
28
27
26
25
24
23
22
21
20
19
18
17
16
15
14
13
12
10
11
9
8
7
6
5
4
3
2
1
0
The Build information is always different from 0 for all official versions.
The Branch information is for internal use.
Only the InfoStack LSB is used, it provides the information on which wireless firmware is
running on CPU2, namely:
• INFO_STACK_TYPE_BLE_STANDARD: 0x01
• INFO_STACK_TYPE_BLE_HCI: 0x02
• INFO_STACK_TYPE_BLE_LIGHT: 0x03
• INFO_STACK_TYPE_BLE_BEACON: 0x04
• INFO_STACK_TYPE_THREAD_FTD: 0x10
• INFO_STACK_TYPE_THREAD_MTD: 0x11
• INFO_STACK_TYPE_ZIGBEE_FFD: 0x30
• INFO_STACK_TYPE_ZIGBEE_RFD: 0x31
• INFO_STACK_TYPE_MAC: 0x40
• INFO_STACK_TYPE_BLE_THREAD_FTD_STATIC: 0x50
• INFO_STACK_TYPE_BLE_THREAD_FTD_DYAMIC: 0x51
• INFO_STACK_TYPE_802154_LLD_TESTS: 0x60
• INFO_STACK_TYPE_802154_PHY_VALID: 0x61
• INFO_STACK_TYPE_BLE_PHY_VALID: 0x62
• INFO_STACK_TYPE_BLE_LLD_TESTS: 0x63
• INFO_STACK_TYPE_BLE_RLV: 0x64
• INFO_STACK_TYPE_802154_RLV: 0x65
• INFO_STACK_TYPE_BLE_ZIGBEE_FFD_STATIC: 0x70
• INFO_STACK_TYPE_BLE_ZIGBEE_RFD_STATIC: 0x71
• INFO_STACK_TYPE_BLE_ZIGBEE_FFD_DYNAMIC: 0x78
• INFO_STACK_TYPE_BLE_ZIGBEE_RFD_DYNAMIC: 0x79
• INFO_STACK_TYPE_RLV: 0x80
When the ECCD is generated from the code section, the CPU2 must restart on the FUS to
request a new wireless firmware install.
On ECCD error the NMI is generated to both CPUs. The algorithm shown in Figure 13
(WB_NMI_NVM_RECOVERY_KEYWORD = 0xAFB449C9, WB_NMI_RESET_KEYWORD
= 0x8518C6F2 and WB_NMI_FUS_JUMP_KEYWORD = 0x7E3FF448) describes the way
the CPU2 manages the ECCD error and the mechanism to allow the CPU1 to hold CPU2
error processing.
Enter
Loop here and wait for No Cpuid from cpuid = 0 for CPU1
reset from CPU1 FLASH_ECCR = 1 cpuid = 1 for CPU2
Yes
No SRAM2A_BASE = No SRAM2A_BASE =
WB_NMI_FUS_JUMP WB_NMI_NVM_RECOVERY_
_KEYWORD? KEYWORD?
Yes Yes
Write WB_NMI_NVM_RECOVERY
_KEYWORD at SRAM2A_BASE
Wait 500 μs
No SRAM2A_BASE =
WB_NMI_NVM_RESET
_KEYWORD?
5 System initialization
Main.c
• Initializes the system (clocks, power mode, RTC, timer server, …)
• Implements the background while loop()
App_entry.c
• Initializes the BSP (LEDs, buttons, …) and the debug capabilities
• Initializes the System channel of the Mailbox
• The user can add here its own app_xxx init
• Returns to the background while loop()
• On receive of the System ready event from the CPU2, calls APP_BLE_Init() to
initialize the BLE stack and/or APP_THREAD_Init() to initialize the Open
Thread stack
already used by the CPU1. The RNG peripheral access requires getting Sem0. In
addition, the CPU2 makes one attempt to get Sem5. If this is successful, it switches ON
the HSI48 oscillator and configures the 48 MHz clock selection of the RNG IP to be
HSI48. This assumes the RCC is configured to feed the RNG IP with a 48 MHz clock
and not either LSI or LSE. In the latter case, the previous step is done anyway even
though not relevant and the RNG operates on the selected clock. When Sem5 is busy,
the CPU2 does not change anything in the RNG clock configuration and uses the
current configuration. The HSI48 oscillator is switched OFF by the CPU2 when a
wireless stack is started. This requires Sem5 to be available and only one attempt to
take Sem5 is made.
• The NVM consistency is checked, and, if corrupted, it is reformatted. This operation
requires to erase flash memory sectors. The access to the NVM at startup is compliant
with the general rules to start any process on the memory. It first requires getting Sem2
to take the ownership of the flash memory, the CPU1 has the capability to hold any
operation using Sem6.
All these steps must be completed before sending the System ready event so when any
semaphore is required by the CPU2, it polls on it until it is free.
The CPU2 can execute its startup sequence until it reports the System ready event without
the need for an external HSE or LSE oscillator.
Once the CPU2 has reported the System ready event, all system commands are supported
without any external HSE or LSE oscillator.
HSE and LSE oscillators are required when a wireless protocol stack is started. If power
consumption is not an issue it is possible to get rid of the external LSE oscillator and
configure the device to use the HSE (32 MHz) / 1024 (= 32.768 kHz) instead.
Note: On STM32WB1x devices the HSI48 oscillator is not present, so when a wireless protocol
stack is running, LSE or LSI oscillators must be active.
6 PLL management
6.1 How to switch the system clock between HSE and PLL
To switch the system clock between HSE and PLL clock, the C2HPRE divider in
RCC_EXTCFGR register must be changed to provide to CPU2 a clock at 32 MHz.
Figure 15. Algorithm to switch the system clock from PLL to HSE
ENTER
ENTER_CRITICAL_SECTION
EXIT_CRITICAL_SECTION
Release Sem3
MS56508V1
Figure 16. Algorithm to switch the system clock from HSE to PLL
ENTER
ENTER_CRITICAL_SECTION
Release Sem3
EXIT_CRITICAL_SECTION
ENTER_CRITICAL_SECTION
Release Sem3
EXIT_CRITICAL_SECTION
MS56250V1
The SHCI_C2_SetSystemClock system command has one parameter that can take the
following values:
• SET_SYSTEM_CLOCK_HSE_TO_PLL
• SET_SYSTEM_CLOCK_PLL_ON_TO_HSE
• SET_SYSTEM_CLOCK_PLL_OFF_TO_HSE
To switch the system clock from HSE to PLL, the flow is:
1. CPU1 configures and starts the PLL (PLLON = 1)
2. CPU1 does not need to wait for PLL to be ready
3. CPU1 calls the system command
SHCI_C2_SetSystemClock(SET_SYSTEM_CLOCK_HSE_TO_PLL - 0x00). CPU2 is
responsible to wait for PLL to be locked, set the FLASH LATENCY to 3 WS, set
C2HPRE divide to divide the CPU2 clock by 2, and switch the system clock on PLL.
4. In return of the command, the system clock runs on the PLL.
To switch the system clock from PLL to HSE, the flow is:
1. CPU1 starts HSE (HSEON = 1)
2. CPU1 does not need to wait for HSE to be ready
3. CPU1 calls the system command
SHCI_C2_SetSystemClock(SET_SYSTEM_CLOCK_PLL_ON_TO_HSE - 0x01) or
SHCI_C2_SetSystemClock(SET_SYSTEM_CLOCK_PLL_OFF_TO_HSE - 0x02).
CPU2 is responsible to switch the system clock on HSE, set C2HPRE divider to divide
the CPU2 clock by 1, and set the FLASH LATENCY to 1 WS. The PLL is switched off if
requested with parameter 0x02. Otherwise, CPU1 must switch it off.
4. On return of the command, the system clock is running on HSE.
Before entering in Stop mode (or Standby on STM32WB1x), the CPU1 must request the
switch from PLL to HSE with the system command if the system clock used is the PLL. To
call this command from critical section, the system command must be used in polling mode
(see How to use the system command in polling mode). To reduce the duration when this
command is sent from critical section, the BLE command can be used in blocking mode
(see How to use BLE commands in blocking mode).
Note: If the applications use only Sleep mode, it is advised to switch on the PLL before CPU2
starts. When the device is in Sleep mode, CPU2 does not make any change on the system
clock. It is not required to switch on HSE on CPU1 before entering Sleep mode. The
requirement is valid only for Stop mode (and Standby mode for STM32WB1x devices).
This section provides information and code examples on how to design and implement a
BLE application on a STM32WB device.
aci_gap_start_limited_discover Starts the limited discovery procedure. The controller starts the
y_proc () active scanning. Only the devices in limited discoverable mode
are returned to the upper layers.
aci_gap_start_general_discover Starts the general discovery procedure. The controller starts
y_proc () active scanning.
The following APIs be used in the procedure to establish the GAP connection
aci_gap_start_auto_connection_ Starts the auto connection procedure. The specified devices are
establish_proc () added to the controller white list and initiate connection calls to
the GAP controller using the initiator filter policy set to “use
white list to determine which advertiser to connect to”.
aci_gap_create_connection () Starts the direct connection procedure. A create connection call
is made to the controller by GAP with the initiator filter policy set
to “ignore white list and process connectible advertising packets
only for the specified device”.
aci_gap_start_auto_connection_ Starts the auto connection procedure. The specified devices are
establish_proc () added to the controller white list and a create connection call is
made to the controller by GAP with the initiator filter policy set to
“use white list to determine which advertiser to connect to”.
aci_gap_start_general_connecti Starts a general connection procedure. The device enables a
on_establish_proc() controller scan with the scanner filter policy set to “accept all
advertising packets” and from the scanning results, all the
devices are sent to the upper layer using the event callback
hci_le_advertising_report_event().
aci_gap_start_selective_connec Starts a selective connection procedure. The GAP adds the
tion_establish_proc () specified device addresses into the white list and enables a
controller scan with the scanner filter policy set to “accept
packets only from devices in white list”. All the devices found are
sent to the upper layer by the event callback
hci_le_advertising_report_event()
aci_gap_terminate_gap_proc() Terminates the specified GAP procedure.
uint8_t Security_Permissions,
uint8_t GATT_Evt_Mask,
uint8_t Enc_Key_Size,
uint8_t Is_Variable,
uint16_t *Char_Handle);
This procedure returns the pointer to the characteristic handle (Char_Handle), which is used
to identify the characteristic within the user application.
If the characteristic owner is in Notify or Indicate mode and enabled, the GATT server side
must use the following API to send a notification or indication to the GATT client.
aci_gatt_update_char_value()
aci_gap_set_io_capability()
Sets the IO capabilities of the device. This command must be given only when the device is
not in a connected state.
aci_gap_set_authentication_requirement()
Sets the authentication requirements for the device. This command must be given only
when the device is not in a connected state.
This command defines bonding mode information, MITM mode, LE secure connection
support values, keypress notification support values, encryption key size, use or not of fixed
pin, its value, and identity address type.
• SC_Support parameter defines the LE Secure connections support values.
– 0x00: Secure connections pairing not supported (legacy pairing mode)
– 0x01: Secure connections pairing supported but optional
– 0x02: Secure connections pairing supported and mandatory (SC only mode)
Once the connection is established, the security procedure can be started:
• By the master with aci_gap_set_pairing_req()
– Sends the SM pairing request to start a pairing process. The authentication
requirements and IO capabilities must be set before issuing this command.
– The force_rebond parameter value determines if the pairing request is sent even if
the device was previously bonded.
• By the slave with aci_gap_slave_security_req()
– Sends a slave security request to the master. This command must be issued to
notify the master of the security requirements of the slave. The master can encrypt
the link, initiate the pairing procedure, or reject the request.
– aci_gap_pairing_complete_event is returned after the pairing process is
completed.
Depending on the SC_Support parameter value, the device answers to security requests
with one of the commands listed in Table 9.
aci_gap_get_bonded_devices() This command gets the list of the devices which are bonded. It
returns the number of addresses and the corresponding address
types and values.
aci_gap_is_device_bonded() This command determines whether the device, whose address
is specified in the command, is bonded. If the device uses a
resolvable private address and has been bonded, then the
command returns ble_status_success.
aci_gap_get_security_level() This command can be used to get the current security settings
of the device.
If keypress notification is supported, use:
aci_gap_passkey_input() This command permits to tell the stack the input type detected
during passkey input.
HCI_LE_Set_default_Phy() Specifies the preferred PHY to implement (for RX and TX), not linked to
a connection. By default, the preferred PHY is 2M.
Once a connection is established (1M), the preferred PHY for TX and RX can be sent by each device:
HCI_LE_Set_Phy() Makes it possible for the host to specify the preferred values for a
connection.
During a connection, the RX or TX PHY used can be read:
HCI_LE_Read_Phy() Reads the current PHY TX and RX for a connection.
This command status event is used to indicate that the command described by the
Command_Opcode parameter has been received, and that the BLE stack controller is
currently performing the task for this command.
For any problem, the Status event parameters contains the corresponding error code (see
[7], v5.0, Vol. 2, part D).
On the GATT client side, the GATT discovery procedure can fail due to several reasons. The
aci_gatt_error_resp_event() is generated when an error response is received from the
GATT server. This does not mean that the procedure ended with an error, but this error is
part of the procedure itself.
All GATT client procedures have to be completed with either a success or error on the
aci_gatt_proc_complete_event(), so the GATT client can start a new procedure.
This section describes the specification and implementation of the following applications
running on CPU1:
• STMicroelectronicsg specific application
– Transparent mode - Direct test mode
• BT-SIG GATT-based application
– Heart rate sensor
• STMicroelectronics proprietary GATT-based application
– P2P application (server / client)
– FUOTA
Application
ACI - HCI
CPU1
SRAM2 IPCC
MS51826V1
Here are the HCI test commands, which are fully compliant with the specifications:
• HCI_LE_Transmitter_Test
• HCI_LE_Enhanced_Transmitter_Test
• HCI_LE_Receiver_Test
• HCI_LE_Enhanced_Receiver_Test
• HCI_LE_Test_End
The number of the received test packets is a return value of the function HCI_LE_Test_End.
The additional available functions are listed in Table 13.
The command sequence below is a typical flow for enabling CW transmission from the
STM32WB radio:
hci_reset
aci_hal_set_tx_power_level
aci_hal_tone_start
aci_hal_tone_stop
8.1.3 Configuration
STM32WB is seen as a 2-wire UART interface (TXD, RXD). CPU1 application to be used is
“Ble_TransparentMode”. This firmware does not interpret the command and event but just
communicates to the wireless BLE stack via the IPCC and the selected UART interface
(USART1 or LPUART1).
The UART interface and configuration selection is done usingapp_conf.h:
#define CFG_UART_GUI hw_uart1
The P-NUCLEO-WB55 board includes the ST-LINK with Virtual COM port capability.
The following project is configured to communicate via the VCP of the ST-LINK:
\Projects\ NUCLEO-WB55.Nucleo\Applications\BLE\Ble_TransparentMode.
The UART1 (PB6, PB7) is connected to the P-NUCLEO-WB55 board ST-LINK VCP.
Figure 18. Transparent mode with P-NUCLEO-WB55 board and ST-LINK VCP
STM32WB microcontroller
STM32CubeMonitor-RF
Transparent
BLE
mode
stack
firmware
The P-NUCLEO-WB55 dongle board is provided without the ST-LINK. The project
.\Projects\ NUCLEO-WB55.USBDongle\Applications\BLE\BLE_TransparentModeVCP
includes Virtual COM port implementation in addition to the transparent mode feature.
It is also possible to connect the STM3232WB UART interface directly to RS232 serial
communication via a level shifter (not included on the NUCLEO-WB55RG board). This
approach can be used to connect, among others, an RF tester.
Figure 19. Transparent mode with P-NUCLEO-WB55 board and level shifter
STM32WB microcontroller
STM32CubeMonitor-RF
Transparent
BLE
mode
stack
firmware
UART Level
Tx/Rx
RS232 shifter
Figure 20. Simple setup with BLE RF tester and P-NUCLEO board
MS51832V1
Figure 22. Simple setup with BLE RF tester and P-NUCLEO board
Figure 23. Smart phone - ST BLE sensor with heart rate application
• svc_ctl.c: Initializes the BLE stack and manages the services of the application (GATT
events)
• hrs.c: used for the creation of:
– A service and its characteristics for the application,
– To update the service characteristics,
– To receive the notification or write command and
– to make a link between the BLE stack and the applicative.
For the application, the subfolder to create specific code is STM32_WPAN\app
• app_entry.c: Initializes the BLE Transport layer and the BSP (e.g. LEDs, buttons)
• app_ble.c: Initializes the GAP and manages the connection (e.g. advertising, scan)
• hrs_app.c: Initializes the GATT and manages the application
Service Init - HRS_Init() – Registers the heart rate event handle to the service controller
– Initializes the service UUID
aci_gatt_add_serv – Adds the heart rate service as primary service
– Initializes the heart rate measurement characteristic
aci_gatt_add_char – Adds the heart rate characteristic
– Initializes the body sensor location characteristic
– Adds the body sensor location characteristic
aci_gatt_add_char
– Updates the heart rate measurement characteristic
aci_gatt_update_char_value – Updates the requested characteristics within the specified value
– Updates the body sensor location characteristic value
aci_gatt_update_char_value – Updates the requested characteristics within the specified value
HeartRate_Event_Handler(void
– Manages the HCI Vendor Type Event
*Event)
EVT_BLUE_GATT_WRITE_PERMIT_REQ – Server receives a Write command
– HR control point characteristic value
– Resets energy expended command, then:
Sends an aci_gatt_write_response() with an OK status.
Notifies the HRS application to reset expended energy
Or sends an aci_gatt_write_response() with an error.
EVT_BLUE_GATT_ATTRIBUTE_MODIFIED – HR measurement characteristic description value
– ENABLE or DISABLE notification
– Notifies HRS application of the measurement notification
The purpose of the service implementation is to register the heart rate services and selected
characteristics withthe BLE stack GATT database.
/**
* @brief Service Heart Rate initialization
* @param None
* @retval None
*/
void HRS_Init(void)
{
REGISTER HEART RATE EVENT HANDLER
? SVCCTL_RegisterSvcHandler(HeartRate_Event_Handler);
• Allow the application to update the characteristics to BLE stack GATT database
/**
* @brief Characteristic update
* @param UUID: UUID of the characteristic
* @retval BodySensorLocationValue: The new value to be written
*/
tBleStatus HRS_UpdateChar(uint16_t UUID, uint8_t *pPayload)
{
UPDATE BODY SENSOR LOCATION
HRSAPP_Init() Receives and reacts to the internal events coming from the BLE stack
at GATT level.
HRS_Notification() Calls the service functions to update the characteristics (notify/write).
HRSAPP_Measurement() -
Figure 24. Heart rate project - Interaction between middleware and user application
Initialization Event from BLE stack Command to the BLE stack
GAP event
svc_ctl.c HRS notification GATT
notification
SVCCTL_Init SVCCTL_UserEvtRx
GATT update
GATT event characteristic
hrs.c dis.c
HeartRate
HRS_Init
_Event_Handler
DIS_Init DIS_App_Update_Char
Event Add service
HRS_
(GAP, GATT) Add characteristic
UpdateChar Add service
Register event handler Add characteristic
Table 16. AD structure according to the Bluetooth 5 Core specification Vol. 3 part C
Field name Type Len Record size
TX_POWER_LEVEL 0x0A 2 3
COMPLETE_NAME 0x09 8 9
MANUF_SPECIFIC 0xFF 13 14
FLAGS 0x01 2 3
Group B features
• Bit mask Thread: used to advertise the presence of the Thread switch characteristics.
• Bit mask OTA reboot request: used to advertise presence of the BLE reboot
characteristics.
0x00 Generic
0x83 STM32WB P2P server 1
0x84 STM32WB P2P server 2
0x85 STM32WB P2P router
0x86 STM32WB FUOTA
0,
NULL,
0, 0);
To be used, a GAP central and GATT client device must discover and connect to the P2P
server application. Figure 27 explains the data exchange procedure.
Figure 28. P2P server connected to ST BLE sensor smart phone application
void P2PS_STM_App_Notification(P2PS_STM_App_Notification_evt_t
*pNotification)
{
Switch (pNotification->P2P_Evt_Opcode)
{
case P2PS_STM__NOTIFY_ENABLED_EVT:
P2P_Server_App_Context.Notification_Status = 1;
APP_DBG_MSG("-- P2P APPLICATION SERVER : NOTIFICATION ENABLED\n");
APP_DBG_MSG(" \n\r");
break;
case P2PS_STM_NOTIFY_DISABLED_EVT:
P2P_Server_App_Context.Notification_Status = 0;
. APP_DBG_MSG("-- P2P APPLICATION SERVER : NOTIFICATION DISABLED\n");
APP_DBG_MSG(" \n\r");
break;
case P2PS_STM_WRITE_EVT:
if(pNotification->DataTransfered.pPayload[0] == 0x00){
. if(pNotification->DataTransfered.pPayload[1] == 0x01)
{
BSP_LED_On(LED_BLUE);
APP_DBG_MSG("-- P2P APPLICATION SERVER : LED1 ON\n");
APP_DBG_MSG(" \n\r");
P2P_Server_App_Context.LedControl.Led1=0x01;
}
if(pNotification->DataTransfered.pPayload[1] == 0x00)
{
BSP_LED_Off(LED_BLUE);
APP_DBG_MSG("-- P2P APPLICATION SERVER : LED1 OFF\n");
APP_DBG_MSG(" \n\r");
P2P_Server_App_Context.LedControl.Led1=0x00;
}
}
if(P2P_Server_App_Context.Notification_Status){
APP_DBG_MSG("P2P APPLICATION SERVER : INFORM CLIENT BUTTON 1 PUSHED \n
");
APP_DBG_MSG(" \n\r");
P2PS_STM_App_Update_Char(P2P_NOTIFY_CHAR_UUID, (uint8_t *)
&P2P_Server_App_Context.ButtonControl);
} else {
APP_DBG_MSG("P2P APPLICATION SERVER : CAN'T INFORM CLIENT - NOTIFICATION
DISABLED\n ");
}
return;
}
There is no service created for the P2P client. It is only necessary to register the GATT client
Handler SVCCTL_RegisterCltHandler() to be notified of any GATT events at application
level.
In the application, the subfolder to create specific code is “User”
• app_entry.c: initializes the BLE transport layer and the BSP (e.g. LEDs, buttons)
• app_ble.c: initializes the GAP and manages the connection (scan and connect)
• p2p_client_app.c: initializes the GATT and manages the GATT client application.
if (result == BLE_STATUS_SUCCESS)
{
APP_DBG_MSG(" \r\n\r** START GENERAL DISCOVERY (SCAN) ** \r\n\r");
} else {
APP_DBG_MSG("-- BLE_App_Start_Limited_Disc_Req, Failed \r\n\r");
BSP_LED_On(LED_RED);
}
}
return;
}
• Receives the ADV events report to be filtered to save the P2P server BD address:J’rric
case AD_TYPE_MANUFACTURER_SPECIFIC_DATA: // Manufactureur Specific
if (adlength >= 7 && le_advertising_event->Advertising_Report[0].Data[k +
2] == 0x01) {
APP_DBG_MSG("--- ST MANUFACTURER ID --- \n");
switch (le_advertising_event->Advertising_Report[0].Data[k + 3]) {
case CFG_DEV_ID_P2P_SERVER1:
APP_DBG_MSG("-- SERVER DETECTED -- VIA MAN ID\n");
BleApplicationContext.DeviceServerFound = 0x01;
SERVER_REMOTE_BDADDR[0] = le_advertising_event-
>Advertising_Report[0].Address[0];
SERVER_REMOTE_BDADDR[1] = le_advertising_event-
>Advertising_Report[0].Address[1];
SERVER_REMOTE_BDADDR[2] = le_advertising_event-
>Advertising_Report[0].Address[2];
SERVER_REMOTE_BDADDR[3] = le_advertising_event-
>Advertising_Report[0].Address[3];
SERVER_REMOTE_BDADDR[4] = le_advertising_event-
>Advertising_Report[0].Address[4];
SERVER_REMOTE_BDADDR[5] = le_advertising_event-
>Advertising_Report[0].Address[5];
break;
• Initiates the connection to any detected P2P servers:
static void Connect_Request( void )
{
tBleStatus result;
APP_DBG_MSG("\r\n\r** CREATE CONNECTION TO SERVER ** \r\n\r");
if (BleApplicationContext.Device_Connection_Status !=
APP_BLE_CONNECTED_CLIENT) {
result = aci_gap_create_connection(
SCAN_P,
SCAN_L,
PUBLIC_ADDR, SERVER_REMOTE_BDADDR,
PUBLIC_ADDR,
CONN_P1,
CONN_P2,
0,
SUPERV_TIMEOUT,
CONN_L1,
CONN_L2);
• Starts “Services Discovery Procedure” once connection is established:
case EVT_LE_CONN_COMPLETE:
/**
* The connection is established
*/
connection_complete_event = (hci_le_connection_complete_event_rp0 *)
meta_evt->data;
BleApplicationContext.BleApplicationContext_legacy.connectionHandle =
connection_complete_event->Connection_Handle;
BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_CLIENT;
case APP_BLE_DISCOVER_SERVICES:
APP_DBG_MSG("P2P_DISCOVER_SERVICES\n");
break;
case APP_BLE_DISCOVER_CHARACS:
}
uuid = UNPACK_2_BYTE_PARAMETER(&pr->Handle_Value_Pair_Data[idx]);
/* store the characteristic handle not the attribute handle */
handle = UNPACK_2_BYTE_PARAMETER(&pr->Handle_Value_Pair_Data[idx-14]);
if(uuid == P2P_WRITE_CHAR_UUID){
APP_DBG_MSG("-- GATT : WRITE_UUID FOUND - connection handle 0x%x\n",
aP2PClientContext[index].connHandle);
aP2PClientContext[index].state = APP_BLE_DISCOVER_WRITE_DESC;
aP2PClientContext[index].P2PWriteToServerCharHdle = handle;
}
else if(uuid == P2P_NOTIFY_CHAR_UUID){
APP_DBG_MSG("-- GATT : NOTIFICATION_CHAR_UUID FOUND - connection handle
0x%x\n", aP2PClientContext[index].connHandle);
aP2PClientContext[index].state = APP_BLE_DISCOVER_NOTIFICATION_CHAR_DESC;
aP2PClientContext[index].P2PNotificationCharHdle = handle;
}
Once the P2P server services and characteristics handle are discovered, the application is
able to:
• Control the remote device using the “Write” characteristic
tBleStatus Write_Char(uint16_t UUID, uint8_t Service_Instance, uint8_t
*pPayload){
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
uint8_t index;
index = 0;
while((index < BLE_CFG_CLT_MAX_NBR_CB) && (aP2PClientContext[index].state
!= APP_BLE_IDLE)){
switch(UUID){
case P2P_WRITE_CHAR_UUID:
ret =aci_gatt_write_without_resp(aP2PClientContext[index].connHandle,
aP2PClientContext[index].P2PWriteToServerCharHdle,
2, /* charValueLen */
(uint8_t *) pPayload);
break;
• Receive notifications via the “Notify” characteristics
void Gatt_Notification(P2P_Client_App_Notification_evt_t *pNotification){
switch(pNotification->P2P_Client_Evt_Opcode){
case P2P_NOTIFICATION_INFO_RECEIVED_EVT: {
P2P_Client_App_Context.LedControl.Device_Led_Selection=pNotification-
>DataTransfered.pPayload[0];
switch(P2P_Client_App_Context.LedControl.Device_Led_Selection) {
case 0x01 : {
P2P_Client_App_Context.LedControl.Led1=pNotification-
>DataTransfered.pPayload[1];
if(P2P_Client_App_Context.LedControl.Led1==0x00){
BSP_LED_Off(LED_BLUE);
APP_DBG_MSG(" -- P2P CLIENT : NOTIFICATION RECEIVED - LED OFF \n\r");
} else {
BSP_LED_On(LED_BLUE);
APP_DBG_MSG(" -- P2P CLIENT : NOTIFICATION RECEIVED - LED ON\n\r");
}
break;
}
APP_BLE_Init
SVCCTL_App Update_Service Event from
_Notification BLE stack
Write_Char Command to
the BLE stack
Event_Handler Gatt_Notification
GAP
event
GATT event
The application area contains the application standalone binary. It can be fully updated with
FUOTA application.
n2 sectors
Sector index (S-n2)
Application
n1 sectors
0x0800 7000 Sector index 7
User data
1 sector
0x0800 6000 Sector index 5
FUOTA application
6 sectors
0x0800 0000 Sector index 0
MS51861V1
Boot
No FW application No
SW reset source
available
Yes Yes
OTA FW
- - - 0000FE20-cc7a-482a-984a-7f2ed5b3e58f
update
Write without
LED - Base address 4 bytes 0000FE22-8e22-4541-9d4c-21edae82ed19
response
button
control File upload reboot
- 1 byte Indicate 0000FE23-8e22-4541-9d4c-21edae82ed19
confirmation
Write without
- OTA raw data 20 bytes 0000FE24-8e22-4541-9d4c-21edae82ed19
response
Name Indication
Value 0x01 Reboot
Starting from application including the reboot request characteristic, this is the flow to
update CPU1 application:
1. BLE application includes the reboot characteristics.
2. Once the GAP connection is established, the remote GATT client device researches
services and characteristics (reboot request characteristics detected).
3. Next, in order to switch to the FUOTA application, the remote device writes the Reboot
request characteristics with information for boot mode option and sectors to erase.
4. At this stage the BLE Link is disconnected to reboot on STMicroelectronics proprietary
FUOTA GATT service and characteristics application.
5. Application sectors are erased with the information provided by the reboot
characteristics and STMicroelectronics proprietary FUOTA GATT service and
characteristics application starts advertising.
6. New connection has to be established by the remote device to discover the FUOTA
service and characteristics.
7. The base address characteristic is used to initiate the new binary upload.
8. All the data is transferred via the raw data characteristic and programmed directly to
the flash memory once received.
9. The end of file transfer is confirmed by the base address characteristic.
10. Confirmation of the received file is indicate by the file upload confirmation
characteristic.
11. At this stage, the FUOTA application checks the integrity of the new binary and reboots
to start the new uploaded application.
12. If the application integrity is not ensured, the application sectors are erased to reboot
on the FUOTA application.
Once rebooted, the address to upload the application binary file is selected. The default
address is 0x7000 (sector 7 - application). Changing the binary file to upload is still possible
at this stage, if needed.
Once the upload is finished, the reboot procedure is executed to start the new application.
Next, run a new scan procedure to discover the heart rate sensor advertising packets, and
connect to it.
After connecting to the device, the heart rate measurement values are notified by the
sensor.
Note: The smart phone application associates the GATT database with remote Bluetooth address.
To solve this issue the FUOTA application advertising address is increased by 1.
**************************************************************************
****/
#define BLE_CFG_OTA_REBOOT_CHAR 1 /**< REBOOT OTA MODE
CHARACTERISTIC */
• app_ble.c
/**
* Initialization of ADV - Ad Manufacturer Element - Support OTA Bit Mask
*/
#if(BLE_CFG_OTA_REBOOT_CHAR != 0)
manuf_data[sizeof(manuf_data)-8] = CFG_FEATURE_OTA_REBOOT;
#endif
• p2p_stm.c to add the characteristics (middleware)
#if(BLE_CFG_OTA_REBOOT_CHAR != 0)
/**
* Add Boot Request Characteristic
*/
aci_gatt_add_char(aPeerToPeerContext.PeerToPeerSvcHdle,
BM_UUID_LENGTH,
(Char_UUID_t *)BM_REQ_CHAR_UUID,
BM_REQ_CHAR_SIZE,
CHAR_PROP_WRITE_WITHOUT_RESP,
ATTR_PERMISSION_NONE,
GATT_NOTIFY_ATTRIBUTE_WRITE,
10,
0,
&(aPeerToPeerContext.RebootReqCharHdle));
#endif
• p2p_stm.c to receive the request at GATT level and inform the application (middleware)
else if(attribute_modified->Attr_Handle ==
(aPeerToPeerContext.RebootReqCharHdle + 1))
{
BLE_DBG_P2P_STM_MSG("-- GATT : REBOOT REQUEST RECEIVED\n");
Notification.P2P_Evt_Opcode = P2PS_STM_BOOT_REQUEST_EVT;
Notification.DataTransfered.Length=attribute_modified->Attr_Data_Length;
Notification.DataTransfered.pPayload=attribute_modified->Attr_Data;
P2PS_STM_App_Notification(&Notification);
• p2p_server_app.c to manage the reboot request (application)
void P2PS_STM_App_Notification(P2PS_STM_App_Notification_evt_t
*pNotification)
{
switch(pNotification->P2P_Evt_Opcode)
{
#if(BLE_CFG_OTA_REBOOT_CHAR != 0)
case P2PS_STM_BOOT_REQUEST_EVT:
APP_DBG_MSG("-- P2P APPLICATION SERVER : BOOT REQUESTED\n");
APP_DBG_MSG(" \n\r");
*(uint32_t*)SRAM1_BASE = *(uint32_t*)pNotification-
>DataTransfered.pPayload;
NVIC_SystemReset();
break;
#endif
if(udn != 0xFFFFFFFF) {
company_id = LL_FLASH_GetSTCompanyID();
device_id = LL_FLASH_GetDeviceID();
Write ER:
aci_hal_write_config_data( CONFIG_DATA_ER_OFFSET, CONFIG_DATA_ER_LEN,
(uint8_t*)BLE_CFG_ER_VALUE );
/**< Add in that list all tasks that never send a ACI/HCI command */
typedef enum
{
CFG_FIRST_TASK_ID_WITH_NO_HCICMD = CFG_LAST_TASK_ID_WITH_HCICMD - 1,
/**< Shall be FIRST in the list */
CFG_TASK_SYSTEM_HCI_ASYNCH_EVT_ID,
CFG_LAST_TASK_ID_WITHO_NO_HCICMD
/**< Shall be LAST in the list */
} CFG_Task_Id_With_NO_HCI_Cmd_t;
#define UTIL_SEQ_CONF_TASK_NBR CFG_LAST_TASK_ID_WITHO_NO_HCICMD
CFG_BLE_NUM_GATT_ATTRIBUTES
Maximum number of attribute records related to all the required characteristics (excluding
the services) that can be stored in the GATT database, for the specific BLE user application.
For each characteristic, the number of attribute records goes from two to five depending on
the characteristic properties:
• minimum of two (one for declaration and one for the value)
• add one more record for each additional property: notify or indicate, broadcast,
extended property.
The total calculated value must be increased by 9, due to the records related to the standard
attribute profile and GAP service characteristics, and automatically added when initializing
GATT and GAP layers
• Min value: <number of user attributes> + 9
• Max value: depending on the GATT database defined by user application
CFG_BLE_NUM_GATT_SERVICES
Defines the maximum number of services that can be stored in the GATT database. Note
that the GAP and GATT services are automatically added at initialization so this parameter
must be the number of user services increased by two.
• Min value: <number of user service> + 2
• Max value: depending GATT database defined by user application
CFG_BLE_ATT_VALUE_ARRAY_SIZE
Size of the storage area for the attribute values.
To calculate CFG_BLE_ATT_VALUE_ARRAY_SIZE, it is necessary to add characteristics
related to default GATT services.
By default, two services are present and must be included, with dedicated characteristics:
• Generic access service: service UUID 0x1800, with its three mandatory characteristics:
– Device name UUID 0x2A00
– Appearance UUID 0x2A01
– Peripheral preferred connection parameters. UUID 0x2A04
• Generic attribute service. UUID 0x1801, with one optional characteristic:
– Service changed UUID 0x2A05
Each characteristic contributes to the attrValueArrSize value as follows:
• Characteristic value length plus:
– 5 bytes if characteristic UUID is 16 bits
– 19 bytes if characteristic UUID is 128 bits
– 2 bytes if characteristic has a server configuration descriptor
– 2 bytes * CFG_BLE_NUM_LINK if the characteristic has a client configuration
descriptor
– 2 bytes if the characteristic has extended properties
Each descriptor contributes to the attrValueArrSize value as follows:
• Descriptor length
CFG_BLE_NUM_LINK
Maximum number of BLE links supported
• Min value: 1
• Max value: 8
CFG_BLE_DATA_LENGTH_EXTENSION
Disable/enable the extended packet length BLE 5.0 feature
• Disable: 0
• Enable: 1
CFG_BLE_PREPARE_WRITE_LIST_SIZE
Maximum number of supported “prepare write request”. The minimum required value can be
calculated using the following DEFAULT_PREP_WRITE_LIST_SIZE macro:
#define DIVC(x, y) (((x) + (y) - 1) / (y))
/**
* DEFAULT_ATT_MTU: minimum mtu value that GATT must support.
* 5.2.1 ATT_MTU, BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G]
*/
#define DEFAULT_ATT_MTU (23)
/**
* DEFAULT_MAX_ATT_SIZE: maximum attribute size.
*/
#define DEFAULT_MAX_ATT_SIZE (512)
/**
* PREP_WRITE_X_ATT(max_att): compute how many Prepare Write Request are
needed
* to write a characteristic with size max_att when the used ATT_MTU value
is
* equal to DEFAULT_ATT_MTU (23).
*/
#define PREP_WRITE_X_ATT(max_att) (DIV_CEIL(max_att, DEFAULT_ATT_MTU
- 5U) * 2)
/**
* DEFAULT_PREP_WRITE_LIST_SIZE: default minimum Prepare Write List size.
*/
#define DEFAULT_PREP_WRITE_LIST_SIZE
PREP_WRITE_X_ATT(DEFAULT_MAX_ATT_SIZE)
CFG_BLE_MBLOCK_COUNT
Number of allocated memory blocks for the BLE stack. The minimum required value can be
calculated using the following MBLOCKS_CALC macros:
#define MEM_BLOCK_SIZE (32)
/**
* MEM_BLOCK_X_MTU (mtu): compute how many memory blocks are needed to
compose an ATT
* Packet with ATT_MTU = mtu.
* 7.2 FRAGMENTATION AND RECOMBINATION, BLUETOOTH SPECIFICATION Version 4.2
* [Vol 3, Part A]
*/
#define MEM_BLOCK_X_TX (mtu) (DIV_CEIL((mtu) + 4U,
MEM_BLOCK_SIZE) + 1U)
#define MEM_BLOCK_X_RX (mtu, n_link) ((DIV_CEIL((mtu) + 4U,
MEM_BLOCK_SIZE) + 2U) * (n_link) + 1)
#define MEM_BLOCK_X_MTU (mtu, n_link) (MEM_BLOCK_X_TX(mtu) +
MEM_BLOCK_X_RX(mtu, (n_link)))
/**
* Minimum number of blocks required for secure connections
*/
#define MBLOCKS_SECURE_CONNECTIONS (4)
/**
* MBLOCKS_CALC(pw, mtu, n_link): minimum number of buffers needed by the
stack.
* This is the minimum racomanded value and depends on:
* - pw: size of Prepare Write List
* - mtu: ATT_MTU size
* - n_link: maximum number of simultaneous connections
*/
#define MBLOCKS_CALC(pw, mtu, n_link) ((pw) + MAX(MEM_BLOCK_X_MTU(mtu,
n_link), (MBLOCKS_SECURE_CONNECTIONS)))
CFG_BLE_MAX_ATT_MTU
Maximum ATT MTU size supported.
• Min value: 23
• Max value: 512
CFG_BLE_SLAVE_SCA
The sleep clock accuracy (ppm value) used in BLE connected slave mode to calculate the
window widening (in combination with the sleep clock accuracy sent by master in
CONNECT_REQ PDU), refer to BLE 5.0 specifications - Vol 6 - Part B - chap 4.5.7 and
4.2.2.
• Min value: 0
• Max value: 500 (worst possible admitted by specification)
CFG_BLE_MASTER_SCA
The sleep clock accuracy handled in master mode. It is used to determine the connection
and advertising events timing. It is transmitted to the slave in CONNEC_REQ PDU used by
the slave to calculate the window widening, see CFG_BLE_SLAVE_SCA and [7], v5.0 Vol 6
- Part B - chap 4.5.7 and 4.2.2.
Possible values:
• 251 ppm to 500 ppm: 0
• 151 ppm to 250 ppm: 1
• 101 ppm to 150 ppm: 2
• 76 ppm to 100 ppm: 3
• 51 ppm to 75 ppm: 4
• 31 ppm to 50 ppm: 5
• 21 ppm to 30 ppm: 6
• 0 ppm to 20 ppm: 7
CFG_BLE_LSE_SOURCE
Source for the 32 kHz slow speed clock.
• External crystal LSE: 0 - No calibration
• Internal RO (LSI): 1 - The accuracy of this oscillator can vary depending upon external
conditions (temperature), hence it is calibrated every second to ensure correct
behavior of timing sensitive BLE operations.
CFG_BLE_MAX_CONN_EVENT_LENGTH
This parameter determines the maximum duration of a slave connection event. When this
duration is reached the slave closes the current connections event (whatever is the
CE_length parameter specified by the master in HCI_CREATE_CONNECTION HCI
command), expressed in units of 625 / 256 µs (~2.44 µs).
• Min value: 0 (if 0 is specified, the master and slave perform only a single TX-RX
exchange per connection event).
• Max value: 1638400 (4000 ms). A higher value can be specified (max 0xFFFFFFFF)
but results in a maximum connection time of 4000 ms as specified. In this case the
parameter is not applied, and the predicted CE length calculated on slave is not
shortened.
CFG_BLE_HSE_STARTUP_TIME
Startup time of the high speed (16 or 32 MHz) crystal oscillator, in units of 625/256 µs
(~2.44 µs).
• Minimum: 0x148 (328 * 2.44 µs = 800 µs), default value
• Maximum: 0x334 (820 * 2.44 µs = 2 ms)
Due to the design of the BLE radio, it is recommended to not modify the default value of this
parameter.
CFG_BLE_VITERBI_MODE
Viterbi implementation in BLE LL reception
• 0: Enabled
• 1: Disabled
CFG_BLE_OPTIONS
This is an 8-bit parameter, each bit enables/disables an option:
• bit 0:
– 1: LL only
– 0: LL + host
• bit 1:
– 1: No service change description
– 0: With service change description
• bit 2:
– 1: Device name read-only
– 0: Device name R/W
• bit 3:
– 1: Extended advertising supported
– 0: Extended advertising not supported
• bit 4:
– 1: CS Algo #2 supported
– 0: CS Algo #2 not supported
• bits 5 and 6: Reserved (must be kept to 0)
• bit 7:
– 1: LE power class 1
– 0: LE power classes 2 and 3
application with the notification Custom_xxx_Notification(). Each GATT event is relevant for
only one BLE service. To avoid the service controller to call all registered BLE services to
report the received event, the callback informs the service controller if the GATT has been
processed or ignored.
Three values can be returned:
1. SVCCTL_EvtNotAck: means the GATT event was not relevant for that BLE service.
The service controller keeps reporting this GATT event to other registered BLE
services until it gets an ack. When a GATT event is not acknowledged by all registered
BLE services, it is reported to the application with the notification
SVCCTL_App_Notification().
2. SVCCTL_EvtAckFlowEnable: means the GATT event has been processed and the
service controller does not report it to either other registered BLE services or to the
application.
3. SVCCTL_EvtAckFlowDisable: means the GATT event has been acknowledged and
the service controller does not report it to either other registered BLE services or to the
application. However, the GATT event has not been processed. The service controller
notifies the transport layer that this event shall not be discarded. In that case, the
transport layer does not report any more event until the command hci_resume_flow()
has been called. As soon as the flow is resumed, the not acknowledged event is
reported one more time. These are all BLE user hci events not reported anymore, not
only those to the BLE service that did not acknowledge the GATT event.
CPU2 can be used as a BLE HCI layer co-processor. In that case, the user must either
implement its own HCI application, or use an existing open source BLE host stack.
Most BLE host stacks use a UART interface to communicate with a BLE HCI co-processor.
The equivalent physical layer on the STM32WB device is the mailbox, as described in
Section 14.2: Mailbox interface.
The mailbox provides an interface for both the BLE and the System channel. The BLE host
stack builds the command buffer to be sent over the BLE channel on the mailbox and must
provide an interface to report the events received through the mailbox. In addition to the
mailbox BLE host stack adaptation, the user must notify the mailbox driver when an
asynchronous packet is be released.
The system channel is not handled by a BLE host stack. The user must implement its own
transport layer to build the System command buffer to be sent to the mailbox driver and to
manage the event received from the mailbox (including the notification to release an
asynchronous buffer to the mailbox driver), or use the mailbox extended driver (as
described in Section 14.3: Mailbox interface - Extended), which provides an interface on top
of the provided transport layer which builds the System command buffer and to manage the
system asynchronous event.
The BLE_TransparentMode project can be used as an example to build an application on
top of a BLE HCI layer co-processor using the mailbox as described in Section 14.2:
Mailbox interface.
10 Thread
10.1 Overview
The Thread stack embarked in CPU2 core is provided by OpenThread, an open-source
implementation of the Thread networking protocol, and is released by Nest.
OpenThread provides several APIs that address different services at different levels inside
the stack. All these APIs (documented in the STM32WB firmware package) are exported on
CPU1 core and can be used directly by the application.
The STM32WB firmware package is provided with several examples demonstrating how to
run simple Thread applications. To run these applications, the appropriate CPU2 firmware
binary need to be downloaded.
There are three major MO firmwares available, as detailed in Table 28.
Caution: The OpenThread stack provides several compilation flags to set different configurations.
Nevertheless, since the stack inside the STM32WB is delivered as a binary, those flags are
fixed and cannot be modified by the user. The selected flags can be seen in the files listed in
Table 29.
When building a Thread application, the appropriate configuration file must be used
depending on the downloaded CPU2 firmware. The flags inside this configuration file are
used to define which APIs are exported and available for CPU1 application. As mentioned
before, these flags must not be modified by the user.
Application UARTs
Infrastructure
Profiles Services
CPU1
IPCC
M0 firmware
Radio
BLE radio 802.15.4 radio BLE IP
CPU2
MS52415V
otXXX() call
Waiting until the completion of the command otXXX() processing
otYYY() call
Waiting until the completion of the command otYYY() processing
MS52422V1
ot callback
MS52423V1
function. This is due to the dual core architecture constraints. The application callback must
be passed in the context parameter, as shown in Figure 42.
error = otCoapSendRequest(NULL,
pOT_Message,
& OT_MessageInfo,
& APP_THREAD_DummyRespHandler,
( void*)&APP_THREAD_CoapRespHandler);
void APP_THREAD_DummyRespHandler (
void * p_context,
void APP_THREAD_CoapRespHandler(otCoapHeader *pHeader, otCoapHeader * pHeader,
otMessage * pMessage, otMessage * pMessage,
const otMessageInfo *pMessageInfo, const otMessageInfo * pMessageInfo,
otError Result) otError Result)
{ {
if (Result == OT_ERROR_NONE) UNUSED(p_context);
APP_DBG_MSG(" ********* The COAP has been well acknowledged “) UNUSED(pHeader);
else UNUSED(pMessage);
APP_THREAD_Error(ERR_THREAD_RESPONSE_HANDLER, Result) UNUSED(pMessageInfo);
} UNUSED(Result);
}
MS52424V1
Note: The easiest way to see how OpenThread callbacks are managed is to refer to the different
applications provided in the STM32WB firmware delivery.
Boot
Thread processing
Call to SHCI_C2_FLASH_StoreData
MS51872V1
This section provides information and code examples on how to design and implement an
OpenThread application on a STM32WB device.
Note: This section does not detail all the OpenThread APIs related to CoAP, but gives an overview
of the main functions and abstractions available as part of STM32WB Thread examples.
This structure can be initialized as shown in the different Thread Coap application examples
provided inside the firmware package
#define C_RESSOURCE "light"
static otCoapResource OT_Ressource = {C_RESSOURCE,
APP_THREAD_CoapRequestHandler,"MyOwnContext", NULL};
11.4 Commissioning
Commissioning requires a device with the commissioner role, and another with the joiner
role. The commissioner is either a Thread device in an existing network, or a device external
to the Thread network (such as a mobile phone) that performs the role.
The joiner is the device wishing to join the Thread network.
A Thread commissioner is used to authenticate a device on the network. It does not transfer,
nor have possession of Thread network credentials such as the master key.
This document covers basic, on-mesh commissioning without an external commissioner or
border router.
Thread_Commissioning application demonstrates a simple commissioning example.
11.5 CLI
The OpenThread stack exposes configuration and management APIs via a command line
interface.
The certification environment (GRL test harness) uses the CLI to execute test cases.
11.6 Traces
Traces from CPU1 and CPU2 applications are routed to the UART (configured at
compilation time using app_conf.h file).
OpenThread stack trace levels are dynamically configurable via OpenThread APIs using
otSetDynamicLogLevel().
12.1 Thread_Cli_Cmd
This application shows how to control the OpenThread stack via Command lines.
CLI commands are sent via a UART from an HyperTerminal (PC) to the
STM32WBxx_NUCLEO board.
This is the application used for certification process.
12.2 Thread_Coap_DataTransfer
This application demonstrates how to transfer large blocks of data using CoAP messaging
protocol.
In this application the mesh-local scope and multicast addressing types are used to probe
the mesh-local IP addresses of the child devices to which the file is transferred.
Nodes are split into two forwarding roles: router or end device.
In this application, which uses two devices, one acting as a leader (router) and the other one
as an end device (child mode).
After the reset of the two boards, one of them is in leader mode (green LED2 on) and the
other one in child mode (red LED3 on).
Once the child mode is established for one of the devices, it starts the provisioning
procedure in multicast mode to probe the IP address of the leader device.
Then this is used to start the file transfer procedure in unicast mode, the successful
operation is signaled by the lightening of the blue LED.
12.3 Thread_Coap_Generic
This application demonstrates the use of CoAP messages. It provides the abstraction level
to send CoAP multicast request.
This application requires two STM32WB boards. The objective is to demonstrate both
boards exchanging CoAP messages with each other. In this application, one board is acting
as leader and the other one is acting as end device or router.
12.4 Thread_Coap_Multiboard
This application shows how to use CoAP to send messages to multiple boards in a unicast
way. It is designed to use from two to five STM32WBxx_NUCLEO boards.
The purpose of this application is to create a small Thread mesh network.
When the setup is correctly configured, a CoAP request is automatically and continuously
transferred from one board to next in the following order:
• Board 1 → Board 2 → (...) → Board n → Board 1 → …
12.5 Thread_Commissioning
This application demonstrates the commissioning process between a commissioner and a
joiner.
It shows a device distributing its Thread parameters (channel, panid, masterkey) to another
device using to the commissioning process.
This application requires two STM32WBxx_NUCLEO boards.
One device acts as commissioner and the other one as joiner.
In this application, the commissioner accepts a newcomer in its Thread network.
12.6 Thread_FTD_Coap_Multicast
This application demonstrates the use of CoAP multicast message for Full Thread devices.
Note: To be used with Thread FTD CPU2 binary.
This application uses two devices, one device acts as a leader (router) and the other one as
end device (child mode).
After the reset of the two boards (named respectively A and B), one board is in leader mode
(green LED2 on), the other one in child mode (red LED3 on).
To send a CoAP command from board A to board B, press the SW1 pushbutton on board A.
Board B receives the CoAP command to turn on its blue LED1. Pressing again the same
push-button turns the blue LED1 off.
Same CoAP commands can be sent from board B to board A.
12.7 Thread_SED_Coap_Multicast
This application demonstrates the use of CoAP multicast message sent from a sleepy end
device.
Note: To be used with Thread MTD CPU2 binary.
Two boards are needed for the proposed use case:
• One board acts as a leader (router) in FTD mode (board A)
• The other one acts as a SED in MTD mode (board B).
The board acting as a leader must be flashed with the FTD application: use the application
“Thread_FTD_Coap_Multicast” + Thread FTD binary on CPU2.
The board acting as a SED must be flashed with the MTD application
“Thread_SED_Coap_Multicast” + Thread MTD binary on CPU2.
After the two boards are reset, one board (A) automatically reaches the leader mode (green
LED2 on) and the other one (B) the SED mode (red LED3 on) a few seconds later.
At this stage, these two boards belong to the same Thread network and device 2 sends a
CoAP multicast request every second to device 1 to turn on/off its blue LED.
12.8.1 Principle
The goal is to use Thread protocol to update CPU1 application binary or CPU2 wireless
coprocessor binary on a remote device.
CPU1: Thread_Ota
CPU2: Thread_FTD or Thread_MTD
...
MS52679V1
This thread requires at least two STM32WBxx boards (see Figure 46) running Thread
protocol with specific applications:
• one board running Thread_Ota_Server application
• one or more boards running Thread_Ota application
FUOTA process can take place only on one device at a time.
The server initiates a FUOTA provisioning process and one client must respond to it.
Multiple clients are updated one at the time.
Wireless FW
SFSA (security boundary set by OB)
Client side
On the client side, before receiving the binary from the server, the flash memory is as shown
in Figure 48.
Wireless FW
SFSA (security boundary set by OB)
Non secured
FLASH_BASE + 0x10000
Thread_Ota
FLASH_BASE
MS52681V1
After it has received the binary data from server side, the flash memory is updated as shown
in Figure 49 and Figure 50, respectively, for CPU1 binary transfer and CPU2 binary transfer.
Figure 49. FUOTA server flash memory mapping after CPU1 binary transfer
FLASH_BASE + FLASH_SIZE
FUS
Secured
Wireless FW
SFSA (security boundary set by OB)
Free
Encrypted coprocessor
wireless binary has been
Non secured
Figure 50. FUOTA server flash memory mapping after CPU2 binary transfer
FLASH_BASE + FLASH_SIZE
FUS
Secured
Wireless FW
SFSA (security boundary set by OB)
Free
Encrypted coprocessor
wireless binary has been
Non secured
FUOTA_PROVISIONING
First step: Provisioning
(get address of the
device to be updated) Address of the device (MeshLocalEid)
No
End of transfer?
At the end of binary transfer
Yes (magic keyword found in
received data) start
Transfer completed boot/startup procedure
MS52685V1
1. Server sends a message to record the address of the remote device on which FUOTA
is processed.
Server sends a multicast, Non-Confirmable, Get CoAP request on resource:
“FUOTA_PROVISIONING”
The remote device answers with the Mesh Local Eid (Endpoint IDentifier), which
identifies a Thread interface, independent from the network topology.
2. OtaContext data structure is sent to the remote device. It contains:
– File type: FW_APP update or FW_COPRO_WIRELESS update
– Binary size: size in bytes of the binary to be transferred
– Base address: start address in flash memory for remote device to copy binary data
to
– Magic keyword: keyword specifying end of the binary
3. Transfer binary to remote device.
The transfer is performed by buffers of FUOTA_PAYLOAD_SIZE (default 400 bytes).
This is configurable on Server side.
For each transfer, Thread_Ota_Server waits for acknowledgment from the remote
device before continuing the next buffer transfer.
On the remote side, each data buffer received is written to flash memory.
When magic keyword is found, it means that this is the last buffer to transmit.
Boot
No FW No
application SW reset source
available
Yes Yes
MS52683V1
Boot
CPU2 APPE_SysEvtReadyProcessing
No SRAM1_BASE =
CFG_REBOOT_ON_CPU2
_UPGRADE
Run OTA Reboot on FW
application application
MS52684V1
12.8.5 Applications
Thread_Ota_Server
This application must be loaded on STM32WB 1Nucleo board acting as FUOTA server.
Thread_Ota
This application must be loaded on STM32WB Nucleo board acting as FUOTA client.
Thread_Coap_Generic_Ota
This application is almost identical to Thread_Coap_Generic, the differences are:
• Use special tags (to manage end data transfer and data consistency):
– TAG_OTA_END: the Magic keyword value is checked in the thread_ota
application
– TAG_OTA_START: the Magic keyword address shall be mapped at 0x140 from
start of the binary image
Therefore, by reading memory content at 0x140 it must be equal to the Magic keyword
value.
• Scatter file must be updated to place the sections above
Example for IAR:
Vector table and ROM start @ moved to 0x08010000:
define symbol __ICFEDIT_intvec_start__ = 0x08010000;
define symbol __ICFEDIT_region_ROM_start__ = 0x08010000;
define region OTA_TAG_region = mem:[from
(__ICFEDIT_region_ROM_start__ + 0x140) to
(__ICFEDIT_region_ROM_start__ + 0x140 + 4)];
keep { section TAG_OTA_START};
keep { section TAG_OTA_END };
place in OTA_TAG_region { section TAG_OTA_START };
place in ROM_region { readonly, last section TAG_OTA_END };
13.1 Overview
MAC IEEE Std 802.15.4-2011 layer is embedded by the MAC dedicated firmware running
on CPU2 core (Radio protocol processor). The MAC layer relies on the PHY layer, which
addresses the RF subsystem component.
As this implementation is provided in binary format and running on CPU2, the MAC API is
exposed to CPU1 core to let user address MAC service access points. The user can then
set up its STM32WB device as an FFD (full feature device, or coordinator), or as an RFD
(reduced feature devices, or nodes) as described in IEEE Std 802.15.4-2011 specification
document.
13.2 Architecture
Figure 54 shows the MAC software architecture used when the customer expects to
implement an in house 802.15.4 network by integrating a custom solution or a third party
solution on the application processor.
13.3 API
The MAC IEEE Std 802.15.4-2011 specification document defines an interface between
802.15.4 network layers and the medium access control layer. This API allows the user to
address the MAC management entity service called MLME (MAC sub - Layer management
entity) as the MAC data service called MCPS (MAC common part sub layer entity).
A MAC API dedicated to the application core with its associated implementation is available
from the middleware provided under \Middlewares\ST\STM32_WPAN\mac_802_15_4 (see
Figure 55).
A readme.txt file describes the MAC sequence handled by each 802.15.4 devices. The files
are available from each root projects.
13.4.4 Output
The user can use an OTA sniffer, on the right channel, to listen to the negotiation between
the two boards during the association phase and to watch the data exchange once the node
is registered in network managed by the coordinator.
Application traces are routes to the UART. User can then start a HyperTerminal session,
using a preferred terminal emulator, on each of the implemented Virtual COM port to check
every MAC step.
The TTY Session configuration to connect console:
• Baud: 115200
• Data bits: 8
• Stop bits: 1
• Parity: None
• Flow control: XON/XOFF.
Running the two applications leads to the Hyper terminal shown in figures 59 to 61.
Service provider
MS52425V1
The proposed API lets the user call REQ and RES primitives with associated defined
structure initialized from the upper layer. To get notification from the MAC layer, custom call
function have to be implemented called MAC indication (IND) or MAC confirmation (CNF).
memcpy(AssociateRes.a_device_address,g_MAC_associateInd.a_device_address,0
x08);
memcpy(AssociateRes.a_assoc_short_address,&shortAssociationAddr,0x08);
AssociateRes.security_level = 0x00;
AssociateRes.status = MAC_SUCCESS;
MacStatus = MAC_MLMEAssociateRes(&AssociateRes);
memcpy(&g_DataInd,pDataInd,sizeof(MAC_dataInd_t));
return MAC_SUCCESS;
14 Annexes
APPE_Init()
Initialization of the system
and memory channel
to the CPU2
TL_Init()
Initialization of the
reference table shared
with the CPU2 in SRAM2
shci_init()
Initialization of the
system transport layer
TL_SYS_Init()
Initialization of the system
channel to the CPU2
TL_MM_Init()
Initialization of the memory
channel to the CPU2
TL_Enable()
Starts CPU2
While loop
MS51885V1
When CPU2 is ready to receive system commands, it sends a notification to CPU1. Upon
the recieving the notification shci_notify_asynch_evt(), the user must call
shci_user_evt_proc() to allow the system transport layer to process the event. The user
application is notified with APPE_SysUserEvtRx() that a system event is received. As the
APPE_SysUserEvtRx()
MS51886V1
APP_BLE_Init()
hci_init()
transport layer
TL_BLE_Init()
OR
TL_BLE_Init()
CPU2 runs HCI
only interface
SHCI_C2_BLE_Init()
shci_send()
TL_SYS_SendCMd()
Command sent over the
system channel to start
the BLE stack
MS52621V1
On receiving a system event, the BLE transport layer is initialized and a system command is
sent to CPU2 to start the BLE stack. As soon as the SHCI_C2_BLE_Init() system command
has been sent to CPU2, CPUit is ready to receive BLE commands.
When CPU2 runs an HCI only interface, the BLE transport layer starts running in the Host
BLE stack on CPU1. Therefore, the BLE transport layer provided must not be used and
initialized.
User tl_mbox.c
TL_Init()
TL_SYS_Init()
TL_MM_Init()
TL_Enable()
Starts CPU2
MS51881V1
TL_BLE_Init()
MS51889V1
TL_BLE_SendCmd()
IoBusEvtCallBack
MS51890V1
TL_BLE_SendAclData()
IoBusAclDataTxAck
MS51891V1
The user must wait for the acknowledge received with IoBusAclDataTxAck before sending a
new ACL data packet. The IoBusAclDataTxAck is generated asynchronously in the IPCC
interrupt context. It is recommended, depending on the processing load, to implement a
background mechanism to handle the acknowledgment (out of the IPCC interrupt context).
The ACL data packet interface is supported only in HCI Mode. When supported, it is
possible to send ACL data packets while a BLE command is pending. The BLE command
and ACL data packets do not share resources.
TL_SYS_SendCmd()
IoBusCallBackCmdEvt
MS51892V1
Figure 72. BLE and system user event received by the mailbox
User tl_mbox.c User tl_mbox.c
IPCC interrupt context IPCC interrupt context
IoBusEvtCallBack IoBusCallBackUserEvt
TL_MM_EvtDone() TL_MM_EvtDone()
MS51893V1
shci_init()
Initialization of the
system transport layer
shci_register_io_bus()
Register the Mailbox interface
• TL_SYS_Init
• TL_SYS_SendCmd
TL_SYS_Init()
MS52619V1
StatusNotCallBack(SHCI_TL_CmdBusy)
TL_SYS_SendCmd()
Command sent over
the system channel
to the CPU2
shci_cmd_resp_wait()
Wait for
the command response
IoBusCallBackCmdEvt
interrupt
context
IPCC
shci_cmd_resp_release()
StatusNotCallBack(SHCI_TLCmdAvailable)
MS52622V1
SHCI_C2_xxx()
The list of supported system commands that can be used by the application is in the file
shci.h.
shci_notify_asynch_evt()
shci_user_evt_proc()
SHciAsynchEventQueue is empty
Read from the received packet
from the SHciAsynchEventQueue
UserEvtRx()
Do this until
Handle the received
user event
TL_MM_EvtDone()
MS52623V1
void shci_user_evt_proc(void):
This function reports the received event to the user with UserEvtRx(). As the received event
queue SHciAsynchEventQueue is filled within the IPCC interrupt context, new events can
be stored in the queue while the user is processing an event. UserEvtRx() is called for each
event retrieved from the queue. The shci_user_evt_proc() process frees the buffer to CPU2
memory manager for each return of UserEvtRx().
User shci_tl.c
shci_user_evt_proc()
UserEvtRx()
Status = SHCI_TL_UserEventFlow_Disable
Reject the event (impossible to handle it now)
shci_resume_flow()
shci_notify_asynch_evt()
MS52620V1
system transport layer does not release the system event and does not report any new
incoming events.
When the user is ready to process a system event, it must send the shci_resume_flow() that
informs the system transport layer to restart reporting of system event.
void hci_init(void(*
UserEvtRx)(void* pData), Initializes the BLE transport layer
void* pConf);
void
hci_register_io_bus(tHciIO* Registers the mailbox interface to the BLE transport layer
fops);
void
hci_notify_asynch_evt(void* Requests the user to call hci_user_evt_proc
pdata);
Resumes the asynchronous user event reporting when
void hci_resume_flow (void)
stopped by the user
void
hci_cmd_resp_wait(uint32_t Waits for a command response
timeout)
void
hci_cmd_resp_release(uint32_t Notifies that a command response has been received
flag)
Process the received asynchronous user event and call
void hci_user_evt_proc(void
UserEvtRx
user tl_mbox.c
TL_Init()
TL_SYS_Init()
TL_MM_Init()
TL_Enable()
Start CPU2
MS52625V1
StatusNotCallBack(HCI_TL_CmdBusy)
TL_BLE_SendCmd()
Command sent over
the BLE channel to
the BLE stack
hci_cmd_resp_wait()
Wait for
the command response
IoBusEvtCallBack
interrupt
context
IPCC
hci_cmd_resp_release()
StatusNotCallBack(HCI_TLCmdAvailable)
MS52633V1
ACI_xxx() / HCI_LE_xxx()
The list of supported system commands that can be used by the application is in the folder
\Middlewares\ST\STM32_WPAN\ble\core\Inc\core.
IoBusCallBackUserEvt
IPCC interrupt
context
Chain the received packet in the
HciAsynchEventQueue
hci_notify_asynch_evt()
hci_user_evt_proc()
HciAsynchEventQueue is empty
Read from the received packet
from the HciAsynchEventQueue
UserEvtRx()
Do this until
Handle the received
user event
TL_MM_EvtDone()
MS52623V2
void hci_user_evt_proc(void):
This function reports the received event to the user throughUserEvtRx(). As the received
event queue HciCmdEventQueue is filled within the IPCC interrupt context, new events can
be stored in the queue while the user is processing an event. UserEvtRx() is called for each
event retrieved from the queue. The hci_user_evt_proc() process is responsible for freeing
the buffer to the CPU2 memory manager on each UserEvtRx() return.
UserEvtRx()
Status = SHCI_TL_UserEventFlow_Disable
Reject the event (impossible to handle it now)
hci_resume_flow()
hci_notify_asynch_evt()
MS52626V1
When the user is ready to process BLE user event, it must send the hci_resume_flow() that
informs the HCI transport layer to resume BLE user event reporting.
The result of these commands is a Command_complete event with 0xFDOE code, and
according to the command, the following result bytes for:
LHCI_C1_Write_Register:
Byte[0]: Status
LHCI_C1_Read_Register:
Byte[0]: Status
Byte[1:4]: Value
LHCI_C1_Read_Device_Information:
Byte[0]: Status
Byte[1:2]: Revision ID (from DBGMCU_ICODE register)
Bytes[3:4]: Device code ID (from DBGMCU_ICODE register)
Byte[5]: Package type (from package data register)
Byte[6]: Device type ID (from FLASH UID64)
Bytes[7:10]: ST company ID (from FLASH UID64)
14.6.1 Commands
SHCI_C2_FUS_GetState() 0xFC52
SHCI_C2_FUS_FwUpgrade() 0xFC54
SHCI_C2_FUS_FwDelete() 0xFC55
SHCI_C2_FUS_UpdateAuthKey() 0xFC56 Refer to [6].
The following commands are supported by both the
SHCI_C2_FUS_LockAuthKey() 0xFC57 FUS and the wireless firmware:
SHCI_C2_FUS_StoreUsrKey()(2) 0xFC58 – SHCI_C2_FUS_StoreUsrKey
– SHCI_C2_FUS_LoadUsrKey
SHCI_C2_FUS_LoadUsrKey() 0xFC59
– SHCI_C2_FUS_LockUsrKey
SHCI_C2_FUS_StartWs() 0xFC5A – SHCI_C2_FUS_UnloadUsrKey
SHCI_C2_FUS_LockUsrKey() 0xFC5D
SHCI_C2_FUS_UnloadUsrKey() 0xFC5E
SHCI_C2_FUS_ActivateAntiRollback() 0xFC5F
Sends the BLE Init parameters. Must be sent before
SHCI_C2_BLE_Init() 0xFC66 any ACI command. Refer to Section 8.6.9: How to
maximize data throughput for more details.
Refer to Section 10.8: System commands for Thread
SHCI_C2_THREAD_Init() 0xFC67
applications.
SHCI_C2_Reinit()
On reset, the CPU2 is started with the TL_Enable() command and when it has finalized its
initialization, it reports the SHCI_SUB_EVT_CODE_READY system event. Once it has
started, it cannot be reset to restart its startup sequence.
As result, when an application is started from a primary boot application that has already
started the CPU2, the application never receives the SHCI_SUB_EVT_CODE_READY
system event because it has already been reported to the primary boot application.
The SHCI_C2_Reinit() must be sent by the primary boot application (if it has started the
CPU2) just before jumping to the main application that is expected to receive the
SHCI_SUB_EVT_CODE_READY system event.
When the CPU2 receives the SHCI_C2_Reinit() command, the following steps are executed
• Execute both __SEV() and __WFE() instructions to set and clear the event register
• Enable the EXTI rising edge for the line41 (C1SEV interrupt to CPU2)
• Send response of the command to the CPU1
• Execute WFE and wait for the CPU1 event
• On wake-up from WFE, restart startup code
This command does not reset the hardware, but requires the CPU2 to restart from its reset
vector.
SHCI_C2_FUS_StoreUsrKey()
To store user keys in flash memory, one or more SHCI_C2_FUS_StoreUsrKey commands
need to be called. Since the request comes from CPU1, the Sem2 shall be taken to take the
ownership of flash IP before calling the command and release once all commands have
been sent.
14.6.2 Events
The events listed in Table 35 can be enabled/disabled with the SHCI_C2_Config() system
command
Preferred PHY is 2M
By default 1M link is established hci_le_set_default_phy
hci_le_set_default_phy (0x00,0x02,0x02)
(0x00, 0x02, 0x02)
ADV_IND
aci_gap_IO_capability
aci_gap_IO_capacity (display yes / no) (display yes / no)
aci_gap_set_auth
_requirement (MITM, no
aci_gap_set_auth_requirement (MITM, fixed pin, bonding,
no fixed pin,bounding, SC_Support = SC_Support = SC only
SC only mode, keypress notification mode, keypress notification
not supported) not supported)
CONNECT_REQ
aci_gap_pro_create_connection aci_gap_set_discoverable
(peripheral address)
aci_gap_proc
_complete_event
aci_gap_pro_complete_event
hci_le_phy
(conn_handle, 0x00, 0x02, 0x02, 0)
LL_PHY_REQ
EVT_LE_PHY_UPDATE_COMPLETE LL_PHY_RESP
Link 2M is established
EVT_LE_PHY_UPDATE
_COMPLETE
Figure 82. Master initiates the connection update with HCI command
Connection established
aci_gap_start_connection_
update LL_CONNECTION_UPDATE_IND
PREPARE_WRITE_RESP
hci_le_connection_update
hci_le_connection_update
_complete_event
_complete_event
Once a connection is established, it is possible for the slave to update the connection
parameters with aci_l2cap_connection_parameter_update_req command.
Figure 83. Slave initiates the connection update with L2CAP command
Connection established
hci_le_connection_update
Connection _parameter_update_request _complete_event
aci_gap_start_connection_
update
Connection _parameter_update_response
hci_le_connection_update hci_le_connection_update
_complete_event _complete_event
14.10.1 Introduction
The Thread stack is an open standard for reliable, cost-effective, low-power, wireless D2D
communication. It is designed specifically for connected home applications where IP-based
networking is desired and a variety of application layers can be used on the stack.
The full specification ([9]) is available on http://threadgroup.org/.
This standard is based on the IEEE 802.15.4 [IEEE802154] PHY (physical) and MAC
(media access control) layers operating at 250 kbps in the 2.4 GHz band.
14.10.3 Layers
Thread is based on mature and well proven standards, as shown in Figure 86.
Thread Standard
Application layer
RFC 768, RFC 6347, RFC 4279,
UDP + DTLS RFC 4492, RFC 3315, RFC 5007
MS52426V1
size is limited to 127 bytes. For this, Thread uses the 6LowPan layer. This layer
implements two techniques:
– Fragmentation (splitting the packets in small TX pieces, and reassembling them in
RX)
– Header compression (in some cases, a header of 48 bytes can be compressed
into a header of only 6 bytes).
Sender Receiver
IPv6 IPv6
6LoWPAN 6LoWPAN
be very robust and is able to manage dynamic routing adaptation. Routers periodically
send advertisement messages, which contain the following parameters:
– Link quality between the sender and its neighbors
– Route cost to access to all routers in the Thread network partition.
All routers contain a table with the link quality in UL and DL with all its neighboring
routers and the routing cost for all the routers present inside the mesh network.
In this table, there are also the “next hops” defining how to travel all over the network
and the so called “age” value, which represents the elapsed time since the latest
advertisement reception.
The quality link is a value comprised between 0 and 3, 3 being the best quality (when
the signal strength recorded is above 20 dB). Having only four possible link quality
values minimizes the overhead of communicating the link quality with neighbors.
Routers acting as leaders maintain an additional database for tracking router ID
assignments and the extended address associated with each router.
• Application layer: Thread supports CoAP, and this protocol acts as the application layer
in our design. CoAP can be considered as a very light version of http protocol. It
requires far less resources than http and has a very low overhead. Like http, CoAP is
based on the REST model: servers make resources available under a URL, and clients
access these resources using requests such as Get, Put, Post, and Delete. The URL
(uniform resource locator) specifies the resources and the way to access them. There
are four types of messages:
– Non confirmable message
– Confirmable message
– Acknowledgment message
– Reset message.
The size of the Mesh network is configurable. There is a maximum of 32 active routers.
Each router can be connected to different child devices. Each child ID is coded on 9 bits,
resulting in a theoretical maximum number of 511 children per router. Because of memory
constraints on STM32WB the number of child per router is limited to 10.
Leader
Router
End device
MS52439V1
Thread network
STM32WB
STM32WB
WiFi® or Ethernet
Border router
STM32WB
MS52022V1
REED
Router
Router
Leader
EndDevice
Router
SleepyEndDevice
MTD
FTD
MS52429V1
15 Conclusion
Bluetooth Low Energy (BLE) or 802.15.4 applications based on for STM32WB series
microcontrollers require the understanding of dedicated software protocols and
architectures.
This document details how the developer must create the embedded application software, a
key element is to follow the correct procedure for the system initialization.
16 Revision history
Added Section 7.6.2: How to set IRK (identity root key) and ERK
(encryption root key), Section 8.6.6: BLE GATT DB and security record in
NVM and Section 8.6.7: How to calculate the maximum number of
bonded devices that can be stored in NVM.
14-Dec-2021 6
Updated Section 8.6.1: How to set Bluetooth device address,
CFG_BLE_ATT_VALUE_ARRAY_SIZE, Section 14.2: Mailbox interface,
SHCI_C2_xxx(), and ACI_xxx() / HCI_LE_xxx().
Updated Table 31: Interface APIs.
Updated Section 4.3: Shared peripherals, Section 4.6: Low power
manager, and Section 8.2.2: STM32WB heart rate sensor application -
Middleware application.
15-Jul-2022 7 Added Section 8.6.8: NVM write access.
Updated Figure 24: Heart rate project - Interaction between middleware
and user application.
Minor text edits across the whole document.
Updated CFG_BLE_OPTIONS.
Added Section 7.6.11: How to switch from 32 to 64 MHz and
24-Nov-2022 8
Section 7.6.12: How to re-enable the PLL when exiting low power mode.
Minor text edits across the whole document.
Added Section 14.5: Vendor specific HCI commands for controller.
11-Apr-2023 9
Minor text edits across the whole document.
Updated document title.
Updated Security attack, CFG_BLE_HSE_STARTUP_TIME, and
18-Jul-2023 10 SHCI_C2_xxx().
Updated Table 34: System interface commands.
Minor text edits across the whole document.
Updated Table 2: Semaphores.
18-Aug-2023 11 Updated Section 4.3: Shared peripherals.
Minor text edits across the whole document.
Updated Section 8.6.2: How to set IR (Identity Root) and ER (Encryption
25-Sep-2023 12
Root).
Updated Section 4.7.3: Conflict between RF activity and flash memory
management and Section 8.6.8: NVM write access.
Updated Table 34: System interface commands.
24-Oct-2023 13 Added note in Section 5.2: CPU2 startup, Section 6: PLL management
and its subsections, Section 8.6.11: How to use BLE commands in
blocking mode, and How to use the system command in polling mode.
Removed former Section 7.6.11: How to switch from 32 to 64 MHz and
Section 7.6.12: How to re-enable the PLL when exiting low power mode.
Updated Section 14.6.1: Commands.
27-Nov-2023 14
Updated Table 34: System interface commands.
Updated Table 23: FUOTA service and characteristics UUID and
04-Dec-2023 15
Table 24: Base address characteristics specification.
07-Feb-2024 16 Updated Section 6.1.2: Case 2: CPU2 is started.
STMicroelectronics NV and its subsidiaries (“ST”) reserve the right to make changes, corrections, enhancements, modifications, and
improvements to ST products and/or to this document at any time without notice. Purchasers should obtain the latest relevant information on
ST products before placing orders. ST products are sold pursuant to ST’s terms and conditions of sale in place at the time of order
acknowledgment.
Purchasers are solely responsible for the choice, selection, and use of ST products and ST assumes no liability for application assistance or
the design of purchasers’ products.
Resale of ST products with provisions different from the information set forth herein shall void any warranty granted by ST for such product.
ST and the ST logo are trademarks of ST. For additional information about ST trademarks, refer to www.st.com/trademarks. All other
product or service names are the property of their respective owners.
Information in this document supersedes and replaces information previously supplied in any prior versions of this document.