From 5bc5dec715cb46a92111c8de4c290c625372a79f Mon Sep 17 00:00:00 2001 From: puckoprutt Date: Mon, 13 Jul 2026 05:06:53 +0200 Subject: [PATCH] first version, lets see what misstakes i made :D --- Cargo.lock | 1054 ++++++++++++++++++++++++++++++++++++ Cargo.toml | 35 ++ README.md | 31 ++ build.rs | 27 + src/config.rs | 60 ++ src/example/usb-example.rs | 93 ++++ src/lib.rs | 393 ++++++++++++++ src/message.rs | 119 ++++ src/rx.rs | 143 +++++ src/special_chars.rs | 39 ++ 10 files changed, 1994 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 build.rs create mode 100644 src/config.rs create mode 100644 src/example/usb-example.rs create mode 100644 src/lib.rs create mode 100644 src/message.rs create mode 100644 src/rx.rs create mode 100644 src/special_chars.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4136865 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1054 @@ +# This file is automatically @generated by Cargo. +# 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 = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "ascii-canvas" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891" +dependencies = [ + "term", +] + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal", + "bitfield 0.13.2", + "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 = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[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", + "typenum", +] + +[[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 = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "embedded-dma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" +dependencies = [ + "stable_deref_trait", +] + +[[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-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "embedded-io" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eb1aa714776b75c7e67e1da744b81a129b3ff919c8712b5e1b32252c1f07cc7" + +[[package]] +name = "ena" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1" +dependencies = [ + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "frunk" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28aef0f9aa070bce60767c12ba9cb41efeaf1a2bc6427f87b7d83f11239a16d7" +dependencies = [ + "frunk_core 0.4.4", + "frunk_derives", +] + +[[package]] +name = "frunk_core" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476eeaa382e3462b84da5d6ba3da97b5786823c2d0d3a0d04ef088d073da225c" + +[[package]] +name = "frunk_core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bd3c9ba2e323e8b19e77f15873f60974a7d82f89b80e50c53be44b8b92927c1" + +[[package]] +name = "frunk_derives" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b4095fc99e1d858e5b8c7125d2638372ec85aa0fe6c807105cf10b0265ca6c" +dependencies = [ + "frunk_proc_macro_helpers", + "quote", + "syn", +] + +[[package]] +name = "frunk_proc_macro_helpers" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b70229a1347a20d4af9c06116cc452acef34f798668c6b69e97dd5c8a88052bd" +dependencies = [ + "frunk_core 0.5.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fugit" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e639847d312d9a82d2e75b0edcc1e934efcc64e6cb7aa94f0b1fbec0bc231d6" +dependencies = [ + "gcd", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[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 = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lalrpop" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4ebbd48ce411c1d10fb35185f5a51a7bfa3d8b24b4e330d30c9e3a34129501" +dependencies = [ + "ascii-canvas", + "bit-set", + "ena", + "itertools 0.14.0", + "lalrpop-util", + "petgraph", + "pico-args", + "regex", + "regex-syntax", + "sha3", + "string_cache", + "term", + "unicode-xid", + "walkdir", +] + +[[package]] +name = "lalrpop-util" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5baa5e9ff84f1aefd264e6869907646538a52147a755d494517a8007fb48733" +dependencies = [ + "regex-automata", + "rustversion", +] + +[[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.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[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 = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pio" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ba4153cee9585abc451271aa437d9e8defdea8b468d48ba6b8f098cbe03d7f" +dependencies = [ + "pio-core", + "pio-proc", +] + +[[package]] +name = "pio-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61d90fddc3d67f21bbf93683bc461b05d6a29c708caf3ffb79947d7ff7095406" +dependencies = [ + "arrayvec", + "num_enum", + "paste", +] + +[[package]] +name = "pio-parser" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "825266c1eaddf54f636d06eefa4bf3c99d774c14ec46a4a6c6e5128a0f10d205" +dependencies = [ + "lalrpop", + "lalrpop-util", + "pio-core", +] + +[[package]] +name = "pio-proc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed4a76571f5fe51af43cc80ac870fe0c79cc0cdd686b9002a6c4c84bfdd0176b" +dependencies = [ + "codespan-reporting", + "lalrpop-util", + "pio-core", + "pio-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[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.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "riscv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9" +dependencies = [ + "critical-section", + "embedded-hal 1.0.0", +] + +[[package]] +name = "riscv-rt" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d35e32cf1383183e8885d8a9aa4402a087fd094dc34c2cb6df6687d0229dfe" +dependencies = [ + "riscv", + "riscv-rt-macros", +] + +[[package]] +name = "riscv-rt-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30f19a85fe107b65031e0ba8ec60c34c2494069fe910d6c297f5e7cb5a6f76d0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "rp-binary-info" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f582261945fa215d40e2988b4df595d11c0908c0fff97a0fe23df766d117b790" + +[[package]] +name = "rp-hal-common" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8288358786b1458fb2caac8c4b40fb529ef4200d6c46467e2695b7a8ba573ae8" +dependencies = [ + "fugit", +] + +[[package]] +name = "rp235x-hal" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871a18f1bdad479deb080891d65b4d61d000f06f8e4227071f8ca5aa39c25d3a" +dependencies = [ + "bitfield 0.14.0", + "cortex-m", + "cortex-m-rt", + "critical-section", + "embedded-dma", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io 0.7.1", + "frunk", + "fugit", + "gcd", + "itertools 0.13.0", + "nb 1.1.0", + "paste", + "pio", + "rand_core", + "riscv", + "riscv-rt", + "rp-binary-info", + "rp-hal-common", + "rp235x-hal-macros", + "rp235x-pac", + "sha2-const-stable", + "usb-device", + "vcell", + "void", +] + +[[package]] +name = "rp235x-hal-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74edd7a5979e9763bbb98e9746e711bac7464ee3397af7288e6c288ff0d3c764" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "rp235x-pac" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f1227561239cdfbcea69f6f1c66c78cc8197d5b4103db394eacf20aedf74fa" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "vcell", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semihosting" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8e4abf97879f4e80db69a9fba7bd64998e9bdad25f58ef045a778e191172fd4" + +[[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-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[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 = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "term" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "usb-device" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" +dependencies = [ + "heapless 0.8.0", + "portable-atomic", +] + +[[package]] +name = "usb-terminal" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "embedded-hal 1.0.0", + "heapless 0.9.3", + "rp235x-hal", + "semihosting", + "static_cell", + "usb-device", + "usbd-serial", +] + +[[package]] +name = "usbd-serial" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065e4eaf93db81d5adac82d9cef8f8da314cb640fa7f89534b972383f1cf80fc" +dependencies = [ + "embedded-hal 0.2.7", + "embedded-io 0.6.1", + "nb 1.1.0", + "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", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[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 new file mode 100644 index 0000000..b3fa77d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "usb-terminal" +version = "0.1.0" +edition = "2024" + +[profile.dev] +opt-level = 's' # default is opt-level = '0', but that makes very verbose machine code +codegen-units = 1 # trade compile speed for slightly better optimisations + +# cargo build/run --release +[profile.release] +opt-level = 's' # default is opt-level = '3', but that makes quite verbose machine code +codegen-units = 1 # trade compile speed for slightly better optimisations +lto = 'fat' # Use Link Time Optimisations to further inline things across crates +debug = 2 # Leave the debug symbols in (default is no debug info) +strip = true # strip stupid stuff. + +[[example]] +name = "print-banner" +path = "./src/example/usb-example.rs" +test = false +bench = false + +[dependencies] +cortex-m = { version = "0.7.7" } +cortex-m-rt = "0.7.5" +critical-section = { version = "1.2.0", features = ["restore-state-u8"] } +embedded-hal = "1.0.0" +heapless = "0.9.3" +rp235x-hal = { version = "0.4.0", features = ["rt", "critical-section-impl"] } +semihosting = "0.1.25" +static_cell = "2.1.1" +usb-device = "0.3.2" +usbd-serial = "0.2.2" + diff --git a/README.md b/README.md new file mode 100644 index 0000000..9752837 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +> this is a fork of: [rp-usb-serial](https://github.com/sndnvaps/rp-usb-serial) +> i have added more functionality, on top of his/hers construction +> - credits to sndnvaps + +# USB Terminal + +this crate was created to give a smooth console that handles backspace and output with some color. +the original rp-usb-serial crate was a good foundation to build on in my journey to learning embedded rust. + +## compiling the example + +to compile just run: +´´´bash +cargo build --example print-banner +´´´ + +compiling.. + +## running the example + +When running the example it should look like this: + +running example.. + +its made for terminals that can take ANSI-sequence (im guessing most of them do..) and it uses simple standard 8 colors. +example showing how to get notified when user presses enter and work with the whole line. and also how to split line into word. +usually word 0 is a command and the rest of words are arguments. + +## check out the example source + +just check in src/example \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..68369be --- /dev/null +++ b/build.rs @@ -0,0 +1,27 @@ +//! Set up linker scripts for the rp235x-hal examples + +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put the linker script somewhere the linker can find it + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + println!("cargo:rustc-link-search={}", out.display()); + + // The file `memory.x` is loaded by cortex-m-rt's `link.x` script, which + // is what we specify in `.cargo/config.toml` for Arm builds + let memory_x = include_bytes!("memory.x"); + let mut f = File::create(out.join("memory.x")).unwrap(); + f.write_all(memory_x).unwrap(); + println!("cargo:rerun-if-changed=memory.x"); + + // The file `rp235x_riscv.x` is what we specify in `.cargo/config.toml` for + // RISC-V builds + let rp235x_riscv_x = include_bytes!("rp235x_riscv.x"); + let mut f = File::create(out.join("rp235x_riscv.x")).unwrap(); + f.write_all(rp235x_riscv_x).unwrap(); + println!("cargo:rerun-if-changed=rp235x_riscv.x"); + + println!("cargo:rerun-if-changed=build.rs"); +} \ No newline at end of file diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..028e518 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,60 @@ +//! USB Configuration +//! +//! # Examples +//! ``` +//! use usb_terminal::config::UsbConfig; +//! +//! let conf = UsbConfig::new( +//! vendor : 0xffff, +//! device : 0xffff, +//! manufacturer : "puckoprutt", +//! product : "Nuclear driven submarine with built-in toaster", +//! serial_number : "yes" +//! ); +//! ``` + +/// Simple configuration of USB +pub struct UsbConfig { + /// USB vendor ID + pub vendor : u16, + /// USB Device ID + pub device : u16, + /// USB Manufacturer + pub manufacturer : &'static str, + /// USB Product + pub product : &'static str, + /// USB Serial number + pub serial_number : &'static str, +} + +impl UsbConfig { + /// Creates a new UsbConfig. + /// Note that the values manufacturer, product and serial_number have 'static lifetime + pub fn new(usb_vendor_id: u16, usb_device_id: u16, manufacturer: &'static str, product: &'static str, serial_number: &'static str) -> Self { + Self { + vendor : usb_vendor_id, + device : usb_device_id, + manufacturer : manufacturer, + product : product, + serial_number : serial_number + } + } +} + +impl Default for UsbConfig { + /// creates UsbConfig with default values + /// - vendor : 0x1AD8 + /// - device : 0xB00B + /// - manufacturer : "puckoprutt embedded tech" + /// - product : "Jordklots erövrare", + /// - serial_number : "bananbåt", + fn default() -> Self { + Self { + vendor : 0x1AD8, + device : 0xB00B, + manufacturer : "puckoprutt embedded tech", + product : "Jordklots erövrare", + serial_number : "bananbåt", + } + } +} \ No newline at end of file diff --git a/src/example/usb-example.rs b/src/example/usb-example.rs new file mode 100644 index 0000000..72cfc1c --- /dev/null +++ b/src/example/usb-example.rs @@ -0,0 +1,93 @@ +//! example tested on raspberry pi pico 2w. + +#![no_std] +#![no_main] + +use core::panic::PanicInfo; +use core::sync::atomic::Ordering; +use cortex_m::asm; +use cortex_m::peripheral::NVIC; +use cortex_m_rt::entry; +use usb_terminal::config::UsbConfig; +use usb_terminal::GLOBAL_EVENT; +use usb_terminal::Terminal; +use usb_terminal::rx::ReceiveHandler; +use rp235x_hal::{ + self as hal, + block::ImageDef, + pac::Peripherals, +}; + +const XOSC_CRYSTAL_FREQ : u32 = 12000000; + +#[entry] +fn main() -> ! { + let mut pac = Peripherals::take().unwrap(); + let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); + + // clock and timers + let clocks = hal::clocks::init_clocks_and_plls( + XOSC_CRYSTAL_FREQ, + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut watchdog + ).ok().unwrap(); + + let mut rx = ReceiveHandler::new(); + Terminal::init( + pac.USB, + pac.USB_DPRAM, + &mut pac.RESETS, + clocks.usb_clock, + UsbConfig::default() + ); + + unsafe { + NVIC::unmask(hal::pac::Interrupt::USBCTRL_IRQ); + } + + loop { + let event = GLOBAL_EVENT.load(Ordering::Acquire); + if (event >> 7) & 1 == 1 { + // interrupt has happened. + match rx.poll() { + Some(mut msg) => { + Terminal::clear_screen(); + Terminal::print_banner(); + for i in 0..msg.words { + Terminal::pp_debug(&msg.get_word(i).unwrap().word); + Terminal::pp_info(&msg.get_word(i).unwrap().word); + Terminal::pp_warning(&msg.get_word(i).unwrap().word); + Terminal::pp_danger(&msg.get_word(i).unwrap().word); + } + Terminal::user_prompt(); + } + None => { + asm::nop(); + } + } + } + } +} + +#[unsafe(link_section = ".start_block")] +#[used] +pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop { asm::udf() } +} + +pub fn exit() -> ! { + semihosting::process::exit(0); +} + +#[allow(non_snake_case)] +#[cortex_m_rt::exception] +unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! { + semihosting::process::exit(1); +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..02aa30c --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,393 @@ +//! USB Terminal +//! +//! i forked the code from +//! credits to sndnvaps. +//! +//! made some adjustments to fit my needs: +//! - added some comments. +//! - added a AtomicU8 GLOBAL_EVENT and made the interrupt set MSB, the other bits can be used for other interrupts. +//! - added UsbConfig for setting init function +//! - added ReceiveHandler +//! - added UsbMessages +//! - added UsbWord +//! - added clear_screen() function that sends ANSI sequence to host machine. +//! - added pp_debug(), pp_info(), pp_warning(), pp_danger() to print with some colors. +//! - added a print_banner() function. +//! - removed macros, print and println +//! +//! thinks thats it.. +//! +//! # Examples +//! ``` +//! use usb_terminal::config::UsbConfig; +//! use usb_terminal::GLOBAL_EVENT; +//! use usb_terminal::Terminal; +//! use usb_terminal::rx::ReceiveHandler; +//! // .... +//! +//! #[entry] +//! fn main() -> ! { +//! +//! // init your HAL, do your weird ass dance and stuff.. +//! +//! let mut rx = ReceiveHandler::new(); +//! Terminal::init( +//! pac.USB, +//! pac.USB_DPRAM, +//! &mut pac.RESETS, +//! clocks.usb_clock, +//! UsbConfig::default() +//! ); +//! +//! unsafe { +//! NVIC::unmask(hal::pac::Interrupt::USBCTRL_IRQ); +//! } +//! +//! loop { +//! // if MSB is 1 than an interrupt has happend +//! let event = GLOBAL_EVENT.load(Ordering::Acquire); +//! if (event >> 7) & 1 == 1 { +//! // interrupt has happened. +//! match rx.poll() { +//! Some(mut msg) => { // msg is of type UsbMessage +//! Terminal::clear_screen(); // sends ansi sequence to clear screen +//! Terminal::print_banner(); // prints banner +//! for i in 0..msg.words { // for each word in message echo back in the four styles. +//! Terminal::pp_debug(&msg.get_word(i).unwrap().word); +//! Terminal::pp_info(&msg.get_word(i).unwrap().word); +//! Terminal::pp_warning(&msg.get_word(i).unwrap().word); +//! Terminal::pp_danger(&msg.get_word(i).unwrap().word); +//! } +//! Terminal::user_prompt(); // prints out the user_prompt +//! } +//! None => { +//! asm::nop(); // keep being weird, dance your weird dance. +//! } +//! } +//! } +//! } +//! } +//! ``` + +#![no_std] +#![no_main] + +pub mod config; + +mod special_chars; +pub mod message; +pub mod rx; + +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use core::cell::RefCell; +use critical_section::Mutex; +use heapless::spsc::Queue; +use static_cell::StaticCell; +use usb_device::class_prelude::UsbBusAllocator; +use usb_device::device::UsbDeviceState; +use usb_device::prelude::*; +use usbd_serial::SerialPort; +use rp235x_hal as hal; +use hal::pac::{self, interrupt}; +use hal::usb::UsbBus; + +#[doc(inline)] +use crate::config::UsbConfig; + +/// After the interrupt service routine for usb-serial has triggered the most significant bit of this +/// global variable is set. GLOBAL_EVENT needs to hold the same data type as USB_RECEIVED_MASK and USB_RECEIVED_UNMASK. +pub static GLOBAL_EVENT : AtomicU8 = AtomicU8::new(0u8); + +/// Received mask will be applied like: GLOBAL_EVENT | USB_RECEIVED_MASK +pub const USB_RECEIVED_MASK : u8 = 0x80; + +/// After you acknoledge the event apply this like: GLOBAL_EVENT & USB_RECEIVED_MASK +pub const USB_RECEIVED_UNMASK : u8 = 0x7F; + +static GLOBAL_USB_SERIAL : Mutex>> = Mutex::new(RefCell::new(None)); +static GLOBAL_USB_BUS : StaticCell> = StaticCell::new(); +static USB_INIT : AtomicBool = AtomicBool::new(false); +const USB_RX_BUF_SIZE : usize = 256; +const USB_TX_BUF_SIZE : usize = 256; + +/// Represents the Terminal +pub struct Terminal { + device : UsbDevice<'static, UsbBus>, + serial : SerialPort<'static, UsbBus>, + tx_buf : Queue, + rx_buf : Queue, +} + +impl Terminal { + + /// Initialize the terminal + /// if private global AtomicBool USB_INIT is set, this function will do nothing. + /// + /// # Example + /// ``` + /// use usb_terminal::config::UsbConfig; + /// use usb_terminal::Terminal; + /// + /// Terminal::init( + /// pac_usb, + /// pac_usb_dpram, + /// pac_resets, + /// usb_clock, + /// UsbConfig::new( + /// vendor : 0xD00D, + /// device : 0xB00B, + /// manufacturer : "puckoprutt embedded tech", + /// product : "Jordklots erövrare", + /// serial_number : "bananbåt", + /// )); + /// // make the world brighter with your weird dance <3 + /// ``` + pub fn init(usbctrl_reg: pac::USB, usbctrl_dpram: pac::USB_DPRAM, usb_reset: &mut pac::RESETS, clocks: hal::clocks::UsbClock, config: UsbConfig) { + if USB_INIT.load(Ordering::SeqCst) { + return; + } + + let usb_bus = UsbBus::new(usbctrl_reg, usbctrl_dpram, clocks, true, usb_reset); + let alloc = GLOBAL_USB_BUS.init(UsbBusAllocator::new(usb_bus)); + + let serial = SerialPort::new(alloc); + let device = UsbDeviceBuilder::new(alloc, UsbVidPid(config.vendor, config.device)) + .strings(&[StringDescriptors::default() + .manufacturer(config.manufacturer) + .product(config.product) + .serial_number(config.serial_number)]) + .unwrap() + .device_class(2) + .build(); + + critical_section::with(|cs| { + GLOBAL_USB_SERIAL.borrow(cs).replace(Some(Self { + device, + serial, + tx_buf: Queue::new(), + rx_buf: Queue::new(), + })); + }); + + USB_INIT.store(true, Ordering::SeqCst); + } + + /// poll command will run when an interrupt occurs. + /// if private AtomicBool USB_INIT is not set this function will not do anything. + /// else the following private commands in this order: + /// - force_poll + /// - it will take ahold of GLOBAL_USB_SERIAL mutex and poll latest data if any new data + /// - pump_rx_echo + /// - takes the GLOBAL_USB_SERIAL mutex and move the latest data to internal buffer. and also sets MSB of GLOBAL_EVENT + /// - flush_tx + /// - flushes the transmit buffer + /// + /// this command will run when an interrupt occurs + pub fn poll() { + Self::force_poll(); + Self::pump_rx_echo(); + Self::flush_tx(); + } + + #[inline(always)] + fn force_poll() { + if !USB_INIT.load(Ordering::SeqCst) { + return; + } + + critical_section::with(|cs| { + let mut opt = GLOBAL_USB_SERIAL.borrow(cs).borrow_mut(); + if let Some(usb) = opt.as_mut() { + usb.device.poll(&mut [&mut usb.serial]); + } + }); + } + + fn pump_rx_echo() { + if !USB_INIT.load(Ordering::SeqCst) { + return; + } + + critical_section::with(|cs| { + let mut opt = GLOBAL_USB_SERIAL.borrow(cs).borrow_mut(); + let Some(usb) = opt.as_mut() else { return }; + + if usb.device.state() != UsbDeviceState::Configured { + return; + } + + let mut temp = [0u8; 64]; + + loop { + match usb.serial.read(&mut temp) { + Ok(0) => break, + Ok(n) => { + for &b in &temp[..n] { + let _ = usb.rx_buf.enqueue(b); + } + let res = GLOBAL_EVENT.load(Ordering::Acquire) | USB_RECEIVED_MASK; + GLOBAL_EVENT.store(res, Ordering::Release); + } + Err(usb_device::UsbError::WouldBlock) => break, + Err(_) => break, + } + } + }); + } + + fn flush_tx() { + if !USB_INIT.load(Ordering::SeqCst) { + return; + } + + critical_section::with(|cs| { + let mut opt = GLOBAL_USB_SERIAL.borrow(cs).borrow_mut(); + let Some(usb) = opt.as_mut() else { return }; + + if usb.device.state() != UsbDeviceState::Configured { + return; + } + + while let Some(b) = usb.tx_buf.dequeue() { + if usb.serial.write(&[b]).is_err() { + let _ = usb.tx_buf.enqueue(b); + break; + } + } + }); + } + + /// writes a byte array to transmit buffer. + pub fn write(data: &[u8]) { + if !USB_INIT.load(Ordering::SeqCst) { + return; + } + + critical_section::with(|cs| { + let mut binding = GLOBAL_USB_SERIAL.borrow(cs).borrow_mut(); + if let Some(usb) = binding.as_mut() { + for &b in data { + let _ = usb.tx_buf.enqueue(b); + } + } + }); + Self::poll(); + } + + /// prints a pretty banner. + pub fn print_banner() { + Self::write(b" 8 o o \r\n"); + Self::write(b" 8 8 8 \r\n"); + Self::write(b"\x1B[0;31m.oPYo.\x1B[0m o o .oPYo. 8 .o .oPYo. \x1B[0;31m.oPYo.\x1B[0m oPYo. o o o8P o8P \r\n"); + Self::write(b"\x1B[0;31m8 8\x1B[0m 8 8 8 ' 8oP' 8 8 \x1B[0;31m8 8\x1B[0m 8 `' 8 8 8 8 \r\n"); + Self::write(b"\x1B[0;31m8 8\x1B[0m 8 8 8 . 8 `b. 8 8 \x1B[0;31m8 8\x1B[0m 8 8 8 8 8 \r\n"); + Self::write(b"\x1B[0;31m8YooP'\x1B[0m `YooP' `YooP' 8 `o. `YooP' \x1B[0;31m8YooP'\x1B[0m 8 `YooP' 8 8 \r\n"); + Self::write(b"\x1B[0;31m8\x1B[0m \x1B[0;32m....::.....::.....:..::...:.....:\x1B[0m\x1B[0;31m8\x1B[0m \x1B[0;32m....:..:::::.....:::..:::..:\x1B[0m\r\n"); + Self::write(b"\x1B[0;31m8\x1B[0m \x1B[0;32m:::::::::::::::::::::::::::::::::\x1B[0m\x1B[0;31m8\x1B[0m \x1B[0;32m::::::\x1B[5m \x1B[0membedded tech \x1B[25m\x1B[0;32m:::::::\r\n"); + Self::write(b"\x1B[0;32m..:::::::::::::::::::::::::::::::::..::::::::::::::::::::::::::::\x1B[0m\r\n\r\n"); + } + + /// prints the user prompt. + pub fn user_prompt() { + Self::write(b"\x1B[0;32m[cool dude]\x1B[0m -> "); + } + + /// prints a debug message. + pub fn pp_debug(data: &[u8]) { + Self::write(b"[puckoprutt] <- "); + Self::write(data); + Self::write(b"\r\n"); + } + + /// prints a info message. + pub fn pp_info(data: &[u8]) { + Self::write(b"\x1B[1;37m[puckoprutt]\x1B[0m <- "); + Self::write(data); + Self::write(b"\r\n"); + } + + /// prints a warning message. + pub fn pp_warning(data: &[u8]) { + Self::write(b"\x1B[43m\x1B[4;37m[puckoprutt]\x1B[0m <- \x1B[4;33m"); + Self::write(data); + Self::write(b"\r\n\x1B[0m"); + } + + /// prints a danger/error message. + pub fn pp_danger(data: &[u8]) { + Self::write(b"\x1B[41m\x1B[1;37m[puckoprutt]\x1B[0m !!DANGER!! \x1B[1;31m"); + Self::write(data); + Self::write(b"\r\n\x1B[0m"); + } + + /// clears the screen with a simple ANSI sequence. + pub fn clear_screen() { + Self::write(b"\x1B[2J"); + } + + /// reads internal buffer to a external buffer and returns the size of the buffer. + pub fn read(buf: &mut [u8]) -> usize { + if !USB_INIT.load(Ordering::SeqCst) { + return 0; + } + + critical_section::with(|cs| { + let mut opt = GLOBAL_USB_SERIAL.borrow(cs).borrow_mut(); + let Some(usb) = opt.as_mut() else { return 0 }; + + let mut n = 0; + while n < buf.len() { + match usb.rx_buf.dequeue() { + Some(b) => { + buf[n] = b; + n += 1; + } + None => break, + } + } + n + }) + } +} + +#[inline(always)] +fn usbctrl_irq_impl() { + Terminal::poll(); + +} + +#[allow(non_snake_case)] +#[interrupt] +fn USBCTRL_IRQ() { + usbctrl_irq_impl(); +} + +/* +#[macro_export] +macro_rules! pp_debug { + ($($tt:tt)*) => { + $crate::Terminal::fmt_write(format_args!($($tt)*), 0); + }; +} + +#[macro_export] +macro_rules! pp_info { + ($($tt:tt)*) => { + $crate::Terminal::fmt_write(format_args!($($tt)*), 10); + }; +} + +#[macro_export] +macro_rules! pp_warning { + ($($tt:tt)*) => { + $crate::Terminal::fmt_write(format_args!($($tt)*), 20); + }; +} + +#[macro_export] +macro_rules! pp_danger { + ($($tt:tt)*) => { + $crate::Terminal::fmt_write(format_args!($($tt)*), 30); + }; +} +*/ \ No newline at end of file diff --git a/src/message.rs b/src/message.rs new file mode 100644 index 0000000..dda5b8d --- /dev/null +++ b/src/message.rs @@ -0,0 +1,119 @@ +//! Messages from ReceiveHandler +//! +//! # Examples +//! ``` +//! /// new_message needs parameter of type [u8; 128] +//! let message = UsbMessage::new_message(message); +//! let how_many_words: usize = message.words; +//! let command = message.get_command(); // same as get_word(0) +//! let argument1 = message.get_word(1); +//! let argument2 = message.get_word(2); +//! ``` +use cortex_m::asm; +use super::Terminal; + +/// trait for trim function +trait SliceExt { + fn trim(&self) -> &Self; +} + +impl SliceExt for [u8; 128] { + /// trim function, will remove trailing spaces from left and right. + fn trim(&self) -> &[u8; 128] { + fn is_whitespace(c: &u8) -> bool { + *c == b'\t' || *c == b' ' + } + + fn is_not_whitespace(c: &u8) -> bool { + !is_whitespace(c) + } + + if let Some(first) = self.iter().position(is_not_whitespace) { + if let Some(last) = self.iter().rposition(is_not_whitespace) { + self[first..last + 1].try_into().unwrap() + } else { + unreachable!(); + } + } else { + &[0u8; 128] + } + } +} + +/// holds a word, each word can be 16 characters long max. +pub struct UsbWord { + /// the word. + pub word: [u8; 16], + /// the length of the word. + pub size: usize +} + +/// holds the whole message and the length +pub struct UsbMessage { + /// the line + pub words: usize, + /// the length + pub message: [u8; 128] +} + +impl UsbMessage { + /// Creates a new UsbMessage + pub fn new_message(s: &[u8; 128]) -> Self{ + Terminal::write(b"\r\n"); + Terminal::pp_info(b"got: something"); + Terminal::user_prompt(); + let msg: &[u8; 128] = s.trim(); + Self { + words : Self::count_word(msg), + message : msg.clone() + } + } + + /// Count the words + fn count_word(s: &[u8]) -> usize { + let mut words: usize = 0; + let mut last_valid = true; + s.iter().take(s.len()).for_each(|w| { + if *w == 0x0 { asm::nop(); } + else if *w == 0x20 { + words += 1; + last_valid = false; + } + else { + last_valid = true + } + }); + if last_valid { + words+1 + } else { + words + } + } + + /// gets the first word + pub fn get_command(&mut self) -> UsbWord { + self.get_word(0).unwrap() + } + + /// gets the word provided or None if out of bounds. + pub fn get_word(&mut self, index: usize) -> Option { + if index > self.words { return None; } + let mut word: usize = 0; + let mut ret_index: usize = 0; + let mut ret = [0u8; 16]; + self.message.iter().take(self.message.len()).for_each(|w| { + if *w == 0x0 { asm::nop(); } + else if *w == 0x20 { + word += 1; + } + else if word == index { + ret[ret_index] = *w; + ret_index += 1; + } + }); + Some(UsbWord{ + word: ret, + size: ret_index + }) + } +} \ No newline at end of file diff --git a/src/rx.rs b/src/rx.rs new file mode 100644 index 0000000..6b10dd8 --- /dev/null +++ b/src/rx.rs @@ -0,0 +1,143 @@ +//! Receive Handling +//! +//! an interrupt occurs for every key pressed, this handler makes it easy to handle +//! in a line-by-line approach. +//! +//! # Examples +//! ``` +//! use usb_terminal::Terminal +//! use usb_terminal::rx::ReceiveHandler +//! // -snip- +//! fn main() { +//! // do your weird dance... +//! let rx = ReceiveHandler::new() +//! // switch it up and do another weird dance +//! +//! // super loop in main +//! loop { +//! match rx.poll() { +//! // msg will be of type Some(UsbMessage) containing a line. +//! Some(mut msg) => { +//! // UsbMessage.words holds how many words there are in the line. +//! for i in 0..msg.words { +//! // echo back each word in red +//! Terminal::pp_danger(&msg.get_word(i).unwrap().word); +//! } +//! } +//! None => { +//! asm::nop(); +//! } +//! } +//! } +//! } // main +//! ``` + +use core::sync::atomic::Ordering; +use cortex_m::asm; +use crate::{ + Terminal, GLOBAL_EVENT, USB_RECEIVED_UNMASK, + special_chars::{ + SpecialButton, + get_special_char, + is_backspace, + is_boring, + }, + message::UsbMessage, +}; + +/// The receive handler. +/// will make it easy to handle commands and arguments sent over usb. +pub struct ReceiveHandler { + /// a special char like the home-button will come as 2 chars + /// this bool will be true if the '[' is received + /// then during the next poll a special character is recognised. + is_special_char : bool, + + /// points to where in the buffer the cursor is. + buffer_ptr : usize, + + /// buffer holding the received characters. + rx_buffer : [u8; 128], +} + +impl ReceiveHandler { + + /// Creates a new ReceiveHandler + /// + /// # Example + /// ``` + /// use usb_terminal::rx::ReceiveHandler + /// + /// let rx = ReceiveHandler::new(); + /// ``` + pub fn new() -> Self { + Self { + is_special_char : false, + buffer_ptr : 0, + rx_buffer : [0u8; 128] + } + } + + /// Polls the read message from the usb interrupt + /// will only return UsbMessage after \r or \n is received and rx buffer has more than 1 character. + /// will dismiss any character from ascii table 1-7 and 9-1F + /// backspace will be printed correct in any system that can read ANSI-sequences, and will + /// also erase the character from rx buffer. + pub fn poll(&mut self) -> Option { + let res = GLOBAL_EVENT.load(Ordering::Acquire) & USB_RECEIVED_UNMASK; + let mut temp_buffer = [0u8; 128]; + let msg_size = Terminal::read(&mut temp_buffer); + let mut ret: Option = None; + temp_buffer.iter_mut().take(msg_size).for_each(|b| { + if self.is_special_char { + self.reset(); + self.is_special_char = false; + Terminal::clear_screen(); + Terminal::print_banner(); + match get_special_char(b) { + SpecialButton::Home => { Terminal::pp_info(b"thats the home key."); } + SpecialButton::Insert => { Terminal::pp_info(b"thats the insert key."); } + SpecialButton::Delete => { Terminal::pp_danger(b"thats the delete key."); } + SpecialButton::ArrowUp => { Terminal::pp_info(b"thats the arrow up key."); } + SpecialButton::ArrowDown => { Terminal::pp_info(b"thats the arrow down key."); } + SpecialButton::ArrowRight => { Terminal::pp_debug(b"thats the arrow right key."); } + SpecialButton::ArrowLeft => { Terminal::pp_warning(b"thats the arrow left key."); } + SpecialButton::Unknown => { Terminal::pp_danger(b"thats the special key: {}"); } + } + } + else if *b == 0x5B { + self.is_special_char = true; + } + else if self.buffer_ptr > 0 && (*b == b'\n' || *b == b'\r') { + // we got a message. + // do something cool here + ret = Some(UsbMessage::new_message(&self.rx_buffer)); + + // after that reset buffer. + self.reset(); + } + else if is_backspace(b) { + if self.buffer_ptr > 0 { + self.buffer_ptr -= 1; + self.rx_buffer[self.buffer_ptr] = 0; + Terminal::write(b"\x1B[1D\x1B[0J"); + } + } + else if is_boring(b) { + asm::nop(); + } + else { + self.rx_buffer[self.buffer_ptr] = *b; + self.buffer_ptr += 1; + Terminal::write(&[*b]) + } + }); + GLOBAL_EVENT.store(res, Ordering::Release); + ret + } + + fn reset(&mut self) { + self.buffer_ptr = 0; + for z in self.rx_buffer.iter_mut() { *z = 0 } + } +} \ No newline at end of file diff --git a/src/special_chars.rs b/src/special_chars.rs new file mode 100644 index 0000000..4ba8fcf --- /dev/null +++ b/src/special_chars.rs @@ -0,0 +1,39 @@ +//! Helper functions for special characters + +/// Known special buttons +pub enum SpecialButton { + Home, + Insert, + Delete, + ArrowUp, + ArrowDown, + ArrowRight, + ArrowLeft, + Unknown +} + +/// map special buttons to SpecialButton +pub fn get_special_char(ch: &u8) -> SpecialButton { + match *ch { + 0x31 => { SpecialButton::Home } + 0x32 => { SpecialButton::Insert } + 0x33 => { SpecialButton::Delete } + 0x41 => { SpecialButton::ArrowUp } + 0x42 => { SpecialButton::ArrowDown } + 0x43 => { SpecialButton::ArrowRight } + 0x44 => { SpecialButton::ArrowLeft } + _ => { SpecialButton::Unknown } + } +} + +/// if character received is backspace +pub fn is_backspace(ch: &u8) -> bool { + if *ch == 0x08 { return true } + false +} + +/// if character received is any number below 'space' in ascii. +pub fn is_boring(ch: &u8) -> bool { + if *ch <= 0x1F {return true} + false +} \ No newline at end of file