Skip to content

Commit

Permalink
samples: usb: add UAC2 explicit feedback sample
Browse files Browse the repository at this point in the history
The sample illustrates how to calculate feedback value based on I2S
sample clock measurement or indirect I2S FRAMESTART and USBD SOF.
The sample is currently only supported on nrf5340dk_nrf5340_cpuapp
target because the feedback measurement uses target specific
peripherals.

While it should be possible to perform feedback value calculation
entirely in software (possibly with some additional filtering due
to software scheduling jitter), the I2S API does not provide necessary
timestamps.

Signed-off-by: Tomasz Moń <tomasz.mon@nordicsemi.no>
  • Loading branch information
tmon-nordic committed Feb 1, 2024
1 parent c0cd83a commit 73a3f2a
Show file tree
Hide file tree
Showing 13 changed files with 950 additions and 0 deletions.
2 changes: 2 additions & 0 deletions doc/connectivity/usb/device/usb_device.rst
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,8 @@ The following Product IDs are currently used:
+----------------------------------------------------+--------+
| :zephyr:code-sample:`wpan-usb` | 0x000D |
+----------------------------------------------------+--------+
| :zephyr:code-sample:`uac2-explicit-feedback` | 0x000E |
+----------------------------------------------------+--------+

The USB device descriptor field ``bcdDevice`` (Device Release Number) represents
the Zephyr kernel major and minor versions as a binary coded decimal value.
14 changes: 14 additions & 0 deletions samples/subsys/usb/uac2_explicit_feedback/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(usb_audio_async_i2s)

include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake)
target_sources(app PRIVATE src/main.c)

if (CONFIG_SOC_COMPATIBLE_NRF5340_CPUAPP)
target_sources(app PRIVATE src/feedback_nrf53.c)
else()
target_sources(app PRIVATE src/feedback_dummy.c)
endif()
19 changes: 19 additions & 0 deletions samples/subsys/usb/uac2_explicit_feedback/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright (c) 2023-2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

menu "UAC2 external feedback sample options"

config APP_USE_I2S_LRCLK_EDGES_COUNTER
bool "Measure I2S LRCLK edges directly"
help
Use this to use I2S LRCLK edge counting for calculating feedback.
On nRF53 this option requires externally connecting I2S LRCLK back to
separate GPIOTE input pin (P1.09).
endmenu

# Source common USB sample options used to initialize new experimental USB
# device stack. The scope of these options is limited to USB samples in project
# tree, you cannot use them in your own application.
source "samples/subsys/usb/common/Kconfig.sample_usbd"

source "Kconfig.zephyr"
64 changes: 64 additions & 0 deletions samples/subsys/usb/uac2_explicit_feedback/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
.. zephyr:code-sample:: uac2-explicit-feedback
:name: USB Audio asynchronous explicit feedback sample
:relevant-api: _usb_device_core_api i2s_interface

USB Audio 2 explicit feedback sample playing audio on I2S.

Overview
********

This sample demonstrates how to implement USB asynchronous audio playback with
explicit feedback. It can run on any board with USB and I2S support, but the
feedback calculation is currently only implemented for the Nordic nRF5340 IC.

The device running this sample presents itself to the host as a Full-Speed
Asynchronous USB Audio 2 class device supporting 48 kHz 16-bit 2-channel
(stereo) playback.

Explicit Feedback
*****************

Asynchronous USB Audio is used when the actual sample clock is not controlled by
USB host. Because the sample clock is independent from USB SOF it is inevitable
that 1 ms according to audio sink (I2S) will be either slightly longer or
shorter than 1 ms according to audio source (USB host). In the long run, this
discrepancy leads to overruns or underruns. By providing explicit feedback to
host, the device can tell host how many samples on average it needs every frame.
The host achieves the average by sending either nominal or nominal ±1 sample
packets every frame.

The dummy feedback implementation, used when there is no target-specific
feedback code available, returns a feedback value that results in host sending
nominal number of samples every frame. Theoretically it should be possible to
obtain the timing information based on I2S and USB interrupts, but currently
neither subsystem provides the necessary timestamp information.

Explcit Feedback on nRF5340
***************************

The nRF5340 is capable of counting both edges of I2S LRCLK relative to USB SOF
with the use of DPPI, TIMER and GPIOTE input. Alternatively, if the GPIOTE input
is not available, the DPPI and TIMER peripherals on nRF5340 can be configured to
provide relative timing information between I2S FRAMESTART and USB SOF.

This sample in both modes (direct sample counting and indirect I2S buffer output
to USB SOF offset) has been tested on :ref:`nrf5340dk_nrf5340`.

The sample defaults to indirect feedback calculation because direct sample
counting requires external connection between I2S LRCLK output pin to GPIOTE
input pin (hardcoded to P1.09) on :ref:`nrf5340dk_nrf5340`. In the indirect mode
no extra connections are necessary and the sample can even be used without any
I2S device connected where I2S signals can be checked e.g. on logic analyzer.

