Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

samples: usb: add UAC2 implicit feedback sample #70029

Merged
merged 1 commit into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -603,6 +603,8 @@ The following Product IDs are currently used:
+----------------------------------------------------+--------+
| :zephyr:code-sample:`uac2-explicit-feedback` | 0x000E |
+----------------------------------------------------+--------+
| :zephyr:code-sample:`uac2-implicit-feedback` | 0x000F |
+----------------------------------------------------+--------+
| :zephyr:code-sample:`usb-dfu` (DFU Mode) | 0xFFFF |
+----------------------------------------------------+--------+

Expand Down
2 changes: 2 additions & 0 deletions doc/connectivity/usb/device_next/usb_device.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ Samples

* :zephyr:code-sample:`uac2-explicit-feedback`

* :zephyr:code-sample:`uac2-implicit-feedback`

Samples ported to new USB device support
----------------------------------------

Expand Down
14 changes: 14 additions & 0 deletions samples/subsys/usb/uac2_implicit_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()
9 changes: 9 additions & 0 deletions samples/subsys/usb/uac2_implicit_feedback/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2023-2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

# 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"
41 changes: 41 additions & 0 deletions samples/subsys/usb/uac2_implicit_feedback/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.. zephyr:code-sample:: uac2-implicit-feedback
:name: USB Audio asynchronous implicit feedback sample
:relevant-api: usbd_api uac2_device i2s_interface

USB Audio 2 implicit feedback sample playing stereo and recording mono audio
on I2S interface.

Overview
********

This sample demonstrates how to implement USB asynchronous bidirectional audio
with implicit feedback. The host adjusts number of stereo samples sent for
headphones playback based on the number of mono microphone samples received.

Requirements
************

Target must be able to measure I2S block start (i.e. first sample from output
buffer gets out) relative to USB SOF. The relative offset must be reported with
single sample accuracy.

This sample has been tested on :ref:`nrf5340dk_nrf5340`. While for actual audio
experience it is necessary to connect external I2S ADC and I2S DAC, simple echo
can be accomplished by shorting I2S data output with I2S data input.

Theoretically it should be possible to obtain the timing information based on
I2S and USB interrupts, but currently neither subsystem currently provides
necessary timestamp information.

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

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

To build and flash the application:

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

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

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

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 = <BIDIRECTIONAL_TERMINAL_HEADSET>;
assoc-terminal = <&mic_input>;
};

mic_input: microphone {
compatible = "zephyr,uac2-input-terminal";
clock-source = <&uac_aclk>;
terminal-type = <BIDIRECTIONAL_TERMINAL_HEADSET>;
/* Circular reference, macros will figure it out and
* provide correct associated terminal ID because the
* terminals associations are always 1-to-1.
*
* assoc-terminal = <&headphones_output>;
*/
front-left;
};

in_terminal: in_terminal {
compatible = "zephyr,uac2-output-terminal";
data-source = <&mic_input>;
clock-source = <&uac_aclk>;
terminal-type = <USB_TERMINAL_STREAMING>;
};

as_iso_out: out_interface {
compatible = "zephyr,uac2-audio-streaming";
linked-terminal = <&out_terminal>;
implicit-feedback;
subslot-size = <2>;
bit-resolution = <16>;
};

as_iso_in: in_interface {
compatible = "zephyr,uac2-audio-streaming";
linked-terminal = <&in_terminal>;
implicit-feedback;
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,29 @@
/*
* 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)>,
<NRF_PSEL(I2S_SDIN, 1, 14)>;
};
};
};

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

i2s_rxtx: &i2s0 {
status = "okay";
pinctrl-0 = <&i2s0_default_alt>;
pinctrl-names = "default";
clock-source = "ACLK";
};
12 changes: 12 additions & 0 deletions samples/subsys/usb/uac2_implicit_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=0x000F
CONFIG_SAMPLE_USBD_PRODUCT="UAC2 implicit feedback sample"

#LOG subsystem related configs
CONFIG_LOG=y
CONFIG_USBD_LOG_LEVEL_WRN=y
CONFIG_UDC_DRIVER_LOG_LEVEL_WRN=y
10 changes: 10 additions & 0 deletions samples/subsys/usb/uac2_implicit_feedback/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
sample:
name: USB Audio 2 asynchronous implicit feedback sample
tests:
sample.subsys.usb.uac2_implicit_feedback:
depends_on:
- usbd
- i2s
tags: usb i2s
platform_allow: nrf5340dk/nrf5340/cpuapp
harness: TBD
35 changes: 35 additions & 0 deletions samples/subsys/usb/uac2_implicit_feedback/src/feedback.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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);

/* Return offset between I2S block start and USB SOF in samples.
*
* Positive offset means that I2S block started at least 1 sample after SOF and
* to correct the situation, shorter than nominal buffers are needed.
*
* Negative offset means that I2S block started at least 1 sample before SOF and
* to correct the situation, larger than nominal buffers are needed.
*
* Offset 0 means that I2S block started within 1 sample around SOF. This is the
* dominant value expected during normal operation.
*/
int feedback_samples_offset(struct feedback_ctx *ctx);

#endif /* FEEDBACK_H_ */
39 changes: 39 additions & 0 deletions samples/subsys/usb/uac2_implicit_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"

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

void feedback_process(struct feedback_ctx *ctx)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With should normally be executed in these functions? Can some comments be added to guide customers?

Copy link
Contributor Author

@tmon-nordic tmon-nordic Apr 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read the comment in feedback.h:
https://github.com/zephyrproject-rtos/zephyr/blob/602054402313f885c2a722616ceee87c735668b6/samples/subsys/usb/uac2_implicit_feedback/src/feedback.h#L22-L33

The comment refers to what has to be reported. How this is achieved is entirely up to the implementer.

I only know how to achieve it using DPPI subsystem and TIMER on nRF53. I hope that eventually someone will come up with platform independent solution, but this is not a trivial task at all and I think it would require some sort of interrupt/event timestamping API that is simply not available yet (and some clever filtering because the control goal is to have I2S FRAMESTART and USB SOF happen at the same time, where obviously one interrupt handler will execute before the other and the two will keep "swapping" with each other which one executes first).

{
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);
}

int feedback_samples_offset(struct feedback_ctx *ctx)
{
ARG_UNUSED(ctx);

/* Always send nominal number of samples */
return 0;
}
Loading
Loading