aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp/src/pio_programs/i2s.rs
diff options
context:
space:
mode:
author1-rafael-1 <[email protected]>2025-09-15 20:07:18 +0200
committer1-rafael-1 <[email protected]>2025-09-15 20:07:18 +0200
commit6bb3d2c0720fa082f27d3cdb70f516058497ec87 (patch)
tree5a1e255cff999b00800f203b91a759c720c973e5 /embassy-rp/src/pio_programs/i2s.rs
parenteb685574601d98c44faed9a3534d056199b46e20 (diff)
parent92a6fd2946f2cbb15359290f68aa360953da2ff7 (diff)
Merge branch 'main' into rp2040-rtc-alarm
Diffstat (limited to 'embassy-rp/src/pio_programs/i2s.rs')
-rw-r--r--embassy-rp/src/pio_programs/i2s.rs114
1 files changed, 105 insertions, 9 deletions
diff --git a/embassy-rp/src/pio_programs/i2s.rs b/embassy-rp/src/pio_programs/i2s.rs
index b967f0160..2382a3f9f 100644
--- a/embassy-rp/src/pio_programs/i2s.rs
+++ b/embassy-rp/src/pio_programs/i2s.rs
@@ -1,14 +1,106 @@
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
100///
101/// The sample bit-depth is set through scratch register `Y`.
102/// `Y` has to be set to sample bit-depth - 2.
103/// (14 = 16bit, 22 = 24bit, 30 = 32bit)
12pub struct PioI2sOutProgram<'d, PIO: Instance> { 104pub struct PioI2sOutProgram<'d, PIO: Instance> {
13 prg: LoadedProgram<'d, PIO>, 105 prg: LoadedProgram<'d, PIO>,
14} 106}
@@ -17,17 +109,17 @@ impl<'d, PIO: Instance> PioI2sOutProgram<'d, PIO> {
17 /// Load the program into the given pio 109 /// Load the program into the given pio
18 pub fn new(common: &mut Common<'d, PIO>) -> Self { 110 pub fn new(common: &mut Common<'d, PIO>) -> Self {
19 let prg = pio::pio_asm!( 111 let prg = pio::pio_asm!(
20 ".side_set 2", 112 ".side_set 2", // side 0bWB - W = Word Clock, B = Bit Clock
21 " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock 113 " mov x, y side 0b01", // y stores sample depth - 2 (14 = 16bit, 22 = 24bit, 30 = 32bit)
22 "left_data:", 114 "left_data:",
23 " out pins, 1 side 0b00", 115 " out pins, 1 side 0b00",
24 " jmp x-- left_data side 0b01", 116 " jmp x-- left_data side 0b01",
25 " out pins 1 side 0b10", 117 " out pins, 1 side 0b10",
26 " set x, 14 side 0b11", 118 " mov x, y side 0b11",
27 "right_data:", 119 "right_data:",
28 " out pins 1 side 0b10", 120 " out pins, 1 side 0b10",
29 " jmp x-- right_data side 0b11", 121 " jmp x-- right_data side 0b11",
30 " out pins 1 side 0b00", 122 " out pins, 1 side 0b00",
31 ); 123 );
32 124
33 let prg = common.load_program(&prg.program); 125 let prg = common.load_program(&prg.program);
@@ -53,7 +145,6 @@ impl<'d, P: Instance, const S: usize> PioI2sOut<'d, P, S> {
53 lr_clock_pin: Peri<'d, impl PioPin>, 145 lr_clock_pin: Peri<'d, impl PioPin>,
54 sample_rate: u32, 146 sample_rate: u32,
55 bit_depth: u32, 147 bit_depth: u32,
56 channels: u32,
57 program: &PioI2sOutProgram<'d, P>, 148 program: &PioI2sOutProgram<'d, P>,
58 ) -> Self { 149 ) -> Self {
59 let data_pin = common.make_pio_pin(data_pin); 150 let data_pin = common.make_pio_pin(data_pin);
@@ -64,7 +155,7 @@ impl<'d, P: Instance, const S: usize> PioI2sOut<'d, P, S> {
64 let mut cfg = Config::default(); 155 let mut cfg = Config::default();
65 cfg.use_program(&program.prg, &[&bit_clock_pin, &left_right_clock_pin]); 156 cfg.use_program(&program.prg, &[&bit_clock_pin, &left_right_clock_pin]);
66 cfg.set_out_pins(&[&data_pin]); 157 cfg.set_out_pins(&[&data_pin]);
67 let clock_frequency = sample_rate * bit_depth * channels; 158 let clock_frequency = sample_rate * bit_depth * 2;
68 cfg.clock_divider = (crate::clocks::clk_sys_freq() as f64 / clock_frequency as f64 / 2.).to_fixed(); 159 cfg.clock_divider = (crate::clocks::clk_sys_freq() as f64 / clock_frequency as f64 / 2.).to_fixed();
69 cfg.shift_out = ShiftConfig { 160 cfg.shift_out = ShiftConfig {
70 threshold: 32, 161 threshold: 32,
@@ -78,6 +169,11 @@ impl<'d, P: Instance, const S: usize> PioI2sOut<'d, P, S> {
78 sm.set_config(&cfg); 169 sm.set_config(&cfg);
79 sm.set_pin_dirs(Direction::Out, &[&data_pin, &left_right_clock_pin, &bit_clock_pin]); 170 sm.set_pin_dirs(Direction::Out, &[&data_pin, &left_right_clock_pin, &bit_clock_pin]);
80 171
172 // Set the `y` register up to configure the sample depth
173 // The SM counts down to 0 and uses one clock cycle to set up the counter,
174 // which results in bit_depth - 2 as register value.
175 unsafe { sm.set_y(bit_depth - 2) };
176
81 sm.set_enable(true); 177 sm.set_enable(true);
82 178
83 Self { dma: dma.into(), sm } 179 Self { dma: dma.into(), sm }