aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp
diff options
context:
space:
mode:
authorThor McAvenia <[email protected]>2025-05-16 00:09:10 -0700
committerDario Nieuwenhuis <[email protected]>2025-09-05 21:01:37 +0200
commit241129c569023dc71d0025cdc41bcfe40418e7b8 (patch)
tree2a6bf63e2deef67efd0d9f71e86d651d37a0a125 /embassy-rp
parent4ac4452c16e1b75bcf70814a9530b7724cc16e7d (diff)
Add PioI2sIn, PioI2sInProgram, and example binary
Diffstat (limited to 'embassy-rp')
-rw-r--r--embassy-rp/CHANGELOG.md3
-rw-r--r--embassy-rp/src/pio_programs/i2s.rs96
2 files changed, 95 insertions, 4 deletions
diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md
index ebdc3e1c8..b50d41dd1 100644
--- a/embassy-rp/CHANGELOG.md
+++ b/embassy-rp/CHANGELOG.md
@@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- Add PIO SPI
12- Add PIO I2S input
13
11## 0.8.0 - 2025-08-26 14## 0.8.0 - 2025-08-26
12 15
13## 0.7.1 - 2025-08-26 16## 0.7.1 - 2025-08-26
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);