Hi everyone, I am attempting to measure I2S rate against internal clock frequency since I am using APLL. I am following examples in NRF5340_Audio application.
What i have in my code is:
- Configure TIMER2 as TIMER, with CC1 with Clear mask set at INT32_MAX to avoid wraparound errors.
- Configure I2S
- Allocate DPPI channel. Subscribe to it with CC0 channel. Publish to it from TXPTRUPD.
What I expect is a value in range from 0 to INT32_MAX returned from nrfx_timer_capture_get for CC0. What I get is constant 0, so i am convinced, that DPPI doesn't trigger. I am also positive, that I2S runs, as interrupt is called for NRF_I2S_EVENT_TXPTRUPD.
I am also attaching my modified code.
/*
* Copyright (c) 2021, PACKETCRAFT, INC.
*
* SPDX-License-Identifier: LicenseRef-PCFT
*/
#include "audio_i2s.h"
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/pinctrl.h>
#include <nrfx_i2s.h>
#include <nrfx_timer.h>
#include <nrfx_clock.h>
#include <nrfx_dppi.h>
#include <zephyr/logging/log.h>
#include <zephyr/logging/log_core.h>
#include "audio_sync_timer.h"
#define I2S_NL DT_NODELABEL(i2s0)
enum audio_i2s_state {
AUDIO_I2S_STATE_UNINIT,
AUDIO_I2S_STATE_IDLE,
AUDIO_I2S_STATE_STARTED,
};
static enum audio_i2s_state state = AUDIO_I2S_STATE_UNINIT;
PINCTRL_DT_DEFINE(I2S_NL);
LOG_MODULE_REGISTER(audio_i2s, LOG_LEVEL_INF);
static const nrfx_timer_t i2s_meas_timer = NRFX_TIMER_INSTANCE(2);
#if CONFIG_AUDIO_SAMPLE_RATE_8000_HZ
#define CONFIG_AUDIO_RATIO NRF_I2S_RATIO_384X
#define MCK_SETUP I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV4
#elif CONFIG_AUDIO_SAMPLE_RATE_16000_HZ
#define CONFIG_AUDIO_RATIO NRF_I2S_RATIO_384X
#define MCK_SETUP I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV2
#elif CONFIG_AUDIO_SAMPLE_RATE_24000_HZ
#define CONFIG_AUDIO_RATIO NRF_I2S_RATIO_256X
#define MCK_SETUP I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV2
#elif CONFIG_AUDIO_SAMPLE_RATE_48000_HZ
#define CONFIG_AUDIO_RATIO NRF_I2S_RATIO_128X
#define MCK_SETUP I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV2
#else
#error "Current AUDIO_SAMPLE_RATE_HZ setting not supported"
#endif
#if CONFIG_NRF5340_AUDIO_CS47L63_DRIVER
#define FORMAT_PCM NRF_I2S_FORMAT_I2S
#else
#define FORMAT_PCM NRF_I2S_FORMAT_ALIGNED
#endif
static nrfx_i2s_t i2s_inst = NRFX_I2S_INSTANCE(0);
static nrfx_i2s_config_t cfg = {
/* Pins are configured by pinctrl. */
.skip_gpio_cfg = true,
.skip_psel_cfg = true,
.irq_priority = DT_IRQ(I2S_NL, priority),
.mode = NRF_I2S_MODE_MASTER,
.format = FORMAT_PCM,
.alignment = NRF_I2S_ALIGN_LEFT,
.ratio = CONFIG_AUDIO_RATIO,
.mck_setup = MCK_SETUP,
#if (CONFIG_AUDIO_BIT_DEPTH_16)
.sample_width = NRF_I2S_SWIDTH_16BIT,
#elif (CONFIG_AUDIO_BIT_DEPTH_32)
.sample_width = NRF_I2S_SWIDTH_32BIT,
#else
#error Invalid bit depth selected
#endif /* (CONFIG_AUDIO_BIT_DEPTH_16) */
.channels = NRF_I2S_CHANNELS_STEREO,
.clksrc = NRF_I2S_CLKSRC_ACLK,
.enable_bypass = false,
};
static i2s_blk_comp_callback_t i2s_blk_comp_callback;
static i2s_blk_comp_single_cb_t i2s_blk_cb_rx;
static i2s_blk_comp_single_cb_t i2s_blk_cb_tx;
static uint32_t* next_tx;
static uint32_t* curr_tx;
static uint32_t* next_rx;
static uint32_t* curr_rx;
static uint32_t backup_buf_rx[I2S_SAMPLES_NUM];
static uint32_t backup_buf_tx[I2S_SAMPLES_NUM];
static void i2s_comp_handler(nrfx_i2s_buffers_t const *released_bufs, uint32_t status)
{
uint32_t frame_start_ts = audio_sync_timer_capture_get();
// uint32_t frame_start_ts = 0;
if(status == NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED){
if(!released_bufs) {
LOG_WRN("I2S TX cant keep up!");
return;
}
if (!(released_bufs->p_rx_buffer || released_bufs->p_tx_buffer)) {
LOG_WRN("I2S callback, got empty bufs!");
return;
}
if(i2s_blk_comp_callback){
i2s_blk_comp_callback(frame_start_ts, released_bufs->p_rx_buffer, released_bufs->p_tx_buffer);
}
}
}
void audio_i2s_set_next_buf(const uint8_t *tx_buf, uint32_t *rx_buf, size_t samples)
{
__ASSERT_NO_MSG(state == AUDIO_I2S_STATE_STARTED);
if (IS_ENABLED(CONFIG_STREAM_BIDIRECTIONAL) || (CONFIG_AUDIO_DEV == GATEWAY)) {
__ASSERT_NO_MSG(rx_buf != NULL);
}
if (IS_ENABLED(CONFIG_STREAM_BIDIRECTIONAL) || (CONFIG_AUDIO_DEV == HEADSET)) {
__ASSERT_NO_MSG(tx_buf != NULL);
}
nrfy_i2s_xfer_desc_t desc = {
.buffer_size = I2S_SAMPLES_NUM,
.p_rx_buffer = rx_buf,
.p_tx_buffer = (uint32_t *)tx_buf,
};
nrfy_i2s_buffers_set(i2s_inst.p_reg, &desc);
}
void audio_i2s_start(const uint8_t *tx_buf, uint32_t *rx_buf) {
nrfy_i2s_enable(i2s_inst.p_reg);
/* Clear spurious RXPTRUPD and TXPTRUPD events (see nRF52 anomaly 55). */
nrfy_i2s_event_clear(i2s_inst.p_reg, NRF_I2S_EVENT_TXPTRUPD | NRF_I2S_INT_STOPPED_MASK);
nrfy_i2s_int_enable(i2s_inst.p_reg, NRF_I2S_INT_TXPTRUPD_MASK);
nrfy_i2s_xfer_desc_t desc = {
.buffer_size = I2S_SAMPLES_NUM,
.p_rx_buffer = rx_buf,
.p_tx_buffer = (uint32_t *)tx_buf,
};
nrfy_i2s_buffers_set(i2s_inst.p_reg, &desc);
nrfy_i2s_xfer_start(i2s_inst.p_reg, NULL);
curr_tx = tx_buf;
curr_rx = rx_buf;
next_tx = NULL;
next_rx = NULL;
state = AUDIO_I2S_STATE_STARTED;
}
void audio_i2s_stop(void)
{
__ASSERT_NO_MSG(state == AUDIO_I2S_STATE_STARTED);
nrfx_i2s_stop(&i2s_inst);
state = AUDIO_I2S_STATE_IDLE;
}
void audio_i2s_blk_comp_cb_register(i2s_blk_comp_callback_t blk_comp_callback, i2s_blk_comp_single_cb_t rx_callback, i2s_blk_comp_single_cb_t tx_callback)
{
i2s_blk_comp_callback = blk_comp_callback;
i2s_blk_cb_rx = rx_callback;
i2s_blk_cb_tx = tx_callback;
}
static struct host_freq_data_bis{
int16_t diff[1000];
int meas_idx;
} freq_measurement_i2s;
// static volatile uint32_t tx_repeat, rx_repeat = 0;
#define NEXT_FREQ_BIS_IDX(cntr) (cntr < 999 ? cntr + 1 : 0)
#define PREV_FREQ_BIS_IDX(cntr) (cntr > 0 ? cntr - 1 : 999)
void audio_i2s_irq(void) {
uint32_t event_mask;
uint32_t *prev_tx, *prev_rx = NULL;
nrfy_i2s_xfer_desc_t * p_xfer;
uint32_t* ptr_tx = NULL;
uint32_t* ptr_rx = NULL;
static int32_t last_frame_us = 0;
// uint32_t frame_start_ts = audio_sync_timer_capture_get();
int32_t frame_start_us = (int32_t)nrfx_timer_capture_get(&i2s_meas_timer, NRF_TIMER_CC_CHANNEL0);
int log_idx = NEXT_FREQ_BIS_IDX(freq_measurement_i2s.meas_idx);
int32_t frame_diff = frame_start_us - last_frame_us;
frame_diff = frame_diff < 0 ? frame_diff + INT32_MAX : frame_diff;
freq_measurement_i2s.diff[log_idx] = frame_start_us - last_frame_us;
last_frame_us = frame_start_us;
freq_measurement_i2s.meas_idx = log_idx;
event_mask = nrfy_i2s_events_process(i2s_inst.p_reg,
NRFY_EVENT_TO_INT_BITMASK(NRF_I2S_EVENT_TXPTRUPD) |
NRFY_EVENT_TO_INT_BITMASK(NRF_I2S_EVENT_RXPTRUPD) |
NRFY_EVENT_TO_INT_BITMASK(NRF_I2S_EVENT_STOPPED),
NULL);
if(event_mask & NRF_I2S_EVENT_STOPPED) {
return; // TODO: Figure out what now!
}
prev_tx = curr_tx;
curr_tx = next_tx;
prev_rx = curr_rx;
curr_rx = next_rx;
// if(i2s_blk_cb_tx) {
// i2s_blk_cb_tx(frame_start_ts, prev_tx, &ptr_tx);
// }
// if(i2s_blk_cb_rx) {
// i2s_blk_cb_rx(frame_start_ts, prev_rx, &ptr_rx);
// }
next_tx = ptr_tx;
next_rx = ptr_rx;
ptr_tx = (ptr_tx != NULL) ? ptr_tx : backup_buf_tx;
ptr_rx = (ptr_rx != NULL) ? ptr_rx : backup_buf_rx;
audio_i2s_set_next_buf(ptr_tx, ptr_rx, I2S_SAMPLES_NUM);
nrf_barrier_w();
}
int32_t audio_i2s_measure_timebase(int samples) {
int32_t acc_diff = 0;
int idx = PREV_FREQ_BIS_IDX(freq_measurement_i2s.meas_idx);
for (int i = 0; i < samples; i++) {
acc_diff += (int32_t)freq_measurement_i2s.diff[idx];
idx = PREV_FREQ_BIS_IDX(idx);
}
return acc_diff;
}
static uint8_t dppi_channel_i2s_tick_capture;
void audio_i2s_init(void)
{
__ASSERT_NO_MSG(state == AUDIO_I2S_STATE_UNINIT);
nrfx_err_t ret;
nrfx_dppi_t dppi = NRFX_DPPI_INSTANCE(0);
curr_tx = NULL;
curr_rx = NULL;
next_tx = NULL;
next_rx = NULL;
nrfx_timer_config_t tmr_config = NRFX_TIMER_DEFAULT_CONFIG(1000000);
tmr_config.mode = NRF_TIMER_MODE_TIMER;
tmr_config.bit_width = NRF_TIMER_BIT_WIDTH_32;
tmr_config.interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY;
ret = nrfx_timer_init(&i2s_meas_timer, &tmr_config, NULL);
nrfx_timer_extended_compare(&i2s_meas_timer, NRF_TIMER_CC_CHANNEL1, INT32_MAX, NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK, false); // Convert timer return to int32_t :)
nrfx_timer_enable(&i2s_meas_timer);
nrfx_clock_hfclkaudio_config_set(HFCLKAUDIO_12_288_MHZ);
NRF_CLOCK->TASKS_HFCLKAUDIOSTART = 1;
/* Wait for ACLK to start */
while (!NRF_CLOCK_EVENT_HFCLKAUDIOSTARTED) {
k_sleep(K_MSEC(1));
}
ret = pinctrl_apply_state(PINCTRL_DT_DEV_CONFIG_GET(I2S_NL), PINCTRL_STATE_DEFAULT);
__ASSERT_NO_MSG(ret == 0);
IRQ_CONNECT(DT_IRQN(I2S_NL), DT_IRQ(I2S_NL, priority), nrfx_isr, audio_i2s_irq, 0);
irq_enable(DT_IRQN(I2S_NL));
ret = nrfx_i2s_init(&i2s_inst, &cfg, i2s_comp_handler);
__ASSERT_NO_MSG(ret == NRFX_SUCCESS);
state = AUDIO_I2S_STATE_IDLE;
/* Initialize capturing of current timestamps */
ret = nrfx_dppi_channel_alloc(&dppi, &dppi_channel_i2s_tick_capture);
if (ret - NRFX_ERROR_BASE_NUM) {
LOG_ERR("nrfx DPPI channel alloc error (I2S RXPupd) - Return value: %d", ret);
return -ENOMEM;
}
nrf_timer_subscribe_set(i2s_meas_timer.p_reg,
NRF_TIMER_CC_CHANNEL0,
dppi_channel_i2s_tick_capture);
nrf_i2s_publish_set(i2s_inst.p_reg, NRF_I2S_EVENT_TXPTRUPD, dppi_channel_i2s_tick_capture);
ret = nrfx_dppi_channel_enable(&dppi, dppi_channel_i2s_tick_capture);
if (ret - NRFX_ERROR_BASE_NUM) {
LOG_ERR("nrfx DPPI channel enable error (I2S RXPupd) - Return value: %d", ret);
return -EIO;
}
}