commit 0d45555ce5069af2a4a38f10e0ec4f0183d84b13 Author: aOK Date: Sat Aug 3 11:50:26 2024 +0300 first commit diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..92f66ea --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,22 @@ +[target.xtensa-esp32-none-elf] +runner = "espflash flash -M -b 2000000 --partition-table partitions.csv -p /dev/cu.usbserial-1420" + +[env] +ESP_LOGLEVEL = "INFO" + +[build] +rustflags = [ + "-C", + "link-arg=-nostartfiles", + "-Dclippy::clone_on_ref_ptr", + "--cfg", + "feature=\"esp32\"", +] + +target = "xtensa-esp32-none-elf" + +[unstable] +build-std = ["alloc", "core"] + +# [build] +# rustflags = ["--cfg", "feature=\"esp32\""] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..73fab07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..979d509 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.check.allTargets": false, +} diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..27e3466 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1739 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "as-slice" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" +dependencies = [ + "generic-array 0.12.4", + "generic-array 0.13.3", + "generic-array 0.14.7", + "stable_deref_trait", +] + +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + +[[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 = "atomic-pool" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c5fc22e05ec2884db458bf307dc7b278c9428888d2b6e6fad9c0ae7804f5f6" +dependencies = [ + "as-slice 0.1.5", + "as-slice 0.2.1", + "atomic-polyfill", + "stable_deref_trait", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bare-metal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" + +[[package]] +name = "basic-toml" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" +dependencies = [ + "serde", +] + +[[package]] +name = "bitfield" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c821a6e124197eb56d907ccc2188eab1038fb919c914f47976e64dd8dbc855d1" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bytemuck" +version = "1.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "core-isa-parser" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ec98e54b735872e54b2335c2e5a5c7fa7d9c3bfd45500f75280f84089a0083" +dependencies = [ + "anyhow", + "enum-as-inner", + "regex", + "strum 0.24.1", + "strum_macros 0.24.3", +] + +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.72", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "delegate" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e018fccbeeb50ff26562ece792ed06659b9c2dae79ece77c4456bb10d9bf79b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "document-features" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +dependencies = [ + "litrs", +] + +[[package]] +name = "e-token" +version = "0.1.0" +dependencies = [ + "embassy-executor", + "embassy-futures", + "embassy-net", + "embassy-net-ppp", + "embassy-sync 0.6.0", + "embassy-time", + "embedded-io", + "embedded-io-async", + "esp-alloc", + "esp-backtrace", + "esp-hal", + "esp-hal-embassy", + "esp-hal-procmacros", + "esp-println", + "esp-wifi", + "heapless 0.8.0", + "log", + "rand", + "rand_core", + "smoltcp", + "static_cell", +] + +[[package]] +name = "embassy-executor" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec648daedd2143466eff4b3e8002024f9f6c1de4ab7666bb679688752624c925" +dependencies = [ + "critical-section", + "document-features", + "embassy-executor-macros", + "embassy-time-driver", + "embassy-time-queue-driver", +] + +[[package]] +name = "embassy-executor-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad454accf80050e9cf7a51e994132ba0e56286b31f9317b68703897c328c59b5" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "embassy-futures" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" + +[[package]] +name = "embassy-net" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cf91dd36dfd623de32242af711fd294d41159f02130052fc93c5c5ba93febe" +dependencies = [ + "as-slice 0.2.1", + "atomic-pool", + "document-features", + "embassy-net-driver", + "embassy-sync 0.5.0", + "embassy-time", + "embedded-io-async", + "embedded-nal-async", + "futures", + "generic-array 0.14.7", + "heapless 0.8.0", + "log", + "managed", + "smoltcp", + "stable_deref_trait", +] + +[[package]] +name = "embassy-net-driver" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524eb3c489760508f71360112bca70f6e53173e6fe48fc5f0efd0f5ab217751d" + +[[package]] +name = "embassy-net-driver-channel" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "584ab4da7e5612efaa7d55ee76161d9549adf788eab48d49362eddbf322f9933" +dependencies = [ + "embassy-futures", + "embassy-net-driver", + "embassy-sync 0.3.0", +] + +[[package]] +name = "embassy-net-ppp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0e5f8c8cba2bcdbf2918d9d45c60c60350f70d1791109d99b61d9695604f4bf" +dependencies = [ + "embassy-futures", + "embassy-net-driver-channel", + "embassy-sync 0.5.0", + "embedded-io-async", + "log", + "ppproto", +] + +[[package]] +name = "embassy-sync" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0525b466ca3ace30b57f2db868a35215dfaecd038d8668cb2db03feb7c069a0" +dependencies = [ + "cfg-if", + "critical-section", + "futures-util", + "heapless 0.7.17", +] + +[[package]] +name = "embassy-sync" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd938f25c0798db4280fcd8026bf4c2f48789aebf8f77b6e5cf8a7693ba114ec" +dependencies = [ + "cfg-if", + "critical-section", + "embedded-io-async", + "futures-util", + "heapless 0.8.0", +] + +[[package]] +name = "embassy-sync" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3e0c49ff02ebe324faf3a8653ba91582e2d0a7fdef5bc88f449d5aa1bfcc05c" +dependencies = [ + "cfg-if", + "critical-section", + "embedded-io-async", + "futures-util", + "heapless 0.8.0", +] + +[[package]] +name = "embassy-time" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "274c019608a9004aed3cafc871e2a3c87ce9351d537dcaab4cc5db184d4a04b1" +dependencies = [ + "cfg-if", + "critical-section", + "document-features", + "embassy-time-driver", + "embassy-time-queue-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "futures-util", + "heapless 0.8.0", +] + +[[package]] +name = "embassy-time-driver" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0c214077aaa9206958b16411c157961fb7990d4ea628120a78d1a5a28aed24" +dependencies = [ + "document-features", +] + +[[package]] +name = "embassy-time-queue-driver" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1177859559ebf42cd24ae7ba8fe6ee707489b01d0bf471f8827b7b12dcb0bc0" + +[[package]] +name = "embassy-usb-driver" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fc247028eae04174b6635104a35b1ed336aabef4654f5e87a8f32327d231970" + +[[package]] +name = "embassy-usb-synopsys-otg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d46be92e72bcf39e623ff74d739a8ab29b02f4909a9b05986ca81c2157ac254a" +dependencies = [ + "critical-section", + "embassy-sync 0.5.0", + "embassy-usb-driver", +] + +[[package]] +name = "embedded-can" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d2e857f87ac832df68fa498d18ddc679175cf3d2e4aa893988e5601baf9438" +dependencies = [ + "nb 1.1.0", +] + +[[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-async" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" +dependencies = [ + "embedded-io", +] + +[[package]] +name = "embedded-nal" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a943fad5ed3d3f8a00f1e80f6bba371f1e7f0df28ec38477535eb318dc19cc" +dependencies = [ + "nb 1.1.0", + "no-std-net", +] + +[[package]] +name = "embedded-nal-async" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72229137a4fc12d239b0b7f50f04b30790678da6d782a0f3f1909bf57ec4b759" +dependencies = [ + "embedded-io-async", + "embedded-nal", + "no-std-net", +] + +[[package]] +name = "enum-as-inner" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21cdad81446a7f7dc43f6a77409efeb9733d2fa65553efef6018ef257c959b73" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "enumset" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "esp-alloc" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e44c0b0c0416865313f3fcb6c00f7bc8ecebef30459bec74dc272eb12e465e3f" +dependencies = [ + "critical-section", + "linked_list_allocator", +] + +[[package]] +name = "esp-backtrace" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a34817530dceba397172d6a9fb660b684a73a3a591fbe7fb0da27bed796f270" +dependencies = [ + "esp-build 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "esp-println", + "semihosting", +] + +[[package]] +name = "esp-build" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94a4b8d74e7cc7baabcca5b2277b41877e039ad9cd49959d48ef94dac7eab4b" +dependencies = [ + "quote", + "syn 2.0.72", + "termcolor", +] + +[[package]] +name = "esp-build" +version = "0.1.0" +source = "git+https://github.com/esp-rs/esp-hal?rev=40d5481eddd70580eeb79b6908f6fec0e9b06d61#40d5481eddd70580eeb79b6908f6fec0e9b06d61" +dependencies = [ + "quote", + "syn 2.0.72", + "termcolor", +] + +[[package]] +name = "esp-hal" +version = "0.19.0" +source = "git+https://github.com/esp-rs/esp-hal?rev=40d5481eddd70580eeb79b6908f6fec0e9b06d61#40d5481eddd70580eeb79b6908f6fec0e9b06d61" +dependencies = [ + "basic-toml", + "bitfield", + "bitflags 2.6.0", + "bytemuck", + "cfg-if", + "critical-section", + "delegate", + "document-features", + "embassy-futures", + "embassy-sync 0.6.0", + "embassy-usb-driver", + "embassy-usb-synopsys-otg", + "embedded-can", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "embedded-io-async", + "enumset", + "esp-build 0.1.0 (git+https://github.com/esp-rs/esp-hal?rev=40d5481eddd70580eeb79b6908f6fec0e9b06d61)", + "esp-hal-procmacros", + "esp-metadata", + "esp-riscv-rt", + "esp32", + "fugit", + "log", + "nb 1.1.0", + "paste", + "portable-atomic", + "rand_core", + "serde", + "strum 0.26.3", + "void", + "xtensa-lx", + "xtensa-lx-rt", +] + +[[package]] +name = "esp-hal-embassy" +version = "0.2.0" +source = "git+https://github.com/esp-rs/esp-hal?rev=40d5481eddd70580eeb79b6908f6fec0e9b06d61#40d5481eddd70580eeb79b6908f6fec0e9b06d61" +dependencies = [ + "cfg-if", + "critical-section", + "document-features", + "embassy-executor", + "embassy-time-driver", + "esp-build 0.1.0 (git+https://github.com/esp-rs/esp-hal?rev=40d5481eddd70580eeb79b6908f6fec0e9b06d61)", + "esp-hal", + "esp-hal-procmacros", + "esp-metadata", + "log", + "portable-atomic", +] + +[[package]] +name = "esp-hal-procmacros" +version = "0.12.0" +source = "git+https://github.com/esp-rs/esp-hal?rev=40d5481eddd70580eeb79b6908f6fec0e9b06d61#40d5481eddd70580eeb79b6908f6fec0e9b06d61" +dependencies = [ + "darling", + "document-features", + "litrs", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "esp-metadata" +version = "0.2.0" +source = "git+https://github.com/esp-rs/esp-hal?rev=40d5481eddd70580eeb79b6908f6fec0e9b06d61#40d5481eddd70580eeb79b6908f6fec0e9b06d61" +dependencies = [ + "anyhow", + "basic-toml", + "clap", + "lazy_static", + "serde", + "strum 0.26.3", +] + +[[package]] +name = "esp-println" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58cd4fa834980ba64aad00a5c2c1a630020af984eadef65a125cb99085f6f54c" +dependencies = [ + "critical-section", + "esp-build 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "portable-atomic", +] + +[[package]] +name = "esp-riscv-rt" +version = "0.9.0" +source = "git+https://github.com/esp-rs/esp-hal?rev=40d5481eddd70580eeb79b6908f6fec0e9b06d61#40d5481eddd70580eeb79b6908f6fec0e9b06d61" +dependencies = [ + "document-features", + "riscv", + "riscv-rt-macros", +] + +[[package]] +name = "esp-wifi" +version = "0.7.1" +source = "git+https://github.com/esp-rs/esp-hal?rev=40d5481eddd70580eeb79b6908f6fec0e9b06d61#40d5481eddd70580eeb79b6908f6fec0e9b06d61" +dependencies = [ + "atomic-waker", + "cfg-if", + "critical-section", + "embedded-io", + "enumset", + "esp-build 0.1.0 (git+https://github.com/esp-rs/esp-hal?rev=40d5481eddd70580eeb79b6908f6fec0e9b06d61)", + "esp-hal", + "esp-hal-embassy", + "esp-wifi-sys", + "fugit", + "futures-util", + "heapless 0.8.0", + "libm", + "linked_list_allocator", + "log", + "no-std-net", + "num-derive", + "num-traits", + "portable-atomic", + "portable_atomic_enum", + "smoltcp", + "toml-cfg", +] + +[[package]] +name = "esp-wifi-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1829bd8c63b22a6d8a5485203a6e6bdc008897d9ee687aa6df8b57d2103a48d2" +dependencies = [ + "anyhow", +] + +[[package]] +name = "esp32" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4f67907f683a5049ad257cb57a757852bce959eed3e0e8f5e8c212de807714" +dependencies = [ + "critical-section", + "vcell", + "xtensa-lx", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fugit" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" +dependencies = [ + "gcd", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[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.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" +dependencies = [ + "typenum", +] + +[[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.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[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", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32 0.3.1", + "portable-atomic", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linked_list_allocator" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minijinja" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55e877d961d4f96ce13615862322df7c0b6d169d40cab71a7ef3f9b9e594451e" +dependencies = [ + "serde", +] + +[[package]] +name = "mutex-trait" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4bb1638d419e12f8b1c43d9e639abd0d1424285bdea2f76aa231e233c63cd3a" + +[[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 = "no-std-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "portable-atomic" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" + +[[package]] +name = "portable_atomic_enum" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d48f60c43e0120bb2bb48589a16d4bed2f4b911be41e299f2d0fc0e0e20885" +dependencies = [ + "portable-atomic", + "portable_atomic_enum_macros", +] + +[[package]] +name = "portable_atomic_enum_macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33fa6ec7f2047f572d49317cca19c87195de99c6e5b6ee492da701cfe02b053" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "ppproto" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b08f23bada7fbe9266b2d485b3abefdff31f1d3cc8c77f067165cf23848c9a32" +dependencies = [ + "heapless 0.7.17", + "log", + "num_enum", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r0" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[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-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d100d466dbb76681ef6a9386f3da9abc570d57394e86da0ba5af8c4408486d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[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.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79d1bfe77da77f48286b7af6b1d718cde31d6f59a3ccae5b7d8bcf0d02b3c2e1" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + +[[package]] +name = "smoltcp" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a1a996951e50b5971a2c8c0fa05a381480d70a933064245c4a223ddc87ccc97" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "cfg-if", + "heapless 0.8.0", + "managed", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_cell" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89b0684884a883431282db1e4343f34afc2ff6996fe1f4a1664519b66e14c1e" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros 0.26.4", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.72", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "toml" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a44eede9b727419af8095cb2d72fab15487a541f54647ad4414b34096ee4631" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.18", +] + +[[package]] +name = "toml-cfg" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c587298ddd135c156e92e8c3eae69614d6eecea8e2d8a09daab011e5e6a21d" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "serde", + "syn 2.0.72", + "toml", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1490595c74d930da779e944f5ba2ecdf538af67df1a9848cbd156af43c1b7cf0" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.16", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[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 = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c" +dependencies = [ + "memchr", +] + +[[package]] +name = "xtensa-lx" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e758f94e1a1f71758f94052a2766dcb12604998eb372b8b2e30576e3ab1ba1e6" +dependencies = [ + "bare-metal", + "mutex-trait", + "spin", +] + +[[package]] +name = "xtensa-lx-rt" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904102108b780c9a5e3275c5f3c63dc348ec43ae5da5237868515498b447d51a" +dependencies = [ + "bare-metal", + "core-isa-parser", + "minijinja", + "r0", + "xtensa-lx-rt-proc-macros", +] + +[[package]] +name = "xtensa-lx-rt-proc-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "082cdede098bbec9af15b0e74085e5f3d16f2923597de7aed7b8112003af2da7" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.72", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9571ac3 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,124 @@ +[package] +name = "e-token" +version = "0.1.0" +authors = ["aok"] +edition = "2021" +license = "MIT OR Apache-2.0" + +[lib] +name = "e_token" +# path = "src/modem/lib.rs" + +[features] +esp32 = [] + +[dependencies] +esp-backtrace = { version = "0.13.0", features = [ + "esp32", + "exception-handler", + "panic-handler", + "println", +] } +hal = { package = "esp-hal", version = "0.19.0", features = [ + "esp32", + "embassy-futures", + "embassy-sync", + "async", + "embedded-io", + "embedded-io-async", + # "esp-hal-embassy", +] } +# hal = { package = "esp-hal", path = "/Users/aok/Projects/Hardware/exprojects/esp-hal/esp-hal", features = [ +# "esp32", +# "embassy-futures", +# "embassy-sync", +# "async", +# "embedded-io", +# "embedded-io-async", +# # "esp-hal-embassy", +# ] } + +esp-hal-embassy = { version = "0.2.0", features = [ + "esp32", + "executors", + "log", + "integrated-timers", +] } +# esp-hal-embassy = { path = "/Users/aok/Projects/Hardware/exprojects/esp-hal/esp-hal-embassy", features = [ +# "esp32", +# "executors", +# "log", +# "integrated-timers", +# ] } +embassy-time = "0.3.1" +embassy-sync = "0.6.0" +embedded-io-async = "0.6.1" +embassy-futures = "0.1.1" +embassy-net = { version = "0.4.0", features = [ + "log", + "medium-ethernet", + "medium-ip", + "tcp", + "udp", + "dns", + "dhcpv4", + "proto-ipv6", +] } +embassy-net-ppp = { version = "0.1.0", features = ["log"] } +static_cell = { version = "2.1.0", features = ["nightly"] } +embassy-executor = { version = "0.5.0", features = [ + "nightly", + "integrated-timers", +] } +esp-println = { version = "0.10.0", features = ["esp32", "log"] } +log = { version = "0.4.21" } +esp-alloc = { version = "0.4.0" } +embedded-io = "0.6.1" +esp-wifi = { version = "0.7.1", features = [ + "esp32", + "phy-enable-usb", + "utils", + "wifi", +] } +# esp-wifi = { path = "/Users/aok/Projects/Hardware/exprojects/esp-hal/esp-wifi", features = [ +# "esp32", +# "phy-enable-usb", +# "utils", +# "wifi", +# ] } +esp-hal-procmacros = { version = "0.12.0", features = ["embassy"] } + +heapless = { version = "0.8.0", default-features = false } +smoltcp = { version = "0.11.0", default-features = false, features = [ + "medium-ethernet", + "proto-dhcpv4", + "proto-igmp", + "proto-ipv4", + "socket-dhcpv4", + "socket-icmp", + "socket-raw", + "socket-tcp", + "socket-udp", +] } +rand_core = { version = "0.6.3", default-features = false } +rand = { version = "0.8.5", default-features = false, features = ["small_rng"] } + +[profile.dev] +# Rust debug is too slow. +# For debug builds always builds with some optimization +opt-level = "s" + +[profile.release] +codegen-units = 1 # LLVM can perform better optimizations using a single thread +debug = 2 +debug-assertions = false +incremental = false +lto = 'fat' +opt-level = 's' +overflow-checks = false + +[patch.crates-io] +hal = { package = "esp-hal", git = "https://github.com/esp-rs/esp-hal", rev = "40d5481eddd70580eeb79b6908f6fec0e9b06d61" } +esp-wifi = { git = "https://github.com/esp-rs/esp-hal", rev = "40d5481eddd70580eeb79b6908f6fec0e9b06d61" } +esp-hal-embassy = { git = "https://github.com/esp-rs/esp-hal", rev = "40d5481eddd70580eeb79b6908f6fec0e9b06d61" } +esp-hal-procmacros = { git = "https://github.com/esp-rs/esp-hal", rev = "40d5481eddd70580eeb79b6908f6fec0e9b06d61" } diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..d2c040e --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..3d3dcba --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright [year] [fullname] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..9c94ad6 --- /dev/null +++ b/build.rs @@ -0,0 +1,5 @@ +fn main() { + println!("cargo:rustc-link-arg-bins=-Tlinkall.x"); + + println!("cargo:rustc-link-arg-bins=-Trom_functions.x"); +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..a2f5ab5 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "esp" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..32a0d51 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,8 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#![deny(clippy::clone_on_ref_ptr)] +extern crate alloc; + +pub mod modem; +pub mod utils; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a044b77 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,147 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#![deny(clippy::clone_on_ref_ptr)] + +use embassy_executor::Spawner; +// use embassy_futures::select::{select, Either}; +use embassy_net::{Config, ConfigV4, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net_ppp::Runner; +use embassy_sync::{blocking_mutex::raw::NoopRawMutex, mutex::Mutex, signal::Signal}; +use embassy_time::{with_timeout, Duration, Timer}; +use esp_backtrace as _; +use hal::{ + clock::{ClockControl, Clocks}, + delay::Delay, + gpio::{GpioPin, Io}, + peripherals::{Peripherals, UART0, UART1}, + prelude::*, + system::SystemControl, + timer::{timg::TimerGroup, ErasedTimer, OneShotTimer, PeriodicTimer}, + uart::{ + config::{Config as UartConfig, DataBits, Parity, StopBits}, + ClockSource, Uart, + }, + Async, +}; +use static_cell::StaticCell; + +extern crate alloc; +use core::mem::MaybeUninit; + +use e_token::{modem::modem_managers::ModemManager, utils::generate_random_seed}; + +#[global_allocator] +static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); + +fn init_heap() { + const HEAP_SIZE: usize = 32 * 1024; + static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = MaybeUninit::uninit(); + + unsafe { + ALLOCATOR.init(HEAP.as_mut_ptr() as *mut u8, HEAP_SIZE); + } +} +// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html +macro_rules! mk_static { + ($t:ty,$val:expr) => {{ + static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); + #[deny(unused_attributes)] + let x = STATIC_CELL.uninit().write(($val)); + x + }}; +} +// #[entry] +#[esp_hal_procmacros::main] +async fn main(spawner: Spawner) { + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + + let clocks = ClockControl::max(system.clock_control).freeze(); + let delay = Delay::new(&clocks); + init_heap(); + + esp_println::logger::init_logger_from_env(); + + // let timer = hal::timer::PeriodicTimer::new( + // hal::timer::timg::TimerGroup::new(peripherals.TIMG1, &clocks) + // .timer0 + // .into(), + // ); + + // let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks, None); + let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let timer0: ErasedTimer = timg0.timer0.into(); + let timer = PeriodicTimer::new(timer0); + + let _init = esp_wifi::initialize( + esp_wifi::EspWifiInitFor::Wifi, + timer, + hal::rng::Rng::new(peripherals.RNG), + peripherals.RADIO_CLK, + &clocks, + ) + .unwrap(); + + #[cfg(feature = "esp32")] + { + // let timg1 = TimerGroup::new(peripherals.TIMG1, &clocks, None); + let timg1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let timer0: ErasedTimer = timg1.timer0.into(); + let timers = [OneShotTimer::new(timer0)]; + let timers = mk_static!([OneShotTimer; 1], timers); + esp_hal_embassy::init(&clocks, timers); + } + + // let timers = [OneShotTimer::new(timer0)]; + // let timers = mk_static!([OneShotTimer; 1], timers); + // esp_hal_embassy::init(&clocks, timers); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let config = UartConfig { + baudrate: 115200, + data_bits: DataBits::DataBits8, + parity: Parity::ParityNone, + stop_bits: StopBits::STOP1, + clock_source: ClockSource::Apb, + rx_fifo_full_threshold: 1, + rx_timeout: Some(3), + }; + + config.rx_fifo_full_threshold(1); + #[cfg(feature = "esp32")] + let (tx_pin, rx_pin) = (io.pins.gpio14, io.pins.gpio15); + + // let mut uart0 = + // Uart::new_async_with_config(peripherals.UART0, config, &clocks, tx_pin, rx_pin).unwrap(); + + let uart: Uart<'static, UART1, Async> = + Uart::new_async_with_config(peripherals.UART1, config, &clocks, tx_pin, rx_pin).unwrap(); + + // let mut modem_manager = ModemManager::new(uart); + // modem_manager.configure_connection().await.unwrap(); + + // Init network device + static STATE: StaticCell> = StaticCell::new(); + let state = STATE.init(embassy_net_ppp::State::<4, 4>::new()); + let (device, runner) = embassy_net_ppp::new(state); + + // Generate random seed + let seed = generate_random_seed(); + + // Init network stack + static STACK: StaticCell>> = StaticCell::new(); + static RESOURCES: StaticCell> = StaticCell::new(); + + let stack = &*STACK.init(Stack::new( + device, + Config::default(), // don't configure IP yet + RESOURCES.init(StackResources::<3>::new()), + seed, + )); + loop { + // modem_manager.check_and_handle_sms().await.unwrap(); + Timer::after(Duration::from_secs(5)).await; + } +} diff --git a/src/modem/mod.rs b/src/modem/mod.rs new file mode 100644 index 0000000..bd7be3a --- /dev/null +++ b/src/modem/mod.rs @@ -0,0 +1 @@ +pub mod modem_managers; diff --git a/src/modem/modem_managers.rs b/src/modem/modem_managers.rs new file mode 100644 index 0000000..22e82f5 --- /dev/null +++ b/src/modem/modem_managers.rs @@ -0,0 +1,390 @@ +use core::{ + fmt, + sync::atomic::{AtomicBool, Ordering}, +}; + +use alloc::{ + format, + string::{String, ToString}, + vec::Vec, +}; +use embassy_futures::select::select; +use embassy_time::{Duration, Instant, Timer}; +use embedded_io_async::{BufRead, Read, Write}; +use log::info; + +pub struct ModemManager { + pub io: ModemIO, + ppp_interval: Duration, + last_ppp_time: Instant, +} + +impl ModemManager { + pub fn new(uart: T) -> Self { + Self { + io: ModemIO::new(uart), + ppp_interval: Duration::from_secs(3600), // Default to 1 hour + last_ppp_time: Instant::now(), + } + } + async fn send_at_command(&mut self, command: &str) -> Result { + self.io.ensure_command_mode().await?; + self.io.inner.write_all(command.as_bytes()).await?; + self.io.inner.write_all(b"\r\n").await?; + + let mut response = String::new(); + let mut buf = [0u8; 64]; + loop { + match self.io.inner.read(&mut buf).await { + Ok(0) => break, + Ok(n) => { + response.push_str(core::str::from_utf8(&buf[..n]).unwrap_or("")); + if response.contains("OK") || response.contains("ERROR") { + break; + } + } + Err(e) => return Err(e), + } + } + Ok(response) + } + + pub async fn configure_connection(&mut self) -> Result<(), T::Error> { + self.send_at_command("AT+CGATT=1").await?; + self.send_at_command("AT+CGDCONT=1,\"IP\",\"cmnet\"") + .await?; + self.send_at_command("AT+CGACT=1,1").await?; + self.send_at_command("AT+CMGF=1").await?; + Ok(()) + } + async fn run(&mut self) -> Result<(), T::Error> { + self.configure_connection().await?; + + loop { + // select(self.check_and_handle_sms(), self.periodic_ppp_session()).await; + + // Wait for a short period before the next iteration + Timer::after(Duration::from_secs(1)).await; + } + } + async fn send_sms(&mut self, recipient: &str, message: &str) -> Result<(), T::Error> { + self.send_at_command(&format!("AT+CMGS=\"{}\"", recipient)) + .await?; + Timer::after(Duration::from_millis(100)).await; + self.io.inner.write_all(message.as_bytes()).await?; + self.io.inner.write_all(&[0x1A]).await?; // Ctrl+Z to send the message + Timer::after(Duration::from_secs(1)).await; + + // Wait for the "OK" response + let mut response = String::new(); + let mut buf = [0u8; 64]; + loop { + match self.io.inner.read(&mut buf).await { + Ok(0) => break, + Ok(n) => { + response.push_str(core::str::from_utf8(&buf[..n]).unwrap_or("")); + if response.contains("OK") || response.contains("ERROR") { + break; + } + } + Err(e) => return Err(e), + } + } + + if response.contains("OK") { + Ok(()) + } else { + // Err(T::Error::from("Failed to send SMS")) + Ok(()) + } + } + + async fn periodic_ppp_session(&mut self) -> Result<(), T::Error> { + if Instant::now().duration_since(self.io.last_ppp_session) >= self.io.ppp_interval { + info!("Starting periodic PPP session"); + self.start_ppp().await?; + + // Run PPP for a fixed duration (e.g., 5 minutes) + Timer::after(Duration::from_secs(300)).await; + + // Exit PPP mode + self.exit_ppp().await?; + + self.io.last_ppp_session = Instant::now(); + } + Ok(()) + } + pub async fn check_and_handle_sms(&mut self) -> Result<(), T::Error> { + if self.io.new_sms_flag.load(Ordering::Relaxed) { + self.io.new_sms_flag.store(false, Ordering::Relaxed); + self.handle_incoming_sms().await?; + } + Ok(()) + } + + async fn handle_incoming_sms(&mut self) -> Result<(), T::Error> { + let response = self.send_at_command("AT+CMGL=\"REC UNREAD\"").await?; + + // Parse the response to extract SMS details + for line in response.lines() { + if line.starts_with("+CMGL:") { + let parts: Vec<&str> = line.split(',').collect(); + if parts.len() >= 3 { + let index = parts[0].split(':').nth(1).unwrap().trim(); + let sender = parts[2].trim_matches('"'); + + // Read the message content (it's in the next line) + if let Some(content) = response + .lines() + .nth(response.lines().position(|l| l == line).unwrap() + 1) + { + info!("New SMS from {}: {}", sender, content); + + // Process the SMS content here + self.process_sms_command(sender, content).await?; + + // Delete the processed message + self.send_at_command(&format!("AT+CMGD={}", index)).await?; + } + } + } + } + Ok(()) + } + + pub async fn start_ppp(&mut self) -> Result<(), T::Error> { + self.io.enter_data_mode().await + } + pub async fn exit_ppp(&mut self) -> Result<(), T::Error> { + // Send "+++" to exit PPP mode + self.io.inner.write_all(b"+++").await?; + Timer::after(Duration::from_secs(1)).await; + + // Confirm we're back in command mode + self.send_at_command("AT").await?; + + self.io.state = ModemState::CommandMode; + Ok(()) + } + async fn process_sms_command(&mut self, sender: &str, content: &str) -> Result<(), T::Error> { + match content.trim().to_lowercase().as_str() { + "status" => { + let status = self.get_connection_status().await?; + self.send_sms(sender, &status).await?; + } + "restart" => { + self.send_sms(sender, "Restarting modem...").await?; + self.restart_modem().await?; + } + "ip" => { + let ip = self.get_ip_address().await?; + self.send_sms(sender, &format!("Current IP: {}", ip)) + .await?; + } + "signal" => { + let signal = self.get_signal_strength().await?; + self.send_sms(sender, &format!("Signal strength: {}", signal)) + .await?; + } + _ => { + self.send_sms( + sender, + "Unknown command. Available commands: status, restart, ip, signal", + ) + .await?; + } + } + Ok(()) + } + async fn get_connection_status(&mut self) -> Result { + let response = self.send_at_command("AT+CREG?").await?; + if response.contains("+CREG: 0,1") || response.contains("+CREG: 0,5") { + Ok("Connected".to_string()) + } else { + Ok("Not connected".to_string()) + } + } + + async fn restart_modem(&mut self) -> Result<(), T::Error> { + self.send_at_command("AT+CFUN=1,1").await?; + Timer::after(Duration::from_secs(10)).await; + self.configure_connection().await + } + + async fn get_ip_address(&mut self) -> Result { + let response = self.send_at_command("AT+CGPADDR=1").await?; + if let Some(ip) = response.lines().find(|line| line.starts_with("+CGPADDR:")) { + Ok(ip + .split(',') + .nth(1) + .unwrap_or("Unknown") + .trim_matches('"') + .to_string()) + } else { + Ok("Unknown".to_string()) + } + } + + async fn get_signal_strength(&mut self) -> Result { + let response = self.send_at_command("AT+CSQ").await?; + if let Some(csq) = response.lines().find(|line| line.starts_with("+CSQ:")) { + let parts: Vec<&str> = csq.split(':').nth(1).unwrap().split(',').collect(); + if let Some(rssi) = parts.first() { + let rssi_val: i32 = rssi.trim().parse().unwrap_or(-1); + if rssi_val >= 0 && rssi_val <= 31 { + let dbm = -113 + (2 * rssi_val); + Ok(format!("{} dBm", dbm)) + } else if rssi_val == 99 { + Ok("Unknown".to_string()) + } else { + Ok("Invalid".to_string()) + } + } else { + Ok("Unknown".to_string()) + } + } else { + Ok("Unknown".to_string()) + } + } + pub async fn handle_server_payload(&mut self, payload: &str) -> Result<(), T::Error> { + if let Some(interval) = payload.strip_prefix("SET_PPP_INTERVAL:") { + if let Ok(seconds) = interval.parse::() { + let new_interval = Duration::from_secs(seconds); + self.io.set_ppp_interval(new_interval); + info!("PPP interval updated to {} seconds", seconds); + } + } + Ok(()) + } +} + +pub struct ModemIO { + pub inner: T, + state: ModemState, + last_write: Option, + sms_check_interval: Duration, + last_sms_check: Instant, + new_sms_flag: AtomicBool, + ppp_interval: Duration, + last_ppp_session: Instant, +} +impl ModemIO { + fn new(inner: T) -> Self { + Self { + inner, + state: ModemState::CommandMode, + last_write: None, + sms_check_interval: Duration::from_secs(30), + last_sms_check: Instant::now(), + new_sms_flag: AtomicBool::new(false), + ppp_interval: Duration::from_secs(3600), // Default 1 hour + last_ppp_session: Instant::now(), + } + } + async fn ensure_command_mode(&mut self) -> Result<(), T::Error> { + if self.state == ModemState::CommandMode { + return Ok(()); + } + + // Send the escape sequence to exit data mode + self.inner.write_all(b"+++").await?; + Timer::after(Duration::from_secs(1)).await; + self.state = ModemState::CommandMode; + Ok(()) + } + async fn enter_data_mode(&mut self) -> Result<(), T::Error> { + self.inner.write_all(b"ATD*99***1#\r\n").await?; + Timer::after(Duration::from_secs(1)).await; + self.state = ModemState::DataMode; + Ok(()) + } + fn set_ppp_interval(&mut self, interval: Duration) { + self.ppp_interval = interval; + } + async fn check_for_sms(&mut self) -> Result<(), T::Error> { + self.ensure_command_mode().await?; + self.state = ModemState::CheckingSMS; + + // Check for new SMS + self.inner.write_all(b"AT+CMGL=\"REC UNREAD\"\r\n").await?; + + let mut response = String::new(); + let mut buf = [0u8; 128]; + loop { + match self.inner.read(&mut buf).await { + Ok(0) => break, + Ok(n) => { + response.push_str(core::str::from_utf8(&buf[..n]).unwrap_or("")); + if response.contains("OK") { + break; + } + } + Err(e) => return Err(e), + } + } + + if response.contains("+CMGL:") { + self.new_sms_flag.store(true, Ordering::Relaxed); + } + + self.state = ModemState::CommandMode; + self.last_sms_check = Instant::now(); + Ok(()) + } +} + +#[derive(PartialEq)] +pub enum ModemState { + CommandMode, + DataMode, + TransitioningToCommandMode, + CheckingSMS, +} + +// Add this custom error type and conversion: +#[derive(Debug)] +pub struct ModemError; + +impl core::fmt::Display for ModemError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Modem error") + } +} + +impl core::convert::From for core::fmt::Error { + fn from(_: ModemError) -> Self { + core::fmt::Error + } +} + +/* +AT+CGATT=1 ;Attach to the GPRS network, can also use parameter 0 to detach. +OK ;Response, attach successful + +AT+CGDCONT=? ;Input test command for help information. ++CGDCONT: (1..7), (IP,IPV6,PPP),(0..3),(0..4) OK ;Response, show the helpful information. + +// AT+CGDCONT=1,"IP","internet" +AT+CGDCONT=1, "IP", "cmnet" ;Before active, use this command to set PDP context. +OK ;Response. Set context OK. + +AT+CGACT=1,1 ;Active command is used to active the specified PDP context. +OK ;Response, active successful. + +ATD*99***1# ;This command is to start PPP translation. +CONNECT ;Response, when get this, the module has been set to data state. + PPP data should be transferred after this response and anything input is treated as data. + i need to get into this mode when i need to make a http request and publish to an mqtt broker + using reqwless crate and rust-mqtt crate respectively. So i need to be in COMMAND state mode + as the default mode to be able to listen to incoming sms URCs and only shift to PPP mode or data state + when i need to communicate to server(s). So i should be in COMMAND state most of the time and + be in data state like three times in 24hrs. + ++++ ;This command is to change the status to online data state. + Notice that before input this command, you need to wait for a + three seconds’ break, and it should also be followed by 3 seconds’ + break, otherwise “+++” will be treated as data. + +ATH ;Use this command to return COMMAND state +ok Response +*/ diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..4142610 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,41 @@ +extern crate rand; +extern crate rand_core; // or your preferred panic handler + +use rand::rngs::SmallRng; +use rand_core::RngCore; +use rand_core::SeedableRng; + +#[macro_export] +macro_rules! singleton { + ($val:expr, $T:ty) => {{ + static STATIC_CELL: ::static_cell::StaticCell<$T> = ::static_cell::StaticCell::new(); + STATIC_CELL.init($val) + }}; +} + +// Dummy entropy source for demonstration purposes +struct DummyEntropySource; + +impl DummyEntropySource { + fn new() -> Self { + DummyEntropySource + } + + fn get_entropy(&self) -> [u8; 16] { + // In a real `no_std` environment, you need to replace this with a proper entropy source. + [0x42; 16] // Just an example, replace with real entropy + } +} + +pub fn generate_random_seed() -> u64 { + let entropy_source = DummyEntropySource::new(); + let seed = entropy_source.get_entropy(); + + let mut rng = SmallRng::from_seed(seed); + + // Generate random seed + let mut seed_bytes = [0u8; 8]; + rng.fill_bytes(&mut seed_bytes); + + u64::from_le_bytes(seed_bytes) +} diff --git a/uart.rs.txt b/uart.rs.txt new file mode 100644 index 0000000..8b11df6 --- /dev/null +++ b/uart.rs.txt @@ -0,0 +1,2663 @@ +//! # Universal Asynchronous Receiver/Transmitter (UART) +//! +//! ## Overview +//! The UART is a hardware peripheral which handles communication using serial +//! communication interfaces, such as RS232 and RS485. This peripheral provides +//! a cheap and ubiquitous method for full- and half-duplex communication +//! between devices. +//! +//! Depending on your device, two or more UART controllers are available for +//! use, all of which can be configured and used in the same way. All UART +//! controllers are compatible with UART-enabled devices from various +//! manufacturers, and can also support Infrared Data Association (IrDA) +//! protocols. +//! +//! ## Configuration +//! Each UART controller is individually configurable, and the usual setting +//! such as baud rate, data bits, parity, and stop bits can easily be +//! configured. Additionally, the transmit (TX) and receive (RX) pins need to +//! be specified. +//! +//! ```rust, no_run +#![doc = crate::before_snippet!()] +//! # use core::option::Option::Some; +//! # use esp_hal::uart::{config::Config, Uart}; +//! use esp_hal::gpio::Io; +//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); +//! +//! let mut uart1 = Uart::new(peripherals.UART1, &clocks, io.pins.gpio1, +//! io.pins.gpio2).unwrap(); +//! # } +//! ``` +//! +//! The UART controller can be configured to invert the polarity of the pins. +//! This is achived by inverting the desired pins, and then constucting the +//! UART instance using the inverted pins. +//! +//! ## Usage +//! The UART driver implements a number of third-party traits, with the +//! intention of making the HAL inter-compatible with various device drivers +//! from the community. This includes, but is not limited to, the [embedded-hal] +//! and [embedded-io] blocking traits, and the [embedded-hal-async] and +//! [embedded-io-async] asynchronous traits. +//! +//! In addition to the interfaces provided by these traits, native APIs are also +//! available. See the examples below for more information on how to interact +//! with this driver. +//! +//! ## Examples +//! ### Sending and Receiving Data +//! ```rust, no_run +#![doc = crate::before_snippet!()] +//! # use esp_hal::uart::{config::Config, Uart}; +//! use esp_hal::gpio::Io; +//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); +//! # let mut uart1 = Uart::new_with_config( +//! # peripherals.UART1, +//! # Config::default(), +//! # &clocks, +//! # io.pins.gpio1, +//! # io.pins.gpio2, +//! # ).unwrap(); +//! // Write bytes out over the UART: +//! uart1.write_bytes("Hello, world!".as_bytes()).expect("write error!"); +//! # } +//! ``` +//! +//! ### Splitting the UART into TX and RX Components +//! ```rust, no_run +#![doc = crate::before_snippet!()] +//! # use esp_hal::uart::{config::Config, Uart}; +//! use esp_hal::gpio::Io; +//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); +//! # let mut uart1 = Uart::new_with_config( +//! # peripherals.UART1, +//! # Config::default(), +//! # &clocks, +//! # io.pins.gpio1, +//! # io.pins.gpio2, +//! # ).unwrap(); +//! // The UART can be split into separate Transmit and Receive components: +//! let (mut tx, mut rx) = uart1.split(); +//! +//! // Each component can be used individually to interact with the UART: +//! tx.write_bytes(&[42u8]).expect("write error!"); +//! let byte = rx.read_byte().expect("read error!"); +//! # } +//! ``` +//! +//! ### Inverting TX and RX Pins +//! ```rust, no_run +#![doc = crate::before_snippet!()] +//! # use esp_hal::uart::{config::Config, Uart}; +//! use esp_hal::gpio::{Io, any_pin::AnyPin}; +//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); +//! +//! let tx = AnyPin::new_inverted(io.pins.gpio1); +//! let rx = AnyPin::new_inverted(io.pins.gpio2); +//! let mut uart1 = Uart::new(peripherals.UART1, &clocks, tx, rx).unwrap(); +//! # } +//! ``` +//! +//! ### Constructing TX and RX Components +//! ```rust, no_run +#![doc = crate::before_snippet!()] +//! # use esp_hal::uart::{config::Config, UartTx, UartRx}; +//! use esp_hal::gpio::{Io, any_pin::AnyPin}; +//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); +//! +//! let tx = UartTx::new(peripherals.UART0, &clocks, +//! io.pins.gpio1).unwrap(); +//! let rx = UartRx::new(peripherals.UART1, &clocks, +//! io.pins.gpio2).unwrap(); +//! # } +//! ``` +//! +//! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/ +//! [embedded-io]: https://docs.rs/embedded-io/latest/embedded_io/ +//! [embedded-hal-async]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/ +//! [embedded-io-async]: https://docs.rs/embedded-io-async/latest/embedded_io_async/ + +use core::marker::PhantomData; + +use self::config::Config; +use crate::{ + clock::Clocks, + gpio::{InputPin, InputSignal, OutputPin, OutputSignal}, + interrupt::InterruptHandler, + peripheral::Peripheral, + peripherals::{ + uart0::{fifo::FIFO_SPEC, RegisterBlock}, + Interrupt, + }, + private::Internal, + system::PeripheralClockControl, + Blocking, + InterruptConfigurable, + Mode, +}; + +const CONSOLE_UART_NUM: usize = 0; +const UART_FIFO_SIZE: u16 = 128; + +#[cfg(not(any(esp32, esp32s2)))] +use crate::soc::constants::RC_FAST_CLK; +#[cfg(any(esp32, esp32s2))] +use crate::soc::constants::REF_TICK; + +// Default TX and RX pins for Uart/Serial communication (UART0) +cfg_if::cfg_if! { + if #[cfg(esp32)] { + pub type DefaultTxPin = crate::gpio::Gpio1; + pub type DefaultRxPin = crate::gpio::Gpio3; + } else if #[cfg(esp32c2)] { + pub type DefaultTxPin = crate::gpio::Gpio20; + pub type DefaultRxPin = crate::gpio::Gpio19; + } else if #[cfg(esp32c3)] { + pub type DefaultTxPin = crate::gpio::Gpio21; + pub type DefaultRxPin = crate::gpio::Gpio20; + }else if #[cfg(esp32c6)] { + pub type DefaultTxPin = crate::gpio::Gpio16; + pub type DefaultRxPin = crate::gpio::Gpio17; + }else if #[cfg(esp32h2)] { + pub type DefaultTxPin = crate::gpio::Gpio24; + pub type DefaultRxPin = crate::gpio::Gpio23; + } else if #[cfg(esp32s2)] { + pub type DefaultTxPin = crate::gpio::Gpio43; + pub type DefaultRxPin = crate::gpio::Gpio44; + } else if #[cfg(esp32s3)] { + pub type DefaultTxPin = crate::gpio::Gpio43; + pub type DefaultRxPin = crate::gpio::Gpio44; + } +} + +/// UART Error +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// An invalid configuration argument was provided + InvalidArgument, + /// The RX FIFO overflowed + #[cfg(feature = "async")] + RxFifoOvf, + #[cfg(feature = "async")] + RxGlitchDetected, + #[cfg(feature = "async")] + RxFrameError, + #[cfg(feature = "async")] + RxParityError, +} + +#[cfg(feature = "embedded-hal")] +impl embedded_hal_nb::serial::Error for Error { + fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { + embedded_hal_nb::serial::ErrorKind::Other + } +} + +#[cfg(feature = "embedded-io")] +impl embedded_io::Error for Error { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } +} + +// (outside of `config` module in order not to "use" it an extra time) +/// UART clock source +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ClockSource { + /// APB_CLK clock source (default for UART on all the chips except of + /// esp32c6 and esp32h2) + Apb, + #[cfg(not(any(esp32, esp32s2)))] + /// RC_FAST_CLK clock source (17.5 MHz) + RcFast, + #[cfg(not(any(esp32, esp32s2)))] + /// XTAL_CLK clock source (default for UART on esp32c6 and esp32h2 and + /// LP_UART) + Xtal, + #[cfg(any(esp32, esp32s2))] + /// REF_TICK clock source (derived from XTAL or RC_FAST, 1MHz) + RefTick, +} + +/// UART Configuration +pub mod config { + + // see + const UART_FULL_THRESH_DEFAULT: u16 = 120; + // see + const UART_TOUT_THRESH_DEFAULT: u8 = 10; + + /// Number of data bits + #[derive(PartialEq, Eq, Copy, Clone, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum DataBits { + DataBits5 = 0, + DataBits6 = 1, + DataBits7 = 2, + DataBits8 = 3, + } + + /// Parity check + #[derive(PartialEq, Eq, Copy, Clone, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum Parity { + ParityNone, + ParityEven, + ParityOdd, + } + + /// Number of stop bits + #[derive(PartialEq, Eq, Copy, Clone, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum StopBits { + /// 1 stop bit + STOP1 = 1, + /// 1.5 stop bits + STOP1P5 = 2, + /// 2 stop bits + STOP2 = 3, + } + + /// UART Configuration + #[derive(Debug, Copy, Clone)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct Config { + pub baudrate: u32, + pub data_bits: DataBits, + pub parity: Parity, + pub stop_bits: StopBits, + pub clock_source: super::ClockSource, + pub rx_fifo_full_threshold: u16, + pub rx_timeout: Option, + } + + impl Config { + pub fn baudrate(mut self, baudrate: u32) -> Self { + self.baudrate = baudrate; + self + } + + pub fn parity_none(mut self) -> Self { + self.parity = Parity::ParityNone; + self + } + + pub fn parity_even(mut self) -> Self { + self.parity = Parity::ParityEven; + self + } + + pub fn parity_odd(mut self) -> Self { + self.parity = Parity::ParityOdd; + self + } + + pub fn data_bits(mut self, data_bits: DataBits) -> Self { + self.data_bits = data_bits; + self + } + + pub fn stop_bits(mut self, stop_bits: StopBits) -> Self { + self.stop_bits = stop_bits; + self + } + + pub fn clock_source(mut self, source: super::ClockSource) -> Self { + self.clock_source = source; + self + } + + pub fn symbol_length(&self) -> u8 { + let mut length: u8 = 1; // start bit + length += match self.data_bits { + DataBits::DataBits5 => 5, + DataBits::DataBits6 => 6, + DataBits::DataBits7 => 7, + DataBits::DataBits8 => 8, + }; + length += match self.parity { + Parity::ParityNone => 0, + _ => 1, + }; + length += match self.stop_bits { + StopBits::STOP1 => 1, + _ => 2, // esp-idf also counts 2 bits for settings 1.5 and 2 stop bits + }; + length + } + + pub fn rx_fifo_full_threshold(mut self, threshold: u16) -> Self { + self.rx_fifo_full_threshold = threshold; + self + } + + pub fn rx_timeout(mut self, timeout: Option) -> Self { + self.rx_timeout = timeout; + self + } + } + + impl Default for Config { + fn default() -> Config { + Config { + baudrate: 115_200, + data_bits: DataBits::DataBits8, + parity: Parity::ParityNone, + stop_bits: StopBits::STOP1, + #[cfg(any(esp32c6, esp32h2, lp_uart))] + clock_source: super::ClockSource::Xtal, + #[cfg(not(any(esp32c6, esp32h2, lp_uart)))] + clock_source: super::ClockSource::Apb, + rx_fifo_full_threshold: UART_FULL_THRESH_DEFAULT, + rx_timeout: Some(UART_TOUT_THRESH_DEFAULT), + } + } + } + + /// Configuration for the AT-CMD detection functionality + pub struct AtCmdConfig { + pub pre_idle_count: Option, + pub post_idle_count: Option, + pub gap_timeout: Option, + pub cmd_char: u8, + pub char_num: Option, + } + + impl AtCmdConfig { + pub fn new( + pre_idle_count: Option, + post_idle_count: Option, + gap_timeout: Option, + cmd_char: u8, + char_num: Option, + ) -> AtCmdConfig { + Self { + pre_idle_count, + post_idle_count, + gap_timeout, + cmd_char, + char_num, + } + } + } +} + +/// UART (Full-duplex) +pub struct Uart<'d, T, M> { + tx: UartTx<'d, T, M>, + rx: UartRx<'d, T, M>, +} + +/// UART (Transmit) +pub struct UartTx<'d, T, M> { + phantom: PhantomData<(&'d mut T, M)>, +} + +/// UART (Receive) +pub struct UartRx<'d, T, M> { + phantom: PhantomData<(&'d mut T, M)>, + at_cmd_config: Option, + rx_timeout_config: Option, + internal_buf: [u8; 1024], // Buffer as part of the struct + #[cfg(not(esp32))] + symbol_len: u8, +} + +impl<'d, T, M> UartTx<'d, T, M> +where + T: Instance, + M: Mode, +{ + fn new_inner() -> Self { + Self { + phantom: PhantomData, + } + } + + /// Configure RTS pin + pub fn with_rts(self, rts: impl Peripheral

