Skip to content

Commit 3dbe5cf

Browse files
Add I2S::swapClocks() for boards w/reversed pins (earlephilhower#1298)
Allow users of boards like the Pico-Audiom where the LRCLK comes before the BCLK pin, to swap the BCLK/LRCLK of the I2S interface. Fixes earlephilhower#1287
1 parent 8e8fea4 commit 3dbe5cf

File tree

6 files changed

+213
-17
lines changed

6 files changed

+213
-17
lines changed

docs/i2s.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ Enables LSB-J format for I2S output. In this mode the MSB comes out at the
6464
same time as the LRCLK changes, and not the normal 1-cycle delay. Useful for
6565
DAC chips like the PT8211.
6666

67+
bool swapClocks()
68+
~~~~~~~~~~~~~~~~~
69+
Certain boards are hardwired with the WCLK before the BCLK, instead of the normal
70+
way around. This call swaps the WCLK and BCLK pins. Note that you still call
71+
``setBCLK(x)`` with ``x`` being the lowest pin ID (i.e. in swapClocks mode the
72+
``setBCLK`` call actually sets LRCLK).
73+
6774
bool begin()/begin(long sampleRate)
6875
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6976
Start the I2S device up with the given sample rate, or with the value set

libraries/I2S/keywords.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ setBitsPerSample KEYWORD2
2020
setFrequency KEYWORD2
2121
setBuffers KEYWORD2
2222
setLSBJFormat KEYWORD2
23+
swapClocks KEYWORD2
2324

2425
read8 KEYWORD2
2526
read16 KEYWORD2

libraries/I2S/src/I2S.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ I2S::I2S(PinMode direction) {
5252
_bufferWords = 0;
5353
_silenceSample = 0;
5454
_isLSBJ = false;
55+
_swapClocks = false;
5556
}
5657

5758
I2S::~I2S() {
@@ -108,6 +109,14 @@ bool I2S::setLSBJFormat() {
108109
return true;
109110
}
110111

112+
bool I2S::swapClocks() {
113+
if (_running || !_isOutput) {
114+
return false;
115+
}
116+
_swapClocks = true;
117+
return true;
118+
}
119+
111120
void I2S::onTransmit(void(*fn)(void)) {
112121
if (_isOutput) {
113122
_cb = fn;
@@ -130,16 +139,20 @@ bool I2S::begin() {
130139
_running = true;
131140
_hasPeeked = false;
132141
int off = 0;
133-
_i2s = new PIOProgram(_isOutput ? (_isLSBJ ? &pio_lsbj_out_program : &pio_i2s_out_program) : &pio_i2s_in_program);
142+
if (!_swapClocks) {
143+
_i2s = new PIOProgram(_isOutput ? (_isLSBJ ? &pio_lsbj_out_program : &pio_i2s_out_program) : &pio_i2s_in_program);
144+
} else {
145+
_i2s = new PIOProgram(_isOutput ? (_isLSBJ ? &pio_lsbj_out_swap_program : &pio_i2s_out_swap_program) : &pio_i2s_in_swap_program);
146+
}
134147
_i2s->prepare(&_pio, &_sm, &off);
135148
if (_isOutput) {
136149
if (_isLSBJ) {
137-
pio_lsbj_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
150+
pio_lsbj_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks);
138151
} else {
139-
pio_i2s_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
152+
pio_i2s_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks);
140153
}
141154
} else {
142-
pio_i2s_in_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
155+
pio_i2s_in_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks);
143156
}
144157
setFrequency(_freq);
145158
if (_bps == 8) {

libraries/I2S/src/I2S.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class I2S : public Stream {
3434
bool setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample = 0);
3535
bool setFrequency(int newFreq);
3636
bool setLSBJFormat();
37+
bool swapClocks();
3738

3839
bool begin(long sampleRate) {
3940
setFrequency(sampleRate);
@@ -107,6 +108,7 @@ class I2S : public Stream {
107108
int32_t _silenceSample;
108109
bool _isLSBJ;
109110
bool _isOutput;
111+
bool _swapClocks;
110112

111113
bool _running;
112114

libraries/I2S/src/pio_i2s.pio

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,28 @@ right:
4141
out pins, 1 side 0b00 ; Last bit of right also has WCLK change
4242
; Loop back to beginning...
4343

44+
.program pio_i2s_out_swap
45+
.side_set 2 ; 0 = wclk, 1=bclk
46+
47+
; The C code should place (number of bits/sample - 2) in Y and
48+
; also update the SHIFTCTRL to be 24 or 32 as appropriate
49+
50+
; +----- BCLK
51+
; |+---- WCLK
52+
mov x, y side 0b10
53+
left:
54+
out pins, 1 side 0b00
55+
jmp x--, left side 0b10
56+
out pins, 1 side 0b01 ; Last bit of left has WCLK change per I2S spec
57+
58+
mov x, y side 0b11
59+
right:
60+
out pins, 1 side 0b01
61+
jmp x--, right side 0b11
62+
out pins, 1 side 0b00 ; Last bit of right also has WCLK change
63+
; Loop back to beginning...
64+
65+
4466

4567
.program pio_lsbj_out
4668
.side_set 2 ; 0 = bclk, 1=wclk
@@ -64,6 +86,28 @@ right:
6486
; Loop back to beginning...
6587

6688

89+
.program pio_lsbj_out_swap
90+
.side_set 2 ; 0 = wclk, 1=bclk
91+
92+
; The C code should place (number of bits/sample - 2) in Y and
93+
; also update the SHIFTCTRL to be 24 or 32 as appropriate
94+
95+
; +----- BCLK
96+
; |+---- WCLK
97+
mov x, y side 0b10
98+
left:
99+
out pins, 1 side 0b01
100+
jmp x--, left side 0b11
101+
out pins, 1 side 0b01
102+
103+
mov x, y side 0b11
104+
right:
105+
out pins, 1 side 0b00
106+
jmp x--, right side 0b10
107+
out pins, 1 side 0b00
108+
; Loop back to beginning...
109+
110+
67111

68112
.program pio_i2s_in ; Note this is the same as _out, just "in" and not "out"
69113
.side_set 2 ; 0 = bclk, 1=wclk
@@ -86,16 +130,39 @@ right:
86130
in pins, 1 side 0b01 ; Last bit of right also has WCLK change
87131
; Loop back to beginning...
88132

89-
133+
134+
.program pio_i2s_in_swap ; Note this is the same as _out, just "in" and not "out"
135+
.side_set 2 ; 0 = wclk, 1=bclk
136+
137+
; The C code should place (number of bits/sample - 2) in Y and
138+
; also update the SHIFTCTRL to be 24 or 32 as appropriate
139+
140+
; +----- BCLK
141+
; |+---- WCLK
142+
mov x, y side 0b00
143+
left:
144+
in pins, 1 side 0b10
145+
jmp x--, left side 0b00
146+
in pins, 1 side 0b11 ; Last bit of left has WCLK change per I2S spec
147+
148+
mov x, y side 0b01
149+
right:
150+
in pins, 1 side 0b11
151+
jmp x--, right side 0b01
152+
in pins, 1 side 0b10 ; Last bit of right also has WCLK change
153+
; Loop back to beginning...
154+
155+
156+
90157

91158
% c-sdk {
92159

93-
static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
160+
static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap) {
94161
pio_gpio_init(pio, data_pin);
95162
pio_gpio_init(pio, clock_pin_base);
96163
pio_gpio_init(pio, clock_pin_base + 1);
97164

98-
pio_sm_config sm_config = pio_i2s_out_program_get_default_config(offset);
165+
pio_sm_config sm_config = swap ? pio_i2s_out_swap_program_get_default_config(offset) : pio_i2s_out_program_get_default_config(offset);
99166

100167
sm_config_set_out_pins(&sm_config, data_pin, 1);
101168
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
@@ -111,12 +178,12 @@ static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint
111178
pio_sm_exec(pio, sm, pio_encode_set(pio_y, bits - 2));
112179
}
113180

114-
static inline void pio_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
181+
static inline void pio_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap) {
115182
pio_gpio_init(pio, data_pin);
116183
pio_gpio_init(pio, clock_pin_base);
117184
pio_gpio_init(pio, clock_pin_base + 1);
118185

119-
pio_sm_config sm_config = pio_lsbj_out_program_get_default_config(offset);
186+
pio_sm_config sm_config = swap ? pio_lsbj_out_swap_program_get_default_config(offset) : pio_lsbj_out_program_get_default_config(offset);
120187

121188
sm_config_set_out_pins(&sm_config, data_pin, 1);
122189
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
@@ -132,12 +199,12 @@ static inline void pio_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint
132199
pio_sm_exec(pio, sm, pio_encode_set(pio_y, bits - 2));
133200
}
134201

135-
static inline void pio_i2s_in_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
202+
static inline void pio_i2s_in_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap) {
136203
pio_gpio_init(pio, data_pin);
137204
pio_gpio_init(pio, clock_pin_base);
138205
pio_gpio_init(pio, clock_pin_base + 1);
139206

140-
pio_sm_config sm_config = pio_i2s_in_program_get_default_config(offset);
207+
pio_sm_config sm_config = swap ? pio_i2s_in_swap_program_get_default_config(offset) : pio_i2s_in_program_get_default_config(offset);
141208

142209
sm_config_set_in_pins(&sm_config, data_pin);
143210
sm_config_set_sideset_pins(&sm_config, clock_pin_base);

libraries/I2S/src/pio_i2s.pio.h

Lines changed: 112 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,41 @@ static inline pio_sm_config pio_i2s_out_program_get_default_config(uint offset)
4343
}
4444
#endif
4545

46+
// ---------------- //
47+
// pio_i2s_out_swap //
48+
// ---------------- //
49+
50+
#define pio_i2s_out_swap_wrap_target 0
51+
#define pio_i2s_out_swap_wrap 7
52+
53+
static const uint16_t pio_i2s_out_swap_program_instructions[] = {
54+
// .wrap_target
55+
0xb022, // 0: mov x, y side 2
56+
0x6001, // 1: out pins, 1 side 0
57+
0x1041, // 2: jmp x--, 1 side 2
58+
0x6801, // 3: out pins, 1 side 1
59+
0xb822, // 4: mov x, y side 3
60+
0x6801, // 5: out pins, 1 side 1
61+
0x1845, // 6: jmp x--, 5 side 3
62+
0x6001, // 7: out pins, 1 side 0
63+
// .wrap
64+
};
65+
66+
#if !PICO_NO_HARDWARE
67+
static const struct pio_program pio_i2s_out_swap_program = {
68+
.instructions = pio_i2s_out_swap_program_instructions,
69+
.length = 8,
70+
.origin = -1,
71+
};
72+
73+
static inline pio_sm_config pio_i2s_out_swap_program_get_default_config(uint offset) {
74+
pio_sm_config c = pio_get_default_sm_config();
75+
sm_config_set_wrap(&c, offset + pio_i2s_out_swap_wrap_target, offset + pio_i2s_out_swap_wrap);
76+
sm_config_set_sideset(&c, 2, false, false);
77+
return c;
78+
}
79+
#endif
80+
4681
// ------------ //
4782
// pio_lsbj_out //
4883
// ------------ //
@@ -78,6 +113,41 @@ static inline pio_sm_config pio_lsbj_out_program_get_default_config(uint offset)
78113
}
79114
#endif
80115

116+
// ----------------- //
117+
// pio_lsbj_out_swap //
118+
// ----------------- //
119+
120+
#define pio_lsbj_out_swap_wrap_target 0
121+
#define pio_lsbj_out_swap_wrap 7
122+
123+
static const uint16_t pio_lsbj_out_swap_program_instructions[] = {
124+
// .wrap_target
125+
0xb022, // 0: mov x, y side 2
126+
0x6801, // 1: out pins, 1 side 1
127+
0x1841, // 2: jmp x--, 1 side 3
128+
0x6801, // 3: out pins, 1 side 1
129+
0xb822, // 4: mov x, y side 3
130+
0x6001, // 5: out pins, 1 side 0
131+
0x1045, // 6: jmp x--, 5 side 2
132+
0x6001, // 7: out pins, 1 side 0
133+
// .wrap
134+
};
135+
136+
#if !PICO_NO_HARDWARE
137+
static const struct pio_program pio_lsbj_out_swap_program = {
138+
.instructions = pio_lsbj_out_swap_program_instructions,
139+
.length = 8,
140+
.origin = -1,
141+
};
142+
143+
static inline pio_sm_config pio_lsbj_out_swap_program_get_default_config(uint offset) {
144+
pio_sm_config c = pio_get_default_sm_config();
145+
sm_config_set_wrap(&c, offset + pio_lsbj_out_swap_wrap_target, offset + pio_lsbj_out_swap_wrap);
146+
sm_config_set_sideset(&c, 2, false, false);
147+
return c;
148+
}
149+
#endif
150+
81151
// ---------- //
82152
// pio_i2s_in //
83153
// ---------- //
@@ -111,12 +181,47 @@ static inline pio_sm_config pio_i2s_in_program_get_default_config(uint offset) {
111181
sm_config_set_sideset(&c, 2, false, false);
112182
return c;
113183
}
184+
#endif
185+
186+
// --------------- //
187+
// pio_i2s_in_swap //
188+
// --------------- //
189+
190+
#define pio_i2s_in_swap_wrap_target 0
191+
#define pio_i2s_in_swap_wrap 7
114192

115-
static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
193+
static const uint16_t pio_i2s_in_swap_program_instructions[] = {
194+
// .wrap_target
195+
0xa022, // 0: mov x, y side 0
196+
0x5001, // 1: in pins, 1 side 2
197+
0x0041, // 2: jmp x--, 1 side 0
198+
0x5801, // 3: in pins, 1 side 3
199+
0xa822, // 4: mov x, y side 1
200+
0x5801, // 5: in pins, 1 side 3
201+
0x0845, // 6: jmp x--, 5 side 1
202+
0x5001, // 7: in pins, 1 side 2
203+
// .wrap
204+
};
205+
206+
#if !PICO_NO_HARDWARE
207+
static const struct pio_program pio_i2s_in_swap_program = {
208+
.instructions = pio_i2s_in_swap_program_instructions,
209+
.length = 8,
210+
.origin = -1,
211+
};
212+
213+
static inline pio_sm_config pio_i2s_in_swap_program_get_default_config(uint offset) {
214+
pio_sm_config c = pio_get_default_sm_config();
215+
sm_config_set_wrap(&c, offset + pio_i2s_in_swap_wrap_target, offset + pio_i2s_in_swap_wrap);
216+
sm_config_set_sideset(&c, 2, false, false);
217+
return c;
218+
}
219+
220+
static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap) {
116221
pio_gpio_init(pio, data_pin);
117222
pio_gpio_init(pio, clock_pin_base);
118223
pio_gpio_init(pio, clock_pin_base + 1);
119-
pio_sm_config sm_config = pio_i2s_out_program_get_default_config(offset);
224+
pio_sm_config sm_config = swap ? pio_i2s_out_swap_program_get_default_config(offset) : pio_i2s_out_program_get_default_config(offset);
120225
sm_config_set_out_pins(&sm_config, data_pin, 1);
121226
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
122227
sm_config_set_out_shift(&sm_config, false, true, (bits <= 16) ? 2 * bits : bits);
@@ -127,11 +232,11 @@ static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint
127232
pio_sm_set_pins(pio, sm, 0); // clear pins
128233
pio_sm_exec(pio, sm, pio_encode_set(pio_y, bits - 2));
129234
}
130-
static inline void pio_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
235+
static inline void pio_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap) {
131236
pio_gpio_init(pio, data_pin);
132237
pio_gpio_init(pio, clock_pin_base);
133238
pio_gpio_init(pio, clock_pin_base + 1);
134-
pio_sm_config sm_config = pio_lsbj_out_program_get_default_config(offset);
239+
pio_sm_config sm_config = swap ? pio_lsbj_out_swap_program_get_default_config(offset) : pio_lsbj_out_program_get_default_config(offset);
135240
sm_config_set_out_pins(&sm_config, data_pin, 1);
136241
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
137242
sm_config_set_out_shift(&sm_config, false, true, (bits <= 16) ? 2 * bits : bits);
@@ -142,11 +247,11 @@ static inline void pio_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint
142247
pio_sm_set_pins(pio, sm, 0); // clear pins
143248
pio_sm_exec(pio, sm, pio_encode_set(pio_y, bits - 2));
144249
}
145-
static inline void pio_i2s_in_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
250+
static inline void pio_i2s_in_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap) {
146251
pio_gpio_init(pio, data_pin);
147252
pio_gpio_init(pio, clock_pin_base);
148253
pio_gpio_init(pio, clock_pin_base + 1);
149-
pio_sm_config sm_config = pio_i2s_in_program_get_default_config(offset);
254+
pio_sm_config sm_config = swap ? pio_i2s_in_swap_program_get_default_config(offset) : pio_i2s_in_program_get_default_config(offset);
150255
sm_config_set_in_pins(&sm_config, data_pin);
151256
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
152257
sm_config_set_in_shift(&sm_config, false, true, (bits <= 16) ? 2 * bits : bits);
@@ -159,3 +264,4 @@ static inline void pio_i2s_in_program_init(PIO pio, uint sm, uint offset, uint d
159264
}
160265

161266
#endif
267+

0 commit comments

Comments
 (0)