Merge branch 'master' into feature/mqtt-convert
This commit is contained in:
commit
9e1f4a864c
6
.github/bors.toml
vendored
6
.github/bors.toml
vendored
@ -1,3 +1,7 @@
|
|||||||
block_labels = [ "S-blocked" ]
|
block_labels = [ "S-blocked" ]
|
||||||
delete_merged_branches = true
|
delete_merged_branches = true
|
||||||
status = ["ci"]
|
status = [
|
||||||
|
"style",
|
||||||
|
"test (stable)",
|
||||||
|
"compile (stable)",
|
||||||
|
]
|
||||||
|
76
.github/workflows/ci.yml
vendored
76
.github/workflows/ci.yml
vendored
@ -16,11 +16,9 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
target: thumbv7em-none-eabihf
|
target: thumbv7em-none-eabihf
|
||||||
override: true
|
override: true
|
||||||
components: rustfmt, clippy
|
|
||||||
- name: cargo fmt --check
|
- name: cargo fmt --check
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
@ -37,59 +35,29 @@ jobs:
|
|||||||
|
|
||||||
compile:
|
compile:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: ${{ matrix.toolchain == 'nightly' }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
toolchain:
|
toolchain: [stable]
|
||||||
- stable
|
features: ['']
|
||||||
bin:
|
|
||||||
- dual-iir
|
|
||||||
- lockin-internal
|
|
||||||
- lockin-external
|
|
||||||
features:
|
|
||||||
- ''
|
|
||||||
include:
|
include:
|
||||||
- toolchain: beta
|
- toolchain: beta
|
||||||
bin: dual-iir
|
|
||||||
features: ''
|
features: ''
|
||||||
- toolchain: stable
|
- toolchain: stable
|
||||||
bin: lockin-internal
|
|
||||||
features: pounder_v1_1
|
features: pounder_v1_1
|
||||||
|
- toolchain: nightly
|
||||||
|
features: nightly
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install Rust ${{ matrix.toolchain }}
|
- uses: actions-rs/toolchain@v1
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.toolchain }}
|
toolchain: ${{ matrix.toolchain }}
|
||||||
target: thumbv7em-none-eabihf
|
target: thumbv7em-none-eabihf
|
||||||
override: true
|
override: true
|
||||||
components: llvm-tools-preview
|
- uses: actions-rs/cargo@v1
|
||||||
- name: cargo build release
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
with:
|
||||||
command: build
|
command: build
|
||||||
args: --release --features "${{ matrix.features }}" --bin ${{ matrix.bin }}
|
args: --release --features "${{ matrix.features }}"
|
||||||
- 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
|
|
||||||
- uses: actions/upload-artifact@v2
|
|
||||||
if: ${{ matrix.toolchain == 'stable' && matrix.features == '' }}
|
|
||||||
with:
|
|
||||||
name: stabilizer_${{ matrix.bin }}
|
|
||||||
path: |
|
|
||||||
target/*/release/${{ matrix.bin }}
|
|
||||||
${{ matrix.bin }}-release.bin
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -100,8 +68,7 @@ jobs:
|
|||||||
- beta
|
- beta
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install Rust ${{ matrix.toolchain }}
|
- uses: actions-rs/toolchain@v1
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.toolchain }}
|
toolchain: ${{ matrix.toolchain }}
|
||||||
- name: cargo test
|
- name: cargo test
|
||||||
@ -114,28 +81,3 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
command: bench
|
command: bench
|
||||||
args: --package dsp --target=x86_64-unknown-linux-gnu
|
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
|
|
||||||
|
45
.github/workflows/release.yml
vendored
Normal file
45
.github/workflows/release.yml
vendored
Normal file
@ -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
|
102
Cargo.lock
generated
102
Cargo.lock
generated
@ -98,9 +98,9 @@ checksum = "c147d86912d04bef727828fda769a76ca81629a46d8ba311a8d58a26aa91473d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bstr"
|
||||||
version = "0.2.14"
|
version = "0.2.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf"
|
checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -110,15 +110,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.4.0"
|
version = "3.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
|
checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.3.4"
|
version = "1.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cast"
|
name = "cast"
|
||||||
@ -148,9 +148,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_fn"
|
name = "const_fn"
|
||||||
version = "0.4.4"
|
version = "0.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826"
|
checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-m"
|
name = "cortex-m"
|
||||||
@ -351,6 +351,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_stringset"
|
name = "derive_stringset"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/vertigo-designs/miniconf.git?branch=develop#256098cafc6db93b456b5014c8ef1b835b5e239d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -362,6 +363,7 @@ name = "dsp"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"criterion",
|
"criterion",
|
||||||
|
"generic-array 0.14.4",
|
||||||
"libm",
|
"libm",
|
||||||
"ndarray",
|
"ndarray",
|
||||||
"rand",
|
"rand",
|
||||||
@ -466,9 +468,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "half"
|
name = "half"
|
||||||
version = "1.6.0"
|
version = "1.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177"
|
checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hash32"
|
name = "hash32"
|
||||||
@ -500,18 +502,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.17"
|
version = "0.1.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
|
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.6.0"
|
version = "1.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
|
checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
@ -537,15 +539,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "0.4.6"
|
version = "0.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.46"
|
version = "0.3.47"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175"
|
checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
@ -558,9 +560,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.81"
|
version = "0.2.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
|
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libm"
|
name = "libm"
|
||||||
@ -618,6 +620,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "miniconf"
|
name = "miniconf"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/vertigo-designs/miniconf.git?branch=develop#256098cafc6db93b456b5014c8ef1b835b5e239d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"derive_stringset",
|
"derive_stringset",
|
||||||
"heapless",
|
"heapless",
|
||||||
@ -628,8 +631,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimq"
|
name = "minimq"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/quartiq/minimq.git#83e946544cebd09c9dd07ff1271be639138ec1ce"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c5e626690b6f62e15710cf9815e5ca25ee54084899298c100a14b2504c80a46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit_field",
|
"bit_field",
|
||||||
"embedded-nal",
|
"embedded-nal",
|
||||||
@ -783,9 +787,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.8"
|
version = "1.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
|
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@ -820,9 +824,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.6.1"
|
version = "0.6.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5"
|
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
@ -869,9 +873,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.4.2"
|
version = "1.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
|
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"regex-syntax",
|
"regex-syntax",
|
||||||
]
|
]
|
||||||
@ -887,9 +891,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.21"
|
version = "0.6.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
|
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtic-core"
|
name = "rtic-core"
|
||||||
@ -1005,9 +1009,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.60"
|
version = "1.0.62"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
|
checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -1082,7 +1086,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "stm32h7xx-hal"
|
name = "stm32h7xx-hal"
|
||||||
version = "0.8.0"
|
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#08231e334a11236fe556668ac19cb1c214da2406"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bare-metal 1.0.0",
|
"bare-metal 1.0.0",
|
||||||
"cast",
|
"cast",
|
||||||
@ -1119,9 +1123,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinytemplate"
|
name = "tinytemplate"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d3dc76004a03cec1c5932bca4cdc2e39aaa798e3f82363dd94f9adf6098c12f"
|
checksum = "a2ada8616fad06a2d0c455adc530de4ef57605a8120cc65da9653e0e9623ca74"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -1147,9 +1151,9 @@ checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcell"
|
name = "vcell"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
|
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
@ -1191,9 +1195,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.69"
|
version = "0.2.70"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e"
|
checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"wasm-bindgen-macro",
|
"wasm-bindgen-macro",
|
||||||
@ -1201,9 +1205,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-backend"
|
name = "wasm-bindgen-backend"
|
||||||
version = "0.2.69"
|
version = "0.2.70"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62"
|
checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
@ -1216,9 +1220,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.69"
|
version = "0.2.70"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084"
|
checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
@ -1226,9 +1230,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.69"
|
version = "0.2.70"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549"
|
checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1239,15 +1243,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.69"
|
version = "0.2.70"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158"
|
checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.46"
|
version = "0.3.47"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3"
|
checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
@ -47,7 +47,7 @@ ad9959 = { path = "ad9959" }
|
|||||||
|
|
||||||
[dependencies.miniconf]
|
[dependencies.miniconf]
|
||||||
git = "https://github.com/vertigo-designs/miniconf.git"
|
git = "https://github.com/vertigo-designs/miniconf.git"
|
||||||
branch = "feature/mqtt-interface"
|
branch = "develop"
|
||||||
|
|
||||||
[dependencies.mcp23017]
|
[dependencies.mcp23017]
|
||||||
git = "https://github.com/mrd0ll4r/mcp23017.git"
|
git = "https://github.com/mrd0ll4r/mcp23017.git"
|
||||||
@ -59,7 +59,7 @@ branch = "main"
|
|||||||
[dependencies.stm32h7xx-hal]
|
[dependencies.stm32h7xx-hal]
|
||||||
features = ["stm32h743v", "rt", "unproven", "ethernet", "quadspi"]
|
features = ["stm32h743v", "rt", "unproven", "ethernet", "quadspi"]
|
||||||
git = "https://github.com/stm32-rs/stm32h7xx-hal"
|
git = "https://github.com/stm32-rs/stm32h7xx-hal"
|
||||||
branch = "dma"
|
branch = "master"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
semihosting = ["panic-semihosting", "cortex-m-log/semihosting"]
|
semihosting = ["panic-semihosting", "cortex-m-log/semihosting"]
|
||||||
|
@ -1 +1 @@
|
|||||||
"0ysy8fg6kbblhmjyavq6pg77n21fcygwc0hvidmg2yywkhgdi348"
|
"138kpxzxs73zhmd4xi5kw3fddb05gac4mpngizm01831n1ycyhl0"
|
||||||
|
@ -8,6 +8,7 @@ edition = "2018"
|
|||||||
libm = "0.2.1"
|
libm = "0.2.1"
|
||||||
serde = { version = "1.0", features = ["derive"], default-features = false }
|
serde = { version = "1.0", features = ["derive"], default-features = false }
|
||||||
serde-json-core = "0.1"
|
serde-json-core = "0.1"
|
||||||
|
generic-array = "0.14"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
|
@ -1,8 +1,19 @@
|
|||||||
|
use core::ops::Mul;
|
||||||
|
|
||||||
use super::{atan2, cossin};
|
use super::{atan2, cossin};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default, PartialEq, Debug)]
|
#[derive(Copy, Clone, Default, PartialEq, Debug)]
|
||||||
pub struct Complex<T>(pub T, pub T);
|
pub struct Complex<T>(pub T, pub T);
|
||||||
|
|
||||||
|
impl<T: Copy> Complex<T> {
|
||||||
|
pub fn map<F>(&self, func: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(T) -> T,
|
||||||
|
{
|
||||||
|
Complex(func(self.0), func(self.1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Complex<i32> {
|
impl Complex<i32> {
|
||||||
/// Return a Complex on the unit circle given an angle.
|
/// Return a Complex on the unit circle given an angle.
|
||||||
///
|
///
|
||||||
@ -21,19 +32,43 @@ impl Complex<i32> {
|
|||||||
|
|
||||||
/// Return the absolute square (the squared magnitude).
|
/// 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:
|
/// Example:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use dsp::Complex;
|
/// use dsp::Complex;
|
||||||
/// assert_eq!(Complex(i32::MAX, 0).abs_sqr(), i32::MAX - 1);
|
/// assert_eq!(Complex(i32::MIN, 0).abs_sqr(), 1 << 31);
|
||||||
/// assert_eq!(Complex(i32::MIN + 1, 0).abs_sqr(), i32::MAX - 1);
|
/// 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.0 as i64) * (self.0 as i64)
|
||||||
+ (self.1 as i64) * (self.1 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.
|
/// Return the angle.
|
||||||
@ -44,14 +79,51 @@ impl Complex<i32> {
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use dsp::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(-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, -1).arg(), -i32::MAX >> 1);
|
||||||
/// assert_eq!(Complex(0, i32::MAX).arg(), (i32::MAX >> 1) + 1);
|
/// assert_eq!(Complex(0, 1).arg(), (i32::MAX >> 1) + 1);
|
||||||
/// assert_eq!(Complex(i32::MAX, i32::MAX).arg(), (i32::MAX >> 2) + 1);
|
/// assert_eq!(Complex(1, 1).arg(), (i32::MAX >> 2) + 1);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn arg(&self) -> i32 {
|
pub fn arg(&self) -> i32 {
|
||||||
atan2(self.1, self.0)
|
atan2(self.1, self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Mul for Complex<i32> {
|
||||||
|
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<i32> for Complex<i32> {
|
||||||
|
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<i16> for Complex<i32> {
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use core::f32::consts::PI;
|
use core::f64::consts::PI;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Generic vector for integer IIR filter.
|
/// Generic vector for integer IIR filter.
|
||||||
@ -19,7 +19,7 @@ impl Vec5 {
|
|||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// 2nd-order IIR filter coefficients in the form [b0,b1,b2,a1,a2]. a0 is set to -1.
|
/// 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.
|
// 3rd order Taylor approximation of sin and cos.
|
||||||
let f = f * 2. * PI;
|
let f = f * 2. * PI;
|
||||||
let f2 = f * f * 0.5;
|
let f2 = f * f * 0.5;
|
||||||
@ -27,10 +27,10 @@ impl Vec5 {
|
|||||||
let fsin = f * (1. - f2 / 3.);
|
let fsin = f * (1. - f2 / 3.);
|
||||||
let alpha = fsin / (2. * q);
|
let alpha = fsin / (2. * q);
|
||||||
// IIR uses Q2.30 fixed point
|
// 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 b0 = (k / 2. * (1. - fcos) / a0 + 0.5) as _;
|
||||||
let a1 = (2. * fcos / a0) as _;
|
let a1 = (2. * fcos / a0 + 0.5) as _;
|
||||||
let a2 = ((alpha - 1.) / a0) as _;
|
let a2 = ((alpha - 1.) / a0 + 0.5) as _;
|
||||||
|
|
||||||
Self([b0, 2 * b0, b0, a1, a2])
|
Self([b0, 2 * b0, b0, a1, a2])
|
||||||
}
|
}
|
||||||
@ -97,7 +97,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lowpass_gen() {
|
fn lowpass_gen() {
|
||||||
let ba = Vec5::lowpass(1e-3, 1. / 2f32.sqrt(), 2.);
|
let ba = Vec5::lowpass(1e-5, 1. / 2f64.sqrt(), 2.);
|
||||||
println!("{:?}", ba.0);
|
println!("{:?}", ba.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,7 @@ mod cossin;
|
|||||||
pub mod iir;
|
pub mod iir;
|
||||||
pub mod iir_int;
|
pub mod iir_int;
|
||||||
pub mod lockin;
|
pub mod lockin;
|
||||||
|
pub mod lowpass;
|
||||||
pub mod pll;
|
pub mod pll;
|
||||||
pub mod rpll;
|
pub mod rpll;
|
||||||
pub mod unwrap;
|
pub mod unwrap;
|
||||||
|
@ -1,41 +1,26 @@
|
|||||||
use super::{
|
use super::{lowpass::Lowpass, Complex};
|
||||||
iir_int::{Vec5, IIR},
|
use generic_array::typenum::U2;
|
||||||
Complex,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default, Deserialize, Serialize)]
|
#[derive(Clone, Default)]
|
||||||
pub struct Lockin {
|
pub struct Lockin {
|
||||||
iir: IIR,
|
state: [Lowpass<U2>; 2],
|
||||||
state: [Vec5; 2],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Lockin {
|
impl Lockin {
|
||||||
/// Create a new Lockin with given IIR coefficients.
|
|
||||||
pub fn new(ba: Vec5) -> Self {
|
|
||||||
Self {
|
|
||||||
iir: IIR { ba },
|
|
||||||
state: [Vec5::default(); 2],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update the lockin with a sample taken at a given phase.
|
/// Update the lockin with a sample taken at a given phase.
|
||||||
pub fn update(&mut self, sample: i32, phase: i32) -> Complex<i32> {
|
/// The lowpass has a gain of `1 << k`.
|
||||||
|
pub fn update(&mut self, sample: i16, phase: i32, k: u8) -> Complex<i32> {
|
||||||
// Get the LO signal for demodulation.
|
// Get the LO signal for demodulation.
|
||||||
let lo = Complex::from_angle(phase);
|
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.
|
// return IQ (in-phase and quadrature) data.
|
||||||
// Note: 32x32 -> 64 bit multiplications are pretty much free.
|
|
||||||
Complex(
|
Complex(
|
||||||
self.iir.update(
|
self.state[0].update(mix.0, k),
|
||||||
&mut self.state[0],
|
self.state[1].update(mix.1, k),
|
||||||
((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 _,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
35
dsp/src/lowpass.rs
Normal file
35
dsp/src/lowpass.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
|
|
||||||
|
/// Arbitrary order, high dynamic range, wide coefficient range,
|
||||||
|
/// lowpass filter implementation. DC gain is 1.
|
||||||
|
///
|
||||||
|
/// Type argument N is the filter order.
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct Lowpass<N: ArrayLength<i32>> {
|
||||||
|
// IIR state storage
|
||||||
|
y: GenericArray<i32, N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: ArrayLength<i32>> Lowpass<N> {
|
||||||
|
/// Update the filter with a new sample.
|
||||||
|
///
|
||||||
|
/// # Args
|
||||||
|
/// * `x`: Input data, needs `k` bits headroom.
|
||||||
|
/// * `k`: Log2 time constant, 0..31.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// 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 << k;
|
||||||
|
for y in self.y.iter_mut() {
|
||||||
|
let dy = (x - *y + (1 << (k - 1))) >> k;
|
||||||
|
*y += dy;
|
||||||
|
x = *y - (dy >> 1);
|
||||||
|
}
|
||||||
|
x
|
||||||
|
}
|
||||||
|
}
|
@ -94,7 +94,6 @@ mod test {
|
|||||||
|
|
||||||
struct Harness {
|
struct Harness {
|
||||||
rpll: RPLL,
|
rpll: RPLL,
|
||||||
dt2: u8,
|
|
||||||
shift_frequency: u8,
|
shift_frequency: u8,
|
||||||
shift_phase: u8,
|
shift_phase: u8,
|
||||||
noise: i32,
|
noise: i32,
|
||||||
@ -109,7 +108,6 @@ mod test {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
rpll: RPLL::new(8),
|
rpll: RPLL::new(8),
|
||||||
dt2: 8,
|
|
||||||
shift_frequency: 9,
|
shift_frequency: 9,
|
||||||
shift_phase: 8,
|
shift_phase: 8,
|
||||||
noise: 0,
|
noise: 0,
|
||||||
@ -122,7 +120,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self, n: usize) -> (Vec<f32>, Vec<f32>) {
|
fn run(&mut self, n: usize) -> (Vec<f32>, Vec<f32>) {
|
||||||
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_frequency);
|
||||||
assert!(self.period < 1 << self.shift_phase + 1);
|
assert!(self.period < 1 << self.shift_phase + 1);
|
||||||
|
|
||||||
@ -130,7 +128,7 @@ mod test {
|
|||||||
let mut f = Vec::<f32>::new();
|
let mut f = Vec::<f32>::new();
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
let timestamp = if self.time - self.next_noisy >= 0 {
|
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);
|
self.next = self.next.wrapping_add(self.period);
|
||||||
let timestamp = self.next_noisy;
|
let timestamp = self.next_noisy;
|
||||||
let p_noise = self.rng.gen_range(-self.noise..=self.noise);
|
let p_noise = self.rng.gen_range(-self.noise..=self.noise);
|
||||||
@ -151,23 +149,23 @@ mod test {
|
|||||||
// phase error
|
// phase error
|
||||||
y.push(yi.wrapping_sub(y_ref) as f32 / 2f32.powi(32));
|
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;
|
let p_sig = fi as u64 * self.period as u64;
|
||||||
// relative frequency error
|
// relative frequency error
|
||||||
f.push(
|
f.push(
|
||||||
p_sig.wrapping_sub(p_ref) as i64 as f32
|
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
|
// advance time
|
||||||
self.time = self.time.wrapping_add(1 << self.dt2);
|
self.time = self.time.wrapping_add(1 << self.rpll.dt2);
|
||||||
}
|
}
|
||||||
(y, f)
|
(y, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn measure(&mut self, n: usize, limits: [f32; 4]) {
|
fn measure(&mut self, n: usize, limits: [f32; 4]) {
|
||||||
let t_settle = (1 << self.shift_frequency - self.dt2 + 4)
|
let t_settle = (1 << self.shift_frequency - self.rpll.dt2 + 4)
|
||||||
+ (1 << self.shift_phase - self.dt2 + 4);
|
+ (1 << self.shift_phase - self.rpll.dt2 + 4);
|
||||||
self.run(t_settle);
|
self.run(t_settle);
|
||||||
|
|
||||||
let (y, f) = self.run(n);
|
let (y, f) = self.run(n);
|
||||||
@ -268,4 +266,18 @@ mod test {
|
|||||||
|
|
||||||
h.measure(1 << 16, [2e-4, 6e-3, 2e-4, 2e-3]);
|
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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
|
|
||||||
|
|
||||||
use stm32h7xx_hal as hal;
|
use stm32h7xx_hal as hal;
|
||||||
|
|
||||||
@ -11,6 +10,7 @@ use stabilizer::hardware;
|
|||||||
|
|
||||||
use miniconf::{
|
use miniconf::{
|
||||||
embedded_nal::{IpAddr, Ipv4Addr},
|
embedded_nal::{IpAddr, Ipv4Addr},
|
||||||
|
minimq,
|
||||||
MqttInterface, StringSet,
|
MqttInterface, StringSet,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@ -44,7 +44,7 @@ const APP: () = {
|
|||||||
afes: (AFE0, AFE1),
|
afes: (AFE0, AFE1),
|
||||||
adcs: (Adc0Input, Adc1Input),
|
adcs: (Adc0Input, Adc1Input),
|
||||||
dacs: (Dac0Output, Dac1Output),
|
dacs: (Dac0Output, Dac1Output),
|
||||||
mqtt_interface: MqttInterface<Settings, NetworkStack>,
|
mqtt_interface: MqttInterface<Settings, NetworkStack, minimq::consts::U256>,
|
||||||
|
|
||||||
// Format: iir_state[ch][cascade-no][coeff]
|
// Format: iir_state[ch][cascade-no][coeff]
|
||||||
#[init([[iir::Vec5([0.; 5]); IIR_CASCADE_LENGTH]; 2])]
|
#[init([[iir::Vec5([0.; 5]); IIR_CASCADE_LENGTH]; 2])]
|
||||||
@ -58,14 +58,19 @@ const APP: () = {
|
|||||||
// Configure the microcontroller
|
// Configure the microcontroller
|
||||||
let (mut stabilizer, _pounder) = hardware::setup(c.core, c.device);
|
let (mut stabilizer, _pounder) = hardware::setup(c.core, c.device);
|
||||||
|
|
||||||
let broker = IpAddr::V4(Ipv4Addr::new(10, 34, 16, 1));
|
let mqtt_interface = {
|
||||||
let mqtt_interface = MqttInterface::new(
|
let mqtt_client = {
|
||||||
stabilizer.net.stack,
|
let broker = IpAddr::V4(Ipv4Addr::new(10, 34, 16, 1));
|
||||||
"stabilizer",
|
minimq::MqttClient::new(broker, "stabilizer", stabilizer.net.stack).unwrap()
|
||||||
broker,
|
};
|
||||||
Settings::new(),
|
|
||||||
)
|
MqttInterface::new(
|
||||||
.unwrap();
|
mqtt_client,
|
||||||
|
"stabilizer",
|
||||||
|
Settings::new(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
// Enable ADC/DAC events
|
// Enable ADC/DAC events
|
||||||
stabilizer.adcs.0.start();
|
stabilizer.adcs.0.start();
|
||||||
|
@ -1,30 +1,26 @@
|
|||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
|
|
||||||
|
|
||||||
use stm32h7xx_hal as hal;
|
use stm32h7xx_hal as hal;
|
||||||
|
|
||||||
use rtic::cyccnt::{Instant, U32Ext};
|
use rtic::cyccnt::{Instant, U32Ext};
|
||||||
|
|
||||||
use stabilizer::{hardware, ADC_SAMPLE_TICKS_LOG2, SAMPLE_BUFFER_SIZE_LOG2};
|
|
||||||
|
|
||||||
use miniconf::{
|
use miniconf::{
|
||||||
embedded_nal::{IpAddr, Ipv4Addr},
|
embedded_nal::{IpAddr, Ipv4Addr},
|
||||||
|
minimq,
|
||||||
MqttInterface, StringSet,
|
MqttInterface, StringSet,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
use dsp::{iir, iir_int, lockin::Lockin, rpll::RPLL, Accu};
|
use serde::Deserialize;
|
||||||
|
use stabilizer::{hardware, hardware::design_parameters};
|
||||||
|
|
||||||
|
use dsp::{lockin::Lockin, rpll::RPLL, Accu};
|
||||||
|
|
||||||
use hardware::{
|
use hardware::{
|
||||||
Adc0Input, Adc1Input, Dac0Output, Dac1Output, InputStamper, AFE0, AFE1,
|
Adc0Input, Adc1Input, Dac0Output, Dac1Output, InputStamper, AFE0, AFE1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SCALE: f32 = i16::MAX as _;
|
|
||||||
|
|
||||||
// The number of cascaded IIR biquads per channel. Select 1 or 2!
|
|
||||||
const IIR_CASCADE_LENGTH: usize = 1;
|
|
||||||
|
|
||||||
#[derive(Deserialize, StringSet)]
|
#[derive(Deserialize, StringSet)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
}
|
}
|
||||||
@ -42,13 +38,7 @@ const APP: () = {
|
|||||||
afes: (AFE0, AFE1),
|
afes: (AFE0, AFE1),
|
||||||
adcs: (Adc0Input, Adc1Input),
|
adcs: (Adc0Input, Adc1Input),
|
||||||
dacs: (Dac0Output, Dac1Output),
|
dacs: (Dac0Output, Dac1Output),
|
||||||
mqtt_interface: MqttInterface<Settings, hardware::NetworkStack>,
|
mqtt_interface: MqttInterface<Settings, hardware::NetworkStack, minimq::consts::U256>,
|
||||||
|
|
||||||
// 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,
|
timestamper: InputStamper,
|
||||||
pll: RPLL,
|
pll: RPLL,
|
||||||
@ -60,19 +50,23 @@ const APP: () = {
|
|||||||
// Configure the microcontroller
|
// Configure the microcontroller
|
||||||
let (mut stabilizer, _pounder) = hardware::setup(c.core, c.device);
|
let (mut stabilizer, _pounder) = hardware::setup(c.core, c.device);
|
||||||
|
|
||||||
let broker = IpAddr::V4(Ipv4Addr::new(10, 34, 16, 1));
|
let mqtt_interface = {
|
||||||
let mqtt_interface = MqttInterface::new(
|
let mqtt_client = {
|
||||||
stabilizer.net.stack,
|
let broker = IpAddr::V4(Ipv4Addr::new(10, 34, 16, 1));
|
||||||
"stabilizer/lockin",
|
minimq::MqttClient::new(broker, "stabilizer", stabilizer.net.stack).unwrap()
|
||||||
broker,
|
};
|
||||||
Settings::new(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let pll = RPLL::new(ADC_SAMPLE_TICKS_LOG2 + SAMPLE_BUFFER_SIZE_LOG2);
|
MqttInterface::new(
|
||||||
|
mqtt_client,
|
||||||
|
"stabilizer",
|
||||||
|
Settings::new(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
let lockin = Lockin::new(
|
let pll = RPLL::new(
|
||||||
iir_int::Vec5::lowpass(1e-3, 0.707, 2.), // TODO: expose
|
design_parameters::ADC_SAMPLE_TICKS_LOG2
|
||||||
|
+ design_parameters::SAMPLE_BUFFER_SIZE_LOG2,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Enable ADC/DAC events
|
// Enable ADC/DAC events
|
||||||
@ -87,6 +81,9 @@ const APP: () = {
|
|||||||
// Start sampling ADCs.
|
// Start sampling ADCs.
|
||||||
stabilizer.adc_dac_timer.start();
|
stabilizer.adc_dac_timer.start();
|
||||||
|
|
||||||
|
// Enable the timestamper.
|
||||||
|
stabilizer.timestamper.start();
|
||||||
|
|
||||||
init::LateResources {
|
init::LateResources {
|
||||||
mqtt_interface,
|
mqtt_interface,
|
||||||
afes: stabilizer.afes,
|
afes: stabilizer.afes,
|
||||||
@ -95,7 +92,7 @@ const APP: () = {
|
|||||||
timestamper: stabilizer.timestamper,
|
timestamper: stabilizer.timestamper,
|
||||||
|
|
||||||
pll,
|
pll,
|
||||||
lockin,
|
lockin: Lockin::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +103,7 @@ const APP: () = {
|
|||||||
/// This is an implementation of a externally (DI0) referenced PLL lockin on the ADC0 signal.
|
/// 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.
|
/// 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.
|
/// 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) {
|
fn process(c: process::Context) {
|
||||||
let adc_samples = [
|
let adc_samples = [
|
||||||
c.resources.adcs.0.acquire_buffer(),
|
c.resources.adcs.0.acquire_buffer(),
|
||||||
@ -118,30 +115,34 @@ const APP: () = {
|
|||||||
c.resources.dacs.1.acquire_buffer(),
|
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 lockin = c.resources.lockin;
|
||||||
|
|
||||||
let timestamp = c
|
let timestamp = c
|
||||||
.resources
|
.resources
|
||||||
.timestamper
|
.timestamper
|
||||||
.latest_timestamp()
|
.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);
|
.map(|t| t as i32);
|
||||||
let (pll_phase, pll_frequency) = c.resources.pll.update(
|
let (pll_phase, pll_frequency) = c.resources.pll.update(
|
||||||
timestamp,
|
timestamp,
|
||||||
22, // frequency settling time (log2 counter cycles), TODO: expose
|
21, // frequency settling time (log2 counter cycles), TODO: expose
|
||||||
22, // phase settling time, TODO: expose
|
21, // phase settling time, TODO: expose
|
||||||
);
|
);
|
||||||
|
|
||||||
// Harmonic index of the LO: -1 to _de_modulate the fundamental (complex conjugate)
|
// Harmonic index of the LO: -1 to _de_modulate the fundamental (complex conjugate)
|
||||||
let harmonic: i32 = -1; // TODO: expose
|
let harmonic: i32 = -1; // TODO: expose
|
||||||
// Demodulation LO phase offset
|
|
||||||
|
// Demodulation LO phase offset
|
||||||
let phase_offset: i32 = 0; // TODO: expose
|
let phase_offset: i32 = 0; // TODO: expose
|
||||||
|
|
||||||
|
// Log2 lowpass time constant
|
||||||
|
let time_constant: u8 = 6; // TODO: expose
|
||||||
|
|
||||||
let sample_frequency = ((pll_frequency
|
let sample_frequency = ((pll_frequency
|
||||||
// .wrapping_add(1 << SAMPLE_BUFFER_SIZE_LOG2 - 1) // half-up rounding bias
|
// half-up rounding bias
|
||||||
>> SAMPLE_BUFFER_SIZE_LOG2) as i32)
|
// .wrapping_add(1 << design_parameters::SAMPLE_BUFFER_SIZE_LOG2 - 1)
|
||||||
|
>> design_parameters::SAMPLE_BUFFER_SIZE_LOG2)
|
||||||
|
as i32)
|
||||||
.wrapping_mul(harmonic);
|
.wrapping_mul(harmonic);
|
||||||
let sample_phase =
|
let sample_phase =
|
||||||
phase_offset.wrapping_add(pll_phase.wrapping_mul(harmonic));
|
phase_offset.wrapping_add(pll_phase.wrapping_mul(harmonic));
|
||||||
@ -151,39 +152,23 @@ const APP: () = {
|
|||||||
.zip(Accu::new(sample_phase, sample_frequency))
|
.zip(Accu::new(sample_phase, sample_frequency))
|
||||||
// Convert to signed, MSB align the ADC sample.
|
// Convert to signed, MSB align the ADC sample.
|
||||||
.map(|(&sample, phase)| {
|
.map(|(&sample, phase)| {
|
||||||
lockin.update((sample as i16 as i32) << 16, phase)
|
lockin.update(sample as i16, phase, time_constant)
|
||||||
})
|
})
|
||||||
.last()
|
.last()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// convert i/q to power/phase,
|
let conf = "frequency_discriminator";
|
||||||
let power_phase = true; // TODO: expose
|
let output = match conf {
|
||||||
|
|
||||||
let mut output = if power_phase {
|
|
||||||
// Convert from IQ to power and phase.
|
// Convert from IQ to power and phase.
|
||||||
[output.abs_sqr() as _, output.arg() as _]
|
"power_phase" => [(output.log2() << 24) as _, output.arg()],
|
||||||
} else {
|
"frequency_discriminator" => [pll_frequency as _, output.arg()],
|
||||||
[output.0 as _, output.1 as _]
|
_ => [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.
|
// Convert to DAC data.
|
||||||
for i in 0..dac_samples[0].len() {
|
for i in 0..dac_samples[0].len() {
|
||||||
unsafe {
|
dac_samples[0][i] = (output[0] >> 16) as u16 ^ 0x8000;
|
||||||
dac_samples[0][i] =
|
dac_samples[1][i] = (output[1] >> 16) as u16 ^ 0x8000;
|
||||||
output[0].to_int_unchecked::<i16>() as u16 ^ 0x8000;
|
|
||||||
dac_samples[1][i] =
|
|
||||||
output[1].to_int_unchecked::<i16>() as u16 ^ 0x8000;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,7 +210,7 @@ const APP: () = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(priority = 1, resources=[mqtt_interface, afes, iir_ch])]
|
#[task(priority = 1, resources=[mqtt_interface, afes])]
|
||||||
fn settings_update(c: settings_update::Context) {
|
fn settings_update(c: settings_update::Context) {
|
||||||
let _settings = &c.resources.mqtt_interface.settings;
|
let _settings = &c.resources.mqtt_interface.settings;
|
||||||
//c.resources.iir_ch.lock(|iir| *iir = settings.iir);
|
//c.resources.iir_ch.lock(|iir| *iir = settings.iir);
|
||||||
|
@ -1,24 +1,23 @@
|
|||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
|
|
||||||
|
|
||||||
use dsp::{iir_int, lockin::Lockin, Accu};
|
use dsp::{lockin::Lockin, Accu};
|
||||||
use hardware::{Adc1Input, Dac0Output, Dac1Output, AFE0, AFE1};
|
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.
|
// A constant sinusoid to send on the DAC output.
|
||||||
// Full-scale gives a +/- 10V amplitude waveform. Scale it down to give +/- 1V.
|
// Full-scale gives a +/- 10V amplitude waveform. Scale it down to give +/- 1V.
|
||||||
const ONE: i16 = (0.1 * u16::MAX as f32) as _;
|
const ONE: i16 = (0.1 * u16::MAX as f32) as _;
|
||||||
const SQRT2: i16 = (ONE as f32 * 0.707) 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];
|
[ONE, SQRT2, 0, -SQRT2, -ONE, -SQRT2, 0, SQRT2];
|
||||||
|
|
||||||
#[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)]
|
#[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)]
|
||||||
const APP: () = {
|
const APP: () = {
|
||||||
struct Resources {
|
struct Resources {
|
||||||
afes: (AFE0, AFE1),
|
afes: (AFE0, AFE1),
|
||||||
adc1: Adc1Input,
|
adc: Adc1Input,
|
||||||
dacs: (Dac0Output, Dac1Output),
|
dacs: (Dac0Output, Dac1Output),
|
||||||
|
|
||||||
lockin: Lockin,
|
lockin: Lockin,
|
||||||
@ -29,10 +28,6 @@ const APP: () = {
|
|||||||
// Configure the microcontroller
|
// Configure the microcontroller
|
||||||
let (mut stabilizer, _pounder) = hardware::setup(c.core, c.device);
|
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
|
|
||||||
);
|
|
||||||
|
|
||||||
// Enable ADC/DAC events
|
// Enable ADC/DAC events
|
||||||
stabilizer.adcs.1.start();
|
stabilizer.adcs.1.start();
|
||||||
stabilizer.dacs.0.start();
|
stabilizer.dacs.0.start();
|
||||||
@ -42,9 +37,9 @@ const APP: () = {
|
|||||||
stabilizer.adc_dac_timer.start();
|
stabilizer.adc_dac_timer.start();
|
||||||
|
|
||||||
init::LateResources {
|
init::LateResources {
|
||||||
lockin,
|
lockin: Lockin::default(),
|
||||||
afes: stabilizer.afes,
|
afes: stabilizer.afes,
|
||||||
adc1: stabilizer.adcs.1,
|
adc: stabilizer.adcs.1,
|
||||||
dacs: stabilizer.dacs,
|
dacs: stabilizer.dacs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,10 +62,10 @@ const APP: () = {
|
|||||||
/// the same time bounds, meeting one also means the other is also met.
|
/// the same time bounds, meeting one also means the other is also met.
|
||||||
///
|
///
|
||||||
/// TODO: Document
|
/// 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) {
|
fn process(c: process::Context) {
|
||||||
let lockin = c.resources.lockin;
|
let lockin = c.resources.lockin;
|
||||||
let adc_samples = c.resources.adc1.acquire_buffer();
|
let adc_samples = c.resources.adc.acquire_buffer();
|
||||||
let dac_samples = [
|
let dac_samples = [
|
||||||
c.resources.dacs.0.acquire_buffer(),
|
c.resources.dacs.0.acquire_buffer(),
|
||||||
c.resources.dacs.1.acquire_buffer(),
|
c.resources.dacs.1.acquire_buffer(),
|
||||||
@ -84,13 +79,18 @@ const APP: () = {
|
|||||||
|
|
||||||
// Reference phase and frequency are known.
|
// Reference phase and frequency are known.
|
||||||
let pll_phase = 0;
|
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
|
// 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
|
// 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_frequency = (pll_frequency as i32).wrapping_mul(harmonic);
|
||||||
let sample_phase = phase_offset
|
let sample_phase = phase_offset
|
||||||
.wrapping_add((pll_phase as i32).wrapping_mul(harmonic));
|
.wrapping_add((pll_phase as i32).wrapping_mul(harmonic));
|
||||||
@ -101,24 +101,14 @@ const APP: () = {
|
|||||||
.zip(Accu::new(sample_phase, sample_frequency))
|
.zip(Accu::new(sample_phase, sample_frequency))
|
||||||
// Convert to signed, MSB align the ADC sample, update the Lockin (demodulate, filter)
|
// Convert to signed, MSB align the ADC sample, update the Lockin (demodulate, filter)
|
||||||
.map(|(&sample, phase)| {
|
.map(|(&sample, phase)| {
|
||||||
lockin.update((sample as i16 as i32) << 16, phase)
|
lockin.update(sample as i16, phase, time_constant)
|
||||||
})
|
})
|
||||||
// Decimate
|
// Decimate
|
||||||
.last()
|
.last()
|
||||||
.unwrap();
|
.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() {
|
for value in dac_samples[1].iter_mut() {
|
||||||
*value = (output[1] >> 16) as u16 ^ 0x8000;
|
*value = (output.arg() >> 16) as u16 ^ 0x8000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,9 +74,9 @@
|
|||||||
///! double-buffered mode offers less overhead due to the DMA disable/enable procedure).
|
///! double-buffered mode offers less overhead due to the DMA disable/enable procedure).
|
||||||
use stm32h7xx_hal as hal;
|
use stm32h7xx_hal as hal;
|
||||||
|
|
||||||
use crate::SAMPLE_BUFFER_SIZE;
|
use super::design_parameters::SAMPLE_BUFFER_SIZE;
|
||||||
|
|
||||||
use super::timers;
|
use super::timers;
|
||||||
|
|
||||||
use hal::dma::{
|
use hal::dma::{
|
||||||
config::Priority,
|
config::Priority,
|
||||||
dma::{DMAReq, DmaConfig},
|
dma::{DMAReq, DmaConfig},
|
||||||
|
@ -1,14 +1,6 @@
|
|||||||
///! Stabilizer hardware configuration
|
///! Stabilizer hardware configuration
|
||||||
///!
|
///!
|
||||||
///! This file contains all of the hardware-specific configuration of Stabilizer.
|
///! 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;
|
|
||||||
|
|
||||||
use stm32h7xx_hal::{
|
use stm32h7xx_hal::{
|
||||||
self as hal,
|
self as hal,
|
||||||
ethernet::{self, PHY},
|
ethernet::{self, PHY},
|
||||||
@ -149,6 +141,11 @@ pub fn setup(
|
|||||||
let dma_streams =
|
let dma_streams =
|
||||||
hal::dma::dma::StreamsTuple::new(device.DMA1, ccdr.peripheral.DMA1);
|
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);
|
||||||
|
|
||||||
// Configure timer 2 to trigger conversions for the ADC
|
// Configure timer 2 to trigger conversions for the ADC
|
||||||
let mut sampling_timer = {
|
let mut sampling_timer = {
|
||||||
// The timer frequency is manually adjusted below, so the 1KHz setting here is a
|
// The timer frequency is manually adjusted below, so the 1KHz setting here is a
|
||||||
@ -164,7 +161,8 @@ pub fn setup(
|
|||||||
timer2.set_tick_freq(design_parameters::TIMER_FREQUENCY);
|
timer2.set_tick_freq(design_parameters::TIMER_FREQUENCY);
|
||||||
|
|
||||||
let mut sampling_timer = timers::SamplingTimer::new(timer2);
|
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,
|
// The sampling timer is used as the master timer for the shadow-sampling timer. Thus,
|
||||||
// it generates a trigger whenever it is enabled.
|
// it generates a trigger whenever it is enabled.
|
||||||
@ -188,7 +186,8 @@ pub fn setup(
|
|||||||
|
|
||||||
let mut shadow_sampling_timer =
|
let mut shadow_sampling_timer =
|
||||||
timers::ShadowSamplingTimer::new(timer3);
|
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
|
// 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
|
// always be in-sync - thus, we configure it to operate in slave mode using "Trigger
|
||||||
@ -639,7 +638,7 @@ pub fn setup(
|
|||||||
let ref_clk: hal::time::Hertz =
|
let ref_clk: hal::time::Hertz =
|
||||||
design_parameters::DDS_REF_CLK.into();
|
design_parameters::DDS_REF_CLK.into();
|
||||||
|
|
||||||
let ad9959 = ad9959::Ad9959::new(
|
let mut ad9959 = ad9959::Ad9959::new(
|
||||||
qspi_interface,
|
qspi_interface,
|
||||||
reset_pin,
|
reset_pin,
|
||||||
&mut io_update,
|
&mut io_update,
|
||||||
@ -650,6 +649,8 @@ pub fn setup(
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
ad9959.self_test().unwrap();
|
||||||
|
|
||||||
// Return IO_Update
|
// Return IO_Update
|
||||||
gpiog.pg7 = io_update.into_analog();
|
gpiog.pg7 = io_update.into_analog();
|
||||||
|
|
||||||
@ -762,7 +763,8 @@ pub fn setup(
|
|||||||
let sample_frequency = {
|
let sample_frequency = {
|
||||||
let timer_frequency: hal::time::Hertz =
|
let timer_frequency: hal::time::Hertz =
|
||||||
design_parameters::TIMER_FREQUENCY.into();
|
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;
|
let sample_period = 1.0 / sample_frequency;
|
||||||
@ -779,11 +781,6 @@ pub fn setup(
|
|||||||
|
|
||||||
#[cfg(feature = "pounder_v1_1")]
|
#[cfg(feature = "pounder_v1_1")]
|
||||||
let pounder_stamper = {
|
let pounder_stamper = {
|
||||||
let dma2_streams = hal::dma::dma::StreamsTuple::new(
|
|
||||||
device.DMA2,
|
|
||||||
ccdr.peripheral.DMA2,
|
|
||||||
);
|
|
||||||
|
|
||||||
let etr_pin = gpioa.pa0.into_alternate_af3();
|
let etr_pin = gpioa.pa0.into_alternate_af3();
|
||||||
|
|
||||||
// The frequency in the constructor is dont-care, as we will modify the period + clock
|
// The frequency in the constructor is dont-care, as we will modify the period + clock
|
||||||
@ -797,22 +794,13 @@ pub fn setup(
|
|||||||
// Pounder is configured to generate a 500MHz reference clock, so a 125MHz sync-clock is
|
// 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
|
// 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.
|
// 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.set_external_clock(timers::Prescaler::Div4);
|
||||||
timestamp_timer.start();
|
timestamp_timer.start();
|
||||||
|
|
||||||
// We want the pounder timestamp timer to overflow once per batch.
|
// Set the timer to wrap at the u16 boundary to meet the PLL periodicity.
|
||||||
let tick_ratio = {
|
// Scale and wrap before or after the PLL.
|
||||||
let sync_clk_mhz: f32 = design_parameters::DDS_SYSTEM_CLK.0
|
timestamp_timer.set_period_ticks(u16::MAX);
|
||||||
as f32
|
|
||||||
/ design_parameters::DDS_SYNC_CLK_DIV as f32;
|
|
||||||
sync_clk_mhz / design_parameters::TIMER_FREQUENCY.0 as f32
|
|
||||||
};
|
|
||||||
|
|
||||||
let period = (tick_ratio
|
|
||||||
* ADC_SAMPLE_TICKS as f32
|
|
||||||
* SAMPLE_BUFFER_SIZE as f32) as u32
|
|
||||||
/ 4;
|
|
||||||
timestamp_timer.set_period_ticks((period - 1).try_into().unwrap());
|
|
||||||
let tim8_channels = timestamp_timer.channels();
|
let tim8_channels = timestamp_timer.channels();
|
||||||
|
|
||||||
pounder::timestamp::Timestamper::new(
|
pounder::timestamp::Timestamper::new(
|
||||||
|
@ -52,9 +52,9 @@
|
|||||||
///! served promptly after the transfer completes.
|
///! served promptly after the transfer completes.
|
||||||
use stm32h7xx_hal as hal;
|
use stm32h7xx_hal as hal;
|
||||||
|
|
||||||
use crate::SAMPLE_BUFFER_SIZE;
|
use super::design_parameters::SAMPLE_BUFFER_SIZE;
|
||||||
|
|
||||||
use super::timers;
|
use super::timers;
|
||||||
|
|
||||||
use hal::dma::{
|
use hal::dma::{
|
||||||
dma::{DMAReq, DmaConfig},
|
dma::{DMAReq, DmaConfig},
|
||||||
traits::TargetAddress,
|
traits::TargetAddress,
|
||||||
|
@ -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).
|
/// The divider from the DDS system clock to the SYNC_CLK output (sync-clk is always 1/4 of sysclk).
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub const DDS_SYNC_CLK_DIV: u8 = 4;
|
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/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.
|
||||||
|
pub const SAMPLE_BUFFER_SIZE_LOG2: u8 = 3;
|
||||||
|
pub const SAMPLE_BUFFER_SIZE: usize = 1 << SAMPLE_BUFFER_SIZE_LOG2;
|
||||||
|
@ -44,8 +44,12 @@ impl InputStamper {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
// Utilize the TIM5 CH4 as an input capture channel - use TI4 (the DI0 input trigger) as the
|
// Utilize the TIM5 CH4 as an input capture channel - use TI4 (the DI0 input trigger) as the
|
||||||
// capture source.
|
// capture source.
|
||||||
let input_capture =
|
let mut input_capture =
|
||||||
timer_channel.into_input_capture(timers::CaptureTrigger::Input24);
|
timer_channel.into_input_capture(timers::tim5::CaptureSource4::TI4);
|
||||||
|
|
||||||
|
// 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 {
|
Self {
|
||||||
capture_channel: input_capture,
|
capture_channel: input_capture,
|
||||||
|
@ -11,10 +11,10 @@ mod adc;
|
|||||||
mod afe;
|
mod afe;
|
||||||
mod configuration;
|
mod configuration;
|
||||||
mod dac;
|
mod dac;
|
||||||
mod design_parameters;
|
pub mod design_parameters;
|
||||||
mod digital_input_stamper;
|
mod digital_input_stamper;
|
||||||
mod eeprom;
|
mod eeprom;
|
||||||
mod pounder;
|
pub mod pounder;
|
||||||
mod timers;
|
mod timers;
|
||||||
|
|
||||||
pub use adc::{Adc0Input, Adc1Input};
|
pub use adc::{Adc0Input, Adc1Input};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
mod attenuators;
|
pub mod attenuators;
|
||||||
mod dds_output;
|
mod dds_output;
|
||||||
pub mod hrtimer;
|
pub mod hrtimer;
|
||||||
mod rf_power;
|
mod rf_power;
|
||||||
|
@ -26,7 +26,7 @@ use stm32h7xx_hal as hal;
|
|||||||
|
|
||||||
use hal::dma::{dma::DmaConfig, PeripheralToMemory, Transfer};
|
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
|
// 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
|
// working data provided to the application. These buffers must exist in a DMA-accessible memory
|
||||||
@ -86,7 +86,7 @@ impl Timestamper {
|
|||||||
|
|
||||||
// The capture channel should capture whenever the trigger input occurs.
|
// The capture channel should capture whenever the trigger input occurs.
|
||||||
let input_capture = capture_channel
|
let input_capture = capture_channel
|
||||||
.into_input_capture(timers::CaptureTrigger::TriggerInput);
|
.into_input_capture(timers::tim8::CaptureSource1::TRC);
|
||||||
input_capture.listen_dma();
|
input_capture.listen_dma();
|
||||||
|
|
||||||
// The data transfer is always a transfer of data from the peripheral to a RAM buffer.
|
// The data transfer is always a transfer of data from the peripheral to a RAM buffer.
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
///! The sampling timer is used for managing ADC sampling and external reference timestamping.
|
///! The sampling timer is used for managing ADC sampling and external reference timestamping.
|
||||||
use super::hal;
|
use super::hal;
|
||||||
|
|
||||||
/// The source of an input capture trigger.
|
use hal::stm32::{
|
||||||
#[allow(dead_code)]
|
// TIM1 and TIM8 have identical registers.
|
||||||
pub enum CaptureTrigger {
|
tim1 as __tim8,
|
||||||
Input13 = 0b01,
|
tim2 as __tim2,
|
||||||
Input24 = 0b10,
|
// TIM2 and TIM5 have identical registers.
|
||||||
TriggerInput = 0b11,
|
tim2 as __tim5,
|
||||||
}
|
tim3 as __tim3,
|
||||||
|
};
|
||||||
|
|
||||||
/// The event that should generate an external trigger from the peripheral.
|
/// The event that should generate an external trigger from the peripheral.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -47,6 +48,13 @@ pub enum SlaveMode {
|
|||||||
Trigger = 0b0110,
|
Trigger = 0b0110,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Optional input capture preconditioning filter configurations.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum InputFilter {
|
||||||
|
Div1N1 = 0b0000,
|
||||||
|
Div1N8 = 0b0011,
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! timer_channels {
|
macro_rules! timer_channels {
|
||||||
($name:ident, $TY:ident, $size:ty) => {
|
($name:ident, $TY:ident, $size:ty) => {
|
||||||
paste::paste! {
|
paste::paste! {
|
||||||
@ -225,6 +233,8 @@ macro_rules! timer_channels {
|
|||||||
|
|
||||||
($index:expr, $TY:ty, $ccmrx:expr, $size:ty) => {
|
($index:expr, $TY:ty, $ccmrx:expr, $size:ty) => {
|
||||||
paste::paste! {
|
paste::paste! {
|
||||||
|
pub use super::[< __ $TY:lower >]::[< $ccmrx _input >]::[< CC $index S_A>] as [< CaptureSource $index >];
|
||||||
|
|
||||||
/// A capture/compare channel of the timer.
|
/// A capture/compare channel of the timer.
|
||||||
pub struct [< Channel $index >] {}
|
pub struct [< Channel $index >] {}
|
||||||
|
|
||||||
@ -267,12 +277,10 @@ macro_rules! timer_channels {
|
|||||||
/// # Args
|
/// # Args
|
||||||
/// * `input` - The input source for the input capture event.
|
/// * `input` - The input source for the input capture event.
|
||||||
#[allow(dead_code)]
|
#[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() };
|
let regs = unsafe { &*<$TY>::ptr() };
|
||||||
|
|
||||||
// Note(unsafe): The bit configuration is guaranteed to be valid by the
|
regs.[< $ccmrx _input >]().modify(|_, w| w.[< cc $index s>]().variant(input));
|
||||||
// CaptureTrigger enum definition.
|
|
||||||
regs.[< $ccmrx _input >]().modify(|_, w| unsafe { w.[< cc $index s>]().bits(input as u8) });
|
|
||||||
|
|
||||||
[< Channel $index InputCapture >] {}
|
[< Channel $index InputCapture >] {}
|
||||||
}
|
}
|
||||||
@ -316,6 +324,9 @@ macro_rules! timer_channels {
|
|||||||
/// Enable the input capture to begin capturing timer values.
|
/// Enable the input capture to begin capturing timer values.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn enable(&mut self) {
|
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.
|
// Note(unsafe): This channel owns all access to the specific timer channel.
|
||||||
// Only atomic operations on completed on the timer registers.
|
// Only atomic operations on completed on the timer registers.
|
||||||
let regs = unsafe { &*<$TY>::ptr() };
|
let regs = unsafe { &*<$TY>::ptr() };
|
||||||
@ -330,6 +341,18 @@ macro_rules! timer_channels {
|
|||||||
let regs = unsafe { &*<$TY>::ptr() };
|
let regs = unsafe { &*<$TY>::ptr() };
|
||||||
regs.sr.read().[< cc $index of >]().bit_is_set()
|
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) {
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note(unsafe): This manually implements DMA support for input-capture channels. This
|
// Note(unsafe): This manually implements DMA support for input-capture channels. This
|
||||||
|
11
src/lib.rs
11
src/lib.rs
@ -1,16 +1,7 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
pub mod hardware;
|
pub mod hardware;
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
Loading…
Reference in New Issue
Block a user