From b57b66647399545475576e7d2a7cff97b115771c Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 3 Feb 2021 13:03:17 +0100 Subject: [PATCH 01/48] Updating input capture for timers --- src/bin/lockin-external.rs | 3 +++ src/hardware/digital_input_stamper.rs | 2 +- src/hardware/timers.rs | 29 +++++++++++++++++---------- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index 11d4958..8dc9907 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -71,6 +71,9 @@ const APP: () = { // Start sampling ADCs. stabilizer.adc_dac_timer.start(); + // Enable the timestamper. + stabilizer.timestamper.start(); + init::LateResources { afes: stabilizer.afes, adcs: stabilizer.adcs, diff --git a/src/hardware/digital_input_stamper.rs b/src/hardware/digital_input_stamper.rs index 6bd8629..1aa0bb1 100644 --- a/src/hardware/digital_input_stamper.rs +++ b/src/hardware/digital_input_stamper.rs @@ -45,7 +45,7 @@ impl InputStamper { // Utilize the TIM5 CH4 as an input capture channel - use TI4 (the DI0 input trigger) as the // capture source. let input_capture = - timer_channel.into_input_capture(timers::CaptureTrigger::Input24); + timer_channel.into_input_capture(timers::tim5::CaptureSource4::TI4); Self { capture_channel: input_capture, diff --git a/src/hardware/timers.rs b/src/hardware/timers.rs index b686220..da11cf3 100644 --- a/src/hardware/timers.rs +++ b/src/hardware/timers.rs @@ -1,13 +1,14 @@ ///! The sampling timer is used for managing ADC sampling and external reference timestamping. use super::hal; -/// The source of an input capture trigger. -#[allow(dead_code)] -pub enum CaptureTrigger { - Input13 = 0b01, - Input24 = 0b10, - TriggerInput = 0b11, -} +use hal::stm32::{ + // TIM1 and TIM8 have identical registers. + tim1 as __tim8, + tim2 as __tim2, + // TIM2 and TIM5 have identical registers. + tim2 as __tim5, + tim3 as __tim3, +}; /// The event that should generate an external trigger from the peripheral. #[allow(dead_code)] @@ -225,6 +226,8 @@ macro_rules! timer_channels { ($index:expr, $TY:ty, $ccmrx:expr, $size:ty) => { paste::paste! { + pub use super::[< __ $TY:lower >]::[< $ccmrx _input >]::[< CC $index S_A>] as [< CaptureSource $index >]; + /// A capture/compare channel of the timer. pub struct [< Channel $index >] {} @@ -232,6 +235,8 @@ macro_rules! timer_channels { pub struct [< Channel $index InputCapture>] {} impl [< Channel $index >] { + + /// Construct a new timer channel. /// /// Note(unsafe): This function must only be called once. Once constructed, the @@ -267,12 +272,11 @@ macro_rules! timer_channels { /// # Args /// * `input` - The input source for the input capture event. #[allow(dead_code)] - pub fn into_input_capture(self, input: super::CaptureTrigger) -> [< Channel $index InputCapture >]{ + pub fn into_input_capture(self, input: [< CaptureSource $index >]) -> [< Channel $index InputCapture >]{ let regs = unsafe { &*<$TY>::ptr() }; - // Note(unsafe): The bit configuration is guaranteed to be valid by the - // CaptureTrigger enum definition. - regs.[< $ccmrx _input >]().modify(|_, w| unsafe { w.[< cc $index s>]().bits(input as u8) }); + regs.[< $ccmrx _input >]().modify(|_, w| w.[< cc $index s>]().variant(input)); + [< Channel $index InputCapture >] {} } @@ -316,6 +320,9 @@ macro_rules! timer_channels { /// Enable the input capture to begin capturing timer values. #[allow(dead_code)] pub fn enable(&mut self) { + // Read the latest input capture to clear any pending data in the register. + let _ = self.latest_capture(); + // Note(unsafe): This channel owns all access to the specific timer channel. // Only atomic operations on completed on the timer registers. let regs = unsafe { &*<$TY>::ptr() }; From 4e6f65b3e011f7b8bb161234bffec95073f2aebf Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 3 Feb 2021 13:42:43 +0100 Subject: [PATCH 02/48] Fixing spacing --- src/hardware/timers.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/hardware/timers.rs b/src/hardware/timers.rs index da11cf3..e2cbbc8 100644 --- a/src/hardware/timers.rs +++ b/src/hardware/timers.rs @@ -235,8 +235,6 @@ macro_rules! timer_channels { pub struct [< Channel $index InputCapture>] {} impl [< Channel $index >] { - - /// Construct a new timer channel. /// /// Note(unsafe): This function must only be called once. Once constructed, the @@ -277,7 +275,6 @@ macro_rules! timer_channels { regs.[< $ccmrx _input >]().modify(|_, w| w.[< cc $index s>]().variant(input)); - [< Channel $index InputCapture >] {} } } From ef22f5ab92d2013ee1a9908d34832894f3c06501 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 3 Feb 2021 14:11:00 +0100 Subject: [PATCH 03/48] Fixing pounder input capture source --- src/hardware/pounder/timestamp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hardware/pounder/timestamp.rs b/src/hardware/pounder/timestamp.rs index 5ccf235..e709bfa 100644 --- a/src/hardware/pounder/timestamp.rs +++ b/src/hardware/pounder/timestamp.rs @@ -85,7 +85,7 @@ impl Timestamper { // The capture channel should capture whenever the trigger input occurs. let input_capture = capture_channel - .into_input_capture(timers::CaptureTrigger::TriggerInput); + .into_input_capture(timers::tim8::CaptureSource1::TRC); input_capture.listen_dma(); // The data transfer is always a transfer of data from the peripheral to a RAM buffer. From c55734852376953fc6023ead4a21362566a9c917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 3 Feb 2021 15:26:13 +0100 Subject: [PATCH 04/48] core_intrinsics attr need to be in the lib crate --- src/bin/dual-iir.rs | 1 - src/bin/lockin-external.rs | 1 - src/bin/lockin-internal.rs | 1 - src/lib.rs | 1 + 4 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index e28465b..b90452d 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -1,7 +1,6 @@ #![deny(warnings)] #![no_std] #![no_main] -#![cfg_attr(feature = "nightly", feature(core_intrinsics))] use stm32h7xx_hal as hal; diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index 8dc9907..d3b3410 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -1,7 +1,6 @@ #![deny(warnings)] #![no_std] #![no_main] -#![cfg_attr(feature = "nightly", feature(core_intrinsics))] use stm32h7xx_hal as hal; diff --git a/src/bin/lockin-internal.rs b/src/bin/lockin-internal.rs index e14a4da..4974be2 100644 --- a/src/bin/lockin-internal.rs +++ b/src/bin/lockin-internal.rs @@ -1,7 +1,6 @@ #![deny(warnings)] #![no_std] #![no_main] -#![cfg_attr(feature = "nightly", feature(core_intrinsics))] use dsp::{iir_int, lockin::Lockin, Accu}; use hardware::{Adc1Input, Dac0Output, Dac1Output, AFE0, AFE1}; diff --git a/src/lib.rs b/src/lib.rs index 218e3cf..ab2623d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![cfg_attr(feature = "nightly", feature(core_intrinsics))] #[macro_use] extern crate log; From 748a02fc4f4b836678f6a00d3bef9e8242c51f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 3 Feb 2021 16:50:18 +0100 Subject: [PATCH 05/48] ci: slim down * build binaries in one go * have bors look at specific jobs and not meta-jobs * don't do objdump anymore (gdb/embed handle elfs) * include a nightly build --- .github/bors.toml | 6 +++- .github/workflows/ci.yml | 77 +++++++--------------------------------- 2 files changed, 18 insertions(+), 65 deletions(-) diff --git a/.github/bors.toml b/.github/bors.toml index 4622d5d..83f9ab7 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -1,3 +1,7 @@ block_labels = [ "S-blocked" ] delete_merged_branches = true -status = ["ci"] +status = [ + "style", + "test", + "compile (stable, false)", +] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6fa0e99..f9eda78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,11 +16,9 @@ jobs: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: - profile: minimal toolchain: stable target: thumbv7em-none-eabihf override: true - components: rustfmt, clippy - name: cargo fmt --check uses: actions-rs/cargo@v1 with: @@ -37,59 +35,36 @@ jobs: compile: runs-on: ubuntu-latest + continue-on-error: ${{ matrix.optional }} strategy: matrix: - toolchain: - - stable - bin: - - dual-iir - - lockin-internal - - lockin-external - features: - - '' + toolchain: [stable] + features: [''] + optional: [false] include: - toolchain: beta - bin: dual-iir - features: '' - toolchain: stable - bin: lockin-internal features: pounder_v1_1 + - toolchain: nightly + features: nightly + optional: true steps: - uses: actions/checkout@v2 - - name: Install Rust ${{ matrix.toolchain }} - uses: actions-rs/toolchain@v1 + - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.toolchain }} target: thumbv7em-none-eabihf override: true - components: llvm-tools-preview - - name: cargo build release - uses: actions-rs/cargo@v1 + - uses: actions-rs/cargo@v1 with: command: build - args: --release --features "${{ matrix.features }}" --bin ${{ matrix.bin }} - - name: cargo-binutils - uses: actions-rs/cargo@v1 - with: - command: install - args: cargo-binutils - - name: cargo size - uses: actions-rs/cargo@v1 - with: - command: size - args: --release --features "${{ matrix.features }}" --bin ${{ matrix.bin }} - - name: cargo objcopy - uses: actions-rs/cargo@v1 - with: - command: objcopy - args: --release --features "${{ matrix.features }}" --bin ${{ matrix.bin }} --verbose -- -O binary ${{ matrix.bin }}-release.bin + args: --release --features "${{ matrix.features }}" - uses: actions/upload-artifact@v2 if: ${{ matrix.toolchain == 'stable' && matrix.features == '' }} with: - name: stabilizer_${{ matrix.bin }} + name: stabilizer path: | - target/*/release/${{ matrix.bin }} - ${{ matrix.bin }}-release.bin + target/*/release/dual-iir test: runs-on: ubuntu-latest @@ -100,8 +75,7 @@ jobs: - beta steps: - uses: actions/checkout@v2 - - name: Install Rust ${{ matrix.toolchain }} - uses: actions-rs/toolchain@v1 + - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.toolchain }} - name: cargo test @@ -114,28 +88,3 @@ jobs: with: command: bench args: --package dsp --target=x86_64-unknown-linux-gnu - - # Tell bors about it - # https://github.com/rtic-rs/cortex-m-rtic/blob/8a4f9c6b8ae91bebeea0791680f89375a78bffc6/.github/workflows/build.yml#L566-L603 - ci-success: - name: ci - if: github.event_name == 'push' && success() - needs: - - style - - compile - - test - runs-on: ubuntu-latest - steps: - - name: Mark the job as a success - run: exit 0 - ci-failure: - name: ci - if: github.event_name == 'push' && !success() - needs: - - style - - compile - - test - runs-on: ubuntu-latest - steps: - - name: Mark the job as a failure - run: exit 1 From 5c8f31616092013ea94d2150dc5b38dc4f28489b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 3 Feb 2021 17:24:06 +0100 Subject: [PATCH 06/48] Update ci.yml --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9eda78..4719639 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,8 +43,11 @@ jobs: optional: [false] include: - toolchain: beta + features: '' + optional: false - toolchain: stable features: pounder_v1_1 + optional: false - toolchain: nightly features: nightly optional: true From 4d37a5483f4fb339640d6bfb9471c88f39534ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 3 Feb 2021 17:58:43 +0100 Subject: [PATCH 07/48] ci: add release automation --- .github/workflows/release.yml | 45 +++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..5be1311 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,45 @@ +name: Release binaries + +on: + push: + tags: ['v*'] + +env: + CARGO_TERM_COLOR: always + +jobs: + compile: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: thumbv7em-none-eabihf + override: true + - uses: actions-rs/cargo@v1 + with: + command: build + args: --release --features "" + - run: > + zip bin.zip + target/*/release/dual-iir + target/*/release/lockin-external + target/*/release/lockin-internal + - id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + draft: false + prerelease: false + - uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./bin.zip + asset_name: stabilizer-${{ github.ref }}.zip + asset_content_type: application/zip From 1167c7693d1c81497e74bf52e613ebea1dc7d18b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 3 Feb 2021 18:01:24 +0100 Subject: [PATCH 08/48] bors: depend on test --- .github/bors.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/bors.toml b/.github/bors.toml index 83f9ab7..7168739 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -2,6 +2,6 @@ block_labels = [ "S-blocked" ] delete_merged_branches = true status = [ "style", - "test", + "test (stable)", "compile (stable, false)", ] From 4dfe16fce8a62cd70ee6e7b52d336dff9a3542bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 3 Feb 2021 18:14:42 +0100 Subject: [PATCH 09/48] ci: don't save binaries --- .github/workflows/ci.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4719639..851ef53 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,12 +62,6 @@ jobs: with: command: build args: --release --features "${{ matrix.features }}" - - uses: actions/upload-artifact@v2 - if: ${{ matrix.toolchain == 'stable' && matrix.features == '' }} - with: - name: stabilizer - path: | - target/*/release/dual-iir test: runs-on: ubuntu-latest From f250e036ca78e9ea2b9e8831a916b5d6afc5ffff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 4 Feb 2021 12:46:33 +0100 Subject: [PATCH 10/48] rpll: simplify parameters, add one test --- dsp/src/rpll.rs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/dsp/src/rpll.rs b/dsp/src/rpll.rs index d4bd86f..e312393 100644 --- a/dsp/src/rpll.rs +++ b/dsp/src/rpll.rs @@ -94,7 +94,6 @@ mod test { struct Harness { rpll: RPLL, - dt2: u8, shift_frequency: u8, shift_phase: u8, noise: i32, @@ -109,7 +108,6 @@ mod test { fn default() -> Self { Self { rpll: RPLL::new(8), - dt2: 8, shift_frequency: 9, shift_phase: 8, noise: 0, @@ -122,7 +120,7 @@ mod test { } fn run(&mut self, n: usize) -> (Vec, Vec) { - assert!(self.period >= 1 << self.dt2); + assert!(self.period >= 1 << self.rpll.dt2); assert!(self.period < 1 << self.shift_frequency); assert!(self.period < 1 << self.shift_phase + 1); @@ -130,7 +128,7 @@ mod test { let mut f = Vec::::new(); for _ in 0..n { let timestamp = if self.time - self.next_noisy >= 0 { - assert!(self.time - self.next_noisy < 1 << self.dt2); + assert!(self.time - self.next_noisy < 1 << self.rpll.dt2); self.next = self.next.wrapping_add(self.period); let timestamp = self.next_noisy; let p_noise = self.rng.gen_range(-self.noise..=self.noise); @@ -151,23 +149,23 @@ mod test { // phase error y.push(yi.wrapping_sub(y_ref) as f32 / 2f32.powi(32)); - let p_ref = 1 << 32 + self.dt2; + let p_ref = 1 << 32 + self.rpll.dt2; let p_sig = fi as u64 * self.period as u64; // relative frequency error f.push( p_sig.wrapping_sub(p_ref) as i64 as f32 - / 2f32.powi(32 + self.dt2 as i32), + / 2f32.powi(32 + self.rpll.dt2 as i32), ); // advance time - self.time = self.time.wrapping_add(1 << self.dt2); + self.time = self.time.wrapping_add(1 << self.rpll.dt2); } (y, f) } fn measure(&mut self, n: usize, limits: [f32; 4]) { - let t_settle = (1 << self.shift_frequency - self.dt2 + 4) - + (1 << self.shift_phase - self.dt2 + 4); + let t_settle = (1 << self.shift_frequency - self.rpll.dt2 + 4) + + (1 << self.shift_phase - self.rpll.dt2 + 4); self.run(t_settle); let (y, f) = self.run(n); @@ -268,4 +266,18 @@ mod test { h.measure(1 << 16, [2e-4, 6e-3, 2e-4, 2e-3]); } + + #[test] + fn batch_fast_narrow() { + let mut h = Harness::default(); + h.rpll.dt2 = 8 + 3; + h.period = 2431; + h.next = 35281; + h.next_noisy = h.next; + h.noise = 100; + h.shift_frequency = 23; + h.shift_phase = 23; + + h.measure(1 << 16, [1e-8, 2e-5, 6e-4, 6e-4]); + } } From 7ce90c4d319aa62c53cd0046041b7529b75f5b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 4 Feb 2021 12:47:35 +0100 Subject: [PATCH 11/48] input stamper: add deglitching --- src/hardware/digital_input_stamper.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/hardware/digital_input_stamper.rs b/src/hardware/digital_input_stamper.rs index 1aa0bb1..cc764f5 100644 --- a/src/hardware/digital_input_stamper.rs +++ b/src/hardware/digital_input_stamper.rs @@ -47,6 +47,10 @@ impl InputStamper { let input_capture = timer_channel.into_input_capture(timers::tim5::CaptureSource4::TI4); + // FIXME: hack in de-glitching filter + let regs = unsafe { &*hal::stm32::TIM5::ptr() }; + regs.ccmr2_input().modify(|_, w| w.ic4f().bits(0b0011)); + Self { capture_channel: input_capture, _di0_trigger: trigger, From f47ee38d314f906710da7fd0f0d8473eee3cf2d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 4 Feb 2021 12:48:25 +0100 Subject: [PATCH 12/48] move sample ticks and buffer size to design parameters --- src/bin/lockin-external.rs | 14 ++++++++------ src/bin/lockin-internal.rs | 7 ++++--- src/hardware/adc.rs | 4 ++-- src/hardware/configuration.rs | 19 +++++++++---------- src/hardware/dac.rs | 4 ++-- src/hardware/design_parameters.rs | 10 ++++++++++ src/hardware/mod.rs | 2 +- src/lib.rs | 10 ---------- 8 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index d3b3410..ae25d15 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -11,9 +11,7 @@ use rtic::cyccnt::{Instant, U32Ext}; use heapless::{consts::*, String}; -use stabilizer::{ - hardware, server, ADC_SAMPLE_TICKS_LOG2, SAMPLE_BUFFER_SIZE_LOG2, -}; +use stabilizer::{hardware, hardware::design_parameters, server}; use dsp::{iir, iir_int, lockin::Lockin, rpll::RPLL, Accu}; use hardware::{ @@ -52,7 +50,10 @@ const APP: () = { // Configure the microcontroller let (mut stabilizer, _pounder) = hardware::setup(c.core, c.device); - let pll = RPLL::new(ADC_SAMPLE_TICKS_LOG2 + SAMPLE_BUFFER_SIZE_LOG2); + let pll = RPLL::new( + design_parameters::ADC_SAMPLE_TICKS_LOG2 + + design_parameters::SAMPLE_BUFFER_SIZE_LOG2, + ); let lockin = Lockin::new( iir_int::Vec5::lowpass(1e-3, 0.707, 2.), // TODO: expose @@ -126,8 +127,9 @@ const APP: () = { let phase_offset: i32 = 0; // TODO: expose let sample_frequency = ((pll_frequency - // .wrapping_add(1 << SAMPLE_BUFFER_SIZE_LOG2 - 1) // half-up rounding bias - >> SAMPLE_BUFFER_SIZE_LOG2) as i32) + // .wrapping_add(1 << design_parameters::SAMPLE_BUFFER_SIZE_LOG2 - 1) // half-up rounding bias + >> design_parameters::SAMPLE_BUFFER_SIZE_LOG2) + as i32) .wrapping_mul(harmonic); let sample_phase = phase_offset.wrapping_add(pll_phase.wrapping_mul(harmonic)); diff --git a/src/bin/lockin-internal.rs b/src/bin/lockin-internal.rs index 4974be2..cadf189 100644 --- a/src/bin/lockin-internal.rs +++ b/src/bin/lockin-internal.rs @@ -4,13 +4,13 @@ use dsp::{iir_int, lockin::Lockin, Accu}; use hardware::{Adc1Input, Dac0Output, Dac1Output, AFE0, AFE1}; -use stabilizer::{hardware, SAMPLE_BUFFER_SIZE, SAMPLE_BUFFER_SIZE_LOG2}; +use stabilizer::{hardware, hardware::design_parameters}; // A constant sinusoid to send on the DAC output. // Full-scale gives a +/- 10V amplitude waveform. Scale it down to give +/- 1V. const ONE: i16 = (0.1 * u16::MAX as f32) as _; const SQRT2: i16 = (ONE as f32 * 0.707) as _; -const DAC_SEQUENCE: [i16; SAMPLE_BUFFER_SIZE] = +const DAC_SEQUENCE: [i16; design_parameters::SAMPLE_BUFFER_SIZE] = [ONE, SQRT2, 0, -SQRT2, -ONE, -SQRT2, 0, SQRT2]; #[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] @@ -83,7 +83,8 @@ const APP: () = { // Reference phase and frequency are known. let pll_phase = 0; - let pll_frequency = 1i32 << (32 - SAMPLE_BUFFER_SIZE_LOG2); + let pll_frequency = + 1i32 << (32 - design_parameters::SAMPLE_BUFFER_SIZE_LOG2); // Harmonic index of the LO: -1 to _de_modulate the fundamental let harmonic: i32 = -1; diff --git a/src/hardware/adc.rs b/src/hardware/adc.rs index 1cb6c17..3f2fa5d 100644 --- a/src/hardware/adc.rs +++ b/src/hardware/adc.rs @@ -74,9 +74,9 @@ ///! double-buffered mode offers less overhead due to the DMA disable/enable procedure). use stm32h7xx_hal as hal; -use crate::SAMPLE_BUFFER_SIZE; - +use super::design_parameters::SAMPLE_BUFFER_SIZE; use super::timers; + use hal::dma::{ config::Priority, dma::{DMAReq, DmaConfig}, diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index e173dc7..0b4b156 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -1,11 +1,6 @@ ///! Stabilizer hardware configuration ///! ///! This file contains all of the hardware-specific configuration of Stabilizer. -use crate::ADC_SAMPLE_TICKS; - -#[cfg(feature = "pounder_v1_1")] -use crate::SAMPLE_BUFFER_SIZE; - #[cfg(feature = "pounder_v1_1")] use core::convert::TryInto; @@ -157,7 +152,8 @@ pub fn setup( timer2.set_tick_freq(design_parameters::TIMER_FREQUENCY); let mut sampling_timer = timers::SamplingTimer::new(timer2); - sampling_timer.set_period_ticks((ADC_SAMPLE_TICKS - 1) as u32); + sampling_timer + .set_period_ticks((design_parameters::ADC_SAMPLE_TICKS - 1) as u32); // The sampling timer is used as the master timer for the shadow-sampling timer. Thus, // it generates a trigger whenever it is enabled. @@ -181,7 +177,8 @@ pub fn setup( let mut shadow_sampling_timer = timers::ShadowSamplingTimer::new(timer3); - shadow_sampling_timer.set_period_ticks(ADC_SAMPLE_TICKS - 1); + shadow_sampling_timer + .set_period_ticks(design_parameters::ADC_SAMPLE_TICKS - 1); // The shadow sampling timer is a slave-mode timer to the sampling timer. It should // always be in-sync - thus, we configure it to operate in slave mode using "Trigger @@ -726,7 +723,8 @@ pub fn setup( let sample_frequency = { let timer_frequency: hal::time::Hertz = design_parameters::TIMER_FREQUENCY.into(); - timer_frequency.0 as f32 / ADC_SAMPLE_TICKS as f32 + timer_frequency.0 as f32 + / design_parameters::ADC_SAMPLE_TICKS as f32 }; let sample_period = 1.0 / sample_frequency; @@ -773,8 +771,9 @@ pub fn setup( }; let period = (tick_ratio - * ADC_SAMPLE_TICKS as f32 - * SAMPLE_BUFFER_SIZE as f32) as u32 + * design_parameters::ADC_SAMPLE_TICKS as f32 + * design_parameters::SAMPLE_BUFFER_SIZE as f32) + as u32 / 4; timestamp_timer.set_period_ticks((period - 1).try_into().unwrap()); let tim8_channels = timestamp_timer.channels(); diff --git a/src/hardware/dac.rs b/src/hardware/dac.rs index 5ca65fb..d41ae8c 100644 --- a/src/hardware/dac.rs +++ b/src/hardware/dac.rs @@ -52,9 +52,9 @@ ///! served promptly after the transfer completes. use stm32h7xx_hal as hal; -use crate::SAMPLE_BUFFER_SIZE; - +use super::design_parameters::SAMPLE_BUFFER_SIZE; use super::timers; + use hal::dma::{ dma::{DMAReq, DmaConfig}, traits::TargetAddress, diff --git a/src/hardware/design_parameters.rs b/src/hardware/design_parameters.rs index 3de7c15..9951ee5 100644 --- a/src/hardware/design_parameters.rs +++ b/src/hardware/design_parameters.rs @@ -39,3 +39,13 @@ pub const DDS_SYSTEM_CLK: MegaHertz = /// The divider from the DDS system clock to the SYNC_CLK output (sync-clk is always 1/4 of sysclk). #[allow(dead_code)] pub const DDS_SYNC_CLK_DIV: u8 = 4; + +// The number of ticks in the ADC sampling timer. The timer runs at 100MHz, so the step size is +// equal to 10ns per tick. +// Currently, the sample rate is equal to: Fsample = 100/256 MHz = 390.625 KHz +pub const ADC_SAMPLE_TICKS_LOG2: u8 = 8; +pub const ADC_SAMPLE_TICKS: u16 = 1 << ADC_SAMPLE_TICKS_LOG2; + +// The desired ADC sample processing buffer size. +pub const SAMPLE_BUFFER_SIZE_LOG2: u8 = 3; +pub const SAMPLE_BUFFER_SIZE: usize = 1 << SAMPLE_BUFFER_SIZE_LOG2; diff --git a/src/hardware/mod.rs b/src/hardware/mod.rs index f912057..b54b6f7 100644 --- a/src/hardware/mod.rs +++ b/src/hardware/mod.rs @@ -11,7 +11,7 @@ mod adc; mod afe; mod configuration; mod dac; -mod design_parameters; +pub mod design_parameters; mod digital_input_stamper; mod eeprom; mod pounder; diff --git a/src/lib.rs b/src/lib.rs index ab2623d..c252f37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,13 +6,3 @@ extern crate log; pub mod hardware; pub mod server; - -// The number of ticks in the ADC sampling timer. The timer runs at 100MHz, so the step size is -// equal to 10ns per tick. -// Currently, the sample rate is equal to: Fsample = 100/256 MHz = 390.625 KHz -pub const ADC_SAMPLE_TICKS_LOG2: u8 = 8; -pub const ADC_SAMPLE_TICKS: u16 = 1 << ADC_SAMPLE_TICKS_LOG2; - -// The desired ADC sample processing buffer size. -pub const SAMPLE_BUFFER_SIZE_LOG2: u8 = 3; -pub const SAMPLE_BUFFER_SIZE: usize = 1 << SAMPLE_BUFFER_SIZE_LOG2; From d32378e6c41a81ea73e816e89108644d59bb5dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 4 Feb 2021 12:48:58 +0100 Subject: [PATCH 13/48] lockin-external: ignore timestamps related to capture overflows --- src/bin/lockin-external.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index ae25d15..a36e5d6 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -113,7 +113,7 @@ const APP: () = { .resources .timestamper .latest_timestamp() - .unwrap_or_else(|t| t) // Ignore timer capture overflows. + .unwrap_or(None) // Ignore data from timer capture overflows. .map(|t| t as i32); let (pll_phase, pll_frequency) = c.resources.pll.update( timestamp, From 8314844aeb59cfb7e47670e0946024f4177540bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 4 Feb 2021 13:36:24 +0100 Subject: [PATCH 14/48] pounder: moved SAMPLE_BUFFER_SIZE --- src/hardware/pounder/timestamp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hardware/pounder/timestamp.rs b/src/hardware/pounder/timestamp.rs index 7e39a94..0c06192 100644 --- a/src/hardware/pounder/timestamp.rs +++ b/src/hardware/pounder/timestamp.rs @@ -26,7 +26,7 @@ use stm32h7xx_hal as hal; use hal::dma::{dma::DmaConfig, PeripheralToMemory, Transfer}; -use crate::{hardware::timers, SAMPLE_BUFFER_SIZE}; +use crate::hardware::{design_parameters::SAMPLE_BUFFER_SIZE, timers}; // Three buffers are required for double buffered mode - 2 are owned by the DMA stream and 1 is the // working data provided to the application. These buffers must exist in a DMA-accessible memory From 473bdaa9bcd903a55fb278ba0a250fce6122d712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 4 Feb 2021 15:21:05 +0100 Subject: [PATCH 15/48] iir_int: use f64 for extreme filters --- dsp/src/iir_int.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dsp/src/iir_int.rs b/dsp/src/iir_int.rs index daff43c..c0a17ad 100644 --- a/dsp/src/iir_int.rs +++ b/dsp/src/iir_int.rs @@ -1,4 +1,4 @@ -use core::f32::consts::PI; +use core::f64::consts::PI; use serde::{Deserialize, Serialize}; /// Generic vector for integer IIR filter. @@ -19,7 +19,7 @@ impl Vec5 { /// /// # Returns /// 2nd-order IIR filter coefficients in the form [b0,b1,b2,a1,a2]. a0 is set to -1. - pub fn lowpass(f: f32, q: f32, k: f32) -> Self { + pub fn lowpass(f: f64, q: f64, k: f64) -> Self { // 3rd order Taylor approximation of sin and cos. let f = f * 2. * PI; let f2 = f * f * 0.5; @@ -27,7 +27,7 @@ impl Vec5 { let fsin = f * (1. - f2 / 3.); let alpha = fsin / (2. * q); // IIR uses Q2.30 fixed point - let a0 = (1. + alpha) / (1 << IIR::SHIFT) as f32; + let a0 = (1. + alpha) / (1 << IIR::SHIFT) as f64; let b0 = (k / 2. * (1. - fcos) / a0) as _; let a1 = (2. * fcos / a0) as _; let a2 = ((alpha - 1.) / a0) as _; @@ -97,7 +97,7 @@ mod test { #[test] fn lowpass_gen() { - let ba = Vec5::lowpass(1e-3, 1. / 2f32.sqrt(), 2.); + let ba = Vec5::lowpass(1e-5, 1. / 2f64.sqrt(), 2e5); println!("{:?}", ba.0); } } From 2d492055f30144396b6214e6d44b4e4b75aba473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 4 Feb 2021 15:42:29 +0100 Subject: [PATCH 16/48] pounder stamper: overflow at u32 boundary --- src/hardware/configuration.rs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index 0b4b156..fe36f08 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -759,23 +759,13 @@ pub fn setup( // Pounder is configured to generate a 500MHz reference clock, so a 125MHz sync-clock is // output. As a result, dividing the 125MHz sync-clk provides a 31.25MHz tick rate for // the timestamp timer. 31.25MHz corresponds with a 32ns tick rate. + // This is less than fCK_INT/3 of the timer as required for oversampling the trigger. timestamp_timer.set_external_clock(timers::Prescaler::Div4); timestamp_timer.start(); - // We want the pounder timestamp timer to overflow once per batch. - let tick_ratio = { - let sync_clk_mhz: f32 = design_parameters::DDS_SYSTEM_CLK.0 - as f32 - / design_parameters::DDS_SYNC_CLK_DIV as f32; - sync_clk_mhz / design_parameters::TIMER_FREQUENCY.0 as f32 - }; - - let period = (tick_ratio - * design_parameters::ADC_SAMPLE_TICKS as f32 - * design_parameters::SAMPLE_BUFFER_SIZE as f32) - as u32 - / 4; - timestamp_timer.set_period_ticks((period - 1).try_into().unwrap()); + // Set the timer to wrap at the u32 boundary to meet the PLL periodicity. + // Scale and wrap before or after the PLL. + timestamp_timer.set_period_ticks(u32::MAX); let tim8_channels = timestamp_timer.channels(); pounder::timestamp::Timestamper::new( From f19988a1bdb7102a0f6669519f1047ea1fe55576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 4 Feb 2021 15:42:45 +0100 Subject: [PATCH 17/48] up the sample rate --- src/hardware/design_parameters.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hardware/design_parameters.rs b/src/hardware/design_parameters.rs index 9951ee5..ddc0614 100644 --- a/src/hardware/design_parameters.rs +++ b/src/hardware/design_parameters.rs @@ -42,8 +42,8 @@ pub const DDS_SYNC_CLK_DIV: u8 = 4; // The number of ticks in the ADC sampling timer. The timer runs at 100MHz, so the step size is // equal to 10ns per tick. -// Currently, the sample rate is equal to: Fsample = 100/256 MHz = 390.625 KHz -pub const ADC_SAMPLE_TICKS_LOG2: u8 = 8; +// Currently, the sample rate is equal to: Fsample = 100/128 MHz ~ 800 KHz +pub const ADC_SAMPLE_TICKS_LOG2: u8 = 7; pub const ADC_SAMPLE_TICKS: u16 = 1 << ADC_SAMPLE_TICKS_LOG2; // The desired ADC sample processing buffer size. From 0343e5d8abadca622a89af3acc9f624b94beb37b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 4 Feb 2021 16:51:34 +0100 Subject: [PATCH 18/48] pounder timer is u16 --- src/hardware/configuration.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index fe36f08..d7a07ed 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -1,9 +1,6 @@ ///! Stabilizer hardware configuration ///! ///! This file contains all of the hardware-specific configuration of Stabilizer. -#[cfg(feature = "pounder_v1_1")] -use core::convert::TryInto; - use smoltcp::{iface::Routes, wire::Ipv4Address}; use stm32h7xx_hal::{ @@ -763,9 +760,9 @@ pub fn setup( timestamp_timer.set_external_clock(timers::Prescaler::Div4); timestamp_timer.start(); - // Set the timer to wrap at the u32 boundary to meet the PLL periodicity. + // Set the timer to wrap at the u16 boundary to meet the PLL periodicity. // Scale and wrap before or after the PLL. - timestamp_timer.set_period_ticks(u32::MAX); + timestamp_timer.set_period_ticks(u16::MAX); let tim8_channels = timestamp_timer.channels(); pounder::timestamp::Timestamper::new( From 47d8a74524a3d100c2865e3ab4c68db891607628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 4 Feb 2021 17:01:18 +0100 Subject: [PATCH 19/48] ci: simplify nightly --- .github/bors.toml | 2 +- .github/workflows/ci.yml | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/bors.toml b/.github/bors.toml index 7168739..722246d 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -3,5 +3,5 @@ delete_merged_branches = true status = [ "style", "test (stable)", - "compile (stable, false)", + "compile (stable)", ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 851ef53..f4b66b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,22 +35,18 @@ jobs: compile: runs-on: ubuntu-latest - continue-on-error: ${{ matrix.optional }} + continue-on-error: ${{ matrix.toolchain == 'nightly' }} strategy: matrix: toolchain: [stable] features: [''] - optional: [false] include: - toolchain: beta features: '' - optional: false - toolchain: stable features: pounder_v1_1 - optional: false - toolchain: nightly features: nightly - optional: true steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 From deed11f11027f3abd0156057eb14e07ffe79f77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 5 Feb 2021 18:59:22 +0100 Subject: [PATCH 20/48] lockin-external: simplify --- src/bin/lockin-external.rs | 216 +++---------------------------------- 1 file changed, 16 insertions(+), 200 deletions(-) diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index a36e5d6..337ae99 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -4,28 +4,13 @@ use stm32h7xx_hal as hal; -#[macro_use] -extern crate log; +use stabilizer::{hardware, hardware::design_parameters}; -use rtic::cyccnt::{Instant, U32Ext}; - -use heapless::{consts::*, String}; - -use stabilizer::{hardware, hardware::design_parameters, server}; - -use dsp::{iir, iir_int, lockin::Lockin, rpll::RPLL, Accu}; +use dsp::{iir_int, lockin::Lockin, rpll::RPLL, Accu}; use hardware::{ Adc0Input, Adc1Input, Dac0Output, Dac1Output, InputStamper, AFE0, AFE1, }; -const SCALE: f32 = i16::MAX as _; - -const TCP_RX_BUFFER_SIZE: usize = 8192; -const TCP_TX_BUFFER_SIZE: usize = 8192; - -// The number of cascaded IIR biquads per channel. Select 1 or 2! -const IIR_CASCADE_LENGTH: usize = 1; - #[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] const APP: () = { struct Resources { @@ -34,12 +19,6 @@ const APP: () = { dacs: (Dac0Output, Dac1Output), net_interface: hardware::Ethernet, - // Format: iir_state[ch][cascade-no][coeff] - #[init([[iir::Vec5([0.; 5]); IIR_CASCADE_LENGTH]; 2])] - iir_state: [[iir::Vec5; IIR_CASCADE_LENGTH]; 2], - #[init([[iir::IIR::new(1./(1 << 16) as f32, -SCALE, SCALE); IIR_CASCADE_LENGTH]; 2])] - iir_ch: [[iir::IIR; IIR_CASCADE_LENGTH]; 2], - timestamper: InputStamper, pll: RPLL, lockin: Lockin, @@ -93,7 +72,7 @@ const APP: () = { /// This is an implementation of a externally (DI0) referenced PLL lockin on the ADC0 signal. /// It outputs either I/Q or power/phase on DAC0/DAC1. Data is normalized to full scale. /// PLL bandwidth, filter bandwidth, slope, and x/y or power/phase post-filters are available. - #[task(binds=DMA1_STR4, resources=[adcs, dacs, iir_state, iir_ch, lockin, timestamper, pll], priority=2)] + #[task(binds=DMA1_STR4, resources=[adcs, dacs, lockin, timestamper, pll], priority=2)] fn process(c: process::Context) { let adc_samples = [ c.resources.adcs.0.acquire_buffer(), @@ -105,8 +84,6 @@ const APP: () = { c.resources.dacs.1.acquire_buffer(), ]; - let iir_ch = c.resources.iir_ch; - let iir_state = c.resources.iir_state; let lockin = c.resources.lockin; let timestamp = c @@ -117,8 +94,8 @@ const APP: () = { .map(|t| t as i32); let (pll_phase, pll_frequency) = c.resources.pll.update( timestamp, - 22, // frequency settling time (log2 counter cycles), TODO: expose - 22, // phase settling time, TODO: expose + 21, // frequency settling time (log2 counter cycles), TODO: expose + 21, // phase settling time, TODO: expose ); // Harmonic index of the LO: -1 to _de_modulate the fundamental (complex conjugate) @@ -144,187 +121,26 @@ const APP: () = { .last() .unwrap(); - // convert i/q to power/phase, - let power_phase = true; // TODO: expose - - let mut output = if power_phase { + let conf = "frequency_discriminator"; + let output = match conf { // Convert from IQ to power and phase. - [output.abs_sqr() as _, output.arg() as _] - } else { - [output.0 as _, output.1 as _] + "power_phase" => [output.abs_sqr(), output.arg()], + "frequency_discriminator" => [pll_frequency as i32, output.arg()], + _ => [output.0, output.1], }; - // Filter power and phase through IIR filters. - // Note: Normalization to be done in filters. Phase will wrap happily. - for j in 0..iir_state[0].len() { - for k in 0..output.len() { - output[k] = - iir_ch[k][j].update(&mut iir_state[k][j], output[k]); - } - } - - // Note(unsafe): range clipping to i16 is ensured by IIR filters above. // Convert to DAC data. for i in 0..dac_samples[0].len() { - unsafe { - dac_samples[0][i] = - output[0].to_int_unchecked::() as u16 ^ 0x8000; - dac_samples[1][i] = - output[1].to_int_unchecked::() as u16 ^ 0x8000; - } + dac_samples[0][i] = (output[0] >> 16) as u16 ^ 0x8000; + dac_samples[1][i] = (output[1] >> 16) as u16 ^ 0x8000; } } - #[idle(resources=[net_interface, iir_state, iir_ch, afes])] - fn idle(mut c: idle::Context) -> ! { - let mut socket_set_entries: [_; 8] = Default::default(); - let mut sockets = - smoltcp::socket::SocketSet::new(&mut socket_set_entries[..]); - - let mut rx_storage = [0; TCP_RX_BUFFER_SIZE]; - let mut tx_storage = [0; TCP_TX_BUFFER_SIZE]; - let tcp_handle = { - let tcp_rx_buffer = - smoltcp::socket::TcpSocketBuffer::new(&mut rx_storage[..]); - let tcp_tx_buffer = - smoltcp::socket::TcpSocketBuffer::new(&mut tx_storage[..]); - let tcp_socket = - smoltcp::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); - sockets.add(tcp_socket) - }; - - let mut server = server::Server::new(); - - let mut time = 0u32; - let mut next_ms = Instant::now(); - - // TODO: Replace with reference to CPU clock from CCDR. - next_ms += 400_000.cycles(); - + #[idle(resources=[afes])] + fn idle(_: idle::Context) -> ! { loop { - let tick = Instant::now() > next_ms; - - if tick { - next_ms += 400_000.cycles(); - time += 1; - } - - { - let socket = - &mut *sockets.get::(tcp_handle); - if socket.state() == smoltcp::socket::TcpState::CloseWait { - socket.close(); - } else if !(socket.is_open() || socket.is_listening()) { - socket - .listen(1235) - .unwrap_or_else(|e| warn!("TCP listen error: {:?}", e)); - } else { - server.poll(socket, |req| { - info!("Got request: {:?}", req); - stabilizer::route_request!(req, - readable_attributes: [ - "stabilizer/iir/state": (|| { - let state = c.resources.iir_state.lock(|iir_state| - server::Status { - t: time, - x0: iir_state[0][0].0[0], - y0: iir_state[0][0].0[2], - x1: iir_state[1][0].0[0], - y1: iir_state[1][0].0[2], - }); - - Ok::(state) - }), - // "_b" means cascades 2nd IIR - "stabilizer/iir_b/state": (|| { let state = c.resources.iir_state.lock(|iir_state| - server::Status { - t: time, - x0: iir_state[0][IIR_CASCADE_LENGTH-1].0[0], - y0: iir_state[0][IIR_CASCADE_LENGTH-1].0[2], - x1: iir_state[1][IIR_CASCADE_LENGTH-1].0[0], - y1: iir_state[1][IIR_CASCADE_LENGTH-1].0[2], - }); - - Ok::(state) - }), - "stabilizer/afe0/gain": (|| c.resources.afes.0.get_gain()), - "stabilizer/afe1/gain": (|| c.resources.afes.1.get_gain()) - ], - - modifiable_attributes: [ - "stabilizer/iir0/state": server::IirRequest, (|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][0] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/iir1/state": server::IirRequest, (|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][0] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/iir_b0/state": server::IirRequest, (|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][IIR_CASCADE_LENGTH-1] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/iir_b1/state": server::IirRequest,(|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][IIR_CASCADE_LENGTH-1] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/afe0/gain": hardware::AfeGain, (|gain| { - c.resources.afes.0.set_gain(gain); - Ok::<(), ()>(()) - }), - "stabilizer/afe1/gain": hardware::AfeGain, (|gain| { - c.resources.afes.1.set_gain(gain); - Ok::<(), ()>(()) - }) - ] - ) - }); - } - } - - let sleep = match c.resources.net_interface.poll( - &mut sockets, - smoltcp::time::Instant::from_millis(time as i64), - ) { - Ok(changed) => !changed, - Err(smoltcp::Error::Unrecognized) => true, - Err(e) => { - info!("iface poll error: {:?}", e); - true - } - }; - - if sleep { - cortex_m::asm::wfi(); - } + // TODO: Implement network interface. + cortex_m::asm::wfi(); } } From 9b6ab29eb7a9a0141fc7ba18c6c5d4e52fe5d07b Mon Sep 17 00:00:00 2001 From: Harry Ho Date: Mon, 8 Feb 2021 09:56:42 +0800 Subject: [PATCH 21/48] update cargosha256 --- cargosha256-dual-iir.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cargosha256-dual-iir.nix b/cargosha256-dual-iir.nix index 8775167..4276c72 100644 --- a/cargosha256-dual-iir.nix +++ b/cargosha256-dual-iir.nix @@ -1 +1 @@ -"0ysy8fg6kbblhmjyavq6pg77n21fcygwc0hvidmg2yywkhgdi348" +"0y8mfmy3b8ma4namgkff4rfrshybskaiyh1gja30hbn3l77z43j6" From 1b46f081c1362fe7ba5230899921bac9c989bd66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 8 Feb 2021 11:26:58 +0100 Subject: [PATCH 22/48] better formatting --- src/bin/lockin-external.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index 337ae99..74ca836 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -100,7 +100,8 @@ const APP: () = { // Harmonic index of the LO: -1 to _de_modulate the fundamental (complex conjugate) let harmonic: i32 = -1; // TODO: expose - // Demodulation LO phase offset + + // Demodulation LO phase offset let phase_offset: i32 = 0; // TODO: expose let sample_frequency = ((pll_frequency From 611bd3e855b9e6dd6ef68a342f0ea8002bd93075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 8 Feb 2021 15:24:52 +0100 Subject: [PATCH 23/48] ad9959/pounder: tweaks * make a trait public * use self-test * this hasn't been tested --- src/hardware/configuration.rs | 4 +++- src/hardware/mod.rs | 2 +- src/hardware/pounder/mod.rs | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index d7a07ed..52db011 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -597,7 +597,7 @@ pub fn setup( let ref_clk: hal::time::Hertz = design_parameters::DDS_REF_CLK.into(); - let ad9959 = ad9959::Ad9959::new( + let mut ad9959 = ad9959::Ad9959::new( qspi_interface, reset_pin, &mut io_update, @@ -608,6 +608,8 @@ pub fn setup( ) .unwrap(); + ad9959.self_test().unwrap(); + // Return IO_Update gpiog.pg7 = io_update.into_analog(); diff --git a/src/hardware/mod.rs b/src/hardware/mod.rs index b54b6f7..41bca1b 100644 --- a/src/hardware/mod.rs +++ b/src/hardware/mod.rs @@ -14,7 +14,7 @@ mod dac; pub mod design_parameters; mod digital_input_stamper; mod eeprom; -mod pounder; +pub mod pounder; mod timers; pub use adc::{Adc0Input, Adc1Input}; diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index 3a85937..0497a37 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -mod attenuators; +pub mod attenuators; mod dds_output; pub mod hrtimer; mod rf_power; From 31781a9d0e12c06474fdb0eaaccc30fc034f9a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 9 Feb 2021 12:17:48 +0100 Subject: [PATCH 24/48] iir_int: rounding bias --- dsp/src/iir_int.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dsp/src/iir_int.rs b/dsp/src/iir_int.rs index c0a17ad..f0ce157 100644 --- a/dsp/src/iir_int.rs +++ b/dsp/src/iir_int.rs @@ -28,9 +28,9 @@ impl Vec5 { let alpha = fsin / (2. * q); // IIR uses Q2.30 fixed point let a0 = (1. + alpha) / (1 << IIR::SHIFT) as f64; - let b0 = (k / 2. * (1. - fcos) / a0) as _; - let a1 = (2. * fcos / a0) as _; - let a2 = ((alpha - 1.) / a0) as _; + let b0 = (k / 2. * (1. - fcos) / a0 + 0.5) as _; + let a1 = (2. * fcos / a0 + 0.5) as _; + let a2 = ((alpha - 1.) / a0 + 0.5) as _; Self([b0, 2 * b0, b0, a1, a2]) } @@ -97,7 +97,7 @@ mod test { #[test] fn lowpass_gen() { - let ba = Vec5::lowpass(1e-5, 1. / 2f64.sqrt(), 2e5); + let ba = Vec5::lowpass(1e-5, 1. / 2f64.sqrt(), 2.); println!("{:?}", ba.0); } } From 2e358dea263b82c2a5d4f9b7d1236190666c1f37 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Tue, 9 Feb 2021 14:36:50 +0100 Subject: [PATCH 25/48] Adding support for input capture prefilter configuration --- src/hardware/digital_input_stamper.rs | 8 ++++---- src/hardware/timers.rs | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/hardware/digital_input_stamper.rs b/src/hardware/digital_input_stamper.rs index cc764f5..baddeba 100644 --- a/src/hardware/digital_input_stamper.rs +++ b/src/hardware/digital_input_stamper.rs @@ -44,12 +44,12 @@ impl InputStamper { ) -> Self { // Utilize the TIM5 CH4 as an input capture channel - use TI4 (the DI0 input trigger) as the // capture source. - let input_capture = + let mut input_capture = timer_channel.into_input_capture(timers::tim5::CaptureSource4::TI4); - // FIXME: hack in de-glitching filter - let regs = unsafe { &*hal::stm32::TIM5::ptr() }; - regs.ccmr2_input().modify(|_, w| w.ic4f().bits(0b0011)); + // Do not prescale the input capture signal - require 8 consecutive samples to record an + // incoming event - this prevents spurious glitches from triggering captures. + input_capture.configure_filter(timers::InputFilter::Div1N8); Self { capture_channel: input_capture, diff --git a/src/hardware/timers.rs b/src/hardware/timers.rs index e2cbbc8..fe90677 100644 --- a/src/hardware/timers.rs +++ b/src/hardware/timers.rs @@ -48,6 +48,13 @@ pub enum SlaveMode { Trigger = 0b0110, } +/// Optional input capture preconditioning filter configurations. +#[allow(dead_code)] +pub enum InputFilter { + Div1N1 = 0b0000, + Div1N8 = 0b0011, +} + macro_rules! timer_channels { ($name:ident, $TY:ident, $size:ty) => { paste::paste! { @@ -334,6 +341,16 @@ macro_rules! timer_channels { let regs = unsafe { &*<$TY>::ptr() }; regs.sr.read().[< cc $index of >]().bit_is_set() } + + /// Configure the input capture input pre-filter. + /// + /// # Args + /// * `filter` - The desired input filter stage configuration. Defaults to disabled. + #[allow(dead_code)] + pub fn configure_filter(&mut self, filter: super::InputFilter) { + let regs = unsafe { &*<$TY>::ptr() }; + regs.[< $ccmrx _input >]().modify(|_, w| w.[< ic $index f >]().bits(filter as u8)); + } } // Note(unsafe): This manually implements DMA support for input-capture channels. This From 724768a72e9ac423c8ce10ae9bf6230e64c09cc7 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Tue, 9 Feb 2021 14:37:49 +0100 Subject: [PATCH 26/48] Adding safety docs --- src/hardware/timers.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hardware/timers.rs b/src/hardware/timers.rs index fe90677..78d27b6 100644 --- a/src/hardware/timers.rs +++ b/src/hardware/timers.rs @@ -348,6 +348,8 @@ macro_rules! timer_channels { /// * `filter` - The desired input filter stage configuration. Defaults to disabled. #[allow(dead_code)] pub fn configure_filter(&mut self, filter: super::InputFilter) { + // Note(unsafe): This channel owns all access to the specific timer channel. + // Only atomic operations on completed on the timer registers. let regs = unsafe { &*<$TY>::ptr() }; regs.[< $ccmrx _input >]().modify(|_, w| w.[< ic $index f >]().bits(filter as u8)); } From 208ba8379aff34faee818135959728308ebaaab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 9 Feb 2021 18:30:50 +0100 Subject: [PATCH 27/48] dsp, lockin: use cascaded 1st order lowpasses --- Cargo.lock | 1 + dsp/Cargo.toml | 1 + dsp/src/lib.rs | 1 + dsp/src/lockin.rs | 32 +++++++++++---------------- dsp/src/lowpass.rs | 44 ++++++++++++++++++++++++++++++++++++++ src/bin/lockin-external.rs | 6 ++---- src/bin/lockin-internal.rs | 14 ++++++------ 7 files changed, 68 insertions(+), 31 deletions(-) create mode 100644 dsp/src/lowpass.rs diff --git a/Cargo.lock b/Cargo.lock index ed15a58..17030d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -353,6 +353,7 @@ name = "dsp" version = "0.1.0" dependencies = [ "criterion", + "generic-array 0.14.4", "libm", "ndarray", "rand", diff --git a/dsp/Cargo.toml b/dsp/Cargo.toml index de96d81..8e07060 100644 --- a/dsp/Cargo.toml +++ b/dsp/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" [dependencies] libm = "0.2.1" serde = { version = "1.0", features = ["derive"], default-features = false } +generic-array = "0.14" [dev-dependencies] criterion = "0.3" diff --git a/dsp/src/lib.rs b/dsp/src/lib.rs index f42e7c7..31ff680 100644 --- a/dsp/src/lib.rs +++ b/dsp/src/lib.rs @@ -87,6 +87,7 @@ mod cossin; pub mod iir; pub mod iir_int; pub mod lockin; +pub mod lowpass; pub mod pll; pub mod rpll; pub mod unwrap; diff --git a/dsp/src/lockin.rs b/dsp/src/lockin.rs index ea80836..6abd277 100644 --- a/dsp/src/lockin.rs +++ b/dsp/src/lockin.rs @@ -1,21 +1,19 @@ -use super::{ - iir_int::{Vec5, IIR}, - Complex, -}; -use serde::{Deserialize, Serialize}; +use super::{lowpass::Lowpass, Complex}; +use generic_array::typenum::U3; -#[derive(Copy, Clone, Default, Deserialize, Serialize)] +#[derive(Clone, Default)] pub struct Lockin { - iir: IIR, - state: [Vec5; 2], + state: [Lowpass; 2], + k: u32, } impl Lockin { /// Create a new Lockin with given IIR coefficients. - pub fn new(ba: Vec5) -> Self { + pub fn new(k: u32) -> Self { + let lp = Lowpass::default(); Self { - iir: IIR { ba }, - state: [Vec5::default(); 2], + state: [lp.clone(), lp.clone()], + k, } } @@ -28,14 +26,10 @@ impl Lockin { // return IQ (in-phase and quadrature) data. // Note: 32x32 -> 64 bit multiplications are pretty much free. Complex( - self.iir.update( - &mut self.state[0], - ((sample as i64 * lo.0 as i64) >> 32) as _, - ), - self.iir.update( - &mut self.state[1], - ((sample as i64 * lo.1 as i64) >> 32) as _, - ), + self.state[0] + .update(((sample as i64 * lo.0 as i64) >> 32) as _, self.k), + self.state[1] + .update(((sample as i64 * lo.1 as i64) >> 32) as _, self.k), ) } } diff --git a/dsp/src/lowpass.rs b/dsp/src/lowpass.rs new file mode 100644 index 0000000..50ca604 --- /dev/null +++ b/dsp/src/lowpass.rs @@ -0,0 +1,44 @@ +use generic_array::{ArrayLength, GenericArray}; + +/// Arbitrary order, high dynamic range, wide coefficient range, +/// lowpass filter implementation. +/// +/// Type argument N is the filter order + 1. +#[derive(Clone, Default)] +pub struct Lowpass> { + // IIR state storage + xy: GenericArray, +} + +impl> Lowpass { + /// Update the filter with a new sample. + /// + /// # Args + /// * `x`: Input data + /// * `k`: Cutoff, `u32::MAX` being Nyquist + /// + /// # Returns + /// Filtered output y + pub fn update(&mut self, x: i32, k: u32) -> i32 { + let mut x1 = self.xy[0]; + self.xy[0] = x; + let mut x0 = x; + + // This is an unrolled and optimized first-order IIR loop + // that works for all possible time constants. + for y1 in self.xy[1..].iter_mut() { + // Optimized first order lowpass expression + // Note the zero at Nyquist + let mut y0 = + ((x0 >> 1) as i64 + (x1 >> 1) as i64 - *y1 as i64) * k as i64; + y0 += (*y1 as i64) << 32; + y0 += 1i64 << 31; // Half-up rounding bias + + // Store and advance + x0 = (y0 >> 32) as i32; + x1 = *y1; + *y1 = x0; + } + x0 + } +} diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index 74ca836..ba576e9 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -6,7 +6,7 @@ use stm32h7xx_hal as hal; use stabilizer::{hardware, hardware::design_parameters}; -use dsp::{iir_int, lockin::Lockin, rpll::RPLL, Accu}; +use dsp::{lockin::Lockin, rpll::RPLL, Accu}; use hardware::{ Adc0Input, Adc1Input, Dac0Output, Dac1Output, InputStamper, AFE0, AFE1, }; @@ -34,9 +34,7 @@ const APP: () = { + design_parameters::SAMPLE_BUFFER_SIZE_LOG2, ); - let lockin = Lockin::new( - iir_int::Vec5::lowpass(1e-3, 0.707, 2.), // TODO: expose - ); + let lockin = Lockin::new(1 << 22); // Enable ADC/DAC events stabilizer.adcs.0.start(); diff --git a/src/bin/lockin-internal.rs b/src/bin/lockin-internal.rs index cadf189..e25c9fb 100644 --- a/src/bin/lockin-internal.rs +++ b/src/bin/lockin-internal.rs @@ -2,7 +2,7 @@ #![no_std] #![no_main] -use dsp::{iir_int, lockin::Lockin, Accu}; +use dsp::{lockin::Lockin, Accu}; use hardware::{Adc1Input, Dac0Output, Dac1Output, AFE0, AFE1}; use stabilizer::{hardware, hardware::design_parameters}; @@ -17,7 +17,7 @@ const DAC_SEQUENCE: [i16; design_parameters::SAMPLE_BUFFER_SIZE] = const APP: () = { struct Resources { afes: (AFE0, AFE1), - adc1: Adc1Input, + adc: Adc1Input, dacs: (Dac0Output, Dac1Output), lockin: Lockin, @@ -28,9 +28,7 @@ const APP: () = { // Configure the microcontroller let (mut stabilizer, _pounder) = hardware::setup(c.core, c.device); - let lockin = Lockin::new( - iir_int::Vec5::lowpass(1e-3, 0.707, 2.), // TODO: expose - ); + let lockin = Lockin::new(1 << 22); // TODO: expose // Enable ADC/DAC events stabilizer.adcs.1.start(); @@ -43,7 +41,7 @@ const APP: () = { init::LateResources { lockin, afes: stabilizer.afes, - adc1: stabilizer.adcs.1, + adc: stabilizer.adcs.1, dacs: stabilizer.dacs, } } @@ -66,10 +64,10 @@ const APP: () = { /// the same time bounds, meeting one also means the other is also met. /// /// TODO: Document - #[task(binds=DMA1_STR4, resources=[adc1, dacs, lockin], priority=2)] + #[task(binds=DMA1_STR4, resources=[adc, dacs, lockin], priority=2)] fn process(c: process::Context) { let lockin = c.resources.lockin; - let adc_samples = c.resources.adc1.acquire_buffer(); + let adc_samples = c.resources.adc.acquire_buffer(); let dac_samples = [ c.resources.dacs.0.acquire_buffer(), c.resources.dacs.1.acquire_buffer(), From 30c2c2aac2049f319a49f88057e5a87528df84b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 10 Feb 2021 09:46:49 +0100 Subject: [PATCH 28/48] lowpass: i32, no multiplies --- dsp/src/lockin.rs | 4 ++-- dsp/src/lowpass.rs | 24 ++++++++---------------- src/bin/lockin-external.rs | 2 +- src/bin/lockin-internal.rs | 2 +- 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/dsp/src/lockin.rs b/dsp/src/lockin.rs index 6abd277..d78c01b 100644 --- a/dsp/src/lockin.rs +++ b/dsp/src/lockin.rs @@ -4,12 +4,12 @@ use generic_array::typenum::U3; #[derive(Clone, Default)] pub struct Lockin { state: [Lowpass; 2], - k: u32, + k: u8, } impl Lockin { /// Create a new Lockin with given IIR coefficients. - pub fn new(k: u32) -> Self { + pub fn new(k: u8) -> Self { let lp = Lowpass::default(); Self { state: [lp.clone(), lp.clone()], diff --git a/dsp/src/lowpass.rs b/dsp/src/lowpass.rs index 50ca604..e3a1353 100644 --- a/dsp/src/lowpass.rs +++ b/dsp/src/lowpass.rs @@ -15,27 +15,19 @@ impl> Lowpass { /// /// # Args /// * `x`: Input data - /// * `k`: Cutoff, `u32::MAX` being Nyquist + /// * `k`: Log2 time constant /// - /// # Returns + /// # Return /// Filtered output y - pub fn update(&mut self, x: i32, k: u32) -> i32 { - let mut x1 = self.xy[0]; - self.xy[0] = x; - let mut x0 = x; - + pub fn update(&mut self, x: i32, k: u8) -> i32 { // This is an unrolled and optimized first-order IIR loop // that works for all possible time constants. + // Note the zero(s) at Nyquist and the benign overflow (DF-I). + let mut x0 = x; + let mut x1 = self.xy[0]; + self.xy[0] = x; for y1 in self.xy[1..].iter_mut() { - // Optimized first order lowpass expression - // Note the zero at Nyquist - let mut y0 = - ((x0 >> 1) as i64 + (x1 >> 1) as i64 - *y1 as i64) * k as i64; - y0 += (*y1 as i64) << 32; - y0 += 1i64 << 31; // Half-up rounding bias - - // Store and advance - x0 = (y0 >> 32) as i32; + x0 = *y1 + (((x0 >> 1) + (x1 >> 1) - *y1 + (1 << k - 1)) >> k); x1 = *y1; *y1 = x0; } diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index ba576e9..fad27ec 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -34,7 +34,7 @@ const APP: () = { + design_parameters::SAMPLE_BUFFER_SIZE_LOG2, ); - let lockin = Lockin::new(1 << 22); + let lockin = Lockin::new(10); // Enable ADC/DAC events stabilizer.adcs.0.start(); diff --git a/src/bin/lockin-internal.rs b/src/bin/lockin-internal.rs index e25c9fb..287815a 100644 --- a/src/bin/lockin-internal.rs +++ b/src/bin/lockin-internal.rs @@ -28,7 +28,7 @@ const APP: () = { // Configure the microcontroller let (mut stabilizer, _pounder) = hardware::setup(c.core, c.device); - let lockin = Lockin::new(1 << 22); // TODO: expose + let lockin = Lockin::new(10); // TODO: expose // Enable ADC/DAC events stabilizer.adcs.1.start(); From 13b47556fd3f62e1356a4d0c64c43aebff823234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 10 Feb 2021 13:27:56 +0100 Subject: [PATCH 29/48] lowpass: clippy --- dsp/src/lockin.rs | 2 +- dsp/src/lowpass.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dsp/src/lockin.rs b/dsp/src/lockin.rs index d78c01b..75c7b97 100644 --- a/dsp/src/lockin.rs +++ b/dsp/src/lockin.rs @@ -12,7 +12,7 @@ impl Lockin { pub fn new(k: u8) -> Self { let lp = Lowpass::default(); Self { - state: [lp.clone(), lp.clone()], + state: [lp.clone(), lp], k, } } diff --git a/dsp/src/lowpass.rs b/dsp/src/lowpass.rs index e3a1353..b85e2ed 100644 --- a/dsp/src/lowpass.rs +++ b/dsp/src/lowpass.rs @@ -27,7 +27,7 @@ impl> Lowpass { let mut x1 = self.xy[0]; self.xy[0] = x; for y1 in self.xy[1..].iter_mut() { - x0 = *y1 + (((x0 >> 1) + (x1 >> 1) - *y1 + (1 << k - 1)) >> k); + x0 = *y1 + (((x0 >> 1) + (x1 >> 1) - *y1 + (1 << (k - 1))) >> k); x1 = *y1; *y1 = x0; } From 8d68504026f33f6fb18481f7487489a541741a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 10 Feb 2021 13:31:41 +0100 Subject: [PATCH 30/48] lowpass: symmetric code --- dsp/src/lowpass.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsp/src/lowpass.rs b/dsp/src/lowpass.rs index b85e2ed..24832a8 100644 --- a/dsp/src/lowpass.rs +++ b/dsp/src/lowpass.rs @@ -25,7 +25,7 @@ impl> Lowpass { // Note the zero(s) at Nyquist and the benign overflow (DF-I). let mut x0 = x; let mut x1 = self.xy[0]; - self.xy[0] = x; + self.xy[0] = x0; for y1 in self.xy[1..].iter_mut() { x0 = *y1 + (((x0 >> 1) + (x1 >> 1) - *y1 + (1 << (k - 1))) >> k); x1 = *y1; From beeb43bf8b388e7261188c4d5fe30d18c041030b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 10 Feb 2021 13:44:10 +0100 Subject: [PATCH 31/48] lowpass: robustify --- dsp/src/lowpass.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dsp/src/lowpass.rs b/dsp/src/lowpass.rs index 24832a8..39abffb 100644 --- a/dsp/src/lowpass.rs +++ b/dsp/src/lowpass.rs @@ -15,19 +15,20 @@ impl> Lowpass { /// /// # Args /// * `x`: Input data - /// * `k`: Log2 time constant + /// * `k`: Log2 time constant, 1..32 /// /// # Return /// Filtered output y pub fn update(&mut self, x: i32, k: u8) -> i32 { + debug_assert!((1..32).contains(&k)); // This is an unrolled and optimized first-order IIR loop // that works for all possible time constants. - // Note the zero(s) at Nyquist and the benign overflow (DF-I). + // Note the zero(s) at Nyquist. let mut x0 = x; let mut x1 = self.xy[0]; self.xy[0] = x0; for y1 in self.xy[1..].iter_mut() { - x0 = *y1 + (((x0 >> 1) + (x1 >> 1) - *y1 + (1 << (k - 1))) >> k); + x0 = *y1 + (((x0 >> 2) + (x1 >> 2) - (*y1 >> 1) + (1 << (k - 1))) >> k); x1 = *y1; *y1 = x0; } From a144c099b2e08032c318ee93eb72d526ef3852bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 10 Feb 2021 14:10:28 +0100 Subject: [PATCH 32/48] lowpass: fmt --- dsp/src/lowpass.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dsp/src/lowpass.rs b/dsp/src/lowpass.rs index 39abffb..fb2eedf 100644 --- a/dsp/src/lowpass.rs +++ b/dsp/src/lowpass.rs @@ -28,7 +28,8 @@ impl> Lowpass { let mut x1 = self.xy[0]; self.xy[0] = x0; for y1 in self.xy[1..].iter_mut() { - x0 = *y1 + (((x0 >> 2) + (x1 >> 2) - (*y1 >> 1) + (1 << (k - 1))) >> k); + x0 = *y1 + + (((x0 >> 2) + (x1 >> 2) - (*y1 >> 1) + (1 << (k - 1))) >> k); x1 = *y1; *y1 = x0; } From 3ae0b710bc8e51996a1a28e8e95a83c472fae6cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 11 Feb 2021 14:30:05 +0100 Subject: [PATCH 33/48] lowpass: reimplement better --- dsp/src/lockin.rs | 17 ++++++----------- dsp/src/lowpass.rs | 29 +++++++++++++---------------- src/bin/lockin-external.rs | 9 ++++++--- src/bin/lockin-internal.rs | 24 +++++++++--------------- 4 files changed, 34 insertions(+), 45 deletions(-) diff --git a/dsp/src/lockin.rs b/dsp/src/lockin.rs index 75c7b97..7fc1e85 100644 --- a/dsp/src/lockin.rs +++ b/dsp/src/lockin.rs @@ -1,35 +1,30 @@ use super::{lowpass::Lowpass, Complex}; -use generic_array::typenum::U3; +use generic_array::typenum::U4; #[derive(Clone, Default)] pub struct Lockin { - state: [Lowpass; 2], - k: u8, + state: [Lowpass; 2], } impl Lockin { /// Create a new Lockin with given IIR coefficients. - pub fn new(k: u8) -> Self { + pub fn new() -> Self { let lp = Lowpass::default(); Self { state: [lp.clone(), lp], - k, } } /// Update the lockin with a sample taken at a given phase. - pub fn update(&mut self, sample: i32, phase: i32) -> Complex { + pub fn update(&mut self, sample: i16, phase: i32, k: u8) -> Complex { // Get the LO signal for demodulation. let lo = Complex::from_angle(phase); // Mix with the LO signal, filter with the IIR lowpass, // return IQ (in-phase and quadrature) data. - // Note: 32x32 -> 64 bit multiplications are pretty much free. Complex( - self.state[0] - .update(((sample as i64 * lo.0 as i64) >> 32) as _, self.k), - self.state[1] - .update(((sample as i64 * lo.1 as i64) >> 32) as _, self.k), + self.state[0].update((sample as i32 * (lo.0 >> 16)) >> 16, k), + self.state[1].update((sample as i32 * (lo.1 >> 16)) >> 16, k), ) } } diff --git a/dsp/src/lowpass.rs b/dsp/src/lowpass.rs index fb2eedf..5004923 100644 --- a/dsp/src/lowpass.rs +++ b/dsp/src/lowpass.rs @@ -1,38 +1,35 @@ use generic_array::{ArrayLength, GenericArray}; /// Arbitrary order, high dynamic range, wide coefficient range, -/// lowpass filter implementation. +/// lowpass filter implementation. DC gain is 1. /// -/// Type argument N is the filter order + 1. +/// Type argument N is the filter order. #[derive(Clone, Default)] pub struct Lowpass> { // IIR state storage - xy: GenericArray, + y: GenericArray, } impl> Lowpass { /// Update the filter with a new sample. /// /// # Args - /// * `x`: Input data - /// * `k`: Log2 time constant, 1..32 + /// * `x`: Input data, needs k bits headroom + /// * `k`: Log2 time constant, 0..31 /// /// # Return /// Filtered output y pub fn update(&mut self, x: i32, k: u8) -> i32 { - debug_assert!((1..32).contains(&k)); + debug_assert!(k & 31 == k); // This is an unrolled and optimized first-order IIR loop // that works for all possible time constants. - // Note the zero(s) at Nyquist. - let mut x0 = x; - let mut x1 = self.xy[0]; - self.xy[0] = x0; - for y1 in self.xy[1..].iter_mut() { - x0 = *y1 - + (((x0 >> 2) + (x1 >> 2) - (*y1 >> 1) + (1 << (k - 1))) >> k); - x1 = *y1; - *y1 = x0; + // Note DF-II and the zero(s) at Nyquist. + let mut x = x; + for y in self.y.iter_mut() { + let dy = x - (*y >> k); + *y += dy; + x = (*y - (dy >> 1)) >> k; } - x0 + x } } diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index fad27ec..5f7451e 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -34,7 +34,7 @@ const APP: () = { + design_parameters::SAMPLE_BUFFER_SIZE_LOG2, ); - let lockin = Lockin::new(10); + let lockin = Lockin::new(); // Enable ADC/DAC events stabilizer.adcs.0.start(); @@ -102,6 +102,9 @@ const APP: () = { // Demodulation LO phase offset let phase_offset: i32 = 0; // TODO: expose + // Log2 lowpass time constant + let time_constant: u8 = 8; // TODO: expose + let sample_frequency = ((pll_frequency // .wrapping_add(1 << design_parameters::SAMPLE_BUFFER_SIZE_LOG2 - 1) // half-up rounding bias >> design_parameters::SAMPLE_BUFFER_SIZE_LOG2) @@ -115,7 +118,7 @@ const APP: () = { .zip(Accu::new(sample_phase, sample_frequency)) // Convert to signed, MSB align the ADC sample. .map(|(&sample, phase)| { - lockin.update((sample as i16 as i32) << 16, phase) + lockin.update(sample as i16, phase, time_constant) }) .last() .unwrap(); @@ -125,7 +128,7 @@ const APP: () = { // Convert from IQ to power and phase. "power_phase" => [output.abs_sqr(), output.arg()], "frequency_discriminator" => [pll_frequency as i32, output.arg()], - _ => [output.0, output.1], + _ => [output.0 << 16, output.1 << 16], }; // Convert to DAC data. diff --git a/src/bin/lockin-internal.rs b/src/bin/lockin-internal.rs index 287815a..cfa3115 100644 --- a/src/bin/lockin-internal.rs +++ b/src/bin/lockin-internal.rs @@ -28,7 +28,7 @@ const APP: () = { // Configure the microcontroller let (mut stabilizer, _pounder) = hardware::setup(c.core, c.device); - let lockin = Lockin::new(10); // TODO: expose + let lockin = Lockin::new(); // Enable ADC/DAC events stabilizer.adcs.1.start(); @@ -85,10 +85,14 @@ const APP: () = { 1i32 << (32 - design_parameters::SAMPLE_BUFFER_SIZE_LOG2); // Harmonic index of the LO: -1 to _de_modulate the fundamental - let harmonic: i32 = -1; + let harmonic: i32 = -1; // TODO: expose // Demodulation LO phase offset - let phase_offset: i32 = (0.25 * i32::MAX as f32) as i32; + let phase_offset: i32 = (0.25 * i32::MAX as f32) as i32; // TODO: expose + + // Log2 lowpass time constant. + let time_constant: u8 = 8; + let sample_frequency = (pll_frequency as i32).wrapping_mul(harmonic); let sample_phase = phase_offset .wrapping_add((pll_phase as i32).wrapping_mul(harmonic)); @@ -99,24 +103,14 @@ const APP: () = { .zip(Accu::new(sample_phase, sample_frequency)) // Convert to signed, MSB align the ADC sample, update the Lockin (demodulate, filter) .map(|(&sample, phase)| { - lockin.update((sample as i16 as i32) << 16, phase) + lockin.update(sample as i16, phase, time_constant) }) // Decimate .last() .unwrap(); - // convert i/q to power/phase, - let power_phase = true; // TODO: expose - - let output = if power_phase { - // Convert from IQ to power and phase. - [output.abs_sqr(), output.arg()] - } else { - [output.0, output.1] - }; - for value in dac_samples[1].iter_mut() { - *value = (output[1] >> 16) as u16 ^ 0x8000; + *value = (output.arg() >> 16) as u16 ^ 0x8000; } } From b49f0a2eb952d2c53b5e5b6b2dcc17d2bb3b78b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 11 Feb 2021 18:14:28 +0100 Subject: [PATCH 34/48] complex: log2, update bins --- dsp/src/complex.rs | 42 ++++++++++++++++++++++++++++++-------- dsp/src/lowpass.rs | 6 +++--- src/bin/lockin-external.rs | 4 ++-- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/dsp/src/complex.rs b/dsp/src/complex.rs index 256cde2..ff3feb5 100644 --- a/dsp/src/complex.rs +++ b/dsp/src/complex.rs @@ -21,19 +21,43 @@ impl Complex { /// Return the absolute square (the squared magnitude). /// - /// Note: Normalization is `1 << 31`, i.e. Q0.31. + /// Note: Normalization is `1 << 32`, i.e. U0.32. + /// + /// Note(panic): This will panic for `Complex(i32::MIN, i32::MIN)` /// /// Example: /// /// ``` /// use dsp::Complex; - /// assert_eq!(Complex(i32::MAX, 0).abs_sqr(), i32::MAX - 1); - /// assert_eq!(Complex(i32::MIN + 1, 0).abs_sqr(), i32::MAX - 1); + /// assert_eq!(Complex(i32::MIN, 0).abs_sqr(), 1 << 31); + /// assert_eq!(Complex(i32::MAX, i32::MAX).abs_sqr(), u32::MAX - 3); /// ``` - pub fn abs_sqr(&self) -> i32 { + pub fn abs_sqr(&self) -> u32 { (((self.0 as i64) * (self.0 as i64) + (self.1 as i64) * (self.1 as i64)) - >> 31) as i32 + >> 31) as u32 + } + + /// log2(power) re full scale approximation + /// + /// TODO: scale up, interpolate + /// + /// Panic: + /// This will panic for `Complex(i32::MIN, i32::MIN)` + /// + /// Example: + /// + /// ``` + /// use dsp::Complex; + /// assert_eq!(Complex(i32::MAX, i32::MAX).log2(), -1); + /// assert_eq!(Complex(i32::MAX, 0).log2(), -2); + /// assert_eq!(Complex(1, 0).log2(), -63); + /// assert_eq!(Complex(0, 0).log2(), -64); + /// ``` + pub fn log2(&self) -> i32 { + let a = (self.0 as i64) * (self.0 as i64) + + (self.1 as i64) * (self.1 as i64); + -(a.leading_zeros() as i32) } /// Return the angle. @@ -44,12 +68,12 @@ impl Complex { /// /// ``` /// use dsp::Complex; - /// assert_eq!(Complex(i32::MAX, 0).arg(), 0); + /// assert_eq!(Complex(1, 0).arg(), 0); /// assert_eq!(Complex(-i32::MAX, 1).arg(), i32::MAX); /// assert_eq!(Complex(-i32::MAX, -1).arg(), -i32::MAX); - /// assert_eq!(Complex(0, -i32::MAX).arg(), -i32::MAX >> 1); - /// assert_eq!(Complex(0, i32::MAX).arg(), (i32::MAX >> 1) + 1); - /// assert_eq!(Complex(i32::MAX, i32::MAX).arg(), (i32::MAX >> 2) + 1); + /// assert_eq!(Complex(0, -1).arg(), -i32::MAX >> 1); + /// assert_eq!(Complex(0, 1).arg(), (i32::MAX >> 1) + 1); + /// assert_eq!(Complex(1, 1).arg(), (i32::MAX >> 2) + 1); /// ``` pub fn arg(&self) -> i32 { atan2(self.1, self.0) diff --git a/dsp/src/lowpass.rs b/dsp/src/lowpass.rs index 5004923..c9d3ef6 100644 --- a/dsp/src/lowpass.rs +++ b/dsp/src/lowpass.rs @@ -14,16 +14,16 @@ impl> Lowpass { /// Update the filter with a new sample. /// /// # Args - /// * `x`: Input data, needs k bits headroom + /// * `x`: Input data /// * `k`: Log2 time constant, 0..31 /// /// # Return - /// Filtered output y + /// Filtered output y, needs `k` bits headroom pub fn update(&mut self, x: i32, k: u8) -> i32 { debug_assert!(k & 31 == k); // This is an unrolled and optimized first-order IIR loop // that works for all possible time constants. - // Note DF-II and the zero(s) at Nyquist. + // Note DF-II and the zeros at Nyquist. let mut x = x; for y in self.y.iter_mut() { let dy = x - (*y >> k); diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index 5f7451e..a043201 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -126,8 +126,8 @@ const APP: () = { let conf = "frequency_discriminator"; let output = match conf { // Convert from IQ to power and phase. - "power_phase" => [output.abs_sqr(), output.arg()], - "frequency_discriminator" => [pll_frequency as i32, output.arg()], + "power_phase" => [(output.log2() << 10) as _, output.arg()], + "frequency_discriminator" => [pll_frequency as _, output.arg()], _ => [output.0 << 16, output.1 << 16], }; From 32b7058b4720d1cb632163fd83ecf137d942b919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 11 Feb 2021 23:15:32 +0100 Subject: [PATCH 35/48] lockin: 2nd order lowpass --- dsp/src/lockin.rs | 4 ++-- src/bin/lockin-external.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dsp/src/lockin.rs b/dsp/src/lockin.rs index 7fc1e85..843a793 100644 --- a/dsp/src/lockin.rs +++ b/dsp/src/lockin.rs @@ -1,9 +1,9 @@ use super::{lowpass::Lowpass, Complex}; -use generic_array::typenum::U4; +use generic_array::typenum::U2; #[derive(Clone, Default)] pub struct Lockin { - state: [Lowpass; 2], + state: [Lowpass; 2], } impl Lockin { diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index a043201..cbda83c 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -126,7 +126,7 @@ const APP: () = { let conf = "frequency_discriminator"; let output = match conf { // Convert from IQ to power and phase. - "power_phase" => [(output.log2() << 10) as _, output.arg()], + "power_phase" => [(output.log2() << 24) as _, output.arg()], "frequency_discriminator" => [pll_frequency as _, output.arg()], _ => [output.0 << 16, output.1 << 16], }; From 67f052c0c97d62a4eb43e08a0a136274798a4a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 12 Feb 2021 11:05:50 +0100 Subject: [PATCH 36/48] lockin: add rounding bias --- dsp/src/lockin.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dsp/src/lockin.rs b/dsp/src/lockin.rs index 843a793..100a845 100644 --- a/dsp/src/lockin.rs +++ b/dsp/src/lockin.rs @@ -16,6 +16,7 @@ impl Lockin { } /// Update the lockin with a sample taken at a given phase. + /// The lowpass has a gain of `1 << k`. pub fn update(&mut self, sample: i16, phase: i32, k: u8) -> Complex { // Get the LO signal for demodulation. let lo = Complex::from_angle(phase); @@ -23,8 +24,10 @@ impl Lockin { // Mix with the LO signal, filter with the IIR lowpass, // return IQ (in-phase and quadrature) data. Complex( - self.state[0].update((sample as i32 * (lo.0 >> 16)) >> 16, k), - self.state[1].update((sample as i32 * (lo.1 >> 16)) >> 16, k), + self.state[0] + .update((sample as i32 * (lo.0 >> 16) + (1 << 15)) >> 16, k), + self.state[1] + .update((sample as i32 * (lo.1 >> 16) + (1 << 15)) >> 16, k), ) } } From a6d4099ed3f77aae7b1ccda321856329b5e71887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 12 Feb 2021 11:06:59 +0100 Subject: [PATCH 37/48] lowpass: expose natural gain, add bias --- dsp/src/lowpass.rs | 12 ++++++------ src/bin/lockin-external.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dsp/src/lowpass.rs b/dsp/src/lowpass.rs index c9d3ef6..91fae2f 100644 --- a/dsp/src/lowpass.rs +++ b/dsp/src/lowpass.rs @@ -14,21 +14,21 @@ impl> Lowpass { /// Update the filter with a new sample. /// /// # Args - /// * `x`: Input data - /// * `k`: Log2 time constant, 0..31 + /// * `x`: Input data, needs `k` bits headroom. + /// * `k`: Log2 time constant, 0..31. /// /// # Return - /// Filtered output y, needs `k` bits headroom + /// Filtered output y, with gain of `1 << k`. pub fn update(&mut self, x: i32, k: u8) -> i32 { debug_assert!(k & 31 == k); // This is an unrolled and optimized first-order IIR loop // that works for all possible time constants. // Note DF-II and the zeros at Nyquist. - let mut x = x; + let mut x = x << k; for y in self.y.iter_mut() { - let dy = x - (*y >> k); + let dy = (x - *y + (1 << (k - 1))) >> k; *y += dy; - x = (*y - (dy >> 1)) >> k; + x = *y - (dy >> 1); } x } diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index cbda83c..c0e6cae 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -103,7 +103,7 @@ const APP: () = { let phase_offset: i32 = 0; // TODO: expose // Log2 lowpass time constant - let time_constant: u8 = 8; // TODO: expose + let time_constant: u8 = 6; // TODO: expose let sample_frequency = ((pll_frequency // .wrapping_add(1 << design_parameters::SAMPLE_BUFFER_SIZE_LOG2 - 1) // half-up rounding bias @@ -128,7 +128,7 @@ const APP: () = { // Convert from IQ to power and phase. "power_phase" => [(output.log2() << 24) as _, output.arg()], "frequency_discriminator" => [pll_frequency as _, output.arg()], - _ => [output.0 << 16, output.1 << 16], + _ => [output.0, output.1], }; // Convert to DAC data. From 71c6e52f4d23f18fce74f0abbd20cda575c0c207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 12 Feb 2021 12:03:53 +0100 Subject: [PATCH 38/48] complex: add some traits --- dsp/src/complex.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++++ dsp/src/lockin.rs | 11 ++++++----- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/dsp/src/complex.rs b/dsp/src/complex.rs index ff3feb5..3edc0cc 100644 --- a/dsp/src/complex.rs +++ b/dsp/src/complex.rs @@ -1,8 +1,20 @@ +use core::convert::From; +use core::ops::Mul; + use super::{atan2, cossin}; #[derive(Copy, Clone, Default, PartialEq, Debug)] pub struct Complex(pub T, pub T); +impl Complex { + pub fn map(&self, func: F) -> Self + where + F: Fn(T) -> T, + { + Complex(func(self.0), func(self.1)) + } +} + impl Complex { /// Return a Complex on the unit circle given an angle. /// @@ -79,3 +91,40 @@ impl Complex { atan2(self.1, self.0) } } + +impl Mul for Complex { + type Output = Self; + + fn mul(self, other: Self) -> Self { + let a = self.0 as i64; + let b = self.1 as i64; + let c = other.0 as i64; + let d = other.1 as i64; + Complex( + ((a * c - b * d + (1 << 31)) >> 32) as i32, + ((b * c + a * d + (1 << 31)) >> 32) as i32, + ) + } +} + +impl Mul for Complex { + type Output = Self; + + fn mul(self, other: i32) -> Self { + Complex( + ((other as i64 * self.0 as i64 + (1 << 31)) >> 32) as i32, + ((other as i64 * self.1 as i64 + (1 << 31)) >> 32) as i32, + ) + } +} + +impl Mul for Complex { + type Output = Self; + + fn mul(self, other: i16) -> Self { + Complex( + (other as i32 * (self.0 >> 16) + (1 << 15)) >> 16, + (other as i32 * (self.1 >> 16) + (1 << 15)) >> 16, + ) + } +} diff --git a/dsp/src/lockin.rs b/dsp/src/lockin.rs index 100a845..b2f38c1 100644 --- a/dsp/src/lockin.rs +++ b/dsp/src/lockin.rs @@ -21,13 +21,14 @@ impl Lockin { // Get the LO signal for demodulation. let lo = Complex::from_angle(phase); - // Mix with the LO signal, filter with the IIR lowpass, + // Mix with the LO signal + let mix = lo * sample; + + // Filter with the IIR lowpass, // return IQ (in-phase and quadrature) data. Complex( - self.state[0] - .update((sample as i32 * (lo.0 >> 16) + (1 << 15)) >> 16, k), - self.state[1] - .update((sample as i32 * (lo.1 >> 16) + (1 << 15)) >> 16, k), + self.state[0].update(mix.0, k), + self.state[1].update(mix.1, k), ) } } From 8918952b96f480521f02d5f50c3cb0b394648ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 12 Feb 2021 12:06:00 +0100 Subject: [PATCH 39/48] complex: lint --- dsp/src/complex.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/dsp/src/complex.rs b/dsp/src/complex.rs index 3edc0cc..6353f9a 100644 --- a/dsp/src/complex.rs +++ b/dsp/src/complex.rs @@ -1,4 +1,3 @@ -use core::convert::From; use core::ops::Mul; use super::{atan2, cossin}; From 4130292706fa8e15ac1b5d9c85dfc5638f337900 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 13 Feb 2021 00:04:40 +0000 Subject: [PATCH 40/48] Upgrade to serde-json-core v0.2.0 This also fixes the network interface for writing IIR coefficients. --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/server.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17030d2..444756b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -915,9 +915,9 @@ dependencies = [ [[package]] name = "serde-json-core" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf406405ada9ef326ca78677324ac66994ff348fc48a16030be08caeed29825" +checksum = "89fd6016a00149b485f66da701f76d909210d319040c97b6eff300f6e2ba2153" dependencies = [ "heapless", "serde", diff --git a/Cargo.toml b/Cargo.toml index d5fc525..bb167cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ panic-semihosting = { version = "0.5", optional = true } panic-halt = "0.2" serde = { version = "1.0", features = ["derive"], default-features = false } heapless = { version = "0.5", features = ["serde"] } -serde-json-core = "0.1" +serde-json-core = "0.2" cortex-m-rtic = "0.5.5" embedded-hal = "0.2.4" nb = "1.0.0" diff --git a/src/server.rs b/src/server.rs index 5e6950d..cd9f5ac 100644 --- a/src/server.rs +++ b/src/server.rs @@ -40,7 +40,7 @@ macro_rules! route_request { $( $write_attribute => { let new_value = match serde_json_core::from_str::<$TYPE>(&$request.value) { - Ok(data) => data, + Ok((data, _)) => data, Err(_) => return server::Response::error($request.attribute, "Failed to decode value"), }; @@ -253,7 +253,7 @@ impl Server { &self.data[..self.data.len() - 1], ); match r { - Ok(mut res) => { + Ok((mut res, _)) => { // Note that serde_json_core doesn't escape quotations within a string. // To account for this, we manually translate all single quotes to // double quotes. This occurs because we doubly-serialize this field in From b581a016ce013e63ec482ce17f2ef0c9b2bca02e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Sun, 14 Feb 2021 17:55:01 +0100 Subject: [PATCH 41/48] lockin: redundant new --- dsp/src/lockin.rs | 8 -------- src/bin/lockin-external.rs | 4 +--- src/bin/lockin-internal.rs | 4 +--- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/dsp/src/lockin.rs b/dsp/src/lockin.rs index b2f38c1..6ebf4cb 100644 --- a/dsp/src/lockin.rs +++ b/dsp/src/lockin.rs @@ -7,14 +7,6 @@ pub struct Lockin { } impl Lockin { - /// Create a new Lockin with given IIR coefficients. - pub fn new() -> Self { - let lp = Lowpass::default(); - Self { - state: [lp.clone(), lp], - } - } - /// Update the lockin with a sample taken at a given phase. /// The lowpass has a gain of `1 << k`. pub fn update(&mut self, sample: i16, phase: i32, k: u8) -> Complex { diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index c0e6cae..9b29033 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -34,8 +34,6 @@ const APP: () = { + design_parameters::SAMPLE_BUFFER_SIZE_LOG2, ); - let lockin = Lockin::new(); - // Enable ADC/DAC events stabilizer.adcs.0.start(); stabilizer.adcs.1.start(); @@ -59,7 +57,7 @@ const APP: () = { timestamper: stabilizer.timestamper, pll, - lockin, + lockin: Lockin::default(), } } diff --git a/src/bin/lockin-internal.rs b/src/bin/lockin-internal.rs index cfa3115..c4d5bd6 100644 --- a/src/bin/lockin-internal.rs +++ b/src/bin/lockin-internal.rs @@ -28,8 +28,6 @@ const APP: () = { // Configure the microcontroller let (mut stabilizer, _pounder) = hardware::setup(c.core, c.device); - let lockin = Lockin::new(); - // Enable ADC/DAC events stabilizer.adcs.1.start(); stabilizer.dacs.0.start(); @@ -39,7 +37,7 @@ const APP: () = { stabilizer.adc_dac_timer.start(); init::LateResources { - lockin, + lockin: Lockin::default(), afes: stabilizer.afes, adc: stabilizer.adcs.1, dacs: stabilizer.dacs, From 5fc45a659b6957b8f62c43691397abfd781108bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 15 Feb 2021 08:51:19 +0100 Subject: [PATCH 42/48] lockin-external: comment style [nfc] --- src/bin/lockin-external.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index 9b29033..a454e19 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -104,7 +104,8 @@ const APP: () = { let time_constant: u8 = 6; // TODO: expose let sample_frequency = ((pll_frequency - // .wrapping_add(1 << design_parameters::SAMPLE_BUFFER_SIZE_LOG2 - 1) // half-up rounding bias + // half-up rounding bias + // .wrapping_add(1 << design_parameters::SAMPLE_BUFFER_SIZE_LOG2 - 1) >> design_parameters::SAMPLE_BUFFER_SIZE_LOG2) as i32) .wrapping_mul(harmonic); From cfc4cb382d9b075148adcbe4aa2372fadc69198f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 15 Feb 2021 08:53:55 +0100 Subject: [PATCH 43/48] dependencies: hal ref is on master now [nfc] --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17030d2..e5a80f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1013,7 +1013,7 @@ dependencies = [ [[package]] name = "stm32h7xx-hal" version = "0.8.0" -source = "git+https://github.com/stm32-rs/stm32h7xx-hal?branch=dma#2b8a04caac566a8560f400ddd6503508f78bea77" +source = "git+https://github.com/stm32-rs/stm32h7xx-hal?branch=master#2b8a04caac566a8560f400ddd6503508f78bea77" dependencies = [ "bare-metal 1.0.0", "cast", diff --git a/Cargo.toml b/Cargo.toml index d5fc525..747243a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ default-features = false [dependencies.stm32h7xx-hal] features = ["stm32h743v", "rt", "unproven", "ethernet", "quadspi"] git = "https://github.com/stm32-rs/stm32h7xx-hal" -branch = "dma" +branch = "master" [features] semihosting = ["panic-semihosting", "cortex-m-log/semihosting"] From 43c0ba9ea090a4f185328f74bf24c3db7ad7c301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 15 Feb 2021 09:18:31 +0100 Subject: [PATCH 44/48] dependencies: bump --- Cargo.lock | 94 +++++++++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9cd37c8..64c84cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,9 +98,9 @@ checksum = "c147d86912d04bef727828fda769a76ca81629a46d8ba311a8d58a26aa91473d" [[package]] name = "bstr" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf" +checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" dependencies = [ "lazy_static", "memchr", @@ -110,15 +110,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.4.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" +checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" [[package]] name = "byteorder" -version = "1.3.4" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" [[package]] name = "cast" @@ -148,9 +148,9 @@ dependencies = [ [[package]] name = "const_fn" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" +checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" [[package]] name = "cortex-m" @@ -446,9 +446,9 @@ dependencies = [ [[package]] name = "half" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177" +checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" [[package]] name = "hash32" @@ -480,18 +480,18 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" dependencies = [ "libc", ] [[package]] name = "indexmap" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" +checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" dependencies = [ "autocfg", "hashbrown", @@ -517,15 +517,15 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "js-sys" -version = "0.3.46" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" +checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65" dependencies = [ "wasm-bindgen", ] @@ -538,9 +538,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.81" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" +checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" [[package]] name = "libm" @@ -734,9 +734,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ "proc-macro2", ] @@ -771,9 +771,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" dependencies = [ "getrandom", ] @@ -820,9 +820,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" dependencies = [ "regex-syntax", ] @@ -838,9 +838,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.21" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" [[package]] name = "rtic-core" @@ -946,9 +946,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.60" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779" +checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" dependencies = [ "itoa", "ryu", @@ -1013,7 +1013,7 @@ dependencies = [ [[package]] name = "stm32h7xx-hal" version = "0.8.0" -source = "git+https://github.com/stm32-rs/stm32h7xx-hal?branch=master#2b8a04caac566a8560f400ddd6503508f78bea77" +source = "git+https://github.com/stm32-rs/stm32h7xx-hal?branch=master#08231e334a11236fe556668ac19cb1c214da2406" dependencies = [ "bare-metal 1.0.0", "cast", @@ -1050,9 +1050,9 @@ dependencies = [ [[package]] name = "tinytemplate" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d3dc76004a03cec1c5932bca4cdc2e39aaa798e3f82363dd94f9adf6098c12f" +checksum = "a2ada8616fad06a2d0c455adc530de4ef57605a8120cc65da9653e0e9623ca74" dependencies = [ "serde", "serde_json", @@ -1078,9 +1078,9 @@ checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "vcell" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" [[package]] name = "version_check" @@ -1122,9 +1122,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.69" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" +checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1132,9 +1132,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.69" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" +checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7" dependencies = [ "bumpalo", "lazy_static", @@ -1147,9 +1147,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.69" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" +checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1157,9 +1157,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.69" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" +checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" dependencies = [ "proc-macro2", "quote", @@ -1170,15 +1170,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.69" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" +checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" [[package]] name = "web-sys" -version = "0.3.46" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" +checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3" dependencies = [ "js-sys", "wasm-bindgen", From 12ea66a3f7f8b1716235003cee9e8c2c1d9c59b9 Mon Sep 17 00:00:00 2001 From: Harry Ho Date: Tue, 16 Feb 2021 11:45:44 +0800 Subject: [PATCH 45/48] update cargosha256 --- cargosha256-dual-iir.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cargosha256-dual-iir.nix b/cargosha256-dual-iir.nix index 4276c72..d58968f 100644 --- a/cargosha256-dual-iir.nix +++ b/cargosha256-dual-iir.nix @@ -1 +1 @@ -"0y8mfmy3b8ma4namgkff4rfrshybskaiyh1gja30hbn3l77z43j6" +"138kpxzxs73zhmd4xi5kw3fddb05gac4mpngizm01831n1ycyhl0" From 3abd47494b386774305cdbf0e9e6491b28dab298 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Tue, 16 Feb 2021 14:12:13 +0100 Subject: [PATCH 46/48] Fixing DMA2 instantiation issue --- src/hardware/configuration.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index 52db011..fba7048 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -134,6 +134,12 @@ pub fn setup( let dma_streams = hal::dma::dma::StreamsTuple::new(device.DMA1, ccdr.peripheral.DMA1); + #[cfg(feature = "pounder_v1_1")] + let dma2_streams = hal::dma::dma::StreamsTuple::new( + device.DMA2, + ccdr.peripheral.DMA2, + ); + // Configure timer 2 to trigger conversions for the ADC let mut sampling_timer = { // The timer frequency is manually adjusted below, so the 1KHz setting here is a @@ -740,11 +746,6 @@ pub fn setup( #[cfg(feature = "pounder_v1_1")] let pounder_stamper = { - let dma2_streams = hal::dma::dma::StreamsTuple::new( - device.DMA2, - ccdr.peripheral.DMA2, - ); - let etr_pin = gpioa.pa0.into_alternate_af3(); // The frequency in the constructor is dont-care, as we will modify the period + clock From fdb0c40764afcfc208e86e3d2a799d6c0e585d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 16 Feb 2021 15:22:58 +0100 Subject: [PATCH 47/48] fmt --- src/hardware/configuration.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index fba7048..dabd2ef 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -135,10 +135,8 @@ pub fn setup( hal::dma::dma::StreamsTuple::new(device.DMA1, ccdr.peripheral.DMA1); #[cfg(feature = "pounder_v1_1")] - let dma2_streams = hal::dma::dma::StreamsTuple::new( - device.DMA2, - ccdr.peripheral.DMA2, - ); + let dma2_streams = + hal::dma::dma::StreamsTuple::new(device.DMA2, ccdr.peripheral.DMA2); // Configure timer 2 to trigger conversions for the ADC let mut sampling_timer = { From aeaeac1f17e2cc5ea705edd7d8cad81bd688d8a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 16 Feb 2021 15:58:16 +0100 Subject: [PATCH 48/48] add comment on dma2 init --- src/hardware/configuration.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index dabd2ef..8ba24fe 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -134,6 +134,7 @@ pub fn setup( let dma_streams = hal::dma::dma::StreamsTuple::new(device.DMA1, ccdr.peripheral.DMA1); + // Early, before the DMA1 peripherals (#272) #[cfg(feature = "pounder_v1_1")] let dma2_streams = hal::dma::dma::StreamsTuple::new(device.DMA2, ccdr.peripheral.DMA2);