From 1b06976c69396e6fd94a9417fef694282d6aab97 Mon Sep 17 00:00:00 2001 From: Augustin Godinot Date: Sun, 26 Sep 2021 17:59:53 +0200 Subject: [PATCH 1/2] Add initial PDM peripheral HAL --- nrf-hal-common/src/lib.rs | 2 + nrf-hal-common/src/pdm.rs | 134 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 nrf-hal-common/src/pdm.rs diff --git a/nrf-hal-common/src/lib.rs b/nrf-hal-common/src/lib.rs index e1423ef7..97d3f510 100644 --- a/nrf-hal-common/src/lib.rs +++ b/nrf-hal-common/src/lib.rs @@ -85,6 +85,8 @@ pub mod uicr; #[cfg(feature = "nrf-usbd")] pub mod usbd; pub mod wdt; +#[cfg(feature = "52840")] +pub mod pdm; pub mod prelude { pub use crate::hal::digital::v2::*; diff --git a/nrf-hal-common/src/pdm.rs b/nrf-hal-common/src/pdm.rs new file mode 100644 index 00000000..354d3cf2 --- /dev/null +++ b/nrf-hal-common/src/pdm.rs @@ -0,0 +1,134 @@ +//! HAL interface to the PDM peripheral +//! +//! The PDM (Pulse Density Modulation) peripheral enables the sampling of pulse +//! density signals. + +use crate::{ + hal::digital::v2::OutputPin, + gpio::{Floating, Input, Output, Pin, PushPull}, + pac::PDM, +}; +// Publicly re-export configuration enums for convenience +pub use crate::pac::pdm::{ + gainl::GAINL_A as GainL, + gainr::GAINR_A as GainR, + mode::EDGE_A as Sampling, + mode::OPERATION_A as Channel, + pdmclkctrl::FREQ_A as Frequency, + ratio::RATIO_A as Ratio, +}; + +pub struct Pdm { + pdm: PDM, + clk: Pin>, + din: Pin>, +} + +impl Pdm { + /// Create the `Pdm` instance, initialize the raw peripheral and enable it. + pub fn new(pdm: PDM, mut clk: Pin>, din: Pin>) -> Self { + // Set the CLK pin low as requested by the docs + clk.set_low().unwrap(); + + // Configure the pins + pdm.psel.clk.write(|w| { + unsafe { w.bits(clk.psel_bits()) }; + w.connect().connected() + }); + pdm.psel.din.write(|w| { + unsafe { w.bits(din.psel_bits()) }; + w.connect().connected() + }); + + Self { pdm, clk, din } + } + + /// Set clock frequency + pub fn frequency(&self, frequency: Frequency) -> &Self { + self.pdm.pdmclkctrl.write(|w| w.freq().variant(frequency)); + + self + } + + /// Set the hardware decimation filter gain for the left channel (this is + /// also the gain used in mono mode) + pub fn left_gain(&self, gain: GainL) -> &Self { + self.pdm.gainl.write(|w| w.gainl().variant(gain)); + + self + } + + /// Set the hardware decimation filter gain for the left channel (this is + /// also the gain used in mono mode) + pub fn right_gain(&self, gain: GainR) -> &Self { + self.pdm.gainr.write(|w| w.gainr().variant(gain)); + + self + } + + /// Set the ratio clock frequency/sample rate (sample rate = clock frequency / ratio) + pub fn ratio(&self, ratio: Ratio) -> &Self { + self.pdm.ratio.write(|w| w.ratio().variant(ratio)); + + self + } + + /// Set whether the left (or mono) samples are taken on a clock rise or fall. + pub fn sampling(&self, sampling: Sampling) -> &Self { + self.pdm.mode.write(|w| w.edge().variant(sampling)); + + self + } + + /// Set the channel mode : mono or stereo + pub fn channel(&self, channel: Channel) -> &Self { + self.pdm.mode.write(|w| w.operation().variant(channel)); + + self + } + + /// Enable the peripheral + pub fn enable(&self) { + self.pdm.enable.write(|w| w.enable().enabled()); + } + + /// Return ownership of underlying pins and peripheral + pub fn free(self) -> (PDM, Pin>, Pin>) { + (self.pdm, self.clk, self.din) + } + + /// Perform one blocking acquisition, filling the given buffer with samples. + /// + /// The buffer length must not exceed 2^16 - 1 + pub fn read(&self, buffer: &mut [i16]) { + // Setup the buffer address and the number of samples to acquire + self.pdm.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(buffer.as_ptr() as u32) }); + self.pdm.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(buffer.len() as u16) }); + + // Start the acquisition + self.pdm.tasks_start.write(|w| w.tasks_start().set_bit()); + + // Wait for the acquisition to start then prevent it from restarting + // after + while !self.pdm.events_started.read().events_started().bit_is_set() {} + self.pdm.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(0) }); + + // Wait for the acquisition to finish + while !self.pdm.events_end.read().events_end().bit_is_set() {} + + self.clear_events(); + } + + /// Clear all events + fn clear_events(&self) { + self.pdm.events_started.write(|w| w.events_started().clear_bit()); + self.pdm.events_stopped.write(|w| w.events_stopped().clear_bit()); + self.pdm.events_end.write(|w| w.events_end().clear_bit()); + } +} From 373ef6fe3e734ebe55ffecbd77c9f1df7b5a3d33 Mon Sep 17 00:00:00 2001 From: Augustin Godinot Date: Sun, 26 Sep 2021 18:00:08 +0200 Subject: [PATCH 2/2] Add PDM example --- examples/pdm-demo/Embed.toml | 63 +++++++++++++++++++++++++++++ examples/pdm-demo/README.md | 12 ++++++ examples/pdm-demo/src/main.rs | 75 +++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+) create mode 100755 examples/pdm-demo/Embed.toml create mode 100644 examples/pdm-demo/README.md create mode 100755 examples/pdm-demo/src/main.rs diff --git a/examples/pdm-demo/Embed.toml b/examples/pdm-demo/Embed.toml new file mode 100755 index 00000000..fda29b6b --- /dev/null +++ b/examples/pdm-demo/Embed.toml @@ -0,0 +1,63 @@ +[default.probe] +# USB vendor ID +# usb_vid = "1337" +# USB product ID +# usb_pid = "1337" +# Serial number +# serial = "12345678" +# The protocol to be used for communicating with the target. +protocol = "Swd" +# The speed in kHz of the data link to the target. +# speed = 1337 + +[default.flashing] +# Whether or not the target should be flashed. +enabled = true +# Whether or not the target should be halted after reset. +# DEPRECATED, moved to reset section +halt_afterwards = false +# Whether or not bytes erased but not rewritten with data from the ELF +# should be restored with their contents before erasing. +restore_unwritten_bytes = false +# The path where an SVG of the assembled flash layout should be written to. +# flash_layout_output_path = "out.svg" + +[default.reset] +# Whether or not the target should be reset. +# When flashing is enabled as well, the target will be reset after flashing. +enabled = true +# Whether or not the target should be halted after reset. +halt_afterwards = false + +[default.general] +# The chip name of the chip to be debugged. +chip = "nRF52840" +# A list of chip descriptions to be loaded during runtime. +chip_descriptions = [] +# The default log level to be used. Possible values are one of: +# "OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE" +log_level = "WARN" + +[default.rtt] +# Whether or not an RTTUI should be opened after flashing. +# This is exclusive and cannot be used with GDB at the moment. +enabled = true +# A list of channel associations to be displayed. If left empty, all channels are displayed. +channels = [ + # { up = 0, down = 0, name = "name", format = "String" } +] +# The duration in ms for which the logger should retry to attach to RTT. +timeout = 3000 +# Whether timestamps in the RTTUI are enabled +show_timestamps = true +# Whether to save rtt history buffer on exit. +log_enabled = false +# Where to save rtt history buffer relative to manifest path. +log_path = "./logs" + +[default.gdb] +# Whether or not a GDB server should be opened after flashing. +# This is exclusive and cannot be used with RTT at the moment. +enabled = false +# The connection string in host:port format wher the GDB server will open a socket. +# gdb_connection_string diff --git a/examples/pdm-demo/README.md b/examples/pdm-demo/README.md new file mode 100644 index 00000000..fa88800c --- /dev/null +++ b/examples/pdm-demo/README.md @@ -0,0 +1,12 @@ +# Pulse Density Modulation demo + +This example showcases how to use the PDM peripheral to acquire microphone samples on an Arduino Nano 33 BLE board. + + +## How to run + +If using `cargo-embed`, just run + +```console +$ cargo embed --release --target=thumbv7em-none-eabihf +``` \ No newline at end of file diff --git a/examples/pdm-demo/src/main.rs b/examples/pdm-demo/src/main.rs new file mode 100755 index 00000000..c7e8d1f6 --- /dev/null +++ b/examples/pdm-demo/src/main.rs @@ -0,0 +1,75 @@ +#![no_main] +#![no_std] + +use core::{ + panic::PanicInfo, + sync::atomic::{compiler_fence, Ordering}, +}; + +use nrf52840_hal as hal; +use hal::{ + pdm::{self, Pdm}, + clocks::Clocks, + gpio::Level, +}; +use rtt_target::{rprintln, rtt_init_print}; + +// The lenght of the buffer allocated to the pdm samples (in mono mode it is +// exactly the number of samples per read) +const PDM_BUFFER_LEN: usize = 8192; + +#[cortex_m_rt::entry] +fn main() -> ! { + rtt_init_print!(); + rprintln!("Hello, world!"); + + let peripherals = hal::pac::Peripherals::take().unwrap(); + let port0 = hal::gpio::p0::Parts::new(peripherals.P0); + + // Enable the high frequency oscillator if not already enabled + let _clocks = Clocks::new(peripherals.CLOCK).enable_ext_hfosc(); + + // Retreive the right pins used in the Arduino Nano 33 BLE Sense board + let _mic_vcc = port0.p0_17.into_push_pull_output(Level::High); + let mic_clk = port0.p0_26.into_push_pull_output(Level::Low).degrade(); + let mic_din = port0.p0_25.into_floating_input().degrade(); + + let pdm = Pdm::new(peripherals.PDM, mic_clk, mic_din); + pdm.sampling(pdm::Sampling::LEFTFALLING) + .channel(pdm::Channel::MONO) + .frequency(pdm::Frequency::_1280K) + .left_gain(pdm::GainL::MAXGAIN) + .enable(); + + // Allocate the buffer + let mut buffer = [0; PDM_BUFFER_LEN]; + + // Skip a few samples as suggested by the nrf-52840 docs + for i in 0..10 { + rprintln!("{}", i); + pdm.read(&mut buffer); + } + + + // Output the power of the received signal + loop { + // Ask the pdm peripheral to fill our buffer with samples + pdm.read(&mut buffer); + + let square_sum = buffer.iter().fold(0i64, |sum, &item| { + sum + (item as i64).pow(2) + }); + rprintln!("Energy : {}", square_sum as f32 / PDM_BUFFER_LEN as f32); + + for _ in 0..10_000 {} + } +} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + cortex_m::interrupt::disable(); + rprintln!("{}", info); + loop { + compiler_fence(Ordering::SeqCst); + } +} \ No newline at end of file