diff options
| -rw-r--r-- | embassy-nrf/src/chips/nrf52832.rs | 7 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52833.rs | 7 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52840.rs | 7 | ||||
| -rw-r--r-- | embassy-nrf/src/i2s.rs | 1141 | ||||
| -rw-r--r-- | embassy-nrf/src/lib.rs | 2 | ||||
| -rw-r--r-- | examples/nrf/src/bin/i2s_effect.rs | 117 | ||||
| -rw-r--r-- | examples/nrf/src/bin/i2s_monitor.rs | 115 | ||||
| -rw-r--r-- | examples/nrf/src/bin/i2s_waveform.rs | 151 |
8 files changed, 1544 insertions, 3 deletions
diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 879600adb..152dad4e3 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs | |||
| @@ -138,6 +138,9 @@ embassy_hal_common::peripherals! { | |||
| 138 | 138 | ||
| 139 | // QDEC | 139 | // QDEC |
| 140 | QDEC, | 140 | QDEC, |
| 141 | |||
| 142 | // I2S | ||
| 143 | I2S, | ||
| 141 | } | 144 | } |
| 142 | 145 | ||
| 143 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); | 146 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); |
| @@ -241,6 +244,8 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); | |||
| 241 | impl_saadc_input!(P0_30, ANALOG_INPUT6); | 244 | impl_saadc_input!(P0_30, ANALOG_INPUT6); |
| 242 | impl_saadc_input!(P0_31, ANALOG_INPUT7); | 245 | impl_saadc_input!(P0_31, ANALOG_INPUT7); |
| 243 | 246 | ||
| 247 | impl_i2s!(I2S, I2S, I2S); | ||
| 248 | |||
| 244 | pub mod irqs { | 249 | pub mod irqs { |
| 245 | use embassy_cortex_m::interrupt::_export::declare; | 250 | use embassy_cortex_m::interrupt::_export::declare; |
| 246 | 251 | ||
| @@ -281,6 +286,6 @@ pub mod irqs { | |||
| 281 | declare!(PWM2); | 286 | declare!(PWM2); |
| 282 | declare!(SPIM2_SPIS2_SPI2); | 287 | declare!(SPIM2_SPIS2_SPI2); |
| 283 | declare!(RTC2); | 288 | declare!(RTC2); |
| 284 | declare!(I2S); | ||
| 285 | declare!(FPU); | 289 | declare!(FPU); |
| 290 | declare!(I2S); | ||
| 286 | } | 291 | } |
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index d406c32c2..a99ca6343 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs | |||
| @@ -161,6 +161,9 @@ embassy_hal_common::peripherals! { | |||
| 161 | 161 | ||
| 162 | // PDM | 162 | // PDM |
| 163 | PDM, | 163 | PDM, |
| 164 | |||
| 165 | // I2S | ||
| 166 | I2S, | ||
| 164 | } | 167 | } |
| 165 | 168 | ||
| 166 | #[cfg(feature = "nightly")] | 169 | #[cfg(feature = "nightly")] |
| @@ -287,6 +290,8 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); | |||
| 287 | impl_saadc_input!(P0_30, ANALOG_INPUT6); | 290 | impl_saadc_input!(P0_30, ANALOG_INPUT6); |
| 288 | impl_saadc_input!(P0_31, ANALOG_INPUT7); | 291 | impl_saadc_input!(P0_31, ANALOG_INPUT7); |
| 289 | 292 | ||
| 293 | impl_i2s!(I2S, I2S, I2S); | ||
| 294 | |||
| 290 | pub mod irqs { | 295 | pub mod irqs { |
| 291 | use embassy_cortex_m::interrupt::_export::declare; | 296 | use embassy_cortex_m::interrupt::_export::declare; |
| 292 | 297 | ||
| @@ -327,10 +332,10 @@ pub mod irqs { | |||
| 327 | declare!(PWM2); | 332 | declare!(PWM2); |
| 328 | declare!(SPIM2_SPIS2_SPI2); | 333 | declare!(SPIM2_SPIS2_SPI2); |
| 329 | declare!(RTC2); | 334 | declare!(RTC2); |
| 330 | declare!(I2S); | ||
| 331 | declare!(FPU); | 335 | declare!(FPU); |
| 332 | declare!(USBD); | 336 | declare!(USBD); |
| 333 | declare!(UARTE1); | 337 | declare!(UARTE1); |
| 334 | declare!(PWM3); | 338 | declare!(PWM3); |
| 335 | declare!(SPIM3); | 339 | declare!(SPIM3); |
| 340 | declare!(I2S); | ||
| 336 | } | 341 | } |
diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index f9e271c97..4f7463be2 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs | |||
| @@ -164,6 +164,9 @@ embassy_hal_common::peripherals! { | |||
| 164 | 164 | ||
| 165 | // PDM | 165 | // PDM |
| 166 | PDM, | 166 | PDM, |
| 167 | |||
| 168 | // I2S | ||
| 169 | I2S, | ||
| 167 | } | 170 | } |
| 168 | 171 | ||
| 169 | #[cfg(feature = "nightly")] | 172 | #[cfg(feature = "nightly")] |
| @@ -292,6 +295,8 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); | |||
| 292 | impl_saadc_input!(P0_30, ANALOG_INPUT6); | 295 | impl_saadc_input!(P0_30, ANALOG_INPUT6); |
| 293 | impl_saadc_input!(P0_31, ANALOG_INPUT7); | 296 | impl_saadc_input!(P0_31, ANALOG_INPUT7); |
| 294 | 297 | ||
| 298 | impl_i2s!(I2S, I2S, I2S); | ||
| 299 | |||
| 295 | pub mod irqs { | 300 | pub mod irqs { |
| 296 | use embassy_cortex_m::interrupt::_export::declare; | 301 | use embassy_cortex_m::interrupt::_export::declare; |
| 297 | 302 | ||
| @@ -332,7 +337,6 @@ pub mod irqs { | |||
| 332 | declare!(PWM2); | 337 | declare!(PWM2); |
| 333 | declare!(SPIM2_SPIS2_SPI2); | 338 | declare!(SPIM2_SPIS2_SPI2); |
| 334 | declare!(RTC2); | 339 | declare!(RTC2); |
| 335 | declare!(I2S); | ||
| 336 | declare!(FPU); | 340 | declare!(FPU); |
| 337 | declare!(USBD); | 341 | declare!(USBD); |
| 338 | declare!(UARTE1); | 342 | declare!(UARTE1); |
| @@ -340,4 +344,5 @@ pub mod irqs { | |||
| 340 | declare!(CRYPTOCELL); | 344 | declare!(CRYPTOCELL); |
| 341 | declare!(PWM3); | 345 | declare!(PWM3); |
| 342 | declare!(SPIM3); | 346 | declare!(SPIM3); |
| 347 | declare!(I2S); | ||
| 343 | } | 348 | } |
diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs new file mode 100644 index 000000000..7e9507751 --- /dev/null +++ b/embassy-nrf/src/i2s.rs | |||
| @@ -0,0 +1,1141 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 3 | //! Support for I2S audio | ||
| 4 | |||
| 5 | use core::future::poll_fn; | ||
| 6 | use core::marker::PhantomData; | ||
| 7 | use core::mem::size_of; | ||
| 8 | use core::ops::{Deref, DerefMut}; | ||
| 9 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 10 | use core::task::Poll; | ||
| 11 | |||
| 12 | use embassy_cortex_m::interrupt::InterruptExt; | ||
| 13 | use embassy_hal_common::drop::OnDrop; | ||
| 14 | use embassy_hal_common::{into_ref, PeripheralRef}; | ||
| 15 | |||
| 16 | use crate::gpio::{AnyPin, Pin as GpioPin}; | ||
| 17 | use crate::interrupt::Interrupt; | ||
| 18 | use crate::pac::i2s::RegisterBlock; | ||
| 19 | use crate::util::{slice_in_ram_or, slice_ptr_parts}; | ||
| 20 | use crate::{Peripheral, EASY_DMA_SIZE}; | ||
| 21 | |||
| 22 | pub type DoubleBuffering<S, const NS: usize> = MultiBuffering<S, 2, NS>; | ||
| 23 | |||
| 24 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 25 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 26 | #[non_exhaustive] | ||
| 27 | pub enum Error { | ||
| 28 | BufferTooLong, | ||
| 29 | BufferZeroLength, | ||
| 30 | BufferNotInDataMemory, | ||
| 31 | BufferMisaligned, | ||
| 32 | BufferLengthMisaligned, | ||
| 33 | } | ||
| 34 | |||
| 35 | /// I2S configuration. | ||
| 36 | #[derive(Clone)] | ||
| 37 | #[non_exhaustive] | ||
| 38 | pub struct Config { | ||
| 39 | pub sample_width: SampleWidth, | ||
| 40 | pub align: Align, | ||
| 41 | pub format: Format, | ||
| 42 | pub channels: Channels, | ||
| 43 | } | ||
| 44 | |||
| 45 | impl Config { | ||
| 46 | pub fn sample_width(mut self, sample_width: SampleWidth) -> Self { | ||
| 47 | self.sample_width = sample_width; | ||
| 48 | self | ||
| 49 | } | ||
| 50 | |||
| 51 | pub fn align(mut self, align: Align) -> Self { | ||
| 52 | self.align = align; | ||
| 53 | self | ||
| 54 | } | ||
| 55 | |||
| 56 | pub fn format(mut self, format: Format) -> Self { | ||
| 57 | self.format = format; | ||
| 58 | self | ||
| 59 | } | ||
| 60 | |||
| 61 | pub fn channels(mut self, channels: Channels) -> Self { | ||
| 62 | self.channels = channels; | ||
| 63 | self | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | impl Default for Config { | ||
| 68 | fn default() -> Self { | ||
| 69 | Self { | ||
| 70 | sample_width: SampleWidth::_16bit, | ||
| 71 | align: Align::Left, | ||
| 72 | format: Format::I2S, | ||
| 73 | channels: Channels::Stereo, | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | /// I2S Mode | ||
| 79 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 80 | pub struct MasterClock { | ||
| 81 | freq: MckFreq, | ||
| 82 | ratio: Ratio, | ||
| 83 | } | ||
| 84 | |||
| 85 | impl MasterClock { | ||
| 86 | pub fn new(freq: MckFreq, ratio: Ratio) -> Self { | ||
| 87 | Self { freq, ratio } | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | impl MasterClock { | ||
| 92 | pub fn sample_rate(&self) -> u32 { | ||
| 93 | self.freq.to_frequency() / self.ratio.to_divisor() | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | /// Master clock generator frequency. | ||
| 98 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 99 | pub enum MckFreq { | ||
| 100 | _32MDiv8, | ||
| 101 | _32MDiv10, | ||
| 102 | _32MDiv11, | ||
| 103 | _32MDiv15, | ||
| 104 | _32MDiv16, | ||
| 105 | _32MDiv21, | ||
| 106 | _32MDiv23, | ||
| 107 | _32MDiv30, | ||
| 108 | _32MDiv31, | ||
| 109 | _32MDiv32, | ||
| 110 | _32MDiv42, | ||
| 111 | _32MDiv63, | ||
| 112 | _32MDiv125, | ||
| 113 | } | ||
| 114 | |||
| 115 | impl MckFreq { | ||
| 116 | const REGISTER_VALUES: &'static [u32] = &[ | ||
| 117 | 0x20000000, 0x18000000, 0x16000000, 0x11000000, 0x10000000, 0x0C000000, 0x0B000000, 0x08800000, 0x08400000, | ||
| 118 | 0x08000000, 0x06000000, 0x04100000, 0x020C0000, | ||
| 119 | ]; | ||
| 120 | |||
| 121 | const FREQUENCIES: &'static [u32] = &[ | ||
| 122 | 4000000, 3200000, 2909090, 2133333, 2000000, 1523809, 1391304, 1066666, 1032258, 1000000, 761904, 507936, | ||
| 123 | 256000, | ||
| 124 | ]; | ||
| 125 | |||
| 126 | /// Return the value that needs to be written to the register. | ||
| 127 | pub fn to_register_value(&self) -> u32 { | ||
| 128 | Self::REGISTER_VALUES[usize::from(*self)] | ||
| 129 | } | ||
| 130 | |||
| 131 | /// Return the master clock frequency. | ||
| 132 | pub fn to_frequency(&self) -> u32 { | ||
| 133 | Self::FREQUENCIES[usize::from(*self)] | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | impl From<MckFreq> for usize { | ||
| 138 | fn from(variant: MckFreq) -> Self { | ||
| 139 | variant as _ | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | /// Master clock frequency ratio | ||
| 144 | /// | ||
| 145 | /// Sample Rate = LRCK = MCK / Ratio | ||
| 146 | /// | ||
| 147 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 148 | pub enum Ratio { | ||
| 149 | _32x, | ||
| 150 | _48x, | ||
| 151 | _64x, | ||
| 152 | _96x, | ||
| 153 | _128x, | ||
| 154 | _192x, | ||
| 155 | _256x, | ||
| 156 | _384x, | ||
| 157 | _512x, | ||
| 158 | } | ||
| 159 | |||
| 160 | impl Ratio { | ||
| 161 | const RATIOS: &'static [u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512]; | ||
| 162 | |||
| 163 | /// Return the value that needs to be written to the register. | ||
| 164 | pub fn to_register_value(&self) -> u8 { | ||
| 165 | usize::from(*self) as u8 | ||
| 166 | } | ||
| 167 | |||
| 168 | pub fn to_divisor(&self) -> u32 { | ||
| 169 | Self::RATIOS[usize::from(*self)] | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | impl From<Ratio> for usize { | ||
| 174 | fn from(variant: Ratio) -> Self { | ||
| 175 | variant as _ | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | /// Approximate sample rates. | ||
| 180 | /// | ||
| 181 | /// Those are common sample rates that can not be configured without an small error. | ||
| 182 | /// | ||
| 183 | /// For custom master clock configuration, please refer to [MasterClock]. | ||
| 184 | #[derive(Clone, Copy)] | ||
| 185 | pub enum ApproxSampleRate { | ||
| 186 | _11025, | ||
| 187 | _16000, | ||
| 188 | _22050, | ||
| 189 | _32000, | ||
| 190 | _44100, | ||
| 191 | _48000, | ||
| 192 | } | ||
| 193 | |||
| 194 | impl From<ApproxSampleRate> for MasterClock { | ||
| 195 | fn from(value: ApproxSampleRate) -> Self { | ||
| 196 | match value { | ||
| 197 | // error = 86 | ||
| 198 | ApproxSampleRate::_11025 => MasterClock::new(MckFreq::_32MDiv15, Ratio::_192x), | ||
| 199 | // error = 127 | ||
| 200 | ApproxSampleRate::_16000 => MasterClock::new(MckFreq::_32MDiv21, Ratio::_96x), | ||
| 201 | // error = 172 | ||
| 202 | ApproxSampleRate::_22050 => MasterClock::new(MckFreq::_32MDiv15, Ratio::_96x), | ||
| 203 | // error = 254 | ||
| 204 | ApproxSampleRate::_32000 => MasterClock::new(MckFreq::_32MDiv21, Ratio::_48x), | ||
| 205 | // error = 344 | ||
| 206 | ApproxSampleRate::_44100 => MasterClock::new(MckFreq::_32MDiv15, Ratio::_48x), | ||
| 207 | // error = 381 | ||
| 208 | ApproxSampleRate::_48000 => MasterClock::new(MckFreq::_32MDiv21, Ratio::_32x), | ||
| 209 | } | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | impl ApproxSampleRate { | ||
| 214 | pub fn sample_rate(&self) -> u32 { | ||
| 215 | MasterClock::from(*self).sample_rate() | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | /// Exact sample rates. | ||
| 220 | /// | ||
| 221 | /// Those are non standard sample rates that can be configured without error. | ||
| 222 | /// | ||
| 223 | /// For custom master clock configuration, please refer to [Mode]. | ||
| 224 | #[derive(Clone, Copy)] | ||
| 225 | pub enum ExactSampleRate { | ||
| 226 | _8000, | ||
| 227 | _10582, | ||
| 228 | _12500, | ||
| 229 | _15625, | ||
| 230 | _15873, | ||
| 231 | _25000, | ||
| 232 | _31250, | ||
| 233 | _50000, | ||
| 234 | _62500, | ||
| 235 | _100000, | ||
| 236 | _125000, | ||
| 237 | } | ||
| 238 | |||
| 239 | impl ExactSampleRate { | ||
| 240 | pub fn sample_rate(&self) -> u32 { | ||
| 241 | MasterClock::from(*self).sample_rate() | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | impl From<ExactSampleRate> for MasterClock { | ||
| 246 | fn from(value: ExactSampleRate) -> Self { | ||
| 247 | match value { | ||
| 248 | ExactSampleRate::_8000 => MasterClock::new(MckFreq::_32MDiv125, Ratio::_32x), | ||
| 249 | ExactSampleRate::_10582 => MasterClock::new(MckFreq::_32MDiv63, Ratio::_48x), | ||
| 250 | ExactSampleRate::_12500 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_256x), | ||
| 251 | ExactSampleRate::_15625 => MasterClock::new(MckFreq::_32MDiv32, Ratio::_64x), | ||
| 252 | ExactSampleRate::_15873 => MasterClock::new(MckFreq::_32MDiv63, Ratio::_32x), | ||
| 253 | ExactSampleRate::_25000 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_128x), | ||
| 254 | ExactSampleRate::_31250 => MasterClock::new(MckFreq::_32MDiv32, Ratio::_32x), | ||
| 255 | ExactSampleRate::_50000 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_64x), | ||
| 256 | ExactSampleRate::_62500 => MasterClock::new(MckFreq::_32MDiv16, Ratio::_32x), | ||
| 257 | ExactSampleRate::_100000 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_32x), | ||
| 258 | ExactSampleRate::_125000 => MasterClock::new(MckFreq::_32MDiv8, Ratio::_32x), | ||
| 259 | } | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | /// Sample width. | ||
| 264 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 265 | pub enum SampleWidth { | ||
| 266 | _8bit, | ||
| 267 | _16bit, | ||
| 268 | _24bit, | ||
| 269 | } | ||
| 270 | |||
| 271 | impl From<SampleWidth> for u8 { | ||
| 272 | fn from(variant: SampleWidth) -> Self { | ||
| 273 | variant as _ | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | /// Channel used for the most significant sample value in a frame. | ||
| 278 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 279 | pub enum Align { | ||
| 280 | Left, | ||
| 281 | Right, | ||
| 282 | } | ||
| 283 | |||
| 284 | impl From<Align> for bool { | ||
| 285 | fn from(variant: Align) -> Self { | ||
| 286 | match variant { | ||
| 287 | Align::Left => false, | ||
| 288 | Align::Right => true, | ||
| 289 | } | ||
| 290 | } | ||
| 291 | } | ||
| 292 | |||
| 293 | /// Frame format. | ||
| 294 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 295 | pub enum Format { | ||
| 296 | I2S, | ||
| 297 | Aligned, | ||
| 298 | } | ||
| 299 | |||
| 300 | impl From<Format> for bool { | ||
| 301 | fn from(variant: Format) -> Self { | ||
| 302 | match variant { | ||
| 303 | Format::I2S => false, | ||
| 304 | Format::Aligned => true, | ||
| 305 | } | ||
| 306 | } | ||
| 307 | } | ||
| 308 | |||
| 309 | /// Channels | ||
| 310 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 311 | pub enum Channels { | ||
| 312 | Stereo, | ||
| 313 | MonoLeft, | ||
| 314 | MonoRight, | ||
| 315 | } | ||
| 316 | |||
| 317 | impl From<Channels> for u8 { | ||
| 318 | fn from(variant: Channels) -> Self { | ||
| 319 | variant as _ | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | /// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload. | ||
| 324 | pub struct I2S<'d, T: Instance> { | ||
| 325 | i2s: PeripheralRef<'d, T>, | ||
| 326 | irq: PeripheralRef<'d, T::Interrupt>, | ||
| 327 | mck: Option<PeripheralRef<'d, AnyPin>>, | ||
| 328 | sck: PeripheralRef<'d, AnyPin>, | ||
| 329 | lrck: PeripheralRef<'d, AnyPin>, | ||
| 330 | sdin: Option<PeripheralRef<'d, AnyPin>>, | ||
| 331 | sdout: Option<PeripheralRef<'d, AnyPin>>, | ||
| 332 | master_clock: Option<MasterClock>, | ||
| 333 | config: Config, | ||
| 334 | } | ||
| 335 | |||
| 336 | impl<'d, T: Instance> I2S<'d, T> { | ||
| 337 | /// Create a new I2S in master mode | ||
| 338 | pub fn master( | ||
| 339 | i2s: impl Peripheral<P = T> + 'd, | ||
| 340 | irq: impl Peripheral<P = T::Interrupt> + 'd, | ||
| 341 | mck: impl Peripheral<P = impl GpioPin> + 'd, | ||
| 342 | sck: impl Peripheral<P = impl GpioPin> + 'd, | ||
| 343 | lrck: impl Peripheral<P = impl GpioPin> + 'd, | ||
| 344 | master_clock: MasterClock, | ||
| 345 | config: Config, | ||
| 346 | ) -> Self { | ||
| 347 | into_ref!(i2s, irq, mck, sck, lrck); | ||
| 348 | Self { | ||
| 349 | i2s, | ||
| 350 | irq, | ||
| 351 | mck: Some(mck.map_into()), | ||
| 352 | sck: sck.map_into(), | ||
| 353 | lrck: lrck.map_into(), | ||
| 354 | sdin: None, | ||
| 355 | sdout: None, | ||
| 356 | master_clock: Some(master_clock), | ||
| 357 | config, | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | /// Create a new I2S in slave mode | ||
| 362 | pub fn slave( | ||
| 363 | i2s: impl Peripheral<P = T> + 'd, | ||
| 364 | irq: impl Peripheral<P = T::Interrupt> + 'd, | ||
| 365 | sck: impl Peripheral<P = impl GpioPin> + 'd, | ||
| 366 | lrck: impl Peripheral<P = impl GpioPin> + 'd, | ||
| 367 | config: Config, | ||
| 368 | ) -> Self { | ||
| 369 | into_ref!(i2s, irq, sck, lrck); | ||
| 370 | Self { | ||
| 371 | i2s, | ||
| 372 | irq, | ||
| 373 | mck: None, | ||
| 374 | sck: sck.map_into(), | ||
| 375 | lrck: lrck.map_into(), | ||
| 376 | sdin: None, | ||
| 377 | sdout: None, | ||
| 378 | master_clock: None, | ||
| 379 | config, | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 383 | /// I2S output only | ||
| 384 | pub fn output<S: Sample, const NB: usize, const NS: usize>( | ||
| 385 | mut self, | ||
| 386 | sdout: impl Peripheral<P = impl GpioPin> + 'd, | ||
| 387 | buffers: MultiBuffering<S, NB, NS>, | ||
| 388 | ) -> OutputStream<'d, T, S, NB, NS> { | ||
| 389 | self.sdout = Some(sdout.into_ref().map_into()); | ||
| 390 | OutputStream { | ||
| 391 | _p: self.build(), | ||
| 392 | buffers, | ||
| 393 | } | ||
| 394 | } | ||
| 395 | |||
| 396 | /// I2S input only | ||
| 397 | pub fn input<S: Sample, const NB: usize, const NS: usize>( | ||
| 398 | mut self, | ||
| 399 | sdin: impl Peripheral<P = impl GpioPin> + 'd, | ||
| 400 | buffers: MultiBuffering<S, NB, NS>, | ||
| 401 | ) -> InputStream<'d, T, S, NB, NS> { | ||
| 402 | self.sdin = Some(sdin.into_ref().map_into()); | ||
| 403 | InputStream { | ||
| 404 | _p: self.build(), | ||
| 405 | buffers, | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | /// I2S full duplex (input and output) | ||
| 410 | pub fn full_duplex<S: Sample, const NB: usize, const NS: usize>( | ||
| 411 | mut self, | ||
| 412 | sdin: impl Peripheral<P = impl GpioPin> + 'd, | ||
| 413 | sdout: impl Peripheral<P = impl GpioPin> + 'd, | ||
| 414 | buffers_out: MultiBuffering<S, NB, NS>, | ||
| 415 | buffers_in: MultiBuffering<S, NB, NS>, | ||
| 416 | ) -> FullDuplexStream<'d, T, S, NB, NS> { | ||
| 417 | self.sdout = Some(sdout.into_ref().map_into()); | ||
| 418 | self.sdin = Some(sdin.into_ref().map_into()); | ||
| 419 | |||
| 420 | FullDuplexStream { | ||
| 421 | _p: self.build(), | ||
| 422 | buffers_out, | ||
| 423 | buffers_in, | ||
| 424 | } | ||
| 425 | } | ||
| 426 | |||
| 427 | fn build(self) -> PeripheralRef<'d, T> { | ||
| 428 | self.apply_config(); | ||
| 429 | self.select_pins(); | ||
| 430 | self.setup_interrupt(); | ||
| 431 | |||
| 432 | let device = Device::<T>::new(); | ||
| 433 | device.enable(); | ||
| 434 | |||
| 435 | self.i2s | ||
| 436 | } | ||
| 437 | |||
| 438 | fn apply_config(&self) { | ||
| 439 | let c = &T::regs().config; | ||
| 440 | match &self.master_clock { | ||
| 441 | Some(MasterClock { freq, ratio }) => { | ||
| 442 | c.mode.write(|w| w.mode().master()); | ||
| 443 | c.mcken.write(|w| w.mcken().enabled()); | ||
| 444 | c.mckfreq | ||
| 445 | .write(|w| unsafe { w.mckfreq().bits(freq.to_register_value()) }); | ||
| 446 | c.ratio.write(|w| unsafe { w.ratio().bits(ratio.to_register_value()) }); | ||
| 447 | } | ||
| 448 | None => { | ||
| 449 | c.mode.write(|w| w.mode().slave()); | ||
| 450 | } | ||
| 451 | }; | ||
| 452 | |||
| 453 | c.swidth | ||
| 454 | .write(|w| unsafe { w.swidth().bits(self.config.sample_width.into()) }); | ||
| 455 | c.align.write(|w| w.align().bit(self.config.align.into())); | ||
| 456 | c.format.write(|w| w.format().bit(self.config.format.into())); | ||
| 457 | c.channels | ||
| 458 | .write(|w| unsafe { w.channels().bits(self.config.channels.into()) }); | ||
| 459 | } | ||
| 460 | |||
| 461 | fn select_pins(&self) { | ||
| 462 | let psel = &T::regs().psel; | ||
| 463 | |||
| 464 | if let Some(mck) = &self.mck { | ||
| 465 | psel.mck.write(|w| { | ||
| 466 | unsafe { w.bits(mck.psel_bits()) }; | ||
| 467 | w.connect().connected() | ||
| 468 | }); | ||
| 469 | } | ||
| 470 | |||
| 471 | psel.sck.write(|w| { | ||
| 472 | unsafe { w.bits(self.sck.psel_bits()) }; | ||
| 473 | w.connect().connected() | ||
| 474 | }); | ||
| 475 | |||
| 476 | psel.lrck.write(|w| { | ||
| 477 | unsafe { w.bits(self.lrck.psel_bits()) }; | ||
| 478 | w.connect().connected() | ||
| 479 | }); | ||
| 480 | |||
| 481 | if let Some(sdin) = &self.sdin { | ||
| 482 | psel.sdin.write(|w| { | ||
| 483 | unsafe { w.bits(sdin.psel_bits()) }; | ||
| 484 | w.connect().connected() | ||
| 485 | }); | ||
| 486 | } | ||
| 487 | |||
| 488 | if let Some(sdout) = &self.sdout { | ||
| 489 | psel.sdout.write(|w| { | ||
| 490 | unsafe { w.bits(sdout.psel_bits()) }; | ||
| 491 | w.connect().connected() | ||
| 492 | }); | ||
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 496 | fn setup_interrupt(&self) { | ||
| 497 | self.irq.set_handler(Self::on_interrupt); | ||
| 498 | self.irq.unpend(); | ||
| 499 | self.irq.enable(); | ||
| 500 | |||
| 501 | let device = Device::<T>::new(); | ||
| 502 | device.disable_tx_ptr_interrupt(); | ||
| 503 | device.disable_rx_ptr_interrupt(); | ||
| 504 | device.disable_stopped_interrupt(); | ||
| 505 | |||
| 506 | device.reset_tx_ptr_event(); | ||
| 507 | device.reset_rx_ptr_event(); | ||
| 508 | device.reset_stopped_event(); | ||
| 509 | |||
| 510 | device.enable_tx_ptr_interrupt(); | ||
| 511 | device.enable_rx_ptr_interrupt(); | ||
| 512 | device.enable_stopped_interrupt(); | ||
| 513 | } | ||
| 514 | |||
| 515 | fn on_interrupt(_: *mut ()) { | ||
| 516 | let device = Device::<T>::new(); | ||
| 517 | let s = T::state(); | ||
| 518 | |||
| 519 | if device.is_tx_ptr_updated() { | ||
| 520 | trace!("TX INT"); | ||
| 521 | s.tx_waker.wake(); | ||
| 522 | device.disable_tx_ptr_interrupt(); | ||
| 523 | } | ||
| 524 | |||
| 525 | if device.is_rx_ptr_updated() { | ||
| 526 | trace!("RX INT"); | ||
| 527 | s.rx_waker.wake(); | ||
| 528 | device.disable_rx_ptr_interrupt(); | ||
| 529 | } | ||
| 530 | |||
| 531 | if device.is_stopped() { | ||
| 532 | trace!("STOPPED INT"); | ||
| 533 | s.stop_waker.wake(); | ||
| 534 | device.disable_stopped_interrupt(); | ||
| 535 | } | ||
| 536 | } | ||
| 537 | |||
| 538 | async fn stop() { | ||
| 539 | compiler_fence(Ordering::SeqCst); | ||
| 540 | |||
| 541 | let device = Device::<T>::new(); | ||
| 542 | device.stop(); | ||
| 543 | |||
| 544 | T::state().started.store(false, Ordering::Relaxed); | ||
| 545 | |||
| 546 | poll_fn(|cx| { | ||
| 547 | T::state().stop_waker.register(cx.waker()); | ||
| 548 | |||
| 549 | if device.is_stopped() { | ||
| 550 | trace!("STOP: Ready"); | ||
| 551 | device.reset_stopped_event(); | ||
| 552 | Poll::Ready(()) | ||
| 553 | } else { | ||
| 554 | trace!("STOP: Pending"); | ||
| 555 | Poll::Pending | ||
| 556 | } | ||
| 557 | }) | ||
| 558 | .await; | ||
| 559 | |||
| 560 | device.disable(); | ||
| 561 | } | ||
| 562 | |||
| 563 | async fn send_from_ram<S>(buffer_ptr: *const [S]) -> Result<(), Error> | ||
| 564 | where | ||
| 565 | S: Sample, | ||
| 566 | { | ||
| 567 | trace!("SEND: {}", buffer_ptr as *const S as u32); | ||
| 568 | |||
| 569 | slice_in_ram_or(buffer_ptr, Error::BufferNotInDataMemory)?; | ||
| 570 | |||
| 571 | compiler_fence(Ordering::SeqCst); | ||
| 572 | |||
| 573 | let device = Device::<T>::new(); | ||
| 574 | |||
| 575 | device.update_tx(buffer_ptr)?; | ||
| 576 | |||
| 577 | Self::wait_tx_ptr_update().await; | ||
| 578 | |||
| 579 | compiler_fence(Ordering::SeqCst); | ||
| 580 | |||
| 581 | Ok(()) | ||
| 582 | } | ||
| 583 | |||
| 584 | async fn wait_tx_ptr_update() { | ||
| 585 | let drop = OnDrop::new(move || { | ||
| 586 | trace!("TX DROP: Stopping"); | ||
| 587 | |||
| 588 | let device = Device::<T>::new(); | ||
| 589 | device.disable_tx_ptr_interrupt(); | ||
| 590 | device.reset_tx_ptr_event(); | ||
| 591 | device.disable_tx(); | ||
| 592 | |||
| 593 | // TX is stopped almost instantly, spinning is fine. | ||
| 594 | while !device.is_tx_ptr_updated() {} | ||
| 595 | |||
| 596 | trace!("TX DROP: Stopped"); | ||
| 597 | }); | ||
| 598 | |||
| 599 | poll_fn(|cx| { | ||
| 600 | T::state().tx_waker.register(cx.waker()); | ||
| 601 | |||
| 602 | let device = Device::<T>::new(); | ||
| 603 | if device.is_tx_ptr_updated() { | ||
| 604 | trace!("TX POLL: Ready"); | ||
| 605 | device.reset_tx_ptr_event(); | ||
| 606 | device.enable_tx_ptr_interrupt(); | ||
| 607 | Poll::Ready(()) | ||
| 608 | } else { | ||
| 609 | trace!("TX POLL: Pending"); | ||
| 610 | Poll::Pending | ||
| 611 | } | ||
| 612 | }) | ||
| 613 | .await; | ||
| 614 | |||
| 615 | drop.defuse(); | ||
| 616 | } | ||
| 617 | |||
| 618 | async fn receive_from_ram<S>(buffer_ptr: *mut [S]) -> Result<(), Error> | ||
| 619 | where | ||
| 620 | S: Sample, | ||
| 621 | { | ||
| 622 | trace!("RECEIVE: {}", buffer_ptr as *const S as u32); | ||
| 623 | |||
| 624 | // NOTE: RAM slice check for rx is not necessary, as a mutable | ||
| 625 | // slice can only be built from data located in RAM. | ||
| 626 | |||
| 627 | compiler_fence(Ordering::SeqCst); | ||
| 628 | |||
| 629 | let device = Device::<T>::new(); | ||
| 630 | |||
| 631 | device.update_rx(buffer_ptr)?; | ||
| 632 | |||
| 633 | Self::wait_rx_ptr_update().await; | ||
| 634 | |||
| 635 | compiler_fence(Ordering::SeqCst); | ||
| 636 | |||
| 637 | Ok(()) | ||
| 638 | } | ||
| 639 | |||
| 640 | async fn wait_rx_ptr_update() { | ||
| 641 | let drop = OnDrop::new(move || { | ||
| 642 | trace!("RX DROP: Stopping"); | ||
| 643 | |||
| 644 | let device = Device::<T>::new(); | ||
| 645 | device.disable_rx_ptr_interrupt(); | ||
| 646 | device.reset_rx_ptr_event(); | ||
| 647 | device.disable_rx(); | ||
| 648 | |||
| 649 | // TX is stopped almost instantly, spinning is fine. | ||
| 650 | while !device.is_rx_ptr_updated() {} | ||
| 651 | |||
| 652 | trace!("RX DROP: Stopped"); | ||
| 653 | }); | ||
| 654 | |||
| 655 | poll_fn(|cx| { | ||
| 656 | T::state().rx_waker.register(cx.waker()); | ||
| 657 | |||
| 658 | let device = Device::<T>::new(); | ||
| 659 | if device.is_rx_ptr_updated() { | ||
| 660 | trace!("RX POLL: Ready"); | ||
| 661 | device.reset_rx_ptr_event(); | ||
| 662 | device.enable_rx_ptr_interrupt(); | ||
| 663 | Poll::Ready(()) | ||
| 664 | } else { | ||
| 665 | trace!("RX POLL: Pending"); | ||
| 666 | Poll::Pending | ||
| 667 | } | ||
| 668 | }) | ||
| 669 | .await; | ||
| 670 | |||
| 671 | drop.defuse(); | ||
| 672 | } | ||
| 673 | } | ||
| 674 | |||
| 675 | /// I2S output | ||
| 676 | pub struct OutputStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { | ||
| 677 | _p: PeripheralRef<'d, T>, | ||
| 678 | buffers: MultiBuffering<S, NB, NS>, | ||
| 679 | } | ||
| 680 | |||
| 681 | impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> OutputStream<'d, T, S, NB, NS> { | ||
| 682 | /// Get a mutable reference to the current buffer. | ||
| 683 | pub fn buffer(&mut self) -> &mut [S] { | ||
| 684 | self.buffers.get_mut() | ||
| 685 | } | ||
| 686 | |||
| 687 | /// Prepare the initial buffer and start the I2S transfer. | ||
| 688 | pub async fn start(&mut self) -> Result<(), Error> | ||
| 689 | where | ||
| 690 | S: Sample, | ||
| 691 | { | ||
| 692 | let device = Device::<T>::new(); | ||
| 693 | |||
| 694 | let s = T::state(); | ||
| 695 | if s.started.load(Ordering::Relaxed) { | ||
| 696 | self.stop().await; | ||
| 697 | } | ||
| 698 | |||
| 699 | device.enable(); | ||
| 700 | device.enable_tx(); | ||
| 701 | |||
| 702 | device.update_tx(self.buffers.switch())?; | ||
| 703 | |||
| 704 | s.started.store(true, Ordering::Relaxed); | ||
| 705 | |||
| 706 | device.start(); | ||
| 707 | |||
| 708 | I2S::<T>::wait_tx_ptr_update().await; | ||
| 709 | |||
| 710 | Ok(()) | ||
| 711 | } | ||
| 712 | |||
| 713 | /// Stops the I2S transfer and waits until it has stopped. | ||
| 714 | #[inline(always)] | ||
| 715 | pub async fn stop(&self) { | ||
| 716 | I2S::<T>::stop().await | ||
| 717 | } | ||
| 718 | |||
| 719 | /// Sends the current buffer for transmission in the DMA. | ||
| 720 | /// Switches to use the next available buffer. | ||
| 721 | pub async fn send(&mut self) -> Result<(), Error> | ||
| 722 | where | ||
| 723 | S: Sample, | ||
| 724 | { | ||
| 725 | I2S::<T>::send_from_ram(self.buffers.switch()).await | ||
| 726 | } | ||
| 727 | } | ||
| 728 | |||
| 729 | /// I2S input | ||
| 730 | pub struct InputStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { | ||
| 731 | _p: PeripheralRef<'d, T>, | ||
| 732 | buffers: MultiBuffering<S, NB, NS>, | ||
| 733 | } | ||
| 734 | |||
| 735 | impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> InputStream<'d, T, S, NB, NS> { | ||
| 736 | /// Get a mutable reference to the current buffer. | ||
| 737 | pub fn buffer(&mut self) -> &mut [S] { | ||
| 738 | self.buffers.get_mut() | ||
| 739 | } | ||
| 740 | |||
| 741 | /// Prepare the initial buffer and start the I2S transfer. | ||
| 742 | pub async fn start(&mut self) -> Result<(), Error> | ||
| 743 | where | ||
| 744 | S: Sample, | ||
| 745 | { | ||
| 746 | let device = Device::<T>::new(); | ||
| 747 | |||
| 748 | let s = T::state(); | ||
| 749 | if s.started.load(Ordering::Relaxed) { | ||
| 750 | self.stop().await; | ||
| 751 | } | ||
| 752 | |||
| 753 | device.enable(); | ||
| 754 | device.enable_rx(); | ||
| 755 | |||
| 756 | device.update_rx(self.buffers.switch())?; | ||
| 757 | |||
| 758 | s.started.store(true, Ordering::Relaxed); | ||
| 759 | |||
| 760 | device.start(); | ||
| 761 | |||
| 762 | I2S::<T>::wait_rx_ptr_update().await; | ||
| 763 | |||
| 764 | Ok(()) | ||
| 765 | } | ||
| 766 | |||
| 767 | /// Stops the I2S transfer and waits until it has stopped. | ||
| 768 | #[inline(always)] | ||
| 769 | pub async fn stop(&self) { | ||
| 770 | I2S::<T>::stop().await | ||
| 771 | } | ||
| 772 | |||
| 773 | /// Sets the current buffer for reception from the DMA. | ||
| 774 | /// Switches to use the next available buffer. | ||
| 775 | #[allow(unused_mut)] | ||
| 776 | pub async fn receive(&mut self) -> Result<(), Error> | ||
| 777 | where | ||
| 778 | S: Sample, | ||
| 779 | { | ||
| 780 | I2S::<T>::receive_from_ram(self.buffers.switch_mut()).await | ||
| 781 | } | ||
| 782 | } | ||
| 783 | |||
| 784 | /// I2S full duplex stream (input & output) | ||
| 785 | pub struct FullDuplexStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { | ||
| 786 | _p: PeripheralRef<'d, T>, | ||
| 787 | buffers_out: MultiBuffering<S, NB, NS>, | ||
| 788 | buffers_in: MultiBuffering<S, NB, NS>, | ||
| 789 | } | ||
| 790 | |||
| 791 | impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> FullDuplexStream<'d, T, S, NB, NS> { | ||
| 792 | /// Get the current output and input buffers. | ||
| 793 | pub fn buffers(&mut self) -> (&mut [S], &[S]) { | ||
| 794 | (self.buffers_out.get_mut(), self.buffers_in.get()) | ||
| 795 | } | ||
| 796 | |||
| 797 | /// Prepare the initial buffers and start the I2S transfer. | ||
| 798 | pub async fn start(&mut self) -> Result<(), Error> | ||
| 799 | where | ||
| 800 | S: Sample, | ||
| 801 | { | ||
| 802 | let device = Device::<T>::new(); | ||
| 803 | |||
| 804 | let s = T::state(); | ||
| 805 | if s.started.load(Ordering::Relaxed) { | ||
| 806 | self.stop().await; | ||
| 807 | } | ||
| 808 | |||
| 809 | device.enable(); | ||
| 810 | device.enable_tx(); | ||
| 811 | device.enable_rx(); | ||
| 812 | |||
| 813 | device.update_tx(self.buffers_out.switch())?; | ||
| 814 | device.update_rx(self.buffers_in.switch_mut())?; | ||
| 815 | |||
| 816 | s.started.store(true, Ordering::Relaxed); | ||
| 817 | |||
| 818 | device.start(); | ||
| 819 | |||
| 820 | I2S::<T>::wait_tx_ptr_update().await; | ||
| 821 | I2S::<T>::wait_rx_ptr_update().await; | ||
| 822 | |||
| 823 | Ok(()) | ||
| 824 | } | ||
| 825 | |||
| 826 | /// Stops the I2S transfer and waits until it has stopped. | ||
| 827 | #[inline(always)] | ||
| 828 | pub async fn stop(&self) { | ||
| 829 | I2S::<T>::stop().await | ||
| 830 | } | ||
| 831 | |||
| 832 | /// Sets the current buffers for output and input for transmission/reception from the DMA. | ||
| 833 | /// Switch to use the next available buffers for output/input. | ||
| 834 | pub async fn send_and_receive(&mut self) -> Result<(), Error> | ||
| 835 | where | ||
| 836 | S: Sample, | ||
| 837 | { | ||
| 838 | I2S::<T>::send_from_ram(self.buffers_out.switch()).await?; | ||
| 839 | I2S::<T>::receive_from_ram(self.buffers_in.switch_mut()).await?; | ||
| 840 | Ok(()) | ||
| 841 | } | ||
| 842 | } | ||
| 843 | |||
| 844 | /// Helper encapsulating common I2S device operations. | ||
| 845 | struct Device<T>(&'static RegisterBlock, PhantomData<T>); | ||
| 846 | |||
| 847 | impl<T: Instance> Device<T> { | ||
| 848 | fn new() -> Self { | ||
| 849 | Self(T::regs(), PhantomData) | ||
| 850 | } | ||
| 851 | |||
| 852 | #[inline(always)] | ||
| 853 | pub fn enable(&self) { | ||
| 854 | trace!("ENABLED"); | ||
| 855 | self.0.enable.write(|w| w.enable().enabled()); | ||
| 856 | } | ||
| 857 | |||
| 858 | #[inline(always)] | ||
| 859 | pub fn disable(&self) { | ||
| 860 | trace!("DISABLED"); | ||
| 861 | self.0.enable.write(|w| w.enable().disabled()); | ||
| 862 | } | ||
| 863 | |||
| 864 | #[inline(always)] | ||
| 865 | fn enable_tx(&self) { | ||
| 866 | trace!("TX ENABLED"); | ||
| 867 | self.0.config.txen.write(|w| w.txen().enabled()); | ||
| 868 | } | ||
| 869 | |||
| 870 | #[inline(always)] | ||
| 871 | fn disable_tx(&self) { | ||
| 872 | trace!("TX DISABLED"); | ||
| 873 | self.0.config.txen.write(|w| w.txen().disabled()); | ||
| 874 | } | ||
| 875 | |||
| 876 | #[inline(always)] | ||
| 877 | fn enable_rx(&self) { | ||
| 878 | trace!("RX ENABLED"); | ||
| 879 | self.0.config.rxen.write(|w| w.rxen().enabled()); | ||
| 880 | } | ||
| 881 | |||
| 882 | #[inline(always)] | ||
| 883 | fn disable_rx(&self) { | ||
| 884 | trace!("RX DISABLED"); | ||
| 885 | self.0.config.rxen.write(|w| w.rxen().disabled()); | ||
| 886 | } | ||
| 887 | |||
| 888 | #[inline(always)] | ||
| 889 | fn start(&self) { | ||
| 890 | trace!("START"); | ||
| 891 | self.0.tasks_start.write(|w| unsafe { w.bits(1) }); | ||
| 892 | } | ||
| 893 | |||
| 894 | #[inline(always)] | ||
| 895 | fn stop(&self) { | ||
| 896 | self.0.tasks_stop.write(|w| unsafe { w.bits(1) }); | ||
| 897 | } | ||
| 898 | |||
| 899 | #[inline(always)] | ||
| 900 | fn is_stopped(&self) -> bool { | ||
| 901 | self.0.events_stopped.read().bits() != 0 | ||
| 902 | } | ||
| 903 | |||
| 904 | #[inline(always)] | ||
| 905 | fn reset_stopped_event(&self) { | ||
| 906 | trace!("STOPPED EVENT: Reset"); | ||
| 907 | self.0.events_stopped.reset(); | ||
| 908 | } | ||
| 909 | |||
| 910 | #[inline(always)] | ||
| 911 | fn disable_stopped_interrupt(&self) { | ||
| 912 | trace!("STOPPED INTERRUPT: Disabled"); | ||
| 913 | self.0.intenclr.write(|w| w.stopped().clear()); | ||
| 914 | } | ||
| 915 | |||
| 916 | #[inline(always)] | ||
| 917 | fn enable_stopped_interrupt(&self) { | ||
| 918 | trace!("STOPPED INTERRUPT: Enabled"); | ||
| 919 | self.0.intenset.write(|w| w.stopped().set()); | ||
| 920 | } | ||
| 921 | |||
| 922 | #[inline(always)] | ||
| 923 | fn reset_tx_ptr_event(&self) { | ||
| 924 | trace!("TX PTR EVENT: Reset"); | ||
| 925 | self.0.events_txptrupd.reset(); | ||
| 926 | } | ||
| 927 | |||
| 928 | #[inline(always)] | ||
| 929 | fn reset_rx_ptr_event(&self) { | ||
| 930 | trace!("RX PTR EVENT: Reset"); | ||
| 931 | self.0.events_rxptrupd.reset(); | ||
| 932 | } | ||
| 933 | |||
| 934 | #[inline(always)] | ||
| 935 | fn disable_tx_ptr_interrupt(&self) { | ||
| 936 | trace!("TX PTR INTERRUPT: Disabled"); | ||
| 937 | self.0.intenclr.write(|w| w.txptrupd().clear()); | ||
| 938 | } | ||
| 939 | |||
| 940 | #[inline(always)] | ||
| 941 | fn disable_rx_ptr_interrupt(&self) { | ||
| 942 | trace!("RX PTR INTERRUPT: Disabled"); | ||
| 943 | self.0.intenclr.write(|w| w.rxptrupd().clear()); | ||
| 944 | } | ||
| 945 | |||
| 946 | #[inline(always)] | ||
| 947 | fn enable_tx_ptr_interrupt(&self) { | ||
| 948 | trace!("TX PTR INTERRUPT: Enabled"); | ||
| 949 | self.0.intenset.write(|w| w.txptrupd().set()); | ||
| 950 | } | ||
| 951 | |||
| 952 | #[inline(always)] | ||
| 953 | fn enable_rx_ptr_interrupt(&self) { | ||
| 954 | trace!("RX PTR INTERRUPT: Enabled"); | ||
| 955 | self.0.intenset.write(|w| w.rxptrupd().set()); | ||
| 956 | } | ||
| 957 | |||
| 958 | #[inline(always)] | ||
| 959 | fn is_tx_ptr_updated(&self) -> bool { | ||
| 960 | self.0.events_txptrupd.read().bits() != 0 | ||
| 961 | } | ||
| 962 | |||
| 963 | #[inline(always)] | ||
| 964 | fn is_rx_ptr_updated(&self) -> bool { | ||
| 965 | self.0.events_rxptrupd.read().bits() != 0 | ||
| 966 | } | ||
| 967 | |||
| 968 | #[inline] | ||
| 969 | fn update_tx<S>(&self, buffer_ptr: *const [S]) -> Result<(), Error> { | ||
| 970 | let (ptr, maxcnt) = Self::validated_dma_parts(buffer_ptr)?; | ||
| 971 | self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); | ||
| 972 | self.0.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); | ||
| 973 | Ok(()) | ||
| 974 | } | ||
| 975 | |||
| 976 | #[inline] | ||
| 977 | fn update_rx<S>(&self, buffer_ptr: *const [S]) -> Result<(), Error> { | ||
| 978 | let (ptr, maxcnt) = Self::validated_dma_parts(buffer_ptr)?; | ||
| 979 | self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); | ||
| 980 | self.0.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); | ||
| 981 | Ok(()) | ||
| 982 | } | ||
| 983 | |||
| 984 | fn validated_dma_parts<S>(buffer_ptr: *const [S]) -> Result<(u32, u32), Error> { | ||
| 985 | let (ptr, len) = slice_ptr_parts(buffer_ptr); | ||
| 986 | let ptr = ptr as u32; | ||
| 987 | let bytes_len = len * size_of::<S>(); | ||
| 988 | let maxcnt = (bytes_len / size_of::<u32>()) as u32; | ||
| 989 | |||
| 990 | trace!("PTR={}, MAXCNT={}", ptr, maxcnt); | ||
| 991 | |||
| 992 | if ptr % 4 != 0 { | ||
| 993 | Err(Error::BufferMisaligned) | ||
| 994 | } else if bytes_len % 4 != 0 { | ||
| 995 | Err(Error::BufferLengthMisaligned) | ||
| 996 | } else if maxcnt as usize > EASY_DMA_SIZE { | ||
| 997 | Err(Error::BufferTooLong) | ||
| 998 | } else { | ||
| 999 | Ok((ptr, maxcnt)) | ||
| 1000 | } | ||
| 1001 | } | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | /// Sample details | ||
| 1005 | pub trait Sample: Sized + Copy + Default { | ||
| 1006 | const WIDTH: usize; | ||
| 1007 | const SCALE: Self; | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | impl Sample for i8 { | ||
| 1011 | const WIDTH: usize = 8; | ||
| 1012 | const SCALE: Self = 1 << (Self::WIDTH - 1); | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | impl Sample for i16 { | ||
| 1016 | const WIDTH: usize = 16; | ||
| 1017 | const SCALE: Self = 1 << (Self::WIDTH - 1); | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | impl Sample for i32 { | ||
| 1021 | const WIDTH: usize = 24; | ||
| 1022 | const SCALE: Self = 1 << (Self::WIDTH - 1); | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | /// A 4-bytes aligned buffer. | ||
| 1026 | #[derive(Clone, Copy)] | ||
| 1027 | #[repr(align(4))] | ||
| 1028 | pub struct AlignedBuffer<T: Sample, const N: usize>([T; N]); | ||
| 1029 | |||
| 1030 | impl<T: Sample, const N: usize> AlignedBuffer<T, N> { | ||
| 1031 | pub fn new(array: [T; N]) -> Self { | ||
| 1032 | Self(array) | ||
| 1033 | } | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | impl<T: Sample, const N: usize> Default for AlignedBuffer<T, N> { | ||
| 1037 | fn default() -> Self { | ||
| 1038 | Self([T::default(); N]) | ||
| 1039 | } | ||
| 1040 | } | ||
| 1041 | |||
| 1042 | impl<T: Sample, const N: usize> Deref for AlignedBuffer<T, N> { | ||
| 1043 | type Target = [T]; | ||
| 1044 | fn deref(&self) -> &Self::Target { | ||
| 1045 | self.0.as_slice() | ||
| 1046 | } | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | impl<T: Sample, const N: usize> DerefMut for AlignedBuffer<T, N> { | ||
| 1050 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 1051 | self.0.as_mut_slice() | ||
| 1052 | } | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | pub struct MultiBuffering<S: Sample, const NB: usize, const NS: usize> { | ||
| 1056 | buffers: [AlignedBuffer<S, NS>; NB], | ||
| 1057 | index: usize, | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | impl<S: Sample, const NB: usize, const NS: usize> MultiBuffering<S, NB, NS> { | ||
| 1061 | pub fn new() -> Self { | ||
| 1062 | assert!(NB > 1); | ||
| 1063 | Self { | ||
| 1064 | buffers: [AlignedBuffer::<S, NS>::default(); NB], | ||
| 1065 | index: 0, | ||
| 1066 | } | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | fn get(&self) -> &[S] { | ||
| 1070 | &self.buffers[self.index] | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | fn get_mut(&mut self) -> &mut [S] { | ||
| 1074 | &mut self.buffers[self.index] | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | /// Advance to use the next buffer and return a non mutable pointer to the previous one. | ||
| 1078 | fn switch(&mut self) -> *const [S] { | ||
| 1079 | let prev_index = self.index; | ||
| 1080 | self.index = (self.index + 1) % NB; | ||
| 1081 | self.buffers[prev_index].deref() as *const [S] | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | /// Advance to use the next buffer and return a mutable pointer to the previous one. | ||
| 1085 | fn switch_mut(&mut self) -> *mut [S] { | ||
| 1086 | let prev_index = self.index; | ||
| 1087 | self.index = (self.index + 1) % NB; | ||
| 1088 | self.buffers[prev_index].deref_mut() as *mut [S] | ||
| 1089 | } | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | pub(crate) mod sealed { | ||
| 1093 | use core::sync::atomic::AtomicBool; | ||
| 1094 | |||
| 1095 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 1096 | |||
| 1097 | /// Peripheral static state | ||
| 1098 | pub struct State { | ||
| 1099 | pub started: AtomicBool, | ||
| 1100 | pub rx_waker: AtomicWaker, | ||
| 1101 | pub tx_waker: AtomicWaker, | ||
| 1102 | pub stop_waker: AtomicWaker, | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | impl State { | ||
| 1106 | pub const fn new() -> Self { | ||
| 1107 | Self { | ||
| 1108 | started: AtomicBool::new(false), | ||
| 1109 | rx_waker: AtomicWaker::new(), | ||
| 1110 | tx_waker: AtomicWaker::new(), | ||
| 1111 | stop_waker: AtomicWaker::new(), | ||
| 1112 | } | ||
| 1113 | } | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | pub trait Instance { | ||
| 1117 | fn regs() -> &'static crate::pac::i2s::RegisterBlock; | ||
| 1118 | fn state() -> &'static State; | ||
| 1119 | } | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { | ||
| 1123 | type Interrupt: Interrupt; | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | macro_rules! impl_i2s { | ||
| 1127 | ($type:ident, $pac_type:ident, $irq:ident) => { | ||
| 1128 | impl crate::i2s::sealed::Instance for peripherals::$type { | ||
| 1129 | fn regs() -> &'static crate::pac::i2s::RegisterBlock { | ||
| 1130 | unsafe { &*pac::$pac_type::ptr() } | ||
| 1131 | } | ||
| 1132 | fn state() -> &'static crate::i2s::sealed::State { | ||
| 1133 | static STATE: crate::i2s::sealed::State = crate::i2s::sealed::State::new(); | ||
| 1134 | &STATE | ||
| 1135 | } | ||
| 1136 | } | ||
| 1137 | impl crate::i2s::Instance for peripherals::$type { | ||
| 1138 | type Interrupt = crate::interrupt::$irq; | ||
| 1139 | } | ||
| 1140 | }; | ||
| 1141 | } | ||
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 9054bc300..4832e143f 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs | |||
| @@ -78,6 +78,8 @@ pub mod buffered_uarte; | |||
| 78 | pub mod gpio; | 78 | pub mod gpio; |
| 79 | #[cfg(feature = "gpiote")] | 79 | #[cfg(feature = "gpiote")] |
| 80 | pub mod gpiote; | 80 | pub mod gpiote; |
| 81 | #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] | ||
| 82 | pub mod i2s; | ||
| 81 | #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] | 83 | #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] |
| 82 | pub mod nvmc; | 84 | pub mod nvmc; |
| 83 | #[cfg(any( | 85 | #[cfg(any( |
diff --git a/examples/nrf/src/bin/i2s_effect.rs b/examples/nrf/src/bin/i2s_effect.rs new file mode 100644 index 000000000..3cca005b1 --- /dev/null +++ b/examples/nrf/src/bin/i2s_effect.rs | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::f32::consts::PI; | ||
| 6 | |||
| 7 | use defmt::{error, info}; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_nrf::i2s::{self, Channels, Config, MasterClock, MultiBuffering, Sample as _, SampleWidth, I2S}; | ||
| 10 | use embassy_nrf::interrupt; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | type Sample = i16; | ||
| 14 | |||
| 15 | const NUM_BUFFERS: usize = 2; | ||
| 16 | const NUM_SAMPLES: usize = 4; | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) { | ||
| 20 | let p = embassy_nrf::init(Default::default()); | ||
| 21 | |||
| 22 | let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); | ||
| 23 | |||
| 24 | let sample_rate = master_clock.sample_rate(); | ||
| 25 | info!("Sample rate: {}", sample_rate); | ||
| 26 | |||
| 27 | let config = Config::default() | ||
| 28 | .sample_width(SampleWidth::_16bit) | ||
| 29 | .channels(Channels::MonoLeft); | ||
| 30 | |||
| 31 | let irq = interrupt::take!(I2S); | ||
| 32 | let buffers_out = MultiBuffering::<Sample, NUM_BUFFERS, NUM_SAMPLES>::new(); | ||
| 33 | let buffers_in = MultiBuffering::<Sample, NUM_BUFFERS, NUM_SAMPLES>::new(); | ||
| 34 | let mut full_duplex_stream = I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).full_duplex( | ||
| 35 | p.P0_29, | ||
| 36 | p.P0_28, | ||
| 37 | buffers_out, | ||
| 38 | buffers_in, | ||
| 39 | ); | ||
| 40 | |||
| 41 | let mut modulator = SineOsc::new(); | ||
| 42 | modulator.set_frequency(8.0, 1.0 / sample_rate as f32); | ||
| 43 | modulator.set_amplitude(1.0); | ||
| 44 | |||
| 45 | full_duplex_stream.start().await.expect("I2S Start"); | ||
| 46 | |||
| 47 | loop { | ||
| 48 | let (buff_out, buff_in) = full_duplex_stream.buffers(); | ||
| 49 | for i in 0..NUM_SAMPLES { | ||
| 50 | let modulation = (Sample::SCALE as f32 * bipolar_to_unipolar(modulator.generate())) as Sample; | ||
| 51 | buff_out[i] = buff_in[i] * modulation; | ||
| 52 | } | ||
| 53 | |||
| 54 | if let Err(err) = full_duplex_stream.send_and_receive().await { | ||
| 55 | error!("{}", err); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | struct SineOsc { | ||
| 61 | amplitude: f32, | ||
| 62 | modulo: f32, | ||
| 63 | phase_inc: f32, | ||
| 64 | } | ||
| 65 | |||
| 66 | impl SineOsc { | ||
| 67 | const B: f32 = 4.0 / PI; | ||
| 68 | const C: f32 = -4.0 / (PI * PI); | ||
| 69 | const P: f32 = 0.225; | ||
| 70 | |||
| 71 | pub fn new() -> Self { | ||
| 72 | Self { | ||
| 73 | amplitude: 1.0, | ||
| 74 | modulo: 0.0, | ||
| 75 | phase_inc: 0.0, | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | pub fn set_frequency(&mut self, freq: f32, inv_sample_rate: f32) { | ||
| 80 | self.phase_inc = freq * inv_sample_rate; | ||
| 81 | } | ||
| 82 | |||
| 83 | pub fn set_amplitude(&mut self, amplitude: f32) { | ||
| 84 | self.amplitude = amplitude; | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn generate(&mut self) -> f32 { | ||
| 88 | let signal = self.parabolic_sin(self.modulo); | ||
| 89 | self.modulo += self.phase_inc; | ||
| 90 | if self.modulo < 0.0 { | ||
| 91 | self.modulo += 1.0; | ||
| 92 | } else if self.modulo > 1.0 { | ||
| 93 | self.modulo -= 1.0; | ||
| 94 | } | ||
| 95 | signal * self.amplitude | ||
| 96 | } | ||
| 97 | |||
| 98 | fn parabolic_sin(&mut self, modulo: f32) -> f32 { | ||
| 99 | let angle = PI - modulo * 2.0 * PI; | ||
| 100 | let y = Self::B * angle + Self::C * angle * abs(angle); | ||
| 101 | Self::P * (y * abs(y) - y) + y | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | #[inline] | ||
| 106 | fn abs(value: f32) -> f32 { | ||
| 107 | if value < 0.0 { | ||
| 108 | -value | ||
| 109 | } else { | ||
| 110 | value | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | #[inline] | ||
| 115 | fn bipolar_to_unipolar(value: f32) -> f32 { | ||
| 116 | (value + 1.0) / 2.0 | ||
| 117 | } | ||
diff --git a/examples/nrf/src/bin/i2s_monitor.rs b/examples/nrf/src/bin/i2s_monitor.rs new file mode 100644 index 000000000..48eb7d581 --- /dev/null +++ b/examples/nrf/src/bin/i2s_monitor.rs | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{debug, error, info}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; | ||
| 8 | use embassy_nrf::interrupt; | ||
| 9 | use embassy_nrf::pwm::{Prescaler, SimplePwm}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | type Sample = i16; | ||
| 13 | |||
| 14 | const NUM_SAMPLES: usize = 500; | ||
| 15 | |||
| 16 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) { | ||
| 18 | let p = embassy_nrf::init(Default::default()); | ||
| 19 | |||
| 20 | let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); | ||
| 21 | |||
| 22 | let sample_rate = master_clock.sample_rate(); | ||
| 23 | info!("Sample rate: {}", sample_rate); | ||
| 24 | |||
| 25 | let config = Config::default() | ||
| 26 | .sample_width(SampleWidth::_16bit) | ||
| 27 | .channels(Channels::MonoLeft); | ||
| 28 | |||
| 29 | let irq = interrupt::take!(I2S); | ||
| 30 | let buffers = DoubleBuffering::<Sample, NUM_SAMPLES>::new(); | ||
| 31 | let mut input_stream = | ||
| 32 | I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).input(p.P0_29, buffers); | ||
| 33 | |||
| 34 | // Configure the PWM to use the pins corresponding to the RGB leds | ||
| 35 | let mut pwm = SimplePwm::new_3ch(p.PWM0, p.P0_23, p.P0_22, p.P0_24); | ||
| 36 | pwm.set_prescaler(Prescaler::Div1); | ||
| 37 | pwm.set_max_duty(255); | ||
| 38 | |||
| 39 | let mut rms_online = RmsOnline::<NUM_SAMPLES>::default(); | ||
| 40 | |||
| 41 | input_stream.start().await.expect("I2S Start"); | ||
| 42 | |||
| 43 | loop { | ||
| 44 | let rms = rms_online.process(input_stream.buffer()); | ||
| 45 | let rgb = rgb_from_rms(rms); | ||
| 46 | |||
| 47 | debug!("RMS: {}, RGB: {:?}", rms, rgb); | ||
| 48 | for i in 0..3 { | ||
| 49 | pwm.set_duty(i, rgb[i].into()); | ||
| 50 | } | ||
| 51 | |||
| 52 | if let Err(err) = input_stream.receive().await { | ||
| 53 | error!("{}", err); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /// RMS from 0.0 until 0.75 will give green with a proportional intensity | ||
| 59 | /// RMS from 0.75 until 0.9 will give a blend between orange and red proportionally to the intensity | ||
| 60 | /// RMS above 0.9 will give a red with a proportional intensity | ||
| 61 | fn rgb_from_rms(rms: f32) -> [u8; 3] { | ||
| 62 | if rms < 0.75 { | ||
| 63 | let intensity = rms / 0.75; | ||
| 64 | [0, (intensity * 165.0) as u8, 0] | ||
| 65 | } else if rms < 0.9 { | ||
| 66 | let intensity = (rms - 0.75) / 0.15; | ||
| 67 | [200, 165 - (165.0 * intensity) as u8, 0] | ||
| 68 | } else { | ||
| 69 | let intensity = (rms - 0.9) / 0.1; | ||
| 70 | [200 + (55.0 * intensity) as u8, 0, 0] | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | pub struct RmsOnline<const N: usize> { | ||
| 75 | pub squares: [f32; N], | ||
| 76 | pub head: usize, | ||
| 77 | } | ||
| 78 | |||
| 79 | impl<const N: usize> Default for RmsOnline<N> { | ||
| 80 | fn default() -> Self { | ||
| 81 | RmsOnline { | ||
| 82 | squares: [0.0; N], | ||
| 83 | head: 0, | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | impl<const N: usize> RmsOnline<N> { | ||
| 89 | pub fn reset(&mut self) { | ||
| 90 | self.squares = [0.0; N]; | ||
| 91 | self.head = 0; | ||
| 92 | } | ||
| 93 | |||
| 94 | pub fn process(&mut self, buf: &[Sample]) -> f32 { | ||
| 95 | buf.iter() | ||
| 96 | .for_each(|sample| self.push(*sample as f32 / Sample::SCALE as f32)); | ||
| 97 | |||
| 98 | let sum_of_squares = self.squares.iter().fold(0.0, |acc, v| acc + *v); | ||
| 99 | Self::approx_sqrt(sum_of_squares / N as f32) | ||
| 100 | } | ||
| 101 | |||
| 102 | pub fn push(&mut self, signal: f32) { | ||
| 103 | let square = signal * signal; | ||
| 104 | self.squares[self.head] = square; | ||
| 105 | self.head = (self.head + 1) % N; | ||
| 106 | } | ||
| 107 | |||
| 108 | /// Approximated sqrt taken from [micromath] | ||
| 109 | /// | ||
| 110 | /// [micromath]: https://docs.rs/micromath/latest/src/micromath/float/sqrt.rs.html#11-17 | ||
| 111 | /// | ||
| 112 | fn approx_sqrt(value: f32) -> f32 { | ||
| 113 | f32::from_bits((value.to_bits() + 0x3f80_0000) >> 1) | ||
| 114 | } | ||
| 115 | } | ||
diff --git a/examples/nrf/src/bin/i2s_waveform.rs b/examples/nrf/src/bin/i2s_waveform.rs new file mode 100644 index 000000000..1b0e8ebc8 --- /dev/null +++ b/examples/nrf/src/bin/i2s_waveform.rs | |||
| @@ -0,0 +1,151 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::f32::consts::PI; | ||
| 6 | |||
| 7 | use defmt::{error, info}; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; | ||
| 10 | use embassy_nrf::interrupt; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | type Sample = i16; | ||
| 14 | |||
| 15 | const NUM_SAMPLES: usize = 50; | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let p = embassy_nrf::init(Default::default()); | ||
| 20 | |||
| 21 | let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); | ||
| 22 | |||
| 23 | let sample_rate = master_clock.sample_rate(); | ||
| 24 | info!("Sample rate: {}", sample_rate); | ||
| 25 | |||
| 26 | let config = Config::default() | ||
| 27 | .sample_width(SampleWidth::_16bit) | ||
| 28 | .channels(Channels::MonoLeft); | ||
| 29 | |||
| 30 | let irq = interrupt::take!(I2S); | ||
| 31 | let buffers = DoubleBuffering::<Sample, NUM_SAMPLES>::new(); | ||
| 32 | let mut output_stream = | ||
| 33 | I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28, buffers); | ||
| 34 | |||
| 35 | let mut waveform = Waveform::new(1.0 / sample_rate as f32); | ||
| 36 | |||
| 37 | waveform.process(output_stream.buffer()); | ||
| 38 | |||
| 39 | output_stream.start().await.expect("I2S Start"); | ||
| 40 | |||
| 41 | loop { | ||
| 42 | waveform.process(output_stream.buffer()); | ||
| 43 | |||
| 44 | if let Err(err) = output_stream.send().await { | ||
| 45 | error!("{}", err); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | struct Waveform { | ||
| 51 | inv_sample_rate: f32, | ||
| 52 | carrier: SineOsc, | ||
| 53 | freq_mod: SineOsc, | ||
| 54 | amp_mod: SineOsc, | ||
| 55 | } | ||
| 56 | |||
| 57 | impl Waveform { | ||
| 58 | fn new(inv_sample_rate: f32) -> Self { | ||
| 59 | let mut carrier = SineOsc::new(); | ||
| 60 | carrier.set_frequency(110.0, inv_sample_rate); | ||
| 61 | |||
| 62 | let mut freq_mod = SineOsc::new(); | ||
| 63 | freq_mod.set_frequency(1.0, inv_sample_rate); | ||
| 64 | freq_mod.set_amplitude(1.0); | ||
| 65 | |||
| 66 | let mut amp_mod = SineOsc::new(); | ||
| 67 | amp_mod.set_frequency(16.0, inv_sample_rate); | ||
| 68 | amp_mod.set_amplitude(0.5); | ||
| 69 | |||
| 70 | Self { | ||
| 71 | inv_sample_rate, | ||
| 72 | carrier, | ||
| 73 | freq_mod, | ||
| 74 | amp_mod, | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | fn process(&mut self, buf: &mut [Sample]) { | ||
| 79 | for sample in buf.chunks_mut(1) { | ||
| 80 | let freq_modulation = bipolar_to_unipolar(self.freq_mod.generate()); | ||
| 81 | self.carrier | ||
| 82 | .set_frequency(110.0 + 440.0 * freq_modulation, self.inv_sample_rate); | ||
| 83 | |||
| 84 | let amp_modulation = bipolar_to_unipolar(self.amp_mod.generate()); | ||
| 85 | self.carrier.set_amplitude(amp_modulation); | ||
| 86 | |||
| 87 | let signal = self.carrier.generate(); | ||
| 88 | |||
| 89 | sample[0] = (Sample::SCALE as f32 * signal) as Sample; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | struct SineOsc { | ||
| 95 | amplitude: f32, | ||
| 96 | modulo: f32, | ||
| 97 | phase_inc: f32, | ||
| 98 | } | ||
| 99 | |||
| 100 | impl SineOsc { | ||
| 101 | const B: f32 = 4.0 / PI; | ||
| 102 | const C: f32 = -4.0 / (PI * PI); | ||
| 103 | const P: f32 = 0.225; | ||
| 104 | |||
| 105 | pub fn new() -> Self { | ||
| 106 | Self { | ||
| 107 | amplitude: 1.0, | ||
| 108 | modulo: 0.0, | ||
| 109 | phase_inc: 0.0, | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | pub fn set_frequency(&mut self, freq: f32, inv_sample_rate: f32) { | ||
| 114 | self.phase_inc = freq * inv_sample_rate; | ||
| 115 | } | ||
| 116 | |||
| 117 | pub fn set_amplitude(&mut self, amplitude: f32) { | ||
| 118 | self.amplitude = amplitude; | ||
| 119 | } | ||
| 120 | |||
| 121 | pub fn generate(&mut self) -> f32 { | ||
| 122 | let signal = self.parabolic_sin(self.modulo); | ||
| 123 | self.modulo += self.phase_inc; | ||
| 124 | if self.modulo < 0.0 { | ||
| 125 | self.modulo += 1.0; | ||
| 126 | } else if self.modulo > 1.0 { | ||
| 127 | self.modulo -= 1.0; | ||
| 128 | } | ||
| 129 | signal * self.amplitude | ||
| 130 | } | ||
| 131 | |||
| 132 | fn parabolic_sin(&mut self, modulo: f32) -> f32 { | ||
| 133 | let angle = PI - modulo * 2.0 * PI; | ||
| 134 | let y = Self::B * angle + Self::C * angle * abs(angle); | ||
| 135 | Self::P * (y * abs(y) - y) + y | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | #[inline] | ||
| 140 | fn abs(value: f32) -> f32 { | ||
| 141 | if value < 0.0 { | ||
| 142 | -value | ||
| 143 | } else { | ||
| 144 | value | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | #[inline] | ||
| 149 | fn bipolar_to_unipolar(value: f32) -> f32 { | ||
| 150 | (value + 1.0) / 2.0 | ||
| 151 | } | ||