Building and Running
********************

The code can be found in :zephyr_file:`samples/subsys/usb/uac2_explicit_feedback`.

To build and flash the application:

.. zephyr-app-commands::
:zephyr-app: samples/subsys/usb/uac2_explicit_feedback
:board: nrf5340dk_nrf5340_cpuapp
:goals: build flash
:compact:
44 changes: 44 additions & 0 deletions samples/subsys/usb/uac2_explicit_feedback/app.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <dt-bindings/usb/audio.h>

/ {
uac2_headphones: usb_audio2 {
compatible = "zephyr,uac2";
status = "okay";
audio-function = <AUDIO_FUNCTION_OTHER>;

uac_aclk: aclk {
compatible = "zephyr,uac2-clock-source";
clock-type = "internal-programmable";
frequency-control = "host-programmable";
sampling-frequencies = <48000>;
};

out_terminal: out_terminal {
compatible = "zephyr,uac2-input-terminal";
clock-source = <&uac_aclk>;
terminal-type = <USB_TERMINAL_STREAMING>;
front-left;
front-right;
};

headphones_output: headphones {
compatible = "zephyr,uac2-output-terminal";
data-source = <&out_terminal>;
clock-source = <&uac_aclk>;
terminal-type = <OUTPUT_TERMINAL_HEADPHONES>;
};

as_iso_out: out_interface {
compatible = "zephyr,uac2-audio-streaming";
linked-terminal = <&out_terminal>;
subslot-size = <2>;
bit-resolution = <16>;
};
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#Enable timer for asynchronous feedback
CONFIG_NRFX_TIMER2=y
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "../app.overlay"

&pinctrl {
i2s0_default_alt: i2s0_default_alt {
group1 {
psels = <NRF_PSEL(I2S_SCK_M, 1, 15)>,
<NRF_PSEL(I2S_LRCK_M, 1, 12)>,
<NRF_PSEL(I2S_SDOUT, 1, 13)>;
};
};
};

&clock {
hfclkaudio-frequency = <12288000>;
};

i2s_tx: &i2s0 {
status = "okay";
pinctrl-0 = <&i2s0_default_alt>;
pinctrl-names = "default";
clock-source = "ACLK";
};
12 changes: 12 additions & 0 deletions samples/subsys/usb/uac2_explicit_feedback/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CONFIG_I2S=y

#USB related configs
CONFIG_USB_DEVICE_STACK_NEXT=y
CONFIG_USBD_AUDIO2_CLASS=y
CONFIG_SAMPLE_USBD_PID=0x000E
CONFIG_SAMPLE_USBD_PRODUCT="UAC2 explicit feedback sample"

#LOG subsystem related configs
CONFIG_LOG=y
CONFIG_USBD_LOG_LEVEL_WRN=y
CONFIG_UDC_DRIVER_LOG_LEVEL_WRN=y
6 changes: 6 additions & 0 deletions samples/subsys/usb/uac2_explicit_feedback/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
sample:
name: USB Audio 2 asynchronous explicit feedback sample
tests:
sample.subsys.usb.uac2_explicit_feedback:
tags: usb i2s
platform_allow: nrf5340dk_nrf5340_cpuapp
23 changes: 23 additions & 0 deletions samples/subsys/usb/uac2_explicit_feedback/src/feedback.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef FEEDBACK_H_
#define FEEDBACK_H_

#include <stdint.h>

/* Nominal number of samples received on each SOF. This sample is currently
* supporting only 48 kHz sample rate.
*/
#define SAMPLES_PER_SOF 48

struct feedback_ctx *feedback_init(void);
void feedback_reset_ctx(struct feedback_ctx *ctx);
void feedback_process(struct feedback_ctx *ctx);
void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued);
uint32_t feedback_value(struct feedback_ctx *ctx);

#endif /* FEEDBACK_H_ */
39 changes: 39 additions & 0 deletions samples/subsys/usb/uac2_explicit_feedback/src/feedback_dummy.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include "feedback.h"

#warning "No target specific feedback code, overruns/underruns will occur"

#define FEEDBACK_K 10

struct feedback_ctx *feedback_init(void)
{
return NULL;
}

void feedback_process(struct feedback_ctx *ctx)
{
ARG_UNUSED(ctx);
}

void feedback_reset_ctx(struct feedback_ctx *ctx)
{
ARG_UNUSED(ctx);
}

void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued)
{
ARG_UNUSED(ctx);
ARG_UNUSED(i2s_blocks_queued);
}

uint32_t feedback_value(struct feedback_ctx *ctx)
{
/* Always request nominal number of samples */
return SAMPLES_PER_SOF << FEEDBACK_K;
}
Loading

0 comments on commit 73a3f2a

Please sign in to comment.