aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp/src/pio_programs/i2s.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2025-09-05 19:08:06 +0000
committerGitHub <[email protected]>2025-09-05 19:08:06 +0000
commit46bf0c71cceacc68fa739726d374e27f1a17bba0 (patch)
tree1a620a78feef49cd01eb42403f486cba7c068720 /embassy-rp/src/pio_programs/i2s.rs
parentbbcf9af87eddbf9e1dba3281a3f1a9e5e419477f (diff)
parent241129c569023dc71d0025cdc41bcfe40418e7b8 (diff)
Merge pull request #4210 from mcaveniathor/pio_i2s_rx
Add PioI2sIn, PioI2sInProgram, and example binary
Diffstat (limited to 'embassy-rp/src/pio_programs/i2s.rs')
-rw-r--r--embassy-rp/src/pio_programs/i2s.rs96
1 files changed, 92 insertions, 4 deletions
diff --git a/embassy-rp/src/pio_programs/i2s.rs b/embassy-rp/src/pio_programs/i2s.rs
index 7ceed3fa6..2382a3f9f 100644
--- a/embassy-rp/src/pio_programs/i2s.rs
+++ b/embassy-rp/src/pio_programs/i2s.rs
@@ -1,13 +1,101 @@
1//! Pio backed I2s output 1//! Pio backed I2s output and output drivers
2 2
3use fixed::traits::ToFixed; 3use fixed::traits::ToFixed;
4 4
5use crate::dma::{AnyChannel, Channel, Transfer}; 5use crate::dma::{AnyChannel, Channel, Transfer};
6use crate::gpio::Pull;
6use crate::pio::{ 7use crate::pio::{
7 Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, 8 Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine,
8}; 9};
9use crate::Peri; 10use crate::Peri;
10 11
12/// This struct represents an i2s receiver & controller driver program
13pub struct PioI2sInProgram<'d, PIO: Instance> {
14 prg: LoadedProgram<'d, PIO>,
15}
16
17impl<'d, PIO: Instance> PioI2sInProgram<'d, PIO> {
18 /// Load the input program into the given pio
19 pub fn new(common: &mut Common<'d, PIO>) -> Self {
20 let prg = pio::pio_asm! {
21 ".side_set 2",
22 " set x, 14 side 0b01",
23 "left_data:",
24 " in pins, 1 side 0b00", // read one left-channel bit from SD
25 " jmp x-- left_data side 0b01",
26 " in pins, 1 side 0b10", // ws changes 1 clock before MSB
27 " set x, 14 side 0b11",
28 "right_data:",
29 " in pins, 1 side 0b10",
30 " jmp x-- right_data side 0b11",
31 " in pins, 1 side 0b00" // ws changes 1 clock before ms
32 };
33 let prg = common.load_program(&prg.program);
34 Self { prg }
35 }
36}
37
38/// Pio backed I2s input driver
39pub struct PioI2sIn<'d, P: Instance, const S: usize> {
40 dma: Peri<'d, AnyChannel>,
41 sm: StateMachine<'d, P, S>,
42}
43
44impl<'d, P: Instance, const S: usize> PioI2sIn<'d, P, S> {
45 /// Configure a state machine to act as both the controller (provider of SCK and WS) and receiver (of SD) for an I2S signal
46 pub fn new(
47 common: &mut Common<'d, P>,
48 mut sm: StateMachine<'d, P, S>,
49 dma: Peri<'d, impl Channel>,
50 // Whether or not to use the MCU's internal pull-down resistor, as the
51 // Pico 2 is known to have problems with the inbuilt pulldowns, many
52 // opt to just use an external pull down resistor to meet requirements of common
53 // i2s microphones such as the INMP441
54 data_pulldown: bool,
55 data_pin: Peri<'d, impl PioPin>,
56 bit_clock_pin: Peri<'d, impl PioPin>,
57 lr_clock_pin: Peri<'d, impl PioPin>,
58 sample_rate: u32,
59 bit_depth: u32,
60 channels: u32,
61 program: &PioI2sInProgram<'d, P>,
62 ) -> Self {
63 let mut data_pin = common.make_pio_pin(data_pin);
64 if data_pulldown {
65 data_pin.set_pull(Pull::Down);
66 }
67 let bit_clock_pin = common.make_pio_pin(bit_clock_pin);
68 let left_right_clock_pin = common.make_pio_pin(lr_clock_pin);
69
70 let cfg = {
71 let mut cfg = Config::default();
72 cfg.use_program(&program.prg, &[&bit_clock_pin, &left_right_clock_pin]);
73 cfg.set_in_pins(&[&data_pin]);
74 let clock_frequency = sample_rate * bit_depth * channels;
75 cfg.clock_divider = (crate::clocks::clk_sys_freq() as f64 / clock_frequency as f64 / 2.).to_fixed();
76 cfg.shift_in = ShiftConfig {
77 threshold: 32,
78 direction: ShiftDirection::Left,
79 auto_fill: true,
80 };
81 // join fifos to have twice the time to start the next dma transfer
82 cfg.fifo_join = FifoJoin::RxOnly; // both control signals are sent via side-setting
83 cfg
84 };
85 sm.set_config(&cfg);
86 sm.set_pin_dirs(Direction::In, &[&data_pin]);
87 sm.set_pin_dirs(Direction::Out, &[&left_right_clock_pin, &bit_clock_pin]);
88 sm.set_enable(true);
89
90 Self { dma: dma.into(), sm }
91 }
92
93 /// Return an in-prograss dma transfer future. Awaiting it will guarentee a complete transfer.
94 pub fn read<'b>(&'b mut self, buff: &'b mut [u32]) -> Transfer<'b, AnyChannel> {
95 self.sm.rx().dma_pull(self.dma.reborrow(), buff, false)
96 }
97}
98
11/// This struct represents an i2s output driver program 99/// This struct represents an i2s output driver program
12/// 100///
13/// The sample bit-depth is set through scratch register `Y`. 101/// The sample bit-depth is set through scratch register `Y`.
@@ -26,12 +114,12 @@ impl<'d, PIO: Instance> PioI2sOutProgram<'d, PIO> {
26 "left_data:", 114 "left_data:",
27 " out pins, 1 side 0b00", 115 " out pins, 1 side 0b00",
28 " jmp x-- left_data side 0b01", 116 " jmp x-- left_data side 0b01",
29 " out pins 1 side 0b10", 117 " out pins, 1 side 0b10",
30 " mov x, y side 0b11", 118 " mov x, y side 0b11",
31 "right_data:", 119 "right_data:",
32 " out pins 1 side 0b10", 120 " out pins, 1 side 0b10",
33 " jmp x-- right_data side 0b11", 121 " jmp x-- right_data side 0b11",
34 " out pins 1 side 0b00", 122 " out pins, 1 side 0b00",
35 ); 123 );
36 124
37 let prg = common.load_program(&prg.program); 125 let prg = common.load_program(&prg.program);