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 /examples/nrf | |
| parent | 199504be564b231154e07c58bcc52b11afdc9fe7 (diff) | |
Move the responsibility to manage buffers to the I2S stream
Diffstat (limited to 'examples/nrf')
| -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 |
3 files changed, 241 insertions, 17 deletions
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(); |
