Compare commits
15 Commits
gmap-testi
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd84f76b59 | ||
|
|
066fe6201b | ||
|
|
e736be6ca4 | ||
|
|
6bb8af2a3f | ||
|
|
ce657eb852 | ||
|
|
db4e0e32c7 | ||
|
|
cf3d9e613a | ||
|
|
4bfe9c07f6 | ||
|
|
5b94c9522d | ||
|
|
779c31f97d | ||
|
|
a82a9ff85a | ||
|
|
2a89491008 | ||
|
|
caffd7b8a1 | ||
|
|
b7dd60fd52 | ||
|
|
16f411c7dc |
@@ -9,8 +9,14 @@ set(SRCS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
|
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
|
||||||
)
|
)
|
||||||
|
|
||||||
if (CONFIG_AUDIO_SYNC_TIMER_USES_RTC)
|
if (CONFIG_SOC_COMPATIBLE_NRF52X)
|
||||||
list(APPEND SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/audio_sync_timer_rtc.c)
|
target_sources(app PRIVATE src/controller_time_nrf52.c)
|
||||||
|
elseif (CONFIG_SOC_COMPATIBLE_NRF5340_CPUAPP)
|
||||||
|
target_sources(app PRIVATE src/audio_sync_timer_rtc.c)
|
||||||
|
elseif (CONFIG_SOC_SERIES_NRF54LX OR CONFIG_SOC_SERIES_NRF54HX)
|
||||||
|
target_sources(app PRIVATE src/controller_time_nrf54.c)
|
||||||
|
else()
|
||||||
|
MESSAGE(FATAL_ERROR "Unsupported series")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_sources(app PRIVATE ${SRCS})
|
target_sources(app PRIVATE ${SRCS})
|
||||||
|
|||||||
2
Kconfig
2
Kconfig
@@ -4,7 +4,7 @@ menu "Modules"
|
|||||||
rsource "Kconfig.defaults"
|
rsource "Kconfig.defaults"
|
||||||
|
|
||||||
config AUDIO_SYNC_TIMER_USES_RTC
|
config AUDIO_SYNC_TIMER_USES_RTC
|
||||||
bool "Enables syncronization between the APP and NET core running SD"
|
bool "Enables synchronization between the APP and NET core running SD"
|
||||||
default !BT_LL_ACS_NRF53 && SOC_SERIES_NRF53X
|
default !BT_LL_ACS_NRF53 && SOC_SERIES_NRF53X
|
||||||
select NRFX_RTC0
|
select NRFX_RTC0
|
||||||
select NRFX_DPPI
|
select NRFX_DPPI
|
||||||
|
|||||||
10
Kconfig.sysbuild
Normal file
10
Kconfig.sysbuild
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2024 Nordic Semiconductor
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
#
|
||||||
|
|
||||||
|
config NRF_DEFAULT_IPC_RADIO
|
||||||
|
default y
|
||||||
|
|
||||||
|
source "${ZEPHYR_BASE}/share/sysbuild/Kconfig"
|
||||||
129
README.md
129
README.md
@@ -15,18 +15,99 @@ It has been tested on the nRF5340 Audio DK, but it should work with any nRF5340
|
|||||||
- Response: HCI Command Complete Event with status and 4 bytes timestamp in microseconds
|
- Response: HCI Command Complete Event with status and 4 bytes timestamp in microseconds
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## nRF58233 Development Kit
|
||||||
|
|
||||||
|
The first Virtual UART (UART1, ...) is Zephyr UART 0
|
||||||
|
The second Virtual UART (UART2, ...) is Zephyr UART 1
|
||||||
|
|
||||||
|
### Pinout
|
||||||
|
|
||||||
|
Signal direction as seen from the nRF52833.
|
||||||
|
|
||||||
|
| PIN | Arduino | MCU | Direction |
|
||||||
|
|----------|---------|-------|-----------|
|
||||||
|
| TX | D0 | P0.05 | out |
|
||||||
|
| RX | D1 | P0.06 | in |
|
||||||
|
| RTS | D7 | P0.07 | out |
|
||||||
|
| CTS | D8 | P0.08 | in |
|
||||||
|
| Time Sync| D10 | P1.01 | out |
|
||||||
|
|
||||||
|
### HCI over UART 0 connected to first Virtual UART in J-Link Probe
|
||||||
|
|
||||||
|
Release build:
|
||||||
|
```sh
|
||||||
|
west build --pristine -b nrf52833dk/nrf52833
|
||||||
|
```
|
||||||
|
Debug build:
|
||||||
|
```sh
|
||||||
|
west build --pristine -b nrf52833dk/nrf52833 -- -DOVERLAY_CONFIG=debug.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
To use UART 0 via Arduino headers, the virtual UART of the J-Link probe needs to be disabled, e.g. with the JLink Configuration Tool.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## nRF5340 Development Kit
|
## nRF5340 Development Kit
|
||||||
|
|
||||||
|
The first Virtual UART (UART1, ...) is Zephyr UART 1
|
||||||
|
The second Virtual UART (UART2, ...) is Zephyr UART 0
|
||||||
|
|
||||||
|
### Pinout
|
||||||
|
|
||||||
|
Signal direction as seen from the nRF5340.
|
||||||
|
|
||||||
|
| PIN | Arduino | MCU | Direction |
|
||||||
|
|----------|---------|-------|-----------|
|
||||||
|
| TX | D0 | P1.00 | out |
|
||||||
|
| RX | D1 | P1.01 | in |
|
||||||
|
| RTS | D7 | P1.11 | out |
|
||||||
|
| CTS | D8 | P1.10 | in |
|
||||||
|
| Time Sync| D10 | P1.06 | out |
|
||||||
|
|
||||||
### HCI over USB CDC
|
### HCI over USB CDC
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
west build -b nrf5340dk/nrf5340/cpuapp -- -DEXTRA_DTC_OVERLAY_FILE=usb.overlay -DOVERLAY_CONFIG=overlay-usb.conf
|
west build --pristine -b nrf5340dk/nrf5340/cpuapp -- -DEXTRA_DTC_OVERLAY_FILE=usb.overlay -DOVERLAY_CONFIG=overlay-usb.conf
|
||||||
```
|
```
|
||||||
_HCI over UART and timesync not tested._
|
|
||||||
|
|
||||||
|
### HCI over UART 0 connected to second Virtual UART in J-Link Probe
|
||||||
|
```sh
|
||||||
|
west build --pristine -b nrf5340dk/nrf5340/cpuapp
|
||||||
|
```
|
||||||
|
|
||||||
|
- HCI over second Virtual port / UART 0
|
||||||
|
- Boot banner on Arduino Header UART (P1.01) / UART 1
|
||||||
|
|
||||||
|
|
||||||
|
### HCI over UART 1 connected to first Virtual UART in J-Link Probe as well as Arduino Headers
|
||||||
|
```sh
|
||||||
|
west build --pristine -b nrf5340dk/nrf5340/cpuapp -- -DEXTRA_DTC_OVERLAY_FILE=uart1.overlay
|
||||||
|
```
|
||||||
|
|
||||||
|
- No Boot Banner on Arduino Header UART
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## nRF5340 Audio DK
|
## nRF5340 Audio DK
|
||||||
|
|
||||||
|
The first UART (UART1, P1.04, P1.05) is only available via test points (TP60, TP61, ...)
|
||||||
|
The second UART (UART2, P1.08, P1.09) is connected to the Arduino headers.
|
||||||
|
|
||||||
|
### Pinout
|
||||||
|
|
||||||
|
Signal direction as seen from the nRF5340.
|
||||||
|
|
||||||
|
| PIN | Arduino | MCU | Direction |
|
||||||
|
|----------|---------|-------|-----------|
|
||||||
|
| TX | D0 | P1.09 | out |
|
||||||
|
| RX | D1 | P1.08 | in |
|
||||||
|
| RTS | D7 | P1.11 | out |
|
||||||
|
| CTS | D8 | P1.10 | in |
|
||||||
|
| Time Sync| D10 | P1.01 | out |
|
||||||
|
|
||||||
|
|
||||||
### HCI over USB CDC
|
### HCI over USB CDC
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -36,7 +117,7 @@ west build --pristine -b nrf5340_audio_dk/nrf5340/cpuapp -- -DEXTRA_DTC_OVERLAY_
|
|||||||
### HCI over UART 0 connected to Virtual UART in J-Link Probe
|
### HCI over UART 0 connected to Virtual UART in J-Link Probe
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
west build --pristine -b nrf5340_audio_dk_nrf5340_cpuapp
|
west build --pristine -b nrf5340_audio_dk/nrf5340/cpuapp
|
||||||
```
|
```
|
||||||
|
|
||||||
### HCI over UART 1 connected to Virtual UART in J-Link Probe as well as Arduino Headers
|
### HCI over UART 1 connected to Virtual UART in J-Link Probe as well as Arduino Headers
|
||||||
@@ -53,14 +134,38 @@ west build --pristine -b nrf5340_audio_dk/nrf5340/cpuapp -- -DEXTRA_DTC_OVERLAY_
|
|||||||
|
|
||||||
To use UART 1 via Arduino headers, the virtual UART of the J-Link probe needs to be disabled, e.g. with the JLink Configuration Tool.
|
To use UART 1 via Arduino headers, the virtual UART of the J-Link probe needs to be disabled, e.g. with the JLink Configuration Tool.
|
||||||
|
|
||||||
### UART 1 + Time Sync Pinout
|
|
||||||
|
|
||||||
Signal direction as seen from the nRF5340.
|
|
||||||
|
|
||||||
| PIN | Arduino | nrf | Direction |
|
## nRF54L15
|
||||||
|----------|---------|-------|-----------|
|
|
||||||
| TX | D0 | P1.09 | out |
|
### HCI over UART 0 connected to second Virtual UART in J-Link Probe
|
||||||
| RX | D1 | P1.08 | in |
|
|
||||||
| RTS | D7 | P1.11 | out |
|
Release build:
|
||||||
| CTS | D8 | P1.10 | in |
|
```sh
|
||||||
| Timesync | D10 | P1.01 | out |
|
west build -d nrf54l15-iso --pristine -b nrf54l15dk/nrf54l15/cpuapp
|
||||||
|
```
|
||||||
|
Debug build:
|
||||||
|
```sh
|
||||||
|
west build -d nrf54l15-iso --pristine -b nrf54l15dk/nrf54l15/cpuapp -- -DOVERLAY_CONFIG=debug.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
To use UART 0 via Arduino headers, the virtual UART of the J-Link probe needs to be disabled, e.g. with the JLink Configuration Tool.
|
||||||
|
|
||||||
|
### Pinout
|
||||||
|
|
||||||
|
Signal direction as seen from the nRF54L15.
|
||||||
|
|
||||||
|
| PIN | MCU | Direction |
|
||||||
|
|----------|-------|-----------|
|
||||||
|
| TX | P0.00 | out |
|
||||||
|
| RX | P0.01 | in |
|
||||||
|
| RTS | P0.02 | out |
|
||||||
|
| CTS | P0.03 | in |
|
||||||
|
| Time Sync| P1.11 | out |
|
||||||
|
|
||||||
|
|
||||||
|
## Maintainer Notes
|
||||||
|
- nRF5340 use Controller configuration in `sybuild/ipc_radio/prj.conf`, while others, e.g. nRF54L15, use configuration from `prj.conf`. Please update both at the same time.
|
||||||
|
- We can detect nRF5340 SoC in CMake with `if(CONFIG_SOC STREQUAL "nrf5340")` after find_package zephyr.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
9
boards/nrf52833dk_nrf52833.conf
Normal file
9
boards/nrf52833dk_nrf52833.conf
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2024 Nordic Semiconductor
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
#
|
||||||
|
|
||||||
|
CONFIG_NRFX_TIMER1=y
|
||||||
|
CONFIG_NRFX_RTC2=y
|
||||||
|
CONFIG_NRFX_PPI=y
|
||||||
@@ -4,6 +4,17 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/ {
|
||||||
|
host_interface {
|
||||||
|
compatible = "gpio-outputs";
|
||||||
|
status = "okay";
|
||||||
|
timesync: pin_0 {
|
||||||
|
gpios = <&gpio1 01 GPIO_ACTIVE_HIGH>;
|
||||||
|
label = "Controller to host timesync pin";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
&uart0 {
|
&uart0 {
|
||||||
compatible = "nordic,nrf-uarte";
|
compatible = "nordic,nrf-uarte";
|
||||||
current-speed = <1000000>;
|
current-speed = <1000000>;
|
||||||
|
|||||||
9
boards/nrf52840dk_nrf52840.conf
Normal file
9
boards/nrf52840dk_nrf52840.conf
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2024 Nordic Semiconductor
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
#
|
||||||
|
|
||||||
|
CONFIG_NRFX_TIMER1=y
|
||||||
|
CONFIG_NRFX_RTC2=y
|
||||||
|
CONFIG_NRFX_PPI=y
|
||||||
23
boards/nrf54l15dk_nrf54l15_cpuapp.overlay
Normal file
23
boards/nrf54l15dk_nrf54l15_cpuapp.overlay
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/ {
|
||||||
|
host_interface {
|
||||||
|
compatible = "gpio-outputs";
|
||||||
|
status = "okay";
|
||||||
|
timesync: pin_0 {
|
||||||
|
gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>;
|
||||||
|
label = "Controller to host timesync pin";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
&uart20 {
|
||||||
|
compatible = "nordic,nrf-uarte";
|
||||||
|
current-speed = <1000000>;
|
||||||
|
status = "okay";
|
||||||
|
hw-flow-control;
|
||||||
|
};
|
||||||
33
boards/nrf54l15dk_nrf54l15_cpuapp_df.overlay
Normal file
33
boards/nrf54l15dk_nrf54l15_cpuapp_df.overlay
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
&uart20 {
|
||||||
|
compatible = "nordic,nrf-uarte";
|
||||||
|
current-speed = <1000000>;
|
||||||
|
status = "okay";
|
||||||
|
hw-flow-control;
|
||||||
|
};
|
||||||
|
|
||||||
|
&radio {
|
||||||
|
status = "okay";
|
||||||
|
/* This is an example number of antennas that may be available
|
||||||
|
* on antenna matrix board.
|
||||||
|
*/
|
||||||
|
dfe-antenna-num = <10>;
|
||||||
|
/* This is an example switch pattern that will be used to set an
|
||||||
|
* antenna for Tx PDU (period before start of Tx CTE).
|
||||||
|
*/
|
||||||
|
dfe-pdu-antenna = <0x0>;
|
||||||
|
|
||||||
|
/* These are example GPIO pin numbers that are provided to
|
||||||
|
* Radio peripheral. The pins will be acquired by Radio to
|
||||||
|
* drive antenna switching when AoD is enabled.
|
||||||
|
*/
|
||||||
|
dfegpio0-gpios = <&gpio1 4 0>;
|
||||||
|
dfegpio1-gpios = <&gpio1 5 0>;
|
||||||
|
dfegpio2-gpios = <&gpio1 6 0>;
|
||||||
|
dfegpio3-gpios = <&gpio1 7 0>;
|
||||||
|
};
|
||||||
@@ -21,7 +21,7 @@ CONFIG_BT_CTLR_ADV_ISO_SET=2
|
|||||||
CONFIG_BT_CTLR_ADV_ISO_STREAM_COUNT=3
|
CONFIG_BT_CTLR_ADV_ISO_STREAM_COUNT=3
|
||||||
CONFIG_BT_CTLR_SYNC_PERIODIC_ADV_LIST_SIZE=1
|
CONFIG_BT_CTLR_SYNC_PERIODIC_ADV_LIST_SIZE=1
|
||||||
|
|
||||||
# Support two links as a central, or one link as a peripheral
|
# Support six links as a central, or one link as a peripheral
|
||||||
CONFIG_BT_MAX_CONN=8
|
CONFIG_BT_MAX_CONN=8
|
||||||
CONFIG_BT_CTLR_SDC_PERIPHERAL_COUNT=2
|
CONFIG_BT_CTLR_SDC_PERIPHERAL_COUNT=2
|
||||||
|
|
||||||
|
|||||||
37
prj.conf
37
prj.conf
@@ -14,25 +14,38 @@ CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE=255
|
|||||||
CONFIG_BT_CTLR_ASSERT_HANDLER=y
|
CONFIG_BT_CTLR_ASSERT_HANDLER=y
|
||||||
CONFIG_BT_MAX_CONN=16
|
CONFIG_BT_MAX_CONN=16
|
||||||
CONFIG_BT_TINYCRYPT_ECC=n
|
CONFIG_BT_TINYCRYPT_ECC=n
|
||||||
CONFIG_BT_CTLR_DTM_HCI=y
|
|
||||||
|
|
||||||
# Enable ISO support
|
# Setup ISO Buffer
|
||||||
CONFIG_BT_ISO_PERIPHERAL=y
|
|
||||||
CONFIG_BT_ISO_CENTRAL=y
|
|
||||||
CONFIG_BT_ISO_BROADCASTER=y
|
|
||||||
CONFIG_BT_ISO_TX_BUF_COUNT=10
|
CONFIG_BT_ISO_TX_BUF_COUNT=10
|
||||||
CONFIG_BT_ISO_TX_MTU=251
|
CONFIG_BT_ISO_TX_MTU=251
|
||||||
CONFIG_BT_ISO_RX_BUF_COUNT=10
|
CONFIG_BT_ISO_RX_BUF_COUNT=10
|
||||||
CONFIG_BT_ISO_RX_MTU=251
|
CONFIG_BT_ISO_RX_MTU=251
|
||||||
|
|
||||||
# setup rpmsg/Bluetooth buffer
|
# Enable ISO support
|
||||||
CONFIG_BT_BUF_EVT_RX_COUNT=16
|
CONFIG_BT_ISO_PERIPHERAL=y
|
||||||
CONFIG_BT_BUF_EVT_RX_SIZE=255
|
CONFIG_BT_ISO_CENTRAL=y
|
||||||
CONFIG_BT_BUF_ACL_RX_SIZE=255
|
CONFIG_BT_ISO_BROADCASTER=y
|
||||||
CONFIG_BT_BUF_ACL_TX_SIZE=251
|
CONFIG_BT_ISO_SYNC_RECEIVER=y
|
||||||
CONFIG_BT_BUF_CMD_TX_SIZE=255
|
CONFIG_BT_EXT_ADV=y
|
||||||
|
CONFIG_BT_PER_ADV_SYNC_TRANSFER_RECEIVER=y
|
||||||
|
CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER=y
|
||||||
|
|
||||||
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=512
|
# Configure Controller
|
||||||
|
CONFIG_BT_CTLR_CONN_ISO_GROUPS=1
|
||||||
|
CONFIG_BT_CTLR_CONN_ISO_STREAMS=3
|
||||||
|
CONFIG_BT_CTLR_SYNC_ISO_STREAM_COUNT=2
|
||||||
|
CONFIG_BT_CTLR_ADV_EXT=y
|
||||||
|
CONFIG_BT_CTLR_ADV_SET=2
|
||||||
|
CONFIG_BT_CTLR_ADV_ISO_SET=2
|
||||||
|
CONFIG_BT_CTLR_ADV_ISO_STREAM_COUNT=3
|
||||||
|
CONFIG_BT_CTLR_SYNC_PERIODIC_ADV_LIST_SIZE=1
|
||||||
|
|
||||||
|
# Support six links as a central, or one link as a peripheral
|
||||||
|
CONFIG_BT_MAX_CONN=8
|
||||||
|
CONFIG_BT_CTLR_SDC_PERIPHERAL_COUNT=2
|
||||||
|
|
||||||
|
# Allow using more than default advertising event length
|
||||||
|
CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=251
|
||||||
|
|
||||||
# Workaround: Unable to allocate command buffer when using K_NO_WAIT since
|
# Workaround: Unable to allocate command buffer when using K_NO_WAIT since
|
||||||
# Host number of completed commands does not follow normal flow control.
|
# Host number of completed commands does not follow normal flow control.
|
||||||
|
|||||||
40
src/controller_time.h
Normal file
40
src/controller_time.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
// BK: based on / taken from ncs/nrf/samples/bluetooth/conn_time_sync/src/controller_time_sync.h
|
||||||
|
|
||||||
|
#ifndef CONTROLLER_TIME_SYNC_H__
|
||||||
|
#define CONTROLLER_TIME_SYNC_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
|
||||||
|
/** @brief Obtain the current Bluetooth controller time.
|
||||||
|
*
|
||||||
|
* The timestamps are based upon this clock.
|
||||||
|
*
|
||||||
|
* @return The current controller time.
|
||||||
|
*/
|
||||||
|
uint64_t controller_time_us_get(void);
|
||||||
|
|
||||||
|
/** @brief Set the controller to trigger a PPI event at the given timestamp.
|
||||||
|
*
|
||||||
|
* @param timestamp_us The timestamp where it will trigger.
|
||||||
|
*/
|
||||||
|
void controller_time_trigger_set(uint64_t timestamp_us);
|
||||||
|
|
||||||
|
/** @brief Get the address of the event that will trigger.
|
||||||
|
*
|
||||||
|
* @return The address of the event that will trigger.
|
||||||
|
*/
|
||||||
|
uint32_t controller_time_trigger_event_addr_get(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
292
src/controller_time_nrf52.c
Normal file
292
src/controller_time_nrf52.c
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** This file implements controller time management for 52 Series devices
|
||||||
|
*
|
||||||
|
* As the controller clock is not directly accessible, the controller
|
||||||
|
* time is obtained using a mirrored RTC peripheral.
|
||||||
|
* To achieve microsecond accurate toggling, a timer peripheral is also used.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// BK: taken from ncs/nrf/samples/bluetooth/conn_time_sync/src/controller_time_nrf52.c
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/sys/barrier.h>
|
||||||
|
#include <mpsl_clock.h>
|
||||||
|
#include <nrfx_rtc.h>
|
||||||
|
#include <nrfx_timer.h>
|
||||||
|
#include <helpers/nrfx_gppi.h>
|
||||||
|
#include <hal/nrf_egu.h>
|
||||||
|
#include <soc.h>
|
||||||
|
#include "controller_time.h"
|
||||||
|
|
||||||
|
static const nrfx_rtc_t app_rtc_instance = NRFX_RTC_INSTANCE(2);
|
||||||
|
static const nrfx_timer_t app_timer_instance = NRFX_TIMER_INSTANCE(1);
|
||||||
|
|
||||||
|
static uint8_t ppi_chan_on_rtc_match;
|
||||||
|
static volatile uint32_t num_rtc_overflows;
|
||||||
|
|
||||||
|
static uint32_t offset_ticks_and_controller_to_app_rtc;
|
||||||
|
|
||||||
|
static void rtc_isr_handler(nrfx_rtc_int_type_t int_type)
|
||||||
|
{
|
||||||
|
if (int_type == NRFX_RTC_INT_OVERFLOW) {
|
||||||
|
num_rtc_overflows++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unused_timer_isr_handler(nrf_timer_event_t event_type, void *ctx)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(event_type);
|
||||||
|
ARG_UNUSED(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t rtc_diff_get(void)
|
||||||
|
{
|
||||||
|
uint32_t controller_ticks = nrf_rtc_counter_get(NRF_RTC0);
|
||||||
|
uint32_t app_ticks = nrf_rtc_counter_get(app_rtc_instance.p_reg);
|
||||||
|
|
||||||
|
return controller_ticks - app_ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rtc_config(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
const nrfx_rtc_config_t rtc_cfg = NRFX_RTC_DEFAULT_CONFIG;
|
||||||
|
|
||||||
|
ret = nrfx_rtc_init(&app_rtc_instance, &rtc_cfg, rtc_isr_handler);
|
||||||
|
if (ret != NRFX_SUCCESS) {
|
||||||
|
printk("Failed initializing RTC (ret: %d)\n", ret - NRFX_ERROR_BASE_NUM);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef BT_CTLR_SDC_BSIM_BUILD
|
||||||
|
IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_RTC_INST_GET(2)), IRQ_PRIO_LOWEST,
|
||||||
|
NRFX_RTC_INST_HANDLER_GET(2), NULL, 0);
|
||||||
|
#else
|
||||||
|
IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_RTC_INST_GET(2)), 0,
|
||||||
|
(void *)NRFX_RTC_INST_HANDLER_GET(2), NULL, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
nrfx_rtc_overflow_enable(&app_rtc_instance, true);
|
||||||
|
nrfx_rtc_tick_enable(&app_rtc_instance, false);
|
||||||
|
nrfx_rtc_enable(&app_rtc_instance);
|
||||||
|
|
||||||
|
/* To obtain the controller timestamp without modifying the state of RTC0,
|
||||||
|
* we find the offset between RTC0 and our mirrored RTC.
|
||||||
|
* We achieve this by reading out the counter value of both of them.
|
||||||
|
* When the diff between those two have been equal twice, we know the
|
||||||
|
* time difference in ticks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint32_t sync_attempts = 10;
|
||||||
|
|
||||||
|
while (sync_attempts > 0) {
|
||||||
|
sync_attempts--;
|
||||||
|
|
||||||
|
int32_t diff_measurement_1 = rtc_diff_get();
|
||||||
|
|
||||||
|
/* We need to wait half an RTC tick to ensure we are not measuring
|
||||||
|
* the diff between the two RTCs at the point in time where their
|
||||||
|
* values are transitioning.
|
||||||
|
*/
|
||||||
|
k_busy_wait(15);
|
||||||
|
|
||||||
|
int32_t diff_measurement_2 = rtc_diff_get();
|
||||||
|
|
||||||
|
if (diff_measurement_1 == diff_measurement_2) {
|
||||||
|
offset_ticks_and_controller_to_app_rtc = diff_measurement_1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printk("Controller time sync failure\n");
|
||||||
|
offset_ticks_and_controller_to_app_rtc = 0;
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int timer_config(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uint8_t ppi_chan_timer_clear_on_rtc_tick;
|
||||||
|
const nrfx_timer_config_t timer_cfg = {
|
||||||
|
.frequency = NRFX_MHZ_TO_HZ(1UL),
|
||||||
|
.mode = NRF_TIMER_MODE_TIMER,
|
||||||
|
.bit_width = NRF_TIMER_BIT_WIDTH_8,
|
||||||
|
.interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY,
|
||||||
|
.p_context = NULL};
|
||||||
|
|
||||||
|
ret = nrfx_timer_init(&app_timer_instance, &timer_cfg, unused_timer_isr_handler);
|
||||||
|
if (ret != NRFX_SUCCESS) {
|
||||||
|
printk("Failed initializing timer (ret: %d)\n", ret - NRFX_ERROR_BASE_NUM);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear the TIMER every RTC tick. */
|
||||||
|
if (nrfx_gppi_channel_alloc(&ppi_chan_timer_clear_on_rtc_tick) != NRFX_SUCCESS) {
|
||||||
|
printk("Failed allocating for clearing TIMER on RTC TICK\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
nrfx_gppi_channel_endpoints_setup(ppi_chan_timer_clear_on_rtc_tick,
|
||||||
|
nrfx_rtc_event_address_get(&app_rtc_instance,
|
||||||
|
NRF_RTC_EVENT_TICK),
|
||||||
|
nrfx_timer_task_address_get(&app_timer_instance,
|
||||||
|
NRF_TIMER_TASK_CLEAR));
|
||||||
|
|
||||||
|
nrfx_gppi_channels_enable(BIT(ppi_chan_timer_clear_on_rtc_tick));
|
||||||
|
|
||||||
|
nrfx_timer_enable(&app_timer_instance);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Configure the TIMER and RTC in such a way that microsecond accurate timing can be achieved.
|
||||||
|
*
|
||||||
|
* To get microsecond accurate toggling, we need to combine both the RTC and TIMER peripheral.
|
||||||
|
* That is, both a RTC CC value and TIMER CC value needs to be set.
|
||||||
|
* We should only trigger an EGU task when the TIMER CC value matches after the
|
||||||
|
* RTC CC value matched.
|
||||||
|
* This is achieved using a PPI group. The group is enabled when the RTC CC matches and disabled
|
||||||
|
* again then the TIMER CC matches.
|
||||||
|
*/
|
||||||
|
int config_egu_trigger_on_rtc_and_timer_match(void)
|
||||||
|
{
|
||||||
|
uint8_t ppi_chan_on_timer_match;
|
||||||
|
|
||||||
|
if (nrfx_gppi_channel_alloc(&ppi_chan_on_rtc_match) != NRFX_SUCCESS) {
|
||||||
|
printk("Failed allocating for RTC match\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nrfx_gppi_channel_alloc(&ppi_chan_on_timer_match) != NRFX_SUCCESS) {
|
||||||
|
printk("Failed allocating for TIMER match\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
nrfx_gppi_group_clear(NRFX_GPPI_CHANNEL_GROUP0);
|
||||||
|
nrfx_gppi_group_disable(NRFX_GPPI_CHANNEL_GROUP0);
|
||||||
|
nrfx_gppi_channels_include_in_group(
|
||||||
|
BIT(ppi_chan_on_timer_match) | BIT(ppi_chan_on_rtc_match),
|
||||||
|
NRFX_GPPI_CHANNEL_GROUP0);
|
||||||
|
|
||||||
|
nrfx_gppi_channel_endpoints_setup(ppi_chan_on_rtc_match,
|
||||||
|
nrfx_rtc_event_address_get(&app_rtc_instance,
|
||||||
|
NRF_RTC_EVENT_COMPARE_0),
|
||||||
|
nrfx_gppi_task_address_get(NRFX_GPPI_TASK_CHG0_EN));
|
||||||
|
|
||||||
|
nrfx_gppi_channel_endpoints_setup(ppi_chan_on_timer_match,
|
||||||
|
nrfx_timer_event_address_get(&app_timer_instance,
|
||||||
|
NRF_TIMER_EVENT_COMPARE0),
|
||||||
|
nrf_egu_task_address_get(NRF_EGU0,
|
||||||
|
NRF_EGU_TASK_TRIGGER0));
|
||||||
|
nrfx_gppi_fork_endpoint_setup(ppi_chan_on_timer_match,
|
||||||
|
nrfx_gppi_task_address_get(NRFX_GPPI_TASK_CHG0_DIS));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int controller_time_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = rtc_config();
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = timer_config();
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config_egu_trigger_on_rtc_and_timer_match();
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t rtc_ticks_to_us(uint32_t rtc_ticks)
|
||||||
|
{
|
||||||
|
const uint64_t rtc_ticks_in_femto_units = 30517578125UL;
|
||||||
|
|
||||||
|
return (rtc_ticks * rtc_ticks_in_femto_units) / 1000000000UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t us_to_rtc_ticks(uint32_t timestamp_us)
|
||||||
|
{
|
||||||
|
const uint64_t rtc_ticks_in_femto_units = 30517578125UL;
|
||||||
|
|
||||||
|
return ((uint64_t)(timestamp_us) * 1000000000UL) / rtc_ticks_in_femto_units;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t controller_time_us_get(void)
|
||||||
|
{
|
||||||
|
const uint64_t rtc_overflow_time_us = 512000000UL;
|
||||||
|
|
||||||
|
/* On the 52 series the RTC has to task to capture the current RTC value.
|
||||||
|
* Therefore we cannot capture the TIMER and RTC value simultaneously.
|
||||||
|
* Therefore we only use the RTC to read out the current time here.
|
||||||
|
* This will result in an error of maximum one RTC tick.
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint32_t captured_rtc_overflows;
|
||||||
|
uint32_t captured_rtc_ticks;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
captured_rtc_overflows = num_rtc_overflows;
|
||||||
|
|
||||||
|
/* Read out RTC ticks after reading number of overflows. */
|
||||||
|
barrier_isync_fence_full();
|
||||||
|
|
||||||
|
captured_rtc_ticks = nrf_rtc_counter_get(app_rtc_instance.p_reg);
|
||||||
|
|
||||||
|
/* Read out number of overflows after reading number of RTC ticks */
|
||||||
|
barrier_isync_fence_full();
|
||||||
|
|
||||||
|
if (captured_rtc_overflows == num_rtc_overflows) {
|
||||||
|
/* There were no new overflows after reading ticks.
|
||||||
|
* That is, we can use the captured value.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtc_ticks_to_us(captured_rtc_ticks) +
|
||||||
|
(captured_rtc_overflows * rtc_overflow_time_us) +
|
||||||
|
rtc_ticks_to_us(offset_ticks_and_controller_to_app_rtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void controller_time_trigger_set(uint64_t timestamp_us)
|
||||||
|
{
|
||||||
|
uint64_t timestamp_without_rtc_offset =
|
||||||
|
timestamp_us - rtc_ticks_to_us(offset_ticks_and_controller_to_app_rtc);
|
||||||
|
|
||||||
|
uint32_t num_overflows = timestamp_without_rtc_offset / 512000000UL;
|
||||||
|
uint64_t overflow_time_us = num_overflows * 512000000UL;
|
||||||
|
|
||||||
|
uint32_t rtc_remainder_time_us = timestamp_without_rtc_offset - overflow_time_us;
|
||||||
|
uint32_t rtc_val = us_to_rtc_ticks(rtc_remainder_time_us);
|
||||||
|
uint8_t timer_val = timestamp_without_rtc_offset - rtc_ticks_to_us(rtc_val);
|
||||||
|
|
||||||
|
/* Ensure the timer value lies between 1 and 30 so that it will
|
||||||
|
* always be between two RTC ticks.
|
||||||
|
*/
|
||||||
|
timer_val = MAX(timer_val, 1);
|
||||||
|
timer_val = MIN(timer_val, 30);
|
||||||
|
|
||||||
|
if (nrfx_rtc_cc_set(&app_rtc_instance, 0, rtc_val, false) != NRFX_SUCCESS) {
|
||||||
|
printk("Failed setting trigger\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
nrfx_timer_compare(&app_timer_instance, 0, timer_val, false);
|
||||||
|
nrfx_gppi_channels_enable(BIT(ppi_chan_on_rtc_match));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t controller_time_trigger_event_addr_get(void)
|
||||||
|
{
|
||||||
|
return nrf_egu_event_address_get(NRF_EGU0, NRF_EGU_EVENT_TRIGGERED0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SYS_INIT(controller_time_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
||||||
270
src/controller_time_nrf53_app.c
Normal file
270
src/controller_time_nrf53_app.c
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** This file implements controller time management for 53 Series devices
|
||||||
|
*
|
||||||
|
* As the controller clock is not directly accessible, the controller
|
||||||
|
* time is obtained using a mirrored RTC peripheral.
|
||||||
|
* The RTC is started upon starting the controller clock on the network core.
|
||||||
|
* To achieve microsecond accurate toggling, a timer peripheral is also used.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// BK: taken from ncs/nrf/samples/bluetooth/conn_time_sync/src/controller_time_nrf53_app.c
|
||||||
|
// - we already have a similar code in audio_sync_timer_rtc.c from nrf5340_audio
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/init.h>
|
||||||
|
#include <zephyr/sys/barrier.h>
|
||||||
|
#include <helpers/nrfx_gppi.h>
|
||||||
|
#include <nrfx_rtc.h>
|
||||||
|
#include <nrfx_timer.h>
|
||||||
|
#include <hal/nrf_ipc.h>
|
||||||
|
#include <hal/nrf_egu.h>
|
||||||
|
#include "conn_time_sync.h"
|
||||||
|
|
||||||
|
static const nrfx_rtc_t app_rtc_instance = NRFX_RTC_INSTANCE(0);
|
||||||
|
static const nrfx_timer_t app_timer_instance = NRFX_TIMER_INSTANCE(0);
|
||||||
|
|
||||||
|
static uint8_t ppi_chan_on_rtc_match;
|
||||||
|
static volatile uint32_t num_rtc_overflows;
|
||||||
|
|
||||||
|
static void rtc_isr_handler(nrfx_rtc_int_type_t int_type)
|
||||||
|
{
|
||||||
|
if (int_type == NRFX_RTC_INT_OVERFLOW) {
|
||||||
|
num_rtc_overflows++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unused_timer_isr_handler(nrf_timer_event_t event_type, void *ctx)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(event_type);
|
||||||
|
ARG_UNUSED(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rtc_config(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uint8_t dppi_channel_rtc_start;
|
||||||
|
const nrfx_rtc_config_t rtc_cfg = NRFX_RTC_DEFAULT_CONFIG;
|
||||||
|
|
||||||
|
ret = nrfx_rtc_init(&app_rtc_instance, &rtc_cfg, rtc_isr_handler);
|
||||||
|
if (ret != NRFX_SUCCESS) {
|
||||||
|
printk("Failed initializing RTC (ret: %d)\n", ret - NRFX_ERROR_BASE_NUM);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef BT_CTLR_SDC_BSIM_BUILD
|
||||||
|
IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_RTC_INST_GET(0)), IRQ_PRIO_LOWEST,
|
||||||
|
NRFX_RTC_INST_HANDLER_GET(0), NULL, 0);
|
||||||
|
#else
|
||||||
|
IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_RTC_INST_GET(0)), 0,
|
||||||
|
(void *)NRFX_RTC_INST_HANDLER_GET(0), NULL, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
nrfx_rtc_overflow_enable(&app_rtc_instance, true);
|
||||||
|
nrfx_rtc_tick_enable(&app_rtc_instance, false);
|
||||||
|
nrfx_rtc_enable(&app_rtc_instance);
|
||||||
|
|
||||||
|
|
||||||
|
/* The application core RTC is started synchronously with the controller
|
||||||
|
* RTC using PPI over IPC.
|
||||||
|
*/
|
||||||
|
ret = nrfx_gppi_channel_alloc(&dppi_channel_rtc_start);
|
||||||
|
if (ret != NRFX_SUCCESS) {
|
||||||
|
printk("nrfx DPPI channel alloc error for starting RTC: %d", ret);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
nrf_ipc_receive_config_set(NRF_IPC, 4, NRF_IPC_CHANNEL_4);
|
||||||
|
|
||||||
|
nrfx_gppi_channel_endpoints_setup(dppi_channel_rtc_start,
|
||||||
|
nrfx_rtc_task_address_get(&app_rtc_instance,
|
||||||
|
NRF_RTC_TASK_CLEAR),
|
||||||
|
nrf_ipc_event_address_get(NRF_IPC,
|
||||||
|
NRF_IPC_EVENT_RECEIVE_4));
|
||||||
|
|
||||||
|
nrfx_gppi_channels_enable(BIT(dppi_channel_rtc_start));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int timer_config(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uint8_t ppi_chan_timer_clear_on_rtc_tick;
|
||||||
|
const nrfx_timer_config_t timer_cfg = {
|
||||||
|
.frequency = NRFX_MHZ_TO_HZ(1UL),
|
||||||
|
.mode = NRF_TIMER_MODE_TIMER,
|
||||||
|
.bit_width = NRF_TIMER_BIT_WIDTH_8,
|
||||||
|
.interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY,
|
||||||
|
.p_context = NULL};
|
||||||
|
|
||||||
|
ret = nrfx_timer_init(&app_timer_instance, &timer_cfg, unused_timer_isr_handler);
|
||||||
|
if (ret != NRFX_SUCCESS) {
|
||||||
|
printk("Failed initializing timer (ret: %d)\n", ret - NRFX_ERROR_BASE_NUM);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear the TIMER every RTC tick. */
|
||||||
|
if (nrfx_gppi_channel_alloc(&ppi_chan_timer_clear_on_rtc_tick) != NRFX_SUCCESS) {
|
||||||
|
printk("Failed allocating for clearing TIMER on RTC TICK\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
nrfx_gppi_channel_endpoints_setup(ppi_chan_timer_clear_on_rtc_tick,
|
||||||
|
nrfx_rtc_event_address_get(&app_rtc_instance,
|
||||||
|
NRF_RTC_EVENT_TICK),
|
||||||
|
nrfx_timer_task_address_get(&app_timer_instance,
|
||||||
|
NRF_TIMER_TASK_CLEAR));
|
||||||
|
|
||||||
|
nrfx_gppi_channels_enable(BIT(ppi_chan_timer_clear_on_rtc_tick));
|
||||||
|
|
||||||
|
nrfx_timer_enable(&app_timer_instance);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Configure the TIMER and RTC in such a way that microsecond accurate timing can be achieved.
|
||||||
|
*
|
||||||
|
* To get microsecond accurate toggling, we need to combine both the RTC and TIMER peripheral.
|
||||||
|
* That is, both a RTC CC value and TIMER CC value needs to be set.
|
||||||
|
* We should only trigger an EGU task when the TIMER CC value
|
||||||
|
* matches after the RTC CC value matched.
|
||||||
|
* This is achieved using a PPI group. The group is enabled when the RTC CC matches and disabled
|
||||||
|
* again then the TIMER CC matches.
|
||||||
|
*/
|
||||||
|
int config_egu_trigger_on_rtc_and_timer_match(void)
|
||||||
|
{
|
||||||
|
uint8_t ppi_chan_on_timer_match;
|
||||||
|
|
||||||
|
if (nrfx_gppi_channel_alloc(&ppi_chan_on_rtc_match) != NRFX_SUCCESS) {
|
||||||
|
printk("Failed allocating for RTC match\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nrfx_gppi_channel_alloc(&ppi_chan_on_timer_match) != NRFX_SUCCESS) {
|
||||||
|
printk("Failed allocating for TIMER match\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
nrfx_gppi_group_clear(NRFX_GPPI_CHANNEL_GROUP0);
|
||||||
|
nrfx_gppi_group_disable(NRFX_GPPI_CHANNEL_GROUP0);
|
||||||
|
nrfx_gppi_channels_include_in_group(
|
||||||
|
BIT(ppi_chan_on_timer_match) | BIT(ppi_chan_on_rtc_match),
|
||||||
|
NRFX_GPPI_CHANNEL_GROUP0);
|
||||||
|
|
||||||
|
nrfx_gppi_channel_endpoints_setup(ppi_chan_on_rtc_match,
|
||||||
|
nrfx_rtc_event_address_get(&app_rtc_instance,
|
||||||
|
NRF_RTC_EVENT_COMPARE_0),
|
||||||
|
nrfx_gppi_task_address_get(NRFX_GPPI_TASK_CHG0_EN));
|
||||||
|
nrfx_gppi_channel_endpoints_setup(ppi_chan_on_timer_match,
|
||||||
|
nrfx_timer_event_address_get(&app_timer_instance,
|
||||||
|
NRF_TIMER_EVENT_COMPARE0),
|
||||||
|
nrfx_gppi_task_address_get(NRFX_GPPI_TASK_CHG0_DIS));
|
||||||
|
nrfx_gppi_fork_endpoint_setup(ppi_chan_on_timer_match,
|
||||||
|
nrf_egu_task_address_get(NRF_EGU0, NRF_EGU_TASK_TRIGGER0));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int controller_time_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = rtc_config();
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = timer_config();
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config_egu_trigger_on_rtc_and_timer_match();
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t rtc_ticks_to_us(uint32_t rtc_ticks)
|
||||||
|
{
|
||||||
|
const uint64_t rtc_ticks_in_femto_units = 30517578125UL;
|
||||||
|
|
||||||
|
return (rtc_ticks * rtc_ticks_in_femto_units) / 1000000000UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t us_to_rtc_ticks(uint32_t timestamp_us)
|
||||||
|
{
|
||||||
|
const uint64_t rtc_ticks_in_femto_units = 30517578125UL;
|
||||||
|
|
||||||
|
return ((uint64_t)(timestamp_us) * 1000000000UL) / rtc_ticks_in_femto_units;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t controller_time_us_get(void)
|
||||||
|
{
|
||||||
|
/* Simply use the RTC time here.
|
||||||
|
* It is possible to combine RTC and TIMER information here to get a more accurate
|
||||||
|
* timestamp. That requires setting up a PPI channel to capture both values simultaneously.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const uint64_t rtc_overflow_time_us = 512000000UL;
|
||||||
|
|
||||||
|
uint32_t captured_rtc_overflows;
|
||||||
|
uint32_t captured_rtc_ticks;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
captured_rtc_overflows = num_rtc_overflows;
|
||||||
|
|
||||||
|
/* Read out RTC ticks after reading number of overflows. */
|
||||||
|
barrier_isync_fence_full();
|
||||||
|
|
||||||
|
captured_rtc_ticks = nrf_rtc_counter_get(app_rtc_instance.p_reg);
|
||||||
|
|
||||||
|
/* Read out number of overflows after reading number of RTC ticks */
|
||||||
|
barrier_isync_fence_full();
|
||||||
|
|
||||||
|
if (captured_rtc_overflows == num_rtc_overflows) {
|
||||||
|
/* There were no new overflows after reading ticks.
|
||||||
|
* That is, we can use the captured value.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtc_ticks_to_us(captured_rtc_ticks) +
|
||||||
|
(captured_rtc_overflows * rtc_overflow_time_us);
|
||||||
|
}
|
||||||
|
|
||||||
|
void controller_time_trigger_set(uint64_t timestamp_us)
|
||||||
|
{
|
||||||
|
uint32_t num_overflows = timestamp_us / 512000000UL;
|
||||||
|
uint64_t overflow_time_us = num_overflows * 512000000UL;
|
||||||
|
|
||||||
|
uint32_t remainder_time_us = timestamp_us - overflow_time_us;
|
||||||
|
uint32_t rtc_val = us_to_rtc_ticks(remainder_time_us);
|
||||||
|
uint8_t timer_val = timestamp_us - rtc_ticks_to_us(rtc_val);
|
||||||
|
|
||||||
|
/* Ensure the timer value lies between 1 and 30 so that it will
|
||||||
|
* always be between two RTC ticks.
|
||||||
|
*/
|
||||||
|
timer_val = MAX(timer_val, 1);
|
||||||
|
timer_val = MIN(timer_val, 30);
|
||||||
|
|
||||||
|
if (nrfx_rtc_cc_set(&app_rtc_instance, 0, rtc_val, false) != NRFX_SUCCESS) {
|
||||||
|
printk("Failed setting trigger\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
nrfx_timer_compare(&app_timer_instance, 0, timer_val, false);
|
||||||
|
nrfx_gppi_channels_enable(BIT(ppi_chan_on_rtc_match));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t controller_time_trigger_event_addr_get(void)
|
||||||
|
{
|
||||||
|
return nrf_egu_event_address_get(NRF_EGU0, NRF_EGU_EVENT_TRIGGERED0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The controller time initialization must happen before
|
||||||
|
* starting the network core.
|
||||||
|
*/
|
||||||
|
SYS_INIT(controller_time_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
||||||
68
src/controller_time_nrf54.c
Normal file
68
src/controller_time_nrf54.c
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** This file implements controller time management for 54 Series devices
|
||||||
|
*/
|
||||||
|
|
||||||
|
// BK: taken from ncs/nrf/samples/bluetooth/conn_time_sync/src/controller_time_nrf54.c
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <nrfx_grtc.h>
|
||||||
|
#include "controller_time.h"
|
||||||
|
|
||||||
|
static uint8_t grtc_channel;
|
||||||
|
|
||||||
|
int controller_time_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = nrfx_grtc_channel_alloc(&grtc_channel);
|
||||||
|
if (ret != NRFX_SUCCESS) {
|
||||||
|
printk("Failed allocating GRTC channel (ret: %d)\n",
|
||||||
|
ret - NRFX_ERROR_BASE_NUM);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
nrf_grtc_sys_counter_compare_event_enable(NRF_GRTC, grtc_channel);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t controller_time_us_get(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uint64_t current_time_us;
|
||||||
|
|
||||||
|
ret = nrfx_grtc_syscounter_get(¤t_time_us);
|
||||||
|
if (ret != NRFX_SUCCESS) {
|
||||||
|
printk("Failed obtaining system time (ret: %d)\n", ret - NRFX_ERROR_BASE_NUM);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return current_time_us;
|
||||||
|
}
|
||||||
|
|
||||||
|
void controller_time_trigger_set(uint64_t timestamp_us)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
nrfx_grtc_channel_t chan_data = {
|
||||||
|
.channel = grtc_channel,
|
||||||
|
};
|
||||||
|
|
||||||
|
ret = nrfx_grtc_syscounter_cc_absolute_set(&chan_data, timestamp_us, false);
|
||||||
|
if (ret != NRFX_SUCCESS) {
|
||||||
|
printk("Failed setting CC (ret: %d)\n", ret - NRFX_ERROR_BASE_NUM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t controller_time_trigger_event_addr_get(void)
|
||||||
|
{
|
||||||
|
return nrf_grtc_event_address_get(NRF_GRTC,
|
||||||
|
nrf_grtc_sys_counter_compare_event_get(grtc_channel));
|
||||||
|
}
|
||||||
|
|
||||||
|
SYS_INIT(controller_time_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
||||||
19
src/main.c
19
src/main.c
@@ -31,8 +31,12 @@
|
|||||||
#include <zephyr/bluetooth/buf.h>
|
#include <zephyr/bluetooth/buf.h>
|
||||||
#include <zephyr/bluetooth/hci_raw.h>
|
#include <zephyr/bluetooth/hci_raw.h>
|
||||||
|
|
||||||
|
// nRF5340 - from ncs/nrf/nrf5340_audio example
|
||||||
#include "audio_sync_timer.h"
|
#include "audio_sync_timer.h"
|
||||||
|
|
||||||
|
// nRF54L15 - from ncs/nrf/samples/bluetooth/conn_time_sync
|
||||||
|
#include "controller_time.h"
|
||||||
|
|
||||||
#define LOG_MODULE_NAME hci_uart
|
#define LOG_MODULE_NAME hci_uart
|
||||||
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
||||||
|
|
||||||
@@ -360,7 +364,10 @@ static int hci_uart_init(void)
|
|||||||
|
|
||||||
SYS_INIT(hci_uart_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
|
SYS_INIT(hci_uart_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
|
||||||
|
|
||||||
#ifdef CONFIG_AUDIO_SYNC_TIMER_USES_RTC
|
#define ENABLE_ISO_TIMESYNC
|
||||||
|
|
||||||
|
#ifdef ENABLE_ISO_TIMESYNC
|
||||||
|
|
||||||
#define TIMESYNC_GPIO DT_NODELABEL(timesync)
|
#define TIMESYNC_GPIO DT_NODELABEL(timesync)
|
||||||
|
|
||||||
#if DT_NODE_HAS_STATUS(TIMESYNC_GPIO, okay)
|
#if DT_NODE_HAS_STATUS(TIMESYNC_GPIO, okay)
|
||||||
@@ -384,11 +391,12 @@ uint8_t hci_cmd_iso_timesync_cb(struct net_buf *buf)
|
|||||||
LOG_INF("buf %p type %u len %u", buf, bt_buf_get_type(buf), buf->len);
|
LOG_INF("buf %p type %u len %u", buf, bt_buf_get_type(buf), buf->len);
|
||||||
LOG_INF("buf[0] = 0x%02x", buf->data[0]);
|
LOG_INF("buf[0] = 0x%02x", buf->data[0]);
|
||||||
|
|
||||||
uint32_t timestamp_second_us;
|
uint32_t timestamp_second_us = 0;
|
||||||
|
|
||||||
// Lock interrupts to avoid interrupt between time capture and gpio toggle
|
// Lock interrupts to avoid interrupt between time capture and gpio toggle
|
||||||
uint32_t key = arch_irq_lock();
|
uint32_t key = arch_irq_lock();
|
||||||
|
|
||||||
|
#ifdef CONFIG_SOC_NRF5340_CPUAPP
|
||||||
// Get current time
|
// Get current time
|
||||||
uint32_t timestamp_first_us = audio_sync_timer_capture();
|
uint32_t timestamp_first_us = audio_sync_timer_capture();
|
||||||
|
|
||||||
@@ -402,6 +410,11 @@ uint8_t hci_cmd_iso_timesync_cb(struct net_buf *buf)
|
|||||||
}
|
}
|
||||||
timestamp_first_us = timestamp_second_us;
|
timestamp_first_us = timestamp_second_us;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CONFIG_SOC_NRF54L15_CPUAPP) || defined(CONFIG_SOC_NRF52833)
|
||||||
|
timestamp_second_us = (uint32_t) controller_time_us_get();
|
||||||
|
#endif
|
||||||
|
|
||||||
#if DT_NODE_HAS_STATUS(TIMESYNC_GPIO, okay)
|
#if DT_NODE_HAS_STATUS(TIMESYNC_GPIO, okay)
|
||||||
gpio_pin_toggle_dt( ×ync_pin );
|
gpio_pin_toggle_dt( ×ync_pin );
|
||||||
@@ -464,7 +477,7 @@ int main(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_AUDIO_SYNC_TIMER_USES_RTC
|
#ifdef ENABLE_ISO_TIMESYNC
|
||||||
/* Register iso_timesync command */
|
/* Register iso_timesync command */
|
||||||
static struct bt_hci_raw_cmd_ext cmd_list = {
|
static struct bt_hci_raw_cmd_ext cmd_list = {
|
||||||
.op = BT_OP(BT_OGF_VS, HCI_CMD_ISO_TIMESYNC),
|
.op = BT_OP(BT_OGF_VS, HCI_CMD_ISO_TIMESYNC),
|
||||||
|
|||||||
53
sysbuild/ipc_radio/prj.conf
Normal file
53
sysbuild/ipc_radio/prj.conf
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2023 Nordic Semiconductor ASA
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
#
|
||||||
|
|
||||||
|
CONFIG_HEAP_MEM_POOL_SIZE=8192
|
||||||
|
CONFIG_MAIN_STACK_SIZE=2048
|
||||||
|
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
|
||||||
|
|
||||||
|
CONFIG_MBOX=y
|
||||||
|
CONFIG_IPC_SERVICE=y
|
||||||
|
|
||||||
|
CONFIG_BT=y
|
||||||
|
CONFIG_BT_HCI_RAW=y
|
||||||
|
CONFIG_BT_CTLR_ASSERT_HANDLER=y
|
||||||
|
|
||||||
|
CONFIG_BT_ISO_PERIPHERAL=y
|
||||||
|
CONFIG_BT_ISO_CENTRAL=y
|
||||||
|
CONFIG_BT_ISO_BROADCASTER=y
|
||||||
|
CONFIG_BT_ISO_SYNC_RECEIVER=y
|
||||||
|
CONFIG_BT_EXT_ADV=y
|
||||||
|
CONFIG_BT_PER_ADV_SYNC_TRANSFER_RECEIVER=y
|
||||||
|
CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER=y
|
||||||
|
|
||||||
|
CONFIG_BT_CTLR_ADV_ISO_SET=2
|
||||||
|
CONFIG_BT_CTLR_ADV_SET=2
|
||||||
|
CONFIG_BT_CTLR_ADV_ISO_STREAM_COUNT=2
|
||||||
|
CONFIG_BT_CTLR_CONN_ISO_GROUPS=1
|
||||||
|
CONFIG_BT_CTLR_CONN_ISO_STREAMS=2
|
||||||
|
CONFIG_BT_CTLR_SYNC_ISO_STREAM_COUNT=3
|
||||||
|
CONFIG_BT_CTLR_SYNC_PERIODIC_ADV_LIST_SIZE=1
|
||||||
|
|
||||||
|
|
||||||
|
# Support six links as a central, or one link as a peripheral
|
||||||
|
CONFIG_BT_MAX_CONN=8
|
||||||
|
CONFIG_BT_CTLR_SDC_PERIPHERAL_COUNT=2
|
||||||
|
|
||||||
|
# Allow using more than default advertising event length
|
||||||
|
CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=251
|
||||||
|
|
||||||
|
# To present the audio at the right point in time, we need the controller and
|
||||||
|
# audio clock to be synchronized
|
||||||
|
CONFIG_MPSL_TRIGGER_IPC_TASK_ON_RTC_START=y
|
||||||
|
CONFIG_MPSL_TRIGGER_IPC_TASK_ON_RTC_START_CHANNEL=4
|
||||||
|
|
||||||
|
# Needed for builds with nrf21540
|
||||||
|
# Can also be set to 20, but check local restrictions first
|
||||||
|
#CONFIG_BT_CTLR_TX_PWR_ANTENNA=10
|
||||||
|
#CONFIG_MPSL_FEM_NRF21540_TX_GAIN_DB=10
|
||||||
|
|
||||||
|
CONFIG_IPC_RADIO_BT=y
|
||||||
|
CONFIG_IPC_RADIO_BT_HCI_IPC=y
|
||||||
58
sysbuild/ipc_radio/prj_release.conf
Normal file
58
sysbuild/ipc_radio/prj_release.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2023 Nordic Semiconductor ASA
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
#
|
||||||
|
|
||||||
|
CONFIG_HEAP_MEM_POOL_SIZE=8192
|
||||||
|
CONFIG_MAIN_STACK_SIZE=2048
|
||||||
|
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
|
||||||
|
|
||||||
|
CONFIG_MBOX=y
|
||||||
|
CONFIG_IPC_SERVICE=y
|
||||||
|
|
||||||
|
CONFIG_BT=y
|
||||||
|
CONFIG_BT_HCI_RAW=y
|
||||||
|
CONFIG_BT_CTLR_ASSERT_HANDLER=y
|
||||||
|
|
||||||
|
CONFIG_BT_ISO_PERIPHERAL=y
|
||||||
|
CONFIG_BT_ISO_CENTRAL=y
|
||||||
|
CONFIG_BT_ISO_BROADCASTER=y
|
||||||
|
CONFIG_BT_ISO_SYNC_RECEIVER=y
|
||||||
|
CONFIG_BT_EXT_ADV=y
|
||||||
|
CONFIG_BT_PER_ADV_SYNC_TRANSFER_RECEIVER=y
|
||||||
|
CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER=y
|
||||||
|
|
||||||
|
CONFIG_BT_CTLR_ADV_ISO_SET=2
|
||||||
|
CONFIG_BT_CTLR_ADV_SET=2
|
||||||
|
CONFIG_BT_CTLR_ADV_ISO_STREAM_COUNT=2
|
||||||
|
CONFIG_BT_CTLR_CONN_ISO_GROUPS=1
|
||||||
|
CONFIG_BT_CTLR_CONN_ISO_STREAMS=2
|
||||||
|
CONFIG_BT_CTLR_SYNC_ISO_STREAM_COUNT=3
|
||||||
|
CONFIG_BT_CTLR_SYNC_PERIODIC_ADV_LIST_SIZE=1
|
||||||
|
|
||||||
|
# Support six links as a central, or one link as a peripheral
|
||||||
|
CONFIG_BT_MAX_CONN=8
|
||||||
|
CONFIG_BT_CTLR_SDC_PERIPHERAL_COUNT=2
|
||||||
|
|
||||||
|
# Allow using more than default advertising event length
|
||||||
|
CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=251
|
||||||
|
|
||||||
|
# To present the audio at the right point in time, we need the controller and
|
||||||
|
# audio clock to be synchronized
|
||||||
|
CONFIG_MPSL_TRIGGER_IPC_TASK_ON_RTC_START=y
|
||||||
|
CONFIG_MPSL_TRIGGER_IPC_TASK_ON_RTC_START_CHANNEL=4
|
||||||
|
|
||||||
|
CONFIG_IPC_RADIO_BT=y
|
||||||
|
CONFIG_IPC_RADIO_BT_HCI_IPC=y
|
||||||
|
|
||||||
|
# General
|
||||||
|
CONFIG_DEBUG=n
|
||||||
|
CONFIG_ASSERT=n
|
||||||
|
CONFIG_STACK_USAGE=n
|
||||||
|
CONFIG_THREAD_MONITOR=n
|
||||||
|
CONFIG_SERIAL=n
|
||||||
|
CONFIG_CONSOLE=n
|
||||||
|
CONFIG_PRINTK=n
|
||||||
|
CONFIG_UART_CONSOLE=n
|
||||||
|
CONFIG_BOOT_BANNER=n
|
||||||
Reference in New Issue
Block a user