Merge branch 'feature/pounder-support'
This commit is contained in:
commit
2ddcdec537
|
@ -0,0 +1,3 @@
|
|||
[submodule "stm32h7xx-hal"]
|
||||
path = stm32h7xx-hal
|
||||
url = https://github.com/quartiq/stm32h7xx-hal.git
|
|
@ -1,391 +1,572 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "ad9959"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"embedded-hal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aligned"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb1ce8b3382016136ab1d31a1b5ce807144f8b7eb2d5f16b2108f0f07edceb94"
|
||||
dependencies = [
|
||||
"as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"as-slice",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "as-slice"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37dfb65bc03b2bc85ee827004f14a6817e04160e3b1a28931986a666a9290e70"
|
||||
dependencies = [
|
||||
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"generic-array 0.12.3",
|
||||
"generic-array 0.13.2",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asm-delay"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bc4896200a8422e15fbb1899cb436889d215954d376bf32d311a2330ac13440"
|
||||
dependencies = [
|
||||
"bitrate",
|
||||
"cortex-m",
|
||||
"embedded-hal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
|
||||
[[package]]
|
||||
name = "bare-metal"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
|
||||
dependencies = [
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "bitrate"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c147d86912d04bef727828fda769a76ca81629a46d8ba311a8d58a26aa91473d"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763"
|
||||
dependencies = [
|
||||
"aligned 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"aligned",
|
||||
"bare-metal",
|
||||
"volatile-register",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-log"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978caafe65d1023d38b00c76b83564788fc351d954a5005fb72cf992c0d61458"
|
||||
dependencies = [
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m",
|
||||
"cortex-m-semihosting",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00d518da72bba39496024b62607c1d8e37bcece44b2536664f1132a73a499a28"
|
||||
dependencies = [
|
||||
"cortex-m-rt-macros 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-rt-macros",
|
||||
"r0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt-macros"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4717562afbba06e760d34451919f5c3bf3ac15c7bb897e8b04862a7428378647"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rtfm"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaf0b9fd3f042cb3793d15daf3cea201b2f25c99b0b5b936a551bb6909c3ae5b"
|
||||
dependencies = [
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-rtfm-macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heapless 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rtfm-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m",
|
||||
"cortex-m-rt",
|
||||
"cortex-m-rtfm-macros",
|
||||
"heapless",
|
||||
"rtfm-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rtfm-macros"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c62092f6ff344e9b0adb748f0302ed69889ba2fae1fce446e3788d4726ea73bb"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rtfm-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rtfm-syntax",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-semihosting"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "113ef0ecffee2b62b58f9380f4469099b30e9f9cbee2804771b4203ba1762cfa"
|
||||
dependencies = [
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b"
|
||||
dependencies = [
|
||||
"nb",
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-iterator"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c79a6321a1197d7730510c7e3f6cb80432dfefecb32426de8cea0aa19b4bb8d7"
|
||||
dependencies = [
|
||||
"enum-iterator-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-iterator-derive"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e94aa31f7c0dc764f57896dc615ddd76fc13b0d5dca7eb6cc5e018a5a09ec06"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
|
||||
dependencies = [
|
||||
"typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd"
|
||||
dependencies = [
|
||||
"typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc"
|
||||
dependencies = [
|
||||
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.5.3"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73a8a2391a3bc70b31f60e7a90daa5755a360559c0b6b9c5cfc0fee482362dc0"
|
||||
dependencies = [
|
||||
"as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hash32 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"as-slice",
|
||||
"generic-array 0.13.2",
|
||||
"hash32",
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.3.2"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "managed"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6"
|
||||
|
||||
[[package]]
|
||||
name = "mcp23017"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/mrd0ll4r/mcp23017.git#a3d072754abca60a92ece820f7cfb767a0c11669"
|
||||
dependencies = [
|
||||
"embedded-hal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc"
|
||||
|
||||
[[package]]
|
||||
name = "panic-halt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812"
|
||||
|
||||
[[package]]
|
||||
name = "panic-itm"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98830d17a95587207e41edaa3009b143d326ce134b0e3538ac98246a67d66cc3"
|
||||
dependencies = [
|
||||
"cortex-m",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "panic-semihosting"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c03864ac862876c16a308f5286f4aa217f1a69ac45df87ad3cd2847f818a642c"
|
||||
dependencies = [
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m",
|
||||
"cortex-m-semihosting",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.9"
|
||||
name = "paste"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d508492eeb1e5c38ee696371bf7b9fc33c83d46a7d451606b96458fbbbdc2dec"
|
||||
dependencies = [
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"paste-impl",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste-impl"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84f328a6a63192b333fce5fbb4be79db6758a4d518dfac6d54412f1492f72d32"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.3"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r0"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
|
||||
|
||||
[[package]]
|
||||
name = "rtfm-core"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ec893edb2aa5b70320b94896ffea22a7ebb1cf3f942bb67cd5b60a865a63493"
|
||||
|
||||
[[package]]
|
||||
name = "rtfm-syntax"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4455e23c34df3d66454e7e218a4d76a7f83321d04a806be614463341cec4116e"
|
||||
dependencies = [
|
||||
"indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indexmap",
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.105"
|
||||
version = "1.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
|
||||
dependencies = [
|
||||
"serde_derive 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-json-core"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf406405ada9ef326ca78677324ac66994ff348fc48a16030be08caeed29825"
|
||||
dependencies = [
|
||||
"heapless 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heapless",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.105"
|
||||
version = "1.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smoltcp"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"log",
|
||||
"managed",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stabilizer"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-log 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-rtfm 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heapless 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"stm32h7 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ad9959",
|
||||
"asm-delay",
|
||||
"cortex-m",
|
||||
"cortex-m-log",
|
||||
"cortex-m-rt",
|
||||
"cortex-m-rtfm",
|
||||
"embedded-hal",
|
||||
"enum-iterator",
|
||||
"heapless",
|
||||
"log",
|
||||
"mcp23017",
|
||||
"nb",
|
||||
"panic-halt",
|
||||
"panic-semihosting",
|
||||
"serde",
|
||||
"serde-json-core",
|
||||
"smoltcp",
|
||||
"stm32h7-ethernet",
|
||||
"stm32h7xx-hal 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
|
||||
|
||||
[[package]]
|
||||
name = "stm32h7"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9beb5e2a223c82f263c3051bba4614aebc6e98bd40217df3cd8817c83ac7bd8"
|
||||
dependencies = [
|
||||
"bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bare-metal",
|
||||
"cortex-m",
|
||||
"cortex-m-rt",
|
||||
"vcell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stm32h7-ethernet"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/quartiq/stm32h7-ethernet.git#cf9b8bb2e1b440d8ada6ac6048f48dc4ed9c269a"
|
||||
dependencies = [
|
||||
"cortex-m",
|
||||
"log",
|
||||
"smoltcp",
|
||||
"stm32h7xx-hal 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stm32h7xx-hal"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bare-metal",
|
||||
"cast",
|
||||
"cortex-m",
|
||||
"cortex-m-log",
|
||||
"cortex-m-rt",
|
||||
"cortex-m-rtfm",
|
||||
"embedded-hal",
|
||||
"nb",
|
||||
"panic-itm",
|
||||
"paste",
|
||||
"stm32h7",
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stm32h7xx-hal"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "987c66628f30012ed9a41cc738421c5caece03292c0cc8fd1e99956f122735bd"
|
||||
dependencies = [
|
||||
"bare-metal",
|
||||
"cast",
|
||||
"cortex-m",
|
||||
"cortex-m-rt",
|
||||
"embedded-hal",
|
||||
"nb",
|
||||
"paste",
|
||||
"stm32h7",
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.17"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.11.2"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
|
||||
[[package]]
|
||||
name = "vcell"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
[[package]]
|
||||
name = "volatile-register"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"
|
||||
dependencies = [
|
||||
"vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vcell",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum aligned 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eb1ce8b3382016136ab1d31a1b5ce807144f8b7eb2d5f16b2108f0f07edceb94"
|
||||
"checksum as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37dfb65bc03b2bc85ee827004f14a6817e04160e3b1a28931986a666a9290e70"
|
||||
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
"checksum bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
|
||||
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
"checksum cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763"
|
||||
"checksum cortex-m-log 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "978caafe65d1023d38b00c76b83564788fc351d954a5005fb72cf992c0d61458"
|
||||
"checksum cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "00d518da72bba39496024b62607c1d8e37bcece44b2536664f1132a73a499a28"
|
||||
"checksum cortex-m-rt-macros 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4717562afbba06e760d34451919f5c3bf3ac15c7bb897e8b04862a7428378647"
|
||||
"checksum cortex-m-rtfm 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eaf0b9fd3f042cb3793d15daf3cea201b2f25c99b0b5b936a551bb6909c3ae5b"
|
||||
"checksum cortex-m-rtfm-macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c62092f6ff344e9b0adb748f0302ed69889ba2fae1fce446e3788d4726ea73bb"
|
||||
"checksum cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "113ef0ecffee2b62b58f9380f4469099b30e9f9cbee2804771b4203ba1762cfa"
|
||||
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
|
||||
"checksum generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd"
|
||||
"checksum hash32 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc"
|
||||
"checksum heapless 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10b591a0032f114b7a77d4fbfab452660c553055515b7d7ece355db080d19087"
|
||||
"checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292"
|
||||
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
"checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6"
|
||||
"checksum panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812"
|
||||
"checksum panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c03864ac862876c16a308f5286f4aa217f1a69ac45df87ad3cd2847f818a642c"
|
||||
"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435"
|
||||
"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
|
||||
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
|
||||
"checksum rtfm-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec893edb2aa5b70320b94896ffea22a7ebb1cf3f942bb67cd5b60a865a63493"
|
||||
"checksum rtfm-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4455e23c34df3d66454e7e218a4d76a7f83321d04a806be614463341cec4116e"
|
||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)" = "e707fbbf255b8fc8c3b99abb91e7257a622caeb20a9818cbadbeeede4e0932ff"
|
||||
"checksum serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf406405ada9ef326ca78677324ac66994ff348fc48a16030be08caeed29825"
|
||||
"checksum serde_derive 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)" = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8"
|
||||
"checksum smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a"
|
||||
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
|
||||
"checksum stm32h7 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b0045066e082648e8a7ab1dd45c92efa8d7bec2beedf72ac7b62563911f82a"
|
||||
"checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03"
|
||||
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
|
||||
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"
|
||||
|
|
52
Cargo.toml
52
Cargo.toml
|
@ -1,54 +1,18 @@
|
|||
[package]
|
||||
name = "stabilizer"
|
||||
version = "0.3.0"
|
||||
authors = ["Robert Jördens <rj@quartiq.de>"]
|
||||
description = "Firmware for the Sinara Stabilizer board (stm32h743, eth, poe, 2 adc, 2 dac)"
|
||||
categories = ["embedded", "no-std", "hardware-support", "science"]
|
||||
license = "GPL-3.0-or-later"
|
||||
keywords = ["ethernet", "stm32h7", "adc", "dac", "physics"]
|
||||
repository = "https://github.com/quartiq/stabilizer"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/stabilizer/"
|
||||
edition = "2018"
|
||||
exclude = [
|
||||
".travis.yml",
|
||||
".gitignore",
|
||||
"doc/",
|
||||
"doc/*"
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
"stabilizer",
|
||||
"stm32h7xx-hal",
|
||||
"ad9959",
|
||||
]
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "quartiq/stabilizer", branch = "master" }
|
||||
maintenance = { status = "experimental" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = []
|
||||
default-target = "thumbv7em-none-eabihf"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = { version = "0.6", features = ["const-fn"] }
|
||||
cortex-m-rt = { version = "0.6", features = ["device"] }
|
||||
cortex-m-log = { version = "0.6", features = ["log-integration"] }
|
||||
log = "0.4"
|
||||
panic-semihosting = { version = "0.5", optional = true }
|
||||
panic-halt = "0.2"
|
||||
serde = { version = "1.0", features = ["derive"], default-features = false }
|
||||
heapless = "0.5"
|
||||
serde-json-core = "0.1"
|
||||
stm32h7 = { version = "0.10", features = ["stm32h743", "rt"] }
|
||||
cortex-m-rtfm = "0.5"
|
||||
smoltcp = { version = "0.6", features = ["ethernet", "proto-ipv4", "socket-tcp"], default-features = false }
|
||||
|
||||
[features]
|
||||
semihosting = ["panic-semihosting", "cortex-m-log/semihosting"]
|
||||
bkpt = [ ]
|
||||
nightly = ["cortex-m/inline-asm"]
|
||||
|
||||
[profile.dev]
|
||||
codegen-units = 1
|
||||
incremental = false
|
||||
opt-level = 3
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
debug = true
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
Cargo.lock
|
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "ad9959"
|
||||
version = "0.1.0"
|
||||
authors = ["Ryan Summers <ryan.summers@vertigo-designs.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
embedded-hal = {version = "0.2.3", features = ["unproven"]}
|
||||
bit_field = "0.10.0"
|
|
@ -0,0 +1,480 @@
|
|||
#![no_std]
|
||||
|
||||
use bit_field::BitField;
|
||||
use embedded_hal::{
|
||||
digital::v2::OutputPin,
|
||||
blocking::delay::DelayMs,
|
||||
};
|
||||
|
||||
/// A device driver for the AD9959 direct digital synthesis (DDS) chip.
|
||||
///
|
||||
/// This chip provides four independently controllable digital-to-analog output sinusoids with
|
||||
/// configurable phase, amplitude, and frequency. All channels are inherently synchronized as they
|
||||
/// are derived off a common system clock.
|
||||
///
|
||||
/// The chip contains a configurable PLL and supports system clock frequencies up to 500 MHz.
|
||||
///
|
||||
/// The chip supports a number of serial interfaces to improve data throughput, including normal,
|
||||
/// dual, and quad SPI configurations.
|
||||
pub struct Ad9959<INTERFACE, DELAY, UPDATE> {
|
||||
interface: INTERFACE,
|
||||
delay: DELAY,
|
||||
reference_clock_frequency: f32,
|
||||
system_clock_multiplier: u8,
|
||||
io_update: UPDATE,
|
||||
}
|
||||
|
||||
/// A trait that allows a HAL to provide a means of communicating with the AD9959.
|
||||
pub trait Interface {
|
||||
type Error;
|
||||
|
||||
fn configure_mode(&mut self, mode: Mode) -> Result<(), Self::Error>;
|
||||
|
||||
fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Self::Error>;
|
||||
|
||||
fn read(&mut self, addr: u8, dest: &mut [u8]) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Indicates various communication modes of the DDS. The value of this enumeration is equivalent to
|
||||
/// the configuration bits of the DDS CSR register.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum Mode {
|
||||
SingleBitTwoWire = 0b00,
|
||||
SingleBitThreeWire = 0b01,
|
||||
TwoBitSerial = 0b10,
|
||||
FourBitSerial = 0b11,
|
||||
}
|
||||
|
||||
/// The configuration registers within the AD9959 DDS device. The values of each register are
|
||||
/// equivalent to the address.
|
||||
pub enum Register {
|
||||
CSR = 0x00,
|
||||
FR1 = 0x01,
|
||||
FR2 = 0x02,
|
||||
CFR = 0x03,
|
||||
CFTW0 = 0x04,
|
||||
CPOW0 = 0x05,
|
||||
ACR = 0x06,
|
||||
LSRR = 0x07,
|
||||
RDW = 0x08,
|
||||
FDW = 0x09,
|
||||
CW1 = 0x0a,
|
||||
CW2 = 0x0b,
|
||||
CW3 = 0x0c,
|
||||
CW4 = 0x0d,
|
||||
CW5 = 0x0e,
|
||||
CW6 = 0x0f,
|
||||
CW7 = 0x10,
|
||||
CW8 = 0x11,
|
||||
CW9 = 0x12,
|
||||
CW10 = 0x13,
|
||||
CW11 = 0x14,
|
||||
CW12 = 0x15,
|
||||
CW13 = 0x16,
|
||||
CW14 = 0x17,
|
||||
CW15 = 0x18,
|
||||
}
|
||||
|
||||
/// Specifies an output channel of the AD9959 DDS chip.
|
||||
pub enum Channel {
|
||||
One = 0,
|
||||
Two = 1,
|
||||
Three = 2,
|
||||
Four = 3,
|
||||
}
|
||||
|
||||
/// Possible errors generated by the AD9959 driver.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Interface,
|
||||
Check,
|
||||
Bounds,
|
||||
Pin,
|
||||
Frequency,
|
||||
}
|
||||
|
||||
impl <PinE, INTERFACE, DELAY, UPDATE> Ad9959<INTERFACE, DELAY, UPDATE>
|
||||
where
|
||||
INTERFACE: Interface,
|
||||
DELAY: DelayMs<u8>,
|
||||
UPDATE: OutputPin<Error = PinE>,
|
||||
|
||||
{
|
||||
/// Construct and initialize the DDS.
|
||||
///
|
||||
/// Args:
|
||||
/// * `interface` - An interface to the DDS.
|
||||
/// * `reset_pin` - A pin connected to the DDS reset input.
|
||||
/// * `io_update` - A pin connected to the DDS io_update input.
|
||||
/// * `delay` - A delay implementation for blocking operation for specific amounts of time.
|
||||
/// * `desired_mode` - The desired communication mode of the interface to the DDS.
|
||||
/// * `clock_frequency` - The clock frequency of the reference clock input.
|
||||
/// * `multiplier` - The desired clock multiplier for the system clock. This multiplies
|
||||
/// `clock_frequency` to generate the system clock.
|
||||
pub fn new<RST>(interface: INTERFACE,
|
||||
reset_pin: &mut RST,
|
||||
io_update: UPDATE,
|
||||
delay: DELAY,
|
||||
desired_mode: Mode,
|
||||
clock_frequency: f32,
|
||||
multiplier: u8) -> Result<Self, Error>
|
||||
where
|
||||
RST: OutputPin,
|
||||
{
|
||||
let mut ad9959 = Ad9959 {
|
||||
interface: interface,
|
||||
io_update: io_update,
|
||||
delay: delay,
|
||||
reference_clock_frequency: clock_frequency,
|
||||
system_clock_multiplier: 1,
|
||||
};
|
||||
|
||||
ad9959.io_update.set_low().or_else(|_| Err(Error::Pin))?;
|
||||
|
||||
// Reset the AD9959
|
||||
reset_pin.set_high().or_else(|_| Err(Error::Pin))?;
|
||||
|
||||
// Delay for a clock cycle to allow the device to reset.
|
||||
ad9959.delay.delay_ms((1000.0 / clock_frequency as f32) as u8);
|
||||
|
||||
reset_pin.set_low().or_else(|_| Err(Error::Pin))?;
|
||||
|
||||
ad9959.interface.configure_mode(Mode::SingleBitTwoWire).map_err(|_| Error::Interface)?;
|
||||
|
||||
// Program the interface configuration in the AD9959. Default to all channels enabled.
|
||||
let mut csr: [u8; 1] = [0xF0];
|
||||
csr[0].set_bits(1..3, desired_mode as u8);
|
||||
ad9959.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?;
|
||||
|
||||
// Latch the configuration registers to make them active.
|
||||
ad9959.latch_configuration()?;
|
||||
|
||||
ad9959.interface.configure_mode(desired_mode).map_err(|_| Error::Interface)?;
|
||||
|
||||
// Read back the CSR to ensure it specifies the mode correctly.
|
||||
let mut updated_csr: [u8; 1] = [0];
|
||||
ad9959.interface.read(Register::CSR as u8, &mut updated_csr).map_err(|_| Error::Interface)?;
|
||||
if updated_csr[0] != csr[0] {
|
||||
return Err(Error::Check);
|
||||
}
|
||||
|
||||
// Set the clock frequency to configure the device as necessary.
|
||||
ad9959.configure_system_clock(clock_frequency, multiplier)?;
|
||||
Ok(ad9959)
|
||||
}
|
||||
|
||||
/// Latch the DDS configuration to ensure it is active on the output channels.
|
||||
fn latch_configuration(&mut self) -> Result<(), Error> {
|
||||
self.io_update.set_high().or_else(|_| Err(Error::Pin))?;
|
||||
// The SYNC_CLK is 1/4 the system clock frequency. The IO_UPDATE pin must be latched for one
|
||||
// full SYNC_CLK pulse to register. For safety, we latch for 5 here.
|
||||
self.delay.delay_ms((5000.0 / self.system_clock_frequency()) as u8);
|
||||
self.io_update.set_low().or_else(|_| Err(Error::Pin))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Configure the internal system clock of the chip.
|
||||
///
|
||||
/// Arguments:
|
||||
/// * `reference_clock_frequency` - The reference clock frequency provided to the AD9959 core.
|
||||
/// * `multiplier` - The frequency multiplier of the system clock. Must be 1 or 4-20.
|
||||
///
|
||||
/// Returns:
|
||||
/// The actual frequency configured for the internal system clock.
|
||||
pub fn configure_system_clock(&mut self,
|
||||
reference_clock_frequency: f32,
|
||||
multiplier: u8) -> Result<f64, Error>
|
||||
{
|
||||
self.reference_clock_frequency = reference_clock_frequency;
|
||||
|
||||
if multiplier != 1 && (multiplier > 20 || multiplier < 4) {
|
||||
return Err(Error::Bounds);
|
||||
}
|
||||
|
||||
let frequency = multiplier as f64 * self.reference_clock_frequency as f64;
|
||||
if frequency > 500_000_000.0f64 {
|
||||
return Err(Error::Frequency);
|
||||
}
|
||||
|
||||
// TODO: Update / disable any enabled channels?
|
||||
let mut fr1: [u8; 3] = [0, 0, 0];
|
||||
self.interface.read(Register::FR1 as u8, &mut fr1).map_err(|_| Error::Interface)?;
|
||||
fr1[0].set_bits(2..=6, multiplier);
|
||||
|
||||
let vco_range = frequency > 255e6;
|
||||
fr1[0].set_bit(7, vco_range);
|
||||
|
||||
self.interface.write(Register::FR1 as u8, &fr1).map_err(|_| Error::Interface)?;
|
||||
self.system_clock_multiplier = multiplier;
|
||||
|
||||
Ok(self.system_clock_frequency())
|
||||
}
|
||||
|
||||
/// Get the current reference clock frequency in Hz.
|
||||
pub fn get_reference_clock_frequency(&self) -> f32 {
|
||||
self.reference_clock_frequency
|
||||
}
|
||||
|
||||
/// Get the current reference clock multiplier.
|
||||
pub fn get_reference_clock_multiplier(&mut self) -> Result<u8, Error> {
|
||||
let mut fr1: [u8; 3] = [0, 0, 0];
|
||||
self.interface.read(Register::FR1 as u8, &mut fr1).map_err(|_| Error::Interface)?;
|
||||
|
||||
Ok(fr1[0].get_bits(2..=6) as u8)
|
||||
}
|
||||
|
||||
/// Perform a self-test of the communication interface.
|
||||
///
|
||||
/// Note:
|
||||
/// This modifies the existing channel enables. They are restored upon exit.
|
||||
///
|
||||
/// Returns:
|
||||
/// True if the self test succeeded. False otherwise.
|
||||
pub fn self_test(&mut self) -> Result<bool, Error> {
|
||||
let mut csr: [u8; 1] = [0];
|
||||
self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?;
|
||||
let old_csr = csr[0];
|
||||
|
||||
// Enable all channels.
|
||||
csr[0].set_bits(4..8, 0xF);
|
||||
self.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?;
|
||||
|
||||
// Read back the enable.
|
||||
csr[0] = 0;
|
||||
self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?;
|
||||
if csr[0].get_bits(4..8) != 0xF {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// Clear all channel enables.
|
||||
csr[0].set_bits(4..8, 0x0);
|
||||
self.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?;
|
||||
|
||||
// Read back the enable.
|
||||
csr[0] = 0xFF;
|
||||
self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?;
|
||||
if csr[0].get_bits(4..8) != 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// Restore the CSR.
|
||||
csr[0] = old_csr;
|
||||
self.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// Get the current system clock frequency in Hz.
|
||||
fn system_clock_frequency(&self) -> f64 {
|
||||
self.system_clock_multiplier as f64 * self.reference_clock_frequency as f64
|
||||
}
|
||||
|
||||
/// Enable an output channel.
|
||||
pub fn enable_channel(&mut self, channel: Channel) -> Result<(), Error> {
|
||||
let mut csr: [u8; 1] = [0];
|
||||
self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?;
|
||||
csr[0].set_bit(channel as usize + 4, true);
|
||||
self.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Disable an output channel.
|
||||
pub fn disable_channel(&mut self, channel: Channel) -> Result<(), Error> {
|
||||
let mut csr: [u8; 1] = [0];
|
||||
self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?;
|
||||
csr[0].set_bit(channel as usize + 4, false);
|
||||
self.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Determine if an output channel is enabled.
|
||||
pub fn is_enabled(&mut self, channel: Channel) -> Result<bool, Error> {
|
||||
let mut csr: [u8; 1] = [0; 1];
|
||||
self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?;
|
||||
|
||||
Ok(csr[0].get_bit(channel as usize + 4))
|
||||
}
|
||||
|
||||
/// Update an output channel configuration register.
|
||||
///
|
||||
/// Args:
|
||||
/// * `channel` - The channel to configure.
|
||||
/// * `register` - The register to update.
|
||||
/// * `data` - The contents to write to the provided register.
|
||||
fn modify_channel(&mut self, channel: Channel, register: Register, data: &[u8]) -> Result<(), Error> {
|
||||
// Disable all other outputs so that we can update the configuration register of only the
|
||||
// specified channel.
|
||||
let mut csr: [u8; 1] = [0];
|
||||
self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?;
|
||||
|
||||
let mut new_csr = csr;
|
||||
new_csr[0].set_bits(4..8, 0);
|
||||
new_csr[0].set_bit(4 + channel as usize, true);
|
||||
|
||||
self.interface.write(Register::CSR as u8, &new_csr).map_err(|_| Error::Interface)?;
|
||||
|
||||
self.interface.write(register as u8, &data).map_err(|_| Error::Interface)?;
|
||||
|
||||
// Latch the configuration and restore the previous CSR. Note that the re-enable of the
|
||||
// channel happens immediately, so the CSR update does not need to be latched.
|
||||
self.latch_configuration()?;
|
||||
self.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read a configuration register of a specific channel.
|
||||
///
|
||||
/// Args:
|
||||
/// * `channel` - The channel to read.
|
||||
/// * `register` - The register to read.
|
||||
/// * `data` - A location to store the read register contents.
|
||||
fn read_channel(&mut self, channel: Channel, register: Register, mut data: &mut [u8]) -> Result<(), Error> {
|
||||
// Disable all other channels in the CSR so that we can read the configuration register of
|
||||
// only the desired channel.
|
||||
let mut csr: [u8; 1] = [0];
|
||||
self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?;
|
||||
|
||||
let mut new_csr = csr;
|
||||
new_csr[0].set_bits(4..8, 0);
|
||||
new_csr[0].set_bit(4 + channel as usize, true);
|
||||
|
||||
self.interface.write(Register::CSR as u8, &new_csr).map_err(|_| Error::Interface)?;
|
||||
|
||||
self.interface.read(register as u8, &mut data).map_err(|_| Error::Interface)?;
|
||||
|
||||
// Restore the previous CSR. Note that the re-enable of the channel happens immediately, so
|
||||
// the CSR update does not need to be latched.
|
||||
self.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Configure the phase of a specified channel.
|
||||
///
|
||||
/// Arguments:
|
||||
/// * `channel` - The channel to configure the frequency of.
|
||||
/// * `phase_turns` - The desired phase offset in turns.
|
||||
///
|
||||
/// Returns:
|
||||
/// The actual programmed phase offset of the channel in turns.
|
||||
pub fn set_phase(&mut self, channel: Channel, phase_turns: f32) -> Result<f32, Error> {
|
||||
let phase_offset: u16 = (phase_turns * (1 << 14) as f32) as u16 & 0x3FFFu16;
|
||||
|
||||
self.modify_channel(channel, Register::CPOW0, &phase_offset.to_be_bytes())?;
|
||||
|
||||
Ok((phase_offset as f32) / ((1 << 14) as f32))
|
||||
}
|
||||
|
||||
/// Get the current phase of a specified channel.
|
||||
///
|
||||
/// Args:
|
||||
/// * `channel` - The channel to get the phase of.
|
||||
///
|
||||
/// Returns:
|
||||
/// The phase of the channel in turns.
|
||||
pub fn get_phase(&mut self, channel: Channel) -> Result<f32, Error> {
|
||||
let mut phase_offset: [u8; 2] = [0; 2];
|
||||
self.read_channel(channel, Register::CPOW0, &mut phase_offset)?;
|
||||
|
||||
let phase_offset = u16::from_be_bytes(phase_offset) & 0x3FFFu16;
|
||||
|
||||
Ok((phase_offset as f32) / ((1 << 14) as f32))
|
||||
}
|
||||
|
||||
/// Configure the amplitude of a specified channel.
|
||||
///
|
||||
/// Arguments:
|
||||
/// * `channel` - The channel to configure the frequency of.
|
||||
/// * `amplitude` - A normalized amplitude setting [0, 1].
|
||||
///
|
||||
/// Returns:
|
||||
/// The actual normalized amplitude of the channel relative to full-scale range.
|
||||
pub fn set_amplitude(&mut self, channel: Channel, amplitude: f32) -> Result<f32, Error> {
|
||||
if amplitude < 0.0 || amplitude > 1.0 {
|
||||
return Err(Error::Bounds);
|
||||
}
|
||||
|
||||
let amplitude_control: u16 = (amplitude * (1 << 10) as f32) as u16;
|
||||
|
||||
let mut acr: [u8; 3] = [0; 3];
|
||||
|
||||
// Enable the amplitude multiplier for the channel if required. The amplitude control has
|
||||
// full-scale at 0x3FF (amplitude of 1), so the multiplier should be disabled whenever
|
||||
// full-scale is used.
|
||||
if amplitude_control < (1 << 10) {
|
||||
|
||||
let masked_control = amplitude_control & 0x3FF;
|
||||
acr[1] = masked_control.to_be_bytes()[0];
|
||||
acr[2] = masked_control.to_be_bytes()[1];
|
||||
|
||||
// Enable the amplitude multiplier
|
||||
acr[1].set_bit(4, true);
|
||||
}
|
||||
|
||||
self.modify_channel(channel, Register::ACR, &acr)?;
|
||||
|
||||
Ok(amplitude_control as f32 / (1 << 10) as f32)
|
||||
}
|
||||
|
||||
/// Get the configured amplitude of a channel.
|
||||
///
|
||||
/// Args:
|
||||
/// * `channel` - The channel to get the amplitude of.
|
||||
///
|
||||
/// Returns:
|
||||
/// The normalized amplitude of the channel.
|
||||
pub fn get_amplitude(&mut self, channel: Channel) -> Result<f32, Error> {
|
||||
let mut acr: [u8; 3] = [0; 3];
|
||||
self.read_channel(channel, Register::ACR, &mut acr)?;
|
||||
|
||||
if acr[1].get_bit(4) {
|
||||
let amplitude_control: u16 = (((acr[1] as u16) << 8) | (acr[2] as u16)) & 0x3FF;
|
||||
Ok(amplitude_control as f32 / (1 << 10) as f32)
|
||||
} else {
|
||||
Ok(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure the frequency of a specified channel.
|
||||
///
|
||||
/// Arguments:
|
||||
/// * `channel` - The channel to configure the frequency of.
|
||||
/// * `frequency` - The desired output frequency in Hz.
|
||||
///
|
||||
/// Returns:
|
||||
/// The actual programmed frequency of the channel.
|
||||
pub fn set_frequency(&mut self, channel: Channel, frequency: f64) -> Result<f64, Error> {
|
||||
if frequency < 0.0 || frequency > self.system_clock_frequency() {
|
||||
return Err(Error::Bounds);
|
||||
}
|
||||
|
||||
// The function for channel frequency is `f_out = FTW * f_s / 2^32`, where FTW is the
|
||||
// frequency tuning word and f_s is the system clock rate.
|
||||
let tuning_word: u32 = ((frequency as f64 / self.system_clock_frequency())
|
||||
* 1u64.wrapping_shl(32) as f64) as u32;
|
||||
|
||||
self.modify_channel(channel, Register::CFTW0, &tuning_word.to_be_bytes())?;
|
||||
Ok((tuning_word as f64 / 1u64.wrapping_shl(32) as f64) * self.system_clock_frequency())
|
||||
}
|
||||
|
||||
/// Get the frequency of a channel.
|
||||
///
|
||||
/// Arguments:
|
||||
/// * `channel` - The channel to get the frequency of.
|
||||
///
|
||||
/// Returns:
|
||||
/// The frequency of the channel in Hz.
|
||||
pub fn get_frequency(&mut self, channel: Channel) -> Result<f64, Error> {
|
||||
// Read the frequency tuning word for the channel.
|
||||
let mut tuning_word: [u8; 4] = [0; 4];
|
||||
self.read_channel(channel, Register::CFTW0, &mut tuning_word)?;
|
||||
let tuning_word = u32::from_be_bytes(tuning_word);
|
||||
|
||||
// Convert the tuning word into a frequency.
|
||||
Ok(tuning_word as f64 * self.system_clock_frequency() / ((1u64 << 32)) as f64)
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ break rust_begin_unwind
|
|||
|
||||
load
|
||||
# tbreak cortex_m_rt::reset_handler
|
||||
# monitor reset halt
|
||||
monitor reset halt
|
||||
|
||||
# cycle counter delta tool, place two bkpts around the section
|
||||
set var $cc=0xe0001004
|
||||
|
@ -26,4 +26,3 @@ set var $t0=*$cc
|
|||
continue
|
||||
end
|
||||
#set var $t0=*$cc
|
||||
continue
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
#!/usr/bin/python3
|
||||
"""
|
||||
Description: Test Stabilizer communication and DDS configuration.
|
||||
|
||||
Author: Ryan Summers
|
||||
"""
|
||||
|
||||
import socket
|
||||
import json
|
||||
|
||||
HOST = '10.0.16.99'
|
||||
PORT = 1235
|
||||
|
||||
def do_request(s, request):
|
||||
""" Perform a request with the Stabilizer.
|
||||
|
||||
Args:
|
||||
s: The socket to the stabilizer.
|
||||
request: The request to transmit.
|
||||
|
||||
Returns:
|
||||
The received response object.
|
||||
"""
|
||||
# Transform the value field.
|
||||
request['value'] = json.dumps(request['value'], separators=[',', ':']).replace('"', "'")
|
||||
data = (json.dumps(request, separators=[',', ':']) + '\n').encode('ascii')
|
||||
s.send(data)
|
||||
|
||||
response = b''
|
||||
while not response.endswith(b'\n'):
|
||||
response += s.recv(1024)
|
||||
|
||||
# Decode the value array
|
||||
response = json.loads(response.decode('ascii'))
|
||||
response['value'] = response['value'].replace("'", '"')
|
||||
response['value'] = json.loads(response['value'])
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def read_attribute(s, attribute_name):
|
||||
""" Read an attribute on the Stabilizer device.
|
||||
|
||||
Args:
|
||||
s: The socket to the stabilizer.
|
||||
attribute_name: The name of the endpoint to write to (the attribute name).
|
||||
|
||||
Returns:
|
||||
The value of the attribute. May be a string or a dictionary.
|
||||
"""
|
||||
request = {
|
||||
"req": "Read",
|
||||
"attribute": attribute_name,
|
||||
"value": "",
|
||||
}
|
||||
|
||||
response = do_request(s, request)
|
||||
|
||||
if 'code' not in response or response['code'] != 200:
|
||||
raise Exception(f'Failed to read {attribute_name}: {response}')
|
||||
|
||||
return response['value']
|
||||
|
||||
|
||||
def write_attribute(s, attribute_name, attribute_value):
|
||||
""" Write an attribute on the Stabilizer device.
|
||||
|
||||
Args:
|
||||
s: The socket to the stabilizer.
|
||||
attribute_name: The name of the endpoint to write to (the attribute name).
|
||||
attribute_value: The value to write to the attribute. May be a string or a dictionary.
|
||||
"""
|
||||
request = {
|
||||
"req": "Write",
|
||||
"attribute": attribute_name,
|
||||
"value": attribute_value,
|
||||
}
|
||||
|
||||
response = do_request(s, request)
|
||||
|
||||
if 'code' not in response or response['code'] != 200:
|
||||
raise Exception(f'Failed to write {attribute_name}: {response}')
|
||||
|
||||
|
||||
def main():
|
||||
""" Main program entry point. """
|
||||
with socket.socket() as s:
|
||||
|
||||
# Connect to the stabilizer.
|
||||
s.connect((HOST, PORT))
|
||||
|
||||
# A sample configuration for an output channel.
|
||||
channel_config = {
|
||||
'attenuation': 31.5,
|
||||
'parameters': {
|
||||
'phase_offset': 0.5,
|
||||
'frequency': 100.0e6,
|
||||
'amplitude': 0.2,
|
||||
'enabled': True,
|
||||
}
|
||||
}
|
||||
|
||||
# Configure OUT0 and read it back.
|
||||
write_attribute(s, "pounder/out0", channel_config)
|
||||
print('Pounder OUT0: ', read_attribute(s, "pounder/out0"))
|
||||
|
||||
print('Pounder IN1: ', read_attribute(s, "pounder/in1"))
|
||||
print('Pounder OUT1: ', read_attribute(s, "pounder/out1"))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
641
src/board.rs
641
src/board.rs
|
@ -1,641 +0,0 @@
|
|||
use super::eth;
|
||||
use super::i2c;
|
||||
use super::pac;
|
||||
|
||||
fn pwr_setup(pwr: &pac::PWR) {
|
||||
// go to VOS1 voltage scale for high perf
|
||||
pwr.cr3
|
||||
.write(|w| w.scuen().set_bit().ldoen().set_bit().bypass().clear_bit());
|
||||
while pwr.csr1.read().actvosrdy().bit_is_clear() {}
|
||||
pwr.d3cr.write(|w| unsafe { w.vos().bits(0b11) }); // vos1
|
||||
while pwr.d3cr.read().vosrdy().bit_is_clear() {}
|
||||
}
|
||||
|
||||
fn rcc_reset(rcc: &pac::RCC) {
|
||||
// Reset all peripherals
|
||||
rcc.ahb1rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
rcc.ahb1rstr.write(|w| unsafe { w.bits(0) });
|
||||
rcc.apb1lrstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
rcc.apb1lrstr.write(|w| unsafe { w.bits(0) });
|
||||
rcc.apb1hrstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
rcc.apb1hrstr.write(|w| unsafe { w.bits(0) });
|
||||
|
||||
rcc.ahb2rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
rcc.ahb2rstr.write(|w| unsafe { w.bits(0) });
|
||||
rcc.apb2rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
rcc.apb2rstr.write(|w| unsafe { w.bits(0) });
|
||||
|
||||
// do not reset the cpu
|
||||
rcc.ahb3rstr.write(|w| unsafe { w.bits(0x7FFF_FFFF) });
|
||||
rcc.ahb3rstr.write(|w| unsafe { w.bits(0) });
|
||||
rcc.apb3rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
rcc.apb3rstr.write(|w| unsafe { w.bits(0) });
|
||||
|
||||
rcc.ahb4rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
rcc.ahb4rstr.write(|w| unsafe { w.bits(0) });
|
||||
rcc.apb4rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
rcc.apb4rstr.write(|w| unsafe { w.bits(0) });
|
||||
}
|
||||
|
||||
fn rcc_pll_setup(rcc: &pac::RCC, flash: &pac::FLASH) {
|
||||
// Switch to HSI to mess with HSE
|
||||
rcc.cr.modify(|_, w| w.hsion().on());
|
||||
while rcc.cr.read().hsirdy().is_not_ready() {}
|
||||
rcc.cfgr.modify(|_, w| w.sw().hsi());
|
||||
while !rcc.cfgr.read().sws().is_hsi() {}
|
||||
rcc.cr.write(|w| w.hsion().on());
|
||||
rcc.cfgr.reset();
|
||||
|
||||
// Ensure HSE is on and stable
|
||||
rcc.cr.modify(|_, w| w.hseon().on().hsebyp().not_bypassed());
|
||||
while !rcc.cr.read().hserdy().is_ready() {}
|
||||
|
||||
rcc.pllckselr.modify(
|
||||
|_, w| {
|
||||
w.pllsrc()
|
||||
.hse()
|
||||
.divm1()
|
||||
.bits(1) // ref prescaler
|
||||
.divm2()
|
||||
.bits(1)
|
||||
}, // ref prescaler
|
||||
);
|
||||
// Configure PLL1: 8MHz /1 *100 /2 = 400 MHz
|
||||
rcc.pllcfgr.modify(|_, w| {
|
||||
w.pll1vcosel()
|
||||
.wide_vco() // 192-836 MHz VCO
|
||||
.pll1rge()
|
||||
.range8() // 8-16 MHz PFD
|
||||
.pll1fracen()
|
||||
.reset()
|
||||
.divp1en()
|
||||
.enabled()
|
||||
.pll2vcosel()
|
||||
.medium_vco() // 150-420 MHz VCO
|
||||
.pll2rge()
|
||||
.range8() // 8-16 MHz PFD
|
||||
.pll2fracen()
|
||||
.reset()
|
||||
.divp2en()
|
||||
.enabled()
|
||||
.divq2en()
|
||||
.enabled()
|
||||
});
|
||||
rcc.pll1divr.write(|w| unsafe {
|
||||
w.divn1()
|
||||
.bits(100 - 1) // feebdack divider
|
||||
.divp1()
|
||||
.div2() // p output divider
|
||||
});
|
||||
rcc.cr.modify(|_, w| w.pll1on().on());
|
||||
while !rcc.cr.read().pll1rdy().is_ready() {}
|
||||
|
||||
// Configure PLL2: 8MHz /1 *25 / 2 = 100 MHz
|
||||
rcc.pll2divr.write(|w| unsafe {
|
||||
w.divn2()
|
||||
.bits(25 - 1) // feebdack divider
|
||||
.divp2()
|
||||
.bits(2 - 1) // p output divider
|
||||
.divq2()
|
||||
.bits(2 - 1) // q output divider
|
||||
});
|
||||
rcc.cr.modify(|_, w| w.pll2on().on());
|
||||
while !rcc.cr.read().pll2rdy().is_ready() {}
|
||||
|
||||
// hclk 200 MHz, pclk 100 MHz
|
||||
rcc.d1cfgr.write(
|
||||
|w| {
|
||||
w.d1cpre()
|
||||
.div1() // sys_ck not divided
|
||||
.hpre()
|
||||
.div2() // rcc_hclk3 = sys_d1cpre_ck / 2
|
||||
.d1ppre()
|
||||
.div2()
|
||||
}, // rcc_pclk3 = rcc_hclk3 / 2
|
||||
);
|
||||
rcc.d2cfgr.write(
|
||||
|w| {
|
||||
w.d2ppre1()
|
||||
.div2() // rcc_pclk1 = rcc_hclk3 / 2
|
||||
.d2ppre2()
|
||||
.div2()
|
||||
}, // rcc_pclk2 = rcc_hclk3 / 2
|
||||
);
|
||||
rcc.d3cfgr.write(
|
||||
|w| w.d3ppre().div2(), // rcc_pclk4 = rcc_hclk3 / 2
|
||||
);
|
||||
|
||||
// 2 wait states, 0b10 programming delay
|
||||
// 185-210 MHz
|
||||
flash
|
||||
.acr
|
||||
.write(|w| unsafe { w.wrhighfreq().bits(2).latency().bits(2) });
|
||||
while flash.acr.read().latency().bits() != 2 {}
|
||||
|
||||
// CSI for I/O compensationc ell
|
||||
rcc.cr.modify(|_, w| w.csion().on());
|
||||
while !rcc.cr.read().csirdy().is_ready() {}
|
||||
|
||||
// Set system clock to pll1_p
|
||||
rcc.cfgr.modify(|_, w| w.sw().pll1());
|
||||
while !rcc.cfgr.read().sws().is_pll1() {}
|
||||
|
||||
rcc.d1ccipr.write(|w| w.ckpersel().hse());
|
||||
rcc.d2ccip1r
|
||||
.modify(|_, w| w.spi123sel().pll2_p().spi45sel().pll2_q());
|
||||
rcc.d3ccipr.modify(|_, w| w.spi6sel().pll2_q());
|
||||
}
|
||||
|
||||
fn io_compensation_setup(syscfg: &pac::SYSCFG) {
|
||||
syscfg
|
||||
.cccsr
|
||||
.modify(|_, w| w.en().set_bit().cs().clear_bit().hslv().clear_bit());
|
||||
while syscfg.cccsr.read().ready().bit_is_clear() {}
|
||||
}
|
||||
|
||||
fn gpio_setup(
|
||||
gpioa: &pac::GPIOA,
|
||||
gpiob: &pac::GPIOB,
|
||||
gpiod: &pac::GPIOD,
|
||||
gpioe: &pac::GPIOE,
|
||||
gpiof: &pac::GPIOF,
|
||||
gpiog: &pac::GPIOG,
|
||||
) {
|
||||
// FP_LED0
|
||||
gpiod.otyper.modify(|_, w| w.ot5().push_pull());
|
||||
gpiod.moder.modify(|_, w| w.moder5().output());
|
||||
gpiod.odr.modify(|_, w| w.odr5().low());
|
||||
|
||||
// FP_LED1
|
||||
gpiod.otyper.modify(|_, w| w.ot6().push_pull());
|
||||
gpiod.moder.modify(|_, w| w.moder6().output());
|
||||
gpiod.odr.modify(|_, w| w.odr6().low());
|
||||
|
||||
// LED_FP2
|
||||
gpiog.otyper.modify(|_, w| w.ot4().push_pull());
|
||||
gpiog.moder.modify(|_, w| w.moder4().output());
|
||||
gpiog.odr.modify(|_, w| w.odr4().low());
|
||||
|
||||
// LED_FP3
|
||||
gpiod.otyper.modify(|_, w| w.ot12().push_pull());
|
||||
gpiod.moder.modify(|_, w| w.moder12().output());
|
||||
gpiod.odr.modify(|_, w| w.odr12().low());
|
||||
|
||||
// AFE0_A0,1: PG2,PG3
|
||||
gpiog
|
||||
.otyper
|
||||
.modify(|_, w| w.ot2().push_pull().ot3().push_pull());
|
||||
gpiog
|
||||
.moder
|
||||
.modify(|_, w| w.moder2().output().moder3().output());
|
||||
gpiog.odr.modify(|_, w| w.odr2().low().odr3().low());
|
||||
|
||||
// ADC0
|
||||
// SCK: PG11
|
||||
gpiog.moder.modify(|_, w| w.moder11().alternate());
|
||||
gpiog.otyper.modify(|_, w| w.ot11().push_pull());
|
||||
gpiog.ospeedr.modify(|_, w| w.ospeedr11().very_high_speed());
|
||||
gpiog.afrh.modify(|_, w| w.afr11().af5());
|
||||
// MOSI: PD7
|
||||
// MISO: PA6
|
||||
gpioa.moder.modify(|_, w| w.moder6().alternate());
|
||||
gpioa.afrl.modify(|_, w| w.afr6().af5());
|
||||
// NSS: PG10
|
||||
gpiog.moder.modify(|_, w| w.moder10().alternate());
|
||||
gpiog.otyper.modify(|_, w| w.ot10().push_pull());
|
||||
gpiog.ospeedr.modify(|_, w| w.ospeedr10().very_high_speed());
|
||||
gpiog.afrh.modify(|_, w| w.afr10().af5());
|
||||
|
||||
// DAC0
|
||||
// SCK: PB10
|
||||
gpiob.moder.modify(|_, w| w.moder10().alternate());
|
||||
gpiob.otyper.modify(|_, w| w.ot10().push_pull());
|
||||
gpiob.ospeedr.modify(|_, w| w.ospeedr10().very_high_speed());
|
||||
gpiob.afrh.modify(|_, w| w.afr10().af5());
|
||||
// MOSI: PB15
|
||||
gpiob.moder.modify(|_, w| w.moder15().alternate());
|
||||
gpiob.otyper.modify(|_, w| w.ot15().push_pull());
|
||||
gpiob.ospeedr.modify(|_, w| w.ospeedr15().very_high_speed());
|
||||
gpiob.afrh.modify(|_, w| w.afr15().af5());
|
||||
// MISO: PB14
|
||||
// NSS: PB9
|
||||
gpiob.moder.modify(|_, w| w.moder9().alternate());
|
||||
gpiob.otyper.modify(|_, w| w.ot9().push_pull());
|
||||
gpiob.ospeedr.modify(|_, w| w.ospeedr9().very_high_speed());
|
||||
gpiob.afrh.modify(|_, w| w.afr9().af5());
|
||||
|
||||
// DAC0_LDAC: PE11
|
||||
gpioe.moder.modify(|_, w| w.moder11().output());
|
||||
gpioe.otyper.modify(|_, w| w.ot11().push_pull());
|
||||
gpioe.odr.modify(|_, w| w.odr11().low());
|
||||
|
||||
// DAC_CLR: PE12
|
||||
gpioe.moder.modify(|_, w| w.moder12().output());
|
||||
gpioe.otyper.modify(|_, w| w.ot12().push_pull());
|
||||
gpioe.odr.modify(|_, w| w.odr12().high());
|
||||
|
||||
// AFE1_A0,1: PD14,PD15
|
||||
gpiod
|
||||
.otyper
|
||||
.modify(|_, w| w.ot14().push_pull().ot15().push_pull());
|
||||
gpiod
|
||||
.moder
|
||||
.modify(|_, w| w.moder14().output().moder15().output());
|
||||
gpiod.odr.modify(|_, w| w.odr14().low().odr15().low());
|
||||
|
||||
// I2C2: SDA,SCL: PF0,PF1
|
||||
gpiof
|
||||
.moder
|
||||
.modify(|_, w| w.moder0().alternate().moder1().alternate());
|
||||
gpiof.afrl.modify(|_, w| w.afr0().af4().afr1().af4());
|
||||
gpiof
|
||||
.otyper
|
||||
.modify(|_, w| w.ot0().open_drain().ot1().open_drain());
|
||||
|
||||
// ADC1
|
||||
// SCK: PF6
|
||||
gpiof.moder.modify(|_, w| w.moder7().alternate());
|
||||
gpiof.otyper.modify(|_, w| w.ot7().push_pull());
|
||||
gpiof.ospeedr.modify(|_, w| w.ospeedr7().very_high_speed());
|
||||
gpiof.afrl.modify(|_, w| w.afr7().af5());
|
||||
// MOSI: PF9
|
||||
// MISO: PF7
|
||||
gpiof.moder.modify(|_, w| w.moder8().alternate());
|
||||
gpiof.afrh.modify(|_, w| w.afr8().af5());
|
||||
// NSS: PF8
|
||||
gpiof.moder.modify(|_, w| w.moder6().alternate());
|
||||
gpiof.otyper.modify(|_, w| w.ot6().push_pull());
|
||||
gpiof.ospeedr.modify(|_, w| w.ospeedr6().very_high_speed());
|
||||
gpiof.afrl.modify(|_, w| w.afr6().af5());
|
||||
|
||||
// DAC1
|
||||
// SCK: PE2
|
||||
gpioe.moder.modify(|_, w| w.moder2().alternate());
|
||||
gpioe.otyper.modify(|_, w| w.ot2().push_pull());
|
||||
gpioe.ospeedr.modify(|_, w| w.ospeedr2().very_high_speed());
|
||||
gpioe.afrl.modify(|_, w| w.afr2().af5());
|
||||
// MOSI: PE6
|
||||
gpioe.moder.modify(|_, w| w.moder6().alternate());
|
||||
gpioe.otyper.modify(|_, w| w.ot6().push_pull());
|
||||
gpioe.ospeedr.modify(|_, w| w.ospeedr6().very_high_speed());
|
||||
gpioe.afrl.modify(|_, w| w.afr6().af5());
|
||||
// MISO: PE5
|
||||
// NSS: PE4
|
||||
gpioe.moder.modify(|_, w| w.moder4().alternate());
|
||||
gpioe.otyper.modify(|_, w| w.ot4().push_pull());
|
||||
gpioe.ospeedr.modify(|_, w| w.ospeedr4().very_high_speed());
|
||||
gpioe.afrl.modify(|_, w| w.afr4().af5());
|
||||
|
||||
// DAC1_LDAC: PE15
|
||||
gpioe.moder.modify(|_, w| w.moder15().output());
|
||||
gpioe.otyper.modify(|_, w| w.ot15().push_pull());
|
||||
gpioe.odr.modify(|_, w| w.odr15().low());
|
||||
}
|
||||
|
||||
// ADC0
|
||||
fn spi1_setup(spi1: &pac::SPI1) {
|
||||
spi1.cfg1
|
||||
.modify(|_, w| w.mbr().div4().dsize().bits(16 - 1).fthlv().one_frame());
|
||||
spi1.cfg2.modify(|_, w| {
|
||||
w.afcntr()
|
||||
.controlled()
|
||||
.ssom()
|
||||
.not_asserted()
|
||||
.ssoe()
|
||||
.enabled()
|
||||
.ssiop()
|
||||
.active_low()
|
||||
.ssm()
|
||||
.disabled()
|
||||
.cpol()
|
||||
.idle_high()
|
||||
.cpha()
|
||||
.second_edge()
|
||||
.lsbfrst()
|
||||
.msbfirst()
|
||||
.master()
|
||||
.master()
|
||||
.sp()
|
||||
.motorola()
|
||||
.comm()
|
||||
.receiver()
|
||||
.ioswp()
|
||||
.disabled()
|
||||
.midi()
|
||||
.bits(0)
|
||||
.mssi()
|
||||
.bits(6)
|
||||
});
|
||||
spi1.cr2.modify(|_, w| w.tsize().bits(1));
|
||||
spi1.cr1.write(|w| w.spe().enabled());
|
||||
}
|
||||
|
||||
// ADC1
|
||||
fn spi5_setup(spi5: &pac::SPI5) {
|
||||
spi5.cfg1
|
||||
.modify(|_, w| w.mbr().div4().dsize().bits(16 - 1).fthlv().one_frame());
|
||||
spi5.cfg2.modify(|_, w| {
|
||||
w.afcntr()
|
||||
.controlled()
|
||||
.ssom()
|
||||
.not_asserted()
|
||||
.ssoe()
|
||||
.enabled()
|
||||
.ssiop()
|
||||
.active_low()
|
||||
.ssm()
|
||||
.disabled()
|
||||
.cpol()
|
||||
.idle_high()
|
||||
.cpha()
|
||||
.second_edge()
|
||||
.lsbfrst()
|
||||
.msbfirst()
|
||||
.master()
|
||||
.master()
|
||||
.sp()
|
||||
.motorola()
|
||||
.comm()
|
||||
.receiver()
|
||||
.ioswp()
|
||||
.disabled()
|
||||
.midi()
|
||||
.bits(0)
|
||||
.mssi()
|
||||
.bits(6)
|
||||
});
|
||||
spi5.cr2.modify(|_, w| w.tsize().bits(1));
|
||||
spi5.cr1.write(|w| w.spe().enabled());
|
||||
}
|
||||
|
||||
// DAC0
|
||||
fn spi2_setup(spi2: &pac::SPI2) {
|
||||
spi2.cfg1
|
||||
.modify(|_, w| w.mbr().div2().dsize().bits(16 - 1).fthlv().one_frame());
|
||||
spi2.cfg2.modify(|_, w| {
|
||||
w.afcntr()
|
||||
.controlled()
|
||||
.ssom()
|
||||
.not_asserted()
|
||||
.ssoe()
|
||||
.enabled()
|
||||
.ssiop()
|
||||
.active_low()
|
||||
.ssm()
|
||||
.disabled()
|
||||
.cpol()
|
||||
.idle_low()
|
||||
.cpha()
|
||||
.first_edge()
|
||||
.lsbfrst()
|
||||
.msbfirst()
|
||||
.master()
|
||||
.master()
|
||||
.sp()
|
||||
.motorola()
|
||||
.comm()
|
||||
.transmitter()
|
||||
.ioswp()
|
||||
.disabled()
|
||||
.midi()
|
||||
.bits(0)
|
||||
.mssi()
|
||||
.bits(0)
|
||||
});
|
||||
spi2.cr2.modify(|_, w| w.tsize().bits(0));
|
||||
spi2.cr1.write(|w| w.spe().enabled());
|
||||
spi2.cr1.modify(|_, w| w.cstart().started());
|
||||
}
|
||||
|
||||
// DAC1
|
||||
fn spi4_setup(spi4: &pac::SPI4) {
|
||||
spi4.cfg1
|
||||
.modify(|_, w| w.mbr().div2().dsize().bits(16 - 1).fthlv().one_frame());
|
||||
spi4.cfg2.modify(|_, w| {
|
||||
w.afcntr()
|
||||
.controlled()
|
||||
.ssom()
|
||||
.not_asserted()
|
||||
.ssoe()
|
||||
.enabled()
|
||||
.ssiop()
|
||||
.active_low()
|
||||
.ssm()
|
||||
.disabled()
|
||||
.cpol()
|
||||
.idle_low()
|
||||
.cpha()
|
||||
.first_edge()
|
||||
.lsbfrst()
|
||||
.msbfirst()
|
||||
.master()
|
||||
.master()
|
||||
.sp()
|
||||
.motorola()
|
||||
.comm()
|
||||
.transmitter()
|
||||
.ioswp()
|
||||
.disabled()
|
||||
.midi()
|
||||
.bits(0)
|
||||
.mssi()
|
||||
.bits(0)
|
||||
});
|
||||
spi4.cr2.modify(|_, w| w.tsize().bits(0));
|
||||
spi4.cr1.write(|w| w.spe().enabled());
|
||||
spi4.cr1.modify(|_, w| w.cstart().started());
|
||||
}
|
||||
|
||||
fn tim2_setup(tim2: &pac::TIM2) {
|
||||
tim2.psc.write(|w| w.psc().bits(200 - 1)); // from 200 MHz
|
||||
tim2.arr.write(|w| unsafe { w.bits(2 - 1) }); // 2 µs
|
||||
tim2.dier.write(|w| w.ude().set_bit());
|
||||
tim2.egr.write(|w| w.ug().set_bit());
|
||||
tim2.cr1.modify(|_, w| w.dir().clear_bit()); // up
|
||||
}
|
||||
|
||||
fn dma1_setup(
|
||||
dma1: &pac::DMA1,
|
||||
dmamux1: &pac::DMAMUX1,
|
||||
ma: usize,
|
||||
pa0: usize,
|
||||
pa1: usize,
|
||||
) {
|
||||
dma1.st[0].cr.modify(|_, w| w.en().clear_bit());
|
||||
while dma1.st[0].cr.read().en().bit_is_set() {}
|
||||
|
||||
dma1.st[0].par.write(|w| unsafe { w.bits(pa0 as u32) });
|
||||
dma1.st[0].m0ar.write(|w| unsafe { w.bits(ma as u32) });
|
||||
dma1.st[0].ndtr.write(|w| w.ndt().bits(1));
|
||||
dmamux1.ccr[0].modify(|_, w| w.dmareq_id().tim2_up());
|
||||
dma1.st[0].cr.modify(|_, w| {
|
||||
w.pl()
|
||||
.medium()
|
||||
.circ()
|
||||
.enabled()
|
||||
.msize()
|
||||
.bits32()
|
||||
.minc()
|
||||
.fixed()
|
||||
.mburst()
|
||||
.single()
|
||||
.psize()
|
||||
.bits32()
|
||||
.pinc()
|
||||
.fixed()
|
||||
.pburst()
|
||||
.single()
|
||||
.dbm()
|
||||
.disabled()
|
||||
.dir()
|
||||
.memory_to_peripheral()
|
||||
.pfctrl()
|
||||
.dma()
|
||||
});
|
||||
dma1.st[0].fcr.modify(|_, w| w.dmdis().clear_bit());
|
||||
dma1.st[0].cr.modify(|_, w| w.en().set_bit());
|
||||
|
||||
dma1.st[1].cr.modify(|_, w| w.en().clear_bit());
|
||||
while dma1.st[1].cr.read().en().bit_is_set() {}
|
||||
|
||||
dma1.st[1].par.write(|w| unsafe { w.bits(pa1 as u32) });
|
||||
dma1.st[1].m0ar.write(|w| unsafe { w.bits(ma as u32) });
|
||||
dma1.st[1].ndtr.write(|w| w.ndt().bits(1));
|
||||
dmamux1.ccr[1].modify(|_, w| w.dmareq_id().tim2_up());
|
||||
dma1.st[1].cr.modify(|_, w| {
|
||||
w.pl()
|
||||
.medium()
|
||||
.circ()
|
||||
.enabled()
|
||||
.msize()
|
||||
.bits32()
|
||||
.minc()
|
||||
.fixed()
|
||||
.mburst()
|
||||
.single()
|
||||
.psize()
|
||||
.bits32()
|
||||
.pinc()
|
||||
.fixed()
|
||||
.pburst()
|
||||
.single()
|
||||
.dbm()
|
||||
.disabled()
|
||||
.dir()
|
||||
.memory_to_peripheral()
|
||||
.pfctrl()
|
||||
.dma()
|
||||
});
|
||||
dma1.st[1].fcr.modify(|_, w| w.dmdis().clear_bit());
|
||||
dma1.st[1].cr.modify(|_, w| w.en().set_bit());
|
||||
}
|
||||
|
||||
#[link_section = ".sram1.datspi"]
|
||||
static mut DAT: u32 = 0x201; // EN | CSTART
|
||||
|
||||
pub fn init() {
|
||||
let mut cp = unsafe { cortex_m::Peripherals::steal() };
|
||||
let dp = unsafe { pac::Peripherals::steal() };
|
||||
|
||||
let rcc = dp.RCC;
|
||||
rcc_reset(&rcc);
|
||||
pwr_setup(&dp.PWR);
|
||||
rcc_pll_setup(&rcc, &dp.FLASH);
|
||||
rcc.apb4enr.modify(|_, w| w.syscfgen().set_bit());
|
||||
io_compensation_setup(&dp.SYSCFG);
|
||||
|
||||
cp.SCB.enable_icache();
|
||||
// TODO: ETH DMA coherence issues
|
||||
// cp.SCB.enable_dcache(&mut cp.CPUID);
|
||||
cp.DWT.enable_cycle_counter(); // japaric/cortex-m-rtfm#184
|
||||
|
||||
rcc.ahb4enr.modify(|_, w| {
|
||||
w.gpioaen()
|
||||
.set_bit()
|
||||
.gpioben()
|
||||
.set_bit()
|
||||
.gpiocen()
|
||||
.set_bit()
|
||||
.gpioden()
|
||||
.set_bit()
|
||||
.gpioeen()
|
||||
.set_bit()
|
||||
.gpiofen()
|
||||
.set_bit()
|
||||
.gpiogen()
|
||||
.set_bit()
|
||||
});
|
||||
gpio_setup(
|
||||
&dp.GPIOA, &dp.GPIOB, &dp.GPIOD, &dp.GPIOE, &dp.GPIOF, &dp.GPIOG,
|
||||
);
|
||||
|
||||
rcc.apb1lenr.modify(|_, w| w.spi2en().set_bit());
|
||||
let spi2 = dp.SPI2;
|
||||
spi2_setup(&spi2);
|
||||
|
||||
rcc.apb2enr.modify(|_, w| w.spi4en().set_bit());
|
||||
let spi4 = dp.SPI4;
|
||||
spi4_setup(&spi4);
|
||||
|
||||
rcc.apb2enr.modify(|_, w| w.spi1en().set_bit());
|
||||
let spi1 = dp.SPI1;
|
||||
spi1_setup(&spi1);
|
||||
spi1.ier.write(|w| w.eotie().set_bit());
|
||||
|
||||
rcc.apb2enr.modify(|_, w| w.spi5en().set_bit());
|
||||
let spi5 = dp.SPI5;
|
||||
spi5_setup(&spi5);
|
||||
// spi5.ier.write(|w| w.eotie().set_bit());
|
||||
|
||||
rcc.ahb2enr.modify(|_, w| {
|
||||
w.sram1en()
|
||||
.set_bit()
|
||||
.sram2en()
|
||||
.set_bit()
|
||||
.sram3en()
|
||||
.set_bit()
|
||||
});
|
||||
rcc.ahb1enr.modify(|_, w| w.dma1en().set_bit());
|
||||
// init SRAM1 rodata can't load with sram1 disabled
|
||||
unsafe { DAT = 0x201 }; // EN | CSTART
|
||||
cortex_m::asm::dsb();
|
||||
let dat_addr = unsafe { &DAT as *const _ } as usize;
|
||||
cp.SCB.clean_dcache_by_address(dat_addr, 4);
|
||||
|
||||
dma1_setup(
|
||||
&dp.DMA1,
|
||||
&dp.DMAMUX1,
|
||||
dat_addr,
|
||||
&spi1.cr1 as *const _ as usize,
|
||||
&spi5.cr1 as *const _ as usize,
|
||||
);
|
||||
|
||||
rcc.apb1lenr.modify(|_, w| w.tim2en().set_bit());
|
||||
|
||||
// work around the SPI stall erratum
|
||||
let dbgmcu = dp.DBGMCU;
|
||||
dbgmcu.apb1lfz1.modify(|_, w| w.tim2().set_bit());
|
||||
|
||||
tim2_setup(&dp.TIM2);
|
||||
|
||||
rcc.apb1lenr.modify(|_, w| w.i2c2en().set_bit());
|
||||
i2c::setup(&dp.I2C2);
|
||||
|
||||
rcc.apb4enr.modify(|_, w| w.syscfgen().set_bit());
|
||||
rcc.ahb1enr.modify(|_, w| {
|
||||
w.eth1macen()
|
||||
.set_bit()
|
||||
.eth1txen()
|
||||
.set_bit()
|
||||
.eth1rxen()
|
||||
.set_bit()
|
||||
});
|
||||
dp.SYSCFG
|
||||
.pmcr
|
||||
.modify(|_, w| unsafe { w.epis().bits(0b100) }); // RMII
|
||||
eth::setup_pins(&dp.GPIOA, &dp.GPIOB, &dp.GPIOC, &dp.GPIOG);
|
||||
|
||||
// enable TIM2 this must be late to be able to handle the first ADC SPI
|
||||
// interrupt in time
|
||||
dp.TIM2.cr1.modify(|_, w| w.cen().set_bit());
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
use super::i2c;
|
||||
use stm32h7::stm32h743 as pac;
|
||||
|
||||
const I2C_ADDR: u8 = 0xa0;
|
||||
|
||||
pub fn read_eui48(i2c: &pac::I2C2) -> Result<[u8; 6], i2c::Error> {
|
||||
let mut buffer = [0u8; 6];
|
||||
i2c::write_read(i2c, I2C_ADDR, &[0xFAu8], &mut buffer)?;
|
||||
Ok(buffer)
|
||||
}
|
649
src/eth.rs
649
src/eth.rs
|
@ -1,649 +0,0 @@
|
|||
use core::{cmp, slice};
|
||||
use smoltcp::phy;
|
||||
use smoltcp::time::Instant;
|
||||
use smoltcp::wire::EthernetAddress;
|
||||
use smoltcp::Result;
|
||||
use stm32h7::stm32h743 as pac;
|
||||
|
||||
#[allow(dead_code)]
|
||||
mod phy_consts {
|
||||
pub const PHY_REG_BCR: u8 = 0x00;
|
||||
pub const PHY_REG_BSR: u8 = 0x01;
|
||||
pub const PHY_REG_ID1: u8 = 0x02;
|
||||
pub const PHY_REG_ID2: u8 = 0x03;
|
||||
pub const PHY_REG_ANTX: u8 = 0x04;
|
||||
pub const PHY_REG_ANRX: u8 = 0x05;
|
||||
pub const PHY_REG_ANEXP: u8 = 0x06;
|
||||
pub const PHY_REG_ANNPTX: u8 = 0x07;
|
||||
pub const PHY_REG_ANNPRX: u8 = 0x08;
|
||||
pub const PHY_REG_SSR: u8 = 0x1F; // Special Status Register
|
||||
pub const PHY_REG_CTL: u8 = 0x0D; // Ethernet PHY Register Control
|
||||
pub const PHY_REG_ADDAR: u8 = 0x0E; // Ethernet PHY Address or Data
|
||||
|
||||
pub const PHY_REG_WUCSR: u16 = 0x8010;
|
||||
|
||||
pub const PHY_REG_BCR_COLTEST: u16 = 1 << 7;
|
||||
pub const PHY_REG_BCR_FD: u16 = 1 << 8;
|
||||
pub const PHY_REG_BCR_ANRST: u16 = 1 << 9;
|
||||
pub const PHY_REG_BCR_ISOLATE: u16 = 1 << 10;
|
||||
pub const PHY_REG_BCR_POWERDN: u16 = 1 << 11;
|
||||
pub const PHY_REG_BCR_AN: u16 = 1 << 12;
|
||||
pub const PHY_REG_BCR_100M: u16 = 1 << 13;
|
||||
pub const PHY_REG_BCR_LOOPBACK: u16 = 1 << 14;
|
||||
pub const PHY_REG_BCR_RESET: u16 = 1 << 15;
|
||||
|
||||
pub const PHY_REG_BSR_JABBER: u16 = 1 << 1;
|
||||
pub const PHY_REG_BSR_UP: u16 = 1 << 2;
|
||||
pub const PHY_REG_BSR_FAULT: u16 = 1 << 4;
|
||||
pub const PHY_REG_BSR_ANDONE: u16 = 1 << 5;
|
||||
|
||||
pub const PHY_REG_SSR_ANDONE: u16 = 1 << 12;
|
||||
pub const PHY_REG_SSR_SPEED: u16 = 0b111 << 2;
|
||||
pub const PHY_REG_SSR_10BASE_HD: u16 = 0b001 << 2;
|
||||
pub const PHY_REG_SSR_10BASE_FD: u16 = 0b101 << 2;
|
||||
pub const PHY_REG_SSR_100BASE_HD: u16 = 0b010 << 2;
|
||||
pub const PHY_REG_SSR_100BASE_FD: u16 = 0b110 << 2;
|
||||
}
|
||||
use self::phy_consts::*;
|
||||
|
||||
const EMAC_DES3_OWN: u32 = 0x8000_0000;
|
||||
const EMAC_DES3_CTXT: u32 = 0x4000_0000;
|
||||
const EMAC_DES3_FD: u32 = 0x2000_0000;
|
||||
const EMAC_DES3_LD: u32 = 0x1000_0000;
|
||||
const EMAC_DES3_ES: u32 = 0x0000_8000;
|
||||
const EMAC_TDES2_IOC: u32 = 0x8000_0000;
|
||||
const EMAC_RDES3_IOC: u32 = 0x4000_0000;
|
||||
const EMAC_RDES3_PL: u32 = 0x0000_7FFF;
|
||||
const EMAC_RDES3_BUF1V: u32 = 0x0100_0000;
|
||||
const EMAC_TDES2_B1L: u32 = 0x0000_3FFF;
|
||||
const EMAC_DES0_BUF1AP: u32 = 0xFFFF_FFFF;
|
||||
|
||||
// 6 DMAC, 6 SMAC, 4 q tag, 2 ethernet type II, 1500 ip MTU, 4 CRC, 2 padding
|
||||
const ETH_BUFFER_SIZE: usize = 1524;
|
||||
const ETH_DESC_U32_SIZE: usize = 4;
|
||||
const ETH_TX_BUFFER_COUNT: usize = 4;
|
||||
const ETH_RX_BUFFER_COUNT: usize = 4;
|
||||
|
||||
#[allow(dead_code)]
|
||||
mod cr_consts {
|
||||
/* For HCLK 60-100 MHz */
|
||||
pub const ETH_MACMIIAR_CR_HCLK_DIV_42: u8 = 0;
|
||||
/* For HCLK 100-150 MHz */
|
||||
pub const ETH_MACMIIAR_CR_HCLK_DIV_62: u8 = 1;
|
||||
/* For HCLK 20-35 MHz */
|
||||
pub const ETH_MACMIIAR_CR_HCLK_DIV_16: u8 = 2;
|
||||
/* For HCLK 35-60 MHz */
|
||||
pub const ETH_MACMIIAR_CR_HCLK_DIV_26: u8 = 3;
|
||||
/* For HCLK 150-250 MHz */
|
||||
pub const ETH_MACMIIAR_CR_HCLK_DIV_102: u8 = 4;
|
||||
/* For HCLK 250-300 MHz */
|
||||
pub const ETH_MACMIIAR_CR_HCLK_DIV_124: u8 = 5;
|
||||
}
|
||||
use self::cr_consts::*;
|
||||
|
||||
// set clock range in MAC MII address register
|
||||
// 200 MHz AHB clock = eth_hclk
|
||||
const CLOCK_RANGE: u8 = ETH_MACMIIAR_CR_HCLK_DIV_102;
|
||||
|
||||
pub fn setup_pins(
|
||||
gpioa: &pac::GPIOA,
|
||||
gpiob: &pac::GPIOB,
|
||||
gpioc: &pac::GPIOC,
|
||||
gpiog: &pac::GPIOG,
|
||||
) {
|
||||
// PA1 RMII_REF_CLK
|
||||
gpioa.moder.modify(|_, w| w.moder1().alternate());
|
||||
gpioa.afrl.modify(|_, w| w.afr1().af11());
|
||||
gpioa.ospeedr.modify(|_, w| w.ospeedr1().very_high_speed());
|
||||
// PA2 RMII_MDIO
|
||||
gpioa.moder.modify(|_, w| w.moder2().alternate());
|
||||
gpioa.afrl.modify(|_, w| w.afr2().af11());
|
||||
gpioa.ospeedr.modify(|_, w| w.ospeedr2().very_high_speed());
|
||||
// PC1 RMII_MDC
|
||||
gpioc.moder.modify(|_, w| w.moder1().alternate());
|
||||
gpioc.afrl.modify(|_, w| w.afr1().af11());
|
||||
gpioc.ospeedr.modify(|_, w| w.ospeedr1().very_high_speed());
|
||||
// PA7 RMII_CRS_DV
|
||||
gpioa.moder.modify(|_, w| w.moder7().alternate());
|
||||
gpioa.afrl.modify(|_, w| w.afr7().af11());
|
||||
gpioa.ospeedr.modify(|_, w| w.ospeedr7().very_high_speed());
|
||||
// PC4 RMII_RXD0
|
||||
gpioc.moder.modify(|_, w| w.moder4().alternate());
|
||||
gpioc.afrl.modify(|_, w| w.afr4().af11());
|
||||
gpioc.ospeedr.modify(|_, w| w.ospeedr4().very_high_speed());
|
||||
// PC5 RMII_RXD1
|
||||
gpioc.moder.modify(|_, w| w.moder5().alternate());
|
||||
gpioc.afrl.modify(|_, w| w.afr5().af11());
|
||||
gpioc.ospeedr.modify(|_, w| w.ospeedr5().very_high_speed());
|
||||
// PB11 RMII_TX_EN
|
||||
gpiob.moder.modify(|_, w| w.moder11().alternate());
|
||||
gpiob.afrh.modify(|_, w| w.afr11().af11());
|
||||
gpiob.ospeedr.modify(|_, w| w.ospeedr11().very_high_speed());
|
||||
// PB12 RXII_TXD0
|
||||
gpiob.moder.modify(|_, w| w.moder12().alternate());
|
||||
gpiob.afrh.modify(|_, w| w.afr12().af11());
|
||||
gpiob.ospeedr.modify(|_, w| w.ospeedr12().very_high_speed());
|
||||
// PG14 RMII TXD1
|
||||
gpiog.moder.modify(|_, w| w.moder14().alternate());
|
||||
gpiog.afrh.modify(|_, w| w.afr14().af11());
|
||||
gpiog.ospeedr.modify(|_, w| w.ospeedr14().very_high_speed());
|
||||
}
|
||||
|
||||
const PHY_ADDR: u8 = 0;
|
||||
|
||||
fn phy_read(reg_addr: u8, mac: &pac::ETHERNET_MAC) -> u16 {
|
||||
while mac.macmdioar.read().mb().bit_is_set() {}
|
||||
mac.macmdioar.modify(|_, w| unsafe {
|
||||
w.pa()
|
||||
.bits(PHY_ADDR)
|
||||
.rda()
|
||||
.bits(reg_addr)
|
||||
.goc()
|
||||
.bits(0b11) // read
|
||||
.cr()
|
||||
.bits(CLOCK_RANGE)
|
||||
.mb()
|
||||
.set_bit()
|
||||
});
|
||||
while mac.macmdioar.read().mb().bit_is_set() {}
|
||||
mac.macmdiodr.read().md().bits()
|
||||
}
|
||||
|
||||
fn phy_write(reg_addr: u8, reg_data: u16, mac: &pac::ETHERNET_MAC) {
|
||||
while mac.macmdioar.read().mb().bit_is_set() {}
|
||||
mac.macmdiodr.write(|w| unsafe { w.md().bits(reg_data) });
|
||||
mac.macmdioar.modify(|_, w| unsafe {
|
||||
w.pa()
|
||||
.bits(PHY_ADDR)
|
||||
.rda()
|
||||
.bits(reg_addr)
|
||||
.goc()
|
||||
.bits(0b01) // write
|
||||
.cr()
|
||||
.bits(CLOCK_RANGE)
|
||||
.mb()
|
||||
.set_bit()
|
||||
});
|
||||
while mac.macmdioar.read().mb().bit_is_set() {}
|
||||
}
|
||||
|
||||
// Writes a value to an extended PHY register in MMD address space
|
||||
fn phy_write_ext(reg_addr: u16, reg_data: u16, mac: &pac::ETHERNET_MAC) {
|
||||
phy_write(PHY_REG_CTL, 0x0003, mac); // set address
|
||||
phy_write(PHY_REG_ADDAR, reg_addr, mac);
|
||||
phy_write(PHY_REG_CTL, 0x4003, mac); // set data
|
||||
phy_write(PHY_REG_ADDAR, reg_data, mac);
|
||||
}
|
||||
|
||||
#[repr(align(4))]
|
||||
struct RxRing {
|
||||
desc_buf: [[u32; ETH_DESC_U32_SIZE]; ETH_RX_BUFFER_COUNT],
|
||||
pkt_buf: [[u8; ETH_BUFFER_SIZE]; ETH_RX_BUFFER_COUNT],
|
||||
cur_desc: usize,
|
||||
}
|
||||
|
||||
impl RxRing {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
desc_buf: [[0; ETH_DESC_U32_SIZE]; ETH_RX_BUFFER_COUNT],
|
||||
pkt_buf: [[0; ETH_BUFFER_SIZE]; ETH_RX_BUFFER_COUNT],
|
||||
cur_desc: 0,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn init(&mut self, dma: &pac::ETHERNET_DMA) {
|
||||
assert_eq!(self.desc_buf[0].len() % 4, 0);
|
||||
assert_eq!(self.pkt_buf[0].len() % 4, 0);
|
||||
|
||||
for i in 0..self.desc_buf.len() {
|
||||
for j in 0..self.desc_buf[0].len() {
|
||||
self.desc_buf[i][j] = 0;
|
||||
}
|
||||
for j in 0..self.pkt_buf[0].len() {
|
||||
self.pkt_buf[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
let addr = &self.desc_buf as *const _ as u32;
|
||||
assert_eq!(addr & 0x3, 0);
|
||||
dma.dmacrx_dlar.write(|w| w.bits(addr));
|
||||
dma.dmacrx_rlr
|
||||
.write(|w| w.rdrl().bits(self.desc_buf.len() as u16 - 1));
|
||||
|
||||
self.cur_desc = 0;
|
||||
for _ in 0..self.desc_buf.len() {
|
||||
self.buf_release()
|
||||
}
|
||||
}
|
||||
|
||||
fn next_desc(&self) -> usize {
|
||||
(self.cur_desc + 1) % self.desc_buf.len()
|
||||
}
|
||||
|
||||
// not owned by DMA
|
||||
fn buf_owned(&self) -> bool {
|
||||
self.desc_buf[self.cur_desc][3] & EMAC_DES3_OWN == 0
|
||||
}
|
||||
|
||||
fn buf_valid(&self) -> bool {
|
||||
self.desc_buf[self.cur_desc][3]
|
||||
& (EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_ES | EMAC_DES3_CTXT)
|
||||
== (EMAC_DES3_FD | EMAC_DES3_LD)
|
||||
}
|
||||
|
||||
unsafe fn buf_as_slice_mut<'a>(&self) -> &'a mut [u8] {
|
||||
let len = (self.desc_buf[self.cur_desc][3] & EMAC_RDES3_PL) as usize;
|
||||
let len = cmp::min(len, ETH_BUFFER_SIZE);
|
||||
let addr = &self.pkt_buf[self.cur_desc] as *const _ as *mut u8;
|
||||
slice::from_raw_parts_mut(addr, len)
|
||||
}
|
||||
|
||||
fn buf_release(&mut self) {
|
||||
let addr = &self.pkt_buf[self.cur_desc] as *const _;
|
||||
self.desc_buf[self.cur_desc][0] = addr as u32 & EMAC_DES0_BUF1AP;
|
||||
self.desc_buf[self.cur_desc][3] =
|
||||
EMAC_RDES3_BUF1V | EMAC_RDES3_IOC | EMAC_DES3_OWN;
|
||||
|
||||
let addr = &self.desc_buf[self.cur_desc] as *const _ as u32;
|
||||
assert_eq!(addr & 0x3, 0);
|
||||
|
||||
let dma = unsafe { pac::Peripherals::steal().ETHERNET_DMA };
|
||||
|
||||
// Ensure changes to the descriptor (in particular, the OWN flag) are
|
||||
// committed before DMA engine sees tail pointer store.
|
||||
cortex_m::asm::dsb();
|
||||
|
||||
dma.dmacrx_dtpr.write(|w| unsafe { w.bits(addr) });
|
||||
|
||||
self.cur_desc = self.next_desc();
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(align(4))]
|
||||
struct TxRing {
|
||||
desc_buf: [[u32; ETH_DESC_U32_SIZE]; ETH_TX_BUFFER_COUNT],
|
||||
pkt_buf: [[u8; ETH_BUFFER_SIZE]; ETH_TX_BUFFER_COUNT],
|
||||
cur_desc: usize,
|
||||
}
|
||||
|
||||
impl TxRing {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
desc_buf: [[0; ETH_DESC_U32_SIZE]; ETH_TX_BUFFER_COUNT],
|
||||
pkt_buf: [[0; ETH_BUFFER_SIZE]; ETH_TX_BUFFER_COUNT],
|
||||
cur_desc: 0,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn init(&mut self, dma: &pac::ETHERNET_DMA) {
|
||||
assert_eq!(self.desc_buf[0].len() % 4, 0);
|
||||
assert_eq!(self.pkt_buf[0].len() % 4, 0);
|
||||
|
||||
for i in 0..self.desc_buf.len() {
|
||||
for j in 0..self.desc_buf[0].len() {
|
||||
self.desc_buf[i][j] = 0;
|
||||
}
|
||||
for j in 0..self.pkt_buf[0].len() {
|
||||
self.pkt_buf[i][j] = 0;
|
||||
}
|
||||
}
|
||||
self.cur_desc = 0;
|
||||
|
||||
let addr = &self.desc_buf as *const _ as u32;
|
||||
assert_eq!(addr & 0x3, 0);
|
||||
dma.dmactx_dlar.write(|w| w.bits(addr));
|
||||
dma.dmactx_rlr
|
||||
.write(|w| w.tdrl().bits(self.desc_buf.len() as u16 - 1));
|
||||
let addr = &self.desc_buf[0] as *const _ as u32;
|
||||
assert_eq!(addr & 0x3, 0);
|
||||
dma.dmactx_dtpr.write(|w| w.bits(addr));
|
||||
}
|
||||
|
||||
fn next_desc(&self) -> usize {
|
||||
(self.cur_desc + 1) % self.desc_buf.len()
|
||||
}
|
||||
|
||||
// not owned by DMA
|
||||
fn buf_owned(&self) -> bool {
|
||||
self.desc_buf[self.cur_desc][3] & EMAC_DES3_OWN == 0
|
||||
}
|
||||
|
||||
unsafe fn buf_as_slice_mut<'a>(&mut self, len: usize) -> &'a mut [u8] {
|
||||
let len = cmp::min(len, ETH_BUFFER_SIZE);
|
||||
self.desc_buf[self.cur_desc][2] =
|
||||
EMAC_TDES2_IOC | (len as u32 & EMAC_TDES2_B1L);
|
||||
let addr = &self.pkt_buf[self.cur_desc] as *const _ as *mut u8;
|
||||
self.desc_buf[self.cur_desc][0] = addr as u32 & EMAC_DES0_BUF1AP;
|
||||
slice::from_raw_parts_mut(addr, len)
|
||||
}
|
||||
|
||||
fn buf_release(&mut self) {
|
||||
self.desc_buf[self.cur_desc][3] =
|
||||
EMAC_DES3_OWN | EMAC_DES3_FD | EMAC_DES3_LD;
|
||||
self.cur_desc = self.next_desc();
|
||||
|
||||
let addr = &self.desc_buf[self.cur_desc] as *const _ as u32;
|
||||
assert_eq!(addr & 0x3, 0);
|
||||
|
||||
let dma = unsafe { pac::Peripherals::steal().ETHERNET_DMA };
|
||||
|
||||
// Ensure packet contents as well as changes to the descriptor have been
|
||||
// committed before DMA engine sees the tail pointer store.
|
||||
cortex_m::asm::dsb();
|
||||
|
||||
dma.dmactx_dtpr.write(|w| unsafe { w.bits(addr) });
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Device {
|
||||
rx: RxRing,
|
||||
tx: TxRing,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
rx: RxRing::new(),
|
||||
tx: TxRing::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the ethernet peripherals
|
||||
//
|
||||
// # Safety
|
||||
//
|
||||
// This iis transitively unsafe since it sets potentially
|
||||
// unsafe register values. Might ultimately be safe if the values
|
||||
// are correct.
|
||||
//
|
||||
// After `init` is called, `Device` shall not be moved.
|
||||
pub unsafe fn init(
|
||||
&mut self,
|
||||
mac: EthernetAddress,
|
||||
eth_mac: &pac::ETHERNET_MAC,
|
||||
eth_dma: &pac::ETHERNET_DMA,
|
||||
eth_mtl: &pac::ETHERNET_MTL,
|
||||
) {
|
||||
eth_dma.dmamr.modify(|_, w| w.swr().set_bit());
|
||||
while eth_dma.dmamr.read().swr().bit_is_set() {}
|
||||
|
||||
// 200 MHz
|
||||
eth_mac
|
||||
.mac1ustcr
|
||||
.modify(|_, w| w.tic_1us_cntr().bits(200 - 1));
|
||||
|
||||
// Configuration Register
|
||||
eth_mac.maccr.modify(|_, w| {
|
||||
w.arpen()
|
||||
.clear_bit()
|
||||
.ipc()
|
||||
.set_bit()
|
||||
.ipg()
|
||||
.bits(0b000) // 96 bit
|
||||
.ecrsfd()
|
||||
.clear_bit()
|
||||
.dcrs()
|
||||
.clear_bit()
|
||||
.bl()
|
||||
.bits(0b00) // 19
|
||||
.prelen()
|
||||
.bits(0b00) // 7
|
||||
// CRC stripping for Type frames
|
||||
.cst()
|
||||
.set_bit()
|
||||
// Fast Ethernet speed
|
||||
.fes()
|
||||
.set_bit()
|
||||
// Duplex mode
|
||||
.dm()
|
||||
.set_bit()
|
||||
// Automatic pad/CRC stripping
|
||||
.acs()
|
||||
.set_bit()
|
||||
// Retry disable in half-duplex mode
|
||||
.dr()
|
||||
.set_bit()
|
||||
});
|
||||
eth_mac.macecr.modify(|_, w| {
|
||||
w.eipgen()
|
||||
.clear_bit()
|
||||
.usp()
|
||||
.clear_bit()
|
||||
.spen()
|
||||
.clear_bit()
|
||||
.dcrcc()
|
||||
.clear_bit()
|
||||
});
|
||||
// Set the MAC address
|
||||
eth_mac.maca0lr.write(|w| {
|
||||
w.addrlo().bits(
|
||||
u32::from(mac.0[0])
|
||||
| (u32::from(mac.0[1]) << 8)
|
||||
| (u32::from(mac.0[2]) << 16)
|
||||
| (u32::from(mac.0[3]) << 24),
|
||||
)
|
||||
});
|
||||
eth_mac.maca0hr.write(|w| {
|
||||
w.addrhi()
|
||||
.bits(u16::from(mac.0[4]) | (u16::from(mac.0[5]) << 8))
|
||||
});
|
||||
// frame filter register
|
||||
eth_mac.macpfr.modify(|_, w| {
|
||||
w.dntu()
|
||||
.clear_bit()
|
||||
.ipfe()
|
||||
.clear_bit()
|
||||
.vtfe()
|
||||
.clear_bit()
|
||||
.hpf()
|
||||
.clear_bit()
|
||||
.saf()
|
||||
.clear_bit()
|
||||
.saif()
|
||||
.clear_bit()
|
||||
.pcf()
|
||||
.bits(0b00)
|
||||
.dbf()
|
||||
.clear_bit()
|
||||
.pm()
|
||||
.clear_bit()
|
||||
.daif()
|
||||
.clear_bit()
|
||||
.hmc()
|
||||
.clear_bit()
|
||||
.huc()
|
||||
.clear_bit()
|
||||
// Receive All
|
||||
.ra()
|
||||
.clear_bit()
|
||||
// Promiscuous mode
|
||||
.pr()
|
||||
.clear_bit()
|
||||
});
|
||||
eth_mac.macwtr.write(|w| w.pwe().clear_bit());
|
||||
// Flow Control Register
|
||||
eth_mac.macqtx_fcr.modify(|_, w| {
|
||||
// Pause time
|
||||
w.pt().bits(0x100)
|
||||
});
|
||||
eth_mac.macrx_fcr.modify(|_, w| w);
|
||||
eth_mtl.mtlrx_qomr.modify(|_, w| {
|
||||
w
|
||||
// Receive store and forward
|
||||
.rsf()
|
||||
.set_bit()
|
||||
// Dropping of TCP/IP checksum error frames disable
|
||||
.dis_tcp_ef()
|
||||
.clear_bit()
|
||||
// Forward error frames
|
||||
.fep()
|
||||
.clear_bit()
|
||||
// Forward undersized good packets
|
||||
.fup()
|
||||
.clear_bit()
|
||||
});
|
||||
eth_mtl.mtltx_qomr.modify(|_, w| {
|
||||
w
|
||||
// Transmit store and forward
|
||||
.tsf()
|
||||
.set_bit()
|
||||
});
|
||||
|
||||
if (phy_read(PHY_REG_ID1, eth_mac) != 0x0007)
|
||||
| (phy_read(PHY_REG_ID2, eth_mac) != 0xC131)
|
||||
{
|
||||
error!("PHY ID error!");
|
||||
}
|
||||
|
||||
phy_write(PHY_REG_BCR, PHY_REG_BCR_RESET, eth_mac);
|
||||
while phy_read(PHY_REG_BCR, eth_mac) & PHY_REG_BCR_RESET
|
||||
== PHY_REG_BCR_RESET
|
||||
{}
|
||||
phy_write_ext(PHY_REG_WUCSR, 0, eth_mac);
|
||||
phy_write(
|
||||
PHY_REG_BCR,
|
||||
PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M,
|
||||
eth_mac,
|
||||
);
|
||||
/*
|
||||
while phy_read(PHY_REG_BSR) & PHY_REG_BSR_UP == 0 {};
|
||||
while phy_read(PHY_REG_BSR) & PHY_REG_BSR_ANDONE == 0 {};
|
||||
while phy_read(PHY_REG_SSR) & (PHY_REG_SSR_ANDONE | PHY_REG_SSR_SPEED)
|
||||
!= PHY_REG_SSR_ANDONE | PHY_REG_SSR_100BASE_FD {};
|
||||
*/
|
||||
|
||||
// operation mode register
|
||||
eth_dma.dmamr.modify(|_, w| {
|
||||
w.intm()
|
||||
.bits(0b00)
|
||||
// Rx Tx priority ratio 1:1
|
||||
.pr()
|
||||
.bits(0b000)
|
||||
.txpr()
|
||||
.clear_bit()
|
||||
.da()
|
||||
.clear_bit()
|
||||
});
|
||||
// bus mode register
|
||||
eth_dma.dmasbmr.modify(|_, w| {
|
||||
// Address-aligned beats
|
||||
w.aal()
|
||||
.set_bit()
|
||||
// Fixed burst
|
||||
.fb()
|
||||
.set_bit()
|
||||
});
|
||||
eth_dma
|
||||
.dmaccr
|
||||
.modify(|_, w| w.dsl().bits(0).pblx8().clear_bit().mss().bits(536));
|
||||
eth_dma.dmactx_cr.modify(|_, w| {
|
||||
w
|
||||
// Tx DMA PBL
|
||||
.txpbl()
|
||||
.bits(32)
|
||||
.tse()
|
||||
.clear_bit()
|
||||
// Operate on second frame
|
||||
.osf()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
eth_dma.dmacrx_cr.modify(|_, w| {
|
||||
w
|
||||
// receive buffer size
|
||||
.rbsz()
|
||||
.bits(ETH_BUFFER_SIZE as u16)
|
||||
// Rx DMA PBL
|
||||
.rxpbl()
|
||||
.bits(32)
|
||||
// Disable flushing of received frames
|
||||
.rpf()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
self.rx.init(eth_dma);
|
||||
self.tx.init(eth_dma);
|
||||
|
||||
// Manage MAC transmission and reception
|
||||
eth_mac.maccr.modify(|_, w| {
|
||||
w.re()
|
||||
.bit(true) // Receiver Enable
|
||||
.te()
|
||||
.bit(true) // Transmiter Enable
|
||||
});
|
||||
eth_mtl.mtltx_qomr.modify(|_, w| w.ftq().set_bit());
|
||||
|
||||
// Ensure ring buffer descriptors have been set up in memory before
|
||||
// enabling DMA engine.
|
||||
cortex_m::asm::dsb();
|
||||
|
||||
// Manage DMA transmission and reception
|
||||
eth_dma.dmactx_cr.modify(|_, w| w.st().set_bit());
|
||||
eth_dma.dmacrx_cr.modify(|_, w| w.sr().set_bit());
|
||||
|
||||
eth_dma
|
||||
.dmacsr
|
||||
.modify(|_, w| w.tps().set_bit().rps().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> phy::Device<'a> for &'b mut Device {
|
||||
type RxToken = RxToken<'a>;
|
||||
type TxToken = TxToken<'a>;
|
||||
|
||||
fn capabilities(&self) -> phy::DeviceCapabilities {
|
||||
let mut capabilities = phy::DeviceCapabilities::default();
|
||||
// ethernet frame type II (6 smac, 6 dmac, 2 ethertype),
|
||||
// sans CRC (4), 1500 IP MTU
|
||||
capabilities.max_transmission_unit = 1514;
|
||||
capabilities.max_burst_size = Some(self.tx.desc_buf.len());
|
||||
capabilities
|
||||
}
|
||||
|
||||
fn receive(&mut self) -> Option<(RxToken, TxToken)> {
|
||||
// Skip all queued packets with errors.
|
||||
while self.rx.buf_owned() && !self.rx.buf_valid() {
|
||||
self.rx.buf_release()
|
||||
}
|
||||
|
||||
if !(self.rx.buf_owned() && self.tx.buf_owned()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some((RxToken(&mut self.rx), TxToken(&mut self.tx)))
|
||||
}
|
||||
|
||||
fn transmit(&mut self) -> Option<TxToken> {
|
||||
if !self.tx.buf_owned() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(TxToken(&mut self.tx))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RxToken<'a>(&'a mut RxRing);
|
||||
|
||||
impl<'a> phy::RxToken for RxToken<'a> {
|
||||
fn consume<R, F>(self, _timestamp: Instant, f: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
let result = f(unsafe { self.0.buf_as_slice_mut() });
|
||||
self.0.buf_release();
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TxToken<'a>(&'a mut TxRing);
|
||||
|
||||
impl<'a> phy::TxToken for TxToken<'a> {
|
||||
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
let result = f(unsafe { self.0.buf_as_slice_mut(len) });
|
||||
self.0.buf_release();
|
||||
result
|
||||
}
|
||||
}
|
145
src/i2c.rs
145
src/i2c.rs
|
@ -1,145 +0,0 @@
|
|||
use stm32h7::stm32h743 as pac;
|
||||
|
||||
// Adapted from stm32h7xx embedded-hal
|
||||
|
||||
// I2C error
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
// Bus error
|
||||
Bus,
|
||||
// Arbitration loss
|
||||
Arbitration,
|
||||
// Address not ACKd within a reasonable time (no device present?)
|
||||
Timeout,
|
||||
// Unexpected NACK during transfer
|
||||
NAck,
|
||||
}
|
||||
|
||||
// Maximum number of times to retry NACKed address phase before timing out
|
||||
// Note that many devices indicate a busy condition by NACKing (e.g. 24xx
|
||||
// EEPROMs during write)
|
||||
const N_RETRY: usize = 100; // ~ 10ms @ 100 kHz bus clock
|
||||
|
||||
pub fn setup(i2c: &pac::I2C2) {
|
||||
// Disable the peripheral before setting timings
|
||||
i2c.cr1.modify(|_, w| w.pe().clear_bit());
|
||||
|
||||
// Values from STM32MXCube for 100 kHz I2C clock with 100 MHz peripheral clock
|
||||
i2c.timingr.modify(|_, w| {
|
||||
w.presc()
|
||||
.bits(1)
|
||||
.scldel()
|
||||
.bits(0x12)
|
||||
.sdadel()
|
||||
.bits(0)
|
||||
.sclh()
|
||||
.bits(0xec)
|
||||
.scll()
|
||||
.bits(0xff)
|
||||
});
|
||||
|
||||
// Enable the peripheral
|
||||
i2c.cr1.write(|w| w.pe().set_bit());
|
||||
}
|
||||
|
||||
// Busy-wait for a flag to be asserted, erroring out on unrecoverable problems
|
||||
macro_rules! busy_wait_errors {
|
||||
($i2c:expr, $flag:ident) => {
|
||||
loop {
|
||||
let isr = $i2c.isr.read();
|
||||
|
||||
if isr.berr().bit_is_set() {
|
||||
return Err(Error::Bus);
|
||||
} else if isr.arlo().bit_is_set() {
|
||||
return Err(Error::Arbitration);
|
||||
} else if isr.nackf().bit_is_set() {
|
||||
return Err(Error::NAck);
|
||||
} else if isr.$flag().bit_is_set() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn poll_for_start_ack(
|
||||
i2c: &pac::I2C2,
|
||||
addr: u8,
|
||||
r_wn: bool,
|
||||
data_len: usize,
|
||||
autoend: bool,
|
||||
start: bool,
|
||||
) -> Result<(), Error> {
|
||||
for _i in 0..N_RETRY {
|
||||
// START and prepare to send `data_len`
|
||||
i2c.cr2.write(|w| {
|
||||
w.start()
|
||||
.bit(start)
|
||||
.sadd()
|
||||
.bits(addr as u16)
|
||||
.add10()
|
||||
.clear_bit()
|
||||
.rd_wrn()
|
||||
.bit(r_wn)
|
||||
.nbytes()
|
||||
.bits(data_len as u8)
|
||||
.autoend()
|
||||
.bit(autoend)
|
||||
});
|
||||
|
||||
loop {
|
||||
let isr = i2c.isr.read();
|
||||
|
||||
if isr.berr().bit_is_set() {
|
||||
return Err(Error::Bus);
|
||||
} else if isr.arlo().bit_is_set() {
|
||||
return Err(Error::Arbitration);
|
||||
} else if isr.nackf().bit_is_set() {
|
||||
i2c.icr.write(|w| w.nackcf().set_bit());
|
||||
// Wait to finish handling NACK-STOP
|
||||
loop {
|
||||
if i2c.isr.read().busy().bit_is_clear() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else if isr.txis().bit_is_set() || isr.rxne().bit_is_set() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::Timeout)
|
||||
}
|
||||
|
||||
pub fn write_read(
|
||||
i2c: &pac::I2C2,
|
||||
addr: u8,
|
||||
bytes: &[u8],
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), Error> {
|
||||
assert!(bytes.len() < 256 && !bytes.is_empty());
|
||||
assert!(buffer.len() < 256 && !buffer.is_empty());
|
||||
|
||||
poll_for_start_ack(i2c, addr, false, bytes.len(), false, true)?;
|
||||
|
||||
for byte in bytes {
|
||||
// Wait until we are allowed to send data (START has been ACKed or last
|
||||
// byte when through)
|
||||
busy_wait_errors!(i2c, txis);
|
||||
i2c.txdr.write(|w| w.txdata().bits(*byte));
|
||||
}
|
||||
|
||||
// Wait until the last transmission is finished
|
||||
busy_wait_errors!(i2c, tc);
|
||||
|
||||
poll_for_start_ack(i2c, addr | 1, true, buffer.len(), true, true)?;
|
||||
|
||||
for byte in buffer {
|
||||
// Wait until we have received something
|
||||
busy_wait_errors!(i2c, rxne);
|
||||
*byte = i2c.rxdr.read().rxdata().bits();
|
||||
}
|
||||
|
||||
// automatic STOP
|
||||
Ok(())
|
||||
}
|
422
src/main.rs
422
src/main.rs
|
@ -1,422 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![cfg_attr(feature = "nightly", feature(asm))]
|
||||
// Enable returning `!`
|
||||
#![cfg_attr(feature = "nightly", feature(never_type))]
|
||||
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
|
||||
|
||||
#[inline(never)]
|
||||
#[panic_handler]
|
||||
#[cfg(all(feature = "nightly", not(feature = "semihosting")))]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
let gpiod = unsafe { &*pac::GPIOD::ptr() };
|
||||
gpiod.odr.modify(|_, w| w.odr6().high().odr12().high()); // FP_LED_1, FP_LED_3
|
||||
unsafe {
|
||||
core::intrinsics::abort();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "semihosting")]
|
||||
extern crate panic_semihosting;
|
||||
|
||||
#[cfg(not(any(feature = "nightly", feature = "semihosting")))]
|
||||
extern crate panic_halt;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use core::ptr;
|
||||
// use core::sync::atomic::{AtomicU32, AtomicBool, Ordering};
|
||||
use core::fmt::Write;
|
||||
use cortex_m_rt::exception;
|
||||
use heapless::{consts::*, String, Vec};
|
||||
use rtfm::cyccnt::{Instant, U32Ext as _};
|
||||
use stm32h7::stm32h743 as pac;
|
||||
|
||||
use smoltcp as net;
|
||||
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde_json_core::{de::from_slice, ser::to_string};
|
||||
|
||||
mod eth;
|
||||
|
||||
mod iir;
|
||||
use iir::*;
|
||||
|
||||
mod board;
|
||||
mod eeprom;
|
||||
mod i2c;
|
||||
|
||||
#[cfg(not(feature = "semihosting"))]
|
||||
fn init_log() {}
|
||||
|
||||
#[cfg(feature = "semihosting")]
|
||||
fn init_log() {
|
||||
use cortex_m_log::log::{init as init_log, Logger};
|
||||
use cortex_m_log::printer::semihosting::{hio::HStdout, InterruptOk};
|
||||
use log::LevelFilter;
|
||||
static mut LOGGER: Option<Logger<InterruptOk<HStdout>>> = None;
|
||||
let logger = Logger {
|
||||
inner: InterruptOk::<_>::stdout().unwrap(),
|
||||
level: LevelFilter::Info,
|
||||
};
|
||||
let logger = unsafe { LOGGER.get_or_insert(logger) };
|
||||
|
||||
init_log(logger).unwrap();
|
||||
}
|
||||
|
||||
// Pull in build information (from `built` crate)
|
||||
mod build_info {
|
||||
#![allow(dead_code)]
|
||||
// include!(concat!(env!("OUT_DIR"), "/built.rs"));
|
||||
}
|
||||
|
||||
const SCALE: f32 = ((1 << 15) - 1) as f32;
|
||||
|
||||
// static ETHERNET_PENDING: AtomicBool = AtomicBool::new(true);
|
||||
|
||||
const TCP_RX_BUFFER_SIZE: usize = 8192;
|
||||
const TCP_TX_BUFFER_SIZE: usize = 8192;
|
||||
|
||||
macro_rules! create_socket {
|
||||
($set:ident, $rx_storage:ident, $tx_storage:ident, $target:ident) => {
|
||||
let mut $rx_storage = [0; TCP_RX_BUFFER_SIZE];
|
||||
let mut $tx_storage = [0; TCP_TX_BUFFER_SIZE];
|
||||
let tcp_rx_buffer =
|
||||
net::socket::TcpSocketBuffer::new(&mut $rx_storage[..]);
|
||||
let tcp_tx_buffer =
|
||||
net::socket::TcpSocketBuffer::new(&mut $tx_storage[..]);
|
||||
let tcp_socket =
|
||||
net::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
|
||||
let $target = $set.add(tcp_socket);
|
||||
};
|
||||
}
|
||||
|
||||
#[rtfm::app(device = stm32h7::stm32h743, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)]
|
||||
const APP: () = {
|
||||
struct Resources {
|
||||
spi: (pac::SPI1, pac::SPI2, pac::SPI4, pac::SPI5),
|
||||
i2c: pac::I2C2,
|
||||
ethernet_periph:
|
||||
(pac::ETHERNET_MAC, pac::ETHERNET_DMA, pac::ETHERNET_MTL),
|
||||
#[init([[0.; 5]; 2])]
|
||||
iir_state: [IIRState; 2],
|
||||
#[init([IIR { ba: [1., 0., 0., 0., 0.], y_offset: 0., y_min: -SCALE - 1., y_max: SCALE }; 2])]
|
||||
iir_ch: [IIR; 2],
|
||||
#[link_section = ".sram3.eth"]
|
||||
#[init(eth::Device::new())]
|
||||
ethernet: eth::Device,
|
||||
}
|
||||
|
||||
#[init(schedule = [tick])]
|
||||
fn init(c: init::Context) -> init::LateResources {
|
||||
board::init();
|
||||
|
||||
init_log();
|
||||
// info!("Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap());
|
||||
// info!("Built on {}", build_info::BUILT_TIME_UTC);
|
||||
// info!("{} {}", build_info::RUSTC_VERSION, build_info::TARGET);
|
||||
|
||||
// c.schedule.tick(Instant::now()).unwrap();
|
||||
|
||||
let dp = c.device;
|
||||
init::LateResources {
|
||||
spi: (dp.SPI1, dp.SPI2, dp.SPI4, dp.SPI5),
|
||||
i2c: dp.I2C2,
|
||||
ethernet_periph: (
|
||||
dp.ETHERNET_MAC,
|
||||
dp.ETHERNET_DMA,
|
||||
dp.ETHERNET_MTL,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[idle(resources = [ethernet, ethernet_periph, iir_state, iir_ch, i2c])]
|
||||
fn idle(c: idle::Context) -> ! {
|
||||
let (MAC, DMA, MTL) = c.resources.ethernet_periph;
|
||||
|
||||
let hardware_addr = match eeprom::read_eui48(c.resources.i2c) {
|
||||
Err(_) => {
|
||||
info!("Could not read EEPROM, using default MAC address");
|
||||
net::wire::EthernetAddress([0x10, 0xE2, 0xD5, 0x00, 0x03, 0x00])
|
||||
}
|
||||
Ok(raw_mac) => net::wire::EthernetAddress(raw_mac),
|
||||
};
|
||||
info!("MAC: {}", hardware_addr);
|
||||
|
||||
unsafe { c.resources.ethernet.init(hardware_addr, MAC, DMA, MTL) };
|
||||
let mut neighbor_cache_storage = [None; 8];
|
||||
let neighbor_cache =
|
||||
net::iface::NeighborCache::new(&mut neighbor_cache_storage[..]);
|
||||
let local_addr = net::wire::IpAddress::v4(10, 0, 16, 99);
|
||||
let mut ip_addrs = [net::wire::IpCidr::new(local_addr, 24)];
|
||||
let mut iface =
|
||||
net::iface::EthernetInterfaceBuilder::new(c.resources.ethernet)
|
||||
.ethernet_addr(hardware_addr)
|
||||
.neighbor_cache(neighbor_cache)
|
||||
.ip_addrs(&mut ip_addrs[..])
|
||||
.finalize();
|
||||
let mut socket_set_entries: [_; 8] = Default::default();
|
||||
let mut sockets =
|
||||
net::socket::SocketSet::new(&mut socket_set_entries[..]);
|
||||
create_socket!(sockets, tcp_rx_storage0, tcp_tx_storage0, tcp_handle0);
|
||||
create_socket!(sockets, tcp_rx_storage0, tcp_tx_storage0, tcp_handle1);
|
||||
|
||||
// unsafe { eth::enable_interrupt(DMA); }
|
||||
let mut time = 0u32;
|
||||
let mut next_ms = Instant::now();
|
||||
next_ms += 400_000.cycles();
|
||||
let mut server = Server::new();
|
||||
let mut iir_state: resources::iir_state = c.resources.iir_state;
|
||||
let mut iir_ch: resources::iir_ch = c.resources.iir_ch;
|
||||
loop {
|
||||
// if ETHERNET_PENDING.swap(false, Ordering::Relaxed) { }
|
||||
let tick = Instant::now() > next_ms;
|
||||
if tick {
|
||||
next_ms += 400_000.cycles();
|
||||
time += 1;
|
||||
}
|
||||
{
|
||||
let socket =
|
||||
&mut *sockets.get::<net::socket::TcpSocket>(tcp_handle0);
|
||||
if socket.state() == net::socket::TcpState::CloseWait {
|
||||
socket.close();
|
||||
} else if !(socket.is_open() || socket.is_listening()) {
|
||||
socket
|
||||
.listen(1234)
|
||||
.unwrap_or_else(|e| warn!("TCP listen error: {:?}", e));
|
||||
} else if tick && socket.can_send() {
|
||||
let s = iir_state.lock(|iir_state| Status {
|
||||
t: time,
|
||||
x0: iir_state[0][0],
|
||||
y0: iir_state[0][2],
|
||||
x1: iir_state[1][0],
|
||||
y1: iir_state[1][2],
|
||||
});
|
||||
json_reply(socket, &s);
|
||||
}
|
||||
}
|
||||
{
|
||||
let socket =
|
||||
&mut *sockets.get::<net::socket::TcpSocket>(tcp_handle1);
|
||||
if socket.state() == net::socket::TcpState::CloseWait {
|
||||
socket.close();
|
||||
} else if !(socket.is_open() || socket.is_listening()) {
|
||||
socket
|
||||
.listen(1235)
|
||||
.unwrap_or_else(|e| warn!("TCP listen error: {:?}", e));
|
||||
} else {
|
||||
server.poll(socket, |req: &Request| {
|
||||
if req.channel < 2 {
|
||||
iir_ch.lock(|iir_ch| {
|
||||
iir_ch[req.channel as usize] = req.iir
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if !match iface.poll(
|
||||
&mut sockets,
|
||||
net::time::Instant::from_millis(time as i64),
|
||||
) {
|
||||
Ok(changed) => changed,
|
||||
Err(net::Error::Unrecognized) => true,
|
||||
Err(e) => {
|
||||
info!("iface poll error: {:?}", e);
|
||||
true
|
||||
}
|
||||
} {
|
||||
// cortex_m::asm::wfi();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[task(priority = 1, schedule = [tick])]
|
||||
fn tick(c: tick::Context) {
|
||||
static mut TIME: u32 = 0;
|
||||
*TIME += 1;
|
||||
const PERIOD: u32 = 200_000_000;
|
||||
c.schedule.tick(c.scheduled + PERIOD.cycles()).unwrap();
|
||||
}
|
||||
|
||||
// seems to slow it down
|
||||
// #[link_section = ".data.spi1"]
|
||||
#[task(binds = SPI1, resources = [spi, iir_state, iir_ch], priority = 2)]
|
||||
fn spi1(c: spi1::Context) {
|
||||
#[cfg(feature = "bkpt")]
|
||||
cortex_m::asm::bkpt();
|
||||
let (spi1, spi2, spi4, spi5) = c.resources.spi;
|
||||
let iir_ch = c.resources.iir_ch;
|
||||
let iir_state = c.resources.iir_state;
|
||||
|
||||
let sr = spi1.sr.read();
|
||||
if sr.eot().bit_is_set() {
|
||||
spi1.ifcr.write(|w| w.eotc().set_bit());
|
||||
}
|
||||
if sr.rxp().bit_is_set() {
|
||||
let rxdr = &spi1.rxdr as *const _ as *const u16;
|
||||
let a = unsafe { ptr::read_volatile(rxdr) };
|
||||
let x0 = f32::from(a as i16);
|
||||
let y0 = iir_ch[0].update(&mut iir_state[0], x0);
|
||||
let d = y0 as i16 as u16 ^ 0x8000;
|
||||
let txdr = &spi2.txdr as *const _ as *mut u16;
|
||||
unsafe { ptr::write_volatile(txdr, d) };
|
||||
}
|
||||
|
||||
let sr = spi5.sr.read();
|
||||
if sr.eot().bit_is_set() {
|
||||
spi5.ifcr.write(|w| w.eotc().set_bit());
|
||||
}
|
||||
if sr.rxp().bit_is_set() {
|
||||
let rxdr = &spi5.rxdr as *const _ as *const u16;
|
||||
let a = unsafe { ptr::read_volatile(rxdr) };
|
||||
let x0 = f32::from(a as i16);
|
||||
let y0 = iir_ch[1].update(&mut iir_state[1], x0);
|
||||
let d = y0 as i16 as u16 ^ 0x8000;
|
||||
let txdr = &spi4.txdr as *const _ as *mut u16;
|
||||
unsafe { ptr::write_volatile(txdr, d) };
|
||||
}
|
||||
#[cfg(feature = "bkpt")]
|
||||
cortex_m::asm::bkpt();
|
||||
}
|
||||
|
||||
/*
|
||||
#[task(binds = ETH, resources = [ethernet_periph], priority = 1)]
|
||||
fn eth(c: eth::Context) {
|
||||
let dma = &c.resources.ethernet_periph.1;
|
||||
ETHERNET_PENDING.store(true, Ordering::Relaxed);
|
||||
unsafe { eth::interrupt_handler(dma) }
|
||||
}
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
// hw interrupt handlers for RTFM to use for scheduling tasks
|
||||
// one per priority
|
||||
fn DCMI();
|
||||
fn JPEG();
|
||||
fn SDMMC();
|
||||
}
|
||||
};
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
struct Request {
|
||||
channel: u8,
|
||||
iir: IIR,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Response<'a> {
|
||||
code: i32,
|
||||
message: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Status {
|
||||
t: u32,
|
||||
x0: f32,
|
||||
y0: f32,
|
||||
x1: f32,
|
||||
y1: f32,
|
||||
}
|
||||
|
||||
fn json_reply<T: Serialize>(socket: &mut net::socket::TcpSocket, msg: &T) {
|
||||
let mut u: String<U128> = to_string(msg).unwrap();
|
||||
u.push('\n').unwrap();
|
||||
socket.write_str(&u).unwrap();
|
||||
}
|
||||
|
||||
struct Server {
|
||||
data: Vec<u8, U256>,
|
||||
discard: bool,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
data: Vec::new(),
|
||||
discard: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn poll<T, F, R>(
|
||||
&mut self,
|
||||
socket: &mut net::socket::TcpSocket,
|
||||
f: F,
|
||||
) -> Option<R>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
F: FnOnce(&T) -> R,
|
||||
{
|
||||
while socket.can_recv() {
|
||||
let found = socket
|
||||
.recv(|buf| {
|
||||
let (len, found) =
|
||||
match buf.iter().position(|&c| c as char == '\n') {
|
||||
Some(end) => (end + 1, true),
|
||||
None => (buf.len(), false),
|
||||
};
|
||||
if self.data.len() + len >= self.data.capacity() {
|
||||
self.discard = true;
|
||||
self.data.clear();
|
||||
} else if !self.discard && len > 0 {
|
||||
self.data.extend_from_slice(&buf[..len]).unwrap();
|
||||
}
|
||||
(len, found)
|
||||
})
|
||||
.unwrap();
|
||||
if found {
|
||||
if self.discard {
|
||||
self.discard = false;
|
||||
json_reply(
|
||||
socket,
|
||||
&Response {
|
||||
code: 520,
|
||||
message: "command buffer overflow",
|
||||
},
|
||||
);
|
||||
self.data.clear();
|
||||
} else {
|
||||
let r = from_slice::<T>(&self.data[..self.data.len() - 1]);
|
||||
self.data.clear();
|
||||
match r {
|
||||
Ok(res) => {
|
||||
let r = f(&res);
|
||||
json_reply(
|
||||
socket,
|
||||
&Response {
|
||||
code: 200,
|
||||
message: "ok",
|
||||
},
|
||||
);
|
||||
return Some(r);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("parse error {:?}", err);
|
||||
json_reply(
|
||||
socket,
|
||||
&Response {
|
||||
code: 550,
|
||||
message: "parse error",
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[exception]
|
||||
fn HardFault(ef: &cortex_m_rt::ExceptionFrame) -> ! {
|
||||
panic!("HardFault at {:#?}", ef);
|
||||
}
|
||||
|
||||
#[exception]
|
||||
fn DefaultHandler(irqn: i16) {
|
||||
panic!("Unhandled exception (IRQn = {})", irqn);
|
||||
}
|
|
@ -0,0 +1,583 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "ad9959"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/quartiq/ad9959.git?branch=feature/basic-driver#b45bce3f0dd7a58b5a272b778001dc9422c2c160"
|
||||
dependencies = [
|
||||
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aligned"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "aligned"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "as-slice"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asm-delay"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitrate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bare-metal"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bitrate"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aligned 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-log"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cortex-m-rt-macros 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt-macros"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rtfm"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-rtfm-macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rtfm-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rtfm-macros"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rtfm-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-semihosting"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-iterator"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"enum-iterator-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-iterator-derive"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hash32 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "managed"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "mcp23017"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/mrd0ll4r/mcp23017.git#a3d072754abca60a92ece820f7cfb767a0c11669"
|
||||
dependencies = [
|
||||
"embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "panic-halt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "panic-semihosting"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"paste-impl 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste-impl"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r0"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rtfm-core"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rtfm-syntax"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"indexmap 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-json-core"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smoltcp"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stabilizer"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"ad9959 0.1.0 (git+https://github.com/quartiq/ad9959.git?branch=feature/basic-driver)",
|
||||
"asm-delay 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-log 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-rtfm 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"enum-iterator 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mcp23017 0.1.1 (git+https://github.com/mrd0ll4r/mcp23017.git)",
|
||||
"nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"stm32h7-ethernet 0.1.1 (git+https://github.com/quartiq/stm32h7-ethernet.git)",
|
||||
"stm32h7xx-hal 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "stm32h7"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stm32h7-ethernet"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/quartiq/stm32h7-ethernet.git#cf9b8bb2e1b440d8ada6ac6048f48dc4ed9c269a"
|
||||
dependencies = [
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"stm32h7xx-hal 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stm32h7xx-hal"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"paste 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"stm32h7 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stm32h7xx-hal"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"paste 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"stm32h7 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "vcell"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "volatile-register"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum ad9959 0.1.0 (git+https://github.com/quartiq/ad9959.git?branch=feature/basic-driver)" = "<none>"
|
||||
"checksum aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d39da9b88ae1a81c03c9c082b8db83f1d0e93914126041962af61034ab44c4a5"
|
||||
"checksum aligned 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eb1ce8b3382016136ab1d31a1b5ce807144f8b7eb2d5f16b2108f0f07edceb94"
|
||||
"checksum as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37dfb65bc03b2bc85ee827004f14a6817e04160e3b1a28931986a666a9290e70"
|
||||
"checksum asm-delay 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0e0c8eec73de29ae94b2aff405a272304bc286204ddb1cdf20d7e2249078ae20"
|
||||
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
"checksum bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
|
||||
"checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0"
|
||||
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
"checksum bitrate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c147d86912d04bef727828fda769a76ca81629a46d8ba311a8d58a26aa91473d"
|
||||
"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
"checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0"
|
||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
"checksum cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0b159a1e8306949579de3698c841dba58058197b65c60807194e4fa1e7a554"
|
||||
"checksum cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763"
|
||||
"checksum cortex-m-log 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "978caafe65d1023d38b00c76b83564788fc351d954a5005fb72cf992c0d61458"
|
||||
"checksum cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "00d518da72bba39496024b62607c1d8e37bcece44b2536664f1132a73a499a28"
|
||||
"checksum cortex-m-rt-macros 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4717562afbba06e760d34451919f5c3bf3ac15c7bb897e8b04862a7428378647"
|
||||
"checksum cortex-m-rtfm 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eaf0b9fd3f042cb3793d15daf3cea201b2f25c99b0b5b936a551bb6909c3ae5b"
|
||||
"checksum cortex-m-rtfm-macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c62092f6ff344e9b0adb748f0302ed69889ba2fae1fce446e3788d4726ea73bb"
|
||||
"checksum cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "113ef0ecffee2b62b58f9380f4469099b30e9f9cbee2804771b4203ba1762cfa"
|
||||
"checksum embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b"
|
||||
"checksum enum-iterator 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c79a6321a1197d7730510c7e3f6cb80432dfefecb32426de8cea0aa19b4bb8d7"
|
||||
"checksum enum-iterator-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e94aa31f7c0dc764f57896dc615ddd76fc13b0d5dca7eb6cc5e018a5a09ec06"
|
||||
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
|
||||
"checksum generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd"
|
||||
"checksum hash32 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc"
|
||||
"checksum heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "73a8a2391a3bc70b31f60e7a90daa5755a360559c0b6b9c5cfc0fee482362dc0"
|
||||
"checksum indexmap 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe"
|
||||
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
"checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6"
|
||||
"checksum mcp23017 0.1.1 (git+https://github.com/mrd0ll4r/mcp23017.git)" = "<none>"
|
||||
"checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc"
|
||||
"checksum panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812"
|
||||
"checksum panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c03864ac862876c16a308f5286f4aa217f1a69ac45df87ad3cd2847f818a642c"
|
||||
"checksum paste 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "d508492eeb1e5c38ee696371bf7b9fc33c83d46a7d451606b96458fbbbdc2dec"
|
||||
"checksum paste-impl 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "84f328a6a63192b333fce5fbb4be79db6758a4d518dfac6d54412f1492f72d32"
|
||||
"checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
|
||||
"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
|
||||
"checksum quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea"
|
||||
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
|
||||
"checksum rtfm-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec893edb2aa5b70320b94896ffea22a7ebb1cf3f942bb67cd5b60a865a63493"
|
||||
"checksum rtfm-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4455e23c34df3d66454e7e218a4d76a7f83321d04a806be614463341cec4116e"
|
||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
|
||||
"checksum serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf406405ada9ef326ca78677324ac66994ff348fc48a16030be08caeed29825"
|
||||
"checksum serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250"
|
||||
"checksum smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a"
|
||||
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
|
||||
"checksum stm32h7 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9beb5e2a223c82f263c3051bba4614aebc6e98bd40217df3cd8817c83ac7bd8"
|
||||
"checksum stm32h7-ethernet 0.1.1 (git+https://github.com/quartiq/stm32h7-ethernet.git)" = "<none>"
|
||||
"checksum stm32h7xx-hal 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "987c66628f30012ed9a41cc738421c5caece03292c0cc8fd1e99956f122735bd"
|
||||
"checksum syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2"
|
||||
"checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
|
||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"
|
|
@ -0,0 +1,67 @@
|
|||
[package]
|
||||
name = "stabilizer"
|
||||
version = "0.3.0"
|
||||
authors = ["Robert Jördens <rj@quartiq.de>"]
|
||||
description = "Firmware for the Sinara Stabilizer board (stm32h743, eth, poe, 2 adc, 2 dac)"
|
||||
categories = ["embedded", "no-std", "hardware-support", "science"]
|
||||
license = "GPL-3.0-or-later"
|
||||
keywords = ["ethernet", "stm32h7", "adc", "dac", "physics"]
|
||||
repository = "https://github.com/quartiq/stabilizer"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/stabilizer/"
|
||||
edition = "2018"
|
||||
exclude = [
|
||||
".travis.yml",
|
||||
".gitignore",
|
||||
"doc/",
|
||||
"doc/*"
|
||||
]
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "quartiq/stabilizer", branch = "master" }
|
||||
maintenance = { status = "experimental" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = []
|
||||
default-target = "thumbv7em-none-eabihf"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = { version = "0.6", features = ["const-fn"] }
|
||||
cortex-m-rt = { version = "0.6", features = ["device"] }
|
||||
cortex-m-log = { version = "0.6", features = ["log-integration"] }
|
||||
log = "0.4"
|
||||
panic-semihosting = { version = "0.5", optional = true }
|
||||
panic-halt = "0.2"
|
||||
serde = { version = "1.0", features = ["derive"], default-features = false }
|
||||
heapless = { version = "0.5", features = ["serde"] }
|
||||
serde-json-core = "0.1"
|
||||
cortex-m-rtfm = "0.5"
|
||||
embedded-hal = "0.2.3"
|
||||
nb = "0.1.2"
|
||||
asm-delay = "0.7.0"
|
||||
enum-iterator = "0.6.0"
|
||||
|
||||
[dependencies.mcp23017]
|
||||
git = "https://github.com/mrd0ll4r/mcp23017.git"
|
||||
|
||||
[dependencies.smoltcp]
|
||||
version = "0.6"
|
||||
features = ["ethernet", "proto-ipv4", "socket-tcp", "proto-ipv6"]
|
||||
default-features = false
|
||||
|
||||
[dependencies.ad9959]
|
||||
path = "../ad9959"
|
||||
|
||||
[dependencies.stm32h7-ethernet]
|
||||
git = "https://github.com/quartiq/stm32h7-ethernet.git"
|
||||
features = ["stm32h743v"]
|
||||
|
||||
[dependencies.stm32h7xx-hal]
|
||||
path = "../stm32h7xx-hal/"
|
||||
features = ["stm32h743v", "rt", "unproven"]
|
||||
|
||||
[features]
|
||||
semihosting = ["panic-semihosting", "cortex-m-log/semihosting"]
|
||||
bkpt = [ ]
|
||||
nightly = ["cortex-m/inline-asm"]
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
use embedded_hal;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, IntoEnumIterator)]
|
||||
pub enum Gain {
|
||||
G1 = 0b00,
|
||||
G2 = 0b01,
|
||||
G5 = 0b10,
|
||||
G10 = 0b11
|
||||
}
|
||||
|
||||
/// A programmable gain amplifier that allows for setting the gain via GPIO.
|
||||
pub struct ProgrammableGainAmplifier<A0, A1> {
|
||||
a0: A0,
|
||||
a1: A1
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for Gain {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
for gain in Gain::into_enum_iter() {
|
||||
if value == gain as u8 {
|
||||
return Ok(gain)
|
||||
}
|
||||
}
|
||||
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A0, A1> ProgrammableGainAmplifier<A0, A1>
|
||||
where
|
||||
A0: embedded_hal::digital::v2::StatefulOutputPin,
|
||||
A0::Error: core::fmt::Debug,
|
||||
A1: embedded_hal::digital::v2::StatefulOutputPin,
|
||||
A1::Error: core::fmt::Debug,
|
||||
{
|
||||
/// Construct a new programmable gain driver.
|
||||
///
|
||||
/// Args:
|
||||
/// * `a0` - An output connected to the A0 input of the amplifier.
|
||||
/// * `a1` - An output connected to the A1 input of the amplifier.
|
||||
pub fn new(a0: A0, a1: A1) -> Self
|
||||
{
|
||||
let mut afe = Self { a0: a0, a1: a1};
|
||||
|
||||
afe.set_gain(Gain::G1);
|
||||
|
||||
afe
|
||||
}
|
||||
|
||||
/// Set the gain of the front-end.
|
||||
pub fn set_gain(&mut self, gain: Gain) {
|
||||
if (gain as u8 & 0b01) != 0 {
|
||||
self.a0.set_high().unwrap();
|
||||
} else {
|
||||
self.a0.set_low().unwrap();
|
||||
}
|
||||
|
||||
if (gain as u8 & 0b10) != 0 {
|
||||
self.a1.set_high().unwrap()
|
||||
} else {
|
||||
self.a1.set_low().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the programmed gain of the analog front-end.
|
||||
pub fn get_gain(&self) -> Result<Gain, ()> {
|
||||
let mut code: u8 = 0;
|
||||
if self.a0.is_set_high().unwrap() {
|
||||
code |= 0b1;
|
||||
}
|
||||
if self.a1.is_set_high().unwrap() {
|
||||
code |= 0b10;
|
||||
}
|
||||
|
||||
Gain::try_from(code)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
use embedded_hal::blocking::i2c::WriteRead;
|
||||
|
||||
const I2C_ADDR: u8 = 0x50;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn read_eui48<T>(i2c: &mut T) -> Result<[u8; 6], T::Error>
|
||||
where T: WriteRead
|
||||
{
|
||||
let mut buffer = [0u8; 6];
|
||||
i2c.write_read(I2C_ADDR, &[0xFA_u8], &mut buffer)?;
|
||||
Ok(buffer)
|
||||
}
|
|
@ -0,0 +1,738 @@
|
|||
#![deny(warnings)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![cfg_attr(feature = "nightly", feature(asm))]
|
||||
// Enable returning `!`
|
||||
#![cfg_attr(feature = "nightly", feature(never_type))]
|
||||
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
|
||||
|
||||
#[inline(never)]
|
||||
#[panic_handler]
|
||||
#[cfg(all(feature = "nightly", not(feature = "semihosting")))]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
let gpiod = unsafe { &*hal::stm32::GPIOD::ptr() };
|
||||
gpiod.odr.modify(|_, w| w.odr6().high().odr12().high()); // FP_LED_1, FP_LED_3
|
||||
unsafe {
|
||||
core::intrinsics::abort();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "semihosting")]
|
||||
extern crate panic_semihosting;
|
||||
|
||||
#[cfg(not(any(feature = "nightly", feature = "semihosting")))]
|
||||
extern crate panic_halt;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
// use core::sync::atomic::{AtomicU32, AtomicBool, Ordering};
|
||||
use asm_delay;
|
||||
use rtfm::cyccnt::{Instant, U32Ext};
|
||||
use cortex_m_rt::exception;
|
||||
use cortex_m;
|
||||
use stm32h7xx_hal as hal;
|
||||
use stm32h7xx_hal::{
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use embedded_hal::{
|
||||
digital::v2::OutputPin,
|
||||
};
|
||||
|
||||
use stm32h7_ethernet as ethernet;
|
||||
use smoltcp as net;
|
||||
|
||||
use heapless::{
|
||||
String,
|
||||
consts::*,
|
||||
};
|
||||
|
||||
#[link_section = ".sram3.eth"]
|
||||
static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new();
|
||||
|
||||
mod afe;
|
||||
mod eeprom;
|
||||
mod iir;
|
||||
mod pounder;
|
||||
mod server;
|
||||
|
||||
#[cfg(not(feature = "semihosting"))]
|
||||
fn init_log() {}
|
||||
|
||||
#[cfg(feature = "semihosting")]
|
||||
fn init_log() {
|
||||
use cortex_m_log::log::{init as init_log, Logger};
|
||||
use cortex_m_log::printer::semihosting::{hio::HStdout, InterruptOk};
|
||||
use log::LevelFilter;
|
||||
static mut LOGGER: Option<Logger<InterruptOk<HStdout>>> = None;
|
||||
let logger = Logger {
|
||||
inner: InterruptOk::<_>::stdout().unwrap(),
|
||||
level: LevelFilter::Info,
|
||||
};
|
||||
let logger = unsafe { LOGGER.get_or_insert(logger) };
|
||||
|
||||
init_log(logger).unwrap();
|
||||
}
|
||||
|
||||
// Pull in build information (from `built` crate)
|
||||
mod build_info {
|
||||
#![allow(dead_code)]
|
||||
// include!(concat!(env!("OUT_DIR"), "/built.rs"));
|
||||
}
|
||||
|
||||
pub struct NetStorage {
|
||||
ip_addrs: [net::wire::IpCidr; 1],
|
||||
neighbor_cache: [Option<(net::wire::IpAddress, net::iface::Neighbor)>; 8],
|
||||
}
|
||||
|
||||
static mut NET_STORE: NetStorage = NetStorage {
|
||||
// Placeholder for the real IP address, which is initialized at runtime.
|
||||
ip_addrs: [net::wire::IpCidr::Ipv6(net::wire::Ipv6Cidr::SOLICITED_NODE_PREFIX)],
|
||||
|
||||
neighbor_cache: [None; 8],
|
||||
};
|
||||
|
||||
const SCALE: f32 = ((1 << 15) - 1) as f32;
|
||||
|
||||
const SPI_START_CODE: u32 = 0x201;
|
||||
|
||||
// static ETHERNET_PENDING: AtomicBool = AtomicBool::new(true);
|
||||
|
||||
const TCP_RX_BUFFER_SIZE: usize = 8192;
|
||||
const TCP_TX_BUFFER_SIZE: usize = 8192;
|
||||
|
||||
type AFE1 = afe::ProgrammableGainAmplifier<
|
||||
hal::gpio::gpiof::PF2<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||
hal::gpio::gpiof::PF5<hal::gpio::Output<hal::gpio::PushPull>>>;
|
||||
|
||||
type AFE2 = afe::ProgrammableGainAmplifier<
|
||||
hal::gpio::gpiod::PD14<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||
hal::gpio::gpiod::PD15<hal::gpio::Output<hal::gpio::PushPull>>>;
|
||||
|
||||
macro_rules! route_request {
|
||||
($request:ident,
|
||||
readable_attributes: [$($read_attribute:tt: $getter:tt),*],
|
||||
modifiable_attributes: [$($write_attribute:tt: $TYPE:ty, $setter:tt),*]) => {
|
||||
match $request.req {
|
||||
server::AccessRequest::Read => {
|
||||
match $request.attribute {
|
||||
$(
|
||||
$read_attribute => {
|
||||
let value = match $getter() {
|
||||
Ok(data) => data,
|
||||
Err(_) => return server::Response::error($request.attribute,
|
||||
"Failed to read attribute"),
|
||||
};
|
||||
|
||||
let encoded_data: String<U256> = match serde_json_core::to_string(&value) {
|
||||
Ok(data) => data,
|
||||
Err(_) => return server::Response::error($request.attribute,
|
||||
"Failed to encode attribute value"),
|
||||
};
|
||||
|
||||
server::Response::success($request.attribute, &encoded_data)
|
||||
},
|
||||
)*
|
||||
_ => server::Response::error($request.attribute, "Unknown attribute")
|
||||
}
|
||||
},
|
||||
server::AccessRequest::Write => {
|
||||
match $request.attribute {
|
||||
$(
|
||||
$write_attribute => {
|
||||
let new_value = match serde_json_core::from_str::<$TYPE>(&$request.value) {
|
||||
Ok(data) => data,
|
||||
Err(_) => return server::Response::error($request.attribute,
|
||||
"Failed to decode value"),
|
||||
};
|
||||
|
||||
match $setter(new_value) {
|
||||
Ok(_) => server::Response::success($request.attribute, &$request.value),
|
||||
Err(_) => server::Response::error($request.attribute,
|
||||
"Failed to set attribute"),
|
||||
}
|
||||
}
|
||||
)*
|
||||
_ => server::Response::error($request.attribute, "Unknown attribute")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rtfm::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)]
|
||||
const APP: () = {
|
||||
struct Resources {
|
||||
adc1: hal::spi::Spi<hal::stm32::SPI2>,
|
||||
dac1: hal::spi::Spi<hal::stm32::SPI4>,
|
||||
afe1: AFE1,
|
||||
|
||||
adc2: hal::spi::Spi<hal::stm32::SPI3>,
|
||||
dac2: hal::spi::Spi<hal::stm32::SPI5>,
|
||||
afe2: AFE2,
|
||||
|
||||
eeprom_i2c: hal::i2c::I2c<hal::stm32::I2C2>,
|
||||
|
||||
timer: hal::timer::Timer<hal::stm32::TIM2>,
|
||||
net_interface: net::iface::EthernetInterface<'static, 'static, 'static,
|
||||
ethernet::EthernetDMA<'static>>,
|
||||
eth_mac: ethernet::EthernetMAC,
|
||||
mac_addr: net::wire::EthernetAddress,
|
||||
|
||||
pounder: pounder::PounderDevices<asm_delay::AsmDelay>,
|
||||
|
||||
#[init([[0.; 5]; 2])]
|
||||
iir_state: [iir::IIRState; 2],
|
||||
#[init([iir::IIR { ba: [1., 0., 0., 0., 0.], y_offset: 0., y_min: -SCALE - 1., y_max: SCALE }; 2])]
|
||||
iir_ch: [iir::IIR; 2],
|
||||
}
|
||||
|
||||
#[init]
|
||||
fn init(c: init::Context) -> init::LateResources {
|
||||
let dp = c.device;
|
||||
let mut cp = cortex_m::Peripherals::take().unwrap();
|
||||
|
||||
let pwr = dp.PWR.constrain();
|
||||
let vos = pwr.freeze();
|
||||
|
||||
let rcc = dp.RCC.constrain();
|
||||
let mut clocks = rcc
|
||||
//TODO: Re-enable HSE for Stabilizer platform.
|
||||
// .use_hse(8.mhz())
|
||||
.sysclk(400.mhz())
|
||||
.hclk(200.mhz())
|
||||
.per_ck(100.mhz())
|
||||
.pll2_p_ck(100.mhz())
|
||||
.pll2_q_ck(100.mhz())
|
||||
.freeze(vos, &dp.SYSCFG);
|
||||
|
||||
init_log();
|
||||
|
||||
// Enable SRAM3 for the ethernet descriptor ring.
|
||||
clocks.rb.ahb2enr.modify(|_, w| w.sram3en().set_bit());
|
||||
|
||||
clocks.rb.rsr.write(|w| w.rmvf().set_bit());
|
||||
|
||||
clocks.rb.d2ccip1r.modify(|_, w| w.spi123sel().pll2_p().spi45sel().pll2_q());
|
||||
|
||||
let mut delay = hal::delay::Delay::new(cp.SYST, clocks.clocks);
|
||||
|
||||
let gpioa = dp.GPIOA.split(&mut clocks);
|
||||
let gpiob = dp.GPIOB.split(&mut clocks);
|
||||
let gpioc = dp.GPIOC.split(&mut clocks);
|
||||
let gpiod = dp.GPIOD.split(&mut clocks);
|
||||
let gpioe = dp.GPIOE.split(&mut clocks);
|
||||
let gpiof = dp.GPIOF.split(&mut clocks);
|
||||
let gpiog = dp.GPIOG.split(&mut clocks);
|
||||
|
||||
let afe1 = {
|
||||
let a0_pin = gpiof.pf2.into_push_pull_output();
|
||||
let a1_pin = gpiof.pf5.into_push_pull_output();
|
||||
afe::ProgrammableGainAmplifier::new(a0_pin, a1_pin)
|
||||
};
|
||||
|
||||
let afe2 = {
|
||||
let a0_pin = gpiod.pd14.into_push_pull_output();
|
||||
let a1_pin = gpiod.pd15.into_push_pull_output();
|
||||
afe::ProgrammableGainAmplifier::new(a0_pin, a1_pin)
|
||||
};
|
||||
|
||||
// Configure the SPI interfaces to the ADCs and DACs.
|
||||
let adc1_spi = {
|
||||
let spi_miso = gpiob.pb14.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let spi_sck = gpiob.pb10.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _spi_nss = gpiob.pb9.into_alternate_af5();
|
||||
|
||||
let config = hal::spi::Config::new(hal::spi::Mode{
|
||||
polarity: hal::spi::Polarity::IdleHigh,
|
||||
phase: hal::spi::Phase::CaptureOnSecondTransition,
|
||||
})
|
||||
.communication_mode(hal::spi::CommunicationMode::Receiver)
|
||||
.manage_cs()
|
||||
.transfer_size(1)
|
||||
.frame_size(16)
|
||||
.cs_delay(220e-9);
|
||||
|
||||
let mut spi = dp.SPI2.spi(
|
||||
(spi_sck, spi_miso, hal::spi::NoMosi),
|
||||
config,
|
||||
50.mhz(),
|
||||
&clocks);
|
||||
|
||||
spi.listen(hal::spi::Event::Eot);
|
||||
|
||||
spi
|
||||
};
|
||||
|
||||
let adc2_spi = {
|
||||
let spi_miso = gpiob.pb4.into_alternate_af6().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let spi_sck = gpioc.pc10.into_alternate_af6().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _spi_nss = gpioa.pa15.into_alternate_af6();
|
||||
|
||||
|
||||
let config = hal::spi::Config::new(hal::spi::Mode{
|
||||
polarity: hal::spi::Polarity::IdleHigh,
|
||||
phase: hal::spi::Phase::CaptureOnSecondTransition,
|
||||
})
|
||||
.communication_mode(hal::spi::CommunicationMode::Receiver)
|
||||
.manage_cs()
|
||||
.transfer_size(1)
|
||||
.frame_size(16)
|
||||
.cs_delay(220e-9);
|
||||
|
||||
let mut spi = dp.SPI3.spi(
|
||||
(spi_sck, spi_miso, hal::spi::NoMosi),
|
||||
config,
|
||||
50.mhz(),
|
||||
&clocks);
|
||||
|
||||
spi.listen(hal::spi::Event::Eot);
|
||||
|
||||
spi
|
||||
};
|
||||
|
||||
let dac1_spi = {
|
||||
let spi_miso = gpioe.pe5.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let spi_sck = gpioe.pe2.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _spi_nss = gpioe.pe4.into_alternate_af5();
|
||||
|
||||
let config = hal::spi::Config::new(hal::spi::Mode{
|
||||
polarity: hal::spi::Polarity::IdleHigh,
|
||||
phase: hal::spi::Phase::CaptureOnSecondTransition,
|
||||
})
|
||||
.communication_mode(hal::spi::CommunicationMode::Transmitter)
|
||||
.manage_cs()
|
||||
.transfer_size(1)
|
||||
.frame_size(16)
|
||||
.swap_mosi_miso();
|
||||
|
||||
let spi = dp.SPI4.spi((spi_sck, spi_miso, hal::spi::NoMosi), config, 50.mhz(), &clocks);
|
||||
spi
|
||||
};
|
||||
|
||||
let dac2_spi = {
|
||||
let spi_miso = gpiof.pf8.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let spi_sck = gpiof.pf7.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _spi_nss = gpiof.pf6.into_alternate_af5();
|
||||
|
||||
let config = hal::spi::Config::new(hal::spi::Mode{
|
||||
polarity: hal::spi::Polarity::IdleHigh,
|
||||
phase: hal::spi::Phase::CaptureOnSecondTransition,
|
||||
})
|
||||
.communication_mode(hal::spi::CommunicationMode::Transmitter)
|
||||
.manage_cs()
|
||||
.transfer_size(1)
|
||||
.frame_size(16)
|
||||
.swap_mosi_miso();
|
||||
|
||||
let spi = dp.SPI5.spi((spi_sck, spi_miso, hal::spi::NoMosi), config, 50.mhz(), &clocks);
|
||||
|
||||
spi
|
||||
};
|
||||
|
||||
let mut fp_led_0 = gpiod.pd5.into_push_pull_output();
|
||||
let mut fp_led_1 = gpiod.pd6.into_push_pull_output();
|
||||
let mut fp_led_2 = gpiog.pg4.into_push_pull_output();
|
||||
let mut fp_led_3 = gpiod.pd12.into_push_pull_output();
|
||||
|
||||
fp_led_0.set_low().unwrap();
|
||||
fp_led_1.set_low().unwrap();
|
||||
fp_led_2.set_low().unwrap();
|
||||
fp_led_3.set_low().unwrap();
|
||||
|
||||
|
||||
let pounder_devices = {
|
||||
let ad9959 = {
|
||||
let qspi_interface = {
|
||||
// Instantiate the QUADSPI pins and peripheral interface.
|
||||
// TODO: Place these into a pins structure that is provided to the QSPI
|
||||
// constructor.
|
||||
let _qspi_clk = gpiob.pb2.into_alternate_af9().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _qspi_ncs = gpioc.pc11.into_alternate_af9().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _qspi_io0 = gpioe.pe7.into_alternate_af10().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _qspi_io1 = gpioe.pe8.into_alternate_af10().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _qspi_io2 = gpioe.pe9.into_alternate_af10().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _qspi_io3 = gpioe.pe10.into_alternate_af10().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
|
||||
let qspi = hal::qspi::Qspi::new(dp.QUADSPI, &mut clocks, 10.mhz()).unwrap();
|
||||
pounder::QspiInterface::new(qspi).unwrap()
|
||||
};
|
||||
|
||||
let mut reset_pin = gpioa.pa0.into_push_pull_output();
|
||||
let io_update = gpiog.pg7.into_push_pull_output();
|
||||
|
||||
|
||||
let asm_delay = {
|
||||
let frequency_hz = clocks.clocks.c_ck().0;
|
||||
asm_delay::AsmDelay::new(asm_delay::bitrate::Hertz (frequency_hz))
|
||||
};
|
||||
|
||||
ad9959::Ad9959::new(qspi_interface,
|
||||
&mut reset_pin,
|
||||
io_update,
|
||||
asm_delay,
|
||||
ad9959::Mode::FourBitSerial,
|
||||
100_000_000f32,
|
||||
5).unwrap()
|
||||
};
|
||||
|
||||
let io_expander = {
|
||||
let sda = gpiob.pb7.into_alternate_af4().set_open_drain();
|
||||
let scl = gpiob.pb8.into_alternate_af4().set_open_drain();
|
||||
let i2c1 = dp.I2C1.i2c((scl, sda), 100.khz(), &clocks);
|
||||
mcp23017::MCP23017::default(i2c1).unwrap()
|
||||
};
|
||||
|
||||
let spi = {
|
||||
let spi_mosi = gpiod.pd7.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let spi_miso = gpioa.pa6.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let spi_sck = gpiog.pg11.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
|
||||
let config = hal::spi::Config::new(hal::spi::Mode{
|
||||
polarity: hal::spi::Polarity::IdleHigh,
|
||||
phase: hal::spi::Phase::CaptureOnSecondTransition,
|
||||
})
|
||||
.frame_size(8);
|
||||
|
||||
// The maximum frequency of this SPI must be limited due to capacitance on the MISO
|
||||
// line causing a long RC decay.
|
||||
dp.SPI1.spi((spi_sck, spi_miso, spi_mosi), config, 5.mhz(), &clocks)
|
||||
};
|
||||
|
||||
let adc1 = {
|
||||
let mut adc = dp.ADC1.adc(&mut delay, &mut clocks);
|
||||
adc.calibrate();
|
||||
|
||||
adc.enable()
|
||||
};
|
||||
|
||||
let adc2 = {
|
||||
let mut adc = dp.ADC2.adc(&mut delay, &mut clocks);
|
||||
adc.calibrate();
|
||||
|
||||
adc.enable()
|
||||
};
|
||||
|
||||
let adc1_in_p = gpiof.pf11.into_analog();
|
||||
let adc2_in_p = gpiof.pf14.into_analog();
|
||||
|
||||
pounder::PounderDevices::new(io_expander,
|
||||
ad9959,
|
||||
spi,
|
||||
adc1,
|
||||
adc2,
|
||||
adc1_in_p,
|
||||
adc2_in_p).unwrap()
|
||||
};
|
||||
|
||||
let mut eeprom_i2c = {
|
||||
let sda = gpiof.pf0.into_alternate_af4().set_open_drain();
|
||||
let scl = gpiof.pf1.into_alternate_af4().set_open_drain();
|
||||
dp.I2C2.i2c((scl, sda), 100.khz(), &clocks)
|
||||
};
|
||||
|
||||
// Configure ethernet pins.
|
||||
{
|
||||
// Reset the PHY before configuring pins.
|
||||
let mut eth_phy_nrst = gpioe.pe3.into_push_pull_output();
|
||||
eth_phy_nrst.set_low().unwrap();
|
||||
delay.delay_us(200u8);
|
||||
eth_phy_nrst.set_high().unwrap();
|
||||
let _rmii_ref_clk = gpioa.pa1.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _rmii_mdio = gpioa.pa2.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _rmii_mdc = gpioc.pc1.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _rmii_crs_dv = gpioa.pa7.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _rmii_rxd0 = gpioc.pc4.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _rmii_rxd1 = gpioc.pc5.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _rmii_tx_en = gpiob.pb11.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _rmii_txd0 = gpiob.pb12.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
let _rmii_txd1 = gpiog.pg14.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh);
|
||||
}
|
||||
|
||||
let mac_addr = match eeprom::read_eui48(&mut eeprom_i2c) {
|
||||
Err(_) => {
|
||||
info!("Could not read EEPROM, using default MAC address");
|
||||
net::wire::EthernetAddress([0x10, 0xE2, 0xD5, 0x00, 0x03, 0x00])
|
||||
}
|
||||
Ok(raw_mac) => net::wire::EthernetAddress(raw_mac),
|
||||
};
|
||||
|
||||
let (network_interface, eth_mac) = {
|
||||
// Configure the ethernet controller
|
||||
let (eth_dma, eth_mac) = unsafe {
|
||||
ethernet::ethernet_init(
|
||||
dp.ETHERNET_MAC,
|
||||
dp.ETHERNET_MTL,
|
||||
dp.ETHERNET_DMA,
|
||||
&mut DES_RING,
|
||||
mac_addr.clone())
|
||||
};
|
||||
|
||||
unsafe { ethernet::enable_interrupt() };
|
||||
|
||||
let store = unsafe { &mut NET_STORE };
|
||||
|
||||
store.ip_addrs[0] = net::wire::IpCidr::new(net::wire::IpAddress::v4(10, 0, 16, 99), 24);
|
||||
|
||||
let neighbor_cache = net::iface::NeighborCache::new(&mut store.neighbor_cache[..]);
|
||||
|
||||
let interface = net::iface::EthernetInterfaceBuilder::new(eth_dma)
|
||||
.ethernet_addr(mac_addr)
|
||||
.neighbor_cache(neighbor_cache)
|
||||
.ip_addrs(&mut store.ip_addrs[..])
|
||||
.finalize();
|
||||
|
||||
(interface, eth_mac)
|
||||
};
|
||||
|
||||
cp.SCB.enable_icache();
|
||||
|
||||
// info!("Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap());
|
||||
// info!("Built on {}", build_info::BUILT_TIME_UTC);
|
||||
// info!("{} {}", build_info::RUSTC_VERSION, build_info::TARGET);
|
||||
|
||||
// Utilize the cycle counter for RTFM scheduling.
|
||||
cp.DWT.enable_cycle_counter();
|
||||
|
||||
let mut dma = hal::dma::Dma::dma(dp.DMA1, dp.DMAMUX1, &clocks);
|
||||
dma.configure_m2p_stream(hal::dma::Stream::One,
|
||||
&SPI_START_CODE as *const _ as u32,
|
||||
&adc1_spi.spi.cr1 as *const _ as u32,
|
||||
hal::dma::DMAREQ_ID::TIM2_CH1);
|
||||
|
||||
dma.configure_m2p_stream(hal::dma::Stream::Two,
|
||||
&SPI_START_CODE as *const _ as u32,
|
||||
&adc2_spi.spi.cr1 as *const _ as u32,
|
||||
hal::dma::DMAREQ_ID::TIM2_CH2);
|
||||
|
||||
// Configure timer 2 to trigger conversions for the ADC
|
||||
let mut timer2 = dp.TIM2.timer(500.khz(), &mut clocks);
|
||||
timer2.configure_channel(hal::timer::Channel::One, 0.25);
|
||||
timer2.configure_channel(hal::timer::Channel::Two, 0.75);
|
||||
|
||||
timer2.listen(hal::timer::Event::ChannelOneDma);
|
||||
timer2.listen(hal::timer::Event::ChannelTwoDma);
|
||||
|
||||
init::LateResources {
|
||||
adc1: adc1_spi,
|
||||
dac1: dac1_spi,
|
||||
adc2: adc2_spi,
|
||||
dac2: dac2_spi,
|
||||
afe1: afe1,
|
||||
afe2: afe2,
|
||||
|
||||
timer: timer2,
|
||||
pounder: pounder_devices,
|
||||
|
||||
eeprom_i2c: eeprom_i2c,
|
||||
net_interface: network_interface,
|
||||
eth_mac: eth_mac,
|
||||
mac_addr: mac_addr,
|
||||
}
|
||||
}
|
||||
|
||||
#[task(binds = SPI3, resources = [adc2, dac2, iir_state, iir_ch], priority = 2)]
|
||||
fn spi3(c: spi3::Context) {
|
||||
c.resources.adc2.spi.ifcr.write(|w| w.eotc().set_bit());
|
||||
|
||||
let output: u16 = {
|
||||
let a: u16 = c.resources.adc2.read().unwrap();
|
||||
let x0 = f32::from(a as i16);
|
||||
let y0 = c.resources.iir_ch[1].update(&mut c.resources.iir_state[1], x0);
|
||||
y0 as i16 as u16 ^ 0x8000
|
||||
};
|
||||
|
||||
c.resources.dac2.spi.ifcr.write(|w| w.eotc().set_bit().txtfc().set_bit());
|
||||
c.resources.dac2.send(output).unwrap();
|
||||
}
|
||||
|
||||
#[task(binds = SPI2, resources = [adc1, dac1, iir_state, iir_ch], priority = 2)]
|
||||
fn spi2(c: spi2::Context) {
|
||||
c.resources.adc1.spi.ifcr.write(|w| w.eotc().set_bit());
|
||||
|
||||
let output: u16 = {
|
||||
let a: u16 = c.resources.adc1.read().unwrap();
|
||||
let x0 = f32::from(a as i16);
|
||||
let y0 = c.resources.iir_ch[0].update(&mut c.resources.iir_state[0], x0);
|
||||
y0 as i16 as u16 ^ 0x8000
|
||||
};
|
||||
|
||||
c.resources.dac1.spi.ifcr.write(|w| w.eotc().set_bit().txtfc().set_bit());
|
||||
c.resources.dac1.send(output).unwrap();
|
||||
}
|
||||
|
||||
#[idle(resources=[net_interface, pounder, mac_addr, eth_mac, iir_state, iir_ch, afe1, afe2])]
|
||||
fn idle(mut c: idle::Context) -> ! {
|
||||
|
||||
let mut socket_set_entries: [_; 8] = Default::default();
|
||||
let mut sockets = net::socket::SocketSet::new(&mut socket_set_entries[..]);
|
||||
|
||||
let mut rx_storage = [0; TCP_RX_BUFFER_SIZE];
|
||||
let mut tx_storage = [0; TCP_TX_BUFFER_SIZE];
|
||||
let tcp_handle = {
|
||||
let tcp_rx_buffer = net::socket::TcpSocketBuffer::new(&mut rx_storage[..]);
|
||||
let tcp_tx_buffer = net::socket::TcpSocketBuffer::new(&mut tx_storage[..]);
|
||||
let tcp_socket = net::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
|
||||
sockets.add(tcp_socket)
|
||||
};
|
||||
|
||||
let mut server = server::Server::new();
|
||||
|
||||
let mut time = 0u32;
|
||||
let mut next_ms = Instant::now();
|
||||
|
||||
// TODO: Replace with reference to CPU clock from CCDR.
|
||||
next_ms += 400_000.cycles();
|
||||
|
||||
loop {
|
||||
let tick = Instant::now() > next_ms;
|
||||
|
||||
if tick {
|
||||
next_ms += 400_000.cycles();
|
||||
time += 1;
|
||||
}
|
||||
|
||||
{
|
||||
let socket =
|
||||
&mut *sockets.get::<net::socket::TcpSocket>(tcp_handle);
|
||||
if socket.state() == net::socket::TcpState::CloseWait {
|
||||
socket.close();
|
||||
} else if !(socket.is_open() || socket.is_listening()) {
|
||||
socket
|
||||
.listen(1235)
|
||||
.unwrap_or_else(|e| warn!("TCP listen error: {:?}", e));
|
||||
} else {
|
||||
server.poll(socket, |req| {
|
||||
info!("Got request: {:?}", req);
|
||||
route_request!(req,
|
||||
readable_attributes: [
|
||||
"stabilizer/iir/state": (|| {
|
||||
let state = c.resources.iir_state.lock(|iir_state|
|
||||
server::Status {
|
||||
t: time,
|
||||
x0: iir_state[0][0],
|
||||
y0: iir_state[0][2],
|
||||
x1: iir_state[1][0],
|
||||
y1: iir_state[1][2],
|
||||
});
|
||||
|
||||
Ok::<server::Status, ()>(state)
|
||||
}),
|
||||
"stabilizer/afe1/gain": (|| c.resources.afe1.get_gain()),
|
||||
"stabilizer/afe2/gain": (|| c.resources.afe2.get_gain()),
|
||||
"pounder/in0": (|| {
|
||||
c.resources.pounder.get_input_channel_state(
|
||||
pounder::Channel::In0)
|
||||
}),
|
||||
"pounder/in1": (|| {
|
||||
c.resources.pounder.get_input_channel_state(
|
||||
pounder::Channel::In1)
|
||||
}),
|
||||
"pounder/out0": (|| {
|
||||
c.resources.pounder.get_output_channel_state(
|
||||
pounder::Channel::Out0)
|
||||
}),
|
||||
"pounder/out1": (|| {
|
||||
c.resources.pounder.get_output_channel_state(
|
||||
pounder::Channel::Out1)
|
||||
}),
|
||||
"pounder/dds/clock": (|| {
|
||||
c.resources.pounder.get_dds_clock_config()
|
||||
})
|
||||
],
|
||||
|
||||
modifiable_attributes: [
|
||||
"stabilizer/iir1/state": server::IirRequest, (|req: server::IirRequest| {
|
||||
c.resources.iir_ch.lock(|iir_ch| {
|
||||
if req.channel > 1 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
iir_ch[req.channel as usize] = req.iir;
|
||||
|
||||
Ok::<server::IirRequest, ()>(req)
|
||||
})
|
||||
}),
|
||||
"stabilizer/iir2/state": server::IirRequest, (|req: server::IirRequest| {
|
||||
c.resources.iir_ch.lock(|iir_ch| {
|
||||
if req.channel > 1 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
iir_ch[req.channel as usize] = req.iir;
|
||||
|
||||
Ok::<server::IirRequest, ()>(req)
|
||||
})
|
||||
}),
|
||||
"pounder/in0": pounder::ChannelState, (|state| {
|
||||
c.resources.pounder.set_channel_state(pounder::Channel::In0,
|
||||
state)
|
||||
}),
|
||||
"pounder/in1": pounder::ChannelState, (|state| {
|
||||
c.resources.pounder.set_channel_state(pounder::Channel::In1,
|
||||
state)
|
||||
}),
|
||||
"pounder/out0": pounder::ChannelState, (|state| {
|
||||
c.resources.pounder.set_channel_state(pounder::Channel::Out0,
|
||||
state)
|
||||
}),
|
||||
"pounder/out1": pounder::ChannelState, (|state| {
|
||||
c.resources.pounder.set_channel_state(pounder::Channel::Out1,
|
||||
state)
|
||||
}),
|
||||
"pounder/dds/clock": pounder::DdsClockConfig, (|config| {
|
||||
c.resources.pounder.configure_dds_clock(config)
|
||||
}),
|
||||
"stabilizer/afe1/gain": afe::Gain, (|gain| {
|
||||
Ok::<(), ()>(c.resources.afe1.set_gain(gain))
|
||||
}),
|
||||
"stabilizer/afe2/gain": afe::Gain, (|gain| {
|
||||
Ok::<(), ()>(c.resources.afe2.set_gain(gain))
|
||||
})
|
||||
]
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let sleep = match c.resources.net_interface.poll(&mut sockets,
|
||||
net::time::Instant::from_millis(time as i64)) {
|
||||
Ok(changed) => changed == false,
|
||||
Err(net::Error::Unrecognized) => true,
|
||||
Err(e) => {
|
||||
info!("iface poll error: {:?}", e);
|
||||
true
|
||||
}
|
||||
};
|
||||
|
||||
if sleep {
|
||||
cortex_m::asm::wfi();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[task(binds = ETH, priority = 1)]
|
||||
fn eth(_: eth::Context) {
|
||||
unsafe { ethernet::interrupt_handler() }
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// hw interrupt handlers for RTFM to use for scheduling tasks
|
||||
// one per priority
|
||||
fn DCMI();
|
||||
fn JPEG();
|
||||
fn SDMMC();
|
||||
}
|
||||
};
|
||||
|
||||
#[exception]
|
||||
fn HardFault(ef: &cortex_m_rt::ExceptionFrame) -> ! {
|
||||
panic!("HardFault at {:#?}", ef);
|
||||
}
|
||||
|
||||
#[exception]
|
||||
fn DefaultHandler(irqn: i16) {
|
||||
panic!("Unhandled exception (IRQn = {})", irqn);
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
use super::{Channel, Error};
|
||||
|
||||
/// Provide an interface for managing digital attenuators on Pounder hardware.
|
||||
///
|
||||
/// Note: The digital attenuators do not allow read-back of attenuation. To circumvent this, this
|
||||
/// driver maintains the attenuation code in both the shift register as well as the latched output
|
||||
/// register of the attenuators. This allows the "active" attenuation code to be read back by
|
||||
/// reading the shfit register. The downside of this approach is that any read is destructive, so a
|
||||
/// read-writeback approach is employed.
|
||||
pub trait AttenuatorInterface {
|
||||
|
||||
/// Set the attenuation of a single channel.
|
||||
///
|
||||
/// Args:
|
||||
/// * `channel` - The pounder channel to configure the attenuation of.
|
||||
/// * `attenuation` - The desired attenuation of the channel in dB. This has a resolution of
|
||||
/// 0.5dB.
|
||||
fn set_attenuation(&mut self, channel: Channel, attenuation: f32) -> Result<f32, Error> {
|
||||
if attenuation > 31.5 || attenuation < 0.0 {
|
||||
return Err(Error::Bounds);
|
||||
}
|
||||
|
||||
// Calculate the attenuation code to program into the attenuator. The attenuator uses a
|
||||
// code where the LSB is 0.5 dB.
|
||||
let attenuation_code = (attenuation * 2.0) as u8;
|
||||
|
||||
// Read all the channels, modify the channel of interest, and write all the channels back.
|
||||
// This ensures the staging register and the output register are always in sync.
|
||||
let mut channels = [0_u8; 4];
|
||||
self.read_all_attenuators(&mut channels)?;
|
||||
|
||||
// The lowest 2 bits of the 8-bit shift register on the attenuator are ignored. Shift the
|
||||
// attenuator code into the upper 6 bits of the register value. Note that the attenuator
|
||||
// treats inputs as active-low, so the code is inverted before writing.
|
||||
channels[channel as usize] = (!attenuation_code) << 2;
|
||||
self.write_all_attenuators(&channels)?;
|
||||
|
||||
// Finally, latch the output of the updated channel to force it into an active state.
|
||||
self.latch_attenuators(channel)?;
|
||||
|
||||
Ok(attenuation_code as f32 / 2.0)
|
||||
}
|
||||
|
||||
/// Get the attenuation of a channel.
|
||||
///
|
||||
/// Args:
|
||||
/// * `channel` - The channel to get the attenuation of.
|
||||
///
|
||||
/// Returns:
|
||||
/// The programmed attenuation of the channel in dB.
|
||||
fn get_attenuation(&mut self, channel: Channel) -> Result<f32, Error> {
|
||||
let mut channels = [0_u8; 4];
|
||||
|
||||
// Reading the data always shifts data out of the staging registers, so we perform a
|
||||
// duplicate write-back to ensure the staging register is always equal to the output
|
||||
// register.
|
||||
self.read_all_attenuators(&mut channels)?;
|
||||
self.write_all_attenuators(&channels)?;
|
||||
|
||||
// The attenuation code is stored in the upper 6 bits of the register, where each LSB
|
||||
// represents 0.5 dB. The attenuator stores the code as active-low, so inverting the result
|
||||
// (before the shift) has the affect of transforming the bits of interest (and the
|
||||
// dont-care bits) into an active-high state and then masking off the don't care bits. If
|
||||
// the shift occurs before the inversion, the upper 2 bits (which would then be don't
|
||||
// care) would contain erroneous data.
|
||||
let attenuation_code = (!channels[channel as usize]) >> 2;
|
||||
|
||||
// Convert the desired channel code into dB of attenuation.
|
||||
Ok(attenuation_code as f32 / 2.0)
|
||||
}
|
||||
|
||||
fn reset_attenuators(&mut self) -> Result<(), Error>;
|
||||
|
||||
fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error>;
|
||||
fn read_all_attenuators(&mut self, channels: &mut [u8; 4]) -> Result<(), Error>;
|
||||
fn write_all_attenuators(&mut self, channels: &[u8; 4]) -> Result<(), Error>;
|
||||
}
|
|
@ -0,0 +1,499 @@
|
|||
use mcp23017;
|
||||
use ad9959;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
mod attenuators;
|
||||
mod rf_power;
|
||||
|
||||
use super::hal;
|
||||
|
||||
use attenuators::AttenuatorInterface;
|
||||
use rf_power::PowerMeasurementInterface;
|
||||
|
||||
use embedded_hal::{
|
||||
blocking::spi::Transfer,
|
||||
adc::OneShot,
|
||||
};
|
||||
|
||||
const EXT_CLK_SEL_PIN: u8 = 8 + 7;
|
||||
#[allow(dead_code)]
|
||||
const OSC_EN_N_PIN: u8 = 8 + 6;
|
||||
const ATT_RST_N_PIN: u8 = 8 + 5;
|
||||
const ATT_LE3_PIN: u8 = 8 + 3;
|
||||
const ATT_LE2_PIN: u8 = 8 + 2;
|
||||
const ATT_LE1_PIN: u8 = 8 + 1;
|
||||
const ATT_LE0_PIN: u8 = 8 + 0;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Error {
|
||||
Spi,
|
||||
I2c,
|
||||
Dds,
|
||||
Qspi,
|
||||
Bounds,
|
||||
InvalidAddress,
|
||||
InvalidChannel,
|
||||
Adc,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Channel {
|
||||
In0,
|
||||
In1,
|
||||
Out0,
|
||||
Out1,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||
pub struct DdsChannelState {
|
||||
pub phase_offset: f32,
|
||||
pub frequency: f64,
|
||||
pub amplitude: f32,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||
pub struct ChannelState {
|
||||
pub parameters: DdsChannelState,
|
||||
pub attenuation: f32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||
pub struct InputChannelState {
|
||||
pub attenuation: f32,
|
||||
pub power: f32,
|
||||
pub mixer: DdsChannelState,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||
pub struct OutputChannelState {
|
||||
pub attenuation: f32,
|
||||
pub channel: DdsChannelState,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||
pub struct DdsClockConfig {
|
||||
pub multiplier: u8,
|
||||
pub reference_clock: f32,
|
||||
pub external_clock: bool,
|
||||
}
|
||||
|
||||
impl Into<ad9959::Channel> for Channel {
|
||||
/// Translate pounder channels to DDS output channels.
|
||||
fn into(self) -> ad9959::Channel {
|
||||
match self {
|
||||
Channel::In0 => ad9959::Channel::Two,
|
||||
Channel::In1 => ad9959::Channel::Four,
|
||||
Channel::Out0 => ad9959::Channel::One,
|
||||
Channel::Out1 => ad9959::Channel::Three,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure for the QSPI interface for the DDS.
|
||||
pub struct QspiInterface {
|
||||
pub qspi: hal::qspi::Qspi,
|
||||
mode: ad9959::Mode,
|
||||
}
|
||||
|
||||
impl QspiInterface {
|
||||
/// Initialize the QSPI interface.
|
||||
///
|
||||
/// Args:
|
||||
/// * `qspi` - The QSPI peripheral driver.
|
||||
pub fn new(mut qspi: hal::qspi::Qspi) -> Result<Self, Error> {
|
||||
// This driver only supports operation in 4-bit mode due to bus inconsistencies between the
|
||||
// QSPI peripheral and the DDS. Instead, we will bit-bang communications in
|
||||
// single-bit-two-wire to the DDS to configure it to 4-bit operation.
|
||||
qspi.configure_mode(hal::qspi::QspiMode::FourBit).map_err(|_| Error::Qspi)?;
|
||||
Ok(Self { qspi: qspi, mode: ad9959::Mode::SingleBitTwoWire })
|
||||
}
|
||||
}
|
||||
|
||||
impl ad9959::Interface for QspiInterface {
|
||||
type Error = Error;
|
||||
|
||||
/// Configure the operations mode of the interface.
|
||||
///
|
||||
/// Args:
|
||||
/// * `mode` - The newly desired operational mode.
|
||||
fn configure_mode(&mut self, mode: ad9959::Mode) -> Result<(), Error> {
|
||||
self.mode = mode;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write data over QSPI to the DDS.
|
||||
///
|
||||
/// Args:
|
||||
/// * `addr` - The address to write over QSPI to the DDS.
|
||||
/// * `data` - The data to write.
|
||||
fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Error> {
|
||||
if (addr & 0x80) != 0 {
|
||||
return Err(Error::InvalidAddress);
|
||||
}
|
||||
|
||||
// The QSPI interface implementation always operates in 4-bit mode because the AD9959 uses
|
||||
// IO3 as SYNC_IO in some output modes. In order for writes to be successful, SYNC_IO must
|
||||
// be driven low. However, the QSPI peripheral forces IO3 high when operating in 1 or 2 bit
|
||||
// modes. As a result, any writes while in single- or dual-bit modes has to instead write
|
||||
// the data encoded into 4-bit QSPI data so that IO3 can be driven low.
|
||||
match self.mode {
|
||||
ad9959::Mode::SingleBitTwoWire => {
|
||||
// Encode the data into a 4-bit QSPI pattern.
|
||||
|
||||
// In 4-bit mode, we can send 2 bits of address and data per byte transfer. As
|
||||
// such, we need at least 4x more bytes than the length of data. To avoid dynamic
|
||||
// allocation, we assume the maximum transaction length for single-bit-two-wire is
|
||||
// 2 bytes.
|
||||
let mut encoded_data: [u8; 12] = [0; 12];
|
||||
|
||||
if (data.len() * 4) > (encoded_data.len() - 4) {
|
||||
return Err(Error::Bounds);
|
||||
}
|
||||
|
||||
// Encode the address into the first 4 bytes.
|
||||
for address_bit in 0..8 {
|
||||
let offset: u8 = {
|
||||
if address_bit % 2 != 0 {
|
||||
4
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
// Encode MSB first. Least significant bits are placed at the most significant
|
||||
// byte.
|
||||
let byte_position = 3 - (address_bit >> 1) as usize;
|
||||
|
||||
if addr & (1 << address_bit) != 0 {
|
||||
encoded_data[byte_position] |= 1 << offset;
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the data into the remaining bytes.
|
||||
for byte_index in 0..data.len() {
|
||||
let byte = data[byte_index];
|
||||
for bit in 0..8 {
|
||||
let offset: u8 = {
|
||||
if bit % 2 != 0 {
|
||||
4
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
// Encode MSB first. Least significant bits are placed at the most
|
||||
// significant byte.
|
||||
let byte_position = 3 - (bit >> 1) as usize;
|
||||
|
||||
if byte & (1 << bit) != 0 {
|
||||
encoded_data[(byte_index + 1) * 4 + byte_position] |= 1 << offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (encoded_address, encoded_payload) = {
|
||||
let end_index = (1 + data.len()) * 4;
|
||||
(encoded_data[0], &encoded_data[1..end_index])
|
||||
};
|
||||
|
||||
self.qspi.write(encoded_address, &encoded_payload).map_err(|_| Error::Qspi)
|
||||
},
|
||||
ad9959::Mode::FourBitSerial => {
|
||||
self.qspi.write(addr, &data).map_err(|_| Error::Qspi)
|
||||
},
|
||||
_ => {
|
||||
Err(Error::Qspi)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&mut self, addr: u8, mut dest: &mut [u8]) -> Result<(), Error> {
|
||||
if (addr & 0x80) != 0 {
|
||||
return Err(Error::InvalidAddress);
|
||||
}
|
||||
|
||||
// This implementation only supports operation (read) in four-bit-serial mode.
|
||||
if self.mode != ad9959::Mode::FourBitSerial {
|
||||
return Err(Error::Qspi);
|
||||
}
|
||||
|
||||
self.qspi.read(0x80_u8 | addr, &mut dest).map_err(|_| Error::Qspi)
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure containing implementation for Pounder hardware.
|
||||
pub struct PounderDevices<DELAY> {
|
||||
pub ad9959: ad9959::Ad9959<QspiInterface,
|
||||
DELAY,
|
||||
hal::gpio::gpiog::PG7<hal::gpio::Output<hal::gpio::PushPull>>>,
|
||||
mcp23017: mcp23017::MCP23017<hal::i2c::I2c<hal::stm32::I2C1>>,
|
||||
attenuator_spi: hal::spi::Spi<hal::stm32::SPI1>,
|
||||
adc1: hal::adc::Adc<hal::stm32::ADC1, hal::adc::Enabled>,
|
||||
adc2: hal::adc::Adc<hal::stm32::ADC2, hal::adc::Enabled>,
|
||||
adc1_in_p: hal::gpio::gpiof::PF11<hal::gpio::Analog>,
|
||||
adc2_in_p: hal::gpio::gpiof::PF14<hal::gpio::Analog>,
|
||||
}
|
||||
|
||||
impl<DELAY> PounderDevices<DELAY>
|
||||
where
|
||||
DELAY: embedded_hal::blocking::delay::DelayMs<u8>,
|
||||
{
|
||||
/// Construct and initialize pounder-specific hardware.
|
||||
///
|
||||
/// Args:
|
||||
/// * `ad9959` - The DDS driver for the pounder hardware.
|
||||
/// * `attenuator_spi` - A SPI interface to control digital attenuators.
|
||||
/// * `adc1` - The ADC1 peripheral for measuring power.
|
||||
/// * `adc2` - The ADC2 peripheral for measuring power.
|
||||
/// * `adc1_in_p` - The input channel for the RF power measurement on IN0.
|
||||
/// * `adc2_in_p` - The input channel for the RF power measurement on IN1.
|
||||
pub fn new(mcp23017: mcp23017::MCP23017<hal::i2c::I2c<hal::stm32::I2C1>>,
|
||||
ad9959: ad9959::Ad9959<QspiInterface,
|
||||
DELAY,
|
||||
hal::gpio::gpiog::PG7<
|
||||
hal::gpio::Output<hal::gpio::PushPull>>>,
|
||||
attenuator_spi: hal::spi::Spi<hal::stm32::SPI1>,
|
||||
adc1: hal::adc::Adc<hal::stm32::ADC1, hal::adc::Enabled>,
|
||||
adc2: hal::adc::Adc<hal::stm32::ADC2, hal::adc::Enabled>,
|
||||
adc1_in_p: hal::gpio::gpiof::PF11<hal::gpio::Analog>,
|
||||
adc2_in_p: hal::gpio::gpiof::PF14<hal::gpio::Analog>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut devices = Self {
|
||||
mcp23017,
|
||||
ad9959,
|
||||
attenuator_spi,
|
||||
adc1,
|
||||
adc2,
|
||||
adc1_in_p,
|
||||
adc2_in_p,
|
||||
};
|
||||
|
||||
// Configure power-on-default state for pounder. All LEDs are on, on-board oscillator
|
||||
// selected, attenuators out of reset. Note that testing indicates the output state needs to
|
||||
// be set first to properly update the output registers.
|
||||
devices.mcp23017.all_pin_mode(mcp23017::PinMode::OUTPUT).map_err(|_| Error::I2c)?;
|
||||
devices.mcp23017.write_gpio(mcp23017::Port::GPIOA, 0x3F).map_err(|_| Error::I2c)?;
|
||||
devices.mcp23017.write_gpio(mcp23017::Port::GPIOB, 1 << 5).map_err(|_| Error::I2c)?;
|
||||
|
||||
// Select the on-board clock with a 4x prescaler (400MHz).
|
||||
devices.select_onboard_clock(4u8)?;
|
||||
|
||||
Ok(devices)
|
||||
}
|
||||
|
||||
/// Select the an external for the DDS reference clock source.
|
||||
///
|
||||
/// Args:
|
||||
/// * `frequency` - The frequency of the external clock source.
|
||||
/// * `multiplier` - The multiplier of the reference clock to use in the DDS.
|
||||
fn select_external_clock(&mut self, frequency: f32, prescaler: u8) -> Result<(), Error>{
|
||||
self.mcp23017.digital_write(EXT_CLK_SEL_PIN, true).map_err(|_| Error::I2c)?;
|
||||
self.ad9959.configure_system_clock(frequency, prescaler).map_err(|_| Error::Dds)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Select the onboard oscillator for the DDS reference clock source.
|
||||
///
|
||||
/// Args:
|
||||
/// * `multiplier` - The multiplier of the reference clock to use in the DDS.
|
||||
fn select_onboard_clock(&mut self, multiplier: u8) -> Result<(), Error> {
|
||||
self.mcp23017.digital_write(EXT_CLK_SEL_PIN, false).map_err(|_| Error::I2c)?;
|
||||
self.ad9959.configure_system_clock(100_000_000f32, multiplier).map_err(|_| Error::Dds)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Configure the Pounder DDS clock.
|
||||
///
|
||||
/// Args:
|
||||
/// * `config` - The configuration of the DDS clock desired.
|
||||
pub fn configure_dds_clock(&mut self, config: DdsClockConfig) -> Result<(), Error> {
|
||||
if config.external_clock {
|
||||
self.select_external_clock(config.reference_clock, config.multiplier)
|
||||
} else {
|
||||
self.select_onboard_clock(config.multiplier)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the pounder DDS clock configuration
|
||||
///
|
||||
/// Returns:
|
||||
/// The current pounder DDS clock configuration.
|
||||
pub fn get_dds_clock_config(&mut self) -> Result<DdsClockConfig, Error> {
|
||||
let external_clock = self.mcp23017.digital_read(EXT_CLK_SEL_PIN).map_err(|_| Error::I2c)?;
|
||||
let multiplier = self.ad9959.get_reference_clock_multiplier().map_err(|_| Error::Dds)?;
|
||||
let reference_clock = self.ad9959.get_reference_clock_frequency();
|
||||
|
||||
Ok(DdsClockConfig{multiplier, reference_clock, external_clock})
|
||||
}
|
||||
|
||||
/// Get the state of a Pounder input channel.
|
||||
///
|
||||
/// Args:
|
||||
/// * `channel` - The pounder channel to get the state of. Must be an input channel
|
||||
///
|
||||
/// Returns:
|
||||
/// The read-back channel input state.
|
||||
pub fn get_input_channel_state(&mut self, channel: Channel) -> Result<InputChannelState, Error> {
|
||||
match channel {
|
||||
Channel::In0 | Channel::In1 => {
|
||||
let channel_state = self.get_dds_channel_state(channel)?;
|
||||
|
||||
let attenuation = self.get_attenuation(channel)?;
|
||||
let power = self.measure_power(channel)?;
|
||||
|
||||
Ok(InputChannelState {
|
||||
attenuation: attenuation,
|
||||
power: power,
|
||||
mixer: channel_state
|
||||
})
|
||||
}
|
||||
_ => Err(Error::InvalidChannel),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the state of a DDS channel.
|
||||
///
|
||||
/// Args:
|
||||
/// * `channel` - The pounder channel to get the state of.
|
||||
///
|
||||
/// Returns:
|
||||
/// The read-back channel state.
|
||||
fn get_dds_channel_state(&mut self, channel: Channel) -> Result<DdsChannelState, Error> {
|
||||
let frequency = self.ad9959.get_frequency(channel.into()).map_err(|_| Error::Dds)?;
|
||||
let phase_offset = self.ad9959.get_phase(channel.into()).map_err(|_| Error::Dds)?;
|
||||
let amplitude = self.ad9959.get_amplitude(channel.into()).map_err(|_| Error::Dds)?;
|
||||
let enabled = self.ad9959.is_enabled(channel.into()).map_err(|_| Error::Dds)?;
|
||||
|
||||
Ok(DdsChannelState {phase_offset, frequency, amplitude, enabled})
|
||||
}
|
||||
|
||||
/// Get the state of a DDS output channel.
|
||||
///
|
||||
/// Args:
|
||||
/// * `channel` - The pounder channel to get the output state of. Must be an output channel.
|
||||
///
|
||||
/// Returns:
|
||||
/// The read-back output channel state.
|
||||
pub fn get_output_channel_state(&mut self, channel: Channel) -> Result<OutputChannelState, Error> {
|
||||
match channel {
|
||||
Channel::Out0 | Channel::Out1 => {
|
||||
let channel_state = self.get_dds_channel_state(channel)?;
|
||||
let attenuation = self.get_attenuation(channel)?;
|
||||
|
||||
Ok(OutputChannelState {
|
||||
attenuation: attenuation,
|
||||
channel: channel_state,
|
||||
})
|
||||
}
|
||||
_ => Err(Error::InvalidChannel),
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure a DDS channel.
|
||||
///
|
||||
/// Args:
|
||||
/// * `channel` - The pounder channel to configure.
|
||||
/// * `state` - The state to configure the channel for.
|
||||
pub fn set_channel_state(&mut self, channel: Channel, state: ChannelState) -> Result<(), Error> {
|
||||
self.ad9959.set_frequency(channel.into(), state.parameters.frequency).map_err(|_| Error::Dds)?;
|
||||
self.ad9959.set_phase(channel.into(), state.parameters.phase_offset).map_err(|_| Error::Dds)?;
|
||||
self.ad9959.set_amplitude(channel.into(), state.parameters.amplitude).map_err(|_| Error::Dds)?;
|
||||
|
||||
if state.parameters.enabled {
|
||||
self.ad9959.enable_channel(channel.into()).map_err(|_| Error::Dds)?;
|
||||
} else {
|
||||
self.ad9959.disable_channel(channel.into()).map_err(|_| Error::Dds)?;
|
||||
}
|
||||
|
||||
self.set_attenuation(channel, state.attenuation)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<DELAY> AttenuatorInterface for PounderDevices<DELAY>
|
||||
{
|
||||
/// Reset all of the attenuators to a power-on default state.
|
||||
fn reset_attenuators(&mut self) -> Result<(), Error> {
|
||||
self.mcp23017.digital_write(ATT_RST_N_PIN, false).map_err(|_| Error::I2c)?;
|
||||
// TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is
|
||||
// sufficient. Document the delay here.
|
||||
self.mcp23017.digital_write(ATT_RST_N_PIN, true).map_err(|_| Error::I2c)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Latch a configuration into a digital attenuator.
|
||||
///
|
||||
/// Args:
|
||||
/// * `channel` - The attenuator channel to latch.
|
||||
fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error> {
|
||||
let pin = match channel {
|
||||
Channel::In0 => ATT_LE0_PIN,
|
||||
Channel::In1 => ATT_LE2_PIN,
|
||||
Channel::Out0 => ATT_LE1_PIN,
|
||||
Channel::Out1 => ATT_LE3_PIN,
|
||||
};
|
||||
|
||||
self.mcp23017.digital_write(pin, true).map_err(|_| Error::I2c)?;
|
||||
// TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is
|
||||
// sufficient. Document the delay here.
|
||||
self.mcp23017.digital_write(pin, false).map_err(|_| Error::I2c)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read the raw attenuation codes stored in the attenuator shift registers.
|
||||
///
|
||||
/// Args:
|
||||
/// * `channels` - A slice to store the channel readings into.
|
||||
fn read_all_attenuators(&mut self, channels: &mut [u8; 4]) -> Result<(), Error> {
|
||||
self.attenuator_spi.transfer(channels).map_err(|_| Error::Spi)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write the attenuator shift registers.
|
||||
///
|
||||
/// Args:
|
||||
/// * `channels` - The data to write into the attenuators.
|
||||
fn write_all_attenuators(&mut self, channels: &[u8; 4]) -> Result<(), Error> {
|
||||
let mut result = [0_u8; 4];
|
||||
result.clone_from_slice(channels);
|
||||
self.attenuator_spi.transfer(&mut result).map_err(|_| Error::Spi)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<DELAY> PowerMeasurementInterface for PounderDevices<DELAY> {
|
||||
/// Sample an ADC channel.
|
||||
///
|
||||
/// Args:
|
||||
/// * `channel` - The channel to sample.
|
||||
///
|
||||
/// Returns:
|
||||
/// The sampled voltage of the specified channel.
|
||||
fn sample_converter(&mut self, channel: Channel) -> Result<f32, Error> {
|
||||
let adc_scale = match channel {
|
||||
Channel::In0 => {
|
||||
let adc_reading: u32 = self.adc1.read(&mut self.adc1_in_p).map_err(|_| Error::Adc)?;
|
||||
adc_reading as f32 / self.adc1.max_sample() as f32
|
||||
},
|
||||
Channel::In1 => {
|
||||
let adc_reading: u32 = self.adc2.read(&mut self.adc2_in_p).map_err(|_| Error::Adc)?;
|
||||
adc_reading as f32 / self.adc2.max_sample() as f32
|
||||
},
|
||||
_ => return Err(Error::InvalidChannel),
|
||||
};
|
||||
|
||||
// Convert analog percentage to voltage. Note that the ADC uses an external 2.048V analog
|
||||
// reference.
|
||||
Ok(adc_scale * 2.048)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
use super::{Error, Channel};
|
||||
|
||||
/// Provide an interface to measure RF input power in dB.
|
||||
pub trait PowerMeasurementInterface {
|
||||
fn sample_converter(&mut self, channel: Channel) -> Result<f32, Error>;
|
||||
|
||||
/// Measure the power of an input channel in dBm.
|
||||
///
|
||||
/// Note: This function assumes the input channel is connected to an AD8363 output.
|
||||
///
|
||||
/// Args:
|
||||
/// * `channel` - The pounder channel to measure the power of in dBm.
|
||||
fn measure_power(&mut self, channel: Channel) -> Result<f32, Error> {
|
||||
let analog_measurement = self.sample_converter(channel)?;
|
||||
|
||||
// The AD8363 with VSET connected to VOUT provides an output voltage of 51.7mV / dB at
|
||||
// 100MHz. It also indicates a y-intercept of -58dBm.
|
||||
Ok(analog_measurement / 0.0517 - 58.0)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
use heapless::{
|
||||
consts::*,
|
||||
String,
|
||||
Vec
|
||||
};
|
||||
|
||||
use core::fmt::Write;
|
||||
|
||||
|
||||
use serde::{
|
||||
Deserialize,
|
||||
Serialize
|
||||
};
|
||||
|
||||
use serde_json_core::{
|
||||
de::from_slice,
|
||||
ser::to_string
|
||||
};
|
||||
|
||||
use super::net;
|
||||
use super::iir;
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub enum AccessRequest {
|
||||
Read,
|
||||
Write,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct Request<'a> {
|
||||
pub req: AccessRequest,
|
||||
pub attribute: &'a str,
|
||||
pub value: String<U256>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct IirRequest {
|
||||
pub channel: u8,
|
||||
pub iir: iir::IIR,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Response {
|
||||
code: i32,
|
||||
attribute: String<U256>,
|
||||
value: String<U256>,
|
||||
}
|
||||
|
||||
impl<'a> Request<'a> {
|
||||
pub fn restore_value(&mut self) {
|
||||
let mut new_value: String<U256> = String::new();
|
||||
for byte in self.value.as_str().chars() {
|
||||
if byte == '\'' {
|
||||
new_value.push('"').unwrap();
|
||||
} else {
|
||||
new_value.push(byte).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
self.value = new_value;
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
|
||||
/// Remove all double quotation marks from the `value` field of a response.
|
||||
fn sanitize_value(&mut self) {
|
||||
let mut new_value: String<U256> = String::new();
|
||||
for byte in self.value.as_str().chars() {
|
||||
if byte == '"' {
|
||||
new_value.push('\'').unwrap();
|
||||
} else {
|
||||
new_value.push(byte).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
self.value = new_value;
|
||||
}
|
||||
|
||||
/// Remove all double quotation marks from the `value` field of a response and wrap it in single
|
||||
/// quotes.
|
||||
fn wrap_and_sanitize_value(&mut self) {
|
||||
let mut new_value: String<U256> = String::new();
|
||||
new_value.push('\'').unwrap();
|
||||
for byte in self.value.as_str().chars() {
|
||||
if byte == '"' {
|
||||
new_value.push('\'').unwrap();
|
||||
} else {
|
||||
new_value.push(byte).unwrap();
|
||||
}
|
||||
}
|
||||
new_value.push('\'').unwrap();
|
||||
|
||||
self.value = new_value;
|
||||
}
|
||||
|
||||
/// Construct a successful reply.
|
||||
///
|
||||
/// Note: `value` will be sanitized to convert all single quotes to double quotes.
|
||||
///
|
||||
/// Args:
|
||||
/// * `attrbute` - The attribute of the success.
|
||||
/// * `value` - The value of the attribute.
|
||||
pub fn success<'a, 'b>(attribute: &'a str, value: &'b str) -> Self
|
||||
{
|
||||
let mut res = Self { code: 200, attribute: String::from(attribute), value: String::from(value)};
|
||||
res.sanitize_value();
|
||||
res
|
||||
}
|
||||
|
||||
/// Construct an error reply.
|
||||
///
|
||||
/// Note: `message` will be sanitized to convert all single quotes to double quotes.
|
||||
///
|
||||
/// Args:
|
||||
/// * `attrbute` - The attribute of the success.
|
||||
/// * `message` - The message denoting the error.
|
||||
pub fn error<'a, 'b>(attribute: &'a str, message: &'b str) -> Self
|
||||
{
|
||||
let mut res = Self { code: 400, attribute: String::from(attribute), value: String::from(message)};
|
||||
res.wrap_and_sanitize_value();
|
||||
res
|
||||
}
|
||||
|
||||
/// Construct a custom reply.
|
||||
///
|
||||
/// Note: `message` will be sanitized to convert all single quotes to double quotes.
|
||||
///
|
||||
/// Args:
|
||||
/// * `attrbute` - The attribute of the success.
|
||||
/// * `message` - The message denoting the status.
|
||||
pub fn custom<'a>(code: i32, message : &'a str) -> Self
|
||||
{
|
||||
let mut res = Self { code: code, attribute: String::from(""), value: String::from(message)};
|
||||
res.wrap_and_sanitize_value();
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Status {
|
||||
pub t: u32,
|
||||
pub x0: f32,
|
||||
pub y0: f32,
|
||||
pub x1: f32,
|
||||
pub y1: f32,
|
||||
}
|
||||
|
||||
pub fn json_reply<T: Serialize>(socket: &mut net::socket::TcpSocket, msg: &T) {
|
||||
let mut u: String<U512> = to_string(msg).unwrap();
|
||||
u.push('\n').unwrap();
|
||||
socket.write_str(&u).unwrap();
|
||||
}
|
||||
|
||||
pub struct Server {
|
||||
data: Vec<u8, U256>,
|
||||
discard: bool,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
/// Construct a new server object for managing requests.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: Vec::new(),
|
||||
discard: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Poll the server for potential data updates.
|
||||
///
|
||||
/// Args:
|
||||
/// * `socket` - The socket to check contents from.
|
||||
/// * `f` - A closure that can be called if a request has been received on the server.
|
||||
pub fn poll<F>(
|
||||
&mut self,
|
||||
socket: &mut net::socket::TcpSocket,
|
||||
mut f: F,
|
||||
)
|
||||
where
|
||||
F: FnMut(&Request) -> Response
|
||||
{
|
||||
while socket.can_recv() {
|
||||
let found = socket.recv(|buf| {
|
||||
let (len, found) =
|
||||
match buf.iter().position(|&c| c as char == '\n') {
|
||||
Some(end) => (end + 1, true),
|
||||
None => (buf.len(), false),
|
||||
};
|
||||
if self.data.len() + len >= self.data.capacity() {
|
||||
self.discard = true;
|
||||
self.data.clear();
|
||||
} else if !self.discard && len > 0 {
|
||||
self.data.extend_from_slice(&buf[..len]).unwrap();
|
||||
}
|
||||
(len, found)
|
||||
}).unwrap();
|
||||
|
||||
if found {
|
||||
if self.discard {
|
||||
self.discard = false;
|
||||
json_reply(socket, &Response::custom(520, "command buffer overflow"));
|
||||
} else {
|
||||
let r = from_slice::<Request>(&self.data[..self.data.len() - 1]);
|
||||
match r {
|
||||
Ok(mut res) => {
|
||||
// Note that serde_json_core doesn't escape quotations within a string.
|
||||
// To account for this, we manually translate all single quotes to
|
||||
// double quotes. This occurs because we doubly-serialize this field in
|
||||
// some cases.
|
||||
res.restore_value();
|
||||
let response = f(&res);
|
||||
json_reply(socket, &response);
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("parse error {:?}", err);
|
||||
json_reply(socket, &Response::custom(550, "parse error"));
|
||||
}
|
||||
}
|
||||
}
|
||||
self.data.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
Subproject commit ff00e938f2b226211c178f26c092f36462c44404
|
Loading…
Reference in New Issue