diff options
| author | Christian Perez Llamas <[email protected]> | 2022-12-08 20:22:50 +0100 |
|---|---|---|
| committer | Christian Perez Llamas <[email protected]> | 2022-12-08 20:22:50 +0100 |
| commit | 5fdd521a767fd8825a2d55d6b833fd99627353d7 (patch) | |
| tree | 906f042f5da59d68335c95c0e7a52045a120b7d6 | |
| parent | 199504be564b231154e07c58bcc52b11afdc9fe7 (diff) | |
Move the responsibility to manage buffers to the I2S stream
| -rw-r--r-- | embassy-nrf/src/i2s.rs | 157 | ||||
| -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 | 26 |
4 files changed, 353 insertions, 62 deletions
diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 08d4093f2..7e9507751 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs | |||
| @@ -19,6 +19,8 @@ use crate::pac::i2s::RegisterBlock; | |||
| 19 | use crate::util::{slice_in_ram_or, slice_ptr_parts}; | 19 | use crate::util::{slice_in_ram_or, slice_ptr_parts}; |
| 20 | use crate::{Peripheral, EASY_DMA_SIZE}; | 20 | use crate::{Peripheral, EASY_DMA_SIZE}; |
| 21 | 21 | ||
| 22 | pub type DoubleBuffering<S, const NS: usize> = MultiBuffering<S, 2, NS>; | ||
| 23 | |||
| 22 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 24 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 23 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 25 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 24 | #[non_exhaustive] | 26 | #[non_exhaustive] |
| @@ -379,27 +381,47 @@ impl<'d, T: Instance> I2S<'d, T> { | |||
| 379 | } | 381 | } |
| 380 | 382 | ||
| 381 | /// I2S output only | 383 | /// I2S output only |
| 382 | pub fn output(mut self, sdout: impl Peripheral<P = impl GpioPin> + 'd) -> OutputStream<'d, T> { | 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> { | ||
| 383 | self.sdout = Some(sdout.into_ref().map_into()); | 389 | self.sdout = Some(sdout.into_ref().map_into()); |
| 384 | OutputStream { _p: self.build() } | 390 | OutputStream { |
| 391 | _p: self.build(), | ||
| 392 | buffers, | ||
| 393 | } | ||
| 385 | } | 394 | } |
| 386 | 395 | ||
| 387 | /// I2S input only | 396 | /// I2S input only |
| 388 | pub fn input(mut self, sdin: impl Peripheral<P = impl GpioPin> + 'd) -> InputStream<'d, T> { | 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> { | ||
| 389 | self.sdin = Some(sdin.into_ref().map_into()); | 402 | self.sdin = Some(sdin.into_ref().map_into()); |
| 390 | InputStream { _p: self.build() } | 403 | InputStream { |
| 404 | _p: self.build(), | ||
| 405 | buffers, | ||
| 406 | } | ||
| 391 | } | 407 | } |
| 392 | 408 | ||
| 393 | /// I2S full duplex (input and output) | 409 | /// I2S full duplex (input and output) |
| 394 | pub fn full_duplex( | 410 | pub fn full_duplex<S: Sample, const NB: usize, const NS: usize>( |
| 395 | mut self, | 411 | mut self, |
| 396 | sdin: impl Peripheral<P = impl GpioPin> + 'd, | 412 | sdin: impl Peripheral<P = impl GpioPin> + 'd, |
| 397 | sdout: impl Peripheral<P = impl GpioPin> + 'd, | 413 | sdout: impl Peripheral<P = impl GpioPin> + 'd, |
| 398 | ) -> FullDuplexStream<'d, T> { | 414 | buffers_out: MultiBuffering<S, NB, NS>, |
| 415 | buffers_in: MultiBuffering<S, NB, NS>, | ||
| 416 | ) -> FullDuplexStream<'d, T, S, NB, NS> { | ||
| 399 | self.sdout = Some(sdout.into_ref().map_into()); | 417 | self.sdout = Some(sdout.into_ref().map_into()); |
| 400 | self.sdin = Some(sdin.into_ref().map_into()); | 418 | self.sdin = Some(sdin.into_ref().map_into()); |
| 401 | 419 | ||
| 402 | FullDuplexStream { _p: self.build() } | 420 | FullDuplexStream { |
| 421 | _p: self.build(), | ||
| 422 | buffers_out, | ||
| 423 | buffers_in, | ||
| 424 | } | ||
| 403 | } | 425 | } |
| 404 | 426 | ||
| 405 | fn build(self) -> PeripheralRef<'d, T> { | 427 | fn build(self) -> PeripheralRef<'d, T> { |
| @@ -651,14 +673,19 @@ impl<'d, T: Instance> I2S<'d, T> { | |||
| 651 | } | 673 | } |
| 652 | 674 | ||
| 653 | /// I2S output | 675 | /// I2S output |
| 654 | pub struct OutputStream<'d, T: Instance> { | 676 | pub struct OutputStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { |
| 655 | _p: PeripheralRef<'d, T>, | 677 | _p: PeripheralRef<'d, T>, |
| 678 | buffers: MultiBuffering<S, NB, NS>, | ||
| 656 | } | 679 | } |
| 657 | 680 | ||
| 658 | impl<'d, T: Instance> OutputStream<'d, T> { | 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 | |||
| 659 | /// Prepare the initial buffer and start the I2S transfer. | 687 | /// Prepare the initial buffer and start the I2S transfer. |
| 660 | #[allow(unused_mut)] | 688 | pub async fn start(&mut self) -> Result<(), Error> |
| 661 | pub async fn start<S>(&mut self, buffer: &[S]) -> Result<(), Error> | ||
| 662 | where | 689 | where |
| 663 | S: Sample, | 690 | S: Sample, |
| 664 | { | 691 | { |
| @@ -672,7 +699,7 @@ impl<'d, T: Instance> OutputStream<'d, T> { | |||
| 672 | device.enable(); | 699 | device.enable(); |
| 673 | device.enable_tx(); | 700 | device.enable_tx(); |
| 674 | 701 | ||
| 675 | device.update_tx(buffer as *const [S])?; | 702 | device.update_tx(self.buffers.switch())?; |
| 676 | 703 | ||
| 677 | s.started.store(true, Ordering::Relaxed); | 704 | s.started.store(true, Ordering::Relaxed); |
| 678 | 705 | ||
| @@ -689,28 +716,30 @@ impl<'d, T: Instance> OutputStream<'d, T> { | |||
| 689 | I2S::<T>::stop().await | 716 | I2S::<T>::stop().await |
| 690 | } | 717 | } |
| 691 | 718 | ||
| 692 | /// Sets the given `buffer` for transmission in the DMA. | 719 | /// Sends the current buffer for transmission in the DMA. |
| 693 | /// Buffer address must be 4 byte aligned and located in RAM. | 720 | /// Switches to use the next available buffer. |
| 694 | /// The buffer must not be written while being used by the DMA, | 721 | pub async fn send(&mut self) -> Result<(), Error> |
| 695 | /// which takes two other `send`s being awaited. | ||
| 696 | #[allow(unused_mut)] | ||
| 697 | pub async fn send_from_ram<S>(&mut self, buffer: &[S]) -> Result<(), Error> | ||
| 698 | where | 722 | where |
| 699 | S: Sample, | 723 | S: Sample, |
| 700 | { | 724 | { |
| 701 | I2S::<T>::send_from_ram(buffer as *const [S]).await | 725 | I2S::<T>::send_from_ram(self.buffers.switch()).await |
| 702 | } | 726 | } |
| 703 | } | 727 | } |
| 704 | 728 | ||
| 705 | /// I2S input | 729 | /// I2S input |
| 706 | pub struct InputStream<'d, T: Instance> { | 730 | pub struct InputStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { |
| 707 | _p: PeripheralRef<'d, T>, | 731 | _p: PeripheralRef<'d, T>, |
| 732 | buffers: MultiBuffering<S, NB, NS>, | ||
| 708 | } | 733 | } |
| 709 | 734 | ||
| 710 | impl<'d, T: Instance> InputStream<'d, T> { | 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 | |||
| 711 | /// Prepare the initial buffer and start the I2S transfer. | 741 | /// Prepare the initial buffer and start the I2S transfer. |
| 712 | #[allow(unused_mut)] | 742 | pub async fn start(&mut self) -> Result<(), Error> |
| 713 | pub async fn start<S>(&mut self, buffer: &mut [S]) -> Result<(), Error> | ||
| 714 | where | 743 | where |
| 715 | S: Sample, | 744 | S: Sample, |
| 716 | { | 745 | { |
| @@ -724,7 +753,7 @@ impl<'d, T: Instance> InputStream<'d, T> { | |||
| 724 | device.enable(); | 753 | device.enable(); |
| 725 | device.enable_rx(); | 754 | device.enable_rx(); |
| 726 | 755 | ||
| 727 | device.update_rx(buffer as *mut [S])?; | 756 | device.update_rx(self.buffers.switch())?; |
| 728 | 757 | ||
| 729 | s.started.store(true, Ordering::Relaxed); | 758 | s.started.store(true, Ordering::Relaxed); |
| 730 | 759 | ||
| @@ -741,28 +770,32 @@ impl<'d, T: Instance> InputStream<'d, T> { | |||
| 741 | I2S::<T>::stop().await | 770 | I2S::<T>::stop().await |
| 742 | } | 771 | } |
| 743 | 772 | ||
| 744 | /// Sets the given `buffer` for reception from the DMA. | 773 | /// Sets the current buffer for reception from the DMA. |
| 745 | /// Buffer address must be 4 byte aligned and located in RAM. | 774 | /// Switches to use the next available buffer. |
| 746 | /// The buffer must not be read while being used by the DMA, | ||
| 747 | /// which takes two other `receive`s being awaited. | ||
| 748 | #[allow(unused_mut)] | 775 | #[allow(unused_mut)] |
| 749 | pub async fn receive_from_ram<S>(&mut self, buffer: &mut [S]) -> Result<(), Error> | 776 | pub async fn receive(&mut self) -> Result<(), Error> |
| 750 | where | 777 | where |
| 751 | S: Sample, | 778 | S: Sample, |
| 752 | { | 779 | { |
| 753 | I2S::<T>::receive_from_ram(buffer as *mut [S]).await | 780 | I2S::<T>::receive_from_ram(self.buffers.switch_mut()).await |
| 754 | } | 781 | } |
| 755 | } | 782 | } |
| 756 | 783 | ||
| 757 | /// I2S full duplex stream (input & output) | 784 | /// I2S full duplex stream (input & output) |
| 758 | pub struct FullDuplexStream<'d, T: Instance> { | 785 | pub struct FullDuplexStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { |
| 759 | _p: PeripheralRef<'d, T>, | 786 | _p: PeripheralRef<'d, T>, |
| 787 | buffers_out: MultiBuffering<S, NB, NS>, | ||
| 788 | buffers_in: MultiBuffering<S, NB, NS>, | ||
| 760 | } | 789 | } |
| 761 | 790 | ||
| 762 | impl<'d, T: Instance> FullDuplexStream<'d, T> { | 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 | |||
| 763 | /// Prepare the initial buffers and start the I2S transfer. | 797 | /// Prepare the initial buffers and start the I2S transfer. |
| 764 | #[allow(unused_mut)] | 798 | pub async fn start(&mut self) -> Result<(), Error> |
| 765 | pub async fn start<S>(&mut self, buffer_in: &mut [S], buffer_out: &[S]) -> Result<(), Error> | ||
| 766 | where | 799 | where |
| 767 | S: Sample, | 800 | S: Sample, |
| 768 | { | 801 | { |
| @@ -777,8 +810,8 @@ impl<'d, T: Instance> FullDuplexStream<'d, T> { | |||
| 777 | device.enable_tx(); | 810 | device.enable_tx(); |
| 778 | device.enable_rx(); | 811 | device.enable_rx(); |
| 779 | 812 | ||
| 780 | device.update_tx(buffer_out as *const [S])?; | 813 | device.update_tx(self.buffers_out.switch())?; |
| 781 | device.update_rx(buffer_in as *mut [S])?; | 814 | device.update_rx(self.buffers_in.switch_mut())?; |
| 782 | 815 | ||
| 783 | s.started.store(true, Ordering::Relaxed); | 816 | s.started.store(true, Ordering::Relaxed); |
| 784 | 817 | ||
| @@ -796,17 +829,14 @@ impl<'d, T: Instance> FullDuplexStream<'d, T> { | |||
| 796 | I2S::<T>::stop().await | 829 | I2S::<T>::stop().await |
| 797 | } | 830 | } |
| 798 | 831 | ||
| 799 | /// Sets the given `buffer_out` and `buffer_in` for transmission/reception from the DMA. | 832 | /// Sets the current buffers for output and input for transmission/reception from the DMA. |
| 800 | /// Buffer address must be 4 byte aligned and located in RAM. | 833 | /// Switch to use the next available buffers for output/input. |
| 801 | /// The buffers must not be written/read while being used by the DMA, | 834 | pub async fn send_and_receive(&mut self) -> Result<(), Error> |
| 802 | /// which takes two other `send_and_receive` operations being awaited. | ||
| 803 | #[allow(unused_mut)] | ||
| 804 | pub async fn send_and_receive_from_ram<S>(&mut self, buffer_in: &mut [S], buffer_out: &[S]) -> Result<(), Error> | ||
| 805 | where | 835 | where |
| 806 | S: Sample, | 836 | S: Sample, |
| 807 | { | 837 | { |
| 808 | I2S::<T>::send_from_ram(buffer_out as *const [S]).await?; | 838 | I2S::<T>::send_from_ram(self.buffers_out.switch()).await?; |
| 809 | I2S::<T>::receive_from_ram(buffer_in as *mut [S]).await?; | 839 | I2S::<T>::receive_from_ram(self.buffers_in.switch_mut()).await?; |
| 810 | Ok(()) | 840 | Ok(()) |
| 811 | } | 841 | } |
| 812 | } | 842 | } |
| @@ -992,7 +1022,7 @@ impl Sample for i32 { | |||
| 992 | const SCALE: Self = 1 << (Self::WIDTH - 1); | 1022 | const SCALE: Self = 1 << (Self::WIDTH - 1); |
| 993 | } | 1023 | } |
| 994 | 1024 | ||
| 995 | /// A 4-bytes aligned [Buffer]. | 1025 | /// A 4-bytes aligned buffer. |
| 996 | #[derive(Clone, Copy)] | 1026 | #[derive(Clone, Copy)] |
| 997 | #[repr(align(4))] | 1027 | #[repr(align(4))] |
| 998 | pub struct AlignedBuffer<T: Sample, const N: usize>([T; N]); | 1028 | pub struct AlignedBuffer<T: Sample, const N: usize>([T; N]); |
| @@ -1022,6 +1052,43 @@ impl<T: Sample, const N: usize> DerefMut for AlignedBuffer<T, N> { | |||
| 1022 | } | 1052 | } |
| 1023 | } | 1053 | } |
| 1024 | 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 | |||
| 1025 | pub(crate) mod sealed { | 1092 | pub(crate) mod sealed { |
| 1026 | use core::sync::atomic::AtomicBool; | 1093 | use core::sync::atomic::AtomicBool; |
| 1027 | 1094 | ||
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 index 13b1300ea..1b0e8ebc8 100644 --- a/examples/nrf/src/bin/i2s_waveform.rs +++ b/examples/nrf/src/bin/i2s_waveform.rs | |||
| @@ -6,13 +6,12 @@ use core::f32::consts::PI; | |||
| 6 | 6 | ||
| 7 | use defmt::{error, info}; | 7 | use defmt::{error, info}; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_nrf::i2s::{self, Channels, Config, MasterClock, Sample as _, SampleWidth, I2S}; | 9 | use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; |
| 10 | use embassy_nrf::interrupt; | 10 | use embassy_nrf::interrupt; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 12 | ||
| 13 | type Sample = i16; | 13 | type Sample = i16; |
| 14 | 14 | ||
| 15 | const NUM_BUFFERS: usize = 2; | ||
| 16 | const NUM_SAMPLES: usize = 50; | 15 | const NUM_SAMPLES: usize = 50; |
| 17 | 16 | ||
| 18 | #[embassy_executor::main] | 17 | #[embassy_executor::main] |
| @@ -29,29 +28,22 @@ async fn main(_spawner: Spawner) { | |||
| 29 | .channels(Channels::MonoLeft); | 28 | .channels(Channels::MonoLeft); |
| 30 | 29 | ||
| 31 | let irq = interrupt::take!(I2S); | 30 | let irq = interrupt::take!(I2S); |
| 32 | let mut output_stream = I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28); | 31 | let buffers = DoubleBuffering::<Sample, NUM_SAMPLES>::new(); |
| 33 | 32 | let mut output_stream = | |
| 34 | let mut buffers: [i2s::AlignedBuffer<Sample, NUM_SAMPLES>; NUM_BUFFERS] = | 33 | I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28, buffers); |
| 35 | [i2s::AlignedBuffer::default(); NUM_BUFFERS]; | ||
| 36 | 34 | ||
| 37 | let mut waveform = Waveform::new(1.0 / sample_rate as f32); | 35 | let mut waveform = Waveform::new(1.0 / sample_rate as f32); |
| 38 | 36 | ||
| 39 | waveform.process(&mut buffers[0]); | 37 | waveform.process(output_stream.buffer()); |
| 40 | 38 | ||
| 41 | output_stream.start(&buffers[0]).await.expect("I2S Start"); | 39 | output_stream.start().await.expect("I2S Start"); |
| 42 | 40 | ||
| 43 | let mut index = 1; | ||
| 44 | loop { | 41 | loop { |
| 45 | waveform.process(&mut buffers[index]); | 42 | waveform.process(output_stream.buffer()); |
| 46 | 43 | ||
| 47 | if let Err(err) = output_stream.send_from_ram(&buffers[index]).await { | 44 | if let Err(err) = output_stream.send().await { |
| 48 | error!("{}", err); | 45 | error!("{}", err); |
| 49 | } | 46 | } |
| 50 | |||
| 51 | index += 1; | ||
| 52 | if index >= NUM_BUFFERS { | ||
| 53 | index = 0; | ||
| 54 | } | ||
| 55 | } | 47 | } |
| 56 | } | 48 | } |
| 57 | 49 | ||
| @@ -68,7 +60,7 @@ impl Waveform { | |||
| 68 | carrier.set_frequency(110.0, inv_sample_rate); | 60 | carrier.set_frequency(110.0, inv_sample_rate); |
| 69 | 61 | ||
| 70 | let mut freq_mod = SineOsc::new(); | 62 | let mut freq_mod = SineOsc::new(); |
| 71 | freq_mod.set_frequency(8.0, inv_sample_rate); | 63 | freq_mod.set_frequency(1.0, inv_sample_rate); |
| 72 | freq_mod.set_amplitude(1.0); | 64 | freq_mod.set_amplitude(1.0); |
| 73 | 65 | ||
| 74 | let mut amp_mod = SineOsc::new(); | 66 | let mut amp_mod = SineOsc::new(); |
