@@ -84,20 +84,31 @@ static const nrfx_timer_t feedback_timer_instance =
8484 * Full-Speed isochronous feedback is Q10.10 unsigned integer left-justified in
8585 * the 24-bits so it has Q10.14 format. This sample application puts zeroes to
8686 * the 4 least significant bits (does not use the bits for extra precision).
87+ *
88+ * High-Speed isochronous feedback is Q12.13 unsigned integer aligned in the
89+ * 32-bits so the binary point is located between second and third byte so it
90+ * has Q16.16 format. This sample applications puts zeroes to the 3 least
91+ * significant bits (does not use the bits for extra precision).
8792 */
88- #define FEEDBACK_K 10
93+ #define FEEDBACK_FS_K 10
94+ #define FEEDBACK_HS_K 13
8995#if defined(CONFIG_APP_USE_I2S_LRCLK_EDGES_COUNTER )
90- #define FEEDBACK_P 1
96+ #define FEEDBACK_FS_P 1
97+ #define FEEDBACK_HS_P 1
9198#else
92- #define FEEDBACK_P 5
99+ #define FEEDBACK_FS_P 5
100+ #define FEEDBACK_HS_P 5
93101#endif
94102
95103#define FEEDBACK_FS_SHIFT 4
104+ #define FEEDBACK_HS_SHIFT 3
96105
97106static struct feedback_ctx {
98107 uint32_t fb_value ;
99108 int32_t rel_sof_offset ;
100109 int32_t base_sof_offset ;
110+ int counts_per_sof ;
111+ bool high_speed ;
101112 union {
102113 /* For edge counting */
103114 struct {
@@ -190,7 +201,7 @@ struct feedback_ctx *feedback_init(void)
190201
191202 feedback_target_init ();
192203
193- feedback_reset_ctx (& fb_ctx );
204+ feedback_reset_ctx (& fb_ctx , false );
194205
195206 if (IS_ENABLED (CONFIG_APP_USE_I2S_LRCLK_EDGES_COUNTER )) {
196207 err = feedback_edge_counter_setup ();
@@ -239,6 +250,24 @@ struct feedback_ctx *feedback_init(void)
239250 return & fb_ctx ;
240251}
241252
253+ static uint32_t nominal_feedback_value (struct feedback_ctx * ctx )
254+ {
255+ if (ctx -> high_speed ) {
256+ return (SAMPLE_RATE / 8000 ) << (FEEDBACK_HS_K + FEEDBACK_HS_SHIFT );
257+ }
258+
259+ return (SAMPLE_RATE / 1000 ) << (FEEDBACK_FS_K + FEEDBACK_FS_SHIFT );
260+ }
261+
262+ static uint32_t feedback_period (struct feedback_ctx * ctx )
263+ {
264+ if (ctx -> high_speed ) {
265+ return BIT (FEEDBACK_HS_K - FEEDBACK_HS_P );
266+ }
267+
268+ return BIT (FEEDBACK_FS_K - FEEDBACK_FS_P );
269+ }
270+
242271static void update_sof_offset (struct feedback_ctx * ctx , uint32_t sof_cc ,
243272 uint32_t framestart_cc )
244273{
@@ -255,16 +284,16 @@ static void update_sof_offset(struct feedback_ctx *ctx, uint32_t sof_cc,
255284 * when regulated and therefore the relative clock frequency
256285 * discrepancies are essentially negligible.
257286 */
258- clks_per_edge = sof_cc / ( SAMPLES_PER_SOF << FEEDBACK_P ) ;
287+ clks_per_edge = sof_cc / ctx -> counts_per_sof ;
259288 sof_cc /= MAX (clks_per_edge , 1 );
260289 framestart_cc /= MAX (clks_per_edge , 1 );
261290 }
262291
263292 /* /2 because we treat the middle as a turning point from being
264293 * "too late" to "too early".
265294 */
266- if (framestart_cc > ( SAMPLES_PER_SOF << FEEDBACK_P ) /2 ) {
267- sof_offset = framestart_cc - ( SAMPLES_PER_SOF << FEEDBACK_P ) ;
295+ if (framestart_cc > ctx -> counts_per_sof /2 ) {
296+ sof_offset = framestart_cc - ctx -> counts_per_sof ;
268297 } else {
269298 sof_offset = framestart_cc ;
270299 }
@@ -279,40 +308,46 @@ static void update_sof_offset(struct feedback_ctx *ctx, uint32_t sof_cc,
279308
280309 if (sof_offset >= 0 ) {
281310 abs_diff = sof_offset - ctx -> rel_sof_offset ;
282- base_change = - ( SAMPLES_PER_SOF << FEEDBACK_P ) ;
311+ base_change = - ctx -> counts_per_sof ;
283312 } else {
284313 abs_diff = ctx -> rel_sof_offset - sof_offset ;
285- base_change = SAMPLES_PER_SOF << FEEDBACK_P ;
314+ base_change = ctx -> counts_per_sof ;
286315 }
287316
288317 /* Adjust base offset only if the change happened through the
289318 * outer bound. The actual changes should be significantly lower
290319 * than the threshold here.
291320 */
292- if (abs_diff > ( SAMPLES_PER_SOF << FEEDBACK_P ) /2 ) {
321+ if (abs_diff > ctx -> counts_per_sof /2 ) {
293322 ctx -> base_sof_offset += base_change ;
294323 }
295324 }
296325
297326 ctx -> rel_sof_offset = sof_offset ;
298327}
299328
300- static inline int32_t offset_to_correction (int32_t offset )
329+ static inline int32_t offset_to_correction (struct feedback_ctx * ctx , int32_t offset )
301330{
302- return - (offset / BIT (FEEDBACK_P )) * BIT (FEEDBACK_FS_SHIFT );
331+ if (ctx -> high_speed ) {
332+ return - (offset / BIT (FEEDBACK_HS_P )) * BIT (FEEDBACK_HS_SHIFT );
333+ }
334+
335+ return - (offset / BIT (FEEDBACK_FS_P )) * BIT (FEEDBACK_FS_SHIFT );
303336}
304337
305338static int32_t pi_update (struct feedback_ctx * ctx )
306339{
340+ const uint32_t scaling = ctx -> high_speed ? BIT (13 - FEEDBACK_HS_P ) :
341+ BIT (10 - FEEDBACK_FS_P );
307342 int32_t sof_offset = ctx -> rel_sof_offset + ctx -> base_sof_offset ;
308- /* SOF offset is measured in pow(2, -FEEDBACK_P ) samples, i.e. when
309- * FEEDBACK_P is 0, offset is in samples, and for 1 -> half-samples,
343+ /* SOF offset is measured in pow(2, -FEEDBACK_FS_P ) samples, i.e. when
344+ * FEEDBACK_FS_P is 0, offset is in samples, and for 1 -> half-samples,
310345 * 2 -> quarter-samples, 3 -> eightth-samples and so on.
311346 * In order to simplify the PI controller description here, normalize
312347 * the offset to 1/1024 samples (alternatively it can be treated as
313348 * samples in Q10 fixed point format) and use it as Process Variable.
314349 */
315- int32_t PV = BIT ( 10 - FEEDBACK_P ) * sof_offset ;
350+ int32_t PV = scaling * sof_offset ;
316351 /* The control goal is to keep I2S FRAMESTART as close as possible to
317352 * USB SOF and therefore Set Point is 0.
318353 */
@@ -374,40 +409,47 @@ void feedback_process(struct feedback_ctx *ctx)
374409 ctx -> fb_counter += sof_cc ;
375410 ctx -> fb_periods ++ ;
376411
377- if (ctx -> fb_periods == BIT ( FEEDBACK_K - FEEDBACK_P )) {
412+ if (ctx -> fb_periods == feedback_period ( ctx )) {
378413
379- /* fb_counter holds Q10.10 value, left-justify it */
380- fb = ctx -> fb_counter << FEEDBACK_FS_SHIFT ;
414+ if (ctx -> high_speed ) {
415+ fb = ctx -> fb_counter << FEEDBACK_HS_SHIFT ;
416+ } else {
417+ /* fb_counter holds Q10.10 value, left-justify it */
418+ fb = ctx -> fb_counter << FEEDBACK_FS_SHIFT ;
419+ }
381420
382421 /* Align I2S FRAMESTART to USB SOF by adjusting reported
383422 * feedback value. This is endpoint specific correction
384423 * mentioned but not specified in USB 2.0 Specification.
385424 */
386- if (abs (offset ) > BIT (FEEDBACK_P )) {
387- fb += offset_to_correction (offset );
425+ if (abs (offset ) > BIT (ctx -> high_speed ? FEEDBACK_HS_P : FEEDBACK_FS_P )) {
426+ fb += offset_to_correction (ctx , offset );
388427 }
389428
390429 ctx -> fb_value = fb ;
391430 ctx -> fb_counter = 0 ;
392431 ctx -> fb_periods = 0 ;
393432 }
394433 } else {
434+ const uint32_t zero_lsb_mask = ctx -> high_speed ? 0x7 : 0xF ;
435+
395436 /* Use PI controller to generate required feedback deviation
396437 * from nominal feedback value.
397438 */
398- fb = SAMPLES_PER_SOF << ( FEEDBACK_K + FEEDBACK_FS_SHIFT );
439+ fb = nominal_feedback_value ( ctx );
399440 /* Clear the additional LSB bits in feedback value, i.e. do not
400441 * use the optional extra resolution.
401442 */
402- fb += pi_update (ctx ) & ~0xF ;
443+ fb += pi_update (ctx ) & ~zero_lsb_mask ;
403444 ctx -> fb_value = fb ;
404445 }
405446}
406447
407- void feedback_reset_ctx (struct feedback_ctx * ctx )
448+ void feedback_reset_ctx (struct feedback_ctx * ctx , bool microframes )
408449{
409450 /* Reset feedback to nominal value */
410- ctx -> fb_value = SAMPLES_PER_SOF << (FEEDBACK_K + FEEDBACK_FS_SHIFT );
451+ ctx -> high_speed = microframes ;
452+ ctx -> fb_value = nominal_feedback_value (ctx );
411453 if (IS_ENABLED (CONFIG_APP_USE_I2S_LRCLK_EDGES_COUNTER )) {
412454 ctx -> fb_counter = 0 ;
413455 ctx -> fb_periods = 0 ;
@@ -416,19 +458,28 @@ void feedback_reset_ctx(struct feedback_ctx *ctx)
416458 }
417459}
418460
419- void feedback_start (struct feedback_ctx * ctx , int i2s_blocks_queued )
461+ void feedback_start (struct feedback_ctx * ctx , int i2s_blocks_queued ,
462+ bool microframes )
420463{
464+ ctx -> high_speed = microframes ;
465+ ctx -> fb_value = nominal_feedback_value (ctx );
466+
467+ if (microframes ) {
468+ ctx -> counts_per_sof = (SAMPLE_RATE / 8000 ) << FEEDBACK_HS_P ;
469+ } else {
470+ ctx -> counts_per_sof = (SAMPLE_RATE / 1000 ) << FEEDBACK_FS_P ;
471+ }
472+
421473 /* I2S data was supposed to go out at SOF, but it is inevitably
422474 * delayed due to triggering I2S start by software. Set relative
423475 * SOF offset value in a way that ensures that values past "half
424476 * frame" are treated as "too late" instead of "too early"
425477 */
426- ctx -> rel_sof_offset = ( SAMPLES_PER_SOF << FEEDBACK_P ) / 2 ;
478+ ctx -> rel_sof_offset = ctx -> counts_per_sof / 2 ;
427479 /* If there are more than 2 I2S blocks queued, use feedback regulator
428480 * to correct the situation.
429481 */
430- ctx -> base_sof_offset = (i2s_blocks_queued - 2 ) *
431- (SAMPLES_PER_SOF << FEEDBACK_P );
482+ ctx -> base_sof_offset = (i2s_blocks_queued - 2 ) * ctx -> counts_per_sof ;
432483}
433484
434485uint32_t feedback_value (struct feedback_ctx * ctx )
0 commit comments