diff --git a/Cargo.lock b/Cargo.lock index f963bce..be20542 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,71 @@ # It is not intended for manual editing. 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]] 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 = "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]] 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" @@ -30,20 +83,111 @@ dependencies = [ "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]] name = "defmt" version = "0.3.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad" dependencies = [ - "defmt 1.0.1", + "defmt 1.1.0", ] [[package]] name = "defmt" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f" dependencies = [ "bitflags", "defmt-macros", @@ -51,9 +195,9 @@ dependencies = [ [[package]] name = "defmt-macros" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b" dependencies = [ "defmt-parser", "proc-macro-error2", @@ -71,12 +215,107 @@ dependencies = [ "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]] 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 = "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]] name = "hash32" version = "0.3.1" @@ -86,16 +325,188 @@ 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", + "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 = "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]] name = "modular-bitfield" version = "0.13.1" @@ -117,6 +528,95 @@ dependencies = [ "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]] name = "num-traits" version = "0.2.19" @@ -126,6 +626,48 @@ dependencies = [ "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]] name = "portable-atomic" version = "1.13.1" @@ -172,6 +714,116 @@ 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 = "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]] name = "stable_deref_trait" version = "1.2.1" @@ -184,6 +836,15 @@ 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" @@ -215,6 +876,82 @@ dependencies = [ "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]] name = "unicode-ident" version = "1.0.24" @@ -228,7 +965,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" dependencies = [ "defmt 0.3.100", - "heapless", + "heapless 0.8.0", "portable-atomic", ] @@ -237,9 +974,66 @@ name = "usbd-uac2" version = "0.1.0" dependencies = [ "byteorder-embedded-io", - "defmt 1.0.1", + "defmt 1.1.0", "embedded-io", "modular-bitfield", "num-traits", "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", +] diff --git a/Cargo.toml b/Cargo.toml index c38200d..0bb5b10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] name = "usbd-uac2" description = "USB Audio Class 2.0 for usb-device" authors = ["Keenan Tims "] version = "0.1.0" edition = "2024" -keywords = ["no-std", "usb-device"] +keywords = ["no-std", "usb-device", "audio"] [features] defmt = ["dep:defmt", "usb-device/defmt"] @@ -15,4 +26,4 @@ defmt = { version = "1.0.1", optional = true } embedded-io = "0.7.1" modular-bitfield = "0.13.1" num-traits = { version = "0.2.19", default-features = false } -usb-device = { version = "0.3", features = ["control-buffer-256"] } +usb-device.workspace = true diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..1ef2b03 --- /dev/null +++ b/examples/README.md @@ -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 diff --git a/examples/lpc55s28-evk-dma/Cargo.lock b/examples/lpc55s28-evk-dma/Cargo.lock index 62cff03..ebe4aa0 100644 --- a/examples/lpc55s28-evk-dma/Cargo.lock +++ b/examples/lpc55s28-evk-dma/Cargo.lock @@ -313,6 +313,7 @@ dependencies = [ [[package]] name = "lpc55-hal" version = "0.5.0" +source = "git+https://github.com/ktims/lpc55-hal?branch=main#8dfefd62aff4abd2de535f23107812dda68437be" dependencies = [ "block-buffer", "cipher", @@ -353,7 +354,6 @@ dependencies = [ "log-to-defmt", "lpc55-hal", "nb 1.1.0", - "panic-halt", "panic-probe", "static_cell", "usb-device", @@ -458,12 +458,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "panic-halt" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11" - [[package]] name = "panic-probe" version = "1.0.0" diff --git a/examples/lpc55s28-evk-dma/Cargo.toml b/examples/lpc55s28-evk-dma/Cargo.toml index a2ea54d..4765b73 100644 --- a/examples/lpc55s28-evk-dma/Cargo.toml +++ b/examples/lpc55s28-evk-dma/Cargo.toml @@ -2,6 +2,7 @@ name = "lpc55s28-evk-dma" version = "0.1.0" edition = "2024" +publish = false [features] default = ["usbhs"] @@ -16,13 +17,13 @@ defmt-rtt = "1.1.0" embedded-hal = "1.0.0" embedded-io = "0.7.1" 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"] } static_cell = "2.1.1" -usb-device = "0.3" -usbd-uac2 = { version = "0.1.0", path = "../..", features = ["defmt"]} +# 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" diff --git a/examples/lpc55s28-evk-dma/Embed.toml b/examples/lpc55s28-evk-dma/Embed.toml new file mode 100644 index 0000000..a897fdf --- /dev/null +++ b/examples/lpc55s28-evk-dma/Embed.toml @@ -0,0 +1,9 @@ +[default.general] +chip = "LPC55S28JBD100" +[default.rtt] +enabled = true +[default.gdb] +enabled = true + +[debug.rtt] +enabled = false diff --git a/examples/lpc55s28-evk-dma/build.rs b/examples/lpc55s28-evk-dma/build.rs new file mode 100644 index 0000000..ffce9b8 --- /dev/null +++ b/examples/lpc55s28-evk-dma/build.rs @@ -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); +} diff --git a/examples/lpc55s28-evk-dma/src/dma.rs b/examples/lpc55s28-evk-dma/src/dma.rs index 6ffb0c6..e62f21d 100644 --- a/examples/lpc55s28-evk-dma/src/dma.rs +++ b/examples/lpc55s28-evk-dma/src/dma.rs @@ -233,13 +233,37 @@ impl DmaRing { 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 { - self.consumed.load(Ordering::Acquire) * self.slot_bytes - + (self.dma.channel19.xfercfg.read().bits() as usize >> 16 & 0x3ff) + 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 { @@ -319,7 +343,7 @@ impl DmaRing { let slots = unsafe { &mut *self.slots.get() }; let desc = unsafe { &mut *self.desc.get() }; let chan_desc = unsafe { &mut *self.channel_desc.get() }; - defmt::info!("slots base: &{:x}", self.slots.get()); + defmt::debug!("slots base: &{:x}", self.slots.get()); // Pre-fill with silence so underrun replays silence. for i in 0..N { diff --git a/examples/lpc55s28-evk-dma/src/hw.rs b/examples/lpc55s28-evk-dma/src/hw.rs index c6a1b3d..feb3640 100644 --- a/examples/lpc55s28-evk-dma/src/hw.rs +++ b/examples/lpc55s28-evk-dma/src/hw.rs @@ -1,22 +1,19 @@ //! Contains hardware setup unrelated to Usb Audio Class implementation -use crate::hal; -use core::cell::{OnceCell, UnsafeCell}; -use core::mem::MaybeUninit; -use core::ptr::null_mut; - use crate::Syscon; +use crate::hal; use crate::{MCLK_FREQ, SAMPLE_RATE, pac}; -use defmt::debug; + +use core::cell::UnsafeCell; +use core::mem::MaybeUninit; +use defmt::{debug, info}; use hal::{ - Iocon, Pin, + Enabled, Iocon, Pin, drivers::pins, - prelude::*, traits::wg::digital::v2::{OutputPin, ToggleableOutputPin}, typestates::pin::{gpio::direction::Output, state::Gpio}, }; -use lpc55_hal::Enabled; -use static_cell::StaticCell; + pub(crate) struct PllConstants { pub m: u16, // 1-65535 pub n: u8, // 1-255 @@ -138,67 +135,12 @@ pub(crate) fn init_audio_pll() { pmc.pdruncfg0 .modify(|_, w| w.pden_pll0().poweredon().pden_pll0_sscg().poweredon()); - debug!("pll0 wait for lock"); + info!("pll0 wait for lock"); let mut i = 0usize; while syscon.pll0stat.read().lock().bit_is_clear() { i += 1; } - debug!("pll0 locked after {} tries", i); -} - -const SYS_PLL: PllConstants = PllConstants::new(4, 75, 1); // 150MHz - -pub(crate) fn init_sys_pll1() { - 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 pll1: {}", SYS_PLL); - pmc.pdruncfg0.modify(|_, w| w.pden_pll1().poweredoff()); - syscon.pll1clksel.write(|w| w.sel().enum_0x1()); // clk_in - syscon.pll1ctrl.write(|w| unsafe { - w.clken() - .enable() - .seli() - .bits(SYS_PLL.seli) - .selp() - .bits(SYS_PLL.selp) - }); - - syscon - .pll1ndec - .write(|w| unsafe { w.ndiv().bits(SYS_PLL.n) }); - syscon.pll1ndec.write(|w| unsafe { - w.ndiv().bits(SYS_PLL.n).nreq().set_bit() // latch - }); - syscon - .pll1mdec - .write(|w| unsafe { w.mdiv().bits(SYS_PLL.m) }); - syscon - .pll1pdec - .write(|w| unsafe { w.pdiv().bits(SYS_PLL.p) }); - syscon.pll1pdec.write(|w| unsafe { - w.pdiv().bits(SYS_PLL.p).preq().set_bit() // latch - }); - - pmc.pdruncfg0.modify(|_, w| w.pden_pll1().poweredon()); - debug!("pll1 wait for lock"); - let mut i = 0usize; - while syscon.pll1stat.read().lock().bit_is_clear() { - i += 1; - } - debug!("pll1 locked after {} tries", i); - // switch system clock to pll1 - syscon.fmccr.modify(|_, w| w.flashtim().flashtim11()); - syscon.mainclkselb.modify(|_, w| w.sel().enum_0x2()); // pll1 + info!("pll0 locked after {} loops", i); } pub struct I2sTx { @@ -207,7 +149,6 @@ pub struct I2sTx { 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); diff --git a/examples/lpc55s28-evk-dma/src/main.rs b/examples/lpc55s28-evk-dma/src/main.rs index 0df56d3..d2590b2 100644 --- a/examples/lpc55s28-evk-dma/src/main.rs +++ b/examples/lpc55s28-evk-dma/src/main.rs @@ -1,10 +1,15 @@ -//! Interrupt driven example for the LPCXpresso55S28 demo board +//! DMA based audio output example for the LPCXpresso55S28 demo board //! -//! Uses the onboard WM8904 DAC at 48KHz. Clock is generated by PLL0. Simple PI feedback -//! is implemented. +//! Uses the onboard WM8904 DAC at 96KHz. Clock is generated by PLL0. Simple proportional feedback is implemented. //! -//! Packets from USB are placed a `heapless::spsc::Queue`. They are consumed -//! by the I2S FIFO in the FLEXCOMM7 interrupt. +//! 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] @@ -34,13 +39,11 @@ use static_cell::StaticCell; use usb_device::{ bus::{self}, device::{StringDescriptors, UsbDeviceBuilder, UsbVidPid}, - endpoint::IsochronousSynchronizationType, }; -use usbd_uac2::UsbIsochronousFeedback; +use usbd_uac2::TerminalConfig; use usbd_uac2::{ - self, AudioClassConfig, RangeEntry, TerminalConfig, UsbAudioClass, UsbAudioClockImpl, UsbSpeed, - constants::{FunctionCode, TerminalType}, - descriptors::{ChannelConfig, ClockType, FormatType1, LockDelay}, + self, AudioHandler, ClockSource, RangeEntry, UsbAudioClassConfig, UsbIsochronousFeedback, + UsbSpeed, constants::FunctionCode, descriptors::ClockType, }; use crate::dma::DmaRing; @@ -52,41 +55,27 @@ 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 SLOT_SIZE_BYTES: usize = FRAMES_PER_SLOT * BYTES_PER_FRAME; // run the DMA at 2khz -const N_SLOTS: usize = 32; -const FILL_TARGET: i32 = (FRAMES_PER_SLOT * N_SLOTS) as i32 / 2; +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; -struct Clock {} -impl Clock { - const RATES: [RangeEntry; 1] = [RangeEntry::new_fixed(SAMPLE_RATE)]; -} -impl UsbAudioClockImpl for Clock { - const CLOCK_TYPE: usbd_uac2::descriptors::ClockType = ClockType::InternalFixed; - const SOF_SYNC: bool = false; - fn get_sample_rate(&self) -> u32 { - Clock::RATES[0].min - } - fn get_rates( - &self, - ) -> core::result::Result<&[usbd_uac2::RangeEntry], usbd_uac2::UsbAudioClassError> { - Ok(&Clock::RATES) - } - fn get_clock_validity(&self) -> core::result::Result { - Ok(true) - } -} +const LOG_PERIOD: u32 = 1000; -static DMA_RING: StaticCell> = StaticCell::new(); -static mut DMA_RING_REF: Option<&'static DmaRing> = None; +static DMA_RING: StaticCell> = StaticCell::new(); +static mut DMA_RING_REF: Option<&'static DmaRing> = None; #[inline] -fn dma_ring() -> &'static DmaRing { +fn dma_ring() -> &'static DmaRing { unsafe { DMA_RING_REF.unwrap() } } @@ -125,13 +114,15 @@ struct Audio<'a, const N: usize, const MAX_SLOT_BYTES: usize> { running: AtomicBool, i2s: I2sTx, dma: &'a DmaRing, + log_counter: u32, } impl Audio<'_, N, MAX_SLOT_BYTES> { - fn start(&self) { + const RATES: [RangeEntry; 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 starting (DMA)"); + defmt::info!("playback armed (DMA)"); let i2s = &self.i2s.i2s; i2s.fifotrig @@ -150,15 +141,15 @@ impl Audio<'_, N, MAX_SLOT_BYTES> { // 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"); - pac::NVIC::mask(pac::Interrupt::DMA0); green_led().off(); blue_led().off(); } } -impl UsbAudioClass<'_, B> +impl AudioHandler<'_, B> for Audio<'_, N, MAX_SLOT_BYTES> { fn alternate_setting_changed(&mut self, _terminal: usb_device::UsbDirection, alt_setting: u8) { @@ -174,7 +165,8 @@ impl UsbAudioClass< 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). - let mut buf = [0; SAMPLE_RATE.div_ceil(8000) as usize * BYTES_PER_FRAME]; + // 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, @@ -199,8 +191,8 @@ impl UsbAudioClass< } // 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::debug!( - "buffer has {} slots, start dma transfers", + defmt::info!( + "buffer warmed ({} slots) starting playback", self.dma.fill_slots() ); self.dma.run(); @@ -208,52 +200,76 @@ impl UsbAudioClass< } } - /// Provide rate feedback to the host, so that it doesn't over- or underflow - /// the buffer. Proportional-only control is stable with normal hosts, - /// adding an I term with proper tuning (quite weak) would stabilize the - /// rate reported to the host but is not necessary for basic playback. + /// 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 { - const MAX_CORRECTION: i32 = 1 << 10; // ~1.6% + // 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.load(Ordering::Acquire); + let produced_bytes = self.dma.produced_bytes(); let consumed_bytes = self.dma.consumed_bytes(); - let valid = produced_bytes >= consumed_bytes; // else we are in underrun condition + if produced_bytes < consumed_bytes || produced_bytes == 0 { + defmt::error!("[fb] dma underrun detected!"); + red_led().on(); + return Some(nominal_rate); + } - let fill_frames = if valid { - (produced_bytes - consumed_bytes) as i32 / BYTES_PER_FRAME as i32 - } else { - // we will emit a canonical error in the DMA ISR - defmt::debug!("[fb] dma underrun detected"); - 0 - }; - - let mut error = fill_frames - FILL_TARGET; - error = error.clamp(-32, 32); // avoid huge spikes - - let p = error * 256; - let i = 0; // placeholder - - let correction = -(p + i); + 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; - let mut v = nominal_v + correction; + // 0.2% which is a huge clock error + let max_allowed_deviation = nominal_v / 500; - v = v.clamp(nominal_v - MAX_CORRECTION, nominal_v + MAX_CORRECTION); + let p_term = -(error_permille * nominal_v) / 256000; // this works reasonably well to keep the buffer + let i_term = 0; // placeholder - defmt::debug!( - "valid:{} fill:{} err:{} fb:{=u32:x}", - valid, - fill_frames, - error, - v as u32 + 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 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], usbd_uac2::UsbAudioClassError> { + Ok(&Self::RATES) + } + fn clock_validity(&self) -> core::result::Result { + Ok(true) + } +} + #[entry] fn main() -> ! { let hal = hal::new(); @@ -277,7 +293,7 @@ fn main() -> ! { 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, so they do not get used + // 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), @@ -286,18 +302,16 @@ fn main() -> ! { ); debug!("clocks"); - // Run the system clock at 96MHz. The lpc55-hal will run it from the FRO. But we won't actually use these clocks, we just need the guards... let clocks = hal::ClockRequirements::default() .system_frequency(96.MHz()) .configure(&mut anactrl, &mut pmc, &mut syscon) .unwrap(); - let mut _delay_timer = Timer::new( + let mut usb_delay_timer = Timer::new( hal.ctimer .0 .enabled(&mut syscon, clocks.support_1mhz_fro_token().unwrap()), ); - // Start PLL1 at 150MHz as main system clock - hw::init_sys_pll1(); + // 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(); @@ -326,7 +340,7 @@ fn main() -> ! { &mut anactrl, &mut pmc, &mut syscon, - &mut _delay_timer, + &mut usb_delay_timer, clocks.support_usbhs_token().unwrap(), ), ); @@ -348,34 +362,21 @@ fn main() -> ! { defmt::debug!("dma init"); let i2s_dma_addr = &i2s_peripheral.i2s.fifowr as *const _ as *mut u32; - let dma = DmaRing::<32, SLOT_SIZE_BYTES>::new(hal.dma.release(), &mut syscon, i2s_dma_addr, 4) - .unwrap(); + let dma = + DmaRing::::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 clock = Clock {}; let mut audio = Audio { i2s: i2s_peripheral, dma: dma_ring(), running: AtomicBool::new(false), + log_counter: 0, }; defmt::debug!("usb init"); - let config = AudioClassConfig::new(usb_speed, FunctionCode::Other, &mut clock, &mut audio) - .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 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)) diff --git a/examples/lpc55s28-evk/Cargo.lock b/examples/lpc55s28-evk/Cargo.lock index 5ec9984..89166be 100644 --- a/examples/lpc55s28-evk/Cargo.lock +++ b/examples/lpc55s28-evk/Cargo.lock @@ -411,6 +411,7 @@ dependencies = [ [[package]] name = "lpc55-hal" version = "0.5.0" +source = "git+https://github.com/ktims/lpc55-hal?branch=main#8dfefd62aff4abd2de535f23107812dda68437be" dependencies = [ "block-buffer", "cipher", @@ -451,10 +452,7 @@ dependencies = [ "embedded-io", "log-to-defmt", "lpc55-hal", - "nb 1.1.0", - "panic-halt", "panic-probe", - "static_cell", "usb-device", "usbd-uac2", ] @@ -615,12 +613,6 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" -[[package]] -name = "panic-halt" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11" - [[package]] name = "panic-probe" version = "1.0.0" @@ -825,15 +817,6 @@ 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" diff --git a/examples/lpc55s28-evk/Cargo.toml b/examples/lpc55s28-evk/Cargo.toml index 8512603..d90b2fd 100644 --- a/examples/lpc55s28-evk/Cargo.toml +++ b/examples/lpc55s28-evk/Cargo.toml @@ -2,6 +2,7 @@ name = "lpc55s28-evk" version = "0.1.0" edition = "2024" +publish = false [features] default = ["usbhs"] @@ -17,13 +18,11 @@ defmt-rtt = "1.1.0" embedded-hal = "1.0.0" embedded-io = "0.7.1" 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"] } -static_cell = "2.1.1" -usb-device = "0.3" -usbd-uac2 = { version = "0.1.0", path = "../..", features = ["defmt"]} +# 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" diff --git a/examples/lpc55s28-evk/build.rs b/examples/lpc55s28-evk/build.rs new file mode 100644 index 0000000..ffce9b8 --- /dev/null +++ b/examples/lpc55s28-evk/build.rs @@ -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); +} diff --git a/examples/lpc55s28-evk/src/main.rs b/examples/lpc55s28-evk/src/main.rs index 9adc862..1ad70ca 100644 --- a/examples/lpc55s28-evk/src/main.rs +++ b/examples/lpc55s28-evk/src/main.rs @@ -224,7 +224,7 @@ impl AudioHandler<'_, B> for Audio { v = v.clamp(nominal_v - (1 << 14), nominal_v + (1 << 14)); // Note: this log will cause continuous underflows - defmt::debug!("fill:{} err:{} fb:{=u32:x}", fill, error, v); + defmt::debug!("fill:{} err:{} fb:{=u32:x}", fill, error, v as u32); Some(UsbIsochronousFeedback::new(v as u32)) } diff --git a/src/lib.rs b/src/lib.rs index 94d9473..7edd248 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,9 +20,6 @@ use usb_device::device::DEFAULT_ALTERNATE_SETTING; use usb_device::endpoint::{self, Endpoint, EndpointDirection, In, Out}; use usb_device::{UsbDirection, class_prelude::*}; -#[cfg(feature = "defmt")] -use defmt; - mod sealed { pub trait Sealed {} } @@ -934,7 +931,7 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbClass } fn reset(&mut self) { - defmt::info!("usb reset"); + info!("usb reset"); self.audio_impl .alternate_setting_changed(UsbDirection::In, 0); self.audio_impl @@ -955,7 +952,7 @@ impl<'a, B: UsbBus, AU: AudioHandler<'a, B> + ClockSource> UsbAudioClass<'a, B, warn!(" feedback IN failed {:?}", e); } } else { - debug!(" feedback callback returned None") + debug!(" feedback callback returned None"); } } }