+ 'd) -> Self { + crate::into_ref!(rts); + rts.set_to_push_pull_output(Internal); + rts.connect_peripheral_to_output(T::rts_signal(), Internal); + + self + } + + /// Writes bytes + pub fn write_bytes(&mut self, data: &[u8]) -> Result { + let count = data.len(); + + data.iter() + .try_for_each(|c| nb::block!(self.write_byte(*c)))?; + + Ok(count) + } + + fn write_byte(&mut self, word: u8) -> nb::Result<(), Error> { + if T::get_tx_fifo_count() < UART_FIFO_SIZE { + T::register_block() + .fifo() + .write(|w| unsafe { w.rxfifo_rd_byte().bits(word) }); + + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } + + /// Flush the transmit buffer of the UART + pub fn flush_tx(&mut self) -> nb::Result<(), Error> { + if T::is_tx_idle() { + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } +} + +impl<'d, T> UartTx<'d, T, Blocking> +where + T: Instance + 'd, +{ + /// Create a new UART TX instance in [`Blocking`] mode. + pub fn new( + uart: impl Peripheral

+ 'd, + clocks: &Clocks, + tx: impl Peripheral

+ 'd, + ) -> Result { + Self::new_with_config(uart, Default::default(), clocks, tx) + } + + /// Create a new UART TX instance with configuration options in + /// [`Blocking`] mode. + pub fn new_with_config( + uart: impl Peripheral

+ 'd, + config: Config, + clocks: &Clocks, + tx: impl Peripheral

+ 'd, + ) -> Result { + crate::into_ref!(tx); + tx.set_to_push_pull_output(Internal); + tx.connect_peripheral_to_output(T::tx_signal(), Internal); + + let (uart_tx, _) = + Uart::<'d, T, Blocking>::new_with_config_inner(uart, config, clocks)?.split(); + + Ok(uart_tx) + } +} + +impl<'d, T, M> UartRx<'d, T, M> +where + T: Instance, + M: Mode, +{ + fn new_inner(#[cfg(not(esp32))] symbol_len: u8) -> Self { + Self { + phantom: PhantomData, + at_cmd_config: None, + rx_timeout_config: None, + internal_buf: [0; 1024], // Buffer as part of the struct + #[cfg(not(esp32))] + symbol_len, + } + } + + /// Configure CTS pin + pub fn with_cts(self, cts: impl Peripheral

+ 'd) -> Self { + crate::into_ref!(cts); + cts.set_to_input(Internal); + cts.connect_input_to_peripheral(T::cts_signal(), Internal); + + self + } + + /// Fill a buffer with received bytes + pub fn read_bytes(&mut self, mut buf: &mut [u8]) -> Result<(), Error> { + if buf.is_empty() { + return Ok(()); + } + let cap = buf.len(); + let mut total = 0; + loop { + while T::get_rx_fifo_count() == 0 { + // Block until we received at least one byte + } + let read = self.drain_fifo(buf); + total += read; + // drain_fifo only drains bytes that will fit in buf, + // so we will always have an exact total + if total == cap { + break; + } + // update the buffer position based on the bytes read + buf = &mut buf[read..]; + } + Ok(()) + } + + /// Read a byte from the UART + pub fn read_byte(&mut self) -> nb::Result { + // On the ESP32-S2 we need to use PeriBus2 to read the FIFO: + let offset = if cfg!(esp32s2) { 0x20C00000 } else { 0 }; + + if T::get_rx_fifo_count() > 0 { + let value = unsafe { + let fifo = (T::register_block().fifo().as_ptr() as *mut u8).offset(offset) + as *mut crate::peripherals::generic::Reg; + (*fifo).read().rxfifo_rd_byte().bits() + }; + + Ok(value) + } else { + Err(nb::Error::WouldBlock) + } + } + + /// Read all available bytes from the RX FIFO into the provided buffer and + /// returns the number of read bytes. Never blocks + pub fn drain_fifo(&mut self, buf: &mut [u8]) -> usize { + // On the ESP32-S2 we need to use PeriBus2 to read the FIFO: + let offset = if cfg!(esp32s2) { 0x20C00000 } else { 0 }; + + let mut count = 0; + while T::get_rx_fifo_count() > 0 && count < buf.len() { + let value = unsafe { + let fifo = (T::register_block().fifo().as_ptr() as *mut u8).offset(offset) + as *mut crate::peripherals::generic::Reg; + (*fifo).read().rxfifo_rd_byte().bits() + }; + buf[count] = value; + count += 1; + } + count + } + + /// Configures the RX-FIFO threshold + /// + /// # Errors + /// `Err(Error::InvalidArgument)` if provided value exceeds maximum value + /// for SOC : + /// - `esp32` **0x7F** + /// - `esp32c6`, `esp32h2` **0xFF** + /// - `esp32c3`, `esp32c2`, `esp32s2` **0x1FF** + /// - `esp32s3` **0x3FF** + fn set_rx_fifo_full_threshold(&mut self, threshold: u16) -> Result<(), Error> { + #[cfg(esp32)] + const MAX_THRHD: u16 = 0x7F; + #[cfg(any(esp32c6, esp32h2))] + const MAX_THRHD: u16 = 0xFF; + #[cfg(any(esp32c3, esp32c2, esp32s2))] + const MAX_THRHD: u16 = 0x1FF; + #[cfg(esp32s3)] + const MAX_THRHD: u16 = 0x3FF; + + if threshold > MAX_THRHD { + return Err(Error::InvalidArgument); + } + + #[cfg(any(esp32, esp32c6, esp32h2))] + let threshold: u8 = threshold as u8; + + T::register_block() + .conf1() + .modify(|_, w| unsafe { w.rxfifo_full_thrhd().bits(threshold) }); + + Ok(()) + } + + /// Configures the Receive Timeout detection setting + /// + /// # Arguments + /// `timeout` - the number of symbols ("bytes") to wait for before + /// triggering a timeout. Pass None to disable the timeout. + /// + /// # Errors + /// `Err(Error::InvalidArgument)` if the provided value exceeds the maximum + /// value for SOC : + /// - `esp32`: Symbol size is fixed to 8, do not pass a value > **0x7F**. + /// - `esp32c2`, `esp32c3`, `esp32c6`, `esp32h2`, esp32s2`, esp32s3`: The + /// value you pass times the symbol size must be <= **0x3FF** + fn set_rx_timeout(&mut self, timeout: Option) -> Result<(), Error> { + #[cfg(esp32)] + const MAX_THRHD: u8 = 0x7F; // 7 bits + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s2, esp32s3))] + const MAX_THRHD: u16 = 0x3FF; // 10 bits + + #[cfg(esp32)] + let reg_thrhd = &T::register_block().conf1(); + #[cfg(any(esp32c6, esp32h2))] + let reg_thrhd = &T::register_block().tout_conf(); + #[cfg(any(esp32c2, esp32c3, esp32s2, esp32s3))] + let reg_thrhd = &T::register_block().mem_conf(); + + #[cfg(any(esp32c6, esp32h2))] + let reg_en = &T::register_block().tout_conf(); + #[cfg(any(esp32, esp32c2, esp32c3, esp32s2, esp32s3))] + let reg_en = &T::register_block().conf1(); + + match timeout { + None => { + reg_en.modify(|_, w| w.rx_tout_en().clear_bit()); + } + Some(timeout) => { + // the esp32 counts directly in number of symbols (symbol len fixed to 8) + #[cfg(esp32)] + let timeout_reg = timeout; + // all other count in bits, so we need to multiply by the symbol len. + #[cfg(not(esp32))] + let timeout_reg = timeout as u16 * self.symbol_len as u16; + + if timeout_reg > MAX_THRHD { + return Err(Error::InvalidArgument); + } + + reg_thrhd.modify(|_, w| unsafe { w.rx_tout_thrhd().bits(timeout_reg) }); + reg_en.modify(|_, w| w.rx_tout_en().set_bit()); + } + } + + self.rx_timeout_config = timeout; + + Uart::<'d, T, M>::sync_regs(); + Ok(()) + } +} + +impl<'d, T> UartRx<'d, T, Blocking> +where + T: Instance + 'd, +{ + /// Create a new UART RX instance in [`Blocking`] mode. + pub fn new( + uart: impl Peripheral

