You've already forked ivan-torvalds-GuitarPedal
forked from AllSpiceMirrors/torvalds-GuitarPedal
This keeps the i2s_dma_buf as a 128-byte ring buffer, but instead of
being organized as 32 words of 32 bits each, it introduces the notion of a
typedef struct {
s32 left, right;
} raw_sample_t;
and makes the DMA ring-buffer be 16 of these entries instead.
The USB output buffer has also been changed to match this sample model,
and renamed to better reflect the fact that it's for USB.
The i2s PIO program is changed to use auto-pull to make it a bit more
streamlined. It could also have kept doing the explicit 'pull'
instructions, but that would have required another non-looping cycle to
set up.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
110 lines
2.8 KiB
Plaintext
110 lines
2.8 KiB
Plaintext
;
|
|
; We want a target sample frequency of 48kHz, which
|
|
; with 32-bit i2s data means a dot clock of 3.072MHz.
|
|
;
|
|
; We clock the rp2354 to 153.6MHz, which is exactly
|
|
; 50x that dot clock frequency.
|
|
;
|
|
; We then use a PIO divider of 25, and make the PIO
|
|
; program do 2 cycles per clock.
|
|
;
|
|
; i2s: FSYNC and each data bit is transmitted on the
|
|
; falling edge of BCLK, so we set those bits the cycle
|
|
; when BCLK goes low ("side 0") so that they are stable
|
|
; on the rising edge ("side 1").
|
|
;
|
|
; The actual data is transmitted one cycle after FSYNC
|
|
; goes low
|
|
;
|
|
.pio_version 1
|
|
|
|
.program i2s_tx
|
|
.fifo tx
|
|
.clock_div 25
|
|
.side_set 2
|
|
|
|
.wrap_target
|
|
out pins, 1 side 0b00 ; Cycle 0: FSYNC low. Output R0 (from previous frame)
|
|
public start_tx:
|
|
set x, 30 side 0b01
|
|
loop_low:
|
|
out pins, 1 side 0b00 ; Cycle 2: Output L31..L1
|
|
jmp x-- loop_low side 0b01
|
|
|
|
out pins, 1 side 0b10 ; Cycle 64: FSYNC high. Output L0
|
|
set x, 30 side 0b11
|
|
loop_high:
|
|
out pins, 1 side 0b10 ; Cycle 66: Output R31..R1
|
|
jmp x-- loop_high side 0b11
|
|
.wrap
|
|
|
|
% c-sdk {
|
|
//
|
|
// 'pin' is BCLK (MCU output)
|
|
// 'pin+1' is FSYNC (MCU output)
|
|
// 'pin+2' is DIN (MCU output! It's DIN from the CODEC standpoint!)
|
|
// 'pin+3' is DOUT (MCU input! It's DOUT from the CODEC standpoint!)
|
|
//
|
|
static inline void i2s_tx_program_init(PIO pio, uint sm, uint offset, uint pin)
|
|
{
|
|
pio_sm_config c = i2s_tx_program_get_default_config(offset);
|
|
|
|
pio_gpio_init(pio, pin);
|
|
pio_gpio_init(pio, pin+1);
|
|
pio_gpio_init(pio, pin+2);
|
|
pio_gpio_init(pio, pin+3);
|
|
|
|
// three output pins, one input
|
|
pio_sm_set_pindirs_with_mask(pio, sm, 0b0111 << pin, 0b1111 << pin);
|
|
|
|
// BCLK and FSYNC using sideset
|
|
sm_config_set_sideset_pin_base(&c, pin);
|
|
// DOUT using 'OUT' instruction
|
|
sm_config_set_out_pins(&c, pin+2, 1);
|
|
// Auto-pull, shift left (MSB first), 32 bits
|
|
sm_config_set_out_shift(&c, false, true, 32);
|
|
|
|
// Load and run the program
|
|
pio_sm_init(pio, sm, offset + i2s_tx_offset_start_tx, &c);
|
|
pio_sm_set_enabled(pio, sm, true);
|
|
}
|
|
%}
|
|
|
|
.program i2s_rx
|
|
.fifo rx
|
|
.clock_div 25
|
|
|
|
public start_rx:
|
|
wait 1 jmppin ; wait for FSYNC going high
|
|
wait 0 jmppin [1] ; wait for FSYNC going low, delay to sample middle
|
|
|
|
.wrap_target
|
|
set x, 30
|
|
loop_left:
|
|
in pins, 1
|
|
jmp x-- loop_left
|
|
in pins, 1 ; sample LSB of left channel
|
|
|
|
set x, 30
|
|
loop_right:
|
|
in pins, 1
|
|
jmp x-- loop_right
|
|
in pins, 1 ; sample LSB of right channel
|
|
.wrap
|
|
|
|
% c-sdk {
|
|
static inline void i2s_rx_program_init(PIO pio, uint sm, uint offset, uint pin)
|
|
{
|
|
pio_sm_config c = i2s_rx_program_get_default_config(offset);
|
|
|
|
sm_config_set_jmp_pin(&c, pin+1); // FSYNC
|
|
sm_config_set_in_pins(&c, pin+3); // DOUT
|
|
// Auto-push, shift left (MSB first), 32 bits
|
|
sm_config_set_in_shift(&c, false, true, 32);
|
|
|
|
// Load and run the program
|
|
pio_sm_init(pio, sm, offset + i2s_rx_offset_start_rx, &c);
|
|
pio_sm_set_enabled(pio, sm, true);
|
|
}
|
|
%}
|