diff options
| author | Dario Nieuwenhuis <[email protected]> | 2025-09-05 19:08:06 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-09-05 19:08:06 +0000 |
| commit | 46bf0c71cceacc68fa739726d374e27f1a17bba0 (patch) | |
| tree | 1a620a78feef49cd01eb42403f486cba7c068720 /embassy-rp | |
| parent | bbcf9af87eddbf9e1dba3281a3f1a9e5e419477f (diff) | |
| parent | 241129c569023dc71d0025cdc41bcfe40418e7b8 (diff) | |
Merge pull request #4210 from mcaveniathor/pio_i2s_rx
Add PioI2sIn, PioI2sInProgram, and example binary
Diffstat (limited to 'embassy-rp')
| -rw-r--r-- | embassy-rp/CHANGELOG.md | 3 | ||||
| -rw-r--r-- | embassy-rp/src/pio_programs/i2s.rs | 96 |
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 | ||
| 3 | use fixed::traits::ToFixed; | 3 | use fixed::traits::ToFixed; |
| 4 | 4 | ||
| 5 | use crate::dma::{AnyChannel, Channel, Transfer}; | 5 | use crate::dma::{AnyChannel, Channel, Transfer}; |
| 6 | use crate::gpio::Pull; | ||
| 6 | use crate::pio::{ | 7 | use 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 | }; |
| 9 | use crate::Peri; | 10 | use crate::Peri; |
| 10 | 11 | ||
| 12 | /// This struct represents an i2s receiver & controller driver program | ||
| 13 | pub struct PioI2sInProgram<'d, PIO: Instance> { | ||
| 14 | prg: LoadedProgram<'d, PIO>, | ||
| 15 | } | ||
| 16 | |||
| 17 | impl<'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 | ||
| 39 | pub struct PioI2sIn<'d, P: Instance, const S: usize> { | ||
| 40 | dma: Peri<'d, AnyChannel>, | ||
| 41 | sm: StateMachine<'d, P, S>, | ||
| 42 | } | ||
| 43 | |||
| 44 | impl<'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); |