+ 'd, + clocks: &Clocks, + rx: impl Peripheral

+ 'd, + ) -> Result { + Self::new_with_config(uart, Default::default(), clocks, rx) + } + + /// Create a new UART RX instance with configuration options in + /// [`Blocking`] mode. + pub fn new_with_config( + uart: impl Peripheral

+ 'd, + config: Config, + clocks: &Clocks, + rx: impl Peripheral

+ 'd, + ) -> Result { + crate::into_ref!(rx); + rx.set_to_input(Internal); + rx.connect_input_to_peripheral(T::rx_signal(), Internal); + + let (_, uart_rx) = + Uart::<'d, T, Blocking>::new_with_config_inner(uart, config, clocks)?.split(); + + Ok(uart_rx) + } +} + +impl<'d, T> Uart<'d, T, Blocking> +where + T: Instance + 'd, +{ + /// Create a new UART instance with configuration options in [`Blocking`] + /// mode. + pub fn new_with_config( + uart: impl Peripheral

+ 'd, + config: Config, + clocks: &Clocks, + tx: impl Peripheral

+ 'd, + rx: impl Peripheral

+ 'd, + ) -> Result { + crate::into_ref!(tx); + crate::into_ref!(rx); + tx.set_to_push_pull_output(Internal); + tx.connect_peripheral_to_output(T::tx_signal(), Internal); + + rx.set_to_input(Internal); + rx.connect_input_to_peripheral(T::rx_signal(), Internal); + Self::new_with_config_inner(uart, config, clocks) + } + + /// Create a new UART instance with defaults in [`Blocking`] mode. + pub fn new( + uart: impl Peripheral

