Skip to content

Commit 94dfb9f

Browse files
Update hardcoded I2S::SYSCLK exact mappings (#3497)
Adds a utility to run on the Pico and Pico2 to identify the lowest frequency error possible for a given output sample rate. Use that data on the Pico and Pico2 to implement a smarter I2S::setSysClk() method which varies the SYSCLK depending on the sample rate and if the I2S is bidirectional. Originally had the same hardcoded values for RP2040 and RP2350, and the RP2040 values were calculated from the release max F_CPU of 133, not the new official 200MHz support. Fixes #3496
1 parent d86fc4f commit 94dfb9f

3 files changed

Lines changed: 113 additions & 9 deletions

File tree

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Brute force SYSCLK checker for I2S. Counts down from F_CPU and
2+
// calculates the absolute I2S speed error for a given frequency,
3+
// reporting the minimum encountered and at which frequency
4+
// (Note 64K is 32K bidirectional, see #3496)
5+
// Run it on any new CPUs or updated default F_CPUs.
6+
7+
// End users should not need to ever run this
8+
// Released to the public domain 2026 by Earle F. Philhower, III
9+
10+
void setup() {
11+
delay(5000);
12+
int F[11] = { 8000, 16000, 32000, 64000, 48000, 96000, 192000, 11025, 22050, 44100, 88200 };
13+
for (auto x = 0; x < 11; x++) {
14+
int f = F[x];
15+
Serial.printf("Checking for %d\n", f);
16+
int khz = (F_CPU * 1.10) / 1000;
17+
float err = 999999.9;
18+
int bestMatch = khz;
19+
while (khz > 100000) {
20+
uint a, b, c;
21+
bool r = check_sys_clock_khz(khz, &a, &b, &c);
22+
if (!r) {
23+
khz--;
24+
continue;
25+
}
26+
float edgerate = f * 64; // 32 bits, high and low events
27+
float v = (khz * 1000) / edgerate;
28+
float e = v - floor(v);
29+
e /= v;
30+
e *= 100.0f;
31+
if (e < err) {
32+
err = e;
33+
bestMatch = khz;
34+
Serial.printf("%d %f\n", bestMatch, (double)err);
35+
if (err == 0) {
36+
break;
37+
}
38+
}
39+
khz--;
40+
}
41+
Serial.printf("%d: %d, err=%1.6f%%\n", f, bestMatch, (double)err);
42+
}
43+
}
44+
45+
void loop() {
46+
}

libraries/I2S/src/I2S.cpp

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,75 @@ bool I2S::setFrequency(int newFreq) {
162162
}
163163

164164
bool I2S::setSysClk(int samplerate) { // optimise sys_clk for desired samplerate
165-
if (samplerate % 11025 == 0) {
166-
return set_sys_clock_khz(I2SSYSCLK_44_1, false);
165+
if (_isInput && _isOutput) {
166+
samplerate *= 2; // We need 4 clocks per bit, not 2, in dual mode
167+
}
168+
int rate;
169+
// Numbers taken from I2SBrute either at 0.0% error or a "reasonable" if not minimal
170+
#ifdef PICO_RP2350
171+
switch (samplerate) {
172+
case 8000:
173+
case 16000:
174+
case 32000:
175+
case 48000:
176+
case 96000:
177+
rate = 153600; // Very mild overclock
178+
break;
179+
case 64000:
180+
rate = 102400;
181+
break;
182+
case 192000:
183+
rate = 147600;
184+
break;
185+
case 11025:
186+
rate = 156000; // Mild overclock
187+
break;
188+
case 22050:
189+
rate = 120000;
190+
break;
191+
case 44100:
192+
rate = 144000;
193+
break;
194+
case 88200:
195+
rate = 135600;
196+
break;
197+
default:
198+
rate = F_CPU; // unknown, don't change
199+
break;
167200
}
168-
if (samplerate % 8000 == 0) {
169-
return set_sys_clock_khz(I2SSYSCLK_8, false);
201+
#else
202+
switch (samplerate) {
203+
case 8000:
204+
rate = 192000;
205+
break;
206+
case 16000:
207+
case 32000:
208+
case 48000:
209+
case 96000:
210+
rate = 153600;
211+
break;
212+
case 64000:
213+
rate = 213000; // Mild overclock, OTW we drop 50% in speed
214+
break;
215+
case 192000:
216+
rate = 196800;
217+
break;
218+
case 11025:
219+
rate = 204000; // Very mild overclock
220+
break;
221+
case 22050:
222+
rate = 216000; // Mild overclock
223+
break;
224+
case 44100:
225+
case 88200:
226+
rate = 192000;
227+
break;
228+
default:
229+
rate = F_CPU;
230+
break;
170231
}
171-
return false;
232+
#endif
233+
return set_sys_clock_khz(rate, false);
172234
}
173235

174236
bool I2S::setMCLKmult(int mult) {

libraries/I2S/src/I2S.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,4 @@ class I2S : public Stream, public AudioOutputBase {
188188
PIOProgram *_i2sMCLK;
189189
PIO _pio, _pioMCLK;
190190
int _sm, _smMCLK;
191-
192-
static const int I2SSYSCLK_44_1 = 135600; // 44.1, 88.2 kHz sample rates
193-
static const int I2SSYSCLK_8 = 153600; // 8k, 16, 32, 48, 96, 192 kHz
194-
195191
};

0 commit comments

Comments
 (0)