Compare commits
10 Commits
e57a029b93
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
24e0083090
|
|||
|
08b1d10a92
|
|||
|
d64bf1f204
|
|||
|
a82120169f
|
|||
|
066c5dbf96
|
|||
|
e2edb1d468
|
|||
|
a918ebc554
|
|||
|
27c105b0df
|
|||
|
3d862e3b46
|
|||
|
648b4344a8
|
Generated
+802
-8
@@ -2,18 +2,71 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic-polyfill"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||||
|
|
||||||
|
[[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",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bbqueue"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68917624e17aad88607cb5a5936f6da9b607c48c711e4e9ed101e7189aed28c2"
|
||||||
|
dependencies = [
|
||||||
|
"const-init",
|
||||||
|
"critical-section",
|
||||||
|
"maitake-sync",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitfield"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array 0.14.7",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -30,20 +83,111 @@ dependencies = [
|
|||||||
"embedded-io",
|
"embedded-io",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.62"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
|
||||||
|
dependencies = [
|
||||||
|
"find-msvc-tools",
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"inout",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-init"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4bd422bfb4f24a97243f60b6a4443e63d810c925d8da4bb2d8fde26a7c1d57ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cordyceps"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a"
|
||||||
|
dependencies = [
|
||||||
|
"loom",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m"
|
||||||
|
version = "0.7.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
|
||||||
|
dependencies = [
|
||||||
|
"bare-metal",
|
||||||
|
"bitfield",
|
||||||
|
"critical-section",
|
||||||
|
"embedded-hal 0.2.7",
|
||||||
|
"volatile-register",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m-rt"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m-rt-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m-rt-macros"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "critical-section"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array 0.14.7",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "defmt"
|
name = "defmt"
|
||||||
version = "0.3.100"
|
version = "0.3.100"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad"
|
checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"defmt 1.0.1",
|
"defmt 1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "defmt"
|
name = "defmt"
|
||||||
version = "1.0.1"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78"
|
checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"defmt-macros",
|
"defmt-macros",
|
||||||
@@ -51,9 +195,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "defmt-macros"
|
name = "defmt-macros"
|
||||||
version = "1.0.1"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e"
|
checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"defmt-parser",
|
"defmt-parser",
|
||||||
"proc-macro-error2",
|
"proc-macro-error2",
|
||||||
@@ -71,12 +215,107 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-rtt"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0f73a4a4a91609e977ae3b7bd831ffa292edfd42ad140a3244a61d805b0e05e"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
"defmt 1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-hal"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
|
||||||
|
dependencies = [
|
||||||
|
"nb 0.1.3",
|
||||||
|
"void",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-hal"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embedded-io"
|
name = "embedded-io"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9eb1aa714776b75c7e67e1da744b81a129b3ff919c8712b5e1b32252c1f07cc7"
|
checksum = "9eb1aa714776b75c7e67e1da744b81a129b3ff919c8712b5e1b32252c1f07cc7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-time"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d7a4b4d10ac48d08bfe3db7688c402baadb244721f30a77ce360bd24c3dffe58"
|
||||||
|
dependencies = [
|
||||||
|
"num",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "find-msvc-tools"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generator"
|
||||||
|
version = "0.8.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"rustversion",
|
||||||
|
"windows-link",
|
||||||
|
"windows-result",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dab9e9188e97a93276e1fe7b56401b851e2b45a46d045ca658100c1303ada649"
|
||||||
|
dependencies = [
|
||||||
|
"rustversion",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash32"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hash32"
|
name = "hash32"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -86,16 +325,188 @@ dependencies = [
|
|||||||
"byteorder",
|
"byteorder",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heapless"
|
||||||
|
version = "0.7.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-polyfill",
|
||||||
|
"hash32 0.2.1",
|
||||||
|
"rustc_version 0.4.1",
|
||||||
|
"spin",
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heapless"
|
name = "heapless"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hash32",
|
"hash32 0.3.1",
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inout"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array 0.14.7",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.186"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||||
|
dependencies = [
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log-to-defmt"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9454118d78a9089a15d702fbaf413da2fa23f331b8af6c5eed5784a4369173c6"
|
||||||
|
dependencies = [
|
||||||
|
"defmt 0.3.100",
|
||||||
|
"heapless 0.7.17",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "loom"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"generator",
|
||||||
|
"scoped-tls",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lpc55-hal"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "git+https://github.com/ktims/lpc55-hal?branch=main#8dfefd62aff4abd2de535f23107812dda68437be"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"cipher",
|
||||||
|
"cortex-m",
|
||||||
|
"digest",
|
||||||
|
"embedded-hal 0.2.7",
|
||||||
|
"embedded-time",
|
||||||
|
"generic-array 1.4.1",
|
||||||
|
"lpc55-pac",
|
||||||
|
"nb 1.1.0",
|
||||||
|
"rand_core",
|
||||||
|
"usb-device",
|
||||||
|
"vcell",
|
||||||
|
"void",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lpc55-pac"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a4952baed9d9e7e82a6bbc87333f939b90eb41df3e6c0be5e35d0ec61005f91"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m",
|
||||||
|
"cortex-m-rt",
|
||||||
|
"vcell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lpc55s28-evk"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bbqueue",
|
||||||
|
"cortex-m",
|
||||||
|
"cortex-m-rt",
|
||||||
|
"defmt 1.1.0",
|
||||||
|
"defmt-rtt",
|
||||||
|
"embedded-hal 1.0.0",
|
||||||
|
"embedded-io",
|
||||||
|
"log-to-defmt",
|
||||||
|
"lpc55-hal",
|
||||||
|
"panic-probe",
|
||||||
|
"usb-device",
|
||||||
|
"usbd-uac2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lpc55s28-evk-dma"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m",
|
||||||
|
"cortex-m-rt",
|
||||||
|
"defmt 1.1.0",
|
||||||
|
"defmt-rtt",
|
||||||
|
"embedded-hal 1.0.0",
|
||||||
|
"embedded-io",
|
||||||
|
"log-to-defmt",
|
||||||
|
"lpc55-hal",
|
||||||
|
"nb 1.1.0",
|
||||||
|
"panic-probe",
|
||||||
|
"static_cell",
|
||||||
|
"usb-device",
|
||||||
|
"usbd-uac2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "maitake-sync"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d77c365d697828821727b9bc09e6bc3c518b8c63804e79e1be5a5ae091a7c5f"
|
||||||
|
dependencies = [
|
||||||
|
"cordyceps",
|
||||||
|
"critical-section",
|
||||||
|
"loom",
|
||||||
|
"mutex-traits",
|
||||||
|
"mycelium-bitfield",
|
||||||
|
"pin-project",
|
||||||
|
"portable-atomic",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchers"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||||
|
dependencies = [
|
||||||
|
"regex-automata",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "modular-bitfield"
|
name = "modular-bitfield"
|
||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
@@ -117,6 +528,95 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mutex-traits"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3929f2b5633d29cf7b6624992e5f3c1e9334f1193423e12d17be4faf678cde3f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mycelium-bitfield"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24e0cc5e2c585acbd15c5ce911dff71e1f4d5313f43345873311c4f5efd741cc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nb"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
|
||||||
|
dependencies = [
|
||||||
|
"nb 1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nb"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu-ansi-term"
|
||||||
|
version = "0.50.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f"
|
||||||
|
dependencies = [
|
||||||
|
"num-complex",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-complex"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.46"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-iter"
|
||||||
|
version = "0.1.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
@@ -126,6 +626,48 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "panic-probe"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m",
|
||||||
|
"defmt 1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project"
|
||||||
|
version = "1.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-internal",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-internal"
|
||||||
|
version = "1.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "portable-atomic"
|
name = "portable-atomic"
|
||||||
version = "1.13.1"
|
version = "1.13.1"
|
||||||
@@ -172,6 +714,116 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||||
|
|
||||||
|
[[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",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||||
|
dependencies = [
|
||||||
|
"semver 1.0.28",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scoped-tls"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.9.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stable_deref_trait"
|
name = "stable_deref_trait"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@@ -184,6 +836,15 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_cell"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0530892bb4fa575ee0da4b86f86c667132a94b74bb72160f58ee5a4afec74c23"
|
||||||
|
dependencies = [
|
||||||
|
"portable-atomic",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.117"
|
version = "2.0.117"
|
||||||
@@ -215,6 +876,82 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing"
|
||||||
|
version = "0.1.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-lite",
|
||||||
|
"tracing-attributes",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-attributes"
|
||||||
|
version = "0.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-core"
|
||||||
|
version = "0.1.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
|
||||||
|
dependencies = [
|
||||||
|
"matchers",
|
||||||
|
"nu-ansi-term",
|
||||||
|
"once_cell",
|
||||||
|
"regex-automata",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec",
|
||||||
|
"thread_local",
|
||||||
|
"tracing",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.24"
|
version = "1.0.24"
|
||||||
@@ -228,7 +965,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6"
|
checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"defmt 0.3.100",
|
"defmt 0.3.100",
|
||||||
"heapless",
|
"heapless 0.8.0",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -237,9 +974,66 @@ name = "usbd-uac2"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder-embedded-io",
|
"byteorder-embedded-io",
|
||||||
"defmt 1.0.1",
|
"defmt 1.1.0",
|
||||||
"embedded-io",
|
"embedded-io",
|
||||||
"modular-bitfield",
|
"modular-bitfield",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"usb-device",
|
"usb-device",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcell"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[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.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
|
||||||
|
dependencies = [
|
||||||
|
"vcell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|||||||
+13
-2
@@ -1,10 +1,21 @@
|
|||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"examples/lpc55s28-evk",
|
||||||
|
"examples/lpc55s28-evk-dma"
|
||||||
|
]
|
||||||
|
resolver = "2"
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
usb-device = { version = "0.3", features = ["control-buffer-256"] }
|
||||||
|
usbd-uac2 = { path = "." }
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "usbd-uac2"
|
name = "usbd-uac2"
|
||||||
description = "USB Audio Class 2.0 for usb-device"
|
description = "USB Audio Class 2.0 for usb-device"
|
||||||
authors = ["Keenan Tims <ktims@gotroot.ca>"]
|
authors = ["Keenan Tims <ktims@gotroot.ca>"]
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
keywords = ["no-std", "usb-device"]
|
keywords = ["no-std", "usb-device", "audio"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
defmt = ["dep:defmt", "usb-device/defmt"]
|
defmt = ["dep:defmt", "usb-device/defmt"]
|
||||||
@@ -15,4 +26,4 @@ defmt = { version = "1.0.1", optional = true }
|
|||||||
embedded-io = "0.7.1"
|
embedded-io = "0.7.1"
|
||||||
modular-bitfield = "0.13.1"
|
modular-bitfield = "0.13.1"
|
||||||
num-traits = { version = "0.2.19", default-features = false }
|
num-traits = { version = "0.2.19", default-features = false }
|
||||||
usb-device = { version = "0.3", features = ["control-buffer-256"] }
|
usb-device.workspace = true
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# usbd-uac2 Examples
|
||||||
|
|
||||||
|
This repository contains example implementations of a USB Audio Class 2 (UAC2) device.
|
||||||
|
|
||||||
|
Two example backends are provided, both based on the LPCXpresso55S28 demo board, using the onboard WM8904 DAC:
|
||||||
|
|
||||||
|
- **Interrupt-driven example** (`lpc55s28-evk`)
|
||||||
|
- **DMA-based example** (`lpc55s28-evk-dma`)
|
||||||
|
|
||||||
|
|
||||||
|
## Examples Overview
|
||||||
|
|
||||||
|
### Interrupt-driven (`lpc55s28-evk`)
|
||||||
|
|
||||||
|
Running at 32bit/48khz. It can't keep up at 96khz. Works on USBFS and USBHS.
|
||||||
|
|
||||||
|
This is a minimal implementation intended to demonstrate the fundamental
|
||||||
|
structure of the class driver. It fills a `bbqueue` as data comes in from USB,
|
||||||
|
and drains it into the I2S FIFO in the I2S interrupt. This requires a lot of
|
||||||
|
time-critical CPU work managing buffers. Particularly, the USB peripheral driver
|
||||||
|
uses a lot of interrupt-free critical sections which can cause late interrupts
|
||||||
|
and underruns.
|
||||||
|
|
||||||
|
This is intended primarily as a learning/reference implementation
|
||||||
|
|
||||||
|
### DMA-based (`lpc55s28-evk-dma`)
|
||||||
|
|
||||||
|
Running at 32bit/96khz. Works on USBFS and USBHS.
|
||||||
|
|
||||||
|
A more realistic and robust implementation using DMA. It fills a static ring
|
||||||
|
buffer as data comes in from USB, while the DMA chases it around the ring,
|
||||||
|
draining into the TX FIFO. This is efficient and decouples interrupt latency
|
||||||
|
from data delivery. It uses the DMA interrupt to track consumed slots, but as
|
||||||
|
long as the USB doesn't catch up to the read slot before this happens, there is
|
||||||
|
a lot of slack for other things to be happening.
|
||||||
|
|
||||||
|
This is a more useful demonstration, but is still lacking correct handling of
|
||||||
|
edge cases, error conditions and so on you would want in a fully fleshed out
|
||||||
|
implementation. Particularly, it behaves poorly in underrun, since the DMA will
|
||||||
|
keep emitting from the ring regardless of whether the data is valid, which
|
||||||
|
sounds terrible.
|
||||||
|
|
||||||
|
## Running the Examples
|
||||||
|
|
||||||
|
You can flash and run either example using `cargo embed`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo embed --release --example lpc55s28-evk --features usbfs
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
[target.thumbv8m.main-none-eabi]
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
"-C", "debug-assertions",
|
||||||
|
]
|
||||||
|
|
||||||
|
[target.thumbv8m.main-none-eabihf]
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
# "-C", "debug-assertions",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv8m.main-none-eabihf"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "off"
|
||||||
Generated
+695
@@ -0,0 +1,695 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic-polyfill"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||||
|
|
||||||
|
[[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",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitfield"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array 0.14.7",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder-embedded-io"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed6bb9472871706c9b1f648ca527031e33d647a95706d6ab5659f22ca28d419"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"embedded-io",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"inout",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m"
|
||||||
|
version = "0.7.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
|
||||||
|
dependencies = [
|
||||||
|
"bare-metal",
|
||||||
|
"bitfield",
|
||||||
|
"critical-section",
|
||||||
|
"embedded-hal 0.2.7",
|
||||||
|
"volatile-register",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m-rt"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m-rt-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m-rt-macros"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "critical-section"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array 0.14.7",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt"
|
||||||
|
version = "0.3.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad"
|
||||||
|
dependencies = [
|
||||||
|
"defmt 1.0.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"defmt-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-macros"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e"
|
||||||
|
dependencies = [
|
||||||
|
"defmt-parser",
|
||||||
|
"proc-macro-error2",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-parser"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-rtt"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
"defmt 1.0.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-hal"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
|
||||||
|
dependencies = [
|
||||||
|
"nb 0.1.3",
|
||||||
|
"void",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-hal"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-io"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9eb1aa714776b75c7e67e1da744b81a129b3ff919c8712b5e1b32252c1f07cc7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-time"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d7a4b4d10ac48d08bfe3db7688c402baadb244721f30a77ce360bd24c3dffe58"
|
||||||
|
dependencies = [
|
||||||
|
"num",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "1.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542"
|
||||||
|
dependencies = [
|
||||||
|
"rustversion",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash32"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash32"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heapless"
|
||||||
|
version = "0.7.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-polyfill",
|
||||||
|
"hash32 0.2.1",
|
||||||
|
"rustc_version 0.4.1",
|
||||||
|
"spin",
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heapless"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||||
|
dependencies = [
|
||||||
|
"hash32 0.3.1",
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inout"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array 0.14.7",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||||
|
dependencies = [
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log-to-defmt"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9454118d78a9089a15d702fbaf413da2fa23f331b8af6c5eed5784a4369173c6"
|
||||||
|
dependencies = [
|
||||||
|
"defmt 0.3.100",
|
||||||
|
"heapless 0.7.17",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lpc55-hal"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "git+https://github.com/ktims/lpc55-hal?branch=main#8dfefd62aff4abd2de535f23107812dda68437be"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"cipher",
|
||||||
|
"cortex-m",
|
||||||
|
"digest",
|
||||||
|
"embedded-hal 0.2.7",
|
||||||
|
"embedded-time",
|
||||||
|
"generic-array 1.3.5",
|
||||||
|
"lpc55-pac",
|
||||||
|
"nb 1.1.0",
|
||||||
|
"rand_core",
|
||||||
|
"usb-device",
|
||||||
|
"vcell",
|
||||||
|
"void",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lpc55-pac"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a4952baed9d9e7e82a6bbc87333f939b90eb41df3e6c0be5e35d0ec61005f91"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m",
|
||||||
|
"cortex-m-rt",
|
||||||
|
"vcell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lpc55s28-evk-dma"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m",
|
||||||
|
"cortex-m-rt",
|
||||||
|
"defmt 1.0.1",
|
||||||
|
"defmt-rtt",
|
||||||
|
"embedded-hal 1.0.0",
|
||||||
|
"embedded-io",
|
||||||
|
"log-to-defmt",
|
||||||
|
"lpc55-hal",
|
||||||
|
"nb 1.1.0",
|
||||||
|
"panic-probe",
|
||||||
|
"static_cell",
|
||||||
|
"usb-device",
|
||||||
|
"usbd-uac2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "modular-bitfield"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2956e537fc68236d2aa048f55704f231cc93f1c4de42fe1ecb5bd7938061fc4a"
|
||||||
|
dependencies = [
|
||||||
|
"modular-bitfield-impl",
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "modular-bitfield-impl"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59b43b4fd69e3437618106f7754f34021b831a514f9e1a98ae863cabcd8d8dad"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nb"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
|
||||||
|
dependencies = [
|
||||||
|
"nb 1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nb"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f"
|
||||||
|
dependencies = [
|
||||||
|
"num-complex",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-complex"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.46"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-iter"
|
||||||
|
version = "0.1.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "panic-probe"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m",
|
||||||
|
"defmt 1.0.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic"
|
||||||
|
version = "1.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr2"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error2"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr2",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
|
||||||
|
[[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",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||||
|
dependencies = [
|
||||||
|
"semver 1.0.28",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.9.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_cell"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0530892bb4fa575ee0da4b86f86c667132a94b74bb72160f58ee5a4afec74c23"
|
||||||
|
dependencies = [
|
||||||
|
"portable-atomic",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.117"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "usb-device"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6"
|
||||||
|
dependencies = [
|
||||||
|
"defmt 0.3.100",
|
||||||
|
"heapless 0.8.0",
|
||||||
|
"portable-atomic",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "usbd-uac2"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder-embedded-io",
|
||||||
|
"defmt 1.0.1",
|
||||||
|
"embedded-io",
|
||||||
|
"modular-bitfield",
|
||||||
|
"num-traits",
|
||||||
|
"usb-device",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcell"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[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.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
|
||||||
|
dependencies = [
|
||||||
|
"vcell",
|
||||||
|
]
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
[package]
|
||||||
|
name = "lpc55s28-evk-dma"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["usbhs"]
|
||||||
|
usbfs = []
|
||||||
|
usbhs = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] }
|
||||||
|
cortex-m-rt = "0.7.5"
|
||||||
|
defmt = "1.0.1"
|
||||||
|
defmt-rtt = "1.1.0"
|
||||||
|
embedded-hal = "1.0.0"
|
||||||
|
embedded-io = "0.7.1"
|
||||||
|
log-to-defmt = "0.1.0"
|
||||||
|
nb = "1.1.0"
|
||||||
|
panic-probe = { version = "1.0.0", features = ["print-defmt"] }
|
||||||
|
static_cell = "2.1.1"
|
||||||
|
# Includes update to usb-device 0.3, fix for isochronous and smaller critical sections
|
||||||
|
lpc55-hal = { git = "https://github.com/ktims/lpc55-hal", branch = "main" }
|
||||||
|
usb-device.workspace = true
|
||||||
|
usbd-uac2 = { workspace = true, features = ["defmt"] }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = "z"
|
||||||
|
lto = true
|
||||||
|
debug = true
|
||||||
|
codegen-units = 1
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[default.general]
|
||||||
|
chip = "LPC55S28JBD100"
|
||||||
|
[default.rtt]
|
||||||
|
enabled = true
|
||||||
|
[default.gdb]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[debug.rtt]
|
||||||
|
enabled = false
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// Find the actual path of memory.x and add it to link search, required for building in workspace
|
||||||
|
fn main() {
|
||||||
|
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||||
|
println!("cargo:rustc-link-search={}", manifest_dir);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x00000000, LENGTH = 512K
|
||||||
|
|
||||||
|
/* for use with standard link.x */
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 192K
|
||||||
|
|
||||||
|
/* would be used with proper link.x */
|
||||||
|
/* needs changes to r0 (initialization code) */
|
||||||
|
/* SRAM0 : ORIGIN = 0x20000000, LENGTH = 64K */
|
||||||
|
/* SRAM1 : ORIGIN = 0x20010000, LENGTH = 64K */
|
||||||
|
/* SRAM2 : ORIGIN = 0x20020000, LENGTH = 64K */
|
||||||
|
/* SRAM3 : ORIGIN = 0x20030000, LENGTH = 64K */
|
||||||
|
|
||||||
|
/* CASPER SRAM regions */
|
||||||
|
/* SRAMX0: ORIGIN = 0x1400_0000, LENGTH = 4K /1* to 0x1400_0FFF *1/ */
|
||||||
|
/* SRAMX1: ORIGIN = 0x1400_4000, LENGTH = 4K /1* to 0x1400_4FFF *1/ */
|
||||||
|
|
||||||
|
/* USB1 SRAM regin */
|
||||||
|
/* USB1_SRAM : ORIGIN = 0x40100000, LENGTH = 16K */
|
||||||
|
}
|
||||||
@@ -0,0 +1,427 @@
|
|||||||
|
use hal::Syscon;
|
||||||
|
use hal::peripherals::syscon::ClockControl;
|
||||||
|
|
||||||
|
use crate::{hal, pac};
|
||||||
|
use core::cell::UnsafeCell;
|
||||||
|
use core::convert::Infallible;
|
||||||
|
use core::ptr::copy_nonoverlapping;
|
||||||
|
use core::sync::atomic::{AtomicUsize, Ordering, compiler_fence};
|
||||||
|
|
||||||
|
pub const DMA0_FLEXCOMM7_TX: u8 = 19;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct DmaDescriptor {
|
||||||
|
pub xfercfg: u32,
|
||||||
|
pub src_end: *const u8,
|
||||||
|
pub dst_end: *mut u32,
|
||||||
|
pub next: *const DmaDescriptor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl defmt::Format for DmaDescriptor {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(
|
||||||
|
fmt,
|
||||||
|
"xfercfg={:x} src_end={:x} dst_end={:x} next={:x}",
|
||||||
|
self.xfercfg,
|
||||||
|
self.src_end,
|
||||||
|
self.dst_end,
|
||||||
|
self.next
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel descriptor table; linked from SRAMBASE
|
||||||
|
#[repr(C, align(512))]
|
||||||
|
pub struct DescriptorTable {
|
||||||
|
pub d: [DmaDescriptor; 32],
|
||||||
|
}
|
||||||
|
// Our ring that we will transition to once the transfer begins
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct RingDescriptors<const N: usize> {
|
||||||
|
pub d: [DmaDescriptor; N],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub struct PushResult {
|
||||||
|
pub written: usize,
|
||||||
|
pub dropped: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub enum ConfigError {
|
||||||
|
SlotTooLarge,
|
||||||
|
SlotTooSmall,
|
||||||
|
SlotNotAligned,
|
||||||
|
UnsupportedWidth,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DmaError {
|
||||||
|
Underrun,
|
||||||
|
}
|
||||||
|
impl core::error::Error for DmaError {}
|
||||||
|
impl core::fmt::Display for DmaError {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.write_str("DmaUnderrun")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Slot-based DMA ring
|
||||||
|
pub struct DmaRing<const N: usize, const MAX_SLOT_BYTES: usize> {
|
||||||
|
dma: pac::DMA0,
|
||||||
|
|
||||||
|
/// Destination peripheral register (FIFO write register)
|
||||||
|
dst_reg: *mut u32,
|
||||||
|
|
||||||
|
// SAFETY: only written by USB task (on start)
|
||||||
|
pub(crate) channel_desc: UnsafeCell<DescriptorTable>,
|
||||||
|
// SAFETY: only written by USB task (on start)
|
||||||
|
pub(crate) desc: UnsafeCell<RingDescriptors<N>>,
|
||||||
|
slots: UnsafeCell<[[u8; MAX_SLOT_BYTES]; N]>,
|
||||||
|
|
||||||
|
/// Effective bytes per slot. Maybe be smaller than MAX_SLOT_BYTES (e.g. at lower sample rates), as the setup is designed for constant rate not constant size.
|
||||||
|
slot_bytes: usize,
|
||||||
|
/// How many bytes to transfer to the FIFO
|
||||||
|
word_bytes: usize,
|
||||||
|
|
||||||
|
// SAFETY: producer only
|
||||||
|
write_slot: UnsafeCell<usize>,
|
||||||
|
write_off: UnsafeCell<usize>,
|
||||||
|
|
||||||
|
produced: AtomicUsize,
|
||||||
|
consumed: AtomicUsize,
|
||||||
|
|
||||||
|
/// Leave at least one slot empty so producer never overwrites a slot DMA may still read.
|
||||||
|
safety_gap: usize,
|
||||||
|
pub produced_bytes: AtomicUsize,
|
||||||
|
pub consumed_bytes: AtomicUsize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize, const MAX_SLOT_BYTES: usize> DmaRing<N, MAX_SLOT_BYTES> {
|
||||||
|
/// Construct using PAC DMA0 + &mut SYSCON + a destination FIFO register.
|
||||||
|
pub fn new(
|
||||||
|
dma: pac::DMA0,
|
||||||
|
syscon: &mut Syscon,
|
||||||
|
dst_reg: *mut u32,
|
||||||
|
word_bytes: usize,
|
||||||
|
) -> Result<Self, ConfigError> {
|
||||||
|
if word_bytes != 1 && word_bytes != 2 && word_bytes != 4 {
|
||||||
|
return Err(ConfigError::UnsupportedWidth);
|
||||||
|
}
|
||||||
|
// Start the DMA0 clock
|
||||||
|
dma.enable_clock(syscon);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
dma,
|
||||||
|
dst_reg: dst_reg,
|
||||||
|
channel_desc: UnsafeCell::new(DescriptorTable {
|
||||||
|
d: [DmaDescriptor {
|
||||||
|
xfercfg: 0,
|
||||||
|
src_end: core::ptr::null(),
|
||||||
|
dst_end: core::ptr::null_mut(),
|
||||||
|
next: core::ptr::null(),
|
||||||
|
}; 32],
|
||||||
|
}),
|
||||||
|
desc: UnsafeCell::new(RingDescriptors {
|
||||||
|
d: [DmaDescriptor {
|
||||||
|
xfercfg: 0,
|
||||||
|
src_end: core::ptr::null(),
|
||||||
|
dst_end: core::ptr::null_mut(),
|
||||||
|
next: core::ptr::null(),
|
||||||
|
}; N],
|
||||||
|
}),
|
||||||
|
slots: UnsafeCell::new([[0u8; MAX_SLOT_BYTES]; N]),
|
||||||
|
slot_bytes: MAX_SLOT_BYTES,
|
||||||
|
word_bytes,
|
||||||
|
write_slot: UnsafeCell::new(0),
|
||||||
|
write_off: UnsafeCell::new(0),
|
||||||
|
produced: AtomicUsize::new(0),
|
||||||
|
consumed: AtomicUsize::new(0),
|
||||||
|
safety_gap: 1,
|
||||||
|
produced_bytes: AtomicUsize::new(0),
|
||||||
|
consumed_bytes: AtomicUsize::new(0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Optional: adjust safety gap (defaults to 1 empty slot).
|
||||||
|
pub fn set_safety_gap(&mut self, gap_slots: usize) {
|
||||||
|
self.safety_gap = gap_slots.min(N);
|
||||||
|
}
|
||||||
|
pub fn slot_size(&self) -> usize {
|
||||||
|
self.slot_bytes
|
||||||
|
}
|
||||||
|
pub fn set_slot_size(&mut self, slot_bytes: usize) -> Result<(), ConfigError> {
|
||||||
|
if slot_bytes == 0 {
|
||||||
|
return Err(ConfigError::SlotTooSmall);
|
||||||
|
}
|
||||||
|
if slot_bytes > MAX_SLOT_BYTES {
|
||||||
|
return Err(ConfigError::SlotTooLarge);
|
||||||
|
}
|
||||||
|
if slot_bytes % self.word_bytes != 0 {
|
||||||
|
return Err(ConfigError::SlotNotAligned);
|
||||||
|
}
|
||||||
|
self.slot_bytes = slot_bytes;
|
||||||
|
self.reset_producer();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Producer: copy into ring; commits whole slots; reports overflow by returning dropped bytes.
|
||||||
|
pub fn push(&self, mut data: &[u8]) -> PushResult {
|
||||||
|
let mut written = 0usize;
|
||||||
|
|
||||||
|
let write_slot = unsafe { &mut *self.write_slot.get() };
|
||||||
|
let write_off = unsafe { &mut *self.write_off.get() };
|
||||||
|
|
||||||
|
let slots = unsafe { &mut *self.slots.get() };
|
||||||
|
defmt::debug!(
|
||||||
|
"produced={} consumed={} fill={}",
|
||||||
|
self.produced(),
|
||||||
|
self.consumed(),
|
||||||
|
self.fill_slots()
|
||||||
|
);
|
||||||
|
while !data.is_empty() {
|
||||||
|
if self.is_full_for_producer() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cap = self.slot_bytes - *write_off;
|
||||||
|
let n = core::cmp::min(cap, data.len());
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let dst = slots[*write_slot].as_mut_ptr().add(*write_off);
|
||||||
|
copy_nonoverlapping(data.as_ptr(), dst, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
*write_off += n;
|
||||||
|
written += n;
|
||||||
|
data = &data[n..];
|
||||||
|
|
||||||
|
if *write_off == self.slot_bytes {
|
||||||
|
// publish completed slot
|
||||||
|
compiler_fence(Ordering::Release);
|
||||||
|
self.produced.fetch_add(1, Ordering::Release);
|
||||||
|
|
||||||
|
*write_slot = (*write_slot + 1) % N;
|
||||||
|
*write_off = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.produced_bytes.fetch_add(written, Ordering::Release);
|
||||||
|
|
||||||
|
PushResult {
|
||||||
|
written,
|
||||||
|
dropped: data.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call from DMA IRQ bookkeeping when a slot has been consumed.
|
||||||
|
pub fn advance_consumed(&self, slots: usize) -> Result<(), DmaError> {
|
||||||
|
let produced = self.produced.load(Ordering::Acquire);
|
||||||
|
let consumed = self.consumed.load(Ordering::Relaxed);
|
||||||
|
if consumed < produced {
|
||||||
|
self.consumed.fetch_add(slots, Ordering::Release);
|
||||||
|
self.consumed_bytes
|
||||||
|
.fetch_add(slots * self.slot_bytes, Ordering::Relaxed);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
defmt::error!("DMA underrun!");
|
||||||
|
Err(DmaError::Underrun)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn produced(&self) -> usize {
|
||||||
|
self.produced.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
pub fn produced_bytes(&self) -> usize {
|
||||||
|
self.produced_bytes.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
pub fn consumed(&self) -> usize {
|
||||||
|
self.consumed.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
pub fn consumed_bytes(&self) -> usize {
|
||||||
|
loop {
|
||||||
|
let consumed_start = self.consumed.load(Ordering::Acquire);
|
||||||
|
|
||||||
|
let reg_1 = self.dma.channel19.xfercfg.read().bits() as usize >> 16 & 0x3ff;
|
||||||
|
let reg_2 = self.dma.channel19.xfercfg.read().bits() as usize >> 16 & 0x3ff;
|
||||||
|
|
||||||
|
let consumed_end = self.consumed.load(Ordering::Acquire);
|
||||||
|
|
||||||
|
if consumed_start == consumed_end && reg_1 == reg_2 {
|
||||||
|
// 1. Map the hardware remaining countdown into a clean byte count
|
||||||
|
let remaining_bytes = if reg_1 == 0x3ff {
|
||||||
|
0 // 0x3FF means all transfers completed, 0 bytes remaining
|
||||||
|
} else {
|
||||||
|
// Formula from NXP manual: (XFERCOUNT + 1) * Data Width
|
||||||
|
(reg_1 + 1) * self.word_bytes
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. Total bytes consumed in this specific active slot
|
||||||
|
let active_slot_consumed = self.slot_bytes - remaining_bytes;
|
||||||
|
|
||||||
|
// 3. Combine with your software index history accumulator
|
||||||
|
return consumed_start * self.slot_bytes + active_slot_consumed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fill_slots(&self) -> usize {
|
||||||
|
self.produced().wrapping_sub(self.consumed())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(&self) {
|
||||||
|
self.init_descriptors();
|
||||||
|
|
||||||
|
// Descriptor table base
|
||||||
|
let desc = unsafe { &*self.desc.get() };
|
||||||
|
let base = self.channel_desc.get() as u32;
|
||||||
|
self.dma.srambase.write(|w| unsafe { w.bits(base) });
|
||||||
|
self.dma
|
||||||
|
.channel19
|
||||||
|
.cfg
|
||||||
|
.write(|w| w.periphreqen().enabled().hwtrigen().disabled());
|
||||||
|
self.dma
|
||||||
|
.channel19
|
||||||
|
.xfercfg
|
||||||
|
.write(|w| unsafe { w.bits(desc.d[0].xfercfg) });
|
||||||
|
|
||||||
|
self.dma.enableclr0.write(|w| unsafe { w.bits(1 << 19) });
|
||||||
|
self.dma.ctrl.write(|w| w.enable().enabled());
|
||||||
|
self.dma.setvalid0.write(|w| unsafe { w.bits(1 << 19) });
|
||||||
|
self.dma.intenset0.write(|w| unsafe { w.bits(1 << 19) });
|
||||||
|
|
||||||
|
self.dma.settrig0.write(|w| unsafe { w.bits(1 << 19) });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&self) {
|
||||||
|
self.dma.enableset0.write(|w| unsafe { w.bits(1 << 19) });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(&self) {
|
||||||
|
self.dma.enableclr0.write(|w| unsafe { w.bits(1 << 19) });
|
||||||
|
nb::block!(if (self.dma.busy0.read().bits() & 1 << 19) == 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(nb::Error::<Infallible>::WouldBlock)
|
||||||
|
});
|
||||||
|
self.dma.abort0.write(|w| unsafe { w.bits(1 << 19) });
|
||||||
|
self.reset_producer();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_producer(&self) {
|
||||||
|
unsafe {
|
||||||
|
*(&mut *self.write_slot.get()) = 0;
|
||||||
|
*(&mut *self.write_off.get()) = 0;
|
||||||
|
}
|
||||||
|
self.produced.store(0, Ordering::Relaxed);
|
||||||
|
self.produced_bytes.store(0, Ordering::Relaxed);
|
||||||
|
self.consumed.store(0, Ordering::Relaxed);
|
||||||
|
self.consumed_bytes.store(0, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_full_for_producer(&self) -> bool {
|
||||||
|
let fill = self.fill_slots();
|
||||||
|
fill >= N.wrapping_sub(self.safety_gap)
|
||||||
|
}
|
||||||
|
fn reset_producer_init_only(&self) {
|
||||||
|
unsafe {
|
||||||
|
*self.write_slot.get() = 0;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
*self.write_off.get() = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.produced.store(0, Ordering::Relaxed);
|
||||||
|
self.consumed.store(0, Ordering::Relaxed);
|
||||||
|
|
||||||
|
self.produced_bytes.store(0, Ordering::Relaxed);
|
||||||
|
self.consumed_bytes.store(0, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_descriptors(&self) {
|
||||||
|
let slots = unsafe { &mut *self.slots.get() };
|
||||||
|
let desc = unsafe { &mut *self.desc.get() };
|
||||||
|
let chan_desc = unsafe { &mut *self.channel_desc.get() };
|
||||||
|
defmt::debug!("slots base: &{:x}", self.slots.get());
|
||||||
|
|
||||||
|
// Pre-fill with silence so underrun replays silence.
|
||||||
|
for i in 0..N {
|
||||||
|
slots[i][..self.slot_bytes].fill(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let transfers = (self.slot_bytes / self.word_bytes) as u32;
|
||||||
|
|
||||||
|
for i in 0..N {
|
||||||
|
let src_start = slots[i].as_ptr() as usize;
|
||||||
|
let src_end = (src_start + self.slot_bytes - self.word_bytes) as *const u8;
|
||||||
|
|
||||||
|
let next = &desc.d[(i + 1) % N] as *const DmaDescriptor;
|
||||||
|
|
||||||
|
desc.d[i] = DmaDescriptor {
|
||||||
|
xfercfg: encode_xfercfg(
|
||||||
|
true, // valid
|
||||||
|
true, // reload
|
||||||
|
false, // swtrig (we use XFERCFG SWTRIG kick)
|
||||||
|
false, // clrtrig
|
||||||
|
true, // intA
|
||||||
|
false, // intB
|
||||||
|
self.word_bytes as u32,
|
||||||
|
1, // src_inc
|
||||||
|
0, // dst_inc
|
||||||
|
transfers,
|
||||||
|
),
|
||||||
|
src_end,
|
||||||
|
dst_end: self.dst_reg,
|
||||||
|
next,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
chan_desc.d[19] = desc.d[0];
|
||||||
|
chan_desc.d[19].xfercfg = 0;
|
||||||
|
|
||||||
|
// reset producer indices + counters (init-only action)
|
||||||
|
self.reset_producer_init_only();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<const N: usize, const MAX_SLOT_BYTES: usize> Sync for DmaRing<N, MAX_SLOT_BYTES> {}
|
||||||
|
|
||||||
|
/// XFERCFG encoding follows the common LPC DMA layout:
|
||||||
|
/// - SETINTA at bit4, SETINTB at bit5
|
||||||
|
/// - WIDTH at bits 9:8
|
||||||
|
/// - SRCINC at bits 13:12
|
||||||
|
/// - DSTINC at bits 15:14
|
||||||
|
/// - XFERCOUNT at bits 25:16
|
||||||
|
/// This layout is shown in LPC DMA examples. [5](https://www.kernel.org/doc/html/latest/core-api/dma-api-howto.html)
|
||||||
|
fn encode_xfercfg(
|
||||||
|
cfgvalid: bool,
|
||||||
|
reload: bool,
|
||||||
|
swtrig: bool,
|
||||||
|
clrtrig: bool,
|
||||||
|
inta: bool,
|
||||||
|
intb: bool,
|
||||||
|
width_bytes: u32,
|
||||||
|
src_inc: u32,
|
||||||
|
dst_inc: u32,
|
||||||
|
transfers: u32,
|
||||||
|
) -> u32 {
|
||||||
|
let width_code = match width_bytes {
|
||||||
|
1 => 0,
|
||||||
|
2 => 1,
|
||||||
|
4 => 2,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let count_field = transfers.saturating_sub(1) & 0x3FF;
|
||||||
|
|
||||||
|
((cfgvalid as u32) << 0)
|
||||||
|
| ((reload as u32) << 1)
|
||||||
|
| ((swtrig as u32) << 2)
|
||||||
|
| ((clrtrig as u32) << 3)
|
||||||
|
| ((inta as u32) << 4)
|
||||||
|
| ((intb as u32) << 5)
|
||||||
|
| ((width_code & 0x3) << 8)
|
||||||
|
| ((src_inc & 0x3) << 12)
|
||||||
|
| ((dst_inc & 0x3) << 14)
|
||||||
|
| (count_field << 16)
|
||||||
|
}
|
||||||
@@ -0,0 +1,308 @@
|
|||||||
|
//! Contains hardware setup unrelated to Usb Audio Class implementation
|
||||||
|
|
||||||
|
use crate::Syscon;
|
||||||
|
use crate::hal;
|
||||||
|
use crate::{MCLK_FREQ, SAMPLE_RATE, pac};
|
||||||
|
|
||||||
|
use core::cell::UnsafeCell;
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
|
use defmt::{debug, info};
|
||||||
|
use hal::{
|
||||||
|
Enabled, Iocon, Pin,
|
||||||
|
drivers::pins,
|
||||||
|
traits::wg::digital::v2::{OutputPin, ToggleableOutputPin},
|
||||||
|
typestates::pin::{gpio::direction::Output, state::Gpio},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) struct PllConstants {
|
||||||
|
pub m: u16, // 1-65535
|
||||||
|
pub n: u8, // 1-255
|
||||||
|
pub p: u8, // 1-31
|
||||||
|
pub selp: u8, // 5 bits
|
||||||
|
pub seli: u8, // 6 bits
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PllConstants {
|
||||||
|
pub(crate) const fn new(n: u8, m: u16, p: u8) -> Self {
|
||||||
|
assert!(n != 0, "1 <= N <= 255");
|
||||||
|
assert!(m != 0, "1 <= M <= 65535");
|
||||||
|
assert!(p != 0 && p <= 31, "1 <= P <= 31");
|
||||||
|
|
||||||
|
// Following ripped from lpc55-hal and made const
|
||||||
|
// UM 4.6.6.3.2
|
||||||
|
let selp = {
|
||||||
|
let v = (m >> 2) + 1;
|
||||||
|
if v < 31 { v } else { 31 }
|
||||||
|
} as u8;
|
||||||
|
|
||||||
|
let seli = {
|
||||||
|
let v = match m {
|
||||||
|
m if m >= 8000 => 1,
|
||||||
|
m if m >= 122 => 8000 / m,
|
||||||
|
_ => 2 * (m >> 2) + 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
if v < 63 { v } else { 63 }
|
||||||
|
} as u8;
|
||||||
|
// let seli = min(2*(m >> 2) + 3, 63);
|
||||||
|
Self {
|
||||||
|
n,
|
||||||
|
m,
|
||||||
|
p,
|
||||||
|
selp,
|
||||||
|
seli,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl defmt::Format for PllConstants {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
let factor = f32::from(self.m) / (f32::from(self.n) * 2.0 * f32::from(self.p));
|
||||||
|
|
||||||
|
defmt::write!(
|
||||||
|
fmt,
|
||||||
|
"m: {} n: {} p: {} selp: {} seli: {} fout: fin * {}",
|
||||||
|
self.m,
|
||||||
|
self.n,
|
||||||
|
self.p,
|
||||||
|
self.selp,
|
||||||
|
self.seli,
|
||||||
|
factor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fo = M/(N*2*P) * Fin
|
||||||
|
// Fo = 3072/(125*2*8) * 16MHz = 24.576MHz
|
||||||
|
const AUDIO_PLL: PllConstants = PllConstants::new(125, 3072, 8);
|
||||||
|
|
||||||
|
// Set PLL0 to 24.576MHz, start, and wait for lock
|
||||||
|
// This is not exposed by lpc55-hal, unfortunately. Copy their implementation here.
|
||||||
|
pub(crate) fn init_audio_pll() {
|
||||||
|
let syscon = unsafe { &*pac::SYSCON::ptr() };
|
||||||
|
let pmc = unsafe { &*pac::PMC::ptr() };
|
||||||
|
let anactrl = unsafe { &*pac::ANACTRL::ptr() };
|
||||||
|
|
||||||
|
debug!("start clk_in");
|
||||||
|
pmc.pdruncfg0
|
||||||
|
.modify(|_, w| w.pden_xtal32m().poweredon().pden_ldoxo32m().poweredon());
|
||||||
|
syscon.clock_ctrl.modify(|_, w| w.clkin_ena().enable());
|
||||||
|
anactrl
|
||||||
|
.xo32m_ctrl
|
||||||
|
.modify(|_, w| w.enable_system_clk_out().enable());
|
||||||
|
|
||||||
|
debug!("init pll0: {}", AUDIO_PLL);
|
||||||
|
pmc.pdruncfg0
|
||||||
|
.modify(|_, w| w.pden_pll0().poweredoff().pden_pll0_sscg().poweredoff());
|
||||||
|
syscon.pll0clksel.write(|w| w.sel().enum_0x1()); // clk_in
|
||||||
|
syscon.pll0ctrl.write(|w| unsafe {
|
||||||
|
w.clken()
|
||||||
|
.enable()
|
||||||
|
.seli()
|
||||||
|
.bits(AUDIO_PLL.seli)
|
||||||
|
.selp()
|
||||||
|
.bits(AUDIO_PLL.selp)
|
||||||
|
});
|
||||||
|
|
||||||
|
syscon
|
||||||
|
.pll0ndec
|
||||||
|
.write(|w| unsafe { w.ndiv().bits(AUDIO_PLL.n) });
|
||||||
|
syscon.pll0ndec.write(|w| unsafe {
|
||||||
|
w.ndiv().bits(AUDIO_PLL.n).nreq().set_bit() // latch
|
||||||
|
});
|
||||||
|
|
||||||
|
syscon
|
||||||
|
.pll0pdec
|
||||||
|
.write(|w| unsafe { w.pdiv().bits(AUDIO_PLL.p) });
|
||||||
|
syscon.pll0pdec.write(|w| unsafe {
|
||||||
|
w.pdiv().bits(AUDIO_PLL.p).preq().set_bit() // latch
|
||||||
|
});
|
||||||
|
|
||||||
|
syscon.pll0sscg0.write(|w| unsafe { w.md_lbs().bits(0) });
|
||||||
|
|
||||||
|
syscon
|
||||||
|
.pll0sscg1
|
||||||
|
.write(|w| unsafe { w.mdiv_ext().bits(AUDIO_PLL.m).sel_ext().set_bit() });
|
||||||
|
syscon.pll0sscg1.write(|w| unsafe {
|
||||||
|
w.mdiv_ext()
|
||||||
|
.bits(AUDIO_PLL.m)
|
||||||
|
.sel_ext()
|
||||||
|
.set_bit()
|
||||||
|
.mreq()
|
||||||
|
.set_bit() // latch
|
||||||
|
.md_req()
|
||||||
|
.set_bit() // latch
|
||||||
|
});
|
||||||
|
|
||||||
|
pmc.pdruncfg0
|
||||||
|
.modify(|_, w| w.pden_pll0().poweredon().pden_pll0_sscg().poweredon());
|
||||||
|
info!("pll0 wait for lock");
|
||||||
|
let mut i = 0usize;
|
||||||
|
while syscon.pll0stat.read().lock().bit_is_clear() {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
info!("pll0 locked after {} loops", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct I2sTx {
|
||||||
|
pub i2s: pac::I2S7,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_i2s(mut fc7: pac::FLEXCOMM7, i2s7: pac::I2S7, syscon: &mut Syscon) -> I2sTx {
|
||||||
|
defmt::debug!("init i2s");
|
||||||
|
syscon.reset(&mut fc7);
|
||||||
|
syscon.enable_clock(&mut fc7);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
pac::IOCON::ptr().as_ref().unwrap().pio1_31.modify(|_, w| {
|
||||||
|
w.func()
|
||||||
|
.alt1()
|
||||||
|
.mode()
|
||||||
|
.inactive()
|
||||||
|
.slew()
|
||||||
|
.fast()
|
||||||
|
.invert()
|
||||||
|
.disabled()
|
||||||
|
.digimode()
|
||||||
|
.digital()
|
||||||
|
.od()
|
||||||
|
.normal()
|
||||||
|
});
|
||||||
|
pac::SYSCON::ptr()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.fcclksel7()
|
||||||
|
.modify(|_, w| w.sel().enum_0x5()); // MCLK
|
||||||
|
pac::SYSCON::ptr()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.mclkclksel
|
||||||
|
.modify(|_, w| w.sel().enum_0x1()); // PLL0
|
||||||
|
pac::SYSCON::ptr()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.mclkdiv
|
||||||
|
.modify(|_, w| w.div().bits(1).halt().run().reset().released()); // div by 2 = PLL0 fout / 2 = 12.288MHz, max for WM8904 @ 96k
|
||||||
|
pac::SYSCON::ptr()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.mclkio
|
||||||
|
.modify(|_, w| w.mclkio().output());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Select I2S TX function
|
||||||
|
fc7.pselid.write(|w| w.persel().i2s_transmit());
|
||||||
|
|
||||||
|
let regs = i2s7;
|
||||||
|
|
||||||
|
// Enable TX FIFO only
|
||||||
|
regs.fifocfg.modify(|_, w| {
|
||||||
|
w.enabletx()
|
||||||
|
.enabled()
|
||||||
|
.enablerx()
|
||||||
|
.disabled()
|
||||||
|
.dmatx()
|
||||||
|
.disabled()
|
||||||
|
.txi2se0()
|
||||||
|
.zero()
|
||||||
|
});
|
||||||
|
regs.fifotrig.modify(|_, w| unsafe { w.txlvl().bits(6) });
|
||||||
|
|
||||||
|
// Flush
|
||||||
|
regs.fifocfg.modify(|_, w| w.emptytx().set_bit());
|
||||||
|
|
||||||
|
regs.cfg2
|
||||||
|
.modify(|_, w| unsafe { w.position().bits(0).framelen().bits(63) }); // framelen = 64
|
||||||
|
|
||||||
|
let bclk_div = (MCLK_FREQ / SAMPLE_RATE / 64) as u16;
|
||||||
|
regs.div
|
||||||
|
.modify(|_, w| unsafe { w.div().bits(bclk_div - 1) }); // Clock source is MCLK (12.288MHz) / 4 = 3MHz
|
||||||
|
|
||||||
|
// Config
|
||||||
|
regs.cfg1.modify(|_, w| unsafe {
|
||||||
|
w.mstslvcfg()
|
||||||
|
.normal_master()
|
||||||
|
.onechannel()
|
||||||
|
.dual_channel()
|
||||||
|
.datalen()
|
||||||
|
.bits(31)
|
||||||
|
.mainenable()
|
||||||
|
.enabled()
|
||||||
|
.mode()
|
||||||
|
.classic_mode()
|
||||||
|
.datapause()
|
||||||
|
.normal()
|
||||||
|
});
|
||||||
|
|
||||||
|
I2sTx { i2s: regs }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SharedLed<T: OutputPin> {
|
||||||
|
inner: UnsafeCell<T>,
|
||||||
|
}
|
||||||
|
unsafe impl<T: OutputPin> Sync for SharedLed<T> {}
|
||||||
|
impl<T: OutputPin> SharedLed<T> {
|
||||||
|
pub fn new(inner: T) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: UnsafeCell::new(inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn on(&self) {
|
||||||
|
unsafe {
|
||||||
|
(*self.inner.get()).set_low().ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn off(&self) {
|
||||||
|
unsafe {
|
||||||
|
(*self.inner.get()).set_high().ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: OutputPin + ToggleableOutputPin> SharedLed<T> {
|
||||||
|
pub fn toggle(&self) {
|
||||||
|
unsafe {
|
||||||
|
(*self.inner.get()).toggle().ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RedLed = Pin<pins::Pio1_6, Gpio<Output>>;
|
||||||
|
type GreenLed = Pin<pins::Pio1_7, Gpio<Output>>;
|
||||||
|
type BlueLed = Pin<pins::Pio1_4, Gpio<Output>>;
|
||||||
|
pub static RED_LED: MaybeUninit<SharedLed<RedLed>> = MaybeUninit::uninit();
|
||||||
|
pub static GREEN_LED: MaybeUninit<SharedLed<GreenLed>> = MaybeUninit::uninit();
|
||||||
|
pub static BLUE_LED: MaybeUninit<SharedLed<BlueLed>> = MaybeUninit::uninit();
|
||||||
|
|
||||||
|
pub fn init_leds(iocon: &mut Iocon<Enabled>, gpio: &mut hal::Gpio<Enabled>) {
|
||||||
|
let red_led = SharedLed::new(
|
||||||
|
pins::Pio1_6::take()
|
||||||
|
.unwrap()
|
||||||
|
.into_gpio_pin(iocon, gpio)
|
||||||
|
.into_output_low(),
|
||||||
|
);
|
||||||
|
let green_led = SharedLed::new(
|
||||||
|
pins::Pio1_7::take()
|
||||||
|
.unwrap()
|
||||||
|
.into_gpio_pin(iocon, gpio)
|
||||||
|
.into_output_low(),
|
||||||
|
);
|
||||||
|
let blue_led = SharedLed::new(
|
||||||
|
pins::Pio1_4::take()
|
||||||
|
.unwrap()
|
||||||
|
.into_gpio_pin(iocon, gpio)
|
||||||
|
.into_output_low(),
|
||||||
|
);
|
||||||
|
unsafe {
|
||||||
|
core::ptr::write(RED_LED.as_ptr() as *mut SharedLed<RedLed>, red_led);
|
||||||
|
core::ptr::write(GREEN_LED.as_ptr() as *mut SharedLed<GreenLed>, green_led);
|
||||||
|
core::ptr::write(BLUE_LED.as_ptr() as *mut SharedLed<BlueLed>, blue_led);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn red_led() -> &'static SharedLed<RedLed> {
|
||||||
|
unsafe { &*RED_LED.as_ptr() }
|
||||||
|
}
|
||||||
|
pub fn green_led() -> &'static SharedLed<GreenLed> {
|
||||||
|
unsafe { &*GREEN_LED.as_ptr() }
|
||||||
|
}
|
||||||
|
pub fn blue_led() -> &'static SharedLed<BlueLed> {
|
||||||
|
unsafe { &*BLUE_LED.as_ptr() }
|
||||||
|
}
|
||||||
@@ -0,0 +1,401 @@
|
|||||||
|
//! DMA based audio output example for the LPCXpresso55S28 demo board
|
||||||
|
//!
|
||||||
|
//! Uses the onboard WM8904 DAC at 96KHz. Clock is generated by PLL0. Simple proportional feedback is implemented.
|
||||||
|
//!
|
||||||
|
//! USB walks around a static ring of slots, filling them as data comes in from
|
||||||
|
//! the host. DMA chases it, filling the I2S FIFO as it drains to the DAC.
|
||||||
|
//! Feedback ensures that the host doesn't overrun or underrun the ring.
|
||||||
|
//!
|
||||||
|
//! This implementation is more suitable for real use than the interrupt-based
|
||||||
|
//! example, but it is still missing many niceties and behaves worse in
|
||||||
|
//! anomalous situations since the DMA just keeps trucking over the ring
|
||||||
|
//! regardless of the data validity.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[cfg(all(feature = "usbfs", feature = "usbhs"))]
|
||||||
|
compile_error!("Choose one USB peripheral, usbfs and usbhs cannot be used together");
|
||||||
|
|
||||||
|
extern crate panic_probe;
|
||||||
|
#[defmt::panic_handler]
|
||||||
|
fn panic() -> ! {
|
||||||
|
panic_probe::hard_fault()
|
||||||
|
}
|
||||||
|
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use defmt::debug;
|
||||||
|
use defmt_rtt as _;
|
||||||
|
use hal::raw as pac;
|
||||||
|
use hal::{
|
||||||
|
Syscon,
|
||||||
|
drivers::{Timer, UsbBus, pins},
|
||||||
|
prelude::*,
|
||||||
|
time::Hertz,
|
||||||
|
};
|
||||||
|
use lpc55_hal as hal;
|
||||||
|
use pac::interrupt;
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
use usb_device::{
|
||||||
|
bus::{self},
|
||||||
|
device::{StringDescriptors, UsbDeviceBuilder, UsbVidPid},
|
||||||
|
};
|
||||||
|
use usbd_uac2::TerminalConfig;
|
||||||
|
use usbd_uac2::{
|
||||||
|
self, AudioHandler, ClockSource, RangeEntry, UsbAudioClassConfig, UsbIsochronousFeedback,
|
||||||
|
UsbSpeed, constants::FunctionCode, descriptors::ClockType,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::dma::DmaRing;
|
||||||
|
use crate::hw::{I2sTx, blue_led, green_led, red_led};
|
||||||
|
|
||||||
|
mod dma;
|
||||||
|
mod hw;
|
||||||
|
mod wm8904;
|
||||||
|
|
||||||
|
const CODEC_I2C_ADDR: u8 = 0b0011010;
|
||||||
|
const MCLK_FREQ: u32 = 12288000;
|
||||||
|
|
||||||
|
const SAMPLE_RATE: u32 = 96000;
|
||||||
|
const USB_FRAME_RATE: u32 = if cfg!(feature = "usbhs") { 8000 } else { 1000 };
|
||||||
|
|
||||||
|
//latency ≈ (current_fill × FRAMES_PER_SLOT)
|
||||||
|
// + FRAMES_PER_SLOT/2 - average DMA transfer position
|
||||||
|
// + 8 - FIFO depth @ 32-bit samples
|
||||||
|
// with example values, ~2.3ms
|
||||||
|
const BYTES_PER_SAMPLE: usize = 4; // 32 bit samples
|
||||||
|
const BYTES_PER_FRAME: usize = BYTES_PER_SAMPLE * 2; // 2 channels
|
||||||
|
const FRAMES_PER_SLOT: usize = SAMPLE_RATE as usize / 2000; // run the DMA at 2khz
|
||||||
|
const BYTES_PER_SLOT: usize = FRAMES_PER_SLOT * BYTES_PER_FRAME;
|
||||||
|
const N_SLOTS: usize = 8;
|
||||||
|
const FILL_TARGET_BYTES: i32 = (BYTES_PER_SLOT * N_SLOTS) as i32 / 2;
|
||||||
|
|
||||||
|
const LOG_PERIOD: u32 = 1000;
|
||||||
|
|
||||||
|
static DMA_RING: StaticCell<DmaRing<N_SLOTS, BYTES_PER_SLOT>> = StaticCell::new();
|
||||||
|
static mut DMA_RING_REF: Option<&'static DmaRing<N_SLOTS, BYTES_PER_SLOT>> = None;
|
||||||
|
#[inline]
|
||||||
|
fn dma_ring() -> &'static DmaRing<N_SLOTS, BYTES_PER_SLOT> {
|
||||||
|
unsafe { DMA_RING_REF.unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn DMA0() {
|
||||||
|
let dma = unsafe { &*pac::DMA0::ptr() };
|
||||||
|
|
||||||
|
let inta = dma.inta0.read().bits();
|
||||||
|
let err = dma.errint0.read().bits();
|
||||||
|
|
||||||
|
if (err & (1 << 19)) != 0 {
|
||||||
|
let live = dma.channel19.xfercfg.read().bits();
|
||||||
|
|
||||||
|
let desc = unsafe { &*dma_ring().channel_desc.get() };
|
||||||
|
let mem = desc.d[19];
|
||||||
|
defmt::error!(
|
||||||
|
"DMA error ch19: live={=u32:08x} INTA={=u32:x} ERR={=u32:x}\n desc: {}",
|
||||||
|
live,
|
||||||
|
inta,
|
||||||
|
err,
|
||||||
|
mem
|
||||||
|
);
|
||||||
|
red_led().on();
|
||||||
|
dma.errint0.write(|w| unsafe { w.bits(1 << 19) });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inta & (1 << 19)) != 0 {
|
||||||
|
dma.inta0.write(|w| unsafe { w.bits(1 << 19) });
|
||||||
|
if dma_ring().advance_consumed(1).is_err() {
|
||||||
|
red_led().on();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Audio<'a, const N: usize, const MAX_SLOT_BYTES: usize> {
|
||||||
|
running: AtomicBool,
|
||||||
|
i2s: I2sTx,
|
||||||
|
dma: &'a DmaRing<N, MAX_SLOT_BYTES>,
|
||||||
|
log_counter: u32,
|
||||||
|
}
|
||||||
|
impl<const N: usize, const MAX_SLOT_BYTES: usize> Audio<'_, N, MAX_SLOT_BYTES> {
|
||||||
|
const RATES: [RangeEntry<u32>; 1] = [RangeEntry::new_fixed(SAMPLE_RATE)];
|
||||||
|
fn start(&mut self) {
|
||||||
|
red_led().off(); // clear any dma error
|
||||||
|
self.running.store(false, Ordering::Relaxed);
|
||||||
|
|
||||||
|
defmt::info!("playback armed (DMA)");
|
||||||
|
|
||||||
|
let i2s = &self.i2s.i2s;
|
||||||
|
i2s.fifotrig
|
||||||
|
.modify(|_, w| unsafe { w.txlvl().bits(6).txlvlena().enabled() });
|
||||||
|
|
||||||
|
// Enable TX FIFO
|
||||||
|
i2s.fifocfg
|
||||||
|
.modify(|_, w| w.enabletx().enabled().dmatx().enabled());
|
||||||
|
dma_ring().init();
|
||||||
|
// Enable DMA interrupt (channel 19)
|
||||||
|
unsafe { pac::NVIC::unmask(pac::Interrupt::DMA0) };
|
||||||
|
|
||||||
|
green_led().on();
|
||||||
|
}
|
||||||
|
fn stop(&self) {
|
||||||
|
// If we don't disable interrupts while stopped, we will underflow constantly and continuously refill the fifo with 0s
|
||||||
|
// We could actually stop the I2S here, but sometimes that makes the DAC misbehave. The peripheral is configured to send
|
||||||
|
// 0s when the FIFO is empty, so this is fine.
|
||||||
|
pac::NVIC::mask(pac::Interrupt::DMA0);
|
||||||
|
self.running.store(false, Ordering::Relaxed);
|
||||||
|
dma_ring().stop();
|
||||||
|
defmt::info!("playback stopped");
|
||||||
|
green_led().off();
|
||||||
|
blue_led().off();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<const N: usize, const MAX_SLOT_BYTES: usize, B: bus::UsbBus> AudioHandler<'_, B>
|
||||||
|
for Audio<'_, N, MAX_SLOT_BYTES>
|
||||||
|
{
|
||||||
|
fn alternate_setting_changed(&mut self, _terminal: usb_device::UsbDirection, alt_setting: u8) {
|
||||||
|
// alt setting 0 means stopped
|
||||||
|
match alt_setting {
|
||||||
|
0 => self.stop(),
|
||||||
|
1 => self.start(),
|
||||||
|
_ => defmt::error!("unexpected alt setting {}", alt_setting),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn audio_data_rx(
|
||||||
|
&mut self,
|
||||||
|
ep: &usb_device::endpoint::Endpoint<'_, B, usb_device::endpoint::Out>,
|
||||||
|
) {
|
||||||
|
// Buffer must fit 125us of audio data (based on how `usbd_uac2` sets up the descriptors).
|
||||||
|
// Buffer must have room for one additional frame in case the host clock runs slower than the device.
|
||||||
|
let mut buf = [0; (SAMPLE_RATE.div_ceil(USB_FRAME_RATE) + 1) as usize * BYTES_PER_FRAME];
|
||||||
|
|
||||||
|
let len = match ep.read(&mut buf) {
|
||||||
|
Ok(len) => len,
|
||||||
|
Err(_) => {
|
||||||
|
defmt::error!("usb error in rx callback");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let buf = &buf[..len];
|
||||||
|
let res = self.dma.push(buf);
|
||||||
|
|
||||||
|
if res.dropped != 0 {
|
||||||
|
// Overflow: some or all bytes couldn't be queued.
|
||||||
|
blue_led().toggle();
|
||||||
|
defmt::error!(
|
||||||
|
"overflowed dma ring, asked {}, wrote {}, dropped {}",
|
||||||
|
buf.len(),
|
||||||
|
res.written,
|
||||||
|
res.dropped
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// If we're not running yet, wait until we reach 50% full then enable DMA requests
|
||||||
|
if !self.running.load(Ordering::Relaxed) && self.dma.fill_slots() >= (N_SLOTS / 2) {
|
||||||
|
defmt::info!(
|
||||||
|
"buffer warmed ({} slots) starting playback",
|
||||||
|
self.dma.fill_slots()
|
||||||
|
);
|
||||||
|
self.dma.run();
|
||||||
|
self.running.store(true, Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provide rate feedback to the host. P-only is stable and works fine, with
|
||||||
|
/// most hosts. The host can either filter it internally or treat it
|
||||||
|
/// instantaneously and send more data specifically when the error gets
|
||||||
|
/// large; we will absorb reasonable clock drifts with our ring buffer.
|
||||||
|
fn feedback(&mut self, nominal_rate: UsbIsochronousFeedback) -> Option<UsbIsochronousFeedback> {
|
||||||
|
// Don't want to signal an absurd rate when not consuming; let the
|
||||||
|
// buffer fill before starting feedback.
|
||||||
|
if !self.running.load(Ordering::Acquire) {
|
||||||
|
return Some(nominal_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
let produced_bytes = self.dma.produced_bytes();
|
||||||
|
let consumed_bytes = self.dma.consumed_bytes();
|
||||||
|
|
||||||
|
if produced_bytes < consumed_bytes || produced_bytes == 0 {
|
||||||
|
defmt::error!("[fb] dma underrun detected!");
|
||||||
|
red_led().on();
|
||||||
|
return Some(nominal_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
let current_bytes = (produced_bytes - consumed_bytes) as i32;
|
||||||
|
// normalize error wrt. frame size etc.
|
||||||
|
let error_permille = ((current_bytes - FILL_TARGET_BYTES) * 1000) / FILL_TARGET_BYTES;
|
||||||
|
|
||||||
|
let nominal_v = nominal_rate.to_u32_12_13() as i32;
|
||||||
|
|
||||||
|
// 0.2% which is a huge clock error
|
||||||
|
let max_allowed_deviation = nominal_v / 500;
|
||||||
|
|
||||||
|
let p_term = -(error_permille * nominal_v) / 256000; // this works reasonably well to keep the buffer
|
||||||
|
let i_term = 0; // placeholder
|
||||||
|
|
||||||
|
let mut v = nominal_v + p_term + i_term;
|
||||||
|
v = v.clamp(
|
||||||
|
nominal_v - max_allowed_deviation,
|
||||||
|
nominal_v + max_allowed_deviation,
|
||||||
|
);
|
||||||
|
self.log_counter += 1;
|
||||||
|
if self.log_counter.is_multiple_of(LOG_PERIOD) {
|
||||||
|
defmt::info!(
|
||||||
|
"fill:{}% err_pm:{} p:{} i:{} fb_delta:{} fb:{=u32:x}",
|
||||||
|
(current_bytes * 100) / (N_SLOTS * BYTES_PER_SLOT) as i32,
|
||||||
|
error_permille,
|
||||||
|
p_term,
|
||||||
|
i_term,
|
||||||
|
v - nominal_v,
|
||||||
|
v as u32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(UsbIsochronousFeedback::new(v as u32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize, const MAX_SLOT_BYTES: usize> ClockSource for Audio<'_, N, MAX_SLOT_BYTES> {
|
||||||
|
const CLOCK_TYPE: usbd_uac2::descriptors::ClockType = ClockType::InternalFixed;
|
||||||
|
const SOF_SYNC: bool = false;
|
||||||
|
fn sample_rate(&self) -> u32 {
|
||||||
|
Self::RATES[0].min
|
||||||
|
}
|
||||||
|
fn sample_rates(
|
||||||
|
&self,
|
||||||
|
) -> core::result::Result<&[usbd_uac2::RangeEntry<u32>], usbd_uac2::UsbAudioClassError> {
|
||||||
|
Ok(&Self::RATES)
|
||||||
|
}
|
||||||
|
fn clock_validity(&self) -> core::result::Result<bool, usbd_uac2::UsbAudioClassError> {
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let hal = hal::new();
|
||||||
|
|
||||||
|
let mut anactrl = hal.anactrl;
|
||||||
|
let mut pmc = hal.pmc;
|
||||||
|
let mut syscon = hal.syscon;
|
||||||
|
|
||||||
|
let mut gpio = hal.gpio.enabled(&mut syscon);
|
||||||
|
let mut iocon = hal.iocon.enabled(&mut syscon);
|
||||||
|
|
||||||
|
debug!("start");
|
||||||
|
|
||||||
|
hw::init_leds(&mut iocon, &mut gpio);
|
||||||
|
|
||||||
|
debug!("iocon");
|
||||||
|
let usb0_vbus_pin = pins::Pio0_22::take()
|
||||||
|
.unwrap()
|
||||||
|
.into_usb0_vbus_pin(&mut iocon);
|
||||||
|
let codec_i2c_pins = (
|
||||||
|
pins::Pio1_20::take().unwrap().into_i2c4_scl_pin(&mut iocon),
|
||||||
|
pins::Pio1_21::take().unwrap().into_i2c4_sda_pin(&mut iocon),
|
||||||
|
);
|
||||||
|
// We can initialize and iocon these, but there is no peripheral driver, so they do not get used
|
||||||
|
let _codec_i2s_pins = (
|
||||||
|
pins::Pio0_21::take().unwrap().into_spi7_sck_pin(&mut iocon),
|
||||||
|
pins::Pio0_20::take().unwrap().into_i2s7_sda_pin(&mut iocon),
|
||||||
|
pins::Pio0_19::take().unwrap().into_i2s7_ws_pin(&mut iocon),
|
||||||
|
pins::Pio1_31::take().unwrap(), // MCLK
|
||||||
|
);
|
||||||
|
|
||||||
|
debug!("clocks");
|
||||||
|
let clocks = hal::ClockRequirements::default()
|
||||||
|
.system_frequency(96.MHz())
|
||||||
|
.configure(&mut anactrl, &mut pmc, &mut syscon)
|
||||||
|
.unwrap();
|
||||||
|
let mut usb_delay_timer = Timer::new(
|
||||||
|
hal.ctimer
|
||||||
|
.0
|
||||||
|
.enabled(&mut syscon, clocks.support_1mhz_fro_token().unwrap()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Start PLL0 at 24.576MHz as the audio clock. The FRO cannot evenly divide
|
||||||
|
// any common audio frequencies and is not particularly stable anyway.
|
||||||
|
hw::init_audio_pll();
|
||||||
|
|
||||||
|
debug!("peripherals");
|
||||||
|
|
||||||
|
let i2c_peripheral = hal
|
||||||
|
.flexcomm
|
||||||
|
.4
|
||||||
|
.enabled_as_i2c(&mut syscon, &clocks.support_flexcomm_token().unwrap());
|
||||||
|
let mut i2c_bus = I2cMaster::new(
|
||||||
|
i2c_peripheral,
|
||||||
|
codec_i2c_pins,
|
||||||
|
Hertz::try_from(400.kHz()).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let i2s_peripheral = {
|
||||||
|
let fc7 = hal.flexcomm.7.release();
|
||||||
|
hw::init_i2s(fc7.0, fc7.2, &mut syscon)
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "usbhs")]
|
||||||
|
let (usb_speed, usb_peripheral) = (
|
||||||
|
UsbSpeed::High,
|
||||||
|
hal.usbhs.enabled_as_device(
|
||||||
|
&mut anactrl,
|
||||||
|
&mut pmc,
|
||||||
|
&mut syscon,
|
||||||
|
&mut usb_delay_timer,
|
||||||
|
clocks.support_usbhs_token().unwrap(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
#[cfg(feature = "usbfs")]
|
||||||
|
let (usb_speed, usb_peripheral) = (
|
||||||
|
UsbSpeed::Full,
|
||||||
|
hal.usbfs.enabled_as_device(
|
||||||
|
&mut anactrl,
|
||||||
|
&mut pmc,
|
||||||
|
&mut syscon,
|
||||||
|
clocks.support_usbfs_token().unwrap(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
let usb_bus = UsbBus::new(usb_peripheral, usb0_vbus_pin);
|
||||||
|
|
||||||
|
defmt::debug!("codec init");
|
||||||
|
wm8904::init_codec(&mut i2c_bus);
|
||||||
|
|
||||||
|
defmt::debug!("dma init");
|
||||||
|
let i2s_dma_addr = &i2s_peripheral.i2s.fifowr as *const _ as *mut u32;
|
||||||
|
let dma =
|
||||||
|
DmaRing::<N_SLOTS, BYTES_PER_SLOT>::new(hal.dma.release(), &mut syscon, i2s_dma_addr, 4)
|
||||||
|
.unwrap();
|
||||||
|
let dma_ref = DMA_RING.init(dma);
|
||||||
|
unsafe { DMA_RING_REF = Some(dma_ref) };
|
||||||
|
|
||||||
|
let mut audio = Audio {
|
||||||
|
i2s: i2s_peripheral,
|
||||||
|
dma: dma_ring(),
|
||||||
|
running: AtomicBool::new(false),
|
||||||
|
log_counter: 0,
|
||||||
|
};
|
||||||
|
defmt::debug!("usb init");
|
||||||
|
let config = UsbAudioClassConfig::new(usb_speed, FunctionCode::IoBox, &mut audio)
|
||||||
|
.with_output_config(TerminalConfig::builder().base_id(2).build());
|
||||||
|
let mut uac2 = config.build(&usb_bus).unwrap();
|
||||||
|
|
||||||
|
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x1209, 0xcc1d))
|
||||||
|
.composite_with_iads()
|
||||||
|
.strings(&[StringDescriptors::default()
|
||||||
|
.manufacturer("Generic")
|
||||||
|
.product("usbd_uac2 device")
|
||||||
|
.serial_number("123456789")])
|
||||||
|
.unwrap()
|
||||||
|
.max_packet_size_0(64)
|
||||||
|
.unwrap()
|
||||||
|
.device_class(0xef)
|
||||||
|
.device_sub_class(0x02)
|
||||||
|
.device_protocol(0x01)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
defmt::info!("main loop");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
usb_dev.poll(&mut [&mut uac2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
use cortex_m::prelude::{_embedded_hal_blocking_i2c_Write, _embedded_hal_blocking_i2c_WriteRead};
|
||||||
|
use defmt::warn;
|
||||||
|
|
||||||
|
use crate::{BYTES_PER_FRAME, CODEC_I2C_ADDR, MCLK_FREQ, SAMPLE_RATE};
|
||||||
|
|
||||||
|
// copied from NXP SDK WM8904_Init
|
||||||
|
pub(crate) fn init_codec<T>(i2c: &mut T)
|
||||||
|
where
|
||||||
|
T: _embedded_hal_blocking_i2c_WriteRead + _embedded_hal_blocking_i2c_Write,
|
||||||
|
{
|
||||||
|
let mut buf = [0u8; 2];
|
||||||
|
match i2c.write_read(CODEC_I2C_ADDR, &[0], &mut buf) {
|
||||||
|
Ok(_) => {
|
||||||
|
let chip_id = ((buf[0] as u16) << 8) | buf[1] as u16;
|
||||||
|
defmt::info!("Read chip ID: {:x}", chip_id)
|
||||||
|
}
|
||||||
|
Err(_) => defmt::error!("Error reading I2C"),
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x16, 0x00, 0x0f]).ok(); // clock rates 2 = OPCLK_ENA | CLK_SYS_ENA | CLK_DSP_ENA | TOCLK_ENA
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x6c, 0x01, 0x00]).ok(); // write sequencer 0 ENA
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x6f, 0x01, 0x00]).ok(); // write sequencer 3 START, INDEX=0
|
||||||
|
// wait on write sequencer
|
||||||
|
defmt::debug!("[codec] waiting on write seq");
|
||||||
|
loop {
|
||||||
|
let mut buf = [0; 2];
|
||||||
|
i2c.write_read(CODEC_I2C_ADDR, &[0x70], &mut buf).ok();
|
||||||
|
if buf[1] & 1 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defmt::debug!("[codec] write seq done");
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x14, 0x00, 0x00]).ok(); // clock rates 0
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x0c, 0x00, 0x00]).ok(); // power management 0 = IN PGAs disabled
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x0e, 0x00, 0x03]).ok(); // power management 2 = HPL_PGA_ENA | HPR_PGA_ENA
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x0f, 0x00, 0x00]).ok(); // power management 3 = line outs disabled
|
||||||
|
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x12, 0x00, 0x0c]).ok(); // power management 6 = DACL_ENA | DACR_ENA
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x0a, 0x00, 0x00]).ok(); // analog adc 0 = ADC_OSR128
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x18, 0x00, 0x50]).ok(); // audio if 0 = AIFADCR_SRC | AIFDACR_SRC
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x21, 0x00, 0x40]).ok(); // dac digital 1 = DAC_OSR128
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x2c, 0x00, 0x05]).ok(); // analog lin 0 = 0dB (unmute)
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x2d, 0x00, 0x05]).ok(); // analog rin 0 = 0dB (unmute)
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x39, 0x00, 0x39]).ok(); // analog out1 left = vol=0dB
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x3a, 0x00, 0x39]).ok(); // analog out1 right = vol=0dB
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x3b, 0x00, 0x39]).ok(); // analog out2 left = vol=0dB
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x3c, 0x00, 0x39]).ok(); // analog out2 right = vol=0dB
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x43, 0x00, 0x03]).ok(); // dc server 0 = HPOUTL_ENA | HPOUTR_ENA
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x5a, 0x00, 0xff]).ok(); // analog hp 0 = remove all shorts etc
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x5e, 0x00, 0xff]).ok(); // analog lineout 0 = remove all shorts etc
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x68, 0x00, 0x01]).ok(); // enable class w charge pump
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x62, 0x00, 0x01]).ok(); // enable charge pump
|
||||||
|
let aif_wl = match BYTES_PER_FRAME {
|
||||||
|
4 => 0, // 16 bits per sample
|
||||||
|
8 => 3, // 32 bits per sample
|
||||||
|
_ => {
|
||||||
|
warn!("only handling 16 or 32 bit samples for now");
|
||||||
|
8
|
||||||
|
}
|
||||||
|
};
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x19, 0x00, (aif_wl << 2) | 2])
|
||||||
|
.ok(); // audio if 1 = i2s, aif_wl
|
||||||
|
|
||||||
|
// Calculate sysclk vs. fs ratio. SYSCLK = MCLK
|
||||||
|
let fs_ratio = MCLK_FREQ / SAMPLE_RATE;
|
||||||
|
if !MCLK_FREQ.is_multiple_of(SAMPLE_RATE) {
|
||||||
|
warn!("sample rate should be a multiple of mclk")
|
||||||
|
}
|
||||||
|
let clk_sys_rate: u16 = match fs_ratio {
|
||||||
|
64 => 0,
|
||||||
|
128 => 1,
|
||||||
|
192 => 2,
|
||||||
|
256 => 3,
|
||||||
|
384 => 4,
|
||||||
|
512 => 5,
|
||||||
|
768 => 6,
|
||||||
|
1024 => 7,
|
||||||
|
1408 => 8,
|
||||||
|
1536 => 9,
|
||||||
|
_ => {
|
||||||
|
warn!("unsupport ratio {}", fs_ratio);
|
||||||
|
0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let sample_rate: u16 = match SAMPLE_RATE {
|
||||||
|
r if r < 11025 => 0, // 0-11024
|
||||||
|
r if r < 16000 => 1, // 11025 - 15999
|
||||||
|
r if r < 22050 => 2, // 16000 - 22049
|
||||||
|
r if r < 32000 => 3, // 22050 - 31999
|
||||||
|
r if r < 44100 => 4, // 32000 - 44099
|
||||||
|
_ => 5, // 44100+
|
||||||
|
};
|
||||||
|
let clock_rates_1 = ((clk_sys_rate << 10) | sample_rate).to_be_bytes();
|
||||||
|
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x15, clock_rates_1[0], clock_rates_1[1]])
|
||||||
|
.ok(); // sys clock rate 512fs, sample rate 48
|
||||||
|
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x16, 0x00, 0x0f]).ok(); // clock rates 2 = CLK_SYS_ENA
|
||||||
|
|
||||||
|
// Calculate bclk_div
|
||||||
|
let bits_per_frame = BYTES_PER_FRAME * 8;
|
||||||
|
let bits_per_second = bits_per_frame as u32 * SAMPLE_RATE;
|
||||||
|
let bclk_div = MCLK_FREQ / bits_per_second;
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x1a, 0x00, bclk_div as u8])
|
||||||
|
.ok(); // audio interface 2 = no gpio, bclk_div
|
||||||
|
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x1b, 0x00, 0x00]).ok(); // audio interface 3 = input lrclock
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x3d, 0x00, 0x00]).ok(); // analog out12 zc = play source = dac
|
||||||
|
i2c.write(CODEC_I2C_ADDR, &[0x1e, 0x01, 0xff]).ok(); // dac vol left = update left/right = 0dB
|
||||||
|
}
|
||||||
@@ -16,4 +16,4 @@ rustflags = [
|
|||||||
target = "thumbv8m.main-none-eabihf"
|
target = "thumbv8m.main-none-eabihf"
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
DEFMT_LOG = "debug"
|
DEFMT_LOG = "off"
|
||||||
|
|||||||
Generated
+325
-26
@@ -2,6 +2,15 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomic-polyfill"
|
name = "atomic-polyfill"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
@@ -26,6 +35,17 @@ dependencies = [
|
|||||||
"rustc_version 0.2.3",
|
"rustc_version 0.2.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bbqueue"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68917624e17aad88607cb5a5936f6da9b607c48c711e4e9ed101e7189aed28c2"
|
||||||
|
dependencies = [
|
||||||
|
"const-init",
|
||||||
|
"critical-section",
|
||||||
|
"maitake-sync",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitfield"
|
name = "bitfield"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
@@ -63,6 +83,22 @@ dependencies = [
|
|||||||
"embedded-io",
|
"embedded-io",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.61"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
|
||||||
|
dependencies = [
|
||||||
|
"find-msvc-tools",
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cipher"
|
name = "cipher"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
@@ -73,6 +109,22 @@ dependencies = [
|
|||||||
"inout",
|
"inout",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-init"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4bd422bfb4f24a97243f60b6a4443e63d810c925d8da4bb2d8fde26a7c1d57ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cordyceps"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a"
|
||||||
|
dependencies = [
|
||||||
|
"loom",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-m"
|
name = "cortex-m"
|
||||||
version = "0.7.7"
|
version = "0.7.7"
|
||||||
@@ -214,6 +266,27 @@ dependencies = [
|
|||||||
"num",
|
"num",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "find-msvc-tools"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generator"
|
||||||
|
version = "0.8.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"rustversion",
|
||||||
|
"windows-link",
|
||||||
|
"windows-result",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.7"
|
version = "0.14.7"
|
||||||
@@ -275,16 +348,6 @@ dependencies = [
|
|||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "heapless"
|
|
||||||
version = "0.9.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4"
|
|
||||||
dependencies = [
|
|
||||||
"hash32 0.3.1",
|
|
||||||
"stable_deref_trait",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inout"
|
name = "inout"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
@@ -294,6 +357,18 @@ dependencies = [
|
|||||||
"generic-array 0.14.7",
|
"generic-array 0.14.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.186"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.14"
|
version = "0.4.14"
|
||||||
@@ -320,9 +395,23 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "loom"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"generator",
|
||||||
|
"scoped-tls",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lpc55-hal"
|
name = "lpc55-hal"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
source = "git+https://github.com/ktims/lpc55-hal?branch=main#8dfefd62aff4abd2de535f23107812dda68437be"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"cipher",
|
"cipher",
|
||||||
@@ -354,23 +443,51 @@ dependencies = [
|
|||||||
name = "lpc55s28-evk"
|
name = "lpc55s28-evk"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bbqueue",
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
"defmt 1.0.1",
|
"defmt 1.0.1",
|
||||||
"defmt-rtt",
|
"defmt-rtt",
|
||||||
"embedded-hal 1.0.0",
|
"embedded-hal 1.0.0",
|
||||||
"embedded-io",
|
"embedded-io",
|
||||||
"heapless 0.9.3",
|
|
||||||
"log-to-defmt",
|
"log-to-defmt",
|
||||||
"lpc55-hal",
|
"lpc55-hal",
|
||||||
"nb 1.1.0",
|
|
||||||
"panic-halt",
|
|
||||||
"panic-probe",
|
"panic-probe",
|
||||||
"static_cell",
|
|
||||||
"usb-device",
|
"usb-device",
|
||||||
"usbd-uac2",
|
"usbd-uac2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "maitake-sync"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d77c365d697828821727b9bc09e6bc3c518b8c63804e79e1be5a5ae091a7c5f"
|
||||||
|
dependencies = [
|
||||||
|
"cordyceps",
|
||||||
|
"critical-section",
|
||||||
|
"loom",
|
||||||
|
"mutex-traits",
|
||||||
|
"mycelium-bitfield",
|
||||||
|
"pin-project",
|
||||||
|
"portable-atomic",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchers"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||||
|
dependencies = [
|
||||||
|
"regex-automata",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "modular-bitfield"
|
name = "modular-bitfield"
|
||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
@@ -392,6 +509,18 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mutex-traits"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3929f2b5633d29cf7b6624992e5f3c1e9334f1193423e12d17be4faf678cde3f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mycelium-bitfield"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24e0cc5e2c585acbd15c5ce911dff71e1f4d5313f43345873311c4f5efd741cc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nb"
|
name = "nb"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@@ -407,6 +536,15 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu-ansi-term"
|
||||||
|
version = "0.50.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num"
|
name = "num"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -470,10 +608,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "panic-halt"
|
name = "once_cell"
|
||||||
version = "1.0.0"
|
version = "1.21.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11"
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "panic-probe"
|
name = "panic-probe"
|
||||||
@@ -485,6 +623,32 @@ dependencies = [
|
|||||||
"defmt 1.0.1",
|
"defmt 1.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project"
|
||||||
|
version = "1.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cbf0d9e68100b3a7989b4901972f265cd542e560a3a8a724e1e20322f4d06ce9"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-internal",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-internal"
|
||||||
|
version = "1.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a990e22f43e84855daf260dded30524ef4a9021cc7541c26540500a50b624389"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "portable-atomic"
|
name = "portable-atomic"
|
||||||
version = "1.13.1"
|
version = "1.13.1"
|
||||||
@@ -537,6 +701,23 @@ version = "0.6.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
@@ -561,6 +742,12 @@ version = "1.0.22"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scoped-tls"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@@ -588,6 +775,27 @@ version = "0.7.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin"
|
name = "spin"
|
||||||
version = "0.9.8"
|
version = "0.9.8"
|
||||||
@@ -609,15 +817,6 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "static_cell"
|
|
||||||
version = "2.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0530892bb4fa575ee0da4b86f86c667132a94b74bb72160f58ee5a4afec74c23"
|
|
||||||
dependencies = [
|
|
||||||
"portable-atomic",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.117"
|
version = "2.0.117"
|
||||||
@@ -649,6 +848,76 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing"
|
||||||
|
version = "0.1.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-lite",
|
||||||
|
"tracing-attributes",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-attributes"
|
||||||
|
version = "0.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-core"
|
||||||
|
version = "0.1.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
|
||||||
|
dependencies = [
|
||||||
|
"matchers",
|
||||||
|
"nu-ansi-term",
|
||||||
|
"once_cell",
|
||||||
|
"regex-automata",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec",
|
||||||
|
"thread_local",
|
||||||
|
"tracing",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.20.0"
|
version = "1.20.0"
|
||||||
@@ -684,6 +953,12 @@ dependencies = [
|
|||||||
"usb-device",
|
"usb-device",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcell"
|
name = "vcell"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@@ -710,3 +985,27 @@ checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"vcell",
|
"vcell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|||||||
@@ -2,23 +2,27 @@
|
|||||||
name = "lpc55s28-evk"
|
name = "lpc55s28-evk"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["usbhs"]
|
||||||
|
usbfs = []
|
||||||
|
usbhs = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bbqueue = "0.7.0"
|
||||||
cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7.5"
|
cortex-m-rt = "0.7.5"
|
||||||
defmt = "1.0.1"
|
defmt = "1.0.1"
|
||||||
defmt-rtt = "1.1.0"
|
defmt-rtt = "1.1.0"
|
||||||
embedded-hal = "1.0.0"
|
embedded-hal = "1.0.0"
|
||||||
embedded-io = "0.7.1"
|
embedded-io = "0.7.1"
|
||||||
heapless = "0.9.3"
|
|
||||||
log-to-defmt = "0.1.0"
|
log-to-defmt = "0.1.0"
|
||||||
lpc55-hal = { version = "0.5.0", path = "../lpc55-hal" }
|
|
||||||
nb = "1.1.0"
|
|
||||||
panic-halt = "1.0.0"
|
|
||||||
panic-probe = { version = "1.0.0", features = ["print-defmt"] }
|
panic-probe = { version = "1.0.0", features = ["print-defmt"] }
|
||||||
static_cell = "2.1.1"
|
# Includes update to usb-device 0.3, fix for isochronous and smaller critical sections
|
||||||
usb-device = "0.3"
|
lpc55-hal = { git = "https://github.com/ktims/lpc55-hal", branch = "main" }
|
||||||
usbd-uac2 = { version = "0.1.0", path = "../..", features = ["defmt"]}
|
usb-device.workspace = true
|
||||||
|
usbd-uac2 = { workspace = true, features = ["defmt"] }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// Find the actual path of memory.x and add it to link search, required for building in workspace
|
||||||
|
fn main() {
|
||||||
|
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||||
|
println!("cargo:rustc-link-search={}", manifest_dir);
|
||||||
|
}
|
||||||
@@ -0,0 +1,308 @@
|
|||||||
|
//! Contains hardware setup unrelated to Usb Audio Class implementation
|
||||||
|
|
||||||
|
use crate::Syscon;
|
||||||
|
use crate::hal;
|
||||||
|
use crate::{MCLK_FREQ, SAMPLE_RATE, pac};
|
||||||
|
|
||||||
|
use core::cell::UnsafeCell;
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
|
use defmt::debug;
|
||||||
|
use hal::{
|
||||||
|
Enabled, Iocon, Pin,
|
||||||
|
drivers::pins,
|
||||||
|
traits::wg::digital::v2::{OutputPin, ToggleableOutputPin},
|
||||||
|
typestates::pin::{gpio::direction::Output, state::Gpio},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) struct PllConstants {
|
||||||
|
pub m: u16, // 1-65535
|
||||||
|
pub n: u8, // 1-255
|
||||||
|
pub p: u8, // 1-31
|
||||||
|
pub selp: u8, // 5 bits
|
||||||
|
pub seli: u8, // 6 bits
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PllConstants {
|
||||||
|
pub(crate) const fn new(n: u8, m: u16, p: u8) -> Self {
|
||||||
|
assert!(n != 0, "1 <= N <= 255");
|
||||||
|
assert!(m != 0, "1 <= M <= 65535");
|
||||||
|
assert!(p != 0 && p <= 31, "1 <= P <= 31");
|
||||||
|
|
||||||
|
// Following ripped from lpc55-hal and made const
|
||||||
|
// UM 4.6.6.3.2
|
||||||
|
let selp = {
|
||||||
|
let v = (m >> 2) + 1;
|
||||||
|
if v < 31 { v } else { 31 }
|
||||||
|
} as u8;
|
||||||
|
|
||||||
|
let seli = {
|
||||||
|
let v = match m {
|
||||||
|
m if m >= 8000 => 1,
|
||||||
|
m if m >= 122 => 8000 / m,
|
||||||
|
_ => 2 * (m >> 2) + 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
if v < 63 { v } else { 63 }
|
||||||
|
} as u8;
|
||||||
|
// let seli = min(2*(m >> 2) + 3, 63);
|
||||||
|
Self {
|
||||||
|
n,
|
||||||
|
m,
|
||||||
|
p,
|
||||||
|
selp,
|
||||||
|
seli,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl defmt::Format for PllConstants {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
let factor = f32::from(self.m) / (f32::from(self.n) * 2.0 * f32::from(self.p));
|
||||||
|
|
||||||
|
defmt::write!(
|
||||||
|
fmt,
|
||||||
|
"m: {} n: {} p: {} selp: {} seli: {} fout: fin * {}",
|
||||||
|
self.m,
|
||||||
|
self.n,
|
||||||
|
self.p,
|
||||||
|
self.selp,
|
||||||
|
self.seli,
|
||||||
|
factor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fo = M/(N*2*P) * Fin
|
||||||
|
// Fo = 3072/(125*2*8) * 16MHz = 24.576MHz
|
||||||
|
const AUDIO_PLL: PllConstants = PllConstants::new(125, 3072, 8);
|
||||||
|
|
||||||
|
// Set PLL0 to 24.576MHz, start, and wait for lock
|
||||||
|
// This is not exposed by lpc55-hal, unfortunately. Copy their implementation here.
|
||||||
|
pub(crate) fn init_audio_pll() {
|
||||||
|
let syscon = unsafe { &*pac::SYSCON::ptr() };
|
||||||
|
let pmc = unsafe { &*pac::PMC::ptr() };
|
||||||
|
let anactrl = unsafe { &*pac::ANACTRL::ptr() };
|
||||||
|
|
||||||
|
debug!("start clk_in");
|
||||||
|
pmc.pdruncfg0
|
||||||
|
.modify(|_, w| w.pden_xtal32m().poweredon().pden_ldoxo32m().poweredon());
|
||||||
|
syscon.clock_ctrl.modify(|_, w| w.clkin_ena().enable());
|
||||||
|
anactrl
|
||||||
|
.xo32m_ctrl
|
||||||
|
.modify(|_, w| w.enable_system_clk_out().enable());
|
||||||
|
|
||||||
|
debug!("init pll0: {}", AUDIO_PLL);
|
||||||
|
pmc.pdruncfg0
|
||||||
|
.modify(|_, w| w.pden_pll0().poweredoff().pden_pll0_sscg().poweredoff());
|
||||||
|
syscon.pll0clksel.write(|w| w.sel().enum_0x1()); // clk_in
|
||||||
|
syscon.pll0ctrl.write(|w| unsafe {
|
||||||
|
w.clken()
|
||||||
|
.enable()
|
||||||
|
.seli()
|
||||||
|
.bits(AUDIO_PLL.seli)
|
||||||
|
.selp()
|
||||||
|
.bits(AUDIO_PLL.selp)
|
||||||
|
});
|
||||||
|
|
||||||
|
syscon
|
||||||
|
.pll0ndec
|
||||||
|
.write(|w| unsafe { w.ndiv().bits(AUDIO_PLL.n) });
|
||||||
|
syscon.pll0ndec.write(|w| unsafe {
|
||||||
|
w.ndiv().bits(AUDIO_PLL.n).nreq().set_bit() // latch
|
||||||
|
});
|
||||||
|
|
||||||
|
syscon
|
||||||
|
.pll0pdec
|
||||||
|
.write(|w| unsafe { w.pdiv().bits(AUDIO_PLL.p) });
|
||||||
|
syscon.pll0pdec.write(|w| unsafe {
|
||||||
|
w.pdiv().bits(AUDIO_PLL.p).preq().set_bit() // latch
|
||||||
|
});
|
||||||
|
|
||||||
|
syscon.pll0sscg0.write(|w| unsafe { w.md_lbs().bits(0) });
|
||||||
|
|
||||||
|
syscon
|
||||||
|
.pll0sscg1
|
||||||
|
.write(|w| unsafe { w.mdiv_ext().bits(AUDIO_PLL.m).sel_ext().set_bit() });
|
||||||
|
syscon.pll0sscg1.write(|w| unsafe {
|
||||||
|
w.mdiv_ext()
|
||||||
|
.bits(AUDIO_PLL.m)
|
||||||
|
.sel_ext()
|
||||||
|
.set_bit()
|
||||||
|
.mreq()
|
||||||
|
.set_bit() // latch
|
||||||
|
.md_req()
|
||||||
|
.set_bit() // latch
|
||||||
|
});
|
||||||
|
|
||||||
|
pmc.pdruncfg0
|
||||||
|
.modify(|_, w| w.pden_pll0().poweredon().pden_pll0_sscg().poweredon());
|
||||||
|
debug!("pll0 wait for lock");
|
||||||
|
let mut i = 0usize;
|
||||||
|
while syscon.pll0stat.read().lock().bit_is_clear() {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
debug!("pll0 locked after {} tries", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct I2sTx {
|
||||||
|
pub i2s: pac::I2S7,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_i2s(mut fc7: pac::FLEXCOMM7, i2s7: pac::I2S7, syscon: &mut Syscon) -> I2sTx {
|
||||||
|
defmt::debug!("init i2s");
|
||||||
|
// Enable BOTH
|
||||||
|
syscon.reset(&mut fc7);
|
||||||
|
syscon.enable_clock(&mut fc7);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
pac::IOCON::ptr().as_ref().unwrap().pio1_31.modify(|_, w| {
|
||||||
|
w.func()
|
||||||
|
.alt1()
|
||||||
|
.mode()
|
||||||
|
.inactive()
|
||||||
|
.slew()
|
||||||
|
.fast()
|
||||||
|
.invert()
|
||||||
|
.disabled()
|
||||||
|
.digimode()
|
||||||
|
.digital()
|
||||||
|
.od()
|
||||||
|
.normal()
|
||||||
|
});
|
||||||
|
pac::SYSCON::ptr()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.fcclksel7()
|
||||||
|
.modify(|_, w| w.sel().enum_0x5()); // MCLK
|
||||||
|
pac::SYSCON::ptr()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.mclkclksel
|
||||||
|
.modify(|_, w| w.sel().enum_0x1()); // PLL0
|
||||||
|
pac::SYSCON::ptr()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.mclkdiv
|
||||||
|
.modify(|_, w| w.div().bits(1).halt().run().reset().released()); // div by 2 = PLL0 fout / 2 = 12.288MHz, max for WM8904 @ 96k
|
||||||
|
pac::SYSCON::ptr()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.mclkio
|
||||||
|
.modify(|_, w| w.mclkio().output());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Select I2S TX function
|
||||||
|
fc7.pselid.write(|w| w.persel().i2s_transmit());
|
||||||
|
|
||||||
|
let regs = i2s7;
|
||||||
|
|
||||||
|
// Enable TX FIFO only
|
||||||
|
regs.fifocfg.modify(|_, w| {
|
||||||
|
w.enabletx()
|
||||||
|
.enabled()
|
||||||
|
.enablerx()
|
||||||
|
.disabled()
|
||||||
|
.dmatx()
|
||||||
|
.disabled()
|
||||||
|
.txi2se0()
|
||||||
|
.zero()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Flush
|
||||||
|
regs.fifocfg.modify(|_, w| w.emptytx().set_bit());
|
||||||
|
|
||||||
|
regs.cfg2
|
||||||
|
.modify(|_, w| unsafe { w.position().bits(0).framelen().bits(63) }); // framelen = 64
|
||||||
|
|
||||||
|
let bclk_div = (MCLK_FREQ / SAMPLE_RATE / 64) as u16;
|
||||||
|
regs.div
|
||||||
|
.modify(|_, w| unsafe { w.div().bits(bclk_div - 1) }); // Clock source is MCLK (12.288MHz) / 4 = 3MHz
|
||||||
|
|
||||||
|
// Config
|
||||||
|
regs.cfg1.modify(|_, w| unsafe {
|
||||||
|
w.mstslvcfg()
|
||||||
|
.normal_master()
|
||||||
|
.onechannel()
|
||||||
|
.dual_channel()
|
||||||
|
.datalen()
|
||||||
|
.bits(31)
|
||||||
|
.mainenable()
|
||||||
|
.enabled()
|
||||||
|
.mode()
|
||||||
|
.classic_mode()
|
||||||
|
.datapause()
|
||||||
|
.normal()
|
||||||
|
});
|
||||||
|
|
||||||
|
I2sTx { i2s: regs }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SharedLed<T: OutputPin> {
|
||||||
|
inner: UnsafeCell<T>,
|
||||||
|
}
|
||||||
|
unsafe impl<T: OutputPin> Sync for SharedLed<T> {}
|
||||||
|
impl<T: OutputPin> SharedLed<T> {
|
||||||
|
pub fn new(inner: T) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: UnsafeCell::new(inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn on(&self) {
|
||||||
|
unsafe {
|
||||||
|
(*self.inner.get()).set_low().ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn off(&self) {
|
||||||
|
unsafe {
|
||||||
|
(*self.inner.get()).set_high().ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: OutputPin + ToggleableOutputPin> SharedLed<T> {
|
||||||
|
pub fn toggle(&self) {
|
||||||
|
unsafe {
|
||||||
|
(*self.inner.get()).toggle().ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RedLed = Pin<pins::Pio1_6, Gpio<Output>>;
|
||||||
|
type GreenLed = Pin<pins::Pio1_7, Gpio<Output>>;
|
||||||
|
type BlueLed = Pin<pins::Pio1_4, Gpio<Output>>;
|
||||||
|
pub static RED_LED: MaybeUninit<SharedLed<RedLed>> = MaybeUninit::uninit();
|
||||||
|
pub static GREEN_LED: MaybeUninit<SharedLed<GreenLed>> = MaybeUninit::uninit();
|
||||||
|
pub static BLUE_LED: MaybeUninit<SharedLed<BlueLed>> = MaybeUninit::uninit();
|
||||||
|
|
||||||
|
pub fn init_leds(iocon: &mut Iocon<Enabled>, gpio: &mut hal::Gpio<Enabled>) {
|
||||||
|
let red_led = SharedLed::new(
|
||||||
|
pins::Pio1_6::take()
|
||||||
|
.unwrap()
|
||||||
|
.into_gpio_pin(iocon, gpio)
|
||||||
|
.into_output_low(),
|
||||||
|
);
|
||||||
|
let green_led = SharedLed::new(
|
||||||
|
pins::Pio1_7::take()
|
||||||
|
.unwrap()
|
||||||
|
.into_gpio_pin(iocon, gpio)
|
||||||
|
.into_output_low(),
|
||||||
|
);
|
||||||
|
let blue_led = SharedLed::new(
|
||||||
|
pins::Pio1_4::take()
|
||||||
|
.unwrap()
|
||||||
|
.into_gpio_pin(iocon, gpio)
|
||||||
|
.into_output_low(),
|
||||||
|
);
|
||||||
|
unsafe {
|
||||||
|
core::ptr::write(RED_LED.as_ptr() as *mut SharedLed<RedLed>, red_led);
|
||||||
|
core::ptr::write(GREEN_LED.as_ptr() as *mut SharedLed<GreenLed>, green_led);
|
||||||
|
core::ptr::write(BLUE_LED.as_ptr() as *mut SharedLed<BlueLed>, blue_led);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn red_led() -> &'static SharedLed<RedLed> {
|
||||||
|
unsafe { &*RED_LED.as_ptr() }
|
||||||
|
}
|
||||||
|
pub fn green_led() -> &'static SharedLed<GreenLed> {
|
||||||
|
unsafe { &*GREEN_LED.as_ptr() }
|
||||||
|
}
|
||||||
|
pub fn blue_led() -> &'static SharedLed<BlueLed> {
|
||||||
|
unsafe { &*BLUE_LED.as_ptr() }
|
||||||
|
}
|
||||||
+175
-487
@@ -1,514 +1,235 @@
|
|||||||
|
//! Interrupt driven example for the LPCXpresso55S28 demo board.
|
||||||
|
//!
|
||||||
|
//! This is a minimal implementation intended to demonstrate only the minimum
|
||||||
|
//! essential implementation, to clarify usage of the class driver.
|
||||||
|
//!
|
||||||
|
//! Uses the EVK's WM8904 DAC at 48KHz. Clock is generated by PLL0. Simple
|
||||||
|
//! proportional feedback is implemented.
|
||||||
|
//!
|
||||||
|
//! Packets from USB are placed in a `bbqueue`. They are consumed by the I2S
|
||||||
|
//! FIFO in the FLEXCOMM7 interrupt. This makes the implementation very sensitive
|
||||||
|
//! to interrupt::free critical sections (which are widely used in the USB bus driver)
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
#[cfg(all(feature = "usbfs", feature = "usbhs"))]
|
||||||
|
compile_error!("Choose one USB peripheral, usbfs and usbhs cannot be used together");
|
||||||
|
|
||||||
extern crate panic_probe;
|
extern crate panic_probe;
|
||||||
#[defmt::panic_handler]
|
#[defmt::panic_handler]
|
||||||
fn panic() -> ! {
|
fn panic() -> ! {
|
||||||
panic_probe::hard_fault()
|
panic_probe::hard_fault()
|
||||||
}
|
}
|
||||||
|
|
||||||
use core::cell::UnsafeCell;
|
use bbqueue::{
|
||||||
use core::ptr::null_mut;
|
nicknames::Churrasco,
|
||||||
use core::sync::atomic::{AtomicBool, AtomicI32, AtomicUsize, Ordering};
|
prod_cons::stream::{StreamConsumer, StreamProducer},
|
||||||
|
traits::bbqhdl::BbqHandle,
|
||||||
|
};
|
||||||
|
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use defmt;
|
|
||||||
use defmt::debug;
|
use defmt::debug;
|
||||||
use defmt_rtt as _;
|
use defmt_rtt as _;
|
||||||
use hal::Syscon;
|
|
||||||
use hal::drivers::{Timer, UsbBus, pins};
|
|
||||||
use hal::prelude::*;
|
|
||||||
use hal::raw as pac;
|
use hal::raw as pac;
|
||||||
use hal::time::Hertz;
|
use hal::{
|
||||||
use heapless::spsc::Consumer;
|
Syscon,
|
||||||
use lpc55_hal::{self as hal};
|
drivers::{Timer, UsbBus, pins},
|
||||||
|
prelude::*,
|
||||||
|
time::Hertz,
|
||||||
|
};
|
||||||
|
use lpc55_hal as hal;
|
||||||
use pac::interrupt;
|
use pac::interrupt;
|
||||||
use static_cell::StaticCell;
|
|
||||||
use usb_device::{
|
use usb_device::{
|
||||||
bus::{self},
|
bus::{self},
|
||||||
device::{StringDescriptors, UsbDeviceBuilder, UsbVidPid},
|
device::{StringDescriptors, UsbDeviceBuilder, UsbVidPid},
|
||||||
endpoint::IsochronousSynchronizationType,
|
|
||||||
};
|
};
|
||||||
use usbd_uac2::UsbIsochronousFeedback;
|
|
||||||
use usbd_uac2::{
|
use usbd_uac2::{
|
||||||
self, AudioClassConfig, RangeEntry, TerminalConfig, UsbAudioClass, UsbAudioClockImpl, UsbSpeed,
|
self, AudioHandler, ClockSource, RangeEntry, TerminalConfig, UsbAudioClassConfig,
|
||||||
constants::{FunctionCode, TerminalType},
|
UsbIsochronousFeedback, UsbSpeed, constants::FunctionCode, descriptors::ClockType,
|
||||||
descriptors::{ChannelConfig, ClockType, FormatType1, LockDelay},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::hw::{I2sTx, blue_led, green_led, red_led};
|
||||||
|
|
||||||
|
mod hw;
|
||||||
mod wm8904;
|
mod wm8904;
|
||||||
|
|
||||||
struct PllConstants {
|
|
||||||
m: u16, // 1-65535
|
|
||||||
n: u8, // 1-255
|
|
||||||
p: u8, // 1-31
|
|
||||||
selp: u8, // 5 bits
|
|
||||||
seli: u8, // 6 bits
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PllConstants {
|
|
||||||
const fn new(n: u8, m: u16, p: u8) -> Self {
|
|
||||||
assert!(n != 0, "1 <= N <= 255");
|
|
||||||
assert!(m != 0, "1 <= M <= 65535");
|
|
||||||
assert!(p != 0 && p <= 31, "1 <= P <= 31");
|
|
||||||
|
|
||||||
// Following ripped from lpc55-hal and made const
|
|
||||||
// UM 4.6.6.3.2
|
|
||||||
let selp = {
|
|
||||||
let v = (m >> 2) + 1;
|
|
||||||
if v < 31 { v } else { 31 }
|
|
||||||
} as u8;
|
|
||||||
|
|
||||||
let seli = {
|
|
||||||
let v = match m {
|
|
||||||
m if m >= 8000 => 1,
|
|
||||||
m if m >= 122 => 8000 / m,
|
|
||||||
_ => 2 * (m >> 2) + 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
if v < 63 { v } else { 63 }
|
|
||||||
} as u8;
|
|
||||||
// let seli = min(2*(m >> 2) + 3, 63);
|
|
||||||
Self {
|
|
||||||
n,
|
|
||||||
m,
|
|
||||||
p,
|
|
||||||
selp,
|
|
||||||
seli,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl defmt::Format for PllConstants {
|
|
||||||
fn format(&self, fmt: defmt::Formatter) {
|
|
||||||
let factor = f32::from(self.m) / (f32::from(self.n) * 2.0 * f32::from(self.p));
|
|
||||||
|
|
||||||
defmt::write!(
|
|
||||||
fmt,
|
|
||||||
"m: {} n: {} p: {} selp: {} seli: {} fout: fin * {}",
|
|
||||||
self.m,
|
|
||||||
self.n,
|
|
||||||
self.p,
|
|
||||||
self.selp,
|
|
||||||
self.seli,
|
|
||||||
factor
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const CODEC_I2C_ADDR: u8 = 0b0011010;
|
const CODEC_I2C_ADDR: u8 = 0b0011010;
|
||||||
// Fo = M/(N*2*P) * Fin
|
const FIFO_LENGTH: usize = 256; // frames
|
||||||
// Fo = 3072/(125*2*8) * 16MHz = 24.576MHz
|
const MCLK_FREQ: u32 = 12288000;
|
||||||
const AUDIO_PLL: PllConstants = PllConstants::new(125, 3072, 8);
|
|
||||||
const FIFO_LENGTH: usize = 2048; // frames
|
|
||||||
const MCLK_FREQ: u32 = 24576000;
|
|
||||||
const SAMPLE_RATE: u32 = 48000;
|
const SAMPLE_RATE: u32 = 48000;
|
||||||
|
// USB microframe rate
|
||||||
|
#[cfg(feature = "usbhs")]
|
||||||
|
const USB_FRAME_RATE: usize = 8000;
|
||||||
|
#[cfg(feature = "usbfs")]
|
||||||
|
const USB_FRAME_RATE: usize = 1000;
|
||||||
type SampleType = (i32, i32);
|
type SampleType = (i32, i32);
|
||||||
|
|
||||||
// const SINE_LUT: [i32; 32] = [
|
const BYTES_PER_FRAME: usize = 8;
|
||||||
// 0, 1636536, 3210180, 4660460, 5931640, 6974871, 7750062, 8227422, 8388607, 8227422, 7750062,
|
const QUEUE_BYTES: usize = FIFO_LENGTH * BYTES_PER_FRAME;
|
||||||
// 6974871, 5931640, 4660460, 3210180, 1636536, 0, -1636536, -3210180, -4660460, -5931640,
|
// We use bbqueue here for performance in the USB driver that runs almost entirely in interrupt free critical section.
|
||||||
// -6974871, -7750062, -8227422, -8388607, -8227422, -7750062, -6974871, -5931640, -4660460,
|
static QUEUE: Churrasco<QUEUE_BYTES> = Churrasco::new();
|
||||||
// -3210180, -1636536,
|
// Used for feedback calculation of current fifo state
|
||||||
// ];
|
static PRODUCED: AtomicU32 = AtomicU32::new(0);
|
||||||
|
static CONSUMED: AtomicU32 = AtomicU32::new(0);
|
||||||
|
|
||||||
// pub fn i2s_sine_test(i2s: &pac::I2S7) -> ! {
|
#[inline]
|
||||||
// let mut idx = 0;
|
fn try_write_one_frame<T: BbqHandle>(
|
||||||
// let mut count = 0usize;
|
cons: &mut StreamConsumer<T>,
|
||||||
|
i2s: &pac::i2s7::RegisterBlock,
|
||||||
|
) -> bool {
|
||||||
|
if let Ok(rgr) = cons.read() {
|
||||||
|
if rgr.len() >= BYTES_PER_FRAME {
|
||||||
|
let l = u32::from_le_bytes(rgr[0..4].try_into().unwrap());
|
||||||
|
let r = u32::from_le_bytes(rgr[4..8].try_into().unwrap());
|
||||||
|
|
||||||
// defmt::debug!("starting sine test");
|
i2s.fifowr.write(|w| unsafe { w.bits(l) });
|
||||||
|
i2s.fifowr.write(|w| unsafe { w.bits(r) });
|
||||||
|
|
||||||
// loop {
|
// consume exactly one frame (8 bytes)
|
||||||
// if i2s.fifostat.read().txnotfull().bit_is_set() {
|
rgr.release(BYTES_PER_FRAME);
|
||||||
// let sample = SINE_LUT[idx] * 32;
|
CONSUMED.fetch_add(BYTES_PER_FRAME as u32, Ordering::Relaxed);
|
||||||
|
return true;
|
||||||
// // ✅ Left channel
|
} else {
|
||||||
// i2s.fifowr.write(|w| unsafe { w.bits(sample as u32) });
|
// Not enough bytes for a full frame: leave it in the queue.
|
||||||
|
return false;
|
||||||
// // wait for space if needed
|
}
|
||||||
// while !i2s.fifostat.read().txnotfull().bit_is_set() {}
|
|
||||||
|
|
||||||
// // ✅ Right channel
|
|
||||||
// i2s.fifowr.write(|w| unsafe { w.bits(sample as u32) });
|
|
||||||
|
|
||||||
// idx = (idx + 1) & (SINE_LUT.len() - 1);
|
|
||||||
// count += 1;
|
|
||||||
// if count.is_multiple_of(48000) {
|
|
||||||
// defmt::debug!("frames sent: {}", count)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
struct Clock {}
|
|
||||||
impl Clock {
|
|
||||||
const RATES: [RangeEntry<u32>; 1] = [RangeEntry::new_fixed(48000)];
|
|
||||||
}
|
|
||||||
impl UsbAudioClockImpl for Clock {
|
|
||||||
const CLOCK_TYPE: usbd_uac2::descriptors::ClockType = ClockType::InternalFixed;
|
|
||||||
const SOF_SYNC: bool = false;
|
|
||||||
fn get_sample_rate(&self) -> core::result::Result<u32, usbd_uac2::UsbAudioClassError> {
|
|
||||||
Ok(Clock::RATES[0].min)
|
|
||||||
}
|
|
||||||
fn get_rates(
|
|
||||||
&self,
|
|
||||||
) -> core::result::Result<&[usbd_uac2::RangeEntry<u32>], usbd_uac2::UsbAudioClassError> {
|
|
||||||
Ok(&Clock::RATES)
|
|
||||||
}
|
|
||||||
fn get_clock_validity(&self) -> core::result::Result<bool, usbd_uac2::UsbAudioClassError> {
|
|
||||||
Ok(true)
|
|
||||||
}
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct PerfCounters {
|
|
||||||
frames: AtomicUsize,
|
|
||||||
avg_fill: AtomicI32, // i32 to simplify averaging
|
|
||||||
queue_underflows: AtomicUsize,
|
|
||||||
queue_overflows: AtomicUsize,
|
|
||||||
audio_underflows: AtomicUsize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl defmt::Format for PerfCounters {
|
|
||||||
fn format(&self, fmt: defmt::Formatter) {
|
|
||||||
defmt::write!(
|
|
||||||
fmt,
|
|
||||||
"frames: {} avg fill: {} a_underflows: {} q_underflows: {} q_overflows: {}",
|
|
||||||
self.frames.load(Ordering::Relaxed),
|
|
||||||
self.avg_fill.load(Ordering::Relaxed),
|
|
||||||
self.audio_underflows.load(Ordering::Relaxed),
|
|
||||||
self.queue_underflows.load(Ordering::Relaxed),
|
|
||||||
self.queue_overflows.load(Ordering::Relaxed)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static FIFO_CONSUMER_STORE: StaticCell<Consumer<SampleType>> = StaticCell::new();
|
|
||||||
static mut FIFO_CONSUMER: *mut Consumer<SampleType> = null_mut();
|
|
||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
fn FLEXCOMM7() {
|
fn FLEXCOMM7() {
|
||||||
let i2s = unsafe { &*pac::I2S7::ptr() };
|
let i2s = unsafe { &*pac::I2S7::ptr() };
|
||||||
|
// refill until fifo has >6 words
|
||||||
// refil the buffer to 4 frames / 8 samples
|
let mut cons = QUEUE.stream_consumer();
|
||||||
while i2s.fifostat.read().txlvl().bits() <= 6 {
|
while i2s.fifostat.read().txlvl().bits() <= 6 {
|
||||||
let fifo = unsafe { &mut *FIFO_CONSUMER };
|
if !try_write_one_frame(&mut cons, i2s) {
|
||||||
if let Some((l, r)) = fifo.dequeue() {
|
// No complete frame available: write silence to keep FIFO above threshold
|
||||||
i2s.fifowr.write(|w| unsafe { w.bits(l as u32) });
|
defmt::error!("underflow");
|
||||||
i2s.fifowr.write(|w| unsafe { w.bits(r as u32) });
|
red_led().toggle();
|
||||||
} else {
|
|
||||||
i2s.fifowr.write(|w| unsafe { w.bits(0) });
|
i2s.fifowr.write(|w| unsafe { w.bits(0) });
|
||||||
i2s.fifowr.write(|w| unsafe { w.bits(0) });
|
i2s.fifowr.write(|w| unsafe { w.bits(0) });
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Audio<'a> {
|
struct Audio<T: BbqHandle> {
|
||||||
running: AtomicBool,
|
running: AtomicBool,
|
||||||
i2s: I2sTx,
|
i2s: I2sTx,
|
||||||
producer: UnsafeCell<heapless::spsc::Producer<'a, SampleType>>,
|
producer: StreamProducer<T>,
|
||||||
perf: PerfCounters,
|
|
||||||
integrator: AtomicI32,
|
|
||||||
}
|
}
|
||||||
impl<'a> Audio<'a> {
|
impl<T: BbqHandle> Audio<T> {
|
||||||
// fn poll(&self) {
|
const RATES: [RangeEntry<u32>; 1] = [RangeEntry::new_fixed(SAMPLE_RATE)];
|
||||||
// if !self.running.load(Ordering::Relaxed) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if self.i2s.i2s.fifostat.read().txerr().bit_is_set() {
|
|
||||||
// self.i2s.i2s.fifostat.modify(|_, w| w.txerr().set_bit());
|
|
||||||
// // defmt::error!("fifo tx error, txlvl: {}", stat.txlvl().bits());
|
|
||||||
// }
|
|
||||||
// if self.i2s.i2s.fifostat.read().txlvl().bits() == 0 {
|
|
||||||
// self.perf.audio_underflows.fetch_add(1, Ordering::Relaxed);
|
|
||||||
// }
|
|
||||||
// while self.i2s.i2s.fifostat.read().txlvl().bits() <= 6 {
|
|
||||||
// // fifo is 8 deep at 32 bit samples
|
|
||||||
// let fifo = self.consumer.get();
|
|
||||||
// if let Some(sample) = unsafe { (*fifo).dequeue() } {
|
|
||||||
// self.i2s
|
|
||||||
// .i2s
|
|
||||||
// .fifowr
|
|
||||||
// .write(|w| unsafe { w.bits(sample.0 as u32) });
|
|
||||||
// self.i2s
|
|
||||||
// .i2s
|
|
||||||
// .fifowr
|
|
||||||
// .write(|w| unsafe { w.bits(sample.1 as u32) });
|
|
||||||
// } else {
|
|
||||||
// if self.running.load(Ordering::Relaxed) {
|
|
||||||
// self.perf.queue_underflows.fetch_add(1, Ordering::Relaxed);
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// // self.i2s.i2s.fifowr.write(|w| unsafe { w.bits(0 as u32) });
|
|
||||||
// // self.i2s.i2s.fifowr.write(|w| unsafe { w.bits(0 as u32) });
|
|
||||||
// }
|
|
||||||
// self.perf.frames.fetch_add(1, Ordering::Relaxed);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
fn start(&self) {
|
fn start(&self) {
|
||||||
self.running.store(true, Ordering::Relaxed);
|
self.running.store(true, Ordering::Relaxed);
|
||||||
defmt::info!("playback starting, enabling interrupts");
|
defmt::info!("playback starting, enabling interrupts");
|
||||||
self.i2s.i2s.fifointenclr.write(|w| w.txlvl().set_bit());
|
self.i2s.i2s.fifointenclr.write(|w| w.txlvl().set_bit());
|
||||||
// FIFO threshold trigger enable
|
// FIFO trigger threshold = <= 6 entries
|
||||||
self.i2s
|
self.i2s
|
||||||
.i2s
|
.i2s
|
||||||
.fifotrig
|
.fifotrig
|
||||||
.modify(|_, w| unsafe { w.txlvl().bits(4).txlvlena().enabled() });
|
.modify(|_, w| unsafe { w.txlvl().bits(6).txlvlena().enabled() });
|
||||||
// FIFO level interrupt enable
|
// FIFO level interrupt enable
|
||||||
self.i2s.i2s.fifointenset.modify(|_, w| w.txlvl().enabled());
|
self.i2s.i2s.fifointenset.modify(|_, w| w.txlvl().enabled());
|
||||||
unsafe { pac::NVIC::unmask(pac::Interrupt::FLEXCOMM7) };
|
unsafe { pac::NVIC::unmask(pac::Interrupt::FLEXCOMM7) };
|
||||||
|
green_led().on();
|
||||||
}
|
}
|
||||||
fn stop(&self) {
|
fn stop(&self) {
|
||||||
|
// If we don't disable interrupts while stopped, we will underflow constantly and continuously refill the fifo with 0s
|
||||||
|
// We could actually stop the I2S here, but sometimes that makes the DAC misbehave. The peripheral is configured to send
|
||||||
|
// 0s when the FIFO is empty, so this is fine.
|
||||||
self.running.store(true, Ordering::Relaxed);
|
self.running.store(true, Ordering::Relaxed);
|
||||||
defmt::info!("playback stopped: {}", self.perf);
|
defmt::info!("playback stopped");
|
||||||
pac::NVIC::mask(pac::Interrupt::FLEXCOMM7);
|
pac::NVIC::mask(pac::Interrupt::FLEXCOMM7);
|
||||||
|
green_led().off();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a, B: bus::UsbBus> UsbAudioClass<'a, B> for Audio<'_> {
|
|
||||||
fn alternate_setting_changed<CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>>(
|
impl<T: BbqHandle> ClockSource for Audio<T> {
|
||||||
|
const CLOCK_TYPE: usbd_uac2::descriptors::ClockType = ClockType::InternalFixed;
|
||||||
|
const SOF_SYNC: bool = false;
|
||||||
|
fn sample_rate(&self) -> u32 {
|
||||||
|
Self::RATES[0].min
|
||||||
|
}
|
||||||
|
fn sample_rates(
|
||||||
&self,
|
&self,
|
||||||
ac: &mut usbd_uac2::AudioClass<'a, B, CS, AU>,
|
) -> core::result::Result<&[usbd_uac2::RangeEntry<u32>], usbd_uac2::UsbAudioClassError> {
|
||||||
terminal: usb_device::UsbDirection,
|
Ok(&Self::RATES)
|
||||||
alt_setting: u8,
|
}
|
||||||
) {
|
fn clock_validity(&self) -> core::result::Result<bool, usbd_uac2::UsbAudioClassError> {
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: BbqHandle, B: bus::UsbBus> AudioHandler<'_, B> for Audio<T> {
|
||||||
|
fn alternate_setting_changed(&mut self, _terminal: usb_device::UsbDirection, alt_setting: u8) {
|
||||||
|
// alt setting 0 means stopped
|
||||||
match alt_setting {
|
match alt_setting {
|
||||||
0 => self.stop(),
|
0 => self.stop(),
|
||||||
1 => self.start(),
|
1 => self.start(),
|
||||||
_ => defmt::error!("unexpected alt setting {}", alt_setting),
|
_ => defmt::error!("unexpected alt setting {}", alt_setting),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn audio_data_rx(&self, ep: &usb_device::endpoint::Endpoint<'a, B, usb_device::endpoint::Out>) {
|
fn audio_data_rx(
|
||||||
let mut buf = [0; 384];
|
&mut self,
|
||||||
|
ep: &usb_device::endpoint::Endpoint<'_, B, usb_device::endpoint::Out>,
|
||||||
|
) {
|
||||||
|
// Buffer must fit one microframe's (125us @ HS, 1ms @ FS) of audio data
|
||||||
|
// (based on how `usbd_uac2` sets up the descriptors), calculate that
|
||||||
|
// size here.
|
||||||
|
let mut buf =
|
||||||
|
[0; (SAMPLE_RATE as usize / USB_FRAME_RATE + 1) * core::mem::size_of::<SampleType>()];
|
||||||
let len = match ep.read(&mut buf) {
|
let len = match ep.read(&mut buf) {
|
||||||
Ok(len) => len,
|
Ok(len) => len,
|
||||||
Err(e) => {
|
Err(_) => {
|
||||||
defmt::error!("usb error in rx callback");
|
defmt::error!("usb error in rx callback");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let buf = &buf[..len];
|
let buf = &buf[..len];
|
||||||
for sample in buf.chunks_exact(8).map(|b| {
|
|
||||||
(
|
if let Ok(mut wg) = self.producer.grant_exact(buf.len()) {
|
||||||
i32::from_le_bytes(b[..4].try_into().unwrap()),
|
wg.copy_from_slice(buf);
|
||||||
i32::from_le_bytes(b[4..].try_into().unwrap()),
|
wg.commit(buf.len());
|
||||||
)
|
PRODUCED.fetch_add(buf.len() as u32, Ordering::Relaxed);
|
||||||
}) {
|
} else {
|
||||||
if let Err(e) = unsafe { (*self.producer.get()).enqueue(sample) } {
|
blue_led().on();
|
||||||
self.perf.queue_overflows.fetch_add(1, Ordering::Relaxed);
|
defmt::error!("overflowed bbq, asked {}", buf.len());
|
||||||
// defmt::error!("overflowed fifo, len: {}", unsafe {
|
|
||||||
// (*self.producer.get()).len()
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn feedback(&self) -> Option<UsbIsochronousFeedback> {
|
|
||||||
const TARGET: i32 = FIFO_LENGTH as i32 / 2 - 64;
|
|
||||||
const NOMINAL: i32 = 48 << 16;
|
|
||||||
|
|
||||||
let queuelen = unsafe { (*self.producer.get()).len() as i32 };
|
/// Provide rate feedback to the host, so that it doesn't over- or underflow
|
||||||
let error = (queuelen - TARGET).clamp(-32, 32);
|
/// our queue. This implementation is not tuned.
|
||||||
|
fn feedback(&mut self, nominal_rate: UsbIsochronousFeedback) -> Option<UsbIsochronousFeedback> {
|
||||||
|
let target = FIFO_LENGTH as i32 / 2;
|
||||||
|
|
||||||
// --- integrator ---
|
let fill = PRODUCED
|
||||||
let scaled_error = error / 64;
|
.load(Ordering::Acquire)
|
||||||
|
.wrapping_sub(CONSUMED.load(Ordering::Acquire)) as i32
|
||||||
|
/ BYTES_PER_FRAME as i32;
|
||||||
|
|
||||||
let new_i = self.integrator.fetch_add(scaled_error, Ordering::Relaxed) + scaled_error;
|
let error = fill - target;
|
||||||
let clamped = new_i.clamp(-131072, 131072);
|
// Clamp excursions.
|
||||||
|
let (i, _f) = nominal_rate.parts();
|
||||||
|
let error = error.clamp(-(i as i32 * 4), i as i32 * 4);
|
||||||
|
|
||||||
// leak + store final value
|
let p = error << 8;
|
||||||
let leaked = clamped - (clamped >> 8);
|
|
||||||
self.integrator.store(leaked, Ordering::Relaxed);
|
|
||||||
|
|
||||||
// reset on large deviation
|
let correction = -p;
|
||||||
if error.abs() > 96 {
|
let nominal_v = nominal_rate.to_u32_12_13() as i32;
|
||||||
self.integrator.store(0, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- gains ---
|
let mut v = nominal_v + correction;
|
||||||
let p = error / 128;
|
|
||||||
let i = leaked / 32768;
|
|
||||||
|
|
||||||
// correction
|
// Clamp corrections
|
||||||
let correction = (-(p + i)).clamp(-32, 32);
|
v = v.clamp(nominal_v - (1 << 14), nominal_v + (1 << 14));
|
||||||
let v = NOMINAL + (correction << 10);
|
|
||||||
|
|
||||||
// EMA (unchanged, already correct)
|
// Note: this log will cause continuous underflows
|
||||||
let ema = self.perf.avg_fill.load(Ordering::Relaxed);
|
defmt::debug!("fill:{} err:{} fb:{=u32:x}", fill, error, v as u32);
|
||||||
let new = ((ema * 1023) + queuelen + 512) >> 10;
|
|
||||||
self.perf.avg_fill.store(new, Ordering::Relaxed);
|
|
||||||
|
|
||||||
defmt::debug!(
|
|
||||||
"q:{} p:{} i:{} err:{} fb:{}+{}",
|
|
||||||
queuelen,
|
|
||||||
p,
|
|
||||||
i,
|
|
||||||
error,
|
|
||||||
NOMINAL >> 16,
|
|
||||||
correction
|
|
||||||
);
|
|
||||||
|
|
||||||
Some(UsbIsochronousFeedback::new(v as u32))
|
Some(UsbIsochronousFeedback::new(v as u32))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set PLL0 to 24.576MHz, start, and wait for lock
|
|
||||||
// This is not exposed by lpc55-hal, unfortunately. Copy their implementation here.
|
|
||||||
fn init_audio_pll() {
|
|
||||||
let syscon = unsafe { &*pac::SYSCON::ptr() };
|
|
||||||
let pmc = unsafe { &*pac::PMC::ptr() };
|
|
||||||
let anactrl = unsafe { &*pac::ANACTRL::ptr() };
|
|
||||||
|
|
||||||
debug!("start clk_in");
|
|
||||||
pmc.pdruncfg0
|
|
||||||
.modify(|_, w| w.pden_xtal32m().poweredon().pden_ldoxo32m().poweredon());
|
|
||||||
syscon.clock_ctrl.modify(|_, w| w.clkin_ena().enable());
|
|
||||||
anactrl
|
|
||||||
.xo32m_ctrl
|
|
||||||
.modify(|_, w| w.enable_system_clk_out().enable());
|
|
||||||
|
|
||||||
debug!("init pll: {}", AUDIO_PLL);
|
|
||||||
pmc.pdruncfg0
|
|
||||||
.modify(|_, w| w.pden_pll0().poweredoff().pden_pll0_sscg().poweredoff());
|
|
||||||
syscon.pll0clksel.write(|w| w.sel().enum_0x1()); // clk_in
|
|
||||||
syscon.pll0ctrl.write(|w| unsafe {
|
|
||||||
w.clken()
|
|
||||||
.enable()
|
|
||||||
.seli()
|
|
||||||
.bits(AUDIO_PLL.seli)
|
|
||||||
.selp()
|
|
||||||
.bits(AUDIO_PLL.selp)
|
|
||||||
});
|
|
||||||
|
|
||||||
syscon
|
|
||||||
.pll0ndec
|
|
||||||
.write(|w| unsafe { w.ndiv().bits(AUDIO_PLL.n) });
|
|
||||||
syscon.pll0ndec.write(|w| unsafe {
|
|
||||||
w.ndiv().bits(AUDIO_PLL.n).nreq().set_bit() // latch
|
|
||||||
});
|
|
||||||
|
|
||||||
syscon
|
|
||||||
.pll0pdec
|
|
||||||
.write(|w| unsafe { w.pdiv().bits(AUDIO_PLL.p) });
|
|
||||||
syscon.pll0pdec.write(|w| unsafe {
|
|
||||||
w.pdiv().bits(AUDIO_PLL.p).preq().set_bit() // latch
|
|
||||||
});
|
|
||||||
|
|
||||||
syscon.pll0sscg0.write(|w| unsafe { w.md_lbs().bits(0) });
|
|
||||||
|
|
||||||
syscon
|
|
||||||
.pll0sscg1
|
|
||||||
.write(|w| unsafe { w.mdiv_ext().bits(AUDIO_PLL.m).sel_ext().set_bit() });
|
|
||||||
syscon.pll0sscg1.write(|w| unsafe {
|
|
||||||
w.mdiv_ext()
|
|
||||||
.bits(AUDIO_PLL.m)
|
|
||||||
.sel_ext()
|
|
||||||
.set_bit()
|
|
||||||
.mreq()
|
|
||||||
.set_bit() // latch
|
|
||||||
.md_req()
|
|
||||||
.set_bit() // latch
|
|
||||||
});
|
|
||||||
|
|
||||||
pmc.pdruncfg0
|
|
||||||
.modify(|_, w| w.pden_pll0().poweredon().pden_pll0_sscg().poweredon());
|
|
||||||
debug!("pll0 wait for lock");
|
|
||||||
let mut i = 0usize;
|
|
||||||
while syscon.pll0stat.read().lock().bit_is_clear() {
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
debug!("pll0 locked after {} tries", i);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct I2sTx {
|
|
||||||
pub i2s: pac::I2S7,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_i2s(mut fc7: pac::FLEXCOMM7, i2s7: pac::I2S7, syscon: &mut Syscon) -> I2sTx {
|
|
||||||
defmt::debug!("init i2s");
|
|
||||||
// Enable BOTH
|
|
||||||
syscon.reset(&mut fc7);
|
|
||||||
syscon.enable_clock(&mut fc7);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
pac::IOCON::ptr().as_ref().unwrap().pio1_31.modify(|_, w| {
|
|
||||||
w.func()
|
|
||||||
.alt1()
|
|
||||||
.mode()
|
|
||||||
.inactive()
|
|
||||||
.slew()
|
|
||||||
.fast()
|
|
||||||
.invert()
|
|
||||||
.disabled()
|
|
||||||
.digimode()
|
|
||||||
.digital()
|
|
||||||
.od()
|
|
||||||
.normal()
|
|
||||||
});
|
|
||||||
pac::SYSCON::ptr()
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.fcclksel7()
|
|
||||||
.modify(|_, w| w.sel().enum_0x5()); // MCLK
|
|
||||||
pac::SYSCON::ptr()
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.mclkclksel
|
|
||||||
.modify(|_, w| w.sel().enum_0x1()); // PLL0
|
|
||||||
pac::SYSCON::ptr()
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.mclkdiv
|
|
||||||
.modify(|_, w| w.div().bits(0).halt().run().reset().released()); // div by 1 = PLL0 fout
|
|
||||||
pac::SYSCON::ptr()
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.mclkio
|
|
||||||
.modify(|_, w| w.mclkio().output());
|
|
||||||
};
|
|
||||||
|
|
||||||
// Select I2S TX function
|
|
||||||
fc7.pselid.write(|w| w.persel().i2s_transmit());
|
|
||||||
|
|
||||||
let regs = i2s7;
|
|
||||||
|
|
||||||
// Enable TX FIFO only
|
|
||||||
regs.fifocfg.modify(|_, w| {
|
|
||||||
w.enabletx()
|
|
||||||
.enabled()
|
|
||||||
.enablerx()
|
|
||||||
.disabled()
|
|
||||||
.dmatx()
|
|
||||||
.disabled()
|
|
||||||
.txi2se0()
|
|
||||||
.zero()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Flush
|
|
||||||
regs.fifocfg.modify(|_, w| w.emptytx().set_bit());
|
|
||||||
|
|
||||||
regs.cfg2
|
|
||||||
.modify(|_, w| unsafe { w.position().bits(0).framelen().bits(63) });
|
|
||||||
|
|
||||||
regs.div.modify(|_, w| unsafe { w.div().bits(7) }); // Clock source is MCLK (24MHz on FRO96) / 8 = 3MHz
|
|
||||||
|
|
||||||
// Config
|
|
||||||
regs.cfg1.modify(|_, w| unsafe {
|
|
||||||
w.mstslvcfg()
|
|
||||||
.normal_master()
|
|
||||||
.onechannel()
|
|
||||||
.dual_channel()
|
|
||||||
.datalen()
|
|
||||||
.bits(31)
|
|
||||||
.mainenable()
|
|
||||||
.enabled()
|
|
||||||
.mode()
|
|
||||||
.classic_mode()
|
|
||||||
.datapause()
|
|
||||||
.normal()
|
|
||||||
});
|
|
||||||
|
|
||||||
I2sTx { i2s: regs }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
let hal = hal::new();
|
let hal = hal::new();
|
||||||
@@ -522,10 +243,7 @@ fn main() -> ! {
|
|||||||
|
|
||||||
debug!("start");
|
debug!("start");
|
||||||
|
|
||||||
let mut red_led = pins::Pio1_6::take()
|
hw::init_leds(&mut iocon, &mut gpio);
|
||||||
.unwrap()
|
|
||||||
.into_gpio_pin(&mut iocon, &mut gpio)
|
|
||||||
.into_output(hal::drivers::pins::Level::Low); // start turned on
|
|
||||||
|
|
||||||
debug!("iocon");
|
debug!("iocon");
|
||||||
let usb0_vbus_pin = pins::Pio0_22::take()
|
let usb0_vbus_pin = pins::Pio0_22::take()
|
||||||
@@ -535,20 +253,16 @@ fn main() -> ! {
|
|||||||
pins::Pio1_20::take().unwrap().into_i2c4_scl_pin(&mut iocon),
|
pins::Pio1_20::take().unwrap().into_i2c4_scl_pin(&mut iocon),
|
||||||
pins::Pio1_21::take().unwrap().into_i2c4_sda_pin(&mut iocon),
|
pins::Pio1_21::take().unwrap().into_i2c4_sda_pin(&mut iocon),
|
||||||
);
|
);
|
||||||
let codec_i2s_pins = (
|
// We can initialize and iocon these, but there is no peripheral, so they do not get used
|
||||||
|
let _codec_i2s_pins = (
|
||||||
pins::Pio0_21::take().unwrap().into_spi7_sck_pin(&mut iocon),
|
pins::Pio0_21::take().unwrap().into_spi7_sck_pin(&mut iocon),
|
||||||
pins::Pio0_20::take().unwrap().into_i2s7_sda_pin(&mut iocon),
|
pins::Pio0_20::take().unwrap().into_i2s7_sda_pin(&mut iocon),
|
||||||
pins::Pio0_19::take().unwrap().into_i2s7_ws_pin(&mut iocon),
|
pins::Pio0_19::take().unwrap().into_i2s7_ws_pin(&mut iocon),
|
||||||
pins::Pio1_31::take().unwrap(), // MCLK
|
pins::Pio1_31::take().unwrap(), // MCLK
|
||||||
);
|
);
|
||||||
|
|
||||||
// iocon.disabled(&mut syscon).release(); // save the environment :)
|
|
||||||
|
|
||||||
debug!("clocks");
|
debug!("clocks");
|
||||||
// TODO: figure out how to configure the PLL for a more suitable audio clock.
|
|
||||||
let clocks = hal::ClockRequirements::default()
|
let clocks = hal::ClockRequirements::default()
|
||||||
// .system_frequency(24.mhz())
|
|
||||||
// .system_frequency(72.mhz())
|
|
||||||
.system_frequency(96.MHz())
|
.system_frequency(96.MHz())
|
||||||
.configure(&mut anactrl, &mut pmc, &mut syscon)
|
.configure(&mut anactrl, &mut pmc, &mut syscon)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -557,9 +271,8 @@ fn main() -> ! {
|
|||||||
.0
|
.0
|
||||||
.enabled(&mut syscon, clocks.support_1mhz_fro_token().unwrap()),
|
.enabled(&mut syscon, clocks.support_1mhz_fro_token().unwrap()),
|
||||||
);
|
);
|
||||||
|
// Start PLL0 at 24.576MHz as the audio clock. The FRO cannot evenly divide any common audio frequencies.
|
||||||
debug!("pll0");
|
hw::init_audio_pll();
|
||||||
init_audio_pll();
|
|
||||||
|
|
||||||
debug!("peripherals");
|
debug!("peripherals");
|
||||||
|
|
||||||
@@ -575,78 +288,55 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let i2s_peripheral = {
|
let i2s_peripheral = {
|
||||||
let fc7 = hal.flexcomm.7.release();
|
let fc7 = hal.flexcomm.7.release();
|
||||||
init_i2s(fc7.0, fc7.2, &mut syscon)
|
hw::init_i2s(fc7.0, fc7.2, &mut syscon)
|
||||||
};
|
};
|
||||||
|
|
||||||
let usb_peripheral = hal.usbhs.enabled_as_device(
|
#[cfg(feature = "usbhs")]
|
||||||
&mut anactrl,
|
let (usb_speed, usb_peripheral) = (
|
||||||
&mut pmc,
|
UsbSpeed::High,
|
||||||
&mut syscon,
|
hal.usbhs.enabled_as_device(
|
||||||
&mut _delay_timer,
|
&mut anactrl,
|
||||||
clocks.support_usbhs_token().unwrap(),
|
&mut pmc,
|
||||||
|
&mut syscon,
|
||||||
|
&mut _delay_timer,
|
||||||
|
clocks.support_usbhs_token().unwrap(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
#[cfg(feature = "usbfs")]
|
||||||
|
let (usb_speed, usb_peripheral) = (
|
||||||
|
UsbSpeed::Full,
|
||||||
|
hal.usbfs.enabled_as_device(
|
||||||
|
&mut anactrl,
|
||||||
|
&mut pmc,
|
||||||
|
&mut syscon,
|
||||||
|
clocks.support_usbfs_token().unwrap(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let usb_bus = UsbBus::new(usb_peripheral, usb0_vbus_pin);
|
let usb_bus = UsbBus::new(usb_peripheral, usb0_vbus_pin);
|
||||||
let clock = Clock {};
|
|
||||||
|
|
||||||
defmt::debug!("codec init");
|
defmt::debug!("codec init");
|
||||||
wm8904::init_codec(&mut i2c_bus);
|
wm8904::init_codec(&mut i2c_bus);
|
||||||
let queue = cortex_m::singleton!(
|
|
||||||
: heapless::spsc::Queue<SampleType, FIFO_LENGTH>
|
|
||||||
= heapless::spsc::Queue::new()
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let (producer, consumer) = queue.split();
|
|
||||||
|
|
||||||
let consumer_ref = unsafe { FIFO_CONSUMER_STORE.init(consumer) };
|
// let consumer_ref = FIFO_CONSUMER_STORE.init(consumer);
|
||||||
unsafe { FIFO_CONSUMER = consumer_ref as *mut _ };
|
// unsafe { FIFO_CONSUMER = consumer_ref as *mut _ };
|
||||||
|
|
||||||
// i2s_sine_test(&i2s_peripheral.i2s);
|
let mut audio = Audio {
|
||||||
let audio = Audio {
|
|
||||||
i2s: i2s_peripheral,
|
i2s: i2s_peripheral,
|
||||||
producer: UnsafeCell::new(producer),
|
producer: QUEUE.stream_producer(),
|
||||||
running: AtomicBool::new(false),
|
running: AtomicBool::new(false),
|
||||||
perf: PerfCounters::default(),
|
|
||||||
integrator: AtomicI32::new(0),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let config = AudioClassConfig::new(UsbSpeed::High, FunctionCode::Other, &clock, &audio)
|
let config = UsbAudioClassConfig::new(usb_speed, FunctionCode::IoBox, &mut audio)
|
||||||
.with_input_config(TerminalConfig::new(
|
.with_output_config(TerminalConfig::builder().base_id(2).build());
|
||||||
2,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
FormatType1 {
|
|
||||||
bit_resolution: 32,
|
|
||||||
bytes_per_sample: 4,
|
|
||||||
},
|
|
||||||
TerminalType::ExtLineConnector,
|
|
||||||
ChannelConfig::default_chans(2),
|
|
||||||
IsochronousSynchronizationType::Asynchronous,
|
|
||||||
LockDelay::Undefined(0),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
.with_output_config(TerminalConfig::new(
|
|
||||||
4,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
FormatType1 {
|
|
||||||
bit_resolution: 32,
|
|
||||||
bytes_per_sample: 4,
|
|
||||||
},
|
|
||||||
TerminalType::ExtLineConnector,
|
|
||||||
ChannelConfig::default_chans(2),
|
|
||||||
IsochronousSynchronizationType::Asynchronous,
|
|
||||||
LockDelay::Milliseconds(10),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
|
|
||||||
let mut uac2 = config.build(&usb_bus).unwrap();
|
let mut uac2 = config.build(&usb_bus).unwrap();
|
||||||
|
|
||||||
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x1209, 0xcc1d))
|
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x1209, 0xcc1d))
|
||||||
.composite_with_iads()
|
.composite_with_iads()
|
||||||
.strings(&[StringDescriptors::default()
|
.strings(&[StringDescriptors::default()
|
||||||
.manufacturer("VE7XEN")
|
.manufacturer("Generic")
|
||||||
.product("Guac")
|
.product("usbd_uac2 device")
|
||||||
.serial_number("123456789")])
|
.serial_number("123456789")])
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.max_packet_size_0(64)
|
.max_packet_size_0(64)
|
||||||
@@ -660,7 +350,5 @@ fn main() -> ! {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
usb_dev.poll(&mut [&mut uac2]);
|
usb_dev.poll(&mut [&mut uac2]);
|
||||||
// audio.poll();
|
|
||||||
red_led.set_high().ok(); // Turn off
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+300
-167
@@ -6,28 +6,20 @@ mod cursor;
|
|||||||
pub mod descriptors;
|
pub mod descriptors;
|
||||||
mod log;
|
mod log;
|
||||||
|
|
||||||
use core::cell::OnceCell;
|
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::sync::atomic::AtomicUsize;
|
|
||||||
|
|
||||||
use byteorder_embedded_io::{LittleEndian, WriteBytesExt};
|
use byteorder_embedded_io::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use constants::*;
|
use constants::*;
|
||||||
use descriptors::*;
|
use descriptors::*;
|
||||||
use log::*;
|
use log::*;
|
||||||
|
|
||||||
use modular_bitfield::prelude::*;
|
use num_traits::{ConstZero, ToPrimitive};
|
||||||
use num_traits::{ConstZero, Zero};
|
|
||||||
use usb_device::control::{Recipient, Request, RequestType};
|
use usb_device::control::{Recipient, Request, RequestType};
|
||||||
use usb_device::device::DEFAULT_ALTERNATE_SETTING;
|
use usb_device::device::DEFAULT_ALTERNATE_SETTING;
|
||||||
use usb_device::endpoint::{self, Endpoint, EndpointDirection, In, Out};
|
use usb_device::endpoint::{self, Endpoint, EndpointDirection, In, Out};
|
||||||
use usb_device::{UsbDirection, class_prelude::*};
|
use usb_device::{UsbDirection, class_prelude::*};
|
||||||
|
|
||||||
pub use constants::USB_CLASS_AUDIO;
|
|
||||||
|
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
use defmt;
|
|
||||||
|
|
||||||
mod sealed {
|
mod sealed {
|
||||||
pub trait Sealed {}
|
pub trait Sealed {}
|
||||||
}
|
}
|
||||||
@@ -68,6 +60,9 @@ impl RangeType for u8 {}
|
|||||||
impl RangeType for u16 {}
|
impl RangeType for u16 {}
|
||||||
impl RangeType for u32 {}
|
impl RangeType for u32 {}
|
||||||
|
|
||||||
|
/// Represent a range request/response.
|
||||||
|
///
|
||||||
|
/// ref: UAC2 5.2.2
|
||||||
#[derive(PartialEq, Eq, Ord)]
|
#[derive(PartialEq, Eq, Ord)]
|
||||||
pub struct RangeEntry<T: RangeType> {
|
pub struct RangeEntry<T: RangeType> {
|
||||||
pub min: T,
|
pub min: T,
|
||||||
@@ -87,10 +82,7 @@ impl<T: RangeType> RangeEntry<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write<W: embedded_io::Write>(
|
fn write<W: embedded_io::Write>(&self, mut buf: W) -> core::result::Result<usize, W::Error> {
|
||||||
&self,
|
|
||||||
mut buf: W,
|
|
||||||
) -> core::result::Result<usize, W::Error> {
|
|
||||||
buf.write_all(self.min.to_le_bytes().as_ref())?;
|
buf.write_all(self.min.to_le_bytes().as_ref())?;
|
||||||
buf.write_all(self.max.to_le_bytes().as_ref())?;
|
buf.write_all(self.max.to_le_bytes().as_ref())?;
|
||||||
buf.write_all(self.res.to_le_bytes().as_ref())?;
|
buf.write_all(self.res.to_le_bytes().as_ref())?;
|
||||||
@@ -98,48 +90,42 @@ impl<T: RangeType> RangeEntry<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The spec guarantees that ranges do not overlap, so compare by min is correct.
|
// The spec guarantees that ranges do not overlap, so compare by min is correct.
|
||||||
impl<T: RangeType + PartialOrd> PartialOrd for RangeEntry<T> {
|
impl<T: RangeType + PartialOrd> PartialOrd for RangeEntry<T> {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
self.min.partial_cmp(&other.min)
|
self.min.partial_cmp(&other.min)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fixed point 10.14, packed to the least significant 3-bytes of a 4-byte USB feedback endpoint response
|
/// Represent Isochronous feedback as integer and fractional parts, internally as 16.16.
|
||||||
|
///
|
||||||
|
/// Provides conversions to and from the different USB formats.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct UsbIsochronousFeedback {
|
pub struct UsbIsochronousFeedback(u32);
|
||||||
int: u16,
|
|
||||||
frac: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UsbIsochronousFeedback {
|
impl UsbIsochronousFeedback {
|
||||||
|
pub fn new(v: u32) -> Self {
|
||||||
|
Self(v)
|
||||||
|
}
|
||||||
/// Accepts all u16 values, saturating the output depending on format
|
/// Accepts all u16 values, saturating the output depending on format
|
||||||
pub fn new_frac(int: u16, frac: u16) -> Self {
|
pub fn new_frac(int: u16, frac: u16) -> Self {
|
||||||
Self { int, frac }
|
Self(((int as u32) << 16) | frac as u32)
|
||||||
}
|
}
|
||||||
/// Assumed 16.16, not either of the USB formats
|
/// Convert float to fixed point
|
||||||
pub fn new(value: u32) -> Self {
|
pub fn new_float(rate: f32) -> Self {
|
||||||
Self {
|
let fb = (rate * 65536.0 + 0.5) as u32;
|
||||||
int: (value >> 16) as u16,
|
Self(fb)
|
||||||
frac: (value & 0xffff) as u16,
|
}
|
||||||
}
|
pub fn parts(&self) -> (u16, u16) {
|
||||||
|
((self.0 >> 16) as u16, (self.0 & 0xffff) as u16)
|
||||||
}
|
}
|
||||||
/// Serialize into a u32 in 16.16 representation for USB HS
|
/// Serialize into a u32 in 16.16 representation for USB HS
|
||||||
pub fn to_u32_12_13(&self) -> u32 {
|
pub fn to_u32_12_13(&self) -> u32 {
|
||||||
let int = (self.int as u32) << 16;
|
self.0
|
||||||
// ostensibly 13 bits, so should require << 3, but USB allows us to use
|
|
||||||
// these bits for 'extra precision'. So we may as well just treat it as
|
|
||||||
// 16.16. The application can << 3 if it wants to for some reason.
|
|
||||||
let frac = (self.frac as u32) & 0xffff;
|
|
||||||
|
|
||||||
int | frac
|
|
||||||
}
|
}
|
||||||
/// Serialize into a u32 in 10.14 representation for USB FS (take the 3 LSB)
|
/// Serialize into a u32 in 10.14 representation for USB FS
|
||||||
pub fn to_u32_10_14(&self) -> u32 {
|
pub fn to_u32_10_14(&self) -> u32 {
|
||||||
let int = (self.int as u32) << 14;
|
(self.0 + 2) >> 2
|
||||||
let frac = (self.frac as u32) & 0x3fff;
|
|
||||||
|
|
||||||
int | frac
|
|
||||||
}
|
}
|
||||||
/// Serialize into 16.16 little endian byte array for USB HS
|
/// Serialize into 16.16 little endian byte array for USB HS
|
||||||
pub fn to_bytes_12_13(&self) -> [u8; 4] {
|
pub fn to_bytes_12_13(&self) -> [u8; 4] {
|
||||||
@@ -154,38 +140,27 @@ impl UsbIsochronousFeedback {
|
|||||||
|
|
||||||
/// A trait for implementing USB Audio Class 2 devices
|
/// A trait for implementing USB Audio Class 2 devices
|
||||||
///
|
///
|
||||||
/// Contains callback methods which will be called by the class driver. All
|
/// Contains callback methods which will be called by the class driver.
|
||||||
/// callbacks are optional, which may be useful for a tight-loop polling implementation
|
pub trait AudioHandler<'a, B: UsbBus> {
|
||||||
/// but most implementations will want to implement at least `audio_data_rx`.
|
|
||||||
///
|
|
||||||
/// Unimplemented callbacks should return `Ok(())` if a result is required.
|
|
||||||
|
|
||||||
pub trait UsbAudioClass<'a, B: UsbBus> {
|
|
||||||
/// Called when audio data is received from the host. `ep` is ready for
|
/// Called when audio data is received from the host. `ep` is ready for
|
||||||
/// `ep.read()`.
|
/// `ep.read()`.
|
||||||
fn audio_data_rx(&self, ep: &Endpoint<'a, B, endpoint::Out>) {}
|
fn audio_data_rx(&mut self, ep: &Endpoint<'a, B, endpoint::Out>);
|
||||||
|
|
||||||
/// Called when it's time to send an isochronous feedback update. Should
|
/// Called when it's time to send an isochronous feedback update. Should
|
||||||
/// return the correct feedback payload. Should not be considered a great
|
/// return the correct feedback payload. Feedback always runs at 1ms (in
|
||||||
/// timing reference. Better to track sample timing using other means (even
|
/// this implementation), and will be passed the nominal frame size.
|
||||||
/// `audio_data_rx`).
|
|
||||||
///
|
///
|
||||||
/// Required for isochronous asynchronous mode to work properly. If None is
|
/// Required for isochronous asynchronous mode to work properly. If None is
|
||||||
/// returned, no IN packet will be emitted at feedback time.
|
/// returned, no IN packet will be emitted at feedback time.
|
||||||
fn feedback(&self) -> Option<UsbIsochronousFeedback> {
|
fn feedback(&mut self, nominal: UsbIsochronousFeedback) -> Option<UsbIsochronousFeedback>;
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called when the alternate setting of `terminal`'s interface is changed,
|
/// Called when the alternate setting of `terminal`'s interface is changed,
|
||||||
/// before the `AudioStream` is updated. Currently not very useful since we
|
/// before the `AudioStream` is updated. This is how the host signals start/stop
|
||||||
/// don't implement alternate settings.
|
/// of audio streaming. We do not expect audio frames when alt setting is 0.
|
||||||
fn alternate_setting_changed<CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>>(
|
///
|
||||||
&self,
|
/// The implementation will also call `alternate_setting_changed(_, 0)` when
|
||||||
ac: &mut AudioClass<'a, B, CS, AU>,
|
/// the host disconnects / resets.
|
||||||
terminal: UsbDirection,
|
fn alternate_setting_changed(&mut self, terminal: UsbDirection, alt_setting: u8);
|
||||||
alt_setting: u8,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait for implementing Sampling Frequency Control for USB Audio Clock Sources
|
/// A trait for implementing Sampling Frequency Control for USB Audio Clock Sources
|
||||||
@@ -196,13 +171,18 @@ pub trait UsbAudioClass<'a, B: UsbBus> {
|
|||||||
/// Unimplemented callbacks should return `Err(UsbAudioClassError::NotImplemented)`. Other
|
/// Unimplemented callbacks should return `Err(UsbAudioClassError::NotImplemented)`. Other
|
||||||
/// errors will panic (the underlying callbacks are not fallible). If you need to handle errors,
|
/// errors will panic (the underlying callbacks are not fallible). If you need to handle errors,
|
||||||
/// you should use the callback to infalliably signal another task.
|
/// you should use the callback to infalliably signal another task.
|
||||||
pub trait UsbAudioClockImpl {
|
pub trait ClockSource {
|
||||||
const CLOCK_TYPE: ClockType;
|
const CLOCK_TYPE: ClockType;
|
||||||
const SOF_SYNC: bool;
|
const SOF_SYNC: bool;
|
||||||
/// Called when the host requests the current sample rate. Returns the sample rate in Hz.
|
/// Called when the host or class needs the current sample rate. Returns the
|
||||||
fn get_sample_rate(&self) -> core::result::Result<u32, UsbAudioClassError>;
|
/// sample rate in Hz. It should be cheap and infallible as it gets called in every cycle
|
||||||
/// Called when the host requests to set the sample rate. Should reconfigure the clock source
|
/// of the feedback loop. Use clock validity to signal to the host if the clock is not usable.
|
||||||
/// if necessary.
|
///
|
||||||
|
/// Should never return 0 as it may be used in divides in the feedback loop
|
||||||
|
/// and that would cause a hard fault.
|
||||||
|
fn sample_rate(&self) -> u32;
|
||||||
|
/// Called when the host requests to set the sample rate. Not necessarily called at all startups,
|
||||||
|
/// so alt_setting should start/stop the clock. Not required for 'fixed' clocks.
|
||||||
fn set_sample_rate(
|
fn set_sample_rate(
|
||||||
&mut self,
|
&mut self,
|
||||||
sample_rate: u32,
|
sample_rate: u32,
|
||||||
@@ -211,14 +191,14 @@ pub trait UsbAudioClockImpl {
|
|||||||
}
|
}
|
||||||
/// Called when the host requests to get the clock validity. Returns `true`
|
/// Called when the host requests to get the clock validity. Returns `true`
|
||||||
/// if the clock is stable and on frequency.
|
/// if the clock is stable and on frequency.
|
||||||
fn get_clock_validity(&self) -> core::result::Result<bool, UsbAudioClassError> {
|
fn clock_validity(&self) -> core::result::Result<bool, UsbAudioClassError> {
|
||||||
Err(UsbAudioClassError::NotImplemented)
|
Err(UsbAudioClassError::NotImplemented)
|
||||||
}
|
}
|
||||||
/// Called during descriptor construction to describe if the clock validity can be read (write is not valid).
|
/// Called during descriptor construction to describe if the clock validity can be read (write is not valid).
|
||||||
///
|
///
|
||||||
/// By default will call `get_clock_validity` to determine if the clock validity can be read.
|
/// By default will call `get_clock_validity` to determine if the clock validity can be read.
|
||||||
fn get_validity_access(&self) -> core::result::Result<bool, UsbAudioClassError> {
|
fn clock_validity_access(&self) -> core::result::Result<bool, UsbAudioClassError> {
|
||||||
match self.get_clock_validity() {
|
match self.clock_validity() {
|
||||||
Ok(_) => Ok(true),
|
Ok(_) => Ok(true),
|
||||||
Err(UsbAudioClassError::NotImplemented) => Ok(false),
|
Err(UsbAudioClassError::NotImplemented) => Ok(false),
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
@@ -236,28 +216,29 @@ pub trait UsbAudioClockImpl {
|
|||||||
/// its MIN and MAX subattribute and the RES subattribute must be set to zero
|
/// its MIN and MAX subattribute and the RES subattribute must be set to zero
|
||||||
///
|
///
|
||||||
/// ref: USB Audio Class Specification 2.0 5.2.1 & 5.2.3.3
|
/// ref: USB Audio Class Specification 2.0 5.2.1 & 5.2.3.3
|
||||||
fn get_rates(&self) -> core::result::Result<&[RangeEntry<u32>], UsbAudioClassError>;
|
fn sample_rates(&self) -> core::result::Result<&[RangeEntry<u32>], UsbAudioClassError>;
|
||||||
|
|
||||||
/// Build the ClockSource descriptor. It is not intended to override this method.
|
/// Build the ClockSource descriptor. It is not intended to override this
|
||||||
|
/// method, but provided so you can.
|
||||||
///
|
///
|
||||||
/// Assumes access control based on clock type. Internal fixed/variable are read only,
|
/// Assumes access control based on clock type. Internal fixed/variable are read only,
|
||||||
/// external and internal programmable are programmable.
|
/// external and internal programmable are programmable.
|
||||||
fn get_configuration_descriptor(
|
fn clock_configuration_descriptor(
|
||||||
&self,
|
&self,
|
||||||
id: u8,
|
id: u8,
|
||||||
string: Option<StringIndex>,
|
string: Option<StringIndex>,
|
||||||
) -> usb_device::Result<ClockSource> {
|
) -> usb_device::Result<descriptors::ClockSource> {
|
||||||
let frequency_access = match Self::CLOCK_TYPE {
|
let frequency_access = match Self::CLOCK_TYPE {
|
||||||
ClockType::InternalFixed | ClockType::InternalVariable => AccessControl::ReadOnly,
|
ClockType::InternalFixed | ClockType::InternalVariable => AccessControl::ReadOnly,
|
||||||
ClockType::External | ClockType::InternalProgrammable => AccessControl::Programmable,
|
ClockType::External | ClockType::InternalProgrammable => AccessControl::Programmable,
|
||||||
};
|
};
|
||||||
let validity_access = match self.get_validity_access() {
|
let validity_access = match self.clock_validity_access() {
|
||||||
Ok(true) => AccessControl::ReadOnly,
|
Ok(true) => AccessControl::ReadOnly,
|
||||||
Ok(false) | Err(UsbAudioClassError::NotImplemented) => AccessControl::NotPresent,
|
Ok(false) | Err(UsbAudioClassError::NotImplemented) => AccessControl::NotPresent,
|
||||||
_ => return Err(UsbError::Unsupported),
|
_ => return Err(UsbError::Unsupported),
|
||||||
};
|
};
|
||||||
|
|
||||||
let cs = ClockSource {
|
let cs = descriptors::ClockSource {
|
||||||
id: id,
|
id: id,
|
||||||
clock_type: Self::CLOCK_TYPE,
|
clock_type: Self::CLOCK_TYPE,
|
||||||
sof_sync: Self::SOF_SYNC,
|
sof_sync: Self::SOF_SYNC,
|
||||||
@@ -275,6 +256,102 @@ trait TerminalConfigurationDescriptors {
|
|||||||
fn get_configuration_descriptors(&self) -> (InputTerminal, OutputTerminal);
|
fn get_configuration_descriptors(&self) -> (InputTerminal, OutputTerminal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct TerminalConfigBuilder<D: EndpointDirection> {
|
||||||
|
base_id: Option<u8>,
|
||||||
|
clock_source_id: Option<u8>,
|
||||||
|
num_channels: Option<u8>,
|
||||||
|
format: Option<FormatType1>,
|
||||||
|
terminal_type: Option<TerminalType>,
|
||||||
|
channel_config: Option<ChannelConfig>,
|
||||||
|
sync_type: Option<IsochronousSynchronizationType>,
|
||||||
|
lock_delay: Option<LockDelay>,
|
||||||
|
string: Option<StringIndex>,
|
||||||
|
_direction: PhantomData<D>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global builder initialization defaults
|
||||||
|
impl<D: EndpointDirection> TerminalConfigBuilder<D> {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
base_id: None,
|
||||||
|
clock_source_id: None,
|
||||||
|
num_channels: None,
|
||||||
|
format: None,
|
||||||
|
terminal_type: None,
|
||||||
|
channel_config: None,
|
||||||
|
sync_type: None,
|
||||||
|
lock_delay: None,
|
||||||
|
string: None,
|
||||||
|
_direction: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_id(mut self, id: u8) -> Self {
|
||||||
|
self.base_id = Some(id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clock_source_id(mut self, id: u8) -> Self {
|
||||||
|
self.clock_source_id = Some(id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn num_channels(mut self, channels: u8) -> Self {
|
||||||
|
self.num_channels = Some(channels);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(mut self, format: FormatType1) -> Self {
|
||||||
|
self.format = Some(format);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn terminal_type(mut self, t: TerminalType) -> Self {
|
||||||
|
self.terminal_type = Some(t);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn channel_config(mut self, config: ChannelConfig) -> Self {
|
||||||
|
self.channel_config = Some(config);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sync_type(mut self, sync: IsochronousSynchronizationType) -> Self {
|
||||||
|
self.sync_type = Some(sync);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lock_delay(mut self, delay: LockDelay) -> Self {
|
||||||
|
self.lock_delay = Some(delay);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn string(mut self, index: StringIndex) -> Self {
|
||||||
|
self.string = Some(index);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> TerminalConfig<D> {
|
||||||
|
TerminalConfig {
|
||||||
|
base_id: self.base_id.expect("base_id is required"),
|
||||||
|
clock_source_id: self.clock_source_id.unwrap_or(1),
|
||||||
|
num_channels: self.num_channels.unwrap_or(2),
|
||||||
|
format: self.format.unwrap_or(FormatType1 {
|
||||||
|
bytes_per_sample: 4,
|
||||||
|
bit_resolution: 32,
|
||||||
|
}),
|
||||||
|
terminal_type: self.terminal_type.unwrap_or(TerminalType::ExtUndefined),
|
||||||
|
channel_config: self
|
||||||
|
.channel_config
|
||||||
|
.unwrap_or(ChannelConfig::default_chans(self.num_channels.unwrap_or(2))),
|
||||||
|
sync_type: IsochronousSynchronizationType::Asynchronous,
|
||||||
|
lock_delay: LockDelay::Undefined(0),
|
||||||
|
string: self.string, // Implicitly handles None or Option mapping
|
||||||
|
_direction: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TerminalConfig<D: EndpointDirection> {
|
pub struct TerminalConfig<D: EndpointDirection> {
|
||||||
/// USB terminal in the D direction will have this id, audio terminal will have this id + 1
|
/// USB terminal in the D direction will have this id, audio terminal will have this id + 1
|
||||||
base_id: u8,
|
base_id: u8,
|
||||||
@@ -289,9 +366,8 @@ pub struct TerminalConfig<D: EndpointDirection> {
|
|||||||
_direction: PhantomData<D>,
|
_direction: PhantomData<D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: builder pattern
|
|
||||||
impl<D: EndpointDirection> TerminalConfig<D> {
|
impl<D: EndpointDirection> TerminalConfig<D> {
|
||||||
pub fn new(
|
fn new(
|
||||||
base_id: u8,
|
base_id: u8,
|
||||||
clock_source_id: u8,
|
clock_source_id: u8,
|
||||||
num_channels: u8,
|
num_channels: u8,
|
||||||
@@ -319,8 +395,12 @@ impl<D: EndpointDirection> TerminalConfig<D> {
|
|||||||
pub fn bytes_per_frame(&self) -> u32 {
|
pub fn bytes_per_frame(&self) -> u32 {
|
||||||
self.format.bytes_per_sample as u32 * self.num_channels as u32
|
self.format.bytes_per_sample as u32 * self.num_channels as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn builder() -> TerminalConfigBuilder<D> {
|
||||||
|
TerminalConfigBuilder::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl<'a> TerminalConfigurationDescriptors for TerminalConfig<In> {
|
impl<'a> TerminalConfigurationDescriptors for TerminalConfig<Out> {
|
||||||
fn get_configuration_descriptors(&self) -> (InputTerminal, OutputTerminal) {
|
fn get_configuration_descriptors(&self) -> (InputTerminal, OutputTerminal) {
|
||||||
let input_terminal = InputTerminal {
|
let input_terminal = InputTerminal {
|
||||||
id: self.base_id,
|
id: self.base_id,
|
||||||
@@ -357,7 +437,7 @@ impl<'a> TerminalConfigurationDescriptors for TerminalConfig<In> {
|
|||||||
// fn get_interface_descriptor(&self, id: InterfaceIndex) )
|
// fn get_interface_descriptor(&self, id: InterfaceIndex) )
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TerminalConfigurationDescriptors for TerminalConfig<Out> {
|
impl<'a> TerminalConfigurationDescriptors for TerminalConfig<In> {
|
||||||
fn get_configuration_descriptors(&self) -> (InputTerminal, OutputTerminal) {
|
fn get_configuration_descriptors(&self) -> (InputTerminal, OutputTerminal) {
|
||||||
let output_terminal = OutputTerminal {
|
let output_terminal = OutputTerminal {
|
||||||
id: self.base_id,
|
id: self.base_id,
|
||||||
@@ -419,30 +499,21 @@ pub enum UsbSpeed {
|
|||||||
/// A single Clock Source is always required, but a fully custom descriptor set can be built by only providing
|
/// A single Clock Source is always required, but a fully custom descriptor set can be built by only providing
|
||||||
/// the Clock Source and additional descriptors, if the Terminal descriptors are inappropriate.
|
/// the Clock Source and additional descriptors, if the Terminal descriptors are inappropriate.
|
||||||
///
|
///
|
||||||
pub struct AudioClassConfig<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> {
|
pub struct UsbAudioClassConfig<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> {
|
||||||
pub speed: UsbSpeed,
|
pub speed: UsbSpeed,
|
||||||
pub device_category: FunctionCode,
|
pub device_category: FunctionCode,
|
||||||
pub clock_impl: &'a CS,
|
pub audio_impl: &'a mut AU,
|
||||||
pub audio_impl: &'a AU,
|
pub input_config: Option<TerminalConfig<In>>,
|
||||||
pub input_config: Option<TerminalConfig<Out>>,
|
pub output_config: Option<TerminalConfig<Out>>,
|
||||||
pub output_config: Option<TerminalConfig<In>>,
|
|
||||||
pub additional_descriptors: Option<&'a [AudioClassDescriptor]>,
|
pub additional_descriptors: Option<&'a [AudioClassDescriptor]>,
|
||||||
_bus: PhantomData<B>,
|
_bus: PhantomData<B>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>>
|
impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbAudioClassConfig<'a, B, AU> {
|
||||||
AudioClassConfig<'a, B, CS, AU>
|
pub fn new(speed: UsbSpeed, device_category: FunctionCode, audio_impl: &'a mut AU) -> Self {
|
||||||
{
|
|
||||||
pub fn new(
|
|
||||||
speed: UsbSpeed,
|
|
||||||
device_category: FunctionCode,
|
|
||||||
clock_impl: &'a CS,
|
|
||||||
audio_impl: &'a AU,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
speed,
|
speed,
|
||||||
device_category,
|
device_category,
|
||||||
clock_impl,
|
|
||||||
audio_impl,
|
audio_impl,
|
||||||
input_config: None,
|
input_config: None,
|
||||||
output_config: None,
|
output_config: None,
|
||||||
@@ -450,11 +521,11 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>>
|
|||||||
_bus: PhantomData,
|
_bus: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn with_input_config(mut self, input_config: TerminalConfig<Out>) -> Self {
|
pub fn with_input_config(mut self, input_config: TerminalConfig<In>) -> Self {
|
||||||
self.input_config = Some(input_config);
|
self.input_config = Some(input_config);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn with_output_config(mut self, output_config: TerminalConfig<In>) -> Self {
|
pub fn with_output_config(mut self, output_config: TerminalConfig<Out>) -> Self {
|
||||||
self.output_config = Some(output_config);
|
self.output_config = Some(output_config);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -467,16 +538,16 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate the various USB IDs, and build the class implementation
|
/// Allocate the various USB IDs, and build the class implementation
|
||||||
pub fn build(self, alloc: &'a UsbBusAllocator<B>) -> Result<AudioClass<'a, B, CS, AU>> {
|
pub fn build(self, alloc: &'a UsbBusAllocator<B>) -> Result<UsbAudioClass<'a, B, AU>> {
|
||||||
let speed = self.speed;
|
let speed = self.speed;
|
||||||
let interval = match speed {
|
let (interval, fb_interval, audio_rate) = match speed {
|
||||||
UsbSpeed::Full => 1,
|
UsbSpeed::Full => (1, 1, 1000),
|
||||||
UsbSpeed::High | UsbSpeed::Super => 4, // rate = 2^(4-1) * 125us = 1ms, same as full speed
|
UsbSpeed::High | UsbSpeed::Super => (1, 4, 8000), //
|
||||||
UsbSpeed::Low => return Err(Error::InvalidSpeed),
|
UsbSpeed::Low => return Err(Error::InvalidSpeed),
|
||||||
};
|
};
|
||||||
let max_rate = self
|
let max_rate = self
|
||||||
.clock_impl
|
.audio_impl
|
||||||
.get_rates()
|
.sample_rates()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.max()
|
.max()
|
||||||
@@ -484,9 +555,12 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>>
|
|||||||
.max;
|
.max;
|
||||||
let control_iface = alloc.interface();
|
let control_iface = alloc.interface();
|
||||||
|
|
||||||
let mut ac = AudioClass {
|
let nominal_fb = UsbIsochronousFeedback::new_float(
|
||||||
|
self.audio_impl.sample_rate().to_f32().unwrap() / audio_rate.to_f32().unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut ac = UsbAudioClass {
|
||||||
control_iface,
|
control_iface,
|
||||||
clock_impl: self.clock_impl,
|
|
||||||
audio_impl: self.audio_impl,
|
audio_impl: self.audio_impl,
|
||||||
output: None,
|
output: None,
|
||||||
input: None,
|
input: None,
|
||||||
@@ -499,6 +573,8 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>>
|
|||||||
out_ep: 0,
|
out_ep: 0,
|
||||||
fb_ep: 0,
|
fb_ep: 0,
|
||||||
speed,
|
speed,
|
||||||
|
nominal_fb,
|
||||||
|
audio_rate,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(config) = self.output_config {
|
if let Some(config) = self.output_config {
|
||||||
@@ -506,20 +582,20 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>>
|
|||||||
let endpoint = alloc.isochronous(
|
let endpoint = alloc.isochronous(
|
||||||
config.sync_type,
|
config.sync_type,
|
||||||
IsochronousUsageType::Data,
|
IsochronousUsageType::Data,
|
||||||
((config.bytes_per_frame() * max_rate) / 1000) as u16,
|
((max_rate.div_ceil(audio_rate) + 1) * config.bytes_per_frame()) as u16, // headroom of 1 sample for rate control
|
||||||
interval,
|
interval,
|
||||||
);
|
);
|
||||||
let feedback_ep = alloc.isochronous(
|
let feedback_ep = alloc.isochronous(
|
||||||
IsochronousSynchronizationType::NoSynchronization,
|
IsochronousSynchronizationType::NoSynchronization,
|
||||||
IsochronousUsageType::Feedback,
|
IsochronousUsageType::Feedback,
|
||||||
4,
|
4,
|
||||||
interval,
|
fb_interval,
|
||||||
);
|
);
|
||||||
let alt_setting = DEFAULT_ALTERNATE_SETTING;
|
let alt_setting = DEFAULT_ALTERNATE_SETTING;
|
||||||
ac.in_iface = interface.into();
|
ac.out_iface = interface.into();
|
||||||
ac.in_ep = endpoint.address().index();
|
ac.out_ep = endpoint.address().index();
|
||||||
ac.fb_ep = feedback_ep.address().index();
|
ac.fb_ep = feedback_ep.address().index();
|
||||||
ac.input = Some(AudioStream {
|
ac.output = Some(AudioStream {
|
||||||
config,
|
config,
|
||||||
interface,
|
interface,
|
||||||
endpoint,
|
endpoint,
|
||||||
@@ -533,13 +609,14 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>>
|
|||||||
let endpoint = alloc.isochronous(
|
let endpoint = alloc.isochronous(
|
||||||
config.sync_type,
|
config.sync_type,
|
||||||
IsochronousUsageType::Data,
|
IsochronousUsageType::Data,
|
||||||
((config.bytes_per_frame() * max_rate) / 1000) as u16,
|
((max_rate.div_ceil(audio_rate) + 1) * config.bytes_per_frame()) as u16, // headroom of 1 sample for rate control
|
||||||
interval,
|
interval,
|
||||||
);
|
);
|
||||||
let alt_setting = DEFAULT_ALTERNATE_SETTING;
|
let alt_setting = DEFAULT_ALTERNATE_SETTING;
|
||||||
ac.out_iface = interface.into();
|
ac.in_iface = interface.into();
|
||||||
ac.out_ep = endpoint.address().index();
|
ac.in_ep = endpoint.address().index();
|
||||||
ac.output = Some(AudioStream {
|
|
||||||
|
ac.input = Some(AudioStream {
|
||||||
config,
|
config,
|
||||||
interface,
|
interface,
|
||||||
endpoint,
|
endpoint,
|
||||||
@@ -649,10 +726,9 @@ impl<'a, B: UsbBus, D: EndpointDirection> AudioStream<'a, B, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AudioClass<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> {
|
pub struct UsbAudioClass<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> {
|
||||||
control_iface: InterfaceNumber,
|
control_iface: InterfaceNumber,
|
||||||
clock_impl: &'a CS,
|
audio_impl: &'a mut AU,
|
||||||
audio_impl: &'a AU,
|
|
||||||
output: Option<AudioStream<'a, B, Out>>,
|
output: Option<AudioStream<'a, B, Out>>,
|
||||||
input: Option<AudioStream<'a, B, In>>,
|
input: Option<AudioStream<'a, B, In>>,
|
||||||
feedback: Option<Endpoint<'a, B, In>>,
|
feedback: Option<Endpoint<'a, B, In>>,
|
||||||
@@ -664,10 +740,12 @@ pub struct AudioClass<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a
|
|||||||
out_ep: usize,
|
out_ep: usize,
|
||||||
fb_ep: usize,
|
fb_ep: usize,
|
||||||
speed: UsbSpeed,
|
speed: UsbSpeed,
|
||||||
|
nominal_fb: UsbIsochronousFeedback,
|
||||||
|
audio_rate: u32, // audio packet rate in hz
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> UsbClass<B>
|
impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbClass<B>
|
||||||
for AudioClass<'a, B, CS, AU>
|
for UsbAudioClass<'a, B, AU>
|
||||||
{
|
{
|
||||||
fn get_configuration_descriptors(
|
fn get_configuration_descriptors(
|
||||||
&self,
|
&self,
|
||||||
@@ -675,7 +753,7 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> UsbClass<B>
|
|||||||
) -> usb_device::Result<()> {
|
) -> usb_device::Result<()> {
|
||||||
info!(" AudioClass::get_configuration_descriptors");
|
info!(" AudioClass::get_configuration_descriptors");
|
||||||
// Control + 0-2 streaming
|
// Control + 0-2 streaming
|
||||||
let n_interfaces = 1 + (self.input.is_some() as u8) + (self.input.is_some() as u8);
|
let n_interfaces = 1 + (self.input.is_some() as u8) + (self.output.is_some() as u8);
|
||||||
|
|
||||||
debug!("writer.iad()");
|
debug!("writer.iad()");
|
||||||
// UAC2 4.6 Interface Association Descriptor
|
// UAC2 4.6 Interface Association Descriptor
|
||||||
@@ -699,7 +777,7 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> UsbClass<B>
|
|||||||
|
|
||||||
// BUILD CONFIGURATION DESCRIPTORS //
|
// BUILD CONFIGURATION DESCRIPTORS //
|
||||||
let mut total_length: u16 = 9; // HEADER
|
let mut total_length: u16 = 9; // HEADER
|
||||||
let clock_desc = self.clock_impl.get_configuration_descriptor(1, None)?;
|
let clock_desc = self.audio_impl.clock_configuration_descriptor(1, None)?;
|
||||||
total_length += clock_desc.size() as u16;
|
total_length += clock_desc.size() as u16;
|
||||||
let output_descs = match &self.output {
|
let output_descs = match &self.output {
|
||||||
Some(stream) => {
|
Some(stream) => {
|
||||||
@@ -805,59 +883,79 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> UsbClass<B>
|
|||||||
}
|
}
|
||||||
fn endpoint_out(&mut self, addr: EndpointAddress) {
|
fn endpoint_out(&mut self, addr: EndpointAddress) {
|
||||||
debug!("EP {} out data", addr);
|
debug!("EP {} out data", addr);
|
||||||
static COUNTER: AtomicUsize = AtomicUsize::new(0);
|
|
||||||
if addr.index() == self.out_ep {
|
if addr.index() == self.out_ep {
|
||||||
self.audio_impl
|
self.audio_impl
|
||||||
.audio_data_rx(&self.output.as_ref().unwrap().endpoint);
|
.audio_data_rx(&self.output.as_ref().unwrap().endpoint);
|
||||||
let new_count = COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
|
|
||||||
if new_count.is_multiple_of(1 as usize) {
|
|
||||||
if let Some(fb) = self.audio_impl.feedback() {
|
|
||||||
debug!(" emitting feedback IN {:08x}", fb.to_u32_12_13());
|
|
||||||
let r = match self.speed {
|
|
||||||
UsbSpeed::Low | UsbSpeed::Full => {
|
|
||||||
self.feedback.as_ref().unwrap().write(&fb.to_bytes_10_14())
|
|
||||||
}
|
|
||||||
UsbSpeed::High | UsbSpeed::Super => {
|
|
||||||
self.feedback.as_ref().unwrap().write(&fb.to_bytes_12_13())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Err(e) = r {
|
|
||||||
warn!(" feedback IN failed {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
debug!(" unexpected OUT on {}", addr);
|
debug!(" unexpected OUT on {}", addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn endpoint_in_complete(&mut self, addr: EndpointAddress) {
|
||||||
|
debug!("EP {} IN complete", addr);
|
||||||
|
if let Some(fb_ep) = self.feedback.as_ref()
|
||||||
|
&& addr.index() == self.fb_ep
|
||||||
|
{
|
||||||
|
self.emit_feedback();
|
||||||
|
} else {
|
||||||
|
debug!(" unexpected IN on {}", addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn poll(&mut self) {
|
fn poll(&mut self) {
|
||||||
debug!("poll");
|
debug!("poll");
|
||||||
// no streaming in alt 0
|
// no streaming in alt 0
|
||||||
if self.output.as_ref().unwrap().alt_setting != 1 {
|
if self.output.as_ref().is_none_or(|o| o.alt_setting != 0)
|
||||||
|
|| self.input.as_ref().is_none_or(|i| i.alt_setting != 0)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
let mut buf = [0; 1024];
|
if let Some(o) = self.output.as_ref() {
|
||||||
match self.output.as_ref().unwrap().endpoint.read(&mut buf) {
|
let mut buf = [0; 1024];
|
||||||
Ok(len) if len > 0 => {
|
match o.endpoint.read(&mut buf) {
|
||||||
debug!("EP OUT data {:?}", len);
|
Ok(len) if len > 0 => {
|
||||||
}
|
debug!("EP OUT data {:?}", len);
|
||||||
Ok(_) => {
|
}
|
||||||
debug!("EP OUT empty");
|
Ok(_) => {
|
||||||
break;
|
debug!("EP OUT empty");
|
||||||
}
|
break;
|
||||||
Err(UsbError::WouldBlock) => break,
|
}
|
||||||
Err(err) => {
|
Err(UsbError::WouldBlock) => break,
|
||||||
debug!("EP OUT error {:?}", err);
|
Err(err) => {
|
||||||
|
debug!("EP OUT error {:?}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
info!("usb reset");
|
||||||
|
self.audio_impl
|
||||||
|
.alternate_setting_changed(UsbDirection::In, 0);
|
||||||
|
self.audio_impl
|
||||||
|
.alternate_setting_changed(UsbDirection::Out, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass<'a, B, CS, AU> {
|
impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbAudioClass<'a, B, AU> {
|
||||||
|
fn emit_feedback(&mut self) {
|
||||||
|
if let Some(fb_ep) = self.feedback.as_ref() {
|
||||||
|
if let Some(fb) = self.audio_impl.feedback(self.nominal_fb) {
|
||||||
|
debug!(" emitting feedback IN {:08x}", fb.to_u32_12_13());
|
||||||
|
let r = match self.speed {
|
||||||
|
UsbSpeed::Low | UsbSpeed::Full => fb_ep.write(&fb.to_bytes_10_14()),
|
||||||
|
UsbSpeed::High | UsbSpeed::Super => fb_ep.write(&fb.to_bytes_12_13()),
|
||||||
|
};
|
||||||
|
if let Err(e) = r {
|
||||||
|
warn!(" feedback IN failed {:?}", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug!(" feedback callback returned None");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
fn standard_request_out(&mut self, xfer: ControlOut<B>) {
|
fn standard_request_out(&mut self, xfer: ControlOut<B>) {
|
||||||
let req = xfer.request();
|
let req = xfer.request();
|
||||||
match (req.recipient, req.request) {
|
match (req.recipient, req.request) {
|
||||||
@@ -918,7 +1016,7 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass<
|
|||||||
let old_alt = self.input.as_ref().unwrap().alt_setting;
|
let old_alt = self.input.as_ref().unwrap().alt_setting;
|
||||||
if old_alt != alt_setting {
|
if old_alt != alt_setting {
|
||||||
self.audio_impl
|
self.audio_impl
|
||||||
.alternate_setting_changed(self, UsbDirection::In, alt_setting);
|
.alternate_setting_changed(UsbDirection::In, alt_setting);
|
||||||
self.input.as_mut().unwrap().alt_setting = alt_setting;
|
self.input.as_mut().unwrap().alt_setting = alt_setting;
|
||||||
xfer.accept().ok();
|
xfer.accept().ok();
|
||||||
}
|
}
|
||||||
@@ -926,7 +1024,9 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass<
|
|||||||
let old_alt = self.output.as_ref().unwrap().alt_setting;
|
let old_alt = self.output.as_ref().unwrap().alt_setting;
|
||||||
if old_alt != alt_setting {
|
if old_alt != alt_setting {
|
||||||
self.audio_impl
|
self.audio_impl
|
||||||
.alternate_setting_changed(self, UsbDirection::Out, alt_setting);
|
.alternate_setting_changed(UsbDirection::Out, alt_setting);
|
||||||
|
// Start the IN cycle running
|
||||||
|
self.emit_feedback();
|
||||||
self.output.as_mut().unwrap().alt_setting = alt_setting;
|
self.output.as_mut().unwrap().alt_setting = alt_setting;
|
||||||
xfer.accept().ok();
|
xfer.accept().ok();
|
||||||
}
|
}
|
||||||
@@ -1023,6 +1123,10 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass<
|
|||||||
channel: u8,
|
channel: u8,
|
||||||
control: u8,
|
control: u8,
|
||||||
) {
|
) {
|
||||||
|
match entity {
|
||||||
|
1 => return self.set_clock_cur(xfer, channel, control),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
debug!(" Unimplemented.");
|
debug!(" Unimplemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1108,14 +1212,13 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass<
|
|||||||
channel
|
channel
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
xfer.accept(|mut buf| match self.clock_impl.get_sample_rate() {
|
xfer.accept(|mut buf| {
|
||||||
Ok(rate) => {
|
let rate = self.audio_impl.sample_rate();
|
||||||
debug!(" {}", rate);
|
|
||||||
buf.write_u32::<LittleEndian>(rate)
|
debug!(" {}", rate);
|
||||||
.map_err(|_e| UsbError::BufferOverflow)?;
|
buf.write_u32::<LittleEndian>(rate)
|
||||||
Ok(4)
|
.map_err(|_e| UsbError::BufferOverflow)?;
|
||||||
}
|
Ok(4)
|
||||||
Err(_e) => Err(UsbError::InvalidState),
|
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
@@ -1127,11 +1230,12 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass<
|
|||||||
channel
|
channel
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
xfer.accept(|mut buf| match self.clock_impl.get_clock_validity() {
|
xfer.accept(|mut buf| match self.audio_impl.clock_validity() {
|
||||||
Ok(valid) => {
|
Ok(valid) => {
|
||||||
debug!(" {}", valid);
|
debug!(" {}", valid);
|
||||||
buf.write_u8(valid as u8)
|
buf.write_u8(valid as u8)
|
||||||
.map_err(|_e| UsbError::BufferOverflow)?;
|
.map_err(|_e| UsbError::BufferOverflow)
|
||||||
|
.ok();
|
||||||
Ok(1)
|
Ok(1)
|
||||||
}
|
}
|
||||||
Err(_e) => Err(UsbError::InvalidState),
|
Err(_e) => Err(UsbError::InvalidState),
|
||||||
@@ -1143,6 +1247,35 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn set_clock_cur(&mut self, xfer: ControlOut<B>, channel: u8, control: u8) {
|
||||||
|
match control.try_into() {
|
||||||
|
Ok(ClockSourceControlSelector::SamFreqControl) => {
|
||||||
|
debug!(" SamplingFreqControl");
|
||||||
|
if channel != 0 {
|
||||||
|
error!(
|
||||||
|
" Invalid channel {} for SamplingFreqControl GET CUR. Ignoring.",
|
||||||
|
channel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
match xfer.data().read_u32::<LittleEndian>() {
|
||||||
|
Ok(rate) => {
|
||||||
|
debug!(" SET SamplingFreqControl CUR {}", rate);
|
||||||
|
self.audio_impl.set_sample_rate(rate).ok();
|
||||||
|
self.nominal_fb = UsbIsochronousFeedback::new_float(
|
||||||
|
rate.to_f32().unwrap() / self.audio_rate.to_f32().unwrap(),
|
||||||
|
);
|
||||||
|
xfer.accept().ok();
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(" SET SamplingFreqControl CUR ERROR BAD DATA");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
debug!(" Unimplemented.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
fn get_clock_range(&mut self, xfer: ControlIn<B>, channel: u8, control: u8) {
|
fn get_clock_range(&mut self, xfer: ControlIn<B>, channel: u8, control: u8) {
|
||||||
match control.try_into() {
|
match control.try_into() {
|
||||||
Ok(ClockSourceControlSelector::SamFreqControl) => {
|
Ok(ClockSourceControlSelector::SamFreqControl) => {
|
||||||
@@ -1153,7 +1286,7 @@ impl<'a, B: UsbBus, CS: UsbAudioClockImpl, AU: UsbAudioClass<'a, B>> AudioClass<
|
|||||||
channel
|
channel
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
xfer.accept(|mut buf| match self.clock_impl.get_rates() {
|
xfer.accept(|mut buf| match self.audio_impl.sample_rates() {
|
||||||
Ok(rates) => {
|
Ok(rates) => {
|
||||||
buf.write_u16::<LittleEndian>(rates.len() as u16)
|
buf.write_u16::<LittleEndian>(rates.len() as u16)
|
||||||
.map_err(|_e| UsbError::BufferOverflow)?;
|
.map_err(|_e| UsbError::BufferOverflow)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user