+ 'd, + clocks: &Clocks, + tx: impl Peripheral

+ 'd, + rx: impl Peripheral

+ 'd, + ) -> Result { + crate::into_ref!(tx); + crate::into_ref!(rx); + tx.set_to_push_pull_output(Internal); + tx.connect_peripheral_to_output(T::tx_signal(), Internal); + + rx.set_to_input(Internal); + rx.connect_input_to_peripheral(T::rx_signal(), Internal); + Self::new_inner(uart, clocks) + } + + /// Create a new UART instance with defaults in [`Blocking`] mode. + /// Verify that the default pins (DefaultTxPin and DefaultRxPin) are used. + pub fn new_with_default_pins( + uart: impl Peripheral

+ 'd, + clocks: &Clocks, + tx: &mut DefaultTxPin, + rx: &mut DefaultRxPin, + ) -> Result { + tx.set_to_push_pull_output(Internal); + tx.connect_peripheral_to_output(T::tx_signal(), Internal); + + rx.set_to_input(Internal); + rx.connect_input_to_peripheral(T::rx_signal(), Internal); + Self::new_inner(uart, clocks) + } +} + +impl<'d, T, M> Uart<'d, T, M> +where + T: Instance + 'd, + M: Mode, +{ + pub(crate) fn new_with_config_inner( + _uart: impl Peripheral

+ 'd, + config: Config, + clocks: &Clocks, + ) -> Result { + Self::init(); + + let mut serial = Uart { + tx: UartTx::new_inner(), + rx: UartRx::new_inner( + #[cfg(not(esp32))] + config.symbol_length(), + ), + }; + + serial + .rx + .set_rx_fifo_full_threshold(config.rx_fifo_full_threshold)?; + serial.rx.set_rx_timeout(config.rx_timeout)?; + serial.change_baud_internal(config.baudrate, config.clock_source, clocks); + serial.change_data_bits(config.data_bits); + serial.change_parity(config.parity); + serial.change_stop_bits(config.stop_bits); + + // Setting err_wr_mask stops uart from storing data when data is wrong according + // to reference manual + T::register_block() + .conf0() + .modify(|_, w| w.err_wr_mask().set_bit()); + + // Reset Tx/Rx FIFOs + serial.rxfifo_reset(); + serial.txfifo_reset(); + crate::rom::ets_delay_us(15); + + // Make sure we are starting in a "clean state" - previous operations might have + // run into error conditions + T::register_block() + .int_clr() + .write(|w| unsafe { w.bits(u32::MAX) }); + + Ok(serial) + } + + fn inner_set_interrupt_handler(&mut self, handler: InterruptHandler) { + unsafe { + crate::interrupt::bind_interrupt(T::interrupt(), handler.handler()); + crate::interrupt::enable(T::interrupt(), handler.priority()).unwrap(); + } + } + + fn new_inner(uart: impl Peripheral

+ 'd, clocks: &Clocks) -> Result { + Self::new_with_config_inner(uart, Default::default(), clocks) + } + + /// Configure CTS pin + pub fn with_cts(self, cts: impl Peripheral

+ 'd) -> Self { + crate::into_ref!(cts); + cts.set_to_input(Internal); + cts.connect_input_to_peripheral(T::cts_signal(), Internal); + + self + } + + /// Configure RTS pin + pub fn with_rts(self, rts: impl Peripheral

+ 'd) -> Self { + crate::into_ref!(rts); + rts.set_to_push_pull_output(Internal); + rts.connect_peripheral_to_output(T::rts_signal(), Internal); + + self + } + + /// Split the UART into a transmitter and receiver + /// + /// This is particularly useful when having two tasks correlating to + /// transmitting and receiving. + pub fn split(self) -> (UartTx<'d, T, M>, UartRx<'d, T, M>) { + (self.tx, self.rx) + } + + /// Write bytes out over the UART + pub fn write_bytes(&mut self, data: &[u8]) -> Result { + self.tx.write_bytes(data) + } + + /// Fill a buffer with received bytes + pub fn read_bytes(&mut self, buf: &mut [u8]) -> Result<(), Error> { + self.rx.read_bytes(buf) + } + + /// Configures the AT-CMD detection settings + #[allow(clippy::useless_conversion)] + pub fn set_at_cmd(&mut self, config: config::AtCmdConfig) { + #[cfg(not(any(esp32, esp32s2)))] + T::register_block() + .clk_conf() + .modify(|_, w| w.sclk_en().clear_bit()); + + T::register_block().at_cmd_char().write(|w| unsafe { + w.at_cmd_char() + .bits(config.cmd_char) + .char_num() + .bits(config.char_num.unwrap_or(1)) + }); + + if let Some(pre_idle_count) = config.pre_idle_count { + T::register_block() + .at_cmd_precnt() + .write(|w| unsafe { w.pre_idle_num().bits(pre_idle_count.into()) }); + } + + if let Some(post_idle_count) = config.post_idle_count { + T::register_block() + .at_cmd_postcnt() + .write(|w| unsafe { w.post_idle_num().bits(post_idle_count.into()) }); + } + + if let Some(gap_timeout) = config.gap_timeout { + T::register_block() + .at_cmd_gaptout() + .write(|w| unsafe { w.rx_gap_tout().bits(gap_timeout.into()) }); + } + + #[cfg(not(any(esp32, esp32s2)))] + T::register_block() + .clk_conf() + .modify(|_, w| w.sclk_en().set_bit()); + + Self::sync_regs(); + self.rx.at_cmd_config = Some(config); + } + + /// Listen for AT-CMD interrupts + pub fn listen_at_cmd(&mut self) { + T::register_block() + .int_ena() + .modify(|_, w| w.at_cmd_char_det().set_bit()); + } + + /// Stop listening for AT-CMD interrupts + pub fn unlisten_at_cmd(&mut self) { + T::register_block() + .int_ena() + .modify(|_, w| w.at_cmd_char_det().clear_bit()); + } + + /// Listen for TX-DONE interrupts + pub fn listen_tx_done(&mut self) { + T::register_block() + .int_ena() + .modify(|_, w| w.tx_done().set_bit()); + } + + /// Stop listening for TX-DONE interrupts + pub fn unlisten_tx_done(&mut self) { + T::register_block() + .int_ena() + .modify(|_, w| w.tx_done().clear_bit()); + } + + /// Listen for RX-FIFO-FULL interrupts + pub fn listen_rx_fifo_full(&mut self) { + T::register_block() + .int_ena() + .modify(|_, w| w.rxfifo_full().set_bit()); + } + + /// Stop listening for RX-FIFO-FULL interrupts + pub fn unlisten_rx_fifo_full(&mut self) { + T::register_block() + .int_ena() + .modify(|_, w| w.rxfifo_full().clear_bit()); + } + + /// Checks if AT-CMD interrupt is set + pub fn at_cmd_interrupt_set(&self) -> bool { + T::register_block() + .int_raw() + .read() + .at_cmd_char_det() + .bit_is_set() + } + + /// Checks if TX-DONE interrupt is set + pub fn tx_done_interrupt_set(&self) -> bool { + T::register_block().int_raw().read().tx_done().bit_is_set() + } + + /// Checks if RX-FIFO-FULL interrupt is set + pub fn rx_fifo_full_interrupt_set(&self) -> bool { + T::register_block() + .int_raw() + .read() + .rxfifo_full() + .bit_is_set() + } + + /// Reset AT-CMD interrupt + pub fn reset_at_cmd_interrupt(&self) { + T::register_block() + .int_clr() + .write(|w| w.at_cmd_char_det().clear_bit_by_one()); + } + + /// Reset TX-DONE interrupt + pub fn reset_tx_done_interrupt(&self) { + T::register_block() + .int_clr() + .write(|w| w.tx_done().clear_bit_by_one()); + } + + /// Reset RX-FIFO-FULL interrupt + pub fn reset_rx_fifo_full_interrupt(&self) { + T::register_block() + .int_clr() + .write(|w| w.rxfifo_full().clear_bit_by_one()); + } + + /// Write a byte out over the UART + pub fn write_byte(&mut self, word: u8) -> nb::Result<(), Error> { + self.tx.write_byte(word) + } + + /// Flush the transmit buffer of the UART + pub fn flush_tx(&mut self) -> nb::Result<(), Error> { + self.tx.flush_tx() + } + + /// Read a byte from the UART + pub fn read_byte(&mut self) -> nb::Result { + self.rx.read_byte() + } + + /// Change the number of stop bits + pub fn change_stop_bits(&mut self, stop_bits: config::StopBits) -> &mut Self { + // workaround for hardware issue, when UART stop bit set as 2-bit mode. + #[cfg(esp32)] + if stop_bits == config::StopBits::STOP2 { + T::register_block() + .rs485_conf() + .modify(|_, w| w.dl1_en().bit(true)); + + T::register_block() + .conf0() + .modify(|_, w| unsafe { w.stop_bit_num().bits(1) }); + } else { + T::register_block() + .rs485_conf() + .modify(|_, w| w.dl1_en().bit(false)); + + T::register_block() + .conf0() + .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8) }); + } + + #[cfg(not(esp32))] + T::register_block() + .conf0() + .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8) }); + + self + } + + fn change_data_bits(&mut self, data_bits: config::DataBits) -> &mut Self { + T::register_block() + .conf0() + .modify(|_, w| unsafe { w.bit_num().bits(data_bits as u8) }); + + self + } + + fn change_parity(&mut self, parity: config::Parity) -> &mut Self { + T::register_block().conf0().modify(|_, w| match parity { + config::Parity::ParityNone => w.parity_en().clear_bit(), + config::Parity::ParityEven => w.parity_en().set_bit().parity().clear_bit(), + config::Parity::ParityOdd => w.parity_en().set_bit().parity().set_bit(), + }); + + self + } + + #[cfg(any(esp32c2, esp32c3, esp32s3))] + fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks) { + let clk = match clock_source { + ClockSource::Apb => clocks.apb_clock.to_Hz(), + ClockSource::Xtal => clocks.xtal_clock.to_Hz(), + ClockSource::RcFast => RC_FAST_CLK.to_Hz(), + }; + + let max_div = 0b1111_1111_1111 - 1; + let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate); + T::register_block().clk_conf().write(|w| unsafe { + w.sclk_sel() + .bits(match clock_source { + ClockSource::Apb => 1, + ClockSource::RcFast => 2, + ClockSource::Xtal => 3, + }) + .sclk_div_a() + .bits(0) + .sclk_div_b() + .bits(0) + .sclk_div_num() + .bits(clk_div as u8 - 1) + .rx_sclk_en() + .bit(true) + .tx_sclk_en() + .bit(true) + }); + + let divider = (clk << 4) / (baudrate * clk_div); + let divider_integer = (divider >> 4) as u16; + let divider_frag = (divider & 0xf) as u8; + T::register_block() + .clkdiv() + .write(|w| unsafe { w.clkdiv().bits(divider_integer).frag().bits(divider_frag) }); + } + + #[cfg(any(esp32c6, esp32h2))] + fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks) { + let clk = match clock_source { + ClockSource::Apb => clocks.apb_clock.to_Hz(), + ClockSource::Xtal => clocks.xtal_clock.to_Hz(), + ClockSource::RcFast => RC_FAST_CLK.to_Hz(), + }; + + let max_div = 0b1111_1111_1111 - 1; + let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate); + + // UART clocks are configured via PCR + let pcr = unsafe { &*crate::peripherals::PCR::PTR }; + + match T::uart_number() { + 0 => { + pcr.uart0_conf() + .modify(|_, w| w.uart0_rst_en().clear_bit().uart0_clk_en().set_bit()); + + pcr.uart0_sclk_conf().modify(|_, w| unsafe { + w.uart0_sclk_div_a() + .bits(0) + .uart0_sclk_div_b() + .bits(0) + .uart0_sclk_div_num() + .bits(clk_div as u8 - 1) + .uart0_sclk_sel() + .bits(match clock_source { + ClockSource::Apb => 1, + ClockSource::RcFast => 2, + ClockSource::Xtal => 3, + }) + .uart0_sclk_en() + .set_bit() + }); + } + 1 => { + pcr.uart1_conf() + .modify(|_, w| w.uart1_rst_en().clear_bit().uart1_clk_en().set_bit()); + + pcr.uart1_sclk_conf().modify(|_, w| unsafe { + w.uart1_sclk_div_a() + .bits(0) + .uart1_sclk_div_b() + .bits(0) + .uart1_sclk_div_num() + .bits(clk_div as u8 - 1) + .uart1_sclk_sel() + .bits(match clock_source { + ClockSource::Apb => 1, + ClockSource::RcFast => 2, + ClockSource::Xtal => 3, + }) + .uart1_sclk_en() + .set_bit() + }); + } + _ => unreachable!(), // ESP32-C6 only has 2 UART instances + } + + let clk = clk / clk_div; + let divider = clk / baudrate; + let divider = divider as u16; + + T::register_block() + .clkdiv() + .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); + + Self::sync_regs(); + } + + #[cfg(any(esp32, esp32s2))] + fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks) { + let clk = match clock_source { + ClockSource::Apb => clocks.apb_clock.to_Hz(), + ClockSource::RefTick => REF_TICK.to_Hz(), /* ESP32(/-S2) TRM, section 3.2.4.2 + * (6.2.4.2 for S2) */ + }; + + T::register_block().conf0().modify(|_, w| { + w.tick_ref_always_on().bit(match clock_source { + ClockSource::Apb => true, + ClockSource::RefTick => false, + }) + }); + + let divider = clk / baudrate; + + T::register_block() + .clkdiv() + .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); + } + + #[cfg(any(esp32c2, esp32c3, esp32s3))] + #[inline(always)] + fn init() { + let system = unsafe { crate::peripherals::SYSTEM::steal() }; + if !system.perip_clk_en0().read().uart_mem_clk_en().bit() { + system + .perip_clk_en0() + .modify(|_, w| w.uart_mem_clk_en().set_bit()); + } + + // initialize peripheral by setting clk_enable and clearing uart_reset bits + T::enable_peripheral(); + Self::uart_peripheral_reset(); + T::disable_rx_interrupts(); + T::disable_tx_interrupts(); + } + + /// Modify UART baud rate and reset TX/RX fifo. + pub fn change_baud(&mut self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks) { + self.change_baud_internal(baudrate, clock_source, clocks); + self.txfifo_reset(); + self.rxfifo_reset(); + } + + #[cfg(any(esp32c6, esp32h2))] + #[inline(always)] + fn init() { + T::register_block() + .conf0() + .modify(|_, w| w.mem_clk_en().set_bit()); + + // initialize peripheral by setting clk_enable and clearing uart_reset bits + T::enable_peripheral(); + Self::uart_peripheral_reset(); + T::disable_rx_interrupts(); + T::disable_tx_interrupts(); + } + + #[cfg(any(esp32, esp32s2))] + #[inline(always)] + fn init() { + T::enable_peripheral(); + Self::uart_peripheral_reset(); + T::disable_rx_interrupts(); + T::disable_tx_interrupts(); + } + + #[inline(always)] + fn uart_peripheral_reset() { + // don't reset the console UART - this will cause trouble (i.e. the UART will + // start to transmit garbage) + // + // We should only reset the console UART if it was absolutely unused before. + // Apparently the bootloader (and maybe the ROM code) writing to the UART is + // already enough to make this a no-go. (i.e. one needs to mute the ROM + // code via efuse / strapping pin AND use a silent bootloader) + // + // Ideally this should be configurable once we have a solution for https://github.com/esp-rs/esp-hal/issues/1111 + // see https://github.com/espressif/esp-idf/blob/5f4249357372f209fdd57288265741aaba21a2b1/components/esp_driver_uart/src/uart.c#L179 + if T::uart_number() != CONSOLE_UART_NUM { + #[cfg(not(any(esp32, esp32s2)))] + T::register_block() + .clk_conf() + .modify(|_, w| w.rst_core().set_bit()); + + // reset peripheral + T::reset_peripheral(); + + #[cfg(not(any(esp32, esp32s2)))] + T::register_block() + .clk_conf() + .modify(|_, w| w.rst_core().clear_bit()); + } + } + + #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] // TODO introduce a cfg symbol for this + #[inline(always)] + fn sync_regs() { + #[cfg(any(esp32c6, esp32h2))] + let update_reg = T::register_block().reg_update(); + + #[cfg(any(esp32c3, esp32s3))] + let update_reg = T::register_block().id(); + + update_reg.modify(|_, w| w.reg_update().set_bit()); + + while update_reg.read().reg_update().bit_is_set() { + // wait + } + } + + #[cfg(not(any(esp32c3, esp32c6, esp32h2, esp32s3)))] + #[inline(always)] + fn sync_regs() {} + + fn rxfifo_reset(&mut self) { + T::register_block() + .conf0() + .modify(|_, w| w.rxfifo_rst().set_bit()); + Self::sync_regs(); + + T::register_block() + .conf0() + .modify(|_, w| w.rxfifo_rst().clear_bit()); + Self::sync_regs(); + } + + fn txfifo_reset(&mut self) { + T::register_block() + .conf0() + .modify(|_, w| w.txfifo_rst().set_bit()); + Self::sync_regs(); + + T::register_block() + .conf0() + .modify(|_, w| w.txfifo_rst().clear_bit()); + Self::sync_regs(); + } +} + +impl<'d, T> crate::private::Sealed for Uart<'d, T, Blocking> where T: Instance + 'd {} + +impl<'d, T> InterruptConfigurable for Uart<'d, T, Blocking> +where + T: Instance + 'd, +{ + fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { + self.inner_set_interrupt_handler(handler); + } +} + +/// UART Peripheral Instance +pub trait Instance: crate::private::Sealed { + fn register_block() -> &'static RegisterBlock; + fn uart_number() -> usize; + fn interrupt() -> Interrupt; + + fn disable_tx_interrupts() { + Self::register_block().int_clr().write(|w| { + w.txfifo_empty() + .clear_bit_by_one() + .tx_brk_done() + .clear_bit_by_one() + .tx_brk_idle_done() + .clear_bit_by_one() + .tx_done() + .clear_bit_by_one() + }); + + Self::register_block().int_ena().write(|w| { + w.txfifo_empty() + .clear_bit() + .tx_brk_done() + .clear_bit() + .tx_brk_idle_done() + .clear_bit() + .tx_done() + .clear_bit() + }); + } + + fn disable_rx_interrupts() { + Self::register_block().int_clr().write(|w| { + w.rxfifo_full() + .clear_bit_by_one() + .rxfifo_ovf() + .clear_bit_by_one() + .rxfifo_tout() + .clear_bit_by_one() + .at_cmd_char_det() + .clear_bit_by_one() + }); + + Self::register_block().int_ena().write(|w| { + w.rxfifo_full() + .clear_bit() + .rxfifo_ovf() + .clear_bit() + .rxfifo_tout() + .clear_bit() + .at_cmd_char_det() + .clear_bit() + }); + } + + #[allow(clippy::useless_conversion)] + fn get_tx_fifo_count() -> u16 { + Self::register_block() + .status() + .read() + .txfifo_cnt() + .bits() + .into() + } + + #[allow(clippy::useless_conversion)] + fn get_rx_fifo_count() -> u16 { + let fifo_cnt: u16 = Self::register_block() + .status() + .read() + .rxfifo_cnt() + .bits() + .into(); + + // Calculate the real count based on the FIFO read and write offset address: + // https://www.espressif.com/sites/default/files/documentation/esp32_errata_en.pdf + // section 3.17 + #[cfg(esp32)] + { + let rd_addr = Self::register_block() + .mem_rx_status() + .read() + .mem_rx_rd_addr() + .bits(); + let wr_addr = Self::register_block() + .mem_rx_status() + .read() + .mem_rx_wr_addr() + .bits(); + + if wr_addr > rd_addr { + wr_addr - rd_addr + } else if wr_addr < rd_addr { + (wr_addr + UART_FIFO_SIZE) - rd_addr + } else if fifo_cnt > 0 { + UART_FIFO_SIZE + } else { + 0 + } + } + + #[cfg(not(esp32))] + fifo_cnt + } + + fn is_tx_idle() -> bool { + #[cfg(esp32)] + let idle = Self::register_block().status().read().st_utx_out().bits() == 0x0u8; + #[cfg(not(esp32))] + let idle = Self::register_block() + .fsm_status() + .read() + .st_utx_out() + .bits() + == 0x0u8; + + idle + } + + fn is_rx_idle() -> bool { + #[cfg(esp32)] + let idle = Self::register_block().status().read().st_urx_out().bits() == 0x0u8; + #[cfg(not(esp32))] + let idle = Self::register_block() + .fsm_status() + .read() + .st_urx_out() + .bits() + == 0x0u8; + + idle + } + + fn tx_signal() -> OutputSignal; + fn rx_signal() -> InputSignal; + fn cts_signal() -> InputSignal; + fn rts_signal() -> OutputSignal; + fn enable_peripheral(); + fn reset_peripheral(); +} + +macro_rules! impl_instance { + ($inst:ident, $num:expr, $txd:ident, $rxd:ident, $cts:ident, $rts:ident, $peri:ident) => { + impl Instance for crate::peripherals::$inst { + #[inline(always)] + fn register_block() -> &'static RegisterBlock { + unsafe { &*crate::peripherals::$inst::PTR } + } + + #[inline(always)] + fn uart_number() -> usize { + $num + } + + #[inline(always)] + fn interrupt() -> Interrupt { + Interrupt::$inst + } + + fn tx_signal() -> OutputSignal { + OutputSignal::$txd + } + + fn rx_signal() -> InputSignal { + InputSignal::$rxd + } + + fn cts_signal() -> InputSignal { + InputSignal::$cts + } + + fn rts_signal() -> OutputSignal { + OutputSignal::$rts + } + + fn enable_peripheral() { + PeripheralClockControl::enable(crate::system::Peripheral::$peri); + } + + fn reset_peripheral() { + PeripheralClockControl::reset(crate::system::Peripheral::$peri); + } + } + }; +} + +impl_instance!(UART0, 0, U0TXD, U0RXD, U0CTS, U0RTS, Uart0); +impl_instance!(UART1, 1, U1TXD, U1RXD, U1CTS, U1RTS, Uart1); +#[cfg(uart2)] +impl_instance!(UART2, 2, U2TXD, U2RXD, U2CTS, U2RTS, Uart2); + +#[cfg(feature = "ufmt")] +impl ufmt_write::uWrite for Uart<'_, T, M> +where + T: Instance, + M: Mode, +{ + type Error = Error; + + #[inline] + fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { + self.tx.write_str(s) + } + + #[inline] + fn write_char(&mut self, ch: char) -> Result<(), Self::Error> { + self.tx.write_char(ch) + } +} + +#[cfg(feature = "ufmt")] +impl ufmt_write::uWrite for UartTx<'_, T, M> +where + T: Instance, + M: Mode, +{ + type Error = Error; + + #[inline] + fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { + self.write_bytes(s.as_bytes())?; + Ok(()) + } +} + +impl core::fmt::Write for Uart<'_, T, M> +where + T: Instance, + M: Mode, +{ + #[inline] + fn write_str(&mut self, s: &str) -> core::fmt::Result { + self.tx.write_str(s) + } +} + +impl core::fmt::Write for UartTx<'_, T, M> +where + T: Instance, + M: Mode, +{ + #[inline] + fn write_str(&mut self, s: &str) -> core::fmt::Result { + self.write_bytes(s.as_bytes()) + .map_err(|_| core::fmt::Error)?; + Ok(()) + } +} + +#[cfg(feature = "embedded-hal-02")] +impl embedded_hal_02::serial::Write for Uart<'_, T, M> +where + T: Instance, + M: Mode, +{ + type Error = Error; + + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + self.tx.write(word) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.tx.flush() + } +} + +#[cfg(feature = "embedded-hal-02")] +impl embedded_hal_02::serial::Write for UartTx<'_, T, M> +where + T: Instance, + M: Mode, +{ + type Error = Error; + + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + self.write_byte(word) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.flush_tx() + } +} + +#[cfg(feature = "embedded-hal-02")] +impl embedded_hal_02::serial::Read for Uart<'_, T, M> +where + T: Instance, + M: Mode, +{ + type Error = Error; + + fn read(&mut self) -> nb::Result { + self.rx.read() + } +} + +#[cfg(feature = "embedded-hal-02")] +impl embedded_hal_02::serial::Read for UartRx<'_, T, M> +where + T: Instance, + M: Mode, +{ + type Error = Error; + + fn read(&mut self) -> nb::Result { + self.read_byte() + } +} + +#[cfg(feature = "embedded-hal")] +impl embedded_hal_nb::serial::ErrorType for Uart<'_, T, M> { + type Error = Error; +} + +#[cfg(feature = "embedded-hal")] +impl embedded_hal_nb::serial::ErrorType for UartTx<'_, T, M> { + type Error = Error; +} + +#[cfg(feature = "embedded-hal")] +impl embedded_hal_nb::serial::ErrorType for UartRx<'_, T, M> { + type Error = Error; +} + +#[cfg(feature = "embedded-hal")] +impl embedded_hal_nb::serial::Read for Uart<'_, T, M> +where + T: Instance, + M: Mode, +{ + fn read(&mut self) -> nb::Result { + self.read_byte() + } +} + +#[cfg(feature = "embedded-hal")] +impl embedded_hal_nb::serial::Read for UartRx<'_, T, M> +where + T: Instance, + M: Mode, +{ + fn read(&mut self) -> nb::Result { + self.read_byte() + } +} + +#[cfg(feature = "embedded-hal")] +impl embedded_hal_nb::serial::Write for Uart<'_, T, M> +where + T: Instance, + M: Mode, +{ + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + self.write_byte(word) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.flush_tx() + } +} + +#[cfg(feature = "embedded-hal")] +impl embedded_hal_nb::serial::Write for UartTx<'_, T, M> +where + T: Instance, + M: Mode, +{ + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + self.write_byte(word) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.flush_tx() + } +} + +#[cfg(feature = "embedded-io")] +impl embedded_io::ErrorType for Uart<'_, T, M> { + type Error = Error; +} + +#[cfg(feature = "embedded-io")] +impl embedded_io::ErrorType for UartTx<'_, T, M> { + type Error = Error; +} + +#[cfg(feature = "embedded-io")] +impl embedded_io::ErrorType for UartRx<'_, T, M> { + type Error = Error; +} + +#[cfg(feature = "embedded-io")] +impl embedded_io::Read for Uart<'_, T, M> +where + T: Instance, + M: Mode, +{ + fn read(&mut self, buf: &mut [u8]) -> Result { + self.rx.read(buf) + } +} + +#[cfg(feature = "embedded-io")] +impl embedded_io::Read for UartRx<'_, T, M> +where + T: Instance, + M: Mode, +{ + fn read(&mut self, buf: &mut [u8]) -> Result { + if buf.is_empty() { + return Ok(0); + } + + while T::get_rx_fifo_count() == 0 { + // Block until we received at least one byte + } + + Ok(self.drain_fifo(buf)) + } +} + +#[cfg(feature = "embedded-io")] +impl embedded_io::ReadReady for Uart<'_, T, M> +where + T: Instance, + M: Mode, +{ + fn read_ready(&mut self) -> Result { + self.rx.read_ready() + } +} + +#[cfg(feature = "embedded-io")] +impl embedded_io::ReadReady for UartRx<'_, T, M> +where + T: Instance, + M: Mode, +{ + fn read_ready(&mut self) -> Result { + Ok(T::get_rx_fifo_count() > 0) + } +} + +#[cfg(feature = "embedded-io")] +impl embedded_io::Write for Uart<'_, T, M> +where + T: Instance, + M: Mode, +{ + fn write(&mut self, buf: &[u8]) -> Result { + self.tx.write(buf) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.tx.flush() + } +} + +#[cfg(feature = "embedded-io")] +impl embedded_io::Write for UartTx<'_, T, M> +where + T: Instance, + M: Mode, +{ + fn write(&mut self, buf: &[u8]) -> Result { + self.write_bytes(buf) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + loop { + match self.flush_tx() { + Ok(_) => break, + Err(nb::Error::WouldBlock) => { /* Wait */ } + Err(nb::Error::Other(e)) => return Err(e), + } + } + + Ok(()) + } +} + +#[cfg(feature = "async")] +mod asynch { + use core::task::Poll; + + use cfg_if::cfg_if; + use embassy_sync::waitqueue::AtomicWaker; + use enumset::{EnumSet, EnumSetType}; + use procmacros::handler; + + use super::*; + use crate::Async; + + cfg_if! { + if #[cfg(all(uart0, uart1, uart2))] { + const NUM_UART: usize = 3; + } else if #[cfg(all(uart0, uart1))] { + const NUM_UART: usize = 2; + } else if #[cfg(uart0)] { + const NUM_UART: usize = 1; + } + } + + const INIT: AtomicWaker = AtomicWaker::new(); + static TX_WAKERS: [AtomicWaker; NUM_UART] = [INIT; NUM_UART]; + static RX_WAKERS: [AtomicWaker; NUM_UART] = [INIT; NUM_UART]; + + #[derive(EnumSetType, Debug)] + pub(crate) enum TxEvent { + TxDone, + TxFiFoEmpty, + } + #[derive(EnumSetType, Debug)] + pub(crate) enum RxEvent { + RxFifoFull, + RxCmdCharDetected, + RxFifoOvf, + RxFifoTout, + RxGlitchDetected, + RxFrameError, + RxParityError, + } + + /// A future that resolves when the passed interrupt is triggered, + /// or has been triggered in the meantime (flag set in INT_RAW). + /// Upon construction the future enables the passed interrupt and when it + /// is dropped it disables the interrupt again. The future returns the event + /// that was initially passed, when it resolves. + pub(crate) struct UartRxFuture<'d, T: Instance> { + events: EnumSet, + phantom: PhantomData<&'d mut T>, + registered: bool, + internal_buf: [u8; 1024], // Buffer as part of the struct + } + pub(crate) struct UartTxFuture<'d, T: Instance> { + events: EnumSet, + phantom: PhantomData<&'d mut T>, + registered: bool, + // internal_buf: [u8; 1024], // Buffer as part of the struct + } + + impl<'d, T: Instance> UartRxFuture<'d, T> { + pub fn new(events: EnumSet) -> Self { + Self { + events, + phantom: PhantomData, + registered: false, + internal_buf: [0; 1024], // Initialize buffer + } + } + + fn get_triggered_events(&self) -> EnumSet { + let interrupts_enabled = T::register_block().int_ena().read(); + let mut events_triggered = EnumSet::new(); + for event in self.events { + let event_triggered = match event { + RxEvent::RxFifoFull => interrupts_enabled.rxfifo_full().bit_is_clear(), + RxEvent::RxCmdCharDetected => { + interrupts_enabled.at_cmd_char_det().bit_is_clear() + } + + RxEvent::RxFifoOvf => interrupts_enabled.rxfifo_ovf().bit_is_clear(), + RxEvent::RxFifoTout => interrupts_enabled.rxfifo_tout().bit_is_clear(), + RxEvent::RxGlitchDetected => interrupts_enabled.glitch_det().bit_is_clear(), + RxEvent::RxFrameError => interrupts_enabled.frm_err().bit_is_clear(), + RxEvent::RxParityError => interrupts_enabled.parity_err().bit_is_clear(), + }; + if event_triggered { + events_triggered |= event; + } + } + events_triggered + } + } + + impl<'d, T: Instance> core::future::Future for UartRxFuture<'d, T> { + type Output = EnumSet; + + fn poll( + mut self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + if !self.registered { + RX_WAKERS[T::uart_number()].register(cx.waker()); + T::register_block().int_ena().modify(|_, w| { + for event in self.events { + match event { + RxEvent::RxFifoFull => w.rxfifo_full().set_bit(), + RxEvent::RxCmdCharDetected => w.at_cmd_char_det().set_bit(), + RxEvent::RxFifoOvf => w.rxfifo_ovf().set_bit(), + RxEvent::RxFifoTout => w.rxfifo_tout().set_bit(), + RxEvent::RxGlitchDetected => w.glitch_det().set_bit(), + RxEvent::RxFrameError => w.frm_err().set_bit(), + RxEvent::RxParityError => w.parity_err().set_bit(), + }; + } + w + }); + self.registered = true; + } + let events = self.get_triggered_events(); + if !events.is_empty() { + Poll::Ready(events) + } else { + Poll::Pending + } + } + } + + impl<'d, T: Instance> Drop for UartRxFuture<'d, T> { + fn drop(&mut self) { + // Although the isr disables the interrupt that occurred directly, we need to + // disable the other interrupts (= the ones that did not occur), as + // soon as this future goes out of scope. + let int_ena = &T::register_block().int_ena(); + for event in self.events { + match event { + RxEvent::RxFifoFull => int_ena.modify(|_, w| w.rxfifo_full().clear_bit()), + RxEvent::RxCmdCharDetected => { + int_ena.modify(|_, w| w.at_cmd_char_det().clear_bit()) + } + RxEvent::RxGlitchDetected => int_ena.modify(|_, w| w.glitch_det().clear_bit()), + RxEvent::RxFrameError => int_ena.modify(|_, w| w.frm_err().clear_bit()), + RxEvent::RxParityError => int_ena.modify(|_, w| w.parity_err().clear_bit()), + RxEvent::RxFifoOvf => int_ena.modify(|_, w| w.rxfifo_ovf().clear_bit()), + RxEvent::RxFifoTout => int_ena.modify(|_, w| w.rxfifo_tout().clear_bit()), + } + } + } + } + + impl<'d, T: Instance> UartTxFuture<'d, T> { + pub fn new(events: EnumSet) -> Self { + Self { + events, + phantom: PhantomData, + registered: false, + // internal_buf: [0; 1024], // Initialize buffer + } + } + + fn get_triggered_events(&self) -> bool { + let interrupts_enabled = T::register_block().int_ena().read(); + let mut event_triggered = false; + for event in self.events { + event_triggered |= match event { + TxEvent::TxDone => interrupts_enabled.tx_done().bit_is_clear(), + TxEvent::TxFiFoEmpty => interrupts_enabled.txfifo_empty().bit_is_clear(), + } + } + event_triggered + } + } + + impl<'d, T: Instance> core::future::Future for UartTxFuture<'d, T> { + type Output = (); + + fn poll( + mut self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + if !self.registered { + TX_WAKERS[T::uart_number()].register(cx.waker()); + T::register_block().int_ena().modify(|_, w| { + for event in self.events { + match event { + TxEvent::TxDone => w.tx_done().set_bit(), + TxEvent::TxFiFoEmpty => w.txfifo_empty().set_bit(), + }; + } + w + }); + self.registered = true; + } + + if self.get_triggered_events() { + Poll::Ready(()) + } else { + Poll::Pending + } + } + } + + impl<'d, T: Instance> Drop for UartTxFuture<'d, T> { + fn drop(&mut self) { + // Although the isr disables the interrupt that occurred directly, we need to + // disable the other interrupts (= the ones that did not occur), as + // soon as this future goes out of scope. + let int_ena = &T::register_block().int_ena(); + for event in self.events { + match event { + TxEvent::TxDone => int_ena.modify(|_, w| w.tx_done().clear_bit()), + TxEvent::TxFiFoEmpty => int_ena.modify(|_, w| w.txfifo_empty().clear_bit()), + } + } + } + } + + impl<'d, T> Uart<'d, T, Async> + where + T: Instance + 'd, + { + /// Create a new UART instance with configuration options in [`Async`] + /// mode. + pub fn new_async_with_config( + uart: impl Peripheral

+ 'd, + config: Config, + clocks: &Clocks, + tx: impl Peripheral

+ 'd, + rx: impl Peripheral

+ 'd, + ) -> Result { + crate::into_ref!(tx); + crate::into_ref!(rx); + tx.set_to_push_pull_output(Internal); + tx.connect_peripheral_to_output(T::tx_signal(), Internal); + + rx.set_to_input(Internal); + rx.connect_input_to_peripheral(T::rx_signal(), Internal); + let mut this = Self::new_with_config_inner(uart, config, clocks)?; + + this.inner_set_interrupt_handler(match T::uart_number() { + #[cfg(uart0)] + 0 => uart0, + #[cfg(uart1)] + 1 => uart1, + #[cfg(uart2)] + 2 => uart2, + _ => unreachable!(), + }); + + Ok(this) + } + + /// Create a new UART instance with defaults in [`Async`] mode. + pub fn new_async( + uart: impl Peripheral

+ 'd, + clocks: &Clocks, + tx: impl Peripheral

+ 'd, + rx: impl Peripheral

+ 'd, + ) -> Result { + Self::new_async_with_config(uart, Default::default(), clocks, tx, rx) + } + + /// Create a new UART instance with defaults in [`Async`] mode. + pub fn new_async_with_default_pins( + uart: impl Peripheral

+ 'd, + clocks: &Clocks, + tx: DefaultTxPin, + rx: DefaultRxPin, + ) -> Result { + Self::new_async_with_config(uart, Default::default(), clocks, tx, rx) + } + } + + impl Uart<'_, T, Async> + where + T: Instance, + { + /// See [`UartRx::read_async`] + pub async fn read_async(&mut self, buf: &mut [u8]) -> Result { + self.rx.read_async(buf).await + } + // pub async fn fill_buf(&mut self, buf: &mut [u8]) -> Result { + // self.rx.fill_buf_async().await + // } + + pub async fn write_async(&mut self, words: &[u8]) -> Result { + self.tx.write_async(words).await + } + + pub async fn flush_async(&mut self) -> Result<(), Error> { + self.tx.flush_async().await + } + } + + impl<'d, T> UartTx<'d, T, Async> + where + T: Instance + 'd, + { + /// Create a new UART TX instance in [`Async`] mode. + pub fn new_async( + uart: impl Peripheral

+ 'd, + clocks: &Clocks, + tx: impl Peripheral

+ 'd, + ) -> Result { + Self::new_async_with_config(uart, Default::default(), clocks, tx) + } + + /// Create a new UART TX instance with configuration options in + /// [`Async`] mode. + pub fn new_async_with_config( + uart: impl Peripheral

+ 'd, + config: Config, + clocks: &Clocks, + tx: impl Peripheral

+ 'd, + ) -> Result { + crate::into_ref!(tx); + tx.set_to_push_pull_output(Internal); + tx.connect_peripheral_to_output(T::tx_signal(), Internal); + + let mut uart = Uart::<'d, T, Async>::new_with_config_inner(uart, config, clocks)?; + + uart.inner_set_interrupt_handler(match T::uart_number() { + #[cfg(uart0)] + 0 => uart0, + #[cfg(uart1)] + 1 => uart1, + #[cfg(uart2)] + 2 => uart2, + _ => unreachable!(), + }); + + let (uart_tx, _) = uart.split(); + Ok(uart_tx) + } + + pub async fn write_async(&mut self, words: &[u8]) -> Result { + let mut count = 0; + let mut offset: usize = 0; + loop { + let mut next_offset = offset + (UART_FIFO_SIZE - T::get_tx_fifo_count()) as usize; + if next_offset > words.len() { + next_offset = words.len(); + } + + for byte in &words[offset..next_offset] { + self.write_byte(*byte).unwrap(); // should never fail + count += 1; + } + + if next_offset >= words.len() { + break; + } + + offset = next_offset; + UartTxFuture::::new(TxEvent::TxFiFoEmpty.into()).await; + } + + Ok(count) + } + + pub async fn flush_async(&mut self) -> Result<(), Error> { + let count = T::get_tx_fifo_count(); + if count > 0 { + UartTxFuture::::new(TxEvent::TxDone.into()).await; + } + + Ok(()) + } + } + + impl<'d, T> UartRx<'d, T, Async> + where + T: Instance + 'd, + { + /// Create a new UART RX instance in [`Async`] mode. + pub fn new_async( + uart: impl Peripheral

+ 'd, + clocks: &Clocks, + rx: impl Peripheral

+ 'd, + ) -> Result { + Self::new_async_with_config(uart, Default::default(), clocks, rx) + } + + /// Create a new UART RX instance with configuration options in + /// [`Async`] mode. + pub fn new_async_with_config( + uart: impl Peripheral

+ 'd, + config: Config, + clocks: &Clocks, + rx: impl Peripheral

+ 'd, + ) -> Result { + crate::into_ref!(rx); + rx.set_to_input(Internal); + rx.connect_input_to_peripheral(T::rx_signal(), Internal); + + let mut uart = Uart::<'d, T, Async>::new_with_config_inner(uart, config, clocks)?; + + uart.inner_set_interrupt_handler(match T::uart_number() { + #[cfg(uart0)] + 0 => uart0, + #[cfg(uart1)] + 1 => uart1, + #[cfg(uart2)] + 2 => uart2, + _ => unreachable!(), + }); + + let (_, uart_rx) = uart.split(); + Ok(uart_rx) + } + + /// Read async to buffer slice `buf`. + /// Waits until at least one byte is in the Rx FiFo + /// and one of the following interrupts occurs: + /// - `RXFIFO_FULL` + /// - `RXFIFO_OVF` + /// - `AT_CMD_CHAR_DET` (only if `set_at_cmd` was called) + /// - `RXFIFO_TOUT` (only if `set_rx_timeout was called) + /// + /// The interrupts in question are enabled during the body of this + /// function. The method immediately returns when the interrupt + /// has already occurred before calling this method (e.g. status + /// bit set, but interrupt not enabled) + /// + /// # Params + /// - `buf` buffer slice to write the bytes into + /// + /// + /// # Ok + /// When successful, returns the number of bytes written to buf. + /// This method will never return Ok(0) + pub async fn read_async(&mut self, buf: &mut [u8]) -> Result { + if buf.len() == 0 { + return Err(Error::InvalidArgument); + } + + loop { + let mut events = RxEvent::RxFifoFull + | RxEvent::RxFifoOvf + | RxEvent::RxFrameError + | RxEvent::RxGlitchDetected + | RxEvent::RxParityError; + + if self.at_cmd_config.is_some() { + events |= RxEvent::RxCmdCharDetected; + } + + if self.rx_timeout_config.is_some() { + events |= RxEvent::RxFifoTout; + } + let events_happened = UartRxFuture::::new(events).await; + // always drain the fifo, if an error has occurred the data is lost + let read_bytes = self.drain_fifo(buf); + // check error events + for event_happened in events_happened { + match event_happened { + RxEvent::RxFifoOvf => return Err(Error::RxFifoOvf), + RxEvent::RxGlitchDetected => return Err(Error::RxGlitchDetected), + RxEvent::RxFrameError => return Err(Error::RxFrameError), + RxEvent::RxParityError => return Err(Error::RxParityError), + RxEvent::RxFifoFull | RxEvent::RxCmdCharDetected | RxEvent::RxFifoTout => { + continue + } + } + } + // Unfortunately, the uart's rx-timeout counter counts up whenever there is + // data in the fifo, even if the interrupt is disabled and the status bit + // cleared. Since we do not drain the fifo in the interrupt handler, we need to + // reset the counter here, after draining the fifo. + T::register_block() + .int_clr() + .write(|w| w.rxfifo_tout().clear_bit_by_one()); + + if read_bytes > 0 { + return Ok(read_bytes); + } + } + } + + pub async fn fill_buf_async(&mut self) -> Result<&[u8], Error> { + let mut total_read = 0; + let mut internal_buf = [0u8; 1024]; // Adjust size as necessary + + loop { + let mut events = RxEvent::RxFifoFull + | RxEvent::RxFifoOvf + | RxEvent::RxFrameError + | RxEvent::RxGlitchDetected + | RxEvent::RxParityError; + + if self.at_cmd_config.is_some() { + events |= RxEvent::RxCmdCharDetected; + } + + if self.rx_timeout_config.is_some() { + events |= RxEvent::RxFifoTout; + } + + let events_happened = UartRxFuture::::new(events).await; + + // Drain data into the temporary buffer + let read_bytes = self.drain_fifo(&mut internal_buf); + if read_bytes > 0 { + // Copy read data to self.internal_buf + self.internal_buf[..read_bytes].copy_from_slice(&internal_buf[..read_bytes]); + total_read += read_bytes; + + // Check for errors + for event_happened in events_happened { + match event_happened { + RxEvent::RxFifoOvf => return Err(Error::RxFifoOvf), + RxEvent::RxGlitchDetected => return Err(Error::RxGlitchDetected), + RxEvent::RxFrameError => return Err(Error::RxFrameError), + RxEvent::RxParityError => return Err(Error::RxParityError), + RxEvent::RxFifoFull | RxEvent::RxCmdCharDetected | RxEvent::RxFifoTout => { + continue + } + } + } + + // Reset RX timeout counter + T::register_block() + .int_clr() + .write(|w| w.rxfifo_tout().clear_bit_by_one()); + + return Ok(&self.internal_buf[..total_read]); + } + } + } + + pub fn consume_async(&self, amt: usize) { + // Implement buffer consumption logic + // Assuming the presence of an internal buffer management + // Mocked function to represent internal buffer handling + } + } + + impl embedded_io_async::Read for Uart<'_, T, Async> + where + T: Instance, + { + /// In contrast to the documentation of embedded_io_async::Read, this + /// method blocks until an uart interrupt occurs. + /// See UartRx::read_async for more details. + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read_async(buf).await + } + } + + impl embedded_io_async::Read for UartRx<'_, T, Async> + where + T: Instance, + { + /// In contrast to the documentation of embedded_io_async::Read, this + /// method blocks until an uart interrupt occurs. + /// See UartRx::read_async for more details. + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read_async(buf).await + } + } + impl embedded_io_async::BufRead for UartRx<'_, T, Async> + where + T: Instance, + { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.fill_buf_async().await + } + + fn consume(&mut self, amt: usize) { + self.consume_async(amt) + } + } + + impl embedded_io_async::BufRead for Uart<'_, T, Async> + where + T: Instance, + { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.rx.fill_buf_async().await + } + + fn consume(&mut self, amt: usize) { + self.rx.consume_async(amt) + } + } + impl embedded_io_async::Write for Uart<'_, T, Async> + where + T: Instance, + { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write_async(buf).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.flush_async().await + } + } + + impl embedded_io_async::Write for UartTx<'_, T, Async> + where + T: Instance, + { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write_async(buf).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.flush_async().await + } + } + + /// Interrupt handler for all UART instances + /// Clears and disables interrupts that have occurred and have their enable + /// bit set. The fact that an interrupt has been disabled is used by the + /// futures to detect that they should indeed resolve after being woken up + fn intr_handler(uart: &RegisterBlock) -> (bool, bool) { + let interrupts = uart.int_st().read(); + let interrupt_bits = interrupts.bits(); // = int_raw & int_ena + if interrupt_bits == 0 { + return (false, false); + } + let rx_wake = interrupts.rxfifo_full().bit_is_set() + || interrupts.rxfifo_ovf().bit_is_set() + || interrupts.rxfifo_tout().bit_is_set() + || interrupts.at_cmd_char_det().bit_is_set() + || interrupts.glitch_det().bit_is_set() + || interrupts.frm_err().bit_is_set() + || interrupts.parity_err().bit_is_set(); + let tx_wake = interrupts.tx_done().bit_is_set() || interrupts.txfifo_empty().bit_is_set(); + uart.int_clr().write(|w| unsafe { w.bits(interrupt_bits) }); + uart.int_ena() + .modify(|r, w| unsafe { w.bits(r.bits() & !interrupt_bits) }); + + (rx_wake, tx_wake) + } + + #[cfg(uart0)] + #[handler] + fn uart0() { + let uart = unsafe { &*crate::peripherals::UART0::ptr() }; + let (rx, tx) = intr_handler(uart); + if rx { + RX_WAKERS[0].wake(); + } + if tx { + TX_WAKERS[0].wake(); + } + } + + #[cfg(uart1)] + #[handler] + fn uart1() { + let uart = unsafe { &*crate::peripherals::UART1::ptr() }; + let (rx, tx) = intr_handler(uart); + if rx { + RX_WAKERS[1].wake(); + } + if tx { + TX_WAKERS[1].wake(); + } + } + + #[cfg(uart2)] + #[handler] + fn uart2() { + let uart = unsafe { &*crate::peripherals::UART2::ptr() }; + let (rx, tx) = intr_handler(uart); + if rx { + RX_WAKERS[2].wake(); + } + if tx { + TX_WAKERS[2].wake(); + } + } +} + +/// Low-power UART +#[cfg(lp_uart)] +pub mod lp_uart { + use crate::{ + gpio::lp_io::{LowPowerInput, LowPowerOutput}, + peripherals::{LP_CLKRST, LP_UART}, + uart::config::{self, Config}, + }; + /// LP-UART driver + /// + /// The driver uses XTAL as clock source. + pub struct LpUart { + uart: LP_UART, + } + + impl LpUart { + /// Initialize the UART driver using the default configuration + // TODO: CTS and RTS pins + pub fn new(uart: LP_UART, _tx: LowPowerOutput<5>, _rx: LowPowerInput<4>) -> Self { + let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR }; + let lp_aon = unsafe { &*crate::peripherals::LP_AON::PTR }; + + lp_aon + .gpio_mux() + .modify(|r, w| unsafe { w.sel().bits(r.sel().bits() | 1 << 4) }); + lp_aon + .gpio_mux() + .modify(|r, w| unsafe { w.sel().bits(r.sel().bits() | 1 << 5) }); + + lp_io.gpio4().modify(|_, w| unsafe { w.mcu_sel().bits(1) }); + lp_io.gpio5().modify(|_, w| unsafe { w.mcu_sel().bits(1) }); + + Self::new_with_config(uart, Config::default()) + } + + /// Initialize the UART driver using the provided configuration + pub fn new_with_config(uart: LP_UART, config: Config) -> Self { + let mut me = Self { uart }; + + // Set UART mode - do nothing for LP + + // Disable UART parity + // 8-bit world + // 1-bit stop bit + me.uart.conf0().modify(|_, w| unsafe { + w.parity() + .clear_bit() + .parity_en() + .clear_bit() + .bit_num() + .bits(0x3) + .stop_bit_num() + .bits(0x1) + }); + // Set tx idle + me.uart + .idle_conf() + .modify(|_, w| unsafe { w.tx_idle_num().bits(0) }); + // Disable hw-flow control + me.uart + .hwfc_conf() + .modify(|_, w| w.rx_flow_en().clear_bit()); + + // Get source clock frequency + // default == SOC_MOD_CLK_RTC_FAST == 2 + + // LP_CLKRST.lpperi.lp_uart_clk_sel = 0; + unsafe { &*LP_CLKRST::PTR } + .lpperi() + .modify(|_, w| w.lp_uart_clk_sel().clear_bit()); + + // Override protocol parameters from the configuration + // uart_hal_set_baudrate(&hal, cfg->uart_proto_cfg.baud_rate, sclk_freq); + me.change_baud_internal(config.baudrate, config.clock_source); + // uart_hal_set_parity(&hal, cfg->uart_proto_cfg.parity); + me.change_parity(config.parity); + // uart_hal_set_data_bit_num(&hal, cfg->uart_proto_cfg.data_bits); + me.change_data_bits(config.data_bits); + // uart_hal_set_stop_bits(&hal, cfg->uart_proto_cfg.stop_bits); + me.change_stop_bits(config.stop_bits); + // uart_hal_set_tx_idle_num(&hal, LP_UART_TX_IDLE_NUM_DEFAULT); + me.change_tx_idle(0); // LP_UART_TX_IDLE_NUM_DEFAULT == 0 + + // Reset Tx/Rx FIFOs + me.rxfifo_reset(); + me.txfifo_reset(); + + me + } + + fn rxfifo_reset(&mut self) { + self.uart.conf0().modify(|_, w| w.rxfifo_rst().set_bit()); + self.update(); + + self.uart.conf0().modify(|_, w| w.rxfifo_rst().clear_bit()); + self.update(); + } + + fn txfifo_reset(&mut self) { + self.uart.conf0().modify(|_, w| w.txfifo_rst().set_bit()); + self.update(); + + self.uart.conf0().modify(|_, w| w.txfifo_rst().clear_bit()); + self.update(); + } + + fn update(&mut self) { + self.uart + .reg_update() + .modify(|_, w| w.reg_update().set_bit()); + while self.uart.reg_update().read().reg_update().bit_is_set() { + // wait + } + } + + fn change_baud_internal(&mut self, baudrate: u32, clock_source: super::ClockSource) { + // TODO: Currently it's not possible to use XtalD2Clk + let clk = 16_000_000; + let max_div = 0b1111_1111_1111 - 1; + let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate); + + self.uart.clk_conf().modify(|_, w| unsafe { + w.sclk_div_a() + .bits(0) + .sclk_div_b() + .bits(0) + .sclk_div_num() + .bits(clk_div as u8 - 1) + .sclk_sel() + .bits(match clock_source { + super::ClockSource::Xtal => 3, + super::ClockSource::RcFast => 2, + super::ClockSource::Apb => panic!("Wrong clock source for LP_UART"), + }) + .sclk_en() + .set_bit() + }); + + let clk = clk / clk_div; + let divider = clk / baudrate; + let divider = divider as u16; + + self.uart + .clkdiv() + .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); + + self.update(); + } + + /// Modify UART baud rate and reset TX/RX fifo. + pub fn change_baud(&mut self, baudrate: u32, clock_source: super::ClockSource) { + self.change_baud_internal(baudrate, clock_source); + self.txfifo_reset(); + self.rxfifo_reset(); + } + + fn change_parity(&mut self, parity: config::Parity) -> &mut Self { + if parity != config::Parity::ParityNone { + self.uart + .conf0() + .modify(|_, w| w.parity().bit((parity as u8 & 0x1) != 0)); + } + + self.uart.conf0().modify(|_, w| match parity { + config::Parity::ParityNone => w.parity_en().clear_bit(), + config::Parity::ParityEven => w.parity_en().set_bit().parity().clear_bit(), + config::Parity::ParityOdd => w.parity_en().set_bit().parity().set_bit(), + }); + + self + } + + fn change_data_bits(&mut self, data_bits: config::DataBits) -> &mut Self { + self.uart + .conf0() + .modify(|_, w| unsafe { w.bit_num().bits(data_bits as u8) }); + + self.update(); + + self + } + + fn change_stop_bits(&mut self, stop_bits: config::StopBits) -> &mut Self { + self.uart + .conf0() + .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8) }); + + self.update(); + self + } + + fn change_tx_idle(&mut self, idle_num: u16) -> &mut Self { + self.uart + .idle_conf() + .modify(|_, w| unsafe { w.tx_idle_num().bits(idle_num) }); + + self.update(); + self + } + } +}