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
|
||||
)
|
||||
|
||||
if (CONFIG_AUDIO_SYNC_TIMER_USES_RTC)
|
||||
list(APPEND SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/audio_sync_timer_rtc.c)
|
||||
if (CONFIG_SOC_COMPATIBLE_NRF52X)
|
||||
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()
|
||||
|
||||
target_sources(app PRIVATE ${SRCS})
|
||||
|
||||
2
Kconfig
2
Kconfig
@@ -4,7 +4,7 @@ menu "Modules"
|
||||
rsource "Kconfig.defaults"
|
||||
|
||||
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
|
||||
select NRFX_RTC0
|
||||
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
|
||||
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
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
|
||||
|
||||
```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
|
||||
|
||||
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
|
||||
|
||||
```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
|
||||
|
||||
```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
|
||||
@@ -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.
|
||||
|
||||
### UART 1 + Time Sync Pinout
|
||||
|
||||
Signal direction as seen from the nRF5340.
|
||||
|
||||
| PIN | Arduino | nrf | Direction |
|
||||
|----------|---------|-------|-----------|
|
||||
| TX | D0 | P1.09 | out |
|
||||
| RX | D1 | P1.08 | in |
|
||||
| RTS | D7 | P1.11 | out |
|
||||
| CTS | D8 | P1.10 | in |
|
||||
| Timesync | D10 | P1.01 | out |
|
||||
## nRF54L15
|
||||
|
||||
### HCI over UART 0 connected to second Virtual UART in J-Link Probe
|
||||
|
||||
Release build:
|
||||
```sh
|
||||
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
|
||||
*/
|
||||
|
||||
/ {
|
||||
host_interface {
|
||||
compatible = "gpio-outputs";
|
||||
status = "okay";
|
||||
timesync: pin_0 {
|
||||
gpios = <&gpio1 01 GPIO_ACTIVE_HIGH>;
|
||||
label = "Controller to host timesync pin";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
compatible = "nordic,nrf-uarte";
|
||||
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_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_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_MAX_CONN=16
|
||||
CONFIG_BT_TINYCRYPT_ECC=n
|
||||
CONFIG_BT_CTLR_DTM_HCI=y
|
||||
|
||||
# Enable ISO support
|
||||
CONFIG_BT_ISO_PERIPHERAL=y
|
||||
CONFIG_BT_ISO_CENTRAL=y
|
||||
CONFIG_BT_ISO_BROADCASTER=y
|
||||
# Setup ISO Buffer
|
||||
CONFIG_BT_ISO_TX_BUF_COUNT=10
|
||||
CONFIG_BT_ISO_TX_MTU=251
|
||||
CONFIG_BT_ISO_RX_BUF_COUNT=10
|
||||
CONFIG_BT_ISO_RX_MTU=251
|
||||
|
||||
# setup rpmsg/Bluetooth buffer
|
||||
CONFIG_BT_BUF_EVT_RX_COUNT=16
|
||||
CONFIG_BT_BUF_EVT_RX_SIZE=255
|
||||
CONFIG_BT_BUF_ACL_RX_SIZE=255
|
||||
CONFIG_BT_BUF_ACL_TX_SIZE=251
|
||||
CONFIG_BT_BUF_CMD_TX_SIZE=255
|
||||
# Enable ISO support
|
||||
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_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
|
||||
# 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/hci_raw.h>
|
||||
|
||||
// nRF5340 - from ncs/nrf/nrf5340_audio example
|
||||
#include "audio_sync_timer.h"
|
||||
|
||||
// nRF54L15 - from ncs/nrf/samples/bluetooth/conn_time_sync
|
||||
#include "controller_time.h"
|
||||
|
||||
#define LOG_MODULE_NAME hci_uart
|
||||
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);
|
||||
|
||||
#ifdef CONFIG_AUDIO_SYNC_TIMER_USES_RTC
|
||||
#define ENABLE_ISO_TIMESYNC
|
||||
|
||||
#ifdef ENABLE_ISO_TIMESYNC
|
||||
|
||||
#define TIMESYNC_GPIO DT_NODELABEL(timesync)
|
||||
|
||||
#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[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
|
||||
uint32_t key = arch_irq_lock();
|
||||
|
||||
#ifdef CONFIG_SOC_NRF5340_CPUAPP
|
||||
// Get current time
|
||||
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;
|
||||
}
|
||||
#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)
|
||||
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 */
|
||||
static struct bt_hci_raw_cmd_ext cmd_list = {
|
||||
.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