42
42
43
43
/*
44
44
* AUDIO PLL setting: Frequency = Fref * (DIV_SELECT + NUM / DENOM)
45
- * = 24 * (32 + 77/100)
46
- * = 786.48 MHz
45
+ * = 24 * (32 + 96 / 125) = 24 * (32.768)
46
+ * = 786.432 MHz = 48kHz * 16384
47
+ *
48
+ * This default clocking is used during initial configuration; it also works well for
49
+ * frequencies that evenly divide 192kHz, such as 8/12/24/48kHz. However, it doesn't work
50
+ * well for 44.1/22/11kHz, so there's the possibility of using a different
51
+ * setting when playing a particular sample.
47
52
*/
48
53
const clock_audio_pll_config_t audioPllConfig = {
49
54
.loopDivider = 32 , /* PLL loop divider. Valid range for DIV_SELECT divider value: 27~54. */
50
55
.postDivider = 1 , /* Divider after the PLL, should only be 1, 2, 4, 8, 16. */
51
- .numerator = 77 , /* 30 bit numerator of fractional loop divider. */
52
- .denominator = 100 , /* 30 bit denominator of fractional loop divider */
56
+ .numerator = 96 , /* 30 bit numerator of fractional loop divider. */
57
+ .denominator = 125 , /* 30 bit denominator of fractional loop divider */
53
58
};
54
59
55
60
static I2S_Type * const i2s_instances [] = I2S_BASE_PTRS ;
56
- static uint8_t i2s_in_use ;
61
+ static uint8_t i2s_in_use , i2s_playing ;
57
62
58
63
static I2S_Type * SAI_GetPeripheral (int idx ) {
59
64
if (idx < 0 || idx >= (int )MP_ARRAY_SIZE (i2s_instances )) {
@@ -344,7 +349,11 @@ void port_i2s_deinit(i2s_t *self) {
344
349
}
345
350
SAI_TransferAbortSend (self -> peripheral , & self -> handle );
346
351
i2s_clock_off (self -> peripheral );
347
- i2s_in_use &= ~(1 << SAI_GetInstance (self -> peripheral ));
352
+
353
+ uint32_t instance_mask = 1 << SAI_GetInstance (self -> peripheral );
354
+ i2s_in_use &= ~instance_mask ;
355
+ i2s_playing &= ~instance_mask ;
356
+
348
357
if (!i2s_in_use ) {
349
358
CCM_ANALOG -> PLL_AUDIO = CCM_ANALOG_PLL_AUDIO_BYPASS_MASK | CCM_ANALOG_PLL_AUDIO_POWERDOWN_MASK | CCM_ANALOG_PLL_AUDIO_BYPASS_CLK_SRC (kCLOCK_PllClkSrc24M );
350
359
}
@@ -354,13 +363,51 @@ void port_i2s_deinit(i2s_t *self) {
354
363
}
355
364
}
356
365
366
+ static uint32_t gcd (uint32_t a , uint32_t b ) {
367
+ while (b ) {
368
+ uint32_t tmp = a % b ;
369
+ a = b ;
370
+ b = tmp ;
371
+ }
372
+ return a ;
373
+ }
374
+
375
+ static void set_sai_clocking_for_sample_rate (uint32_t sample_rate ) {
376
+ mp_arg_validate_int_range ((mp_uint_t )sample_rate , 4000 , 192000 , MP_QSTR_sample_rate );
377
+
378
+ uint32_t target_rate = sample_rate ;
379
+ // ensure the PWM rate of MQS will be adequately high
380
+ while (target_rate < 175000 ) {
381
+ target_rate <<= 1 ;
382
+ }
383
+ target_rate *= 4096 ; // various prescalers divide by this much
384
+ uint32_t div = gcd (target_rate % 24000000 , 24000000 );
385
+ clock_audio_pll_config_t config = {
386
+ .loopDivider = target_rate / 24000000 ,
387
+ .postDivider = 1 ,
388
+ .numerator = (target_rate % 24000000 ) / div ,
389
+ .denominator = 24000000 / div ,
390
+ };
391
+ CLOCK_InitAudioPll (& config );
392
+ }
393
+
357
394
void port_i2s_play (i2s_t * self , mp_obj_t sample , bool loop ) {
358
395
self -> sample = sample ;
359
396
self -> loop = loop ;
360
397
self -> bytes_per_sample = audiosample_bits_per_sample (sample ) / 8 ;
361
398
self -> channel_count = audiosample_channel_count (sample );
399
+ int instance = SAI_GetInstance (self -> peripheral );
400
+ i2s_playing |= (1 << instance );
362
401
uint32_t sample_rate = audiosample_sample_rate (sample );
363
402
if (sample_rate != self -> sample_rate ) {
403
+ if (__builtin_popcount (i2s_playing ) <= 1 ) {
404
+ // as this is the first/only i2s instance playing audio, we can
405
+ // safely change the overall clock used by the SAI peripheral, to
406
+ // get more accurate frequency reproduction. If another i2s
407
+ // instance is playing, then we can't touch the audio PLL and have
408
+ // to live with what we can get, which may be inaccurate
409
+ set_sai_clocking_for_sample_rate (sample_rate );
410
+ }
364
411
SAI_TxSetBitClockRate (self -> peripheral , SAI_CLOCK_FREQ , sample_rate , 16 , 2 );
365
412
self -> sample_rate = sample_rate ;
366
413
}
0 commit comments