Files
hci_uart_iso_timesync/src/audio_sync_timer_rtc.c
2024-11-20 17:43:46 +01:00

238 lines
7.7 KiB
C

/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/
#include "audio_sync_timer.h"
#include <zephyr/kernel.h>
#include <zephyr/init.h>
#include <nrfx_dppi.h>
#include <nrfx_ipc.h>
#include <nrfx_rtc.h>
#include <nrfx_timer.h>
#include <nrfx_egu.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(audio_sync_timer, CONFIG_AUDIO_SYNC_TIMER_LOG_LEVEL);
#define AUDIO_SYNC_TIMER_NET_APP_IPC_EVT_CHANNEL 4
#define AUDIO_SYNC_TIMER_NET_APP_IPC_EVT NRF_IPC_EVENT_RECEIVE_4
#define AUDIO_SYNC_HF_TIMER_INSTANCE_NUMBER 1
#define AUDIO_SYNC_HF_TIMER_CURR_TIME_CAPTURE_CHANNEL 1
#define AUDIO_SYNC_HF_TIMER_CURR_TIME_CAPTURE NRF_TIMER_TASK_CAPTURE1
static const nrfx_timer_t audio_sync_hf_timer_instance =
NRFX_TIMER_INSTANCE(AUDIO_SYNC_HF_TIMER_INSTANCE_NUMBER);
#define AUDIO_SYNC_LF_TIMER_INSTANCE_NUMBER 0
#define AUDIO_SYNC_LF_TIMER_CURR_TIME_CAPTURE_CHANNEL 1
#define AUDIO_SYNC_LF_TIMER_CURR_TIME_CAPTURE NRF_RTC_TASK_CAPTURE_1
static uint8_t dppi_channel_curr_time_capture;
static const nrfx_rtc_config_t rtc_cfg = NRFX_RTC_DEFAULT_CONFIG;
static const nrfx_rtc_t audio_sync_lf_timer_instance =
NRFX_RTC_INSTANCE(AUDIO_SYNC_LF_TIMER_INSTANCE_NUMBER);
static uint8_t dppi_channel_timer_sync_with_rtc;
static uint8_t dppi_channel_rtc_start;
static volatile uint32_t num_rtc_overflows;
static nrfx_timer_config_t cfg = {.frequency = NRFX_MHZ_TO_HZ(1UL),
.mode = NRF_TIMER_MODE_TIMER,
.bit_width = NRF_TIMER_BIT_WIDTH_32,
.interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY,
.p_context = NULL};
static uint32_t timestamp_from_rtc_and_timer_get(uint32_t ticks, uint32_t remainder_us)
{
const uint64_t rtc_ticks_in_femto_units = 30517578125UL;
const uint32_t rtc_overflow_time_us = 512000000UL;
return ((ticks * rtc_ticks_in_femto_units) / 1000000000UL) +
(num_rtc_overflows * rtc_overflow_time_us) +
remainder_us;
}
uint32_t audio_sync_timer_capture(void)
{
/* Ensure that the follow product specification statement is handled:
*
* There is a delay of 6 PCLK16M periods from when the TASKS_CAPTURE[n] is triggered
* until the corresponding CC[n] register is updated.
*
* Lets have a stale value in the CC[n] register and compare that it is different when
* we capture using DPPI.
*
* We ensure it is stale by setting it as the previous tick relative to current
* counter value.
*/
uint32_t tick_stale = nrf_rtc_counter_get(audio_sync_lf_timer_instance.p_reg);
/* Set a stale value in the CC[n] register */
tick_stale--;
nrf_rtc_cc_set(audio_sync_lf_timer_instance.p_reg,
AUDIO_SYNC_LF_TIMER_CURR_TIME_CAPTURE_CHANNEL, tick_stale);
/* Trigger EGU task to capture RTC and TIMER value */
nrf_egu_task_trigger(NRF_EGU0, NRF_EGU_TASK_TRIGGER0);
/* Read captured RTC value */
uint32_t tick = nrf_rtc_cc_get(audio_sync_lf_timer_instance.p_reg,
AUDIO_SYNC_LF_TIMER_CURR_TIME_CAPTURE_CHANNEL);
/* If required, wait until CC[n] register is updated */
while (tick == tick_stale) {
tick = nrf_rtc_cc_get(audio_sync_lf_timer_instance.p_reg,
AUDIO_SYNC_LF_TIMER_CURR_TIME_CAPTURE_CHANNEL);
}
/* Read captured TIMER value */
uint32_t remainder_us = nrf_timer_cc_get(NRF_TIMER1,
AUDIO_SYNC_HF_TIMER_CURR_TIME_CAPTURE_CHANNEL);
return timestamp_from_rtc_and_timer_get(tick, remainder_us);
}
//#include <stdio.h>
//#include "led.h"
//bool led_status = false;
static void unused_timer_isr_handler(nrf_timer_event_t event_type, void *p_context)
{
ARG_UNUSED(event_type);
ARG_UNUSED(p_context);
#if 0
if(event_type == NRF_TIMER_EVENT_COMPARE2)
{
if( led_status ){
led_off( LED_APP_3_GREEN );
led_status = false;
} else {
led_on( LED_APP_3_GREEN );
led_status = true;
}
// char * p_msg = p_context;
// uint32_t remainder_us = nrf_timer_cc_get(NRF_TIMER1,
// AUDIO_SYNC_HF_TIMER_CURR_TIME_CAPTURE_CHANNEL);
// LOG_INF("%d\n", remainder_us );
// printf("Timer finished. Context passed to the handler: >%s<", p_msg);
}
#endif
}
static void rtc_isr_handler(nrfx_rtc_int_type_t int_type)
{
if (int_type == NRFX_RTC_INT_OVERFLOW) {
num_rtc_overflows++;
}
}
/**
* @brief Initialize audio sync timer
*
* @note The audio sync timers is replicating the controller's clock.
* The controller starts or clears the sync timer using a PPI signal
* sent from the controller. This makes the two clocks synchronized.
*
* @return 0 if successful, error otherwise
*/
static int audio_sync_timer_init(void)
{
nrfx_err_t ret;
ret = nrfx_timer_init(&audio_sync_hf_timer_instance, &cfg, unused_timer_isr_handler);
if (ret - NRFX_ERROR_BASE_NUM) {
LOG_ERR("nrfx timer init error: %d", ret);
return -ENODEV;
}
ret = nrfx_rtc_init(&audio_sync_lf_timer_instance, &rtc_cfg, rtc_isr_handler);
if (ret - NRFX_ERROR_BASE_NUM) {
LOG_ERR("nrfx rtc init error: %d", ret);
return -ENODEV;
}
IRQ_CONNECT(RTC0_IRQn, IRQ_PRIO_LOWEST, nrfx_rtc_0_irq_handler, NULL, 0);
nrfx_rtc_overflow_enable(&audio_sync_lf_timer_instance, true);
/* Initialize capturing of current timestamps */
ret = nrfx_dppi_channel_alloc(&dppi_channel_curr_time_capture);
if (ret - NRFX_ERROR_BASE_NUM) {
LOG_ERR("nrfx DPPI channel alloc error (current time capture) - Return value: %d", ret);
return -ENOMEM;
}
nrf_rtc_subscribe_set(audio_sync_lf_timer_instance.p_reg,
AUDIO_SYNC_LF_TIMER_CURR_TIME_CAPTURE,
dppi_channel_curr_time_capture);
nrf_timer_subscribe_set(audio_sync_hf_timer_instance.p_reg,
AUDIO_SYNC_HF_TIMER_CURR_TIME_CAPTURE,
dppi_channel_curr_time_capture);
nrf_egu_publish_set(NRF_EGU0, NRF_EGU_EVENT_TRIGGERED0, dppi_channel_curr_time_capture);
ret = nrfx_dppi_channel_enable(dppi_channel_curr_time_capture);
if (ret - NRFX_ERROR_BASE_NUM) {
LOG_ERR("nrfx DPPI channel enable error (current time capture) - Return value: %d", ret);
return -EIO;
}
/* Initialize functionality for synchronization between APP and NET core */
ret = nrfx_dppi_channel_alloc(&dppi_channel_rtc_start);
if (ret - NRFX_ERROR_BASE_NUM) {
LOG_ERR("nrfx DPPI channel alloc error (timer clear): %d", ret);
return -ENOMEM;
}
nrf_rtc_subscribe_set(audio_sync_lf_timer_instance.p_reg, NRF_RTC_TASK_CLEAR,
dppi_channel_rtc_start);
nrf_timer_subscribe_set(audio_sync_hf_timer_instance.p_reg, NRF_TIMER_TASK_START,
dppi_channel_rtc_start);
nrf_ipc_receive_config_set(NRF_IPC, AUDIO_SYNC_TIMER_NET_APP_IPC_EVT_CHANNEL,
NRF_IPC_CHANNEL_4);
nrf_ipc_publish_set(NRF_IPC, AUDIO_SYNC_TIMER_NET_APP_IPC_EVT, dppi_channel_rtc_start);
ret = nrfx_dppi_channel_enable(dppi_channel_rtc_start);
if (ret - NRFX_ERROR_BASE_NUM) {
LOG_ERR("nrfx DPPI channel enable error (timer clear): %d", ret);
return -EIO;
}
/* Initialize functionality for synchronization between RTC and TIMER */
ret = nrfx_dppi_channel_alloc(&dppi_channel_timer_sync_with_rtc);
if (ret - NRFX_ERROR_BASE_NUM) {
LOG_ERR("nrfx DPPI channel alloc error (timer clear): %d", ret);
return -ENOMEM;
}
nrf_rtc_publish_set(audio_sync_lf_timer_instance.p_reg, NRF_RTC_EVENT_TICK,
dppi_channel_timer_sync_with_rtc);
nrf_timer_subscribe_set(audio_sync_hf_timer_instance.p_reg, NRF_TIMER_TASK_CLEAR,
dppi_channel_timer_sync_with_rtc);
nrfx_rtc_tick_enable(&audio_sync_lf_timer_instance, false);
ret = nrfx_dppi_channel_enable(dppi_channel_timer_sync_with_rtc);
if (ret - NRFX_ERROR_BASE_NUM) {
LOG_ERR("nrfx DPPI channel enable error (timer clear): %d", ret);
return -EIO;
}
nrfx_rtc_enable(&audio_sync_lf_timer_instance);
LOG_DBG("Audio sync timer initialized");
return 0;
}
SYS_INIT(audio_sync_timer_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);