Merge branch 'master' into rj/itcm
* master: (70 commits) Updating JSON syntax Updating trigger Adding bors timeout dual-iir: add some mqtt parameter docs changelog: spelling update lock let bors handle hitl prepare v0.5.0 release Updating code after review Adding comment, reverting change Adding sleep to HITL run Updating ping deadline Reverting unintended change Updating dependency revision Renaming interface to avoid confusion dual-iir: use InputPin re-export, rename digital_input Updating dependency dual-iir: add enable_hold, force_hold fix bench hardware: add digital input support ...
This commit is contained in:
commit
f0c50d9678
2
.github/bors.toml
vendored
2
.github/bors.toml
vendored
@ -1,7 +1,9 @@
|
|||||||
block_labels = [ "S-blocked" ]
|
block_labels = [ "S-blocked" ]
|
||||||
delete_merged_branches = true
|
delete_merged_branches = true
|
||||||
|
timeout_sec = 1200
|
||||||
status = [
|
status = [
|
||||||
"style",
|
"style",
|
||||||
"test (stable)",
|
"test (stable)",
|
||||||
"compile (stable)",
|
"compile (stable)",
|
||||||
|
"HITL Run Status"
|
||||||
]
|
]
|
||||||
|
4
.github/labeler.yml
vendored
Normal file
4
.github/labeler.yml
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# add changes-hitl label if any hitl scripts are changed
|
||||||
|
# REVIEW those changes before approving HITL deployment!
|
||||||
|
changes-hitl:
|
||||||
|
- any: [hitl/*]
|
18
.github/workflows/hitl.yml
vendored
18
.github/workflows/hitl.yml
vendored
@ -1,18 +0,0 @@
|
|||||||
name: HITL
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ hitl ]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
hitl:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
environment: hitl
|
|
||||||
steps:
|
|
||||||
- uses: peter-evans/repository-dispatch@v1
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.DISPATCH_PAT }}
|
|
||||||
event-type: stabilizer
|
|
||||||
repository: quartiq/hitl
|
|
||||||
client-payload: '{"github": ${{ toJson(github) }}}'
|
|
39
.github/workflows/hitl_trigger.yml
vendored
Normal file
39
.github/workflows/hitl_trigger.yml
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
name: HITL Trigger
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- staging
|
||||||
|
- trying
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
hitl-trigger:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment: hitl
|
||||||
|
steps:
|
||||||
|
- uses: LouisBrunner/checks-action@v1.1.1
|
||||||
|
id: hitl-check
|
||||||
|
with:
|
||||||
|
repo: ${{ github.repository }}
|
||||||
|
sha: ${{ github.event.head_commit.id }}
|
||||||
|
token: ${{ github.token }}
|
||||||
|
name: HITL Run Status
|
||||||
|
status: in_progress
|
||||||
|
details_url: "https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}"
|
||||||
|
output: |
|
||||||
|
{"summary": "Starting..."}
|
||||||
|
|
||||||
|
- uses: peter-evans/repository-dispatch@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.DISPATCH_PAT }}
|
||||||
|
event-type: stabilizer
|
||||||
|
repository: quartiq/hitl
|
||||||
|
client-payload: |
|
||||||
|
{"github": ${{ toJson(github) }}, "check_id": ${{steps.hitl-check.outputs.check_id}}}
|
||||||
|
|
||||||
|
- uses: fountainhead/action-wait-for-check@v1.0.0
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
checkName: HITL Run Status
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
12
.github/workflows/labeler.yml
vendored
Normal file
12
.github/workflows/labeler.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
name: "Pull Request Labeler"
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
labeler:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/labeler@v3
|
||||||
|
with:
|
||||||
|
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
82
CHANGELOG.md
82
CHANGELOG.md
@ -1,6 +1,73 @@
|
|||||||
# Changelog
|
# Change Log
|
||||||
|
|
||||||
## [v0.2.0] 2019-05-28
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||||
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
## [v0.5.0] - 2021-04-21
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Batch sample processing
|
||||||
|
* DMA for ADC and DAC batches
|
||||||
|
* Pounder profile streaming
|
||||||
|
* DSP library with lots of optimized algorithms
|
||||||
|
* Digital input support
|
||||||
|
* Hardware in the loop continuous integration testing
|
||||||
|
* Dependency updates
|
||||||
|
* MQTT settings interface through miniconf/minimq
|
||||||
|
* Multi-binary support
|
||||||
|
* DHCP support
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Removed JSON-over-TCP interface
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Robust EEPROM MAC address reading slow supply start
|
||||||
|
|
||||||
|
## [v0.4.1] - 2020-06-23
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fix DAC clr/ldac, SPI speed
|
||||||
|
|
||||||
|
## [v0.4.0] - 2020-06-22
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Hardware v1.1 only
|
||||||
|
* AD9959/Pounder support
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* HAL port
|
||||||
|
|
||||||
|
## [v0.3.0] - 2020-01-20
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Red LED handling
|
||||||
|
* EEPROM MAC address reading
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Panic handler cleanup
|
||||||
|
* Dependency updates (smoltcp, rtfm)
|
||||||
|
|
||||||
|
## [v0.2.0] - 2019-05-28
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
* Initial basic release
|
* Initial basic release
|
||||||
* Ethernet support
|
* Ethernet support
|
||||||
@ -9,6 +76,15 @@
|
|||||||
* ADC/DAC timing and interrupts
|
* ADC/DAC timing and interrupts
|
||||||
* Board configuration, bootstrap
|
* Board configuration, bootstrap
|
||||||
|
|
||||||
## [v0.1.0] 2019-03-10
|
## [v0.1.0] - 2019-03-10
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
* First bits of code published
|
* First bits of code published
|
||||||
|
|
||||||
|
[Unreleased]: https://github.com/quartiq/stabilizer/compare/v0.5.0...HEAD
|
||||||
|
[v0.5.0]: https://github.com/quartiq/stabilizer/compare/v0.4.1...v0.5.0
|
||||||
|
[v0.4.1]: https://github.com/quartiq/stabilizer/compare/v0.4.0...v0.4.1
|
||||||
|
[v0.4.0]: https://github.com/quartiq/stabilizer/compare/v0.3.0...v0.4.0
|
||||||
|
[v0.3.0]: https://github.com/quartiq/stabilizer/compare/v0.2.0...v0.3.0
|
||||||
|
[v0.2.0]: https://github.com/quartiq/stabilizer/compare/v0.1.0...v0.2.0
|
||||||
|
115
Cargo.lock
generated
115
Cargo.lock
generated
@ -1,5 +1,7 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ad9959"
|
name = "ad9959"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -36,7 +38,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b9a69a963b70ddacfcd382524f72a4576f359af9334b3bf48a79566590bb8bfa"
|
checksum = "b9a69a963b70ddacfcd382524f72a4576f359af9334b3bf48a79566590bb8bfa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitrate",
|
"bitrate",
|
||||||
"cortex-m 0.7.1",
|
"cortex-m 0.7.2",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -87,9 +89,9 @@ checksum = "c147d86912d04bef727828fda769a76ca81629a46d8ba311a8d58a26aa91473d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
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 = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cast"
|
name = "cast"
|
||||||
@ -121,15 +123,15 @@ dependencies = [
|
|||||||
"aligned",
|
"aligned",
|
||||||
"bare-metal 0.2.5",
|
"bare-metal 0.2.5",
|
||||||
"bitfield",
|
"bitfield",
|
||||||
"cortex-m 0.7.1",
|
"cortex-m 0.7.2",
|
||||||
"volatile-register",
|
"volatile-register",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-m"
|
name = "cortex-m"
|
||||||
version = "0.7.1"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a0b756a8bffc56025de45218a48ff9b801180440c0ee49a722b32d49dcebc771"
|
checksum = "643a210c1bdc23d0db511e2a576082f4ff4dcae9d0c37f50b431b8f8439d6d6b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bare-metal 0.2.5",
|
"bare-metal 0.2.5",
|
||||||
"bitfield",
|
"bitfield",
|
||||||
@ -143,7 +145,7 @@ version = "0.7.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e202d2eac4e34adf7524a563e36623bae6f69cc0a73ef9bd22a4c93a5a806fa"
|
checksum = "0e202d2eac4e34adf7524a563e36623bae6f69cc0a73ef9bd22a4c93a5a806fa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cortex-m 0.7.1",
|
"cortex-m 0.7.2",
|
||||||
"cortex-m-semihosting",
|
"cortex-m-semihosting",
|
||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
@ -198,13 +200,13 @@ version = "0.3.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6bffa6c1454368a6aa4811ae60964c38e6996d397ff8095a8b9211b1c1f749bc"
|
checksum = "6bffa6c1454368a6aa4811ae60964c38e6996d397ff8095a8b9211b1c1f749bc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cortex-m 0.7.1",
|
"cortex-m 0.7.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_miniconf"
|
name = "derive_miniconf"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/quartiq/miniconf.git?branch=develop#314fa5587d1aa28e1ad70106f19e30db646e9f28"
|
source = "git+https://github.com/quartiq/miniconf.git?rev=314fa5587d#314fa5587d1aa28e1ad70106f19e30db646e9f28"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -362,9 +364,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.6.1"
|
version = "1.6.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b"
|
checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
@ -372,9 +374,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.86"
|
version = "0.2.93"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
|
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libm"
|
name = "libm"
|
||||||
@ -393,15 +395,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "managed"
|
name = "managed"
|
||||||
version = "0.8.0"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
|
checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matrixmultiply"
|
name = "matrixmultiply"
|
||||||
version = "0.2.4"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "916806ba0031cd542105d916a97c8572e1fa6dd79c9c51e7eb43a09ec2dd84c1"
|
checksum = "1300bdbea33ec2836b01ff1f5a6eed8bad66d0c31f94d9b7993407a8b054c3a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rawpointer",
|
"rawpointer",
|
||||||
]
|
]
|
||||||
@ -409,7 +411,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "mcp23017"
|
name = "mcp23017"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "git+https://github.com/mrd0ll4r/mcp23017.git#61933f857abe5a837800493a5f58e91a3c9435ec"
|
source = "git+https://github.com/mrd0ll4r/mcp23017.git?rev=61933f857a#61933f857abe5a837800493a5f58e91a3c9435ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
]
|
]
|
||||||
@ -417,7 +419,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "miniconf"
|
name = "miniconf"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/quartiq/miniconf.git?branch=develop#314fa5587d1aa28e1ad70106f19e30db646e9f28"
|
source = "git+https://github.com/quartiq/miniconf.git?rev=314fa5587d#314fa5587d1aa28e1ad70106f19e30db646e9f28"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"derive_miniconf",
|
"derive_miniconf",
|
||||||
"heapless 0.6.1",
|
"heapless 0.6.1",
|
||||||
@ -429,7 +431,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "minimq"
|
name = "minimq"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/quartiq/minimq.git#933687c2e4bc8a4d972de9a4d1508b0b554a8b38"
|
source = "git+https://github.com/quartiq/minimq.git?rev=933687c2e4b#933687c2e4bc8a4d972de9a4d1508b0b554a8b38"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit_field",
|
"bit_field",
|
||||||
"embedded-nal",
|
"embedded-nal",
|
||||||
@ -438,6 +440,12 @@ dependencies = [
|
|||||||
"heapless 0.6.1",
|
"heapless 0.6.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nanorand"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac1378b66f7c93a1c0f8464a19bf47df8795083842e5090f4b7305973d5a22d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nb"
|
name = "nb"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@ -455,9 +463,9 @@ checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ndarray"
|
name = "ndarray"
|
||||||
version = "0.14.0"
|
version = "0.15.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c0d5c9540a691d153064dc47a4db2504587a75eae07bf1d73f7a596ebc73c04"
|
checksum = "cc1372704f14bb132a49a6701c2238970a359ee0829fed481b522a63bf25456a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"matrixmultiply",
|
"matrixmultiply",
|
||||||
"num-complex",
|
"num-complex",
|
||||||
@ -474,9 +482,9 @@ checksum = "2178127478ae4ee9be7180bc9c3bffb6354dd7238400db567102f98c413a9f35"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num"
|
name = "num"
|
||||||
version = "0.3.1"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f"
|
checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-complex",
|
"num-complex",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
@ -487,9 +495,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-complex"
|
name = "num-complex"
|
||||||
version = "0.3.1"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5"
|
checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
@ -517,9 +525,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-rational"
|
name = "num-rational"
|
||||||
version = "0.3.2"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
|
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
@ -541,7 +549,7 @@ version = "0.5.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3d55dedd501dfd02514646e0af4d7016ce36bc12ae177ef52056989966a1eec"
|
checksum = "c3d55dedd501dfd02514646e0af4d7016ce36bc12ae177ef52056989966a1eec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cortex-m 0.7.1",
|
"cortex-m 0.7.2",
|
||||||
"cortex-m-semihosting",
|
"cortex-m-semihosting",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -559,9 +567,9 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.24"
|
version = "1.0.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
@ -664,9 +672,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.124"
|
version = "1.0.125"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f"
|
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
@ -674,7 +682,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "serde-json-core"
|
name = "serde-json-core"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/rust-embedded-community/serde-json-core.git?branch=master#ee06ac91bc43b72450a92198a00d9e5c5b9946d2"
|
source = "git+https://github.com/rust-embedded-community/serde-json-core.git?rev=ee06ac91bc#ee06ac91bc43b72450a92198a00d9e5c5b9946d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heapless 0.5.6",
|
"heapless 0.5.6",
|
||||||
"serde",
|
"serde",
|
||||||
@ -682,9 +690,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.124"
|
version = "1.0.125"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b"
|
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -693,8 +701,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smoltcp"
|
name = "smoltcp"
|
||||||
version = "0.7.0"
|
version = "0.7.1"
|
||||||
source = "git+https://github.com/smoltcp-rs/smoltcp.git#43567b9743cb9f422de83fad9ff42a6d13f6e5ee"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97173c1ef35b0a09304cb3882eba594761243005847cbbf6124f966e8da6519a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
@ -704,16 +713,17 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "smoltcp-nal"
|
name = "smoltcp-nal"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/quartiq/smoltcp-nal.git?branch=main#56519012d7c6a382eaa0d7ecb26f2701771d9ce8"
|
source = "git+https://github.com/quartiq/smoltcp-nal.git?rev=8468f11#8468f11abacd7aba82454e6904df19c1d1ab91bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"embedded-nal",
|
"embedded-nal",
|
||||||
"heapless 0.6.1",
|
"heapless 0.6.1",
|
||||||
|
"nanorand",
|
||||||
"smoltcp",
|
"smoltcp",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stabilizer"
|
name = "stabilizer"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ad9959",
|
"ad9959",
|
||||||
"asm-delay",
|
"asm-delay",
|
||||||
@ -746,24 +756,25 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stm32h7"
|
name = "stm32h7"
|
||||||
version = "0.12.1"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7571f17d1ed7d67957d0004de6c52bd1ef5e736ed5ddc2bcecf001512269f77c"
|
checksum = "8b672c837e0ee8158ecc7fce0f9a948dd0693a9c588338e728d14b73307a0b7d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bare-metal 0.2.5",
|
"bare-metal 0.2.5",
|
||||||
"cortex-m 0.6.7",
|
"cortex-m 0.7.2",
|
||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
"vcell",
|
"vcell",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stm32h7xx-hal"
|
name = "stm32h7xx-hal"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
source = "git+https://github.com/stm32-rs/stm32h7xx-hal?branch=master#08231e334a11236fe556668ac19cb1c214da2406"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67034b80041bc33a48df1c1c435b6ae3bb18c35e42aa7e702ce8363b96793398"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bare-metal 1.0.0",
|
"bare-metal 1.0.0",
|
||||||
"cast",
|
"cast",
|
||||||
"cortex-m 0.6.7",
|
"cortex-m 0.7.2",
|
||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
"embedded-dma",
|
"embedded-dma",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
@ -776,9 +787,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.60"
|
version = "1.0.68"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
|
checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -787,9 +798,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.12.0"
|
version = "1.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
@ -805,9 +816,9 @@ checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.2"
|
version = "0.9.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "void"
|
name = "void"
|
||||||
|
39
Cargo.toml
39
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "stabilizer"
|
name = "stabilizer"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
authors = ["Robert Jördens <rj@quartiq.de>"]
|
authors = ["Robert Jördens <rj@quartiq.de>"]
|
||||||
description = "Firmware for the Sinara Stabilizer board (stm32h743, eth, poe, 2 adc, 2 dac)"
|
description = "Firmware for the Sinara Stabilizer board (stm32h743, eth, poe, 2 adc, 2 dac)"
|
||||||
categories = ["embedded", "no-std", "hardware-support", "science"]
|
categories = ["embedded", "no-std", "hardware-support", "science"]
|
||||||
@ -43,9 +43,16 @@ enum-iterator = "0.6.0"
|
|||||||
paste = "1"
|
paste = "1"
|
||||||
dsp = { path = "dsp" }
|
dsp = { path = "dsp" }
|
||||||
ad9959 = { path = "ad9959" }
|
ad9959 = { path = "ad9959" }
|
||||||
smoltcp-nal = "0.1.0"
|
|
||||||
miniconf = "0.1"
|
|
||||||
generic-array = "0.14"
|
generic-array = "0.14"
|
||||||
|
miniconf = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies.mcp23017]
|
||||||
|
git = "https://github.com/mrd0ll4r/mcp23017.git"
|
||||||
|
rev = "61933f857a"
|
||||||
|
|
||||||
|
[dependencies.stm32h7xx-hal]
|
||||||
|
features = ["stm32h743v", "rt", "unproven", "ethernet", "quadspi"]
|
||||||
|
version = "0.9.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1.0"
|
cc = "1.0"
|
||||||
@ -56,31 +63,19 @@ rev = "a2e3ad5"
|
|||||||
|
|
||||||
[patch.crates-io.miniconf]
|
[patch.crates-io.miniconf]
|
||||||
git = "https://github.com/quartiq/miniconf.git"
|
git = "https://github.com/quartiq/miniconf.git"
|
||||||
branch = "develop"
|
rev = "314fa5587d"
|
||||||
|
|
||||||
|
[dependencies.smoltcp-nal]
|
||||||
|
git = "https://github.com/quartiq/smoltcp-nal.git"
|
||||||
|
rev = "8468f11"
|
||||||
|
|
||||||
[patch.crates-io.minimq]
|
[patch.crates-io.minimq]
|
||||||
git = "https://github.com/quartiq/minimq.git"
|
git = "https://github.com/quartiq/minimq.git"
|
||||||
|
rev = "933687c2e4b"
|
||||||
[patch.crates-io.smoltcp-nal]
|
|
||||||
git = "https://github.com/quartiq/smoltcp-nal.git"
|
|
||||||
branch = "main"
|
|
||||||
|
|
||||||
[patch.crates-io.serde-json-core]
|
[patch.crates-io.serde-json-core]
|
||||||
git = "https://github.com/rust-embedded-community/serde-json-core.git"
|
git = "https://github.com/rust-embedded-community/serde-json-core.git"
|
||||||
branch = "master"
|
rev = "ee06ac91bc"
|
||||||
|
|
||||||
[patch.crates-io.smoltcp]
|
|
||||||
# We manually patch smoltcp so that we can get access to unreleased updates to the DHCP server. When
|
|
||||||
# a new release of smoltcp is made, we can remove this patch.
|
|
||||||
git = "https://github.com/smoltcp-rs/smoltcp.git"
|
|
||||||
|
|
||||||
[dependencies.mcp23017]
|
|
||||||
git = "https://github.com/mrd0ll4r/mcp23017.git"
|
|
||||||
|
|
||||||
[dependencies.stm32h7xx-hal]
|
|
||||||
features = ["stm32h743v", "rt", "unproven", "ethernet", "quadspi"]
|
|
||||||
git = "https://github.com/stm32-rs/stm32h7xx-hal"
|
|
||||||
branch = "master"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
semihosting = ["panic-semihosting", "cortex-m-log/semihosting"]
|
semihosting = ["panic-semihosting", "cortex-m-log/semihosting"]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
[![QUARTIQ Matrix Chat](https://img.shields.io/matrix/quartiq:matrix.org)](https://matrix.to/#/#quartiq:matrix.org)
|
[![QUARTIQ Matrix Chat](https://img.shields.io/matrix/quartiq:matrix.org)](https://matrix.to/#/#quartiq:matrix.org)
|
||||||
[![HITL (private)](https://github.com/quartiq/hitl/workflows/Stabilizer/badge.svg)](https://github.com/quartiq/hitl/actions?query=workflow%3AStabilizer)
|
[![Continuous Integration](https://github.com/quartiq/stabilizer/actions/workflows/ci.yml/badge.svg)](https://github.com/quartiq/stabilizer/actions/workflows/ci.yml)
|
||||||
|
[![Stabilizer HITL [Nightly]](https://github.com/quartiq/hitl/actions/workflows/stabilizer-nightly.yml/badge.svg)](https://github.com/quartiq/hitl/actions/workflows/stabilizer-nightly.yml)
|
||||||
|
|
||||||
# Stabilizer Firmware
|
# Stabilizer Firmware
|
||||||
|
|
||||||
|
@ -558,13 +558,15 @@ impl ProfileSerializer {
|
|||||||
/// * `channels` - A list of channels to apply the configuration to.
|
/// * `channels` - A list of channels to apply the configuration to.
|
||||||
/// * `ftw` - If provided, indicates a frequency tuning word for the channels.
|
/// * `ftw` - If provided, indicates a frequency tuning word for the channels.
|
||||||
/// * `pow` - If provided, indicates a phase offset word for the channels.
|
/// * `pow` - If provided, indicates a phase offset word for the channels.
|
||||||
/// * `acr` - If provided, indicates the amplitude control register for the channels.
|
/// * `acr` - If provided, indicates the amplitude control register for the channels. The ACR
|
||||||
|
/// should be stored in the 3 LSB of the word. Note that if amplitude scaling is to be used,
|
||||||
|
/// the "Amplitude multiplier enable" bit must be set.
|
||||||
pub fn update_channels(
|
pub fn update_channels(
|
||||||
&mut self,
|
&mut self,
|
||||||
channels: &[Channel],
|
channels: &[Channel],
|
||||||
ftw: Option<u32>,
|
ftw: Option<u32>,
|
||||||
pow: Option<u16>,
|
pow: Option<u16>,
|
||||||
acr: Option<u16>,
|
acr: Option<u32>,
|
||||||
) {
|
) {
|
||||||
let mut csr: u8 = *0u8.set_bits(1..3, self.mode as u8);
|
let mut csr: u8 = *0u8.set_bits(1..3, self.mode as u8);
|
||||||
for channel in channels.iter() {
|
for channel in channels.iter() {
|
||||||
@ -582,7 +584,7 @@ impl ProfileSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(acr) = acr {
|
if let Some(acr) = acr {
|
||||||
self.add_write(Register::ACR, &acr.to_be_bytes());
|
self.add_write(Register::ACR, &acr.to_be_bytes()[1..=3]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -606,14 +608,14 @@ impl ProfileSerializer {
|
|||||||
// Pad the buffer to 32-bit alignment by adding dummy writes to CSR and LSRR.
|
// Pad the buffer to 32-bit alignment by adding dummy writes to CSR and LSRR.
|
||||||
let padding = 4 - (self.index % 4);
|
let padding = 4 - (self.index % 4);
|
||||||
match padding {
|
match padding {
|
||||||
0 => {}
|
|
||||||
1 => {
|
1 => {
|
||||||
// For a pad size of 1, we have to pad with 5 bytes to align things.
|
// For a pad size of 1, we have to pad with 5 bytes to align things.
|
||||||
self.add_write(Register::CSR, &[(self.mode as u8) << 1]);
|
self.add_write(Register::CSR, &[(self.mode as u8) << 1]);
|
||||||
self.add_write(Register::LSRR, &[0, 0, 0]);
|
self.add_write(Register::LSRR, &[0, 0]);
|
||||||
}
|
}
|
||||||
2 => self.add_write(Register::CSR, &[(self.mode as u8) << 1]),
|
2 => self.add_write(Register::CSR, &[(self.mode as u8) << 1]),
|
||||||
3 => self.add_write(Register::LSRR, &[0, 0, 0]),
|
3 => self.add_write(Register::LSRR, &[0, 0]),
|
||||||
|
4 => {}
|
||||||
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
"138kpxzxs73zhmd4xi5kw3fddb05gac4mpngizm01831n1ycyhl0"
|
"06qsl59bljr637xcrplbij7ma8l7waryi4lkbd4fxjac0gqpn55s"
|
||||||
|
@ -8,13 +8,13 @@ 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 }
|
||||||
generic-array = "0.14"
|
generic-array = "0.14"
|
||||||
num = { version = "0.3.1", default-features = false }
|
num = { version = "0.4.0", default-features = false }
|
||||||
miniconf = "0.1"
|
miniconf = "0.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
easybench = "1.0"
|
easybench = "1.0"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
ndarray = "0.14"
|
ndarray = "0.15"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "micro"
|
name = "micro"
|
||||||
|
@ -67,7 +67,7 @@ fn iir_bench() {
|
|||||||
let mut xy = iir::Vec5::default();
|
let mut xy = iir::Vec5::default();
|
||||||
println!(
|
println!(
|
||||||
"int::IIR::update(s, x): {}",
|
"int::IIR::update(s, x): {}",
|
||||||
bench_env(0.32241, |x| dut.update(&mut xy, *x))
|
bench_env(0.32241, |x| dut.update(&mut xy, *x, true))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ impl IIR {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `xy` - Current filter state.
|
/// * `xy` - Current filter state.
|
||||||
/// * `x0` - New input.
|
/// * `x0` - New input.
|
||||||
pub fn update(&self, xy: &mut Vec5, x0: f32) -> f32 {
|
pub fn update(&self, xy: &mut Vec5, x0: f32, hold: bool) -> f32 {
|
||||||
let n = self.ba.len();
|
let n = self.ba.len();
|
||||||
debug_assert!(xy.len() == n);
|
debug_assert!(xy.len() == n);
|
||||||
// `xy` contains x0 x1 y0 y1 y2
|
// `xy` contains x0 x1 y0 y1 y2
|
||||||
@ -128,7 +128,11 @@ impl IIR {
|
|||||||
// Store x0 x0 x1 x2 y1 y2
|
// Store x0 x0 x1 x2 y1 y2
|
||||||
xy[0] = x0;
|
xy[0] = x0;
|
||||||
// Compute y0 by multiply-accumulate
|
// Compute y0 by multiply-accumulate
|
||||||
let y0 = macc(self.y_offset, xy, &self.ba);
|
let y0 = if hold {
|
||||||
|
xy[n / 2 + 1]
|
||||||
|
} else {
|
||||||
|
macc(self.y_offset, xy, &self.ba)
|
||||||
|
};
|
||||||
// Limit y0
|
// Limit y0
|
||||||
let y0 = max(self.y_min, min(self.y_max, y0));
|
let y0 = max(self.y_min, min(self.y_max, y0));
|
||||||
// Store y0 x0 x1 y0 y1 y2
|
// Store y0 x0 x1 y0 y1 y2
|
||||||
|
25
hitl/README.md
Normal file
25
hitl/README.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Stabilizer HITL Testing
|
||||||
|
|
||||||
|
This directory contains tooling required for Stabilizer hardware-in-the-loop (HITL) testing.
|
||||||
|
|
||||||
|
There is a `Stabilizer` board connected at the Quartiq office that is accessible via a private HITL
|
||||||
|
repository in order to provide secure hardware testing of the stabilizer application in a public
|
||||||
|
repository.
|
||||||
|
|
||||||
|
**Note**: In order to ensure application security, all HITL runs must first be approved by a Quartiq
|
||||||
|
representative before execution.
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
* Stabilizer is configured with an ethernet connection to a router. The router runs a DHCP server for
|
||||||
|
the local network, and ensures that the Stabilizer used for these tests is available under the hostname `stabilizer-hitl`.
|
||||||
|
* An MQTT broker is running at the hostname `mqtt`.
|
||||||
|
|
||||||
|
# HITL Workflow
|
||||||
|
The private HITL repository does the following:
|
||||||
|
|
||||||
|
1. Check out this repository
|
||||||
|
2. Build firmware images using Cargo
|
||||||
|
3. Program stabilizer
|
||||||
|
4. Execute `hitl/run.sh`
|
||||||
|
|
||||||
|
In order to add new HITL tests, update `run.sh` to include the necessary tests.
|
32
hitl/run.sh
Executable file
32
hitl/run.sh
Executable file
@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Title:
|
||||||
|
# Stabilizer hardware-in-the-loop (HITL) test script.
|
||||||
|
#
|
||||||
|
# Description:
|
||||||
|
# This shell file is executed by the hardware runner in Quartiq's office to exercise the various
|
||||||
|
# hardware aspects of Stabilizer.
|
||||||
|
|
||||||
|
# Enable shell operating mode flags.
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
# Set up python for testing
|
||||||
|
python3 -m venv --system-site-packages py
|
||||||
|
. py/bin/activate
|
||||||
|
python3 -m pip install -r requirements.txt
|
||||||
|
|
||||||
|
cargo flash --elf target/thumbv7em-none-eabihf/release/dual-iir --chip STM32H743ZITx
|
||||||
|
|
||||||
|
# Before attempting to ping the device, sleep to allow Stabilizer to boot.
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
# Test pinging Stabilizer. This exercises that:
|
||||||
|
# * DHCP is functional and an IP has been acquired
|
||||||
|
# * Stabilizer's network is functioning as intended
|
||||||
|
# * The stabilizer application is operational
|
||||||
|
ping -c 5 -w 20 stabilizer-hitl
|
||||||
|
|
||||||
|
# Test the MQTT interface.
|
||||||
|
python3 miniconf.py dt/sinara/dual-iir/04-91-62-d9-7e-5f afe/0='"G2"'
|
||||||
|
python3 miniconf.py dt/sinara/dual-iir/04-91-62-d9-7e-5f afe/0='"G1"' iir_ch/0/0=\
|
||||||
|
'{"y_min": -32767, "y_max": 32767, "y_offset": 0, "ba": [1.0, 0, 0, 0, 0]}'
|
20
miniconf.py
20
miniconf.py
@ -83,19 +83,19 @@ class Miniconf:
|
|||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description='Miniconf command line interface.',
|
description='Miniconf command line interface.',
|
||||||
epilog='''Example:
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
%(prog)s -v -b mqtt dt/sinara/stabilizer afe/0 '"G10"'
|
epilog='''Examples:
|
||||||
''')
|
%(prog)s dt/sinara/stabilizer afe/0='"G2"' iir_ch/0/0=\
|
||||||
|
'{"y_min": -32767, "y_max": 32767, "y_offset": 0, "ba": [1.0, 0, 0, 0, 0]}'
|
||||||
|
''')
|
||||||
parser.add_argument('-v', '--verbose', action='count', default=0,
|
parser.add_argument('-v', '--verbose', action='count', default=0,
|
||||||
help='Increase logging verbosity')
|
help='Increase logging verbosity')
|
||||||
parser.add_argument('--broker', '-b', default='mqtt', type=str,
|
parser.add_argument('--broker', '-b', default='mqtt', type=str,
|
||||||
help='The MQTT broker address')
|
help='The MQTT broker address')
|
||||||
parser.add_argument('prefix', type=str,
|
parser.add_argument('prefix', type=str,
|
||||||
help='The MQTT topic prefix of the target')
|
help='The MQTT topic prefix of the target')
|
||||||
parser.add_argument('path', type=str,
|
parser.add_argument('settings', metavar="KEY=VALUE", nargs='+',
|
||||||
help='The setting path to configure')
|
help='JSON encoded values for settings path keys.')
|
||||||
parser.add_argument('value', type=str,
|
|
||||||
help='The value of setting in JSON format')
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@ -107,8 +107,10 @@ def main():
|
|||||||
|
|
||||||
async def configure_settings():
|
async def configure_settings():
|
||||||
interface = await Miniconf.create(args.prefix, args.broker)
|
interface = await Miniconf.create(args.prefix, args.broker)
|
||||||
response = await interface.command(args.path, json.loads(args.value))
|
for kv in args.settings:
|
||||||
print(f"Response: {response}")
|
path, value = kv.split("=", 1)
|
||||||
|
response = await interface.command(path, json.loads(value))
|
||||||
|
print(response)
|
||||||
|
|
||||||
loop.run_until_complete(configure_settings())
|
loop.run_until_complete(configure_settings())
|
||||||
|
|
||||||
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
gmqtt
|
@ -2,35 +2,47 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
use stm32h7xx_hal as hal;
|
use stabilizer::{hardware, net};
|
||||||
|
|
||||||
use stabilizer::hardware;
|
use miniconf::Miniconf;
|
||||||
|
|
||||||
use miniconf::{minimq, Miniconf, MqttInterface};
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use dsp::iir;
|
use dsp::iir;
|
||||||
use hardware::{
|
use hardware::{
|
||||||
Adc0Input, Adc1Input, AfeGain, CycleCounter, Dac0Output, Dac1Output,
|
Adc0Input, Adc1Input, AfeGain, Dac0Output, Dac1Output, DigitalInput1,
|
||||||
NetworkStack, AFE0, AFE1,
|
InputPin, AFE0, AFE1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use net::{Action, MiniconfInterface};
|
||||||
|
|
||||||
const SCALE: f32 = i16::MAX as _;
|
const SCALE: f32 = i16::MAX as _;
|
||||||
|
|
||||||
// The number of cascaded IIR biquads per channel. Select 1 or 2!
|
// The number of cascaded IIR biquads per channel. Select 1 or 2!
|
||||||
const IIR_CASCADE_LENGTH: usize = 1;
|
const IIR_CASCADE_LENGTH: usize = 1;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Miniconf)]
|
#[derive(Clone, Copy, Debug, Deserialize, Miniconf)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
afe: [AfeGain; 2],
|
afe: [AfeGain; 2],
|
||||||
iir_ch: [[iir::IIR; IIR_CASCADE_LENGTH]; 2],
|
iir_ch: [[iir::IIR; IIR_CASCADE_LENGTH]; 2],
|
||||||
|
allow_hold: bool,
|
||||||
|
force_hold: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Settings {
|
impl Default for Settings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
// Analog frontend programmable gain amplifier gains (G1, G2, G5, G10)
|
||||||
afe: [AfeGain::G1, AfeGain::G1],
|
afe: [AfeGain::G1, AfeGain::G1],
|
||||||
|
// IIR filter tap gains are an array `[b0, b1, b2, a1, a2]` such that the
|
||||||
|
// new output is computed as `y0 = a1*y1 + a2*y2 + b0*x0 + b1*x1 + b2*x2`.
|
||||||
|
// The array is `iir_state[channel-index][cascade-index][coeff-index]`.
|
||||||
|
// The IIR coefficients can be mapped to other transfer function
|
||||||
|
// representations, for example as described in https://arxiv.org/abs/1508.06319
|
||||||
iir_ch: [[iir::IIR::new(1., -SCALE, SCALE); IIR_CASCADE_LENGTH]; 2],
|
iir_ch: [[iir::IIR::new(1., -SCALE, SCALE); IIR_CASCADE_LENGTH]; 2],
|
||||||
|
// Permit the DI1 digital input to suppress filter output updates.
|
||||||
|
allow_hold: false,
|
||||||
|
// Force suppress filter output updates.
|
||||||
|
force_hold: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -39,41 +51,34 @@ impl Default for Settings {
|
|||||||
const APP: () = {
|
const APP: () = {
|
||||||
struct Resources {
|
struct Resources {
|
||||||
afes: (AFE0, AFE1),
|
afes: (AFE0, AFE1),
|
||||||
|
digital_input1: DigitalInput1,
|
||||||
adcs: (Adc0Input, Adc1Input),
|
adcs: (Adc0Input, Adc1Input),
|
||||||
dacs: (Dac0Output, Dac1Output),
|
dacs: (Dac0Output, Dac1Output),
|
||||||
mqtt_interface:
|
mqtt_config: MiniconfInterface<Settings>,
|
||||||
MqttInterface<Settings, NetworkStack, minimq::consts::U256>,
|
|
||||||
clock: CycleCounter,
|
|
||||||
|
|
||||||
// Format: iir_state[ch][cascade-no][coeff]
|
|
||||||
#[init([[[0.; 5]; IIR_CASCADE_LENGTH]; 2])]
|
#[init([[[0.; 5]; IIR_CASCADE_LENGTH]; 2])]
|
||||||
iir_state: [[iir::Vec5; IIR_CASCADE_LENGTH]; 2],
|
iir_state: [[iir::Vec5; IIR_CASCADE_LENGTH]; 2],
|
||||||
#[init([[iir::IIR::new(1., -SCALE, SCALE); IIR_CASCADE_LENGTH]; 2])]
|
settings: Settings,
|
||||||
iir_ch: [[iir::IIR; IIR_CASCADE_LENGTH]; 2],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[init]
|
#[init(spawn=[settings_update])]
|
||||||
fn init(c: init::Context) -> init::LateResources {
|
fn init(c: init::Context) -> init::LateResources {
|
||||||
// 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 mqtt_interface = {
|
let mqtt_config = MiniconfInterface::new(
|
||||||
let mqtt_client = {
|
stabilizer.net.stack,
|
||||||
minimq::MqttClient::new(
|
"",
|
||||||
hardware::design_parameters::MQTT_BROKER.into(),
|
&net::get_device_prefix(
|
||||||
"",
|
env!("CARGO_BIN_NAME"),
|
||||||
stabilizer.net.stack,
|
stabilizer.net.mac_address,
|
||||||
)
|
),
|
||||||
.unwrap()
|
stabilizer.net.phy,
|
||||||
};
|
stabilizer.cycle_counter,
|
||||||
|
);
|
||||||
|
|
||||||
MqttInterface::new(
|
// Spawn a settings update for default settings.
|
||||||
mqtt_client,
|
c.spawn.settings_update().unwrap();
|
||||||
"dt/sinara/stabilizer",
|
|
||||||
Settings::default(),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Enable ADC/DAC events
|
// Enable ADC/DAC events
|
||||||
stabilizer.adcs.0.start();
|
stabilizer.adcs.0.start();
|
||||||
@ -85,11 +90,12 @@ const APP: () = {
|
|||||||
stabilizer.adc_dac_timer.start();
|
stabilizer.adc_dac_timer.start();
|
||||||
|
|
||||||
init::LateResources {
|
init::LateResources {
|
||||||
mqtt_interface,
|
|
||||||
afes: stabilizer.afes,
|
afes: stabilizer.afes,
|
||||||
adcs: stabilizer.adcs,
|
adcs: stabilizer.adcs,
|
||||||
dacs: stabilizer.dacs,
|
dacs: stabilizer.dacs,
|
||||||
clock: stabilizer.cycle_counter,
|
mqtt_config,
|
||||||
|
digital_input1: stabilizer.digital_inputs.1,
|
||||||
|
settings: Settings::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +115,7 @@ const APP: () = {
|
|||||||
///
|
///
|
||||||
/// Because the ADC and DAC operate at the same rate, these two constraints actually implement
|
/// Because the ADC and DAC operate at the same rate, these two constraints actually implement
|
||||||
/// 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.
|
||||||
#[task(binds=DMA1_STR4, resources=[adcs, dacs, iir_state, iir_ch], priority=2)]
|
#[task(binds=DMA1_STR4, resources=[adcs, digital_input1, dacs, iir_state, settings], priority=2)]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[link_section = ".itcm.process"]
|
#[link_section = ".itcm.process"]
|
||||||
fn process(c: process::Context) {
|
fn process(c: process::Context) {
|
||||||
@ -123,13 +129,19 @@ const APP: () = {
|
|||||||
c.resources.dacs.1.acquire_buffer(),
|
c.resources.dacs.1.acquire_buffer(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let hold = c.resources.settings.force_hold
|
||||||
|
|| (c.resources.digital_input1.is_high().unwrap()
|
||||||
|
&& c.resources.settings.allow_hold);
|
||||||
|
|
||||||
for channel in 0..adc_samples.len() {
|
for channel in 0..adc_samples.len() {
|
||||||
for sample in 0..adc_samples[0].len() {
|
for sample in 0..adc_samples[0].len() {
|
||||||
let x = f32::from(adc_samples[channel][sample] as i16);
|
let mut y = f32::from(adc_samples[channel][sample] as i16);
|
||||||
let mut y = x;
|
|
||||||
for i in 0..c.resources.iir_state[channel].len() {
|
for i in 0..c.resources.iir_state[channel].len() {
|
||||||
y = c.resources.iir_ch[channel][i]
|
y = c.resources.settings.iir_ch[channel][i].update(
|
||||||
.update(&mut c.resources.iir_state[channel][i], y);
|
&mut c.resources.iir_state[channel][i],
|
||||||
|
y,
|
||||||
|
hold,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// Note(unsafe): The filter limits ensure that the value is in range.
|
// Note(unsafe): The filter limits ensure that the value is in range.
|
||||||
// The truncation introduces 1/2 LSB distortion.
|
// The truncation introduces 1/2 LSB distortion.
|
||||||
@ -140,47 +152,29 @@ const APP: () = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[idle(resources=[mqtt_interface, clock], spawn=[settings_update])]
|
#[idle(resources=[mqtt_config], spawn=[settings_update])]
|
||||||
fn idle(mut c: idle::Context) -> ! {
|
fn idle(mut c: idle::Context) -> ! {
|
||||||
let clock = c.resources.clock;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let sleep = c.resources.mqtt_interface.lock(|interface| {
|
|
||||||
match interface.network_stack().poll(clock.current_ms()) {
|
|
||||||
Ok(updated) => !updated,
|
|
||||||
Err(err) => {
|
|
||||||
log::info!("Network error: {:?}", err);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
match c
|
match c
|
||||||
.resources
|
.resources
|
||||||
.mqtt_interface
|
.mqtt_config
|
||||||
.lock(|interface| interface.update())
|
.lock(|config_interface| config_interface.update())
|
||||||
{
|
{
|
||||||
Ok(update) => {
|
Some(Action::Sleep) => cortex_m::asm::wfi(),
|
||||||
if update {
|
Some(Action::UpdateSettings) => {
|
||||||
c.spawn.settings_update().unwrap();
|
c.spawn.settings_update().unwrap()
|
||||||
} else if sleep {
|
|
||||||
cortex_m::asm::wfi();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(miniconf::MqttError::Network(
|
_ => {}
|
||||||
smoltcp_nal::NetworkError::NoIpAddress,
|
|
||||||
)) => {}
|
|
||||||
Err(error) => log::info!("Unexpected error: {:?}", error),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(priority = 1, resources=[mqtt_interface, afes, iir_ch])]
|
#[task(priority = 1, resources=[mqtt_config, afes, settings])]
|
||||||
fn settings_update(mut c: settings_update::Context) {
|
fn settings_update(mut c: settings_update::Context) {
|
||||||
let settings = &c.resources.mqtt_interface.settings;
|
let settings = &c.resources.mqtt_config.mqtt.settings;
|
||||||
|
|
||||||
// Update the IIR channels.
|
// Update the IIR channels.
|
||||||
c.resources.iir_ch.lock(|iir| *iir = settings.iir_ch);
|
c.resources.settings.lock(|current| *current = *settings);
|
||||||
|
|
||||||
// Update AFEs
|
// Update AFEs
|
||||||
c.resources.afes.0.set_gain(settings.afe[0]);
|
c.resources.afes.0.set_gain(settings.afe[0]);
|
||||||
@ -189,7 +183,7 @@ const APP: () = {
|
|||||||
|
|
||||||
#[task(binds = ETH, priority = 1)]
|
#[task(binds = ETH, priority = 1)]
|
||||||
fn eth(_: eth::Context) {
|
fn eth(_: eth::Context) {
|
||||||
unsafe { hal::ethernet::interrupt_handler() }
|
unsafe { stm32h7xx_hal::ethernet::interrupt_handler() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(binds = SPI2, priority = 3)]
|
#[task(binds = SPI2, priority = 3)]
|
||||||
|
@ -4,16 +4,20 @@
|
|||||||
|
|
||||||
use generic_array::typenum::U4;
|
use generic_array::typenum::U4;
|
||||||
|
|
||||||
use miniconf::{minimq, Miniconf, MqttInterface};
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use dsp::{Accu, Complex, ComplexExt, Lockin, RPLL};
|
use dsp::{Accu, Complex, ComplexExt, Lockin, RPLL};
|
||||||
|
|
||||||
|
use stabilizer::net;
|
||||||
|
|
||||||
use stabilizer::hardware::{
|
use stabilizer::hardware::{
|
||||||
design_parameters, setup, Adc0Input, Adc1Input, AfeGain, CycleCounter,
|
design_parameters, setup, Adc0Input, Adc1Input, AfeGain, Dac0Output,
|
||||||
Dac0Output, Dac1Output, InputStamper, NetworkStack, AFE0, AFE1,
|
Dac1Output, InputStamper, AFE0, AFE1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use miniconf::Miniconf;
|
||||||
|
use stabilizer::net::{Action, MiniconfInterface};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Deserialize, Miniconf)]
|
#[derive(Copy, Clone, Debug, Deserialize, Miniconf)]
|
||||||
enum Conf {
|
enum Conf {
|
||||||
PowerPhase,
|
PowerPhase,
|
||||||
@ -56,11 +60,7 @@ const APP: () = {
|
|||||||
afes: (AFE0, AFE1),
|
afes: (AFE0, AFE1),
|
||||||
adcs: (Adc0Input, Adc1Input),
|
adcs: (Adc0Input, Adc1Input),
|
||||||
dacs: (Dac0Output, Dac1Output),
|
dacs: (Dac0Output, Dac1Output),
|
||||||
clock: CycleCounter,
|
mqtt_config: MiniconfInterface<Settings>,
|
||||||
|
|
||||||
mqtt_interface:
|
|
||||||
MqttInterface<Settings, NetworkStack, minimq::consts::U256>,
|
|
||||||
|
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
|
|
||||||
timestamper: InputStamper,
|
timestamper: InputStamper,
|
||||||
@ -73,21 +73,16 @@ const APP: () = {
|
|||||||
// Configure the microcontroller
|
// Configure the microcontroller
|
||||||
let (mut stabilizer, _pounder) = setup(c.core, c.device);
|
let (mut stabilizer, _pounder) = setup(c.core, c.device);
|
||||||
|
|
||||||
let mqtt_interface = {
|
let mqtt_config = MiniconfInterface::new(
|
||||||
let mqtt_client = minimq::MqttClient::new(
|
stabilizer.net.stack,
|
||||||
design_parameters::MQTT_BROKER.into(),
|
"",
|
||||||
"",
|
&net::get_device_prefix(
|
||||||
stabilizer.net.stack,
|
env!("CARGO_BIN_NAME"),
|
||||||
)
|
stabilizer.net.mac_address,
|
||||||
.unwrap();
|
),
|
||||||
|
stabilizer.net.phy,
|
||||||
MqttInterface::new(
|
stabilizer.cycle_counter,
|
||||||
mqtt_client,
|
);
|
||||||
"dt/sinara/lockin",
|
|
||||||
Settings::default(),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let settings = Settings::default();
|
let settings = Settings::default();
|
||||||
|
|
||||||
@ -118,10 +113,8 @@ const APP: () = {
|
|||||||
afes: stabilizer.afes,
|
afes: stabilizer.afes,
|
||||||
adcs: stabilizer.adcs,
|
adcs: stabilizer.adcs,
|
||||||
dacs: stabilizer.dacs,
|
dacs: stabilizer.dacs,
|
||||||
|
mqtt_config,
|
||||||
timestamper: stabilizer.timestamper,
|
timestamper: stabilizer.timestamper,
|
||||||
clock: stabilizer.cycle_counter,
|
|
||||||
|
|
||||||
mqtt_interface,
|
|
||||||
|
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
@ -202,44 +195,26 @@ const APP: () = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[idle(resources=[mqtt_interface, clock], spawn=[settings_update])]
|
#[idle(resources=[mqtt_config], spawn=[settings_update])]
|
||||||
fn idle(mut c: idle::Context) -> ! {
|
fn idle(mut c: idle::Context) -> ! {
|
||||||
let clock = c.resources.clock;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let sleep = c.resources.mqtt_interface.lock(|interface| {
|
|
||||||
match interface.network_stack().poll(clock.current_ms()) {
|
|
||||||
Ok(updated) => !updated,
|
|
||||||
Err(err) => {
|
|
||||||
log::info!("Network error: {:?}", err);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
match c
|
match c
|
||||||
.resources
|
.resources
|
||||||
.mqtt_interface
|
.mqtt_config
|
||||||
.lock(|interface| interface.update())
|
.lock(|config_interface| config_interface.update())
|
||||||
{
|
{
|
||||||
Ok(update) => {
|
Some(Action::Sleep) => cortex_m::asm::wfi(),
|
||||||
if update {
|
Some(Action::UpdateSettings) => {
|
||||||
c.spawn.settings_update().unwrap();
|
c.spawn.settings_update().unwrap()
|
||||||
} else if sleep {
|
|
||||||
cortex_m::asm::wfi();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(miniconf::MqttError::Network(
|
_ => {}
|
||||||
smoltcp_nal::NetworkError::NoIpAddress,
|
|
||||||
)) => {}
|
|
||||||
Err(error) => log::info!("Unexpected error: {:?}", error),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(priority = 1, resources=[mqtt_interface, settings, afes])]
|
#[task(priority = 1, resources=[mqtt_config, settings, afes])]
|
||||||
fn settings_update(mut c: settings_update::Context) {
|
fn settings_update(mut c: settings_update::Context) {
|
||||||
let settings = &c.resources.mqtt_interface.settings;
|
let settings = &c.resources.mqtt_config.mqtt.settings;
|
||||||
|
|
||||||
c.resources.afes.0.set_gain(settings.afe[0]);
|
c.resources.afes.0.set_gain(settings.afe[0]);
|
||||||
c.resources.afes.1.set_gain(settings.afe[1]);
|
c.resources.afes.1.set_gain(settings.afe[1]);
|
||||||
|
@ -13,8 +13,8 @@ use embedded_hal::digital::v2::{InputPin, OutputPin};
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
adc, afe, cycle_counter::CycleCounter, dac, design_parameters,
|
adc, afe, cycle_counter::CycleCounter, dac, design_parameters,
|
||||||
digital_input_stamper, eeprom, pounder, timers, DdsOutput, NetworkStack,
|
digital_input_stamper, eeprom, pounder, timers, DdsOutput, DigitalInput0,
|
||||||
AFE0, AFE1,
|
DigitalInput1, EthernetPhy, NetworkStack, AFE0, AFE1,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct NetStorage {
|
pub struct NetStorage {
|
||||||
@ -56,7 +56,8 @@ impl NetStorage {
|
|||||||
/// The available networking devices on Stabilizer.
|
/// The available networking devices on Stabilizer.
|
||||||
pub struct NetworkDevices {
|
pub struct NetworkDevices {
|
||||||
pub stack: NetworkStack,
|
pub stack: NetworkStack,
|
||||||
pub phy: ethernet::phy::LAN8742A<ethernet::EthernetMAC>,
|
pub phy: EthernetPhy,
|
||||||
|
pub mac_address: smoltcp::wire::EthernetAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The available hardware interfaces on Stabilizer.
|
/// The available hardware interfaces on Stabilizer.
|
||||||
@ -69,6 +70,7 @@ pub struct StabilizerDevices {
|
|||||||
pub timestamp_timer: timers::TimestampTimer,
|
pub timestamp_timer: timers::TimestampTimer,
|
||||||
pub net: NetworkDevices,
|
pub net: NetworkDevices,
|
||||||
pub cycle_counter: CycleCounter,
|
pub cycle_counter: CycleCounter,
|
||||||
|
pub digital_inputs: (DigitalInput0, DigitalInput1),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The available Pounder-specific hardware interfaces.
|
/// The available Pounder-specific hardware interfaces.
|
||||||
@ -439,6 +441,12 @@ pub fn setup(
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let digital_inputs = {
|
||||||
|
let di0 = gpiog.pg9.into_floating_input();
|
||||||
|
let di1 = gpioc.pc15.into_floating_input();
|
||||||
|
(di0, di1)
|
||||||
|
};
|
||||||
|
|
||||||
let mut eeprom_i2c = {
|
let mut eeprom_i2c = {
|
||||||
let sda = gpiof.pf0.into_alternate_af4().set_open_drain();
|
let sda = gpiof.pf0.into_alternate_af4().set_open_drain();
|
||||||
let scl = gpiof.pf1.into_alternate_af4().set_open_drain();
|
let scl = gpiof.pf1.into_alternate_af4().set_open_drain();
|
||||||
@ -495,13 +503,10 @@ pub fn setup(
|
|||||||
.set_speed(hal::gpio::Speed::VeryHigh);
|
.set_speed(hal::gpio::Speed::VeryHigh);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mac_addr = match eeprom::read_eui48(&mut eeprom_i2c) {
|
let mac_addr = smoltcp::wire::EthernetAddress(eeprom::read_eui48(
|
||||||
Err(_) => {
|
&mut eeprom_i2c,
|
||||||
info!("Could not read EEPROM, using default MAC address");
|
&mut delay,
|
||||||
smoltcp::wire::EthernetAddress([0x10, 0xE2, 0xD5, 0x00, 0x03, 0x00])
|
));
|
||||||
}
|
|
||||||
Ok(raw_mac) => smoltcp::wire::EthernetAddress(raw_mac),
|
|
||||||
};
|
|
||||||
|
|
||||||
let network_devices = {
|
let network_devices = {
|
||||||
// Configure the ethernet controller
|
// Configure the ethernet controller
|
||||||
@ -594,14 +599,27 @@ pub fn setup(
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let random_seed = {
|
||||||
|
let mut rng =
|
||||||
|
device.RNG.constrain(ccdr.peripheral.RNG, &ccdr.clocks);
|
||||||
|
let mut data = [0u8; 4];
|
||||||
|
rng.fill(&mut data).unwrap();
|
||||||
|
data
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut stack = smoltcp_nal::NetworkStack::new(
|
||||||
|
interface,
|
||||||
|
sockets,
|
||||||
|
&handles,
|
||||||
|
Some(dhcp_client),
|
||||||
|
);
|
||||||
|
|
||||||
|
stack.seed_random_port(&random_seed);
|
||||||
|
|
||||||
NetworkDevices {
|
NetworkDevices {
|
||||||
stack: smoltcp_nal::NetworkStack::new(
|
stack,
|
||||||
interface,
|
|
||||||
sockets,
|
|
||||||
&handles,
|
|
||||||
Some(dhcp_client),
|
|
||||||
),
|
|
||||||
phy: lan8742a,
|
phy: lan8742a,
|
||||||
|
mac_address: mac_addr,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -875,6 +893,7 @@ pub fn setup(
|
|||||||
adc_dac_timer: sampling_timer,
|
adc_dac_timer: sampling_timer,
|
||||||
timestamp_timer,
|
timestamp_timer,
|
||||||
cycle_counter,
|
cycle_counter,
|
||||||
|
digital_inputs,
|
||||||
};
|
};
|
||||||
|
|
||||||
// info!("Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap());
|
// info!("Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap());
|
||||||
|
@ -1,12 +1,42 @@
|
|||||||
use embedded_hal::blocking::i2c::WriteRead;
|
use embedded_hal::blocking::{delay::DelayMs, i2c::WriteRead};
|
||||||
|
|
||||||
|
// The EEPROM is a variant without address bits, so the 3 LSB of this word are "dont-cares".
|
||||||
const I2C_ADDR: u8 = 0x50;
|
const I2C_ADDR: u8 = 0x50;
|
||||||
|
|
||||||
pub fn read_eui48<T>(i2c: &mut T) -> Result<[u8; 6], T::Error>
|
// The MAC address is stored in the last 6 bytes of the 256 byte address space.
|
||||||
|
const MAC_POINTER: u8 = 0xFA;
|
||||||
|
|
||||||
|
pub fn read_eui48<T>(i2c: &mut T, delay: &mut impl DelayMs<u8>) -> [u8; 6]
|
||||||
where
|
where
|
||||||
T: WriteRead,
|
T: WriteRead,
|
||||||
{
|
{
|
||||||
let mut buffer = [0u8; 6];
|
let mut previous_read: Option<[u8; 6]> = None;
|
||||||
i2c.write_read(I2C_ADDR, &[0xFA_u8], &mut buffer)?;
|
// On Stabilizer v1.1 and earlier hardware, there is a fault where the I2C bus is not connected
|
||||||
Ok(buffer)
|
// to the CPU until the P12V0A rail enables, which can take many seconds, or may never come up
|
||||||
|
// at all. During these transient turn-on conditions, we may fail the I2C read operation. To
|
||||||
|
// accomodate this, we repeat the I2C read for a set number of attempts with a fixed delay
|
||||||
|
// between them. Then, we wait for the bus to stabilize by waiting until the MAC address
|
||||||
|
// read-out is identical for two consecutive reads.
|
||||||
|
for _ in 0..40 {
|
||||||
|
let mut buffer = [0u8; 6];
|
||||||
|
if i2c
|
||||||
|
.write_read(I2C_ADDR, &[MAC_POINTER], &mut buffer)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
if let Some(old_read) = previous_read {
|
||||||
|
if old_read == buffer {
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previous_read.replace(buffer);
|
||||||
|
} else {
|
||||||
|
// Remove any pending previous read if we failed the last attempt.
|
||||||
|
previous_read.take();
|
||||||
|
}
|
||||||
|
|
||||||
|
delay.delay_ms(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("Failed to read MAC address");
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
///! Module for all hardware-specific setup of Stabilizer
|
///! Module for all hardware-specific setup of Stabilizer
|
||||||
use stm32h7xx_hal as hal;
|
use stm32h7xx_hal as hal;
|
||||||
|
|
||||||
|
// Re-export for the DigitalInputs below:
|
||||||
|
pub use embedded_hal::digital::v2::InputPin;
|
||||||
|
|
||||||
#[cfg(feature = "semihosting")]
|
#[cfg(feature = "semihosting")]
|
||||||
use panic_semihosting as _;
|
use panic_semihosting as _;
|
||||||
|
|
||||||
@ -34,12 +37,22 @@ pub type AFE1 = afe::ProgrammableGainAmplifier<
|
|||||||
hal::gpio::gpiod::PD15<hal::gpio::Output<hal::gpio::PushPull>>,
|
hal::gpio::gpiod::PD15<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
// Type alias for digital input 0 (DI0).
|
||||||
|
pub type DigitalInput0 =
|
||||||
|
hal::gpio::gpiog::PG9<hal::gpio::Input<hal::gpio::Floating>>;
|
||||||
|
|
||||||
|
// Type alias for digital input 1 (DI1).
|
||||||
|
pub type DigitalInput1 =
|
||||||
|
hal::gpio::gpioc::PC15<hal::gpio::Input<hal::gpio::Floating>>;
|
||||||
|
|
||||||
pub type NetworkStack = smoltcp_nal::NetworkStack<
|
pub type NetworkStack = smoltcp_nal::NetworkStack<
|
||||||
'static,
|
'static,
|
||||||
'static,
|
'static,
|
||||||
hal::ethernet::EthernetDMA<'static>,
|
hal::ethernet::EthernetDMA<'static>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
pub type EthernetPhy = hal::ethernet::phy::LAN8742A<hal::ethernet::EthernetMAC>;
|
||||||
|
|
||||||
pub use configuration::{setup, PounderDevices, StabilizerDevices};
|
pub use configuration::{setup, PounderDevices, StabilizerDevices};
|
||||||
|
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
|
@ -144,14 +144,15 @@ impl<'a> ProfileBuilder<'a> {
|
|||||||
/// * `channels` - A list of channels to apply the configuration to.
|
/// * `channels` - A list of channels to apply the configuration to.
|
||||||
/// * `ftw` - If provided, indicates a frequency tuning word for the channels.
|
/// * `ftw` - If provided, indicates a frequency tuning word for the channels.
|
||||||
/// * `pow` - If provided, indicates a phase offset word for the channels.
|
/// * `pow` - If provided, indicates a phase offset word for the channels.
|
||||||
/// * `acr` - If provided, indicates the amplitude control register for the channels.
|
/// * `acr` - If provided, indicates the amplitude control register for the channels. The
|
||||||
|
/// 24-bits of the ACR should be stored in the last 3 LSB.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn update_channels(
|
pub fn update_channels(
|
||||||
mut self,
|
mut self,
|
||||||
channels: &[Channel],
|
channels: &[Channel],
|
||||||
ftw: Option<u32>,
|
ftw: Option<u32>,
|
||||||
pow: Option<u16>,
|
pow: Option<u16>,
|
||||||
acr: Option<u16>,
|
acr: Option<u32>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
self.serializer.update_channels(channels, ftw, pow, acr);
|
self.serializer.update_channels(channels, ftw, pow, acr);
|
||||||
self
|
self
|
||||||
|
@ -231,14 +231,16 @@ impl ad9959::Interface for QspiInterface {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.qspi
|
self.qspi
|
||||||
.write(encoded_address, &encoded_payload)
|
.write(encoded_address.into(), &encoded_payload)
|
||||||
.map_err(|_| Error::Qspi)
|
.map_err(|_| Error::Qspi)
|
||||||
}
|
}
|
||||||
ad9959::Mode::FourBitSerial => {
|
ad9959::Mode::FourBitSerial => {
|
||||||
if self.streaming {
|
if self.streaming {
|
||||||
Err(Error::Qspi)
|
Err(Error::Qspi)
|
||||||
} else {
|
} else {
|
||||||
self.qspi.write(addr, data).map_err(|_| Error::Qspi)?;
|
self.qspi
|
||||||
|
.write(addr.into(), data)
|
||||||
|
.map_err(|_| Error::Qspi)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -257,7 +259,7 @@ impl ad9959::Interface for QspiInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.qspi
|
self.qspi
|
||||||
.read(0x80_u8 | addr, dest)
|
.read((0x80_u8 | addr).into(), dest)
|
||||||
.map_err(|_| Error::Qspi)
|
.map_err(|_| Error::Qspi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,3 +5,4 @@
|
|||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
pub mod hardware;
|
pub mod hardware;
|
||||||
|
pub mod net;
|
||||||
|
148
src/net/mod.rs
Normal file
148
src/net/mod.rs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
use crate::hardware::{
|
||||||
|
design_parameters::MQTT_BROKER, CycleCounter, EthernetPhy, NetworkStack,
|
||||||
|
};
|
||||||
|
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
use heapless::{consts, String};
|
||||||
|
use miniconf::minimq;
|
||||||
|
|
||||||
|
/// Potential actions for firmware to take.
|
||||||
|
pub enum Action {
|
||||||
|
/// Indicates that firmware can sleep for the next event.
|
||||||
|
Sleep,
|
||||||
|
|
||||||
|
/// Indicates that settings have updated and firmware needs to propogate changes.
|
||||||
|
UpdateSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MQTT settings interface.
|
||||||
|
pub struct MiniconfInterface<S>
|
||||||
|
where
|
||||||
|
S: miniconf::Miniconf + Default,
|
||||||
|
{
|
||||||
|
pub mqtt: miniconf::MqttInterface<S, NetworkStack, minimq::consts::U256>,
|
||||||
|
clock: CycleCounter,
|
||||||
|
phy: EthernetPhy,
|
||||||
|
network_was_reset: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> MiniconfInterface<S>
|
||||||
|
where
|
||||||
|
S: miniconf::Miniconf + Default,
|
||||||
|
{
|
||||||
|
/// Construct a new MQTT settings interface.
|
||||||
|
///
|
||||||
|
/// # Args
|
||||||
|
/// * `stack` - The network stack to use for communication.
|
||||||
|
/// * `client_id` - The ID of the MQTT client. May be an empty string for auto-assigning.
|
||||||
|
/// * `prefix` - The MQTT device prefix to use for this device.
|
||||||
|
/// * `phy` - The PHY driver for querying the link state.
|
||||||
|
/// * `clock` - The clock to utilize for querying the current system time.
|
||||||
|
pub fn new(
|
||||||
|
stack: NetworkStack,
|
||||||
|
client_id: &str,
|
||||||
|
prefix: &str,
|
||||||
|
phy: EthernetPhy,
|
||||||
|
clock: CycleCounter,
|
||||||
|
) -> Self {
|
||||||
|
let mqtt = {
|
||||||
|
let mqtt_client = {
|
||||||
|
minimq::MqttClient::new(MQTT_BROKER.into(), client_id, stack)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
miniconf::MqttInterface::new(mqtt_client, prefix, S::default())
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
mqtt,
|
||||||
|
clock,
|
||||||
|
phy,
|
||||||
|
network_was_reset: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the MQTT interface and service the network
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An option containing an action that should be completed as a result of network servicing.
|
||||||
|
pub fn update(&mut self) -> Option<Action> {
|
||||||
|
let now = self.clock.current_ms();
|
||||||
|
|
||||||
|
// First, service the network stack to process and inbound and outbound traffic.
|
||||||
|
let sleep = match self.mqtt.network_stack().poll(now) {
|
||||||
|
Ok(updated) => !updated,
|
||||||
|
Err(err) => {
|
||||||
|
log::info!("Network error: {:?}", err);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the PHY indicates there's no more ethernet link, reset the DHCP server in the network
|
||||||
|
// stack.
|
||||||
|
if self.phy.poll_link() == false {
|
||||||
|
// Only reset the network stack once per link reconnection. This prevents us from
|
||||||
|
// sending an excessive number of DHCP requests.
|
||||||
|
if !self.network_was_reset {
|
||||||
|
self.network_was_reset = true;
|
||||||
|
self.mqtt.network_stack().handle_link_reset();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.network_was_reset = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, service the MQTT interface and handle any necessary messages.
|
||||||
|
match self.mqtt.update() {
|
||||||
|
Ok(true) => Some(Action::UpdateSettings),
|
||||||
|
Ok(false) if sleep => Some(Action::Sleep),
|
||||||
|
Ok(_) => None,
|
||||||
|
|
||||||
|
Err(miniconf::MqttError::Network(
|
||||||
|
smoltcp_nal::NetworkError::NoIpAddress,
|
||||||
|
)) => None,
|
||||||
|
|
||||||
|
Err(error) => {
|
||||||
|
log::info!("Unexpected error: {:?}", error);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the MQTT prefix of a device.
|
||||||
|
///
|
||||||
|
/// # Args
|
||||||
|
/// * `app` - The name of the application that is executing.
|
||||||
|
/// * `mac` - The ethernet MAC address of the device.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// The MQTT prefix used for this device.
|
||||||
|
pub fn get_device_prefix(
|
||||||
|
app: &str,
|
||||||
|
mac: smoltcp_nal::smoltcp::wire::EthernetAddress,
|
||||||
|
) -> String<consts::U128> {
|
||||||
|
let mac_string = {
|
||||||
|
let mut mac_string: String<consts::U32> = String::new();
|
||||||
|
let mac = mac.as_bytes();
|
||||||
|
|
||||||
|
// Note(unwrap): 32-bytes is guaranteed to be valid for any mac address, as the address has
|
||||||
|
// a fixed length.
|
||||||
|
write!(
|
||||||
|
&mut mac_string,
|
||||||
|
"{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
|
||||||
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
mac_string
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note(unwrap): The mac address + binary name must be short enough to fit into this string. If
|
||||||
|
// they are defined too long, this will panic and the device will fail to boot.
|
||||||
|
let mut prefix: String<consts::U128> = String::new();
|
||||||
|
write!(&mut prefix, "dt/sinara/{}/{}", app, mac_string).unwrap();
|
||||||
|
|
||||||
|
prefix
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user