diff --git a/drivers/serial/Kconfig.nrfx b/drivers/serial/Kconfig.nrfx index a39f882f644..1e5087dc1c1 100644 --- a/drivers/serial/Kconfig.nrfx +++ b/drivers/serial/Kconfig.nrfx @@ -213,39 +213,4 @@ nrfx_uart_num = 137 rsource "Kconfig.nrfx_uart_instance" endif -config NRFX_TIMER0 - default y - depends on UART_0_NRF_HW_ASYNC_TIMER = 0 \ - || UART_1_NRF_HW_ASYNC_TIMER = 0 \ - || UART_2_NRF_HW_ASYNC_TIMER = 0 \ - || UART_3_NRF_HW_ASYNC_TIMER = 0 - -config NRFX_TIMER1 - default y - depends on UART_0_NRF_HW_ASYNC_TIMER = 1 \ - || UART_1_NRF_HW_ASYNC_TIMER = 1 \ - || UART_2_NRF_HW_ASYNC_TIMER = 1 \ - || UART_3_NRF_HW_ASYNC_TIMER = 1 - -config NRFX_TIMER2 - default y - depends on UART_0_NRF_HW_ASYNC_TIMER = 2 \ - || UART_1_NRF_HW_ASYNC_TIMER = 2 \ - || UART_2_NRF_HW_ASYNC_TIMER = 2 \ - || UART_3_NRF_HW_ASYNC_TIMER = 2 - -config NRFX_TIMER3 - default y - depends on UART_0_NRF_HW_ASYNC_TIMER = 3 \ - || UART_1_NRF_HW_ASYNC_TIMER = 3 \ - || UART_2_NRF_HW_ASYNC_TIMER = 3 \ - || UART_3_NRF_HW_ASYNC_TIMER = 3 - -config NRFX_TIMER4 - default y - depends on UART_0_NRF_HW_ASYNC_TIMER = 4 \ - || UART_1_NRF_HW_ASYNC_TIMER = 4 \ - || UART_2_NRF_HW_ASYNC_TIMER = 4 \ - || UART_3_NRF_HW_ASYNC_TIMER = 4 - endif # UART_NRFX diff --git a/drivers/serial/uart_nrfx_uarte.c b/drivers/serial/uart_nrfx_uarte.c index 00489e7d885..084e6592f27 100644 --- a/drivers/serial/uart_nrfx_uarte.c +++ b/drivers/serial/uart_nrfx_uarte.c @@ -306,6 +306,7 @@ struct uarte_nrfx_data { #endif #ifdef UARTE_ANY_ASYNC struct uarte_async_cb *async; + nrfx_timer_t timer; #endif atomic_val_t poll_out_lock; atomic_t flags; @@ -448,7 +449,6 @@ struct uarte_nrfx_config { size_t bounce_buf_swap_len; struct uarte_async_rx_cbwt *cbwt_data; #endif - nrfx_timer_t timer; uint8_t *tx_cache; uint8_t *rx_flush_buf; #endif @@ -878,7 +878,7 @@ static void uarte_periph_enable(const struct device *dev) #ifdef UARTE_ANY_ASYNC if (data->async) { if (HW_RX_COUNTING_ENABLED(config)) { - const nrfx_timer_t *timer = &config->timer; + nrfx_timer_t *timer = &data->timer; nrfx_timer_enable(timer); @@ -1067,13 +1067,13 @@ static int uarte_nrfx_rx_counting_init(const struct device *dev) if (HW_RX_COUNTING_ENABLED(cfg)) { nrfx_timer_config_t tmr_config = NRFX_TIMER_DEFAULT_CONFIG( - NRF_TIMER_BASE_FREQUENCY_GET(cfg->timer.p_reg)); + NRF_TIMER_BASE_FREQUENCY_GET(data->timer.p_reg)); uint32_t evt_addr = nrf_uarte_event_address_get(uarte, NRF_UARTE_EVENT_RXDRDY); - uint32_t tsk_addr = nrfx_timer_task_address_get(&cfg->timer, NRF_TIMER_TASK_COUNT); + uint32_t tsk_addr = nrfx_timer_task_address_get(&data->timer, NRF_TIMER_TASK_COUNT); tmr_config.mode = NRF_TIMER_MODE_COUNTER; tmr_config.bit_width = NRF_TIMER_BIT_WIDTH_32; - ret = nrfx_timer_init(&cfg->timer, + ret = nrfx_timer_init(&data->timer, &tmr_config, timer_handler); if (ret != NRFX_SUCCESS) { @@ -1081,12 +1081,12 @@ static int uarte_nrfx_rx_counting_init(const struct device *dev) return -EINVAL; } - nrfx_timer_clear(&cfg->timer); + nrfx_timer_clear(&data->timer); ret = nrfx_gppi_channel_alloc(&data->async->rx.cnt.ppi); if (ret != NRFX_SUCCESS) { LOG_ERR("Failed to allocate PPI Channel"); - nrfx_timer_uninit(&cfg->timer); + nrfx_timer_uninit(&data->timer); return -EINVAL; } @@ -3202,7 +3202,6 @@ static void uarte_pm_resume(const struct device *dev) const struct uarte_nrfx_config *cfg = dev->config; if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) || !LOW_POWER_ENABLED(cfg)) { - (void)pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); uarte_periph_enable(dev); } } @@ -3640,8 +3639,8 @@ static int uarte_instance_deinit(const struct device *dev) IF_ENABLED(CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER, \ (UARTE_COUNT_BYTES_WITH_TIMER_CONFIG(idx))) \ IF_ENABLED(CONFIG_UART_##idx##_NRF_HW_ASYNC, \ - (.timer = NRFX_TIMER_INSTANCE( \ - CONFIG_UART_##idx##_NRF_HW_ASYNC_TIMER),)) \ + (.timer = NRFX_TIMER_INSTANCE(NRF_TIMER_INST_GET( \ + CONFIG_UART_##idx##_NRF_HW_ASYNC_TIMER)),)) \ IF_ENABLED(INSTANCE_IS_FAST(_, /*empty*/, idx, _), \ (.clk_dev = DEVICE_DT_GET_OR_NULL(DT_CLOCKS_CTLR(UARTE(idx))), \ .clk_spec = { \ diff --git a/modules/hal_nordic/nrfx/Kconfig b/modules/hal_nordic/nrfx/Kconfig index 523931be649..30f1aac0607 100644 --- a/modules/hal_nordic/nrfx/Kconfig +++ b/modules/hal_nordic/nrfx/Kconfig @@ -279,8 +279,7 @@ config NRFX_MRAMC config NRFX_NFCT bool "NFCT driver" depends on $(dt_nodelabel_exists,nfct) - select NRFX_TIMER4 if SOC_SERIES_NRF52X - select NRFX_TIMER2 if SOC_SERIES_NRF53X + select NRFX_TIMER config NRFX_NVMC bool "NVMC driver" @@ -764,132 +763,7 @@ config NRFX_TEMP depends on $(dt_nodelabel_exists,temp) config NRFX_TIMER - bool - -config NRFX_TIMER0 - bool "TIMER0 driver instance" - depends on $(dt_nodelabel_exists,timer0) - select NRFX_TIMER - -config NRFX_TIMER1 - bool "TIMER1 driver instance" - depends on $(dt_nodelabel_exists,timer1) - select NRFX_TIMER - -config NRFX_TIMER2 - bool "TIMER2 driver instance" - depends on $(dt_nodelabel_exists,timer2) - select NRFX_TIMER - -config NRFX_TIMER3 - bool "TIMER3 driver instance" - depends on $(dt_nodelabel_exists,timer3) - select NRFX_TIMER - -config NRFX_TIMER4 - bool "TIMER4 driver instance" - depends on $(dt_nodelabel_exists,timer4) - select NRFX_TIMER - -config NRFX_TIMER00 - bool "TIMER00 driver instance" - depends on $(dt_nodelabel_exists,timer00) - select NRFX_TIMER - -config NRFX_TIMER10 - bool "TIMER10 driver instance" - depends on $(dt_nodelabel_exists,timer10) - select NRFX_TIMER - -config NRFX_TIMER20 - bool "TIMER20 driver instance" - depends on $(dt_nodelabel_exists,timer20) - select NRFX_TIMER - -config NRFX_TIMER21 - bool "TIMER21 driver instance" - depends on $(dt_nodelabel_exists,timer21) - select NRFX_TIMER - -config NRFX_TIMER22 - bool "TIMER22 driver instance" - depends on $(dt_nodelabel_exists,timer22) - select NRFX_TIMER - -config NRFX_TIMER23 - bool "TIMER23 driver instance" - depends on $(dt_nodelabel_exists,timer23) - select NRFX_TIMER - -config NRFX_TIMER24 - bool "TIMER24 driver instance" - depends on $(dt_nodelabel_exists,timer24) - select NRFX_TIMER - -config NRFX_TIMER020 - bool "TIMER020 driver instance" - depends on $(dt_nodelabel_exists,timer020) - select NRFX_TIMER - -config NRFX_TIMER021 - bool "TIMER021 driver instance" - depends on $(dt_nodelabel_exists,timer021) - select NRFX_TIMER - -config NRFX_TIMER022 - bool "TIMER022 driver instance" - depends on $(dt_nodelabel_exists,timer022) - select NRFX_TIMER - -config NRFX_TIMER120 - bool "TIMER120 driver instance" - depends on $(dt_nodelabel_exists,timer120) - select NRFX_TIMER - -config NRFX_TIMER121 - bool "TIMER121 driver instance" - depends on $(dt_nodelabel_exists,timer121) - select NRFX_TIMER - -config NRFX_TIMER130 - bool "TIMER130 driver instance" - depends on $(dt_nodelabel_exists,timer130) - select NRFX_TIMER - -config NRFX_TIMER131 - bool "TIMER131 driver instance" - depends on $(dt_nodelabel_exists,timer131) - select NRFX_TIMER - -config NRFX_TIMER132 - bool "TIMER132 driver instance" - depends on $(dt_nodelabel_exists,timer132) - select NRFX_TIMER - -config NRFX_TIMER133 - bool "TIMER133 driver instance" - depends on $(dt_nodelabel_exists,timer133) - select NRFX_TIMER - -config NRFX_TIMER134 - bool "TIMER134 driver instance" - depends on $(dt_nodelabel_exists,timer134) - select NRFX_TIMER - -config NRFX_TIMER135 - bool "TIMER135 driver instance" - depends on $(dt_nodelabel_exists,timer135) - select NRFX_TIMER - -config NRFX_TIMER136 - bool "TIMER136 driver instance" - depends on $(dt_nodelabel_exists,timer136) - select NRFX_TIMER - -config NRFX_TIMER137 - bool "TIMER137 driver instance" - depends on $(dt_nodelabel_exists,timer137) - select NRFX_TIMER + bool "TIMER driver" config NRFX_TWI bool diff --git a/modules/hal_nordic/nrfx/nrfx_glue.c b/modules/hal_nordic/nrfx/nrfx_glue.c index 4e7fc94e11d..dd972ee039d 100644 --- a/modules/hal_nordic/nrfx/nrfx_glue.c +++ b/modules/hal_nordic/nrfx/nrfx_glue.c @@ -45,3 +45,25 @@ char const *nrfx_error_string_get(nrfx_err_t code) default: return "unknown"; } } + +char const *nrfx_new_error_string_get(int code) +{ + #define NRFX_NEW_ERROR_STRING_CASE(code) case code: return #code + switch (-code) + { + NRFX_NEW_ERROR_STRING_CASE(0); + NRFX_NEW_ERROR_STRING_CASE(ECANCELED); + NRFX_NEW_ERROR_STRING_CASE(ENOMEM); + NRFX_NEW_ERROR_STRING_CASE(ENOTSUP); + NRFX_NEW_ERROR_STRING_CASE(EINVAL); + NRFX_NEW_ERROR_STRING_CASE(EINPROGRESS); + NRFX_NEW_ERROR_STRING_CASE(E2BIG); + NRFX_NEW_ERROR_STRING_CASE(ETIMEDOUT); + NRFX_NEW_ERROR_STRING_CASE(EPERM); + NRFX_NEW_ERROR_STRING_CASE(EFAULT); + NRFX_NEW_ERROR_STRING_CASE(EACCES); + NRFX_NEW_ERROR_STRING_CASE(EBUSY); + NRFX_NEW_ERROR_STRING_CASE(EALREADY); + default: return "unknown"; + } +} diff --git a/modules/hal_nordic/nrfx/nrfx_kconfig.h b/modules/hal_nordic/nrfx/nrfx_kconfig.h index 4f0d594a212..c65a75cd0c6 100644 --- a/modules/hal_nordic/nrfx/nrfx_kconfig.h +++ b/modules/hal_nordic/nrfx/nrfx_kconfig.h @@ -659,84 +659,6 @@ #ifdef CONFIG_NRFX_TIMER #define NRFX_TIMER_ENABLED 1 #endif -#ifdef CONFIG_NRFX_TIMER_LOG -#define NRFX_TIMER_CONFIG_LOG_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER0 -#define NRFX_TIMER0_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER1 -#define NRFX_TIMER1_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER2 -#define NRFX_TIMER2_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER3 -#define NRFX_TIMER3_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER4 -#define NRFX_TIMER4_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER00 -#define NRFX_TIMER00_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER10 -#define NRFX_TIMER10_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER20 -#define NRFX_TIMER20_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER21 -#define NRFX_TIMER21_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER22 -#define NRFX_TIMER22_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER23 -#define NRFX_TIMER23_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER24 -#define NRFX_TIMER24_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER020 -#define NRFX_TIMER020_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER021 -#define NRFX_TIMER021_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER022 -#define NRFX_TIMER022_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER120 -#define NRFX_TIMER120_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER121 -#define NRFX_TIMER121_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER130 -#define NRFX_TIMER130_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER131 -#define NRFX_TIMER131_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER132 -#define NRFX_TIMER132_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER133 -#define NRFX_TIMER133_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER134 -#define NRFX_TIMER134_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER135 -#define NRFX_TIMER135_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER136 -#define NRFX_TIMER136_ENABLED 1 -#endif -#ifdef CONFIG_NRFX_TIMER137 -#define NRFX_TIMER137_ENABLED 1 -#endif #ifdef CONFIG_NRFX_TWI #define NRFX_TWI_ENABLED 1 diff --git a/modules/hal_nordic/nrfx/nrfx_log.h b/modules/hal_nordic/nrfx/nrfx_log.h index 682388d7dd1..973ca672b53 100644 --- a/modules/hal_nordic/nrfx/nrfx_log.h +++ b/modules/hal_nordic/nrfx/nrfx_log.h @@ -128,6 +128,16 @@ LOG_MODULE_REGISTER(NRFX_MODULE_PREFIX, NRFX_MODULE_LOG_LEVEL); #define NRFX_LOG_ERROR_STRING_GET(error_code) nrfx_error_string_get(error_code) extern char const *nrfx_error_string_get(nrfx_err_t code); +/** + * @brief Macro for getting the textual representation of a given errno error code. + * + * @param[in] error_code Errno error code. + * + * @return String containing the textual representation of the errno error code. + */ +#define NRFX_NEW_LOG_ERROR_STRING_GET(error_code) nrfx_new_error_string_get(error_code) +extern char const *nrfx_new_error_string_get(int code); + /** @} */ #ifdef __cplusplus diff --git a/samples/subsys/usb/uac2_explicit_feedback/app.overlay b/samples/subsys/usb/uac2_explicit_feedback/app.overlay index 3ff71797aa9..21119a79206 100644 --- a/samples/subsys/usb/uac2_explicit_feedback/app.overlay +++ b/samples/subsys/usb/uac2_explicit_feedback/app.overlay @@ -11,6 +11,7 @@ compatible = "zephyr,uac2"; status = "okay"; full-speed; + high-speed; audio-function = ; uac_aclk: aclk { diff --git a/samples/subsys/usb/uac2_explicit_feedback/boards/nrf5340dk_nrf5340_cpuapp.conf b/samples/subsys/usb/uac2_explicit_feedback/boards/nrf5340dk_nrf5340_cpuapp.conf index ce67033381a..4c7c060177e 100644 --- a/samples/subsys/usb/uac2_explicit_feedback/boards/nrf5340dk_nrf5340_cpuapp.conf +++ b/samples/subsys/usb/uac2_explicit_feedback/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -1,2 +1,2 @@ #Enable timer for asynchronous feedback -CONFIG_NRFX_TIMER2=y +CONFIG_NRFX_TIMER=y diff --git a/samples/subsys/usb/uac2_explicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf b/samples/subsys/usb/uac2_explicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf index 0e35fa1f491..ce07cde3656 100644 --- a/samples/subsys/usb/uac2_explicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf +++ b/samples/subsys/usb/uac2_explicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf @@ -1,8 +1,4 @@ # Enable timer for asynchronous feedback CONFIG_NRFX_GPPI=y -CONFIG_NRFX_TIMER131=y +CONFIG_NRFX_TIMER=y CONFIG_NRFX_GPIOTE130=y - -# Sample is Full-Speed only, prevent High-Speed enumeration -CONFIG_UDC_DRIVER_HIGH_SPEED_SUPPORT_ENABLED=n -CONFIG_USBD_MAX_SPEED_FULL=y diff --git a/samples/subsys/usb/uac2_explicit_feedback/src/feedback.h b/samples/subsys/usb/uac2_explicit_feedback/src/feedback.h index 7f5bf2027d8..37c1dc975e5 100644 --- a/samples/subsys/usb/uac2_explicit_feedback/src/feedback.h +++ b/samples/subsys/usb/uac2_explicit_feedback/src/feedback.h @@ -9,15 +9,14 @@ #include -/* Nominal number of samples received on each SOF. This sample is currently - * supporting only 48 kHz sample rate. - */ -#define SAMPLES_PER_SOF 48 +/* This sample is currently supporting only 48 kHz sample rate. */ +#define SAMPLE_RATE 48000 struct feedback_ctx *feedback_init(void); -void feedback_reset_ctx(struct feedback_ctx *ctx); +void feedback_reset_ctx(struct feedback_ctx *ctx, bool microframes); void feedback_process(struct feedback_ctx *ctx); -void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued); +void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued, + bool microframes); uint32_t feedback_value(struct feedback_ctx *ctx); - +#define SAMPLES_PER_SOF 48 #endif /* FEEDBACK_H_ */ diff --git a/samples/subsys/usb/uac2_explicit_feedback/src/feedback_dummy.c b/samples/subsys/usb/uac2_explicit_feedback/src/feedback_dummy.c index 7e274ca2ed0..378a43e06ef 100644 --- a/samples/subsys/usb/uac2_explicit_feedback/src/feedback_dummy.c +++ b/samples/subsys/usb/uac2_explicit_feedback/src/feedback_dummy.c @@ -5,15 +5,23 @@ */ #include +#include #include "feedback.h" #warning "No target specific feedback code, overruns/underruns will occur" -#define FEEDBACK_K 10 +#define FEEDBACK_FS_K 10 +#define FEEDBACK_FS_SHIFT 4 +#define FEEDBACK_HS_K 13 +#define FEEDBACK_HS_SHIFT 3 + +static struct feedback_ctx { + bool high_speed; +} fb_ctx; struct feedback_ctx *feedback_init(void) { - return NULL; + return &fb_ctx; } void feedback_process(struct feedback_ctx *ctx) @@ -21,19 +29,25 @@ void feedback_process(struct feedback_ctx *ctx) ARG_UNUSED(ctx); } -void feedback_reset_ctx(struct feedback_ctx *ctx) +void feedback_reset_ctx(struct feedback_ctx *ctx, bool microframes) { - ARG_UNUSED(ctx); + ctx->high_speed = microframes; } -void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued) +void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued, + bool microframes) { - ARG_UNUSED(ctx); ARG_UNUSED(i2s_blocks_queued); + + ctx->high_speed = microframes; } uint32_t feedback_value(struct feedback_ctx *ctx) { /* Always request nominal number of samples */ - return SAMPLES_PER_SOF << FEEDBACK_K; + if (USBD_SUPPORTS_HIGH_SPEED && ctx->high_speed) { + return (SAMPLE_RATE / 8000) << (FEEDBACK_HS_K + FEEDBACK_HS_SHIFT); + } + + return (SAMPLE_RATE / 1000) << (FEEDBACK_FS_K + FEEDBACK_FS_SHIFT); } diff --git a/samples/subsys/usb/uac2_explicit_feedback/src/feedback_nrf.c b/samples/subsys/usb/uac2_explicit_feedback/src/feedback_nrf.c index 62a7ad05ef9..860048acaf7 100644 --- a/samples/subsys/usb/uac2_explicit_feedback/src/feedback_nrf.c +++ b/samples/subsys/usb/uac2_explicit_feedback/src/feedback_nrf.c @@ -63,8 +63,8 @@ static inline void feedback_target_init(void) static const nrfx_gpiote_t gpiote = NRFX_GPIOTE_INSTANCE(FEEDBACK_GPIOTE_INSTANCE_NUMBER); -static const nrfx_timer_t feedback_timer_instance = - NRFX_TIMER_INSTANCE(FEEDBACK_TIMER_INSTANCE_NUMBER); +static nrfx_timer_t feedback_timer_instance = + NRFX_TIMER_INSTANCE(NRF_TIMER_INST_GET(FEEDBACK_TIMER_INSTANCE_NUMBER)); /* See 5.12.4.2 Feedback in Universal Serial Bus Specification Revision 2.0 for * more information about the feedback. There is a direct implementation of the @@ -84,8 +84,14 @@ static const nrfx_timer_t feedback_timer_instance = * Full-Speed isochronous feedback is Q10.10 unsigned integer left-justified in * the 24-bits so it has Q10.14 format. This sample application puts zeroes to * the 4 least significant bits (does not use the bits for extra precision). + * + * High-Speed isochronous feedback is Q12.13 unsigned integer aligned in the + * 32-bits so the binary point is located between second and third byte so it + * has Q16.16 format. This sample applications puts zeroes to the 3 least + * significant bits (does not use the bits for extra precision). */ -#define FEEDBACK_K 10 +#define FEEDBACK_FS_K 10 +#define FEEDBACK_HS_K 13 #if defined(CONFIG_APP_USE_I2S_LRCLK_EDGES_COUNTER) #define FEEDBACK_P 1 #else @@ -93,11 +99,14 @@ static const nrfx_timer_t feedback_timer_instance = #endif #define FEEDBACK_FS_SHIFT 4 +#define FEEDBACK_HS_SHIFT 3 static struct feedback_ctx { uint32_t fb_value; int32_t rel_sof_offset; int32_t base_sof_offset; + uint32_t counts_per_sof; + bool high_speed; union { /* For edge counting */ struct { @@ -190,7 +199,7 @@ struct feedback_ctx *feedback_init(void) feedback_target_init(); - feedback_reset_ctx(&fb_ctx); + feedback_reset_ctx(&fb_ctx, false); if (IS_ENABLED(CONFIG_APP_USE_I2S_LRCLK_EDGES_COUNTER)) { err = feedback_edge_counter_setup(); @@ -239,13 +248,31 @@ struct feedback_ctx *feedback_init(void) return &fb_ctx; } +static uint32_t nominal_feedback_value(struct feedback_ctx *ctx) +{ + if (ctx->high_speed) { + return (SAMPLE_RATE / 8000) << (FEEDBACK_HS_K + FEEDBACK_HS_SHIFT); + } + + return (SAMPLE_RATE / 1000) << (FEEDBACK_FS_K + FEEDBACK_FS_SHIFT); +} + +static uint32_t feedback_period(struct feedback_ctx *ctx) +{ + if (ctx->high_speed) { + return BIT(FEEDBACK_HS_K - FEEDBACK_P); + } + + return BIT(FEEDBACK_FS_K - FEEDBACK_P); +} + static void update_sof_offset(struct feedback_ctx *ctx, uint32_t sof_cc, uint32_t framestart_cc) { int sof_offset; if (!IS_ENABLED(CONFIG_APP_USE_I2S_LRCLK_EDGES_COUNTER)) { - uint32_t clks_per_edge; + uint32_t nominator; /* Convert timer clock (independent from both Audio clock and * USB host SOF clock) to fake sample clock shifted by P values. @@ -255,18 +282,17 @@ static void update_sof_offset(struct feedback_ctx *ctx, uint32_t sof_cc, * when regulated and therefore the relative clock frequency * discrepancies are essentially negligible. */ - clks_per_edge = sof_cc / (SAMPLES_PER_SOF << FEEDBACK_P); - sof_cc /= MAX(clks_per_edge, 1); - framestart_cc /= MAX(clks_per_edge, 1); + nominator = MIN(sof_cc, framestart_cc) * ctx->counts_per_sof; + sof_offset = nominator / MAX(sof_cc, 1); + } else { + sof_offset = framestart_cc; } /* /2 because we treat the middle as a turning point from being * "too late" to "too early". */ - if (framestart_cc > (SAMPLES_PER_SOF << FEEDBACK_P)/2) { - sof_offset = framestart_cc - (SAMPLES_PER_SOF << FEEDBACK_P); - } else { - sof_offset = framestart_cc; + if (sof_offset > ctx->counts_per_sof/2) { + sof_offset -= ctx->counts_per_sof; } /* The heuristic above is not enough when the offset gets too large. @@ -279,17 +305,17 @@ static void update_sof_offset(struct feedback_ctx *ctx, uint32_t sof_cc, if (sof_offset >= 0) { abs_diff = sof_offset - ctx->rel_sof_offset; - base_change = -(SAMPLES_PER_SOF << FEEDBACK_P); + base_change = -ctx->counts_per_sof; } else { abs_diff = ctx->rel_sof_offset - sof_offset; - base_change = SAMPLES_PER_SOF << FEEDBACK_P; + base_change = ctx->counts_per_sof; } /* Adjust base offset only if the change happened through the * outer bound. The actual changes should be significantly lower * than the threshold here. */ - if (abs_diff > (SAMPLES_PER_SOF << FEEDBACK_P)/2) { + if (abs_diff > ctx->counts_per_sof/2) { ctx->base_sof_offset += base_change; } } @@ -297,8 +323,12 @@ static void update_sof_offset(struct feedback_ctx *ctx, uint32_t sof_cc, ctx->rel_sof_offset = sof_offset; } -static inline int32_t offset_to_correction(int32_t offset) +static inline int32_t offset_to_correction(struct feedback_ctx *ctx, int32_t offset) { + if (ctx->high_speed) { + return -(offset / BIT(FEEDBACK_P)) * BIT(FEEDBACK_HS_SHIFT); + } + return -(offset / BIT(FEEDBACK_P)) * BIT(FEEDBACK_FS_SHIFT); } @@ -320,39 +350,66 @@ static int32_t pi_update(struct feedback_ctx *ctx) int32_t error = SP - PV; /* - * With above normalization at Full-Speed, when data received during - * SOF n appears on I2S during SOF n+3, the Ziegler Nichols Ultimate - * Gain is around 1.15 and the oscillation period is around 90 SOF. - * (much nicer oscillations with 204.8 SOF period can be observed with - * gain 0.5 when the delay is not n+3, but n+33 - surprisingly the - * resulting PI coefficients after power of two rounding are the same). + * With above normalization, when data received during SOF n appears on + * I2S during SOF n+3, the Ziegler Nichols Ultimate Gain and oscillation + * periods are as follows: * - * Ziegler-Nichols rule with applied stability margin of 2 results in: - * Kc = 0.22 * Ku = 0.22 * 1.15 = 0.253 - * Ti = 0.83 * tu = 0.83 * 80 = 66.4 + * Full-Speed Linux: Ku = 1.34 tu=77 [FS SOFs] + * Full-Speed Mac OS: Ku = 0.173 tu=580 [FS SOFs] + * High-Speed Mac OS: Ku = 0.895 tu=4516 [HS SOFs] + * High-Speed Windows: Ku = 0.515 tu=819 [HS SOFs] * - * Converting the rules above to parallel PI gives: - * Kp = Kc = 0.253 - * Ki = Kc/Ti = 0.254/66.4 ~= 0.0038253 + * Linux and Mac OS oscillations were very neat, while Windows seems to + * be averaging feedback value and therefore it is hard to get steady + * oscillations without getting buffer uderrun. * - * Because we want fixed-point optimized non-tunable implementation, - * the parameters can be conveniently expressed with power of two: - * Kp ~= pow(2, -2) = 0.25 (divide by 4) - * Ki ~= pow(2, -8) = 0.0039 (divide by 256) + * Ziegler-Nichols rule with applied stability margin of 2 results in: + * [FS Linux] [FS Mac] [HS Mac] [HS Windows] + * Kc = 0.22 * Ku 0.2948 0.0381 0.1969 0.1133 + * Ti = 0.83 * tu 63.91 481.4 3748 647.9 * - * This can be implemented as: + * Converting the rules to work with following simple regulator: * ctx->integrator += error; - * return (error + (ctx->integrator / 64)) / 4; - * but unfortunately such regulator is pretty aggressive and keeps - * oscillating rather quickly around the setpoint (within +-1 sample). + * return (error + (ctx->integrator / Ti)) / invKc; + * + * gives following parameters: + * [FS Linux] [FS Mac] [HS Mac] [HS Windows] + * invKc = 1/Kc 3 26 5 8 + * Ti 64 482 3748 648 + * + * The respective regulators seem to give quarter-amplitude-damping on + * respective hosts, but tuning from one host can get into oscillations + * on another host. The regulation goal is to achieve a single set of + * parameters to be used with all hosts, the only parameter difference + * can be based on operating speed. + * + * After a number of tests with all the hosts, following parameters + * were determined to result in nice no-overshoot response: + * [Full-Speed] [High-Speed] + * invKc 128 128 + * Ti 2048 16384 + * + * The power-of-two parameters were arbitrarily chosen for rounding. + * The 16384 = 2048 * 8 can be considered as unifying integration time. * - * Manually tweaking the constants so the regulator output is shifted - * down by 4 bits (i.e. change /64 to /2048 and /4 to /128) yields - * really good results (the outcome is similar, even slightly better, - * than using I2S LRCLK edge counting directly). + * While the no-overshoot is also present with invKc as low as 32, such + * regulator is pretty aggressive and keeps oscillating rather quickly + * around the setpoint (within +-1 sample). Lowering the controller gain + * (increasing invKc value) yields really good results (the outcome is + * similar to using I2S LRCLK edge counting directly). + * + * The most challenging scenario is for the regulator to stabilize right + * after startup when I2S consumes data faster than nominal sample rate + * (48 kHz = 6 samples per SOF at High-Speed, 48 samples at Full-Speed) + * according to host (I2S consuming data slower slower than nominal + * sample rate is not problematic at all because buffer overrun does not + * stop I2S streaming). This regulator should be able to stabilize for + * any frequency that is within required USB SOF accuracy of 500 ppm, + * i.e. when nominal sample rate is 48 kHz the real sample rate can be + * anywhere in [47.976 kHz; 48.024 kHz] range. */ ctx->integrator += error; - return (error + (ctx->integrator / 2048)) / 128; + return (error + (ctx->integrator / (ctx->high_speed ? 16384 : 2048))) / 128; } void feedback_process(struct feedback_ctx *ctx) @@ -374,17 +431,21 @@ void feedback_process(struct feedback_ctx *ctx) ctx->fb_counter += sof_cc; ctx->fb_periods++; - if (ctx->fb_periods == BIT(FEEDBACK_K - FEEDBACK_P)) { + if (ctx->fb_periods == feedback_period(ctx)) { - /* fb_counter holds Q10.10 value, left-justify it */ - fb = ctx->fb_counter << FEEDBACK_FS_SHIFT; + if (ctx->high_speed) { + fb = ctx->fb_counter << FEEDBACK_HS_SHIFT; + } else { + /* fb_counter holds Q10.10 value, left-justify it */ + fb = ctx->fb_counter << FEEDBACK_FS_SHIFT; + } /* Align I2S FRAMESTART to USB SOF by adjusting reported * feedback value. This is endpoint specific correction * mentioned but not specified in USB 2.0 Specification. */ if (abs(offset) > BIT(FEEDBACK_P)) { - fb += offset_to_correction(offset); + fb += offset_to_correction(ctx, offset); } ctx->fb_value = fb; @@ -392,22 +453,25 @@ void feedback_process(struct feedback_ctx *ctx) ctx->fb_periods = 0; } } else { + const uint32_t zero_lsb_mask = ctx->high_speed ? 0x7 : 0xF; + /* Use PI controller to generate required feedback deviation * from nominal feedback value. */ - fb = SAMPLES_PER_SOF << (FEEDBACK_K + FEEDBACK_FS_SHIFT); + fb = nominal_feedback_value(ctx); /* Clear the additional LSB bits in feedback value, i.e. do not * use the optional extra resolution. */ - fb += pi_update(ctx) & ~0xF; + fb += pi_update(ctx) & ~zero_lsb_mask; ctx->fb_value = fb; } } -void feedback_reset_ctx(struct feedback_ctx *ctx) +void feedback_reset_ctx(struct feedback_ctx *ctx, bool microframes) { /* Reset feedback to nominal value */ - ctx->fb_value = SAMPLES_PER_SOF << (FEEDBACK_K + FEEDBACK_FS_SHIFT); + ctx->high_speed = microframes; + ctx->fb_value = nominal_feedback_value(ctx); if (IS_ENABLED(CONFIG_APP_USE_I2S_LRCLK_EDGES_COUNTER)) { ctx->fb_counter = 0; ctx->fb_periods = 0; @@ -416,19 +480,28 @@ void feedback_reset_ctx(struct feedback_ctx *ctx) } } -void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued) +void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued, + bool microframes) { + ctx->high_speed = microframes; + ctx->fb_value = nominal_feedback_value(ctx); + + if (microframes) { + ctx->counts_per_sof = (SAMPLE_RATE / 8000) << FEEDBACK_P; + } else { + ctx->counts_per_sof = (SAMPLE_RATE / 1000) << FEEDBACK_P; + } + /* I2S data was supposed to go out at SOF, but it is inevitably * delayed due to triggering I2S start by software. Set relative * SOF offset value in a way that ensures that values past "half * frame" are treated as "too late" instead of "too early" */ - ctx->rel_sof_offset = (SAMPLES_PER_SOF << FEEDBACK_P) / 2; + ctx->rel_sof_offset = ctx->counts_per_sof / 2; /* If there are more than 2 I2S blocks queued, use feedback regulator * to correct the situation. */ - ctx->base_sof_offset = (i2s_blocks_queued - 2) * - (SAMPLES_PER_SOF << FEEDBACK_P); + ctx->base_sof_offset = (i2s_blocks_queued - 2) * ctx->counts_per_sof; } uint32_t feedback_value(struct feedback_ctx *ctx) diff --git a/samples/subsys/usb/uac2_explicit_feedback/src/main.c b/samples/subsys/usb/uac2_explicit_feedback/src/main.c index 3f4f596f35c..40df3f5b8fc 100644 --- a/samples/subsys/usb/uac2_explicit_feedback/src/main.c +++ b/samples/subsys/usb/uac2_explicit_feedback/src/main.c @@ -20,14 +20,17 @@ LOG_MODULE_REGISTER(uac2_sample, LOG_LEVEL_INF); #define HEADPHONES_OUT_TERMINAL_ID UAC2_ENTITY_ID(DT_NODELABEL(out_terminal)) -#define SAMPLE_FREQUENCY (SAMPLES_PER_SOF * 1000) +#define FS_SAMPLES_PER_SOF 48 +#define HS_SAMPLES_PER_SOF 6 +#define MAX_SAMPLES_PER_SOF MAX(FS_SAMPLES_PER_SOF, HS_SAMPLES_PER_SOF) +#define SAMPLE_FREQUENCY (FS_SAMPLES_PER_SOF * 1000) #define SAMPLE_BIT_WIDTH 16 #define NUMBER_OF_CHANNELS 2 #define BYTES_PER_SAMPLE DIV_ROUND_UP(SAMPLE_BIT_WIDTH, 8) #define BYTES_PER_SLOT (BYTES_PER_SAMPLE * NUMBER_OF_CHANNELS) -#define MIN_BLOCK_SIZE ((SAMPLES_PER_SOF - 1) * BYTES_PER_SLOT) -#define BLOCK_SIZE (SAMPLES_PER_SOF * BYTES_PER_SLOT) -#define MAX_BLOCK_SIZE ((SAMPLES_PER_SOF + 1) * BYTES_PER_SLOT) +#define MIN_BLOCK_SIZE ((MAX_SAMPLES_PER_SOF - 1) * BYTES_PER_SLOT) +#define BLOCK_SIZE (MAX_SAMPLES_PER_SOF * BYTES_PER_SLOT) +#define MAX_BLOCK_SIZE ((MAX_SAMPLES_PER_SOF + 1) * BYTES_PER_SLOT) /* Absolute minimum is 5 buffers (1 actively consumed by I2S, 2nd queued as next * buffer, 3rd acquired by USB stack to receive data to, and 2 to handle SOF/I2S @@ -42,6 +45,7 @@ struct usb_i2s_ctx { const struct device *i2s_dev; bool terminal_enabled; bool i2s_started; + bool microframes; /* Number of blocks written, used to determine when to start I2S. * Overflows are not a problem becuse this variable is not necessary * after I2S is started. @@ -60,15 +64,15 @@ static void uac2_terminal_update_cb(const struct device *dev, uint8_t terminal, * ignore the terminal variable. */ __ASSERT_NO_MSG(terminal == HEADPHONES_OUT_TERMINAL_ID); - /* This sample is for Full-Speed only devices. */ - __ASSERT_NO_MSG(microframes == false); + + ctx->microframes = microframes; ctx->terminal_enabled = enabled; if (ctx->i2s_started && !enabled) { i2s_trigger(ctx->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DROP); ctx->i2s_started = false; ctx->i2s_blocks_written = 0; - feedback_reset_ctx(ctx->fb); + feedback_reset_ctx(ctx->fb, ctx->microframes); } } @@ -114,7 +118,11 @@ static void uac2_data_recv_cb(const struct device *dev, uint8_t terminal, * either disable terminal (or the cable will be disconnected) * which will stop I2S. */ - size = BLOCK_SIZE; + if (USBD_SUPPORTS_HIGH_SPEED && ctx->microframes) { + size = HS_SAMPLES_PER_SOF * BYTES_PER_SLOT; + } else { + size = FS_SAMPLES_PER_SOF * BYTES_PER_SLOT; + } memset(buf, 0, size); } @@ -124,7 +132,7 @@ static void uac2_data_recv_cb(const struct device *dev, uint8_t terminal, if (ret < 0) { ctx->i2s_started = false; ctx->i2s_blocks_written = 0; - feedback_reset_ctx(ctx->fb); + feedback_reset_ctx(ctx->fb, ctx->microframes); /* Most likely underrun occurred, prepare I2S restart */ i2s_trigger(ctx->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_PREPARE); @@ -236,7 +244,7 @@ static void uac2_sof(const struct device *dev, void *user_data) ctx->i2s_blocks_written >= 2) { i2s_trigger(ctx->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START); ctx->i2s_started = true; - feedback_start(ctx->fb, ctx->i2s_blocks_written); + feedback_start(ctx->fb, ctx->i2s_blocks_written, ctx->microframes); } } diff --git a/samples/subsys/usb/uac2_implicit_feedback/app.overlay b/samples/subsys/usb/uac2_implicit_feedback/app.overlay index 799d9e40d43..583b1f8ef7b 100644 --- a/samples/subsys/usb/uac2_implicit_feedback/app.overlay +++ b/samples/subsys/usb/uac2_implicit_feedback/app.overlay @@ -11,6 +11,7 @@ compatible = "zephyr,uac2"; status = "okay"; full-speed; + high-speed; audio-function = ; uac_aclk: aclk { diff --git a/samples/subsys/usb/uac2_implicit_feedback/boards/nrf5340dk_nrf5340_cpuapp.conf b/samples/subsys/usb/uac2_implicit_feedback/boards/nrf5340dk_nrf5340_cpuapp.conf index ce67033381a..4c7c060177e 100644 --- a/samples/subsys/usb/uac2_implicit_feedback/boards/nrf5340dk_nrf5340_cpuapp.conf +++ b/samples/subsys/usb/uac2_implicit_feedback/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -1,2 +1,2 @@ #Enable timer for asynchronous feedback -CONFIG_NRFX_TIMER2=y +CONFIG_NRFX_TIMER=y diff --git a/samples/subsys/usb/uac2_implicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf b/samples/subsys/usb/uac2_implicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf index 1b1edb40666..3f9d5d61d28 100644 --- a/samples/subsys/usb/uac2_implicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf +++ b/samples/subsys/usb/uac2_implicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf @@ -1,7 +1,3 @@ # Enable timer for asynchronous feedback CONFIG_NRFX_GPPI=y -CONFIG_NRFX_TIMER131=y - -# Sample is Full-Speed only, prevent High-Speed enumeration -CONFIG_UDC_DRIVER_HIGH_SPEED_SUPPORT_ENABLED=n -CONFIG_USBD_MAX_SPEED_FULL=y +CONFIG_NRFX_TIMER=y diff --git a/samples/subsys/usb/uac2_implicit_feedback/src/feedback.h b/samples/subsys/usb/uac2_implicit_feedback/src/feedback.h index 3fff2425d8b..584bc0135c2 100644 --- a/samples/subsys/usb/uac2_implicit_feedback/src/feedback.h +++ b/samples/subsys/usb/uac2_implicit_feedback/src/feedback.h @@ -9,15 +9,14 @@ #include -/* Nominal number of samples received on each SOF. This sample is currently - * supporting only 48 kHz sample rate. - */ -#define SAMPLES_PER_SOF 48 +/* This sample is currently supporting only 48 kHz sample rate. */ +#define SAMPLE_RATE 48000 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); +void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued, + bool microframes); /* Return offset between I2S block start and USB SOF in samples. * diff --git a/samples/subsys/usb/uac2_implicit_feedback/src/feedback_dummy.c b/samples/subsys/usb/uac2_implicit_feedback/src/feedback_dummy.c index 3b91deafe5c..d28181033b9 100644 --- a/samples/subsys/usb/uac2_implicit_feedback/src/feedback_dummy.c +++ b/samples/subsys/usb/uac2_implicit_feedback/src/feedback_dummy.c @@ -24,10 +24,12 @@ void feedback_reset_ctx(struct feedback_ctx *ctx) ARG_UNUSED(ctx); } -void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued) +void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued, + bool microframes) { ARG_UNUSED(ctx); ARG_UNUSED(i2s_blocks_queued); + ARG_UNUSED(microframes); } int feedback_samples_offset(struct feedback_ctx *ctx) diff --git a/samples/subsys/usb/uac2_implicit_feedback/src/feedback_nrf.c b/samples/subsys/usb/uac2_implicit_feedback/src/feedback_nrf.c index a7dfad370b8..9e8c2ac0342 100644 --- a/samples/subsys/usb/uac2_implicit_feedback/src/feedback_nrf.c +++ b/samples/subsys/usb/uac2_implicit_feedback/src/feedback_nrf.c @@ -51,8 +51,8 @@ static inline void feedback_target_init(void) #error "Unsupported target" #endif -static const nrfx_timer_t feedback_timer_instance = - NRFX_TIMER_INSTANCE(FEEDBACK_TIMER_INSTANCE_NUMBER); +static nrfx_timer_t feedback_timer_instance = + NRFX_TIMER_INSTANCE(NRF_TIMER_INST_GET(FEEDBACK_TIMER_INSTANCE_NUMBER)); /* While it might be possible to determine I2S FRAMESTART to USB SOF offset * entirely in software, the I2S API lacks appropriate timestamping. Therefore @@ -68,11 +68,12 @@ static const nrfx_timer_t feedback_timer_instance = * SOF offset is around 0 when regulated and therefore the relative clock * frequency discrepancies are essentially negligible. */ -#define CLKS_PER_SAMPLE (16000000 / (SAMPLES_PER_SOF * 1000)) +#define CLKS_PER_SAMPLE (16000000 / (SAMPLE_RATE)) static struct feedback_ctx { int32_t rel_sof_offset; int32_t base_sof_offset; + unsigned int nominal; } fb_ctx; struct feedback_ctx *feedback_init(void) @@ -143,8 +144,8 @@ static void update_sof_offset(struct feedback_ctx *ctx, uint32_t sof_cc, /* /2 because we treat the middle as a turning point from being * "too late" to "too early". */ - if (framestart_cc > (SAMPLES_PER_SOF * CLKS_PER_SAMPLE)/2) { - sof_offset = framestart_cc - SAMPLES_PER_SOF * CLKS_PER_SAMPLE; + if (framestart_cc > (ctx->nominal * CLKS_PER_SAMPLE)/2) { + sof_offset = framestart_cc - ctx->nominal * CLKS_PER_SAMPLE; } else { sof_offset = framestart_cc; } @@ -159,17 +160,17 @@ static void update_sof_offset(struct feedback_ctx *ctx, uint32_t sof_cc, if (sof_offset >= 0) { abs_diff = sof_offset - ctx->rel_sof_offset; - base_change = -(SAMPLES_PER_SOF * CLKS_PER_SAMPLE); + base_change = -(ctx->nominal * CLKS_PER_SAMPLE); } else { abs_diff = ctx->rel_sof_offset - sof_offset; - base_change = SAMPLES_PER_SOF * CLKS_PER_SAMPLE; + base_change = ctx->nominal * CLKS_PER_SAMPLE; } /* Adjust base offset only if the change happened through the * outer bound. The actual changes should be significantly lower * than the threshold here. */ - if (abs_diff > (SAMPLES_PER_SOF * CLKS_PER_SAMPLE)/2) { + if (abs_diff > (ctx->nominal * CLKS_PER_SAMPLE)/2) { ctx->base_sof_offset += base_change; } } @@ -195,19 +196,26 @@ void feedback_reset_ctx(struct feedback_ctx *ctx) ARG_UNUSED(ctx); } -void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued) +void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued, + bool microframes) { + if (microframes) { + ctx->nominal = SAMPLE_RATE / 8000; + } else { + ctx->nominal = SAMPLE_RATE / 1000; + } + /* I2S data was supposed to go out at SOF, but it is inevitably * delayed due to triggering I2S start by software. Set relative * SOF offset value in a way that ensures that values past "half * frame" are treated as "too late" instead of "too early" */ - ctx->rel_sof_offset = (SAMPLES_PER_SOF * CLKS_PER_SAMPLE) / 2; + ctx->rel_sof_offset = (ctx->nominal * CLKS_PER_SAMPLE) / 2; /* If there are more than 2 I2S TX blocks queued, use feedback regulator * to correct the situation. */ ctx->base_sof_offset = (i2s_blocks_queued - 2) * - (SAMPLES_PER_SOF * CLKS_PER_SAMPLE); + (ctx->nominal * CLKS_PER_SAMPLE); } int feedback_samples_offset(struct feedback_ctx *ctx) diff --git a/samples/subsys/usb/uac2_implicit_feedback/src/main.c b/samples/subsys/usb/uac2_implicit_feedback/src/main.c index 12abdf4fe68..06334866e7a 100644 --- a/samples/subsys/usb/uac2_implicit_feedback/src/main.c +++ b/samples/subsys/usb/uac2_implicit_feedback/src/main.c @@ -21,15 +21,17 @@ LOG_MODULE_REGISTER(uac2_sample, LOG_LEVEL_INF); #define HEADPHONES_OUT_TERMINAL_ID UAC2_ENTITY_ID(DT_NODELABEL(out_terminal)) #define MICROPHONE_IN_TERMINAL_ID UAC2_ENTITY_ID(DT_NODELABEL(in_terminal)) -#define SAMPLES_PER_SOF 48 -#define SAMPLE_FREQUENCY (SAMPLES_PER_SOF * 1000) +#define FS_SAMPLES_PER_SOF 48 +#define HS_SAMPLES_PER_SOF 6 +#define MAX_SAMPLES_PER_SOF MAX(FS_SAMPLES_PER_SOF, HS_SAMPLES_PER_SOF) +#define SAMPLE_FREQUENCY (FS_SAMPLES_PER_SOF * 1000) #define SAMPLE_BIT_WIDTH 16 #define NUMBER_OF_CHANNELS 2 #define BYTES_PER_SAMPLE DIV_ROUND_UP(SAMPLE_BIT_WIDTH, 8) #define BYTES_PER_SLOT (BYTES_PER_SAMPLE * NUMBER_OF_CHANNELS) -#define MIN_BLOCK_SIZE ((SAMPLES_PER_SOF - 1) * BYTES_PER_SLOT) -#define BLOCK_SIZE (SAMPLES_PER_SOF * BYTES_PER_SLOT) -#define MAX_BLOCK_SIZE ((SAMPLES_PER_SOF + 1) * BYTES_PER_SLOT) +#define MIN_BLOCK_SIZE ((MAX_SAMPLES_PER_SOF - 1) * BYTES_PER_SLOT) +#define BLOCK_SIZE (MAX_SAMPLES_PER_SOF * BYTES_PER_SLOT) +#define MAX_BLOCK_SIZE ((MAX_SAMPLES_PER_SOF + 1) * BYTES_PER_SLOT) /* Absolute minimum is 5 TX buffers (1 actively consumed by I2S, 2nd queued as * next buffer, 3rd acquired by USB stack to receive data to, and 2 to handle @@ -49,6 +51,7 @@ struct usb_i2s_ctx { bool i2s_started; bool rx_started; bool usb_data_received; + bool microframes; /* Counter used to determine when to start I2S and then when to start * sending RX packets to host. Overflows are not a problem because this * variable is not necessary after both I2S and RX is started. @@ -70,8 +73,8 @@ struct usb_i2s_ctx { * Used to avoid overcompensation in feedback regulator. LSBs indicate * latest write size. */ - uint8_t plus_ones; - uint8_t minus_ones; + uint32_t plus_ones; + uint32_t minus_ones; }; static void uac2_terminal_update_cb(const struct device *dev, uint8_t terminal, @@ -80,8 +83,7 @@ static void uac2_terminal_update_cb(const struct device *dev, uint8_t terminal, { struct usb_i2s_ctx *ctx = user_data; - /* This sample is for Full-Speed only devices. */ - __ASSERT_NO_MSG(microframes == false); + ctx->microframes = microframes; if (terminal == HEADPHONES_OUT_TERMINAL_ID) { ctx->headphones_enabled = enabled; @@ -104,6 +106,15 @@ static void uac2_terminal_update_cb(const struct device *dev, uint8_t terminal, } } +static int nominal_samples_per_sof(struct usb_i2s_ctx *ctx) +{ + if (USBD_SUPPORTS_HIGH_SPEED && ctx->microframes) { + return HS_SAMPLES_PER_SOF; + } + + return FS_SAMPLES_PER_SOF; +} + static void *uac2_get_recv_buf(const struct device *dev, uint8_t terminal, uint16_t size, void *user_data) { @@ -133,6 +144,7 @@ static void uac2_data_recv_cb(const struct device *dev, uint8_t terminal, void *buf, uint16_t size, void *user_data) { struct usb_i2s_ctx *ctx = user_data; + int nominal = nominal_samples_per_sof(ctx); int ret; ctx->usb_data_received = true; @@ -159,11 +171,11 @@ static void uac2_data_recv_cb(const struct device *dev, uint8_t terminal, * of samples sent. */ if (ctx->plus_ones & 1) { - size = (SAMPLES_PER_SOF + 1) * BYTES_PER_SLOT; + size = (nominal + 1) * BYTES_PER_SLOT; } else if (ctx->minus_ones & 1) { - size = (SAMPLES_PER_SOF - 1) * BYTES_PER_SLOT; + size = (nominal - 1) * BYTES_PER_SLOT; } else { - size = SAMPLES_PER_SOF * BYTES_PER_SLOT; + size = nominal * BYTES_PER_SLOT; } memset(buf, 0, size); } @@ -208,6 +220,7 @@ static void uac2_buf_release_cb(const struct device *dev, uint8_t terminal, /* Determine next number of samples to send, called at most once every SOF */ static int next_mic_num_samples(struct usb_i2s_ctx *ctx) { + int nominal = nominal_samples_per_sof(ctx); int offset = feedback_samples_offset(ctx->fb); /* The rolling buffers essentially handle controller dead time, i.e. @@ -217,12 +230,18 @@ static int next_mic_num_samples(struct usb_i2s_ctx *ctx) ctx->plus_ones <<= 1; ctx->minus_ones <<= 1; + /* At Full-Speed only remember last 8 frames */ + if (!USBD_SUPPORTS_HIGH_SPEED || !ctx->microframes) { + ctx->plus_ones &= 0x000000FF; + ctx->minus_ones &= 0x000000FF; + } + if ((offset < 0) && (POPCOUNT(ctx->plus_ones) < -offset)) { /* I2S buffer starts at least 1 sample before SOF, send nominal * + 1 samples to host in order to shift offset towards 0. */ ctx->plus_ones |= 1; - return SAMPLES_PER_SOF + 1; + return nominal + 1; } if ((offset > 0) && (POPCOUNT(ctx->minus_ones) < offset)) { @@ -230,11 +249,11 @@ static int next_mic_num_samples(struct usb_i2s_ctx *ctx) * - 1 samples to host in order to shift offset towards 0 */ ctx->minus_ones |= 1; - return SAMPLES_PER_SOF - 1; + return nominal - 1; } /* I2S is either spot on, or the offset is expected to correct soon */ - return SAMPLES_PER_SOF; + return nominal; } static void process_mic_data(const struct device *dev, struct usb_i2s_ctx *ctx) @@ -488,7 +507,7 @@ static void uac2_sof(const struct device *dev, void *user_data) ctx->i2s_counter >= 2) { i2s_trigger(ctx->i2s_dev, I2S_DIR_BOTH, I2S_TRIGGER_START); ctx->i2s_started = true; - feedback_start(ctx->fb, ctx->i2s_counter); + feedback_start(ctx->fb, ctx->i2s_counter, ctx->microframes); ctx->i2s_counter = 0; } diff --git a/tests/drivers/uart/uart_mix_fifo_poll/testcase.yaml b/tests/drivers/uart/uart_mix_fifo_poll/testcase.yaml index 0ba5944cca6..d481ed33adc 100644 --- a/tests/drivers/uart/uart_mix_fifo_poll/testcase.yaml +++ b/tests/drivers/uart/uart_mix_fifo_poll/testcase.yaml @@ -79,7 +79,7 @@ tests: - CONFIG_UART_0_ENHANCED_POLL_OUT=y - CONFIG_UART_0_NRF_HW_ASYNC=y - CONFIG_UART_0_NRF_HW_ASYNC_TIMER=2 - - CONFIG_NRFX_TIMER2=y + - CONFIG_NRFX_TIMER=y platform_allow: - nrf52840dk/nrf52840 - nrf5340dk/nrf5340/cpuapp diff --git a/tests/drivers/uart/uart_pm/testcase.yaml b/tests/drivers/uart/uart_pm/testcase.yaml index 008eae9d3d7..38da5dca480 100644 --- a/tests/drivers/uart/uart_pm/testcase.yaml +++ b/tests/drivers/uart/uart_pm/testcase.yaml @@ -78,7 +78,7 @@ tests: - CONFIG_UART_0_ASYNC=y - CONFIG_UART_0_NRF_HW_ASYNC=y - CONFIG_UART_0_NRF_HW_ASYNC_TIMER=2 - - CONFIG_NRFX_TIMER2=y + - CONFIG_NRFX_TIMER=y - CONFIG_UART_0_ENHANCED_POLL_OUT=n drivers.uart.pm.async.enhanced_poll: @@ -88,7 +88,7 @@ tests: - CONFIG_UART_0_ASYNC=y - CONFIG_UART_0_NRF_HW_ASYNC=y - CONFIG_UART_0_NRF_HW_ASYNC_TIMER=2 - - CONFIG_NRFX_TIMER2=y + - CONFIG_NRFX_TIMER=y - CONFIG_UART_0_ENHANCED_POLL_OUT=y platform_exclude: - nrf54h20dk/nrf54h20/cpuapp