Skip to content

Commit 341a234

Browse files
authored
Merge pull request #66 from timchinowsky/add_pioasm_i2c_codec
add codec example
2 parents 357f933 + a9f20ea commit 341a234

File tree

1 file changed

+210
-0
lines changed

1 file changed

+210
-0
lines changed

Diff for: examples/pioasm_i2s_codec.py

+210
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
# SPDX-FileCopyrightText: 2024 Tim Chinowsky
2+
# SPDX-License-Identifier: MIT
3+
4+
import array
5+
import board
6+
import rp2pio
7+
8+
import adafruit_pioasm
9+
10+
# Implement extended multichannel I2S interface like that used by audio codecs
11+
# such as the TAC5212. In extended I2S, "Left" and "Right" can each contain
12+
# multiple channels, so for instance 8 channels of audio can be sent as a "left"
13+
# containing 4 channels and a "right" containing 4 channels.
14+
15+
# In this implementation the number of bits per sample, sample rate, and
16+
# number of channels can be independently specified. The number of channels must
17+
# be even, to divide evenly between left and right.
18+
19+
# Ramped test data containing the requested number of sample sets (one set = one
20+
# sample for each channel) and spanning the specified number of bits will be generated
21+
# and sent out over I2S on the specified pins.
22+
23+
# Data will be preceded and followed by a set of zeros for convenience.
24+
# (Some protocol analyzers have trouble analyzing serial data at the the beginning
25+
# and end of a data set)
26+
27+
# At the same time that I2S data is sent out the out_pin, I2S data will be received
28+
# on the in_pin. If the output is looped back (connected) to the input, the data
29+
# received should be the same as the data sent.
30+
31+
# Some samples run in loopback configuration:
32+
33+
# bits per sample: 16
34+
# sample rate: 48000
35+
# channels: 4
36+
# sample sets: 4
37+
38+
# actual sample frequency 47984.6 Hz
39+
# bit clock 3071017.0 Hz
40+
41+
# write: 00000000 00000000 00000000 00000000
42+
# read: 00000000 00000000 00000000 00000000
43+
44+
# write: 00000000 00001111 00002222 00003333
45+
# read: 00000000 00001111 00002222 00003333
46+
47+
# write: 00004444 00005555 00006666 00007777
48+
# read: 00004444 00005555 00006666 00007777
49+
50+
# write: 00008888 00009999 0000aaaa 0000bbbb
51+
# read: 00008888 00009999 0000aaaa 0000bbbb
52+
53+
# write: 0000cccc 0000dddd 0000eeee 0000ffff
54+
# read: 0000cccc 0000dddd 0000eeee 0000ffff
55+
56+
# write: 00000000 00000000 00000000 00000000
57+
# read: 00000000 00000000 00000000 00000000
58+
59+
# bits per sample: 24
60+
# sample rate: 24000
61+
# channels: 8
62+
# sample sets: 5
63+
64+
# actual sample frequency 23987.7 Hz
65+
# bit clock 4605642.0 Hz
66+
67+
# write: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
68+
# read: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
69+
70+
# write: 00000000 00069069 000d20d2 0013b13b 001a41a4 0020d20d 00276276 002df2df
71+
# read: 00000000 00069069 000d20d2 0013b13b 001a41a4 0020d20d 00276276 002df2df
72+
73+
# write: 00348348 003b13b1 0041a41a 00483482 004ec4ec 00555554 005be5be 00627626
74+
# read: 00348348 003b13b1 0041a41a 00483482 004ec4ec 00555554 005be5be 00627626
75+
76+
# write: 00690690 006f96f8 00762762 007cb7ca 00834834 0089d89c 00906904 0096f96c
77+
# read: 00690690 006f96f8 00762762 007cb7ca 00834834 0089d89c 00906904 0096f96c
78+
79+
# write: 009d89d8 00a41a40 00aaaaa8 00b13b10 00b7cb7c 00be5be4 00c4ec4c 00cb7cb4
80+
# read: 009d89d8 00a41a40 00aaaaa8 00b13b10 00b7cb7c 00be5be4 00c4ec4c 00cb7cb4
81+
82+
# write: 00d20d20 00d89d88 00df2df0 00e5be58 00ec4ec4 00f2df2c 00f96f94 00ffffff
83+
# read: 00d20d20 00d89d88 00df2df0 00e5be58 00ec4ec4 00f2df2c 00f96f94 00ffffff
84+
85+
# write: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
86+
# read: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
87+
88+
89+
def i2s_codec( # pylint: disable=too-many-arguments
90+
channels=2,
91+
sample_rate=48000,
92+
bits=16,
93+
bclk_pin=None,
94+
out_pin=None,
95+
in_pin=None,
96+
):
97+
i2s_clock = sample_rate * channels * bits
98+
pio_clock = 4 * i2s_clock
99+
pio_code = """
100+
.program i2s_codec
101+
.side_set 2
102+
; at program start we initialize the bit count top
103+
; (which may be >32) with data
104+
; pulled from the input fifo
105+
pull noblock ; first empty the input fifo
106+
pull noblock
107+
pull noblock
108+
pull noblock
109+
out null, 32 ; then clear OSR so we can get a new value
110+
pull block ; then get the bit count top value from the fifo
111+
; /--- LRCLK
112+
; |/-- BCLK
113+
; ||
114+
mov x, osr; side 0b01 [1] ; save it in x
115+
out null, 32 side 0b00 [1]
116+
mov y, x side 0b01 [1] ; start of main loop (wrap target=8)
117+
bitloop1:
118+
out pins 1 side 0b00
119+
in pins 1 side 0b00
120+
jmp y-- bitloop1 side 0b01 [1]
121+
out pins 1 side 0b10
122+
in pins 1 side 0b10
123+
mov y, x side 0b11 [1]
124+
bitloop0:
125+
out pins 1 side 0b10
126+
in pins 1 side 0b10
127+
jmp y-- bitloop0 side 0b11 [1]
128+
out pins 1 side 0b00
129+
in pins 1 side 0b00
130+
"""
131+
pio_params = {
132+
"frequency": pio_clock,
133+
"first_out_pin": out_pin,
134+
"first_in_pin": in_pin,
135+
"first_sideset_pin": bclk_pin,
136+
"sideset_pin_count": 2,
137+
"auto_pull": True,
138+
"auto_push": True,
139+
"out_shift_right": False,
140+
"in_shift_right": False,
141+
"pull_threshold": bits,
142+
"push_threshold": bits,
143+
"wait_for_txstall": False,
144+
"wrap_target": 8,
145+
}
146+
pio_instructions = adafruit_pioasm.assemble(pio_code)
147+
i2s_clock = sample_rate * channels * bits
148+
pio_clock = 4 * i2s_clock
149+
pio = rp2pio.StateMachine(pio_instructions, **pio_params)
150+
return pio
151+
152+
153+
def spaced_samples(length, bits):
154+
max_int = (1 << bits) - 1
155+
if length == 1:
156+
return [0]
157+
step = max_int / (length - 1)
158+
result = [round(i * step) for i in range(length)]
159+
result[0] = 0
160+
result[-1] = max_int
161+
return result
162+
163+
164+
while True:
165+
print()
166+
BITS = int(input("# bits per sample: "))
167+
SAMPLE_RATE = int(input("# sample rate: "))
168+
CHANNELS = int(input("# channels: "))
169+
SAMPLE_SETS = int(input("# sample sets: "))
170+
171+
n_samples = CHANNELS * SAMPLE_SETS
172+
buffer_type = "L"
173+
buffer_width = 32
174+
data = [0] * CHANNELS + spaced_samples(n_samples, BITS) + [0] * CHANNELS
175+
# initialize pio bit count top value by sending it at the start of output data
176+
bit_count_top = BITS * (CHANNELS // 2) - 2
177+
buffer_out = array.array(
178+
buffer_type, [bit_count_top] + [d << (buffer_width - BITS) for d in data]
179+
)
180+
buffer_in = array.array(buffer_type, [0] * len(data))
181+
182+
PIO = i2s_codec(
183+
channels=CHANNELS,
184+
bits=BITS,
185+
sample_rate=SAMPLE_RATE,
186+
out_pin=board.D9,
187+
in_pin=board.D10,
188+
bclk_pin=board.D5, # L/R signal will be one pin higher, i.e. D6
189+
)
190+
print()
191+
print(f"# actual sample frequency {PIO.frequency/4/CHANNELS/BITS:9.1f} Hz")
192+
print(f"# bit clock {PIO.frequency/4:9.1f} Hz")
193+
print()
194+
PIO.write_readinto(buffer_out, buffer_in)
195+
start = 0
196+
line_length = CHANNELS
197+
198+
while start < len(buffer_in):
199+
print("# write: ", end="")
200+
for i in range(start, min(len(data), start + line_length)):
201+
print(f"{data[i]:0{buffer_width/4}x} ", end=" ")
202+
print()
203+
print("# read: ", end="")
204+
for i in range(start, min(len(buffer_in), start + line_length)):
205+
print(f"{buffer_in[i]:0{buffer_width/4}x} ", end=" ")
206+
print()
207+
print()
208+
start += line_length
209+
PIO.deinit()
210+
del PIO

0 commit comments

Comments
 (0)