Compare commits

..

No commits in common. "bounce_buffer" and "master" have entirely different histories.

24 changed files with 2283 additions and 3271 deletions

201
firmware/Cargo.lock generated
View file

@ -26,17 +26,16 @@ dependencies = [
"embedded-storage-async", "embedded-storage-async",
"embuild", "embuild",
"enumset", "enumset",
"esp-alloc 0.9.0 (git+https://github.com/esp-rs/esp-hal?rev=ee6e26f2fefa4da2168c95839bf618e1ecc22cc1)", "esp-alloc",
"esp-backtrace", "esp-backtrace",
"esp-bootloader-esp-idf", "esp-bootloader-esp-idf",
"esp-hal", "esp-hal",
"esp-hal-bounce-buffers", "esp-metadata-generated",
"esp-metadata-generated 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"esp-println", "esp-println",
"esp-radio", "esp-radio",
"esp-rtos", "esp-rtos",
"esp-storage", "esp-storage",
"esp-sync 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "esp-sync",
"gix", "gix",
"hex", "hex",
"hmac", "hmac",
@ -48,7 +47,6 @@ dependencies = [
"lazy_static", "lazy_static",
"log", "log",
"mutually_exclusive_features", "mutually_exclusive_features",
"ouroboros",
"panic-rtt-target", "panic-rtt-target",
"password-hash 0.1.0", "password-hash 0.1.0",
"pastey 0.2.1", "pastey 0.2.1",
@ -107,12 +105,6 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "aliasable"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
[[package]] [[package]]
name = "aligned" name = "aligned"
version = "0.4.3" version = "0.4.3"
@ -2044,23 +2036,8 @@ dependencies = [
"cfg-if", "cfg-if",
"document-features", "document-features",
"enumset", "enumset",
"esp-config 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "esp-config",
"esp-sync 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "esp-sync",
"linked_list_allocator",
"rlsf",
]
[[package]]
name = "esp-alloc"
version = "0.9.0"
source = "git+https://github.com/esp-rs/esp-hal?rev=ee6e26f2fefa4da2168c95839bf618e1ecc22cc1#ee6e26f2fefa4da2168c95839bf618e1ecc22cc1"
dependencies = [
"allocator-api2 0.3.1",
"cfg-if",
"document-features",
"enumset",
"esp-config 0.6.1 (git+https://github.com/esp-rs/esp-hal?rev=ee6e26f2fefa4da2168c95839bf618e1ecc22cc1)",
"esp-sync 0.1.1 (git+https://github.com/esp-rs/esp-hal?rev=ee6e26f2fefa4da2168c95839bf618e1ecc22cc1)",
"linked_list_allocator", "linked_list_allocator",
"rlsf", "rlsf",
] ]
@ -2073,13 +2050,13 @@ checksum = "3318413fb566c7227387f67736cf70cd74d80a11f2bb31c7b95a9eb48d079669"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"document-features", "document-features",
"esp-config 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "esp-config",
"esp-metadata-generated 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "esp-metadata-generated",
"esp-println", "esp-println",
"heapless 0.9.2", "heapless 0.9.2",
"riscv", "riscv",
"semihosting", "semihosting",
"xtensa-lx 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "xtensa-lx",
] ]
[[package]] [[package]]
@ -2091,9 +2068,9 @@ dependencies = [
"cfg-if", "cfg-if",
"document-features", "document-features",
"embedded-storage", "embedded-storage",
"esp-config 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "esp-config",
"esp-hal-procmacros", "esp-hal-procmacros",
"esp-metadata-generated 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "esp-metadata-generated",
"esp-rom-sys", "esp-rom-sys",
"jiff", "jiff",
"log", "log",
@ -2107,19 +2084,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "102871054f8dd98202177b9890cb4b71d0c6fe1f1413b7a379a8e0841fc2473c" checksum = "102871054f8dd98202177b9890cb4b71d0c6fe1f1413b7a379a8e0841fc2473c"
dependencies = [ dependencies = [
"document-features", "document-features",
"esp-metadata-generated 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "esp-metadata-generated",
"serde",
"serde_yaml",
"somni-expr",
]
[[package]]
name = "esp-config"
version = "0.6.1"
source = "git+https://github.com/esp-rs/esp-hal?rev=ee6e26f2fefa4da2168c95839bf618e1ecc22cc1#ee6e26f2fefa4da2168c95839bf618e1ecc22cc1"
dependencies = [
"document-features",
"esp-metadata-generated 0.3.0 (git+https://github.com/esp-rs/esp-hal?rev=ee6e26f2fefa4da2168c95839bf618e1ecc22cc1)",
"serde", "serde",
"serde_yaml", "serde_yaml",
"somni-expr", "somni-expr",
@ -2152,12 +2117,12 @@ dependencies = [
"embedded-io-async 0.6.1", "embedded-io-async 0.6.1",
"embedded-io-async 0.7.0", "embedded-io-async 0.7.0",
"enumset", "enumset",
"esp-config 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "esp-config",
"esp-hal-procmacros", "esp-hal-procmacros",
"esp-metadata-generated 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "esp-metadata-generated",
"esp-riscv-rt", "esp-riscv-rt",
"esp-rom-sys", "esp-rom-sys",
"esp-sync 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "esp-sync",
"esp-synopsys-usb-otg", "esp-synopsys-usb-otg",
"esp32", "esp32",
"esp32c2", "esp32c2",
@ -2177,20 +2142,10 @@ dependencies = [
"riscv", "riscv",
"strum", "strum",
"ufmt-write", "ufmt-write",
"xtensa-lx 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "xtensa-lx",
"xtensa-lx-rt", "xtensa-lx-rt",
] ]
[[package]]
name = "esp-hal-bounce-buffers"
version = "0.1.0"
dependencies = [
"document-features",
"embassy-sync 0.7.2",
"esp-hal",
"ouroboros",
]
[[package]] [[package]]
name = "esp-hal-procmacros" name = "esp-hal-procmacros"
version = "0.21.0" version = "0.21.0"
@ -2212,11 +2167,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a93e39c8ad8d390d248dc7b9f4b59a873f313bf535218b8e2351356972399e3" checksum = "9a93e39c8ad8d390d248dc7b9f4b59a873f313bf535218b8e2351356972399e3"
[[package]]
name = "esp-metadata-generated"
version = "0.3.0"
source = "git+https://github.com/esp-rs/esp-hal?rev=ee6e26f2fefa4da2168c95839bf618e1ecc22cc1#ee6e26f2fefa4da2168c95839bf618e1ecc22cc1"
[[package]] [[package]]
name = "esp-phy" name = "esp-phy"
version = "0.1.1" version = "0.1.1"
@ -2225,10 +2175,10 @@ checksum = "6b1facf348e1e251517278fc0f5dc134e95e518251f5796cfbb532ca226a29bf"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"document-features", "document-features",
"esp-config 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "esp-config",
"esp-hal", "esp-hal",
"esp-metadata-generated 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "esp-metadata-generated",
"esp-sync 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "esp-sync",
"esp-wifi-sys", "esp-wifi-sys",
] ]
@ -2239,8 +2189,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a30e6c9fbcc01c348d46706fef8131c7775ab84c254a3cd65d0cd3f6414d592" checksum = "5a30e6c9fbcc01c348d46706fef8131c7775ab84c254a3cd65d0cd3f6414d592"
dependencies = [ dependencies = [
"document-features", "document-features",
"esp-metadata-generated 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "esp-metadata-generated",
"esp-sync 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "esp-sync",
"log", "log",
"portable-atomic", "portable-atomic",
] ]
@ -2259,14 +2209,14 @@ dependencies = [
"embedded-io 0.7.1", "embedded-io 0.7.1",
"embedded-io-async 0.6.1", "embedded-io-async 0.6.1",
"embedded-io-async 0.7.0", "embedded-io-async 0.7.0",
"esp-alloc 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "esp-alloc",
"esp-config 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "esp-config",
"esp-hal", "esp-hal",
"esp-hal-procmacros", "esp-hal-procmacros",
"esp-metadata-generated 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "esp-metadata-generated",
"esp-phy", "esp-phy",
"esp-radio-rtos-driver", "esp-radio-rtos-driver",
"esp-sync 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "esp-sync",
"esp-wifi-sys", "esp-wifi-sys",
"heapless 0.9.2", "heapless 0.9.2",
"instability", "instability",
@ -2302,7 +2252,7 @@ checksum = "cd66cccc6dd2d13e9f33668a57717ab14a6d217180ec112e6be533de93e7ecbf"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"document-features", "document-features",
"esp-metadata-generated 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "esp-metadata-generated",
] ]
[[package]] [[package]]
@ -2318,12 +2268,12 @@ dependencies = [
"embassy-sync 0.7.2", "embassy-sync 0.7.2",
"embassy-time-driver", "embassy-time-driver",
"embassy-time-queue-utils", "embassy-time-queue-utils",
"esp-config 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "esp-config",
"esp-hal", "esp-hal",
"esp-hal-procmacros", "esp-hal-procmacros",
"esp-metadata-generated 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "esp-metadata-generated",
"esp-radio-rtos-driver", "esp-radio-rtos-driver",
"esp-sync 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "esp-sync",
"portable-atomic", "portable-atomic",
] ]
@ -2337,9 +2287,9 @@ dependencies = [
"embedded-storage", "embedded-storage",
"esp-hal", "esp-hal",
"esp-hal-procmacros", "esp-hal-procmacros",
"esp-metadata-generated 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "esp-metadata-generated",
"esp-rom-sys", "esp-rom-sys",
"esp-sync 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "esp-sync",
] ]
[[package]] [[package]]
@ -2352,24 +2302,10 @@ dependencies = [
"document-features", "document-features",
"embassy-sync 0.6.2", "embassy-sync 0.6.2",
"embassy-sync 0.7.2", "embassy-sync 0.7.2",
"esp-metadata-generated 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "esp-metadata-generated",
"log", "log",
"riscv", "riscv",
"xtensa-lx 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "xtensa-lx",
]
[[package]]
name = "esp-sync"
version = "0.1.1"
source = "git+https://github.com/esp-rs/esp-hal?rev=ee6e26f2fefa4da2168c95839bf618e1ecc22cc1#ee6e26f2fefa4da2168c95839bf618e1ecc22cc1"
dependencies = [
"cfg-if",
"document-features",
"embassy-sync 0.6.2",
"embassy-sync 0.7.2",
"esp-metadata-generated 0.3.0 (git+https://github.com/esp-rs/esp-hal?rev=ee6e26f2fefa4da2168c95839bf618e1ecc22cc1)",
"riscv",
"xtensa-lx 0.13.0 (git+https://github.com/esp-rs/esp-hal?rev=ee6e26f2fefa4da2168c95839bf618e1ecc22cc1)",
] ]
[[package]] [[package]]
@ -3844,12 +3780,6 @@ dependencies = [
"stable_deref_trait", "stable_deref_trait",
] ]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.5.0" version = "0.5.0"
@ -5423,30 +5353,6 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "ouroboros"
version = "0.18.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59"
dependencies = [
"aliasable",
"ouroboros_macro",
"static_assertions",
]
[[package]]
name = "ouroboros_macro"
version = "0.18.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0"
dependencies = [
"heck 0.4.1",
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
"syn 2.0.114",
]
[[package]] [[package]]
name = "p256" name = "p256"
version = "0.13.2" version = "0.13.2"
@ -5855,19 +5761,6 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "proc-macro2-diagnostics"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"version_check",
"yansi",
]
[[package]] [[package]]
name = "prodash" name = "prodash"
version = "31.0.0" version = "31.0.0"
@ -6813,7 +6706,7 @@ dependencies = [
"bindgen", "bindgen",
"cc", "cc",
"flate2", "flate2",
"heck 0.5.0", "heck",
"pkg-config", "pkg-config",
"regex", "regex",
"serde_json", "serde_json",
@ -6962,7 +6855,7 @@ checksum = "a0f368519fc6c85fc1afdb769fb5a51123f6158013e143656e25a3485a0d401c"
[[package]] [[package]]
name = "spectre-api-sys" name = "spectre-api-sys"
version = "0.1.0" version = "0.1.0"
source = "git+c:/Users/Limeth/workspace/rust/spectre-api-sys#9e844eb056c3dfee8286ac21ec40fa689a8b8aa2" source = "git+https://github.com/Limeth/spectre-api-sys?rev=9e844eb056c3dfee8286ac21ec40fa689a8b8aa2#9e844eb056c3dfee8286ac21ec40fa689a8b8aa2"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"cc", "cc",
@ -7048,7 +6941,7 @@ version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
dependencies = [ dependencies = [
"heck 0.5.0", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.114", "syn 2.0.114",
@ -8562,14 +8455,6 @@ dependencies = [
"critical-section", "critical-section",
] ]
[[package]]
name = "xtensa-lx"
version = "0.13.0"
source = "git+https://github.com/esp-rs/esp-hal?rev=ee6e26f2fefa4da2168c95839bf618e1ecc22cc1#ee6e26f2fefa4da2168c95839bf618e1ecc22cc1"
dependencies = [
"critical-section",
]
[[package]] [[package]]
name = "xtensa-lx-rt" name = "xtensa-lx-rt"
version = "0.21.0" version = "0.21.0"
@ -8577,7 +8462,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8709f037fb123fe7ff146d2bce86f9dc0dfc53045c016bfd9d703317b6502845" checksum = "8709f037fb123fe7ff146d2bce86f9dc0dfc53045c016bfd9d703317b6502845"
dependencies = [ dependencies = [
"document-features", "document-features",
"xtensa-lx 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "xtensa-lx",
"xtensa-lx-rt-proc-macros", "xtensa-lx-rt-proc-macros",
] ]
@ -8618,12 +8503,6 @@ dependencies = [
"hashlink", "hashlink",
] ]
[[package]]
name = "yansi"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
[[package]] [[package]]
name = "yazi" name = "yazi"
version = "0.2.1" version = "0.2.1"
@ -8902,11 +8781,3 @@ dependencies = [
"syn 2.0.114", "syn 2.0.114",
"winnow", "winnow",
] ]
[[patch.unused]]
name = "i-slint-core"
version = "1.14.1"
[[patch.unused]]
name = "slint"
version = "1.14.1"

View file

@ -14,9 +14,6 @@ rustflags = [
# Required to obtain backtraces on riscv (e.g. when using the "esp-backtrace" crate.) # Required to obtain backtraces on riscv (e.g. when using the "esp-backtrace" crate.)
# "-C", "force-frame-pointers", # "-C", "force-frame-pointers",
# Output linker map
# "-C", "link-arg=-Wl,-Map=target/linker.map"
] ]
[env] # These must be kept in sync with /.zed/settings.json [env] # These must be kept in sync with /.zed/settings.json
@ -41,14 +38,3 @@ ACID_COMPOSE_LOCALE = "cs_CZ.UTF-8"
# This can be substituted with a `-Zbuild-std="core,alloc"` cargo flag. # This can be substituted with a `-Zbuild-std="core,alloc"` cargo flag.
[unstable] [unstable]
build-std = ["alloc", "core"] build-std = ["alloc", "core"]
[patch.crates-io]
esp-backtrace = { git = "https://github.com/Limeth/esp-hal.git", rev = "114977583886be4ed866ad7b7c6f16865148e899" }
esp-println = { git = "https://github.com/Limeth/esp-hal.git", rev = "114977583886be4ed866ad7b7c6f16865148e899" }
# esp-hal = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }
# esp-storage = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }
# esp-alloc = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }
# esp-radio = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }
# esp-rtos = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }
# esp-bootloader-esp-idf = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }
# esp-sync = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }

View file

@ -6,7 +6,7 @@ description = "Firmware for the ACID keyboard"
edition = "2024" edition = "2024"
[features] [features]
default = ["usb-log", "limit-fps", "no-alloc-tracing"] default = ["usb-log", "limit-fps"]
# Make RMK not to use USB # Make RMK not to use USB
no-usb = ["rmk/_no_usb"] no-usb = ["rmk/_no_usb"]
# Let RMK use BLE # Let RMK use BLE
@ -30,10 +30,6 @@ develop-usb = ["limit-fps", "usb-log", "no-usb", "ble"]
probe = ["limit-fps", "rtt-log", "no-usb", "ble"] probe = ["limit-fps", "rtt-log", "no-usb", "ble"]
# Formats the EKV database on boot. # Formats the EKV database on boot.
format-db = [] format-db = []
# Avoid entering the critical section for the whole duration of printing a message to console.
racy-logging = []
# Global allocator tracing proxy
no-alloc-tracing = ["esp-alloc/global-allocator"]
[dependencies] [dependencies]
rmk = { version = "0.8.2", git = "https://github.com/Limeth/rmk", rev = "1661c55f5c21e7d80ea3f93255df483302c74b84", default-features = false, features = [ rmk = { version = "0.8.2", git = "https://github.com/Limeth/rmk", rev = "1661c55f5c21e7d80ea3f93255df483302c74b84", default-features = false, features = [
@ -53,7 +49,7 @@ esp-backtrace = { version = "0.18", default-features = false, features = [
] } ] }
esp-hal = { version = "1.0", features = ["esp32s3", "unstable", "psram", "log-04"] } esp-hal = { version = "1.0", features = ["esp32s3", "unstable", "psram", "log-04"] }
esp-storage = { version = "0.8", features = ["esp32s3"] } esp-storage = { version = "0.8", features = ["esp32s3"] }
esp-alloc = { version = "0.9", git = "https://github.com/esp-rs/esp-hal", rev = "ee6e26f2fefa4da2168c95839bf618e1ecc22cc1", default-features = false, features = ["esp32s3", "nightly", "compat"] } esp-alloc = { version = "0.9", features = ["nightly"] }
esp-println = { version = "0.16", features = ["esp32s3", "log-04"] } esp-println = { version = "0.16", features = ["esp32s3", "log-04"] }
esp-radio = { version = "0.17", features = ["esp32s3", "unstable", "ble"], optional = true } esp-radio = { version = "0.17", features = ["esp32s3", "unstable", "ble"], optional = true }
esp-rtos = { version = "0.2", features = ["esp32s3", "esp-radio", "embassy"] } esp-rtos = { version = "0.2", features = ["esp32s3", "esp-radio", "embassy"] }
@ -86,12 +82,10 @@ serde = { version = "1.0", default-features = false, features = ["derive"] }
# serde_with = { version = "3.16", default-features = false, features = ["alloc", "macros"] } # serde_with = { version = "3.16", default-features = false, features = ["alloc", "macros"] }
serde_bytes = { version = "0.11.19", default-features = false, features = ["alloc"] } serde_bytes = { version = "0.11.19", default-features = false, features = ["alloc"] }
chrono = { version = "0.4.43", default-features = false, features = ["alloc", "serde"] } # TODO: defmt chrono = { version = "0.4.43", default-features = false, features = ["alloc", "serde"] } # TODO: defmt
tinyvec = { version = "1.10.0", default-features = false, features = ["alloc", "latest_stable_rust"] } tinyvec = { version = "1.10.0", default-features = false, features = ["alloc"] }
esp-metadata-generated = { version = "0.3.0", features = ["esp32s3"] } esp-metadata-generated = { version = "0.3.0", features = ["esp32s3"] }
hex = { version = "0.4.3", default-features = false, features = ["alloc"] } hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
indoc = "2.0.7" indoc = "2.0.7"
ouroboros = "0.18.5"
esp-hal-bounce-buffers = { git = "https://forgejo.limeth.cz/limeth/esp-hal-bounce-buffers", rev = "8d3763a190368f476aed6d98777264c959bfdc2d", features = ["esp32s3"] }
# A fork of slint with patches for `allocator_api` support. # A fork of slint with patches for `allocator_api` support.
# Don't forget to change `slint-build` in build dependencies, if this is changed. # Don't forget to change `slint-build` in build dependencies, if this is changed.

View file

@ -81,6 +81,7 @@ fn main() {
.map_err(|_| NotBuilt { .map_err(|_| NotBuilt {
lib_build_dir: lib_build_dir_str.clone(), lib_build_dir: lib_build_dir_str.clone(),
})?; })?;
let lib_build_dir = lib_build_dir.display();
if !lib_library_path.is_file() { if !lib_library_path.is_file() {
return Err(NotBuilt { return Err(NotBuilt {
@ -88,9 +89,9 @@ fn main() {
}); });
} }
println!("cargo:rustc-link-search=native={}", lib_build_dir.display()); println!("cargo:rustc-link-search=native={lib_build_dir}");
println!("cargo:rustc-link-lib=static={library}"); println!("cargo:rustc-link-lib=static={library}");
println!("cargo:rerun-if-changed={}", lib_library_path.display()); println!("cargo:rerun-if-changed={lib_build_dir}/lib{library}.a");
Ok(()) Ok(())
} }

View file

@ -18,11 +18,11 @@ impl embedded_io::ErrorType for Writer {
impl embedded_io::Write for Writer { impl embedded_io::Write for Writer {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
with_uart_tx(|uart| uart.write(buf)) with_uart_tx(|_, uart| uart.write(buf))
} }
fn flush(&mut self) -> Result<(), Self::Error> { fn flush(&mut self) -> Result<(), Self::Error> {
with_uart_tx(|uart| uart.flush()) with_uart_tx(|_, uart| uart.flush())
} }
} }

View file

@ -10,14 +10,11 @@ use ekv::{
}; };
use embassy_embedded_hal::{adapter::BlockingAsync, flash::partition::Partition}; use embassy_embedded_hal::{adapter::BlockingAsync, flash::partition::Partition};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync_old::blocking_mutex::raw::CriticalSectionRawMutex as CriticalSectionRawMutexOld;
use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash}; use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash};
use esp_hal::rng::Trng; use esp_hal::rng::Trng;
use esp_storage::FlashStorage; use esp_storage::FlashStorage;
use log::{debug, info}; use log::{debug, info};
use crate::ram::{PSRAM_ALLOCATOR, PsramAllocator};
pub type PartitionAcid = pub type PartitionAcid =
Partition<'static, CriticalSectionRawMutex, BlockingAsync<FlashStorage<'static>>>; Partition<'static, CriticalSectionRawMutex, BlockingAsync<FlashStorage<'static>>>;
@ -27,7 +24,7 @@ struct AlignedBuf<const N: usize>(pub [u8; N]);
pub struct EkvFlash<T> { pub struct EkvFlash<T> {
flash: T, flash: T,
buffer: Box<AlignedBuf<{ ekv::config::PAGE_SIZE }>, PsramAllocator>, buffer: Box<AlignedBuf<{ ekv::config::PAGE_SIZE }>>,
} }
impl<T> EkvFlash<T> { impl<T> EkvFlash<T> {
@ -36,7 +33,7 @@ impl<T> EkvFlash<T> {
flash, flash,
buffer: { buffer: {
// Allocate the buffer directly on the heap. // Allocate the buffer directly on the heap.
let buffer = Box::new_zeroed_in(&PSRAM_ALLOCATOR); let buffer = Box::new_zeroed();
unsafe { buffer.assume_init() } unsafe { buffer.assume_init() }
}, },
} }
@ -105,11 +102,11 @@ impl<T: NorFlash + ReadNorFlash> ekv::flash::Flash for EkvFlash<T> {
} }
pub struct AcidDatabase { pub struct AcidDatabase {
db: Database<EkvFlash<PartitionAcid>, CriticalSectionRawMutexOld>, db: Database<EkvFlash<PartitionAcid>, esp_sync::RawMutex>,
} }
impl Deref for AcidDatabase { impl Deref for AcidDatabase {
type Target = Database<EkvFlash<PartitionAcid>, CriticalSectionRawMutexOld>; type Target = Database<EkvFlash<PartitionAcid>, esp_sync::RawMutex>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.db &self.db
@ -128,7 +125,7 @@ impl AcidDatabase {
db_config.random_seed = Trng::try_new() db_config.random_seed = Trng::try_new()
.expect("A `TrngSource` was not initialized before constructing this `Trng`.") .expect("A `TrngSource` was not initialized before constructing this `Trng`.")
.random(); .random();
let db = Database::<_, CriticalSectionRawMutexOld>::new(EkvFlash::new(flash), db_config); let db = Database::<_, esp_sync::RawMutex>::new(EkvFlash::new(flash), db_config);
#[cfg(feature = "format-db")] #[cfg(feature = "format-db")]
{ {

View file

@ -1,256 +0,0 @@
use alloc::{alloc::Global, boxed::Box, vec::Vec};
use embassy_time::Timer;
use esp_hal::{
Blocking,
dma::{BurstConfig, ExternalBurstConfig, InternalBurstConfig},
gpio::{Flex, Level, Output, OutputConfig},
lcd_cam::{LcdCam, lcd::dpi::Dpi},
ledc::{self, LSGlobalClkSource, Ledc, LowSpeed},
spi::master::AnySpi,
};
use esp_hal_bounce_buffers::{DmaBounce, Swapchain, SwapchainWriter, allocate_dma_buffer_in};
use i_slint_core::software_renderer::{Rgb565Pixel, TargetPixel};
use log::{error, info};
use crate::{DmaBounceController, PSRAM_ALLOCATOR, peripherals::st7701s::St7701s};
#[allow(non_snake_case)]
pub struct DisplayPeripherals {
pub DMA_CH2: esp_hal::peripherals::DMA_CH2<'static>,
pub LCD_CAM: esp_hal::peripherals::LCD_CAM<'static>,
pub LEDC: esp_hal::peripherals::LEDC<'static>,
pub GPIO0: Output<'static>,
pub GPIO1: esp_hal::peripherals::GPIO1<'static>,
pub GPIO2: esp_hal::peripherals::GPIO2<'static>,
pub GPIO3: esp_hal::peripherals::GPIO3<'static>,
pub GPIO4: esp_hal::peripherals::GPIO4<'static>,
#[cfg(not(feature = "alt-log"))]
pub GPIO5: esp_hal::peripherals::GPIO5<'static>,
pub GPIO6: esp_hal::peripherals::GPIO6<'static>,
#[cfg(not(feature = "alt-log"))]
pub GPIO12: esp_hal::peripherals::GPIO12<'static>,
pub GPIO13: esp_hal::peripherals::GPIO13<'static>,
pub GPIO14: esp_hal::peripherals::GPIO14<'static>,
pub GPIO15: esp_hal::peripherals::GPIO15<'static>,
pub GPIO16: esp_hal::peripherals::GPIO16<'static>,
pub GPIO21: esp_hal::peripherals::GPIO21<'static>,
pub GPIO34: esp_hal::peripherals::GPIO34<'static>,
pub GPIO35: esp_hal::peripherals::GPIO35<'static>,
pub GPIO36: esp_hal::peripherals::GPIO36<'static>,
pub GPIO37: esp_hal::peripherals::GPIO37<'static>,
pub GPIO38: esp_hal::peripherals::GPIO38<'static>,
pub GPIO39: esp_hal::peripherals::GPIO39<'static>,
pub GPIO40: esp_hal::peripherals::GPIO40<'static>,
pub GPIO41: esp_hal::peripherals::GPIO41<'static>,
pub GPIO42: esp_hal::peripherals::GPIO42<'static>,
pub GPIO43: esp_hal::peripherals::GPIO43<'static>,
pub GPIO44: esp_hal::peripherals::GPIO44<'static>,
}
impl DisplayPeripherals {
pub async fn into_display(self) -> St7701s<'static, Blocking> {
let mut ledc = Ledc::new(self.LEDC);
ledc.set_global_slow_clock(LSGlobalClkSource::APBClk);
let bl_timer = ledc.timer::<LowSpeed>(ledc::timer::Number::Timer0);
let bl_channel = ledc.channel::<LowSpeed>(ledc::channel::Number::Channel0, self.GPIO21);
let sck = Output::new(self.GPIO36, Level::High, OutputConfig::default());
let mosi = Flex::new(self.GPIO35);
let cs = Output::new(self.GPIO6, Level::High, OutputConfig::default());
let lcd = LcdCam::new(self.LCD_CAM).lcd;
#[allow(unused_mut)]
let mut unconfigured_dpi = Dpi::new(lcd, self.DMA_CH2, Default::default())
.unwrap()
.with_de(self.GPIO37)
.with_pclk(self.GPIO34)
.with_hsync(self.GPIO44)
.with_vsync(self.GPIO43)
// Blue
.with_data0(self.GPIO38)
.with_data1(self.GPIO39)
.with_data2(self.GPIO40)
.with_data3(self.GPIO41)
.with_data4(self.GPIO42)
// Green
.with_data7(self.GPIO13)
.with_data8(self.GPIO14)
.with_data9(self.GPIO15)
.with_data10(self.GPIO16)
// Red
.with_data11(self.GPIO0)
.with_data12(self.GPIO1)
.with_data13(self.GPIO2)
.with_data14(self.GPIO3)
.with_data15(self.GPIO4);
#[cfg(not(feature = "alt-log"))]
{
unconfigured_dpi = unconfigured_dpi
// Green
.with_data5(peripherals.GPIO5)
.with_data6(peripherals.GPIO12);
}
let st7701s = St7701s::new(sck, mosi, cs, unconfigured_dpi, bl_timer, bl_channel).await;
info!("ST7701S-based LCD display initialized!");
st7701s
}
}
// TODO: Rename or get rid of.
pub struct Framebuffer {
pub width: u32,
pub height: u32,
pub swapchain: Option<SwapchainWriter>,
pub bounce_buffers: Option<DmaBounce>,
}
impl Framebuffer {
pub fn new(
channel: esp_hal::peripherals::DMA_CH0<'static>,
peripheral_src: AnySpi<'static>,
peripheral_dst: Dpi<'static, Blocking>,
burst_config: BurstConfig,
front_porch_pixels: u32,
width_pixels: u32,
height_pixels: u32,
rows_per_window: usize,
cyclic: bool,
) -> Self {
const BYTES_PER_PIXEL: usize = core::mem::size_of::<u16>();
let buffer_size = width_pixels as usize * height_pixels as usize * BYTES_PER_PIXEL;
let framebuffers = [
Box::leak(allocate_dma_buffer_in(
buffer_size,
burst_config,
&PSRAM_ALLOCATOR,
)),
Box::leak(allocate_dma_buffer_in(
buffer_size,
burst_config,
&PSRAM_ALLOCATOR,
)),
];
let (swapchain_reader, swapchain_writer) = Swapchain { framebuffers }.into_reader_writer();
let bounce_buffers = DmaBounce::new(
Global,
channel,
peripheral_src,
peripheral_dst,
swapchain_reader,
front_porch_pixels as usize * BYTES_PER_PIXEL,
width_pixels as usize * BYTES_PER_PIXEL,
rows_per_window,
burst_config,
cyclic,
);
Self {
width: width_pixels,
height: height_pixels,
swapchain: Some(swapchain_writer),
bounce_buffers: Some(bounce_buffers),
}
}
}
#[allow(unused)]
pub async fn test_bounce_buffers(
channel: esp_hal::peripherals::DMA_CH0<'static>,
peripheral: esp_hal::peripherals::SPI2<'static>,
display_peripherals: DisplayPeripherals,
) {
error!("TEST BOUNCE BUFFERS SECTION ENTERED");
const BYTES_PER_PIXEL: usize = core::mem::size_of::<u16>();
// Assume highest burst config setting.
const EXTERNAL_BURST_CONFIG: ExternalBurstConfig = ExternalBurstConfig::Size32;
const ALIGNMENT_PIXELS: usize = EXTERNAL_BURST_CONFIG as usize / BYTES_PER_PIXEL;
// The total number of pixels demanded by the DPI, per row.
const WIDTH_TOTAL_PIXELS: usize = 368;
// The total number of rows demanded by the DPI, per frame.
const HEIGHT_PIXELS: usize = 960;
// The number of unused pixels at the start of the row.
const FRONT_PORCH_ACTUAL_PIXELS: usize = 120;
// The number of actually visible pixels, per row.
const WIDTH_VISIBLE_PIXELS: usize = 240;
// The number of pixels not stored in a bounce buffer, per row.
// This many arbitrary pixels are sent to the DPI.
const FRONT_PORCH_SKIPPED_PIXELS: usize =
(FRONT_PORCH_ACTUAL_PIXELS / ALIGNMENT_PIXELS) * ALIGNMENT_PIXELS;
const WIDTH_STORED_PIXELS: usize = WIDTH_TOTAL_PIXELS - FRONT_PORCH_SKIPPED_PIXELS;
const VISIBLE_OFFSET_IN_BUFFER_PIXELS: usize =
FRONT_PORCH_ACTUAL_PIXELS - FRONT_PORCH_SKIPPED_PIXELS;
const ROWS_PER_WINDOW: usize = 16;
let burst_config = BurstConfig {
internal_memory: InternalBurstConfig::Enabled,
external_memory: EXTERNAL_BURST_CONFIG,
};
let (swapchain_reader, mut swapchain_writer) = Swapchain {
framebuffers: [
Box::leak(allocate_dma_buffer_in(
HEIGHT_PIXELS * WIDTH_STORED_PIXELS * BYTES_PER_PIXEL,
burst_config,
&PSRAM_ALLOCATOR,
)),
Box::leak(allocate_dma_buffer_in(
HEIGHT_PIXELS * WIDTH_STORED_PIXELS * BYTES_PER_PIXEL,
burst_config,
&PSRAM_ALLOCATOR,
)),
],
}
.into_reader_writer();
{
let write_guard = &mut swapchain_writer.write();
let buffer_src = bytemuck::cast_slice_mut::<u8, Rgb565Pixel>(write_guard);
let colors = (0..WIDTH_VISIBLE_PIXELS as u8 / 2)
.rev()
.map(|val| Rgb565Pixel::from_rgb(0xFF, val * 2, 0))
.collect::<Vec<_>>();
for (index, pixel) in buffer_src.iter_mut().enumerate() {
let mut x =
(index % WIDTH_STORED_PIXELS) as i16 - VISIBLE_OFFSET_IN_BUFFER_PIXELS as i16;
let mut y = (index / WIDTH_STORED_PIXELS) as i16;
if x < WIDTH_VISIBLE_PIXELS as i16 {
x = core::cmp::min(x, WIDTH_VISIBLE_PIXELS as i16 - 1 - x);
y = core::cmp::min(y, HEIGHT_PIXELS as i16 - 1 - y);
let min = core::cmp::min(x, y);
*pixel = colors[min as usize % colors.len()];
continue;
}
*pixel = Rgb565Pixel::default();
}
}
let mut st7701s = display_peripherals.into_display().await;
let mut dma_bounce = DmaBounce::new(
Global,
channel,
AnySpi::from(peripheral),
st7701s.dpi,
swapchain_reader,
FRONT_PORCH_SKIPPED_PIXELS * BYTES_PER_PIXEL,
WIDTH_STORED_PIXELS * BYTES_PER_PIXEL,
ROWS_PER_WINDOW,
burst_config,
true,
);
let mut bb_controller = DmaBounceController::new(dma_bounce);
error!("TEST BOUNCE BUFFERS TASK LAUNCHED");
loop {
bb_controller.start().await.unwrap();
st7701s.controller.sleep_off().await;
Timer::after_secs(1).await;
st7701s.controller.sleep_on().await;
bb_controller.stop().await.unwrap();
Timer::after_secs(1).await;
}
}

View file

@ -8,7 +8,7 @@ use enumset::EnumSet;
use crate::ffi::string::__xkbc_memcpy; use crate::ffi::string::__xkbc_memcpy;
// Here we select the allocator to use for libxkbcommon. // Here we select the allocator to use for libxkbcommon.
pub use crate::ram::PSRAM_ALLOCATOR as XKBC_ALLOCATOR; pub use crate::PSRAM_ALLOCATOR as XKBC_ALLOCATOR;
// Implementation based on esp-alloc's `compat` feature. // Implementation based on esp-alloc's `compat` feature.

View file

@ -3,6 +3,7 @@ use core::{
ffi::{c_char, c_int, c_size_t, c_uchar, c_ulonglong}, ffi::{c_char, c_int, c_size_t, c_uchar, c_ulonglong},
}; };
use critical_section::Mutex;
use embassy_sync::blocking_mutex::{self, raw::CriticalSectionRawMutex}; use embassy_sync::blocking_mutex::{self, raw::CriticalSectionRawMutex};
use hmac::digest::{FixedOutput, KeyInit, Update}; use hmac::digest::{FixedOutput, KeyInit, Update};
use password_hash::Key; use password_hash::Key;
@ -38,8 +39,7 @@ unsafe extern "C" fn __spre_crypto_hash_sha256(
/// This is the encrypted user key currently being used in the key derivation function of spectre. /// This is the encrypted user key currently being used in the key derivation function of spectre.
/// It decrypts using the user's password into the key that would be derived with the original password hashing function. /// It decrypts using the user's password into the key that would be derived with the original password hashing function.
pub static ACTIVE_ENCRYPTED_USER_KEY: blocking_mutex::Mutex<CriticalSectionRawMutex, Cell<Key>> = pub static ACTIVE_ENCRYPTED_USER_KEY: Mutex<Cell<Key>> = Mutex::new(Cell::new([0; _]));
blocking_mutex::Mutex::new(Cell::new([0; _]));
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
#[must_use] #[must_use]
@ -65,7 +65,7 @@ unsafe extern "C" fn __spre_crypto_pwhash_scryptsalsa208sha256_ll(
}; };
let output: &mut [u8] = unsafe { core::slice::from_raw_parts_mut(output, output_len) }; let output: &mut [u8] = unsafe { core::slice::from_raw_parts_mut(output, output_len) };
let mut user_key = ACTIVE_ENCRYPTED_USER_KEY.lock(|user_key| user_key.get()); let mut user_key = critical_section::with(|cs| ACTIVE_ENCRYPTED_USER_KEY.borrow(cs).get());
password_hash::decrypt_with(&mut user_key, &encryption_key); password_hash::decrypt_with(&mut user_key, &encryption_key);
output.copy_from_slice(&user_key); output.copy_from_slice(&user_key);

View file

@ -149,8 +149,7 @@ pub unsafe extern "C" fn __xkbc_atoi(s: *const c_char) -> c_int {
todo!() todo!()
} }
// A pointer to an array of character attributes. // TODO: What is this even for?
// This is used by `isdigit()`, `isalpha()`, `isspace()`, etc.
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub static __spre__ctype_: [c_char; 0] = []; pub static __spre__ctype_: [c_char; 0] = [];

View file

@ -1,117 +0,0 @@
use core::fmt::Write;
use alloc::{string::String, vec::Vec};
use embassy_embedded_hal::adapter::BlockingAsync;
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex};
use esp_bootloader_esp_idf::partitions::PartitionTable;
use esp_storage::FlashStorage;
use indoc::writedoc;
use log::info;
use rmk::storage::async_flash_wrapper;
use static_cell::StaticCell;
use crate::{PSRAM_ALLOCATOR, ram::PsramAllocator};
pub type Partition = embassy_embedded_hal::flash::partition::Partition<
'static,
CriticalSectionRawMutex,
BlockingAsync<FlashStorage<'static>>,
>;
pub struct Partitions {
pub rmk: Partition,
pub acid: Partition,
}
/// Initialize the flash
pub fn initialize(flash_peripheral: esp_hal::peripherals::FLASH<'static>) -> Partitions {
static PARTITION_TABLE_BUFFER: StaticCell<Vec<u8, PsramAllocator>> = StaticCell::new();
let partition_table_buffer = PARTITION_TABLE_BUFFER.init_with(|| {
let mut buffer = Vec::<u8, _>::new_in(&PSRAM_ALLOCATOR);
buffer.resize(
esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN,
0_u8,
);
buffer
});
static FLASH: StaticCell<(
Mutex<CriticalSectionRawMutex, BlockingAsync<FlashStorage>>,
PartitionTable<'static>,
)> = StaticCell::new();
let (flash, partition_table) = FLASH.init_with(|| {
let mut flash = FlashStorage::new(flash_peripheral)
// Flash memory may not be written to while another core is executing from it.
// By default, `FlashStorage` is configured to abort the operation and log an error message.
// However, it can also be configured to auto-park the other core, such that writing to
// flash succeeds.
// Alternatively, XiP from PSRAM could be used along with the `multicore_ignore` strategy,
// to avoid having to park the other core, which could result in better performance.
// Invalid configuration would then present itself as freezing/UB.
.multicore_auto_park();
let partition_table = {
esp_bootloader_esp_idf::partitions::read_partition_table(
&mut flash,
partition_table_buffer,
)
.expect("Failed to read the partition table.")
};
(
Mutex::<CriticalSectionRawMutex, _>::new(async_flash_wrapper(flash)),
partition_table,
)
});
{
let mut buffer = String::new();
writeln!(buffer, "Partition table:").unwrap();
for (index, partition) in partition_table.iter().enumerate() {
writedoc!(
buffer,
"
Partition #{index} {label:?}:
offset: 0x{offset:x}
length: 0x{len:x}
type: 0x{type:?}
read only: {read_only}
encrypted: {encrypted}
magic: {magic}
",
label = partition.label_as_str(),
offset = partition.offset(),
len = partition.len(),
type = partition.partition_type(),
read_only = partition.is_read_only(),
encrypted = partition.is_encrypted(),
magic = partition.magic(),
)
.unwrap();
}
info!("{}", buffer);
}
let flash_part_info_rmk = partition_table
.iter()
.find(|partition| partition.label_as_str() == "rmk")
.expect("No \"rmk\" partition found. Make sure to use the custom partition-table.csv when flashing.");
let flash_part_info_acid = partition_table
.iter()
.find(|partition| partition.label_as_str() == "acid")
.expect("No \"acid\" partition found. Make sure to use the custom partition-table.csv when flashing.");
Partitions {
rmk: Partition::new(
flash,
flash_part_info_rmk.offset(),
flash_part_info_rmk.len(),
),
acid: Partition::new(
flash,
flash_part_info_acid.offset(),
flash_part_info_acid.len(),
),
}
}

View file

@ -2,12 +2,9 @@ use core::fmt::Arguments;
use log::LevelFilter; use log::LevelFilter;
// TODO: Replace with `log`'s `STATIC_MAX_LEVEL` set via crate features.
pub const LOG_LEVEL_FILTER: LevelFilter = { pub const LOG_LEVEL_FILTER: LevelFilter = {
if let Some(string) = option_env!("ESP_LOG") { if let Some(string) = option_env!("ESP_LOG") {
if string.eq_ignore_ascii_case("OFF") { if string.eq_ignore_ascii_case("ERROR") {
LevelFilter::Off
} else if string.eq_ignore_ascii_case("ERROR") {
LevelFilter::Error LevelFilter::Error
} else if string.eq_ignore_ascii_case("WARN") { } else if string.eq_ignore_ascii_case("WARN") {
LevelFilter::Warn LevelFilter::Warn
@ -60,9 +57,10 @@ fn with_formatted_log_record<R>(
pub mod usb { pub mod usb {
use super::*; use super::*;
pub fn setup_logging() { pub fn setup_logging() -> impl Future<Output = ()> {
esp_println::logger::init_logger(LOG_LEVEL_FILTER); esp_println::logger::init_logger(LOG_LEVEL_FILTER);
log::info!("Logger initialized!"); log::info!("Logger initialized!");
async {}
} }
} }
@ -71,45 +69,27 @@ pub mod usb {
#[macro_use] #[macro_use]
pub mod uart { pub mod uart {
use super::*; use super::*;
use crate::console;
use core::{cell::RefCell, fmt::Write}; use core::{cell::RefCell, fmt::Write};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use critical_section::{CriticalSection, Mutex};
use esp_hal::{Blocking, uart::UartTx}; use esp_hal::{
Blocking,
gpio::interconnect::{PeripheralInput, PeripheralOutput},
uart::{Uart, UartTx},
};
use log::{Log, info}; use log::{Log, info};
#[cfg(feature = "racy-logging")] static ALT_LOGGER_UART: Mutex<RefCell<Option<UartTx<'static, Blocking>>>> =
static ALT_LOGGER_UART: embassy_sync::mutex::Mutex< Mutex::new(RefCell::new(None));
CriticalSectionRawMutex,
RefCell<Option<UartTx<'static, Blocking>>>,
> = embassy_sync::mutex::Mutex::new(RefCell::new(None));
#[cfg(not(feature = "racy-logging"))] pub fn with_uart_tx<R>(
static ALT_LOGGER_UART: embassy_sync::blocking_mutex::Mutex< f: impl FnOnce(CriticalSection<'_>, &'_ mut UartTx<'static, Blocking>) -> R,
CriticalSectionRawMutex, ) -> R {
RefCell<Option<UartTx<'static, Blocking>>>, critical_section::with(|cs| {
> = embassy_sync::blocking_mutex::Mutex::new(RefCell::new(None)); let mut uart = ALT_LOGGER_UART.borrow(cs).borrow_mut();
#[cfg(feature = "racy-logging")]
pub fn with_uart_tx<R>(f: impl FnOnce(&'_ mut UartTx<'static, Blocking>) -> R) -> R {
use crate::util::MutexExt;
// Safety:
// * The guard is not held across yield points.
// * **CARE MUST BE TAKEN NOT TO INVOKE THIS FUNCTION FROM AN INTERRUPT HANDLER.**
let uart = unsafe { ALT_LOGGER_UART.lock_blocking() };
let mut uart = uart.borrow_mut();
let uart = uart.as_mut().unwrap();
(f)(uart)
}
#[cfg(not(feature = "racy-logging"))]
pub fn with_uart_tx<R>(f: impl FnOnce(&'_ mut UartTx<'static, Blocking>) -> R) -> R {
ALT_LOGGER_UART.lock(|uart| {
let mut uart = uart.borrow_mut();
let uart = uart.as_mut().unwrap(); let uart = uart.as_mut().unwrap();
(f)(uart) (f)(cs, uart)
}) })
} }
@ -126,7 +106,7 @@ pub mod uart {
#[allow(unused)] #[allow(unused)]
fn do_print(args: core::fmt::Arguments<'_>) { fn do_print(args: core::fmt::Arguments<'_>) {
with_uart_tx(|uart| { with_uart_tx(|_, uart| {
uart.write_fmt(format_args!("{}\n", args)).unwrap(); uart.write_fmt(format_args!("{}\n", args)).unwrap();
uart.flush().unwrap(); uart.flush().unwrap();
}) })
@ -143,7 +123,7 @@ pub mod uart {
#[allow(unused)] #[allow(unused)]
fn log(&self, record: &log::Record) { fn log(&self, record: &log::Record) {
with_uart_tx(|uart| { with_uart_tx(|cs, uart| {
with_formatted_log_record(record, |args| uart.write_fmt(args)).unwrap(); with_formatted_log_record(record, |args| uart.write_fmt(args)).unwrap();
uart.flush().unwrap(); uart.flush().unwrap();
}) })
@ -173,24 +153,20 @@ pub mod uart {
loop {} loop {}
} }
pub fn setup_logging(uart_tx: UartTx<'static, Blocking>) { pub fn setup_logging(
{ uart: impl esp_hal::uart::Instance + 'static,
#[cfg(feature = "racy-logging")] tx: impl PeripheralOutput<'static>,
{ rx: impl PeripheralInput<'static>,
use crate::util::MutexExt; ) -> impl Future<Output = ()> {
let (uart_rx, uart_tx) = Uart::new(uart, Default::default())
.unwrap()
.with_tx(tx)
.with_rx(rx)
.split();
// Safety: critical_section::with(|cs| {
// * The guard is not held across yield points. *ALT_LOGGER_UART.borrow(cs).borrow_mut() = Some(uart_tx);
// * This function is not invoked from an interrupt handler. });
let uart = unsafe { ALT_LOGGER_UART.lock_blocking() };
*uart.borrow_mut() = Some(uart_tx);
}
#[cfg(not(feature = "racy-logging"))]
ALT_LOGGER_UART.lock(move |uart| {
*uart.borrow_mut() = Some(uart_tx);
});
}
unsafe { unsafe {
log::set_logger_racy(&UartLogger).unwrap(); log::set_logger_racy(&UartLogger).unwrap();
@ -198,6 +174,7 @@ pub mod uart {
} }
info!("Logger initialized!"); info!("Logger initialized!");
console::run_console(uart_rx.into_async())
} }
} }
@ -210,9 +187,10 @@ pub mod rtt {
use panic_rtt_target as _; // Use the RTT panic handler. use panic_rtt_target as _; // Use the RTT panic handler.
use rtt_target::ChannelMode; use rtt_target::ChannelMode;
pub fn setup_logging() { pub fn setup_logging() -> impl Future<Output = ()> {
rtt_target::rtt_init_log!(LOG_LEVEL_FILTER, ChannelMode::BlockIfFull); rtt_target::rtt_init_log!(LOG_LEVEL_FILTER, ChannelMode::BlockIfFull);
log::info!("Logger initialized!"); log::info!("Logger initialized!");
async {}
} }
} }

View file

@ -9,7 +9,6 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(allocator_api)] #![feature(allocator_api)]
#![feature(btreemap_alloc)]
#![feature(macro_metavar_expr)] #![feature(macro_metavar_expr)]
#![feature(c_variadic)] #![feature(c_variadic)]
#![feature(c_size_t)] #![feature(c_size_t)]
@ -17,58 +16,72 @@
extern crate alloc; extern crate alloc;
use core::alloc::Layout;
use core::cell::RefCell; use core::cell::RefCell;
use core::fmt::Write;
use core::sync::atomic::{AtomicBool, Ordering}; use core::sync::atomic::{AtomicBool, Ordering};
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::collections::vec_deque::VecDeque; use alloc::collections::vec_deque::VecDeque;
use alloc::format; use alloc::format;
use alloc::string::String;
use alloc::sync::Arc; use alloc::sync::Arc;
use alloc::vec;
use alloc::vec::Vec;
use embassy_embedded_hal::adapter::BlockingAsync;
use embassy_embedded_hal::flash::partition::Partition;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_sync::blocking_mutex;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::channel::Channel; use embassy_sync::channel::Channel;
use embassy_sync::mutex::Mutex;
use embassy_sync::signal::Signal;
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use esp_alloc::MemoryCapability; use esp_alloc::{HeapRegion, MemoryCapability};
use esp_bootloader_esp_idf::partitions::PartitionTable;
use esp_hal::Blocking;
use esp_hal::clock::CpuClock; use esp_hal::clock::CpuClock;
use esp_hal::dma::{BurstConfig, ExternalBurstConfig, InternalBurstConfig}; use esp_hal::dma::{BurstConfig, DmaDescriptor, DmaTxBuf, ExternalBurstConfig};
use esp_hal::efuse::Efuse; use esp_hal::efuse::Efuse;
use esp_hal::gpio::{Input, InputConfig, Level, Output, OutputConfig, Pull}; use esp_hal::gpio::{Flex, Input, InputConfig, Level, Output, OutputConfig, Pull};
use esp_hal::i2c::master::{I2c, I2cAddress}; use esp_hal::i2c::master::{I2c, I2cAddress};
use esp_hal::interrupt::software::{SoftwareInterrupt, SoftwareInterruptControl}; use esp_hal::interrupt::software::SoftwareInterruptControl;
use esp_hal::lcd_cam::LcdCam;
use esp_hal::lcd_cam::lcd::dpi::Dpi;
use esp_hal::mcpwm::{McPwm, PeripheralClockConfig};
use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock}; use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock};
use esp_hal::ram;
use esp_hal::rng::TrngSource; use esp_hal::rng::TrngSource;
use esp_hal::sha::ShaBackend; use esp_hal::sha::ShaBackend;
use esp_hal::system::Stack; use esp_hal::system::Stack;
use esp_hal::timer::timg::TimerGroup; use esp_hal::timer::timg::TimerGroup;
use esp_hal::uart::{Uart, UartRx}; use esp_rtos::embassy::Executor;
use esp_hal::{Blocking, interrupt};
use esp_hal_bounce_buffers::{DmaBounce, RunningDmaBounceHandle};
use esp_rtos::embassy::{Executor, InterruptExecutor};
use esp_storage::FlashStorage; use esp_storage::FlashStorage;
use indoc::writedoc;
use itertools::Itertools; use itertools::Itertools;
use log::{info, warn}; use log::{error, info, warn};
use rmk::channel::{CONTROLLER_CHANNEL, ControllerSub}; use rmk::channel::{CONTROLLER_CHANNEL, ControllerSub};
use rmk::config::{DeviceConfig, RmkConfig, StorageConfig, VialConfig}; use rmk::config::{
DeviceConfig, RmkConfig, StorageConfig,
VialConfig,
};
use rmk::controller::{Controller, EventController}; use rmk::controller::{Controller, EventController};
use rmk::debounce::default_debouncer::DefaultDebouncer; use rmk::debounce::default_debouncer::DefaultDebouncer;
use rmk::event::ControllerEvent; use rmk::event::ControllerEvent;
use rmk::hid::Report; use rmk::hid::Report;
use rmk::input_device::Runnable; use rmk::input_device::Runnable;
use rmk::join_all;
use rmk::keyboard::Keyboard; use rmk::keyboard::Keyboard;
use rmk::storage::async_flash_wrapper;
use rmk::types::action::{Action, KeyAction}; use rmk::types::action::{Action, KeyAction};
use rmk::join_all;
use rmk::{initialize_keymap_and_storage, run_devices, run_rmk}; use rmk::{initialize_keymap_and_storage, run_devices, run_rmk};
use slint::platform::software_renderer::Rgb565Pixel;
use static_cell::StaticCell; use static_cell::StaticCell;
use {esp_alloc as _, esp_backtrace as _}; use {esp_alloc as _, esp_backtrace as _};
use crate::dpi::{DisplayPeripherals, Framebuffer};
use crate::flash::Partition;
use crate::matrix::IoeMatrix; use crate::matrix::IoeMatrix;
use crate::peripherals::st7701s::St7701sController; use crate::peripherals::st7701s::St7701s;
use crate::proxy::create_hid_report_interceptor; use crate::proxy::create_hid_report_interceptor;
use crate::ram::{PSRAM_ALLOCATOR, STACK_SIZE_CORE_APP}; use crate::ui::backend::{FramebufferPtr, SlintBackend};
use crate::ui::backend::SlintBackend;
use crate::vial::{ use crate::vial::{
CustomKeycodes, VIAL_KEYBOARD_DEF, VIAL_KEYBOARD_ID, VIAL_KEYBOARD_NAME, VIAL_PRODUCT_ID, CustomKeycodes, VIAL_KEYBOARD_DEF, VIAL_KEYBOARD_ID, VIAL_KEYBOARD_NAME, VIAL_PRODUCT_ID,
VIAL_VENDOR_ID, VIAL_VENDOR_ID,
@ -79,14 +92,11 @@ mutually_exclusive_features::none_or_one_of!["usb-log", "alt-log", "rtt-log"];
mod config; mod config;
mod crypto; mod crypto;
mod db; mod db;
mod dpi;
mod ffi; mod ffi;
mod flash;
mod logging; mod logging;
mod matrix; mod matrix;
mod peripherals; mod peripherals;
mod proxy; mod proxy;
mod ram;
mod ui; mod ui;
mod util; mod util;
mod vial; mod vial;
@ -98,12 +108,29 @@ mod console;
// For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description> // For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
esp_bootloader_esp_idf::esp_app_desc!(); esp_bootloader_esp_idf::esp_app_desc!();
// Memory allocation regions.
// These can be debugged using `xtensa-esp32s3-elf-size -A <path-to-binary>`.
// A panic such as `memory allocation of 3740121773 bytes failed` is caused by a heap overflow. The size is `DEEDBAAD` in hex.
/// Total heap size
const HEAP_SIZE: usize = 112 * 1024;
/// Size of the app core's stack
const STACK_SIZE_CORE_APP: usize = 80 * 1024;
// const FRAME_DURATION_MIN: Duration = Duration::from_millis(40); // 25 FPS // const FRAME_DURATION_MIN: Duration = Duration::from_millis(40); // 25 FPS
const FRAME_DURATION_MIN: Duration = Duration::from_millis(100); // 10 FPS const FRAME_DURATION_MIN: Duration = Duration::from_millis(100); // 10 FPS
pub static PSRAM_ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();
static KEYBOARD_REPORT_PROXY: Channel<CriticalSectionRawMutex, Report, 16> = Channel::new(); static KEYBOARD_REPORT_PROXY: Channel<CriticalSectionRawMutex, Report, 16> = Channel::new();
static LCD_ENABLED: AtomicBool = AtomicBool::new(false); static LCD_ENABLED: AtomicBool = AtomicBool::new(false);
/// Used to signal that MCU is ready to submit the framebuffer to the LCD.
static SIGNAL_LCD_SUBMIT: Signal<CriticalSectionRawMutex, ()> = Signal::new();
/// Used to signal that the MCU is ready to render the GUI.
static SIGNAL_UI_RENDER: Signal<CriticalSectionRawMutex, ()> = Signal::new();
#[esp_rtos::main] #[esp_rtos::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let config = esp_hal::Config::default() let config = esp_hal::Config::default()
@ -111,34 +138,45 @@ async fn main(_spawner: Spawner) {
.with_psram(PsramConfig { .with_psram(PsramConfig {
size: PsramSize::AutoDetect, size: PsramSize::AutoDetect,
core_clock: Some(SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m), core_clock: Some(SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m),
flash_frequency: FlashFreq::FlashFreq120m, flash_frequency: FlashFreq::default(),
ram_frequency: SpiRamFreq::Freq120m, ram_frequency: SpiRamFreq::Freq80m,
}); });
let peripherals: esp_hal::peripherals::Peripherals = esp_hal::init(config); let peripherals: esp_hal::peripherals::Peripherals = esp_hal::init(config);
#[allow(unused)] #[cfg(feature = "usb-log")]
let (uart_rx, uart_tx) = { let console_task = logging::usb::setup_logging();
#[cfg(feature = "alt-log")] #[cfg(feature = "alt-log")]
let (tx, rx) = (peripherals.GPIO12, peripherals.GPIO5); let console_task =
#[cfg(not(feature = "alt-log"))] logging::uart::setup_logging(peripherals.UART2, peripherals.GPIO12, peripherals.GPIO5);
let (tx, rx) = (esp_hal::gpio::NoPin, esp_hal::gpio::NoPin); #[cfg(feature = "rtt-log")]
let console_task = logging::rtt::setup_logging();
Uart::new(peripherals.UART2, Default::default()) // Use the internal DRAM as the heap.
.unwrap() // Memory reclaimed from the esp-idf bootloader.
.with_tx(tx) const HEAP_SIZE_RECLAIMED: usize = const {
.with_rx(rx) let range = esp_metadata_generated::memory_range!("DRAM2_UNINIT");
.split() range.end - range.start
}; };
#[cfg(feature = "usb-log")] esp_alloc::heap_allocator!(#[ram(reclaimed)] size: HEAP_SIZE_RECLAIMED);
logging::usb::setup_logging(); esp_alloc::heap_allocator!(size: HEAP_SIZE - HEAP_SIZE_RECLAIMED);
#[cfg(feature = "alt-log")] info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats());
logging::uart::setup_logging(uart_tx);
#[cfg(feature = "rtt-log")]
logging::rtt::setup_logging();
// Set up allocators. // Initialize the PSRAM allocator.
ram::initialize(peripherals.PSRAM); {
let (psram_offset, psram_size) = esp_hal::psram::psram_raw_parts(&peripherals.PSRAM);
unsafe {
PSRAM_ALLOCATOR.add_region(HeapRegion::new(
psram_offset,
psram_size,
MemoryCapability::External.into(),
));
}
info!(
"PSRAM allocator initialized with capacity of {} MiB!",
psram_size / 1024 / 1024
);
}
// let mut io = Io::new(peripherals.IO_MUX); // let mut io = Io::new(peripherals.IO_MUX);
// io.set_interrupt_handler(interrupt_handler); // io.set_interrupt_handler(interrupt_handler);
@ -158,123 +196,21 @@ async fn main(_spawner: Spawner) {
// Enable antenna // Enable antenna
let _ = Output::new(peripherals.GPIO11, Level::Low, OutputConfig::default()); let _ = Output::new(peripherals.GPIO11, Level::Low, OutputConfig::default());
// TODO: Use PWM to control the pwm_pin.
let mut _pwm = McPwm::new(peripherals.MCPWM0, PeripheralClockConfig::with_prescaler(1));
let mut _pwm_pin = Output::new(peripherals.GPIO21, Level::High, OutputConfig::default());
let mut sha_backend = ShaBackend::new(peripherals.SHA); let mut sha_backend = ShaBackend::new(peripherals.SHA);
let _sha_driver_handle = sha_backend.start(); let _sha_driver_handle = sha_backend.start();
let timg0 = TimerGroup::new(peripherals.TIMG0); let timg0 = TimerGroup::new(peripherals.TIMG0);
let software_interrupt = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); let software_interrupt = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start( esp_rtos::start(
timg0.timer0, timg0.timer0, /*, software_interrupt.software_interrupt0 */
// software_interrupt.software_interrupt0,
); );
// A task executor that is able to handle interrupts, and then return back to executing tasks.
static EXECUTOR_CORE_0: StaticCell<InterruptExecutor<2>> = StaticCell::new();
let executor_core_0 = InterruptExecutor::new(software_interrupt.software_interrupt2);
let executor_core_0 = EXECUTOR_CORE_0.init(executor_core_0);
let interrupt_core_0_spawner = executor_core_0.start(interrupt::Priority::Priority1);
// static EXECUTOR_CORE_1: StaticCell<InterruptExecutor<3>> = StaticCell::new();
// let executor_core_1 = InterruptExecutor::new(software_interrupt.software_interrupt3);
// let executor_core_1 = EXECUTOR_CORE_1.init(executor_core_1);
// let interrupt_core_1_spawner = executor_core_1.start(interrupt::Priority::Priority2);
info!("ESP-RTOS started!"); info!("ESP-RTOS started!");
let main_task_peripherals = MainPeripherals {
// high_priority_task_spawner: interrupt_core_1_spawner,
uart_rx,
software_interrupt1: software_interrupt.software_interrupt1,
software_interrupt0: software_interrupt.software_interrupt0,
RNG: peripherals.RNG,
ADC1: peripherals.ADC1,
USB0: peripherals.USB0,
FLASH: peripherals.FLASH,
DMA_CH0: peripherals.DMA_CH0,
SPI2: peripherals.SPI2,
CPU_CTRL: peripherals.CPU_CTRL,
GPIO19: peripherals.GPIO19,
GPIO20: peripherals.GPIO20,
display: DisplayPeripherals {
DMA_CH2: peripherals.DMA_CH2,
LCD_CAM: peripherals.LCD_CAM,
LEDC: peripherals.LEDC,
GPIO0: gpio0,
GPIO1: peripherals.GPIO1,
GPIO2: peripherals.GPIO2,
GPIO3: peripherals.GPIO3,
GPIO4: peripherals.GPIO4,
#[cfg(not(feature = "alt-log"))]
GPIO5: peripherals.GPIO5,
GPIO6: peripherals.GPIO6,
#[cfg(not(feature = "alt-log"))]
GPIO12: peripherals.GPIO12,
GPIO13: peripherals.GPIO13,
GPIO14: peripherals.GPIO14,
GPIO15: peripherals.GPIO15,
GPIO16: peripherals.GPIO16,
GPIO21: peripherals.GPIO21,
GPIO34: peripherals.GPIO34,
GPIO35: peripherals.GPIO35,
GPIO36: peripherals.GPIO36,
GPIO37: peripherals.GPIO37,
GPIO38: peripherals.GPIO38,
GPIO39: peripherals.GPIO39,
GPIO40: peripherals.GPIO40,
GPIO41: peripherals.GPIO41,
GPIO42: peripherals.GPIO42,
GPIO43: peripherals.GPIO43,
GPIO44: peripherals.GPIO44,
},
matrix: MatrixPeripherals {
I2C0: peripherals.I2C0,
GPIO7: peripherals.GPIO7,
GPIO8: peripherals.GPIO8,
GPIO9: peripherals.GPIO9,
},
};
interrupt_core_0_spawner.must_spawn(main_task(main_task_peripherals));
}
/// Peripherals passed to the main task.
#[allow(non_snake_case)]
struct MainPeripherals {
uart_rx: UartRx<'static, Blocking>,
software_interrupt1: SoftwareInterrupt<'static, 1>,
software_interrupt0: SoftwareInterrupt<'static, 0>,
RNG: esp_hal::peripherals::RNG<'static>,
ADC1: esp_hal::peripherals::ADC1<'static>,
USB0: esp_hal::peripherals::USB0<'static>,
FLASH: esp_hal::peripherals::FLASH<'static>,
DMA_CH0: esp_hal::peripherals::DMA_CH0<'static>,
SPI2: esp_hal::peripherals::SPI2<'static>,
CPU_CTRL: esp_hal::peripherals::CPU_CTRL<'static>,
// GPIO10: esp_hal::peripherals::GPIO10<'static>,
// GPIO18: esp_hal::peripherals::GPIO18<'static>,
GPIO19: esp_hal::peripherals::GPIO19<'static>,
GPIO20: esp_hal::peripherals::GPIO20<'static>,
// GPIO33: esp_hal::peripherals::GPIO33<'static>,
// GPIO45: esp_hal::peripherals::GPIO45<'static>,
// GPIO46: esp_hal::peripherals::GPIO46<'static>,
// GPIO47: esp_hal::peripherals::GPIO47<'static>,
// GPIO48: esp_hal::peripherals::GPIO48<'static>,
display: DisplayPeripherals,
matrix: MatrixPeripherals,
}
#[allow(non_snake_case)]
struct MatrixPeripherals {
I2C0: esp_hal::peripherals::I2C0<'static>,
GPIO7: esp_hal::peripherals::GPIO7<'static>,
GPIO8: esp_hal::peripherals::GPIO8<'static>,
GPIO9: esp_hal::peripherals::GPIO9<'static>,
}
#[embassy_executor::task]
async fn main_task(peripherals: MainPeripherals) {
// let _spawner = unsafe { Spawner::for_current_executor() }.await;
// Enable the TRNG source, so `Trng` can be constructed. // Enable the TRNG source, so `Trng` can be constructed.
let _trng_source = TrngSource::new(peripherals.RNG, peripherals.ADC1); let _trng_source = TrngSource::new(peripherals.RNG, peripherals.ADC1);
@ -320,13 +256,131 @@ async fn main_task(peripherals: MainPeripherals) {
}; };
// Initialize the flash // Initialize the flash
let flash_partitions = flash::initialize(peripherals.FLASH); static PARTITION_TABLE_BUFFER: StaticCell<Vec<u8, &'static esp_alloc::EspHeap>> =
StaticCell::new();
let partition_table_buffer = PARTITION_TABLE_BUFFER.init_with(|| {
let mut buffer = Vec::<u8, _>::new_in(&PSRAM_ALLOCATOR);
buffer.resize(1024, 0_u8);
buffer
});
static FLASH: StaticCell<(
Mutex<CriticalSectionRawMutex, BlockingAsync<FlashStorage>>,
PartitionTable<'static>,
)> = StaticCell::new();
let (flash, partition_table) = FLASH.init_with(|| {
let mut flash = FlashStorage::new(peripherals.FLASH)
// Flash memory may not be written to while another core is executing from it.
// By default, `FlashStorage` is configured to abort the operation and log an error message.
// However, it can also be configured to auto-park the other core, such that writing to
// flash succeeds.
// Alternatively, XiP from PSRAM could be used along with the `multicore_ignore` strategy,
// to avoid having to park the other core, which could result in better performance.
// Invalid configuration would then present itself as freezing/UB.
.multicore_auto_park();
let partition_table = {
esp_bootloader_esp_idf::partitions::read_partition_table(
&mut flash,
partition_table_buffer,
)
.expect("Failed to read the partition table.")
};
(
Mutex::<CriticalSectionRawMutex, _>::new(async_flash_wrapper(flash)),
partition_table,
)
});
{
let mut buffer = String::new();
writeln!(buffer, "Partition table:").unwrap();
for (index, partition) in partition_table.iter().enumerate() {
writedoc!(
buffer,
"
Partition #{index} {label:?}:
offset: 0x{offset:x}
length: 0x{len:x}
type: 0x{type:?}
read only: {read_only}
encrypted: {encrypted}
magic: {magic}
",
label = partition.label_as_str(),
offset = partition.offset(),
len = partition.len(),
type = partition.partition_type(),
read_only = partition.is_read_only(),
encrypted = partition.is_encrypted(),
magic = partition.magic(),
)
.unwrap();
}
info!("{}", buffer);
}
let flash_part_info_rmk = partition_table
.iter()
.find(|partition| partition.label_as_str() == "rmk")
.expect("No \"rmk\" partition found. Make sure to use the custom partition-table.csv when flashing.");
let flash_part_info_acid = partition_table
.iter()
.find(|partition| partition.label_as_str() == "acid")
.expect("No \"acid\" partition found. Make sure to use the custom partition-table.csv when flashing.");
let flash_part_rmk = Partition::new(
flash,
flash_part_info_rmk.offset(),
flash_part_info_rmk.len(),
);
let flash_part_acid = Partition::new(
flash,
flash_part_info_acid.offset(),
flash_part_info_acid.len(),
);
info!("Flash memory configured!"); info!("Flash memory configured!");
// Uncomment this to run bounce buffer test code instead. let sck = Output::new(peripherals.GPIO36, Level::High, OutputConfig::default());
// dpi::test_bounce_buffers(peripherals.DMA_CH0, peripherals.SPI2, peripherals.display).await; let mosi = Flex::new(peripherals.GPIO35);
// return; let cs = Output::new(peripherals.GPIO6, Level::High, OutputConfig::default());
let lcd = LcdCam::new(peripherals.LCD_CAM).lcd;
let unconfigured_dpi = Dpi::new(lcd, peripherals.DMA_CH2, Default::default())
.unwrap()
.with_de(peripherals.GPIO37)
.with_pclk(peripherals.GPIO34)
.with_hsync(peripherals.GPIO44)
.with_vsync(peripherals.GPIO43)
// Blue
.with_data0(peripherals.GPIO38)
.with_data1(peripherals.GPIO39)
.with_data2(peripherals.GPIO40)
.with_data3(peripherals.GPIO41)
.with_data4(peripherals.GPIO42)
// Green
.with_data7(peripherals.GPIO13)
.with_data8(peripherals.GPIO14)
.with_data9(peripherals.GPIO15)
.with_data10(peripherals.GPIO16)
// Red
.with_data11(gpio0)
.with_data12(peripherals.GPIO1)
.with_data13(peripherals.GPIO2)
.with_data14(peripherals.GPIO3)
.with_data15(peripherals.GPIO4);
#[cfg(not(feature = "alt-log"))]
let unconfigured_dpi = unconfigured_dpi
// Green
.with_data5(peripherals.GPIO5)
.with_data6(peripherals.GPIO12);
let st7701s = St7701s::new(sck, mosi, cs, unconfigured_dpi).await;
info!("ST7701S-based LCD display initialized!");
// RMK config // RMK config
let vial_config = VialConfig::new(VIAL_KEYBOARD_ID, VIAL_KEYBOARD_DEF, &[(0, 0), (1, 1)]); let vial_config = VialConfig::new(VIAL_KEYBOARD_ID, VIAL_KEYBOARD_DEF, &[(0, 0), (1, 1)]);
@ -334,12 +388,12 @@ async fn main_task(peripherals: MainPeripherals) {
start_addr: 0, start_addr: 0,
num_sectors: { num_sectors: {
assert!( assert!(
flash_partitions.rmk.size() % FlashStorage::SECTOR_SIZE == 0, flash_part_info_rmk.len() % FlashStorage::SECTOR_SIZE == 0,
"The size of the RMK partition must be a multiple of {} bytes. Current size: {}", "The size of the RMK partition must be a multiple of {} bytes. Current size: {}",
FlashStorage::SECTOR_SIZE, FlashStorage::SECTOR_SIZE,
flash_partitions.rmk.size() flash_part_info_rmk.len()
); );
(flash_partitions.rmk.size() / FlashStorage::SECTOR_SIZE) as u8 (flash_part_info_rmk.len() / FlashStorage::SECTOR_SIZE) as u8
}, },
..Default::default() ..Default::default()
}; };
@ -376,7 +430,7 @@ async fn main_task(peripherals: MainPeripherals) {
let mut positional_config = config::get_positional_config(); let mut positional_config = config::get_positional_config();
let (keymap, mut storage) = initialize_keymap_and_storage( let (keymap, mut storage) = initialize_keymap_and_storage(
&mut default_keymap, &mut default_keymap,
flash_partitions.rmk, flash_part_rmk,
&storage_config, &storage_config,
&mut behavior_config, &mut behavior_config,
&mut positional_config, &mut positional_config,
@ -385,15 +439,85 @@ async fn main_task(peripherals: MainPeripherals) {
info!("Initialized keymap and storage for RMK!"); info!("Initialized keymap and storage for RMK!");
// Initialize the matrix and keyboard
const I2C_ADDR_MATRIX_LEFT: I2cAddress = I2cAddress::SevenBit(0b0100000);
const I2C_ADDR_MATRIX_RIGHT: I2cAddress = I2cAddress::SevenBit(0b0100001);
let i2c = I2c::new(peripherals.I2C0, Default::default())
.unwrap()
.with_sda(peripherals.GPIO8)
.with_scl(peripherals.GPIO9);
let matrix_interrupt_low = Input::new(peripherals.GPIO7, InputConfig::default());
let mut matrix = IoeMatrix::new(
matrix_interrupt_low,
i2c.into_async(),
DefaultDebouncer::new(),
[I2C_ADDR_MATRIX_LEFT, I2C_ADDR_MATRIX_RIGHT],
)
.await;
let mut keyboard = Keyboard::new(&keymap); // Initialize the light controller let mut keyboard = Keyboard::new(&keymap); // Initialize the light controller
info!("Keyboard initialized!"); info!("Keyboard initialized!");
static FRAMEBUFFER: StaticCell<Framebuffer> = StaticCell::new();
let framebuffer = FRAMEBUFFER.init(Framebuffer::new(
360 + /* TODO: Figure out why more bytes are needed: */ 8,
960,
));
info!("Framebuffer created!");
// let window_size = [framebuffer.width, framebuffer.height];
let window_size = [framebuffer.height, framebuffer.width];
let framebuffer_ptr = FramebufferPtr(framebuffer.as_target_pixels() as _);
static SECOND_CORE_STACK: StaticCell<Stack<STACK_SIZE_CORE_APP>> = StaticCell::new();
let second_core_stack = SECOND_CORE_STACK.init(Stack::new());
esp_rtos::start_second_core(
peripherals.CPU_CTRL,
software_interrupt.software_interrupt0,
software_interrupt.software_interrupt1,
second_core_stack,
move || {
// static EXECUTOR: StaticCell<InterruptExecutor<2>> = StaticCell::new();
// let exec = EXECUTOR.init(InterruptExecutor::new(
// software_interrupt.software_interrupt2,
// ));
// let spawner = exec.start(Priority::Priority3);
// spawner.must_spawn(run_renderer_task());
static EXECUTOR: StaticCell<Executor> = StaticCell::new();
let executor: &mut Executor = EXECUTOR.init(Executor::new());
executor.run(|spawner| {
let slint_backend = SlintBackend {
// peripherals: RefCell::new(Some(peripherals)),
window_size,
window: RefCell::new(None),
framebuffer: framebuffer_ptr,
quit_event_loop: Default::default(),
events: Arc::new(critical_section::Mutex::new(RefCell::new(VecDeque::new()))),
};
spawner.must_spawn(ui::run_renderer_task(slint_backend, flash_part_acid));
});
},
);
info!("Second core started!");
let mut user_controller = UserController::new();
info!("Awaiting on all tasks..."); info!("Awaiting on all tasks...");
// TODO: Probably want to select! instead and re-try. // TODO: Probably want to select! instead and re-try.
join_all![ join_all![
ram::run_alloc_stats_reporter(), run_alloc_stats_reporter(),
initialize_and_run_rmk_devices(peripherals.matrix), // We currently send the framebuffer data using the main core, which does not seem to slow
// down the rest of the tasks too much.
run_lcd(st7701s, framebuffer),
run_devices! (
(matrix) => rmk::channel::EVENT_CHANNEL,
),
keyboard.run(), // Keyboard is special keyboard.run(), // Keyboard is special
run_rmk( run_rmk(
&keymap, &keymap,
@ -405,180 +529,53 @@ async fn main_task(peripherals: MainPeripherals) {
rmk_config, rmk_config,
), ),
create_hid_report_interceptor(), create_hid_report_interceptor(),
initialize_display_and_renderer( user_controller.event_loop(),
peripherals.display, console_task
peripherals.DMA_CH0,
peripherals.SPI2,
peripherals.CPU_CTRL,
peripherals.software_interrupt0,
peripherals.software_interrupt1,
flash_partitions.acid
),
console::run_console(peripherals.uart_rx.into_async())
] ]
.await; .await;
} }
async fn initialize_and_run_rmk_devices(matrix_peripherals: MatrixPeripherals) { async fn run_alloc_stats_reporter() {
// Initialize the matrix and keyboard let mut psram_used_prev = 0;
const I2C_ADDR_MATRIX_LEFT: I2cAddress = I2cAddress::SevenBit(0b0100000); let mut heap_used_prev = 0;
const I2C_ADDR_MATRIX_RIGHT: I2cAddress = I2cAddress::SevenBit(0b0100001); loop {
let psram_stats = PSRAM_ALLOCATOR.stats();
let i2c = I2c::new(matrix_peripherals.I2C0, Default::default()) let heap_stats = esp_alloc::HEAP.stats();
.unwrap() if psram_stats.current_usage != psram_used_prev {
.with_sda(matrix_peripherals.GPIO8) let difference = psram_stats.current_usage as isize - psram_used_prev as isize;
.with_scl(matrix_peripherals.GPIO9); psram_used_prev = psram_stats.current_usage;
let matrix_interrupt_low = Input::new(matrix_peripherals.GPIO7, InputConfig::default()); warn!(
let mut matrix = IoeMatrix::new( "PSRAM usage changed: {}{}\n{psram_stats}",
matrix_interrupt_low, if difference < 0 { '-' } else { '+' },
i2c.into_async(), difference.abs()
DefaultDebouncer::new(), );
[I2C_ADDR_MATRIX_LEFT, I2C_ADDR_MATRIX_RIGHT],
)
.await;
run_devices! (
(matrix) => rmk::channel::EVENT_CHANNEL,
)
.await
}
async fn initialize_display_and_renderer(
display_peripherals: DisplayPeripherals,
dma_ch0: esp_hal::peripherals::DMA_CH0<'static>,
spi2: esp_hal::peripherals::SPI2<'static>,
cpu_ctrl: esp_hal::peripherals::CPU_CTRL<'static>,
software_interrupt0: SoftwareInterrupt<'static, 0>,
software_interrupt1: SoftwareInterrupt<'static, 1>,
partition_acid: Partition,
) {
let st7701s = display_peripherals.into_display().await;
static FRAMEBUFFER: StaticCell<Framebuffer> = StaticCell::new();
let framebuffer = FRAMEBUFFER.init(Framebuffer::new(
dma_ch0,
spi2.into(),
st7701s.dpi,
BurstConfig {
internal_memory: InternalBurstConfig::Enabled,
external_memory: ExternalBurstConfig::Size32,
},
// The burst config (16/32/64) doesn't seem to affect the alignment of the row size.
//
// | | ( displayed range ) |
// | [ pad ] [ pad ]
// | [ DMA-transmissible range ]
// [ DMA-t. left overscan ] | | |
// 0 112 120 360 368 (index of u16 pixel)
// ^ aligned ^ aligned ^ aligned
//
// TODO: Compute the appropriate ranges to pass to the renderer and DPI peripheral.
// The renderer should pass the size of the `pad`ding to the GUI is parameters,
// to align the content to the displayed range.
112,
368 - 112,
960,
16,
true,
));
info!("Framebuffer created!");
let window_size = [framebuffer.height, framebuffer.width];
let swapchain_writer = framebuffer.swapchain.take().unwrap();
static SECOND_CORE_STACK: StaticCell<Stack<{ STACK_SIZE_CORE_APP }>> = StaticCell::new();
let second_core_stack = SECOND_CORE_STACK.init(Stack::new());
esp_rtos::start_second_core(
cpu_ctrl,
software_interrupt0,
software_interrupt1,
second_core_stack,
move || {
// static EXECUTOR: StaticCell<InterruptExecutor<2>> = StaticCell::new();
// let exec = EXECUTOR.init(InterruptExecutor::new(
// peripherals.software_interrupt2,
// ));
// let spawner = exec.start(Priority::Priority3);
// spawner.must_spawn(run_renderer_task());
static EXECUTOR: StaticCell<Executor> = StaticCell::new();
let executor: &mut Executor = EXECUTOR.init(Executor::new());
executor.run(|spawner| {
let slint_backend = SlintBackend {
// peripherals: RefCell::new(Some(peripherals)),
window_size,
window: RefCell::new(None),
swapchain: RefCell::new(swapchain_writer),
quit_event_loop: Default::default(),
events: Arc::new(blocking_mutex::Mutex::new(RefCell::new(VecDeque::new()))),
};
spawner.must_spawn(ui::run_renderer_task(slint_backend, partition_acid));
});
},
);
info!("Second core started!");
let bb_controller = DmaBounceController::new(framebuffer.bounce_buffers.take().unwrap());
let mut user_controller = UserController::new(st7701s.controller, bb_controller);
user_controller.event_loop().await
}
enum DmaBounceControllerInner {
Idle(DmaBounce),
Transmitting(RunningDmaBounceHandle),
}
struct DmaBounceController {
inner: Option<DmaBounceControllerInner>,
}
impl DmaBounceController {
pub fn new(dma_bounce: DmaBounce) -> Self {
Self {
inner: Some(DmaBounceControllerInner::Idle(dma_bounce)),
} }
} if heap_stats.current_usage != heap_used_prev {
let difference = heap_stats.current_usage as isize - heap_used_prev as isize;
pub async fn start(&mut self) -> Result<(), ()> { heap_used_prev = heap_stats.current_usage;
let DmaBounceControllerInner::Idle(dma_bounce) = self.inner.take().unwrap() else { warn!(
return Err(()); "HEAP usage changed: {}{}\n{heap_stats}",
}; if difference < 0 { '-' } else { '+' },
self.inner = Some(DmaBounceControllerInner::Transmitting( difference.abs()
dma_bounce.launch_interrupt_driven_task().await, );
)); }
Ok(()) Timer::after_secs(1).await;
}
pub async fn stop(&mut self) -> Result<(), ()> {
let DmaBounceControllerInner::Transmitting(running_dma_bounce) = self.inner.take().unwrap()
else {
return Err(());
};
self.inner = Some(DmaBounceControllerInner::Idle(
running_dma_bounce.stop().await,
));
Ok(())
} }
} }
struct UserController<'a> { struct UserController {
sub: ControllerSub, sub: ControllerSub,
lcd_controller: St7701sController<'a>,
bb_controller: DmaBounceController,
} }
impl<'a> UserController<'a> { impl UserController {
fn new(lcd_controller: St7701sController<'a>, bb_controller: DmaBounceController) -> Self { fn new() -> Self {
Self { Self {
sub: CONTROLLER_CHANNEL.subscriber().unwrap(), sub: CONTROLLER_CHANNEL.subscriber().unwrap(),
lcd_controller,
bb_controller,
} }
} }
} }
impl<'a> Controller for UserController<'a> { impl Controller for UserController {
type Event = ControllerEvent; type Event = ControllerEvent;
async fn process_event(&mut self, event: Self::Event) { async fn process_event(&mut self, event: Self::Event) {
@ -594,14 +591,10 @@ impl<'a> Controller for UserController<'a> {
info!("Disabling LCD."); info!("Disabling LCD.");
*rmk::channel::KEYBOARD_REPORT_SENDER.write().await = *rmk::channel::KEYBOARD_REPORT_SENDER.write().await =
&rmk::channel::KEYBOARD_REPORT_RECEIVER; &rmk::channel::KEYBOARD_REPORT_RECEIVER;
self.lcd_controller.sleep_on().await;
self.bb_controller.stop().await.unwrap();
} }
true => { true => {
info!("Enabling LCD."); info!("Enabling LCD.");
*rmk::channel::KEYBOARD_REPORT_SENDER.write().await = &KEYBOARD_REPORT_PROXY; *rmk::channel::KEYBOARD_REPORT_SENDER.write().await = &KEYBOARD_REPORT_PROXY;
self.bb_controller.start().await.unwrap();
self.lcd_controller.sleep_off().await;
} }
} }
} }
@ -612,9 +605,109 @@ impl<'a> Controller for UserController<'a> {
} }
} }
struct Framebuffer {
width: u32,
height: u32,
dma_buf: Option<DmaTxBuf>,
}
impl Framebuffer {
pub fn new(width: u32, height: u32) -> Self {
let buffer_len = width as usize * height as usize * core::mem::size_of::<u16>();
// Allocate the framebuffer in the external PSRAM memory.
// Note: We just leak this buffer.
let buffer_ptr = unsafe {
// ⚠️ Note: For chips that support DMA to/from PSRAM (ESP32-S3) DMA transfers to/from PSRAM
// have extra alignment requirements. The address and size of the buffer pointed to by each
// descriptor must be a multiple of the cache line (block) size. This is 32 bytes on ESP32-S3.
PSRAM_ALLOCATOR.alloc_caps(
MemoryCapability::External.into(),
Layout::from_size_align(buffer_len, 32).unwrap(),
)
};
let buffer = unsafe { core::slice::from_raw_parts_mut(buffer_ptr, buffer_len) };
let burst_config: BurstConfig = ExternalBurstConfig::Size16.into();
info!(
"PSRAM SPI burst config: max_compatible_chunk_size={}",
burst_config.max_compatible_chunk_size()
);
let dma_buf_descs_len = esp_hal::dma::descriptor_count(
buffer_len,
burst_config.max_compatible_chunk_size(),
false,
);
// Descriptors are initialized by `DmaTxBuf::new`.
let dma_buf_descs =
Box::leak(vec![DmaDescriptor::EMPTY; dma_buf_descs_len].into_boxed_slice());
let dma_buf = DmaTxBuf::new(dma_buf_descs, buffer).unwrap();
Self {
width,
height,
dma_buf: Some(dma_buf),
}
}
pub fn as_target_pixels(&mut self) -> &mut [Rgb565Pixel] {
bytemuck::cast_slice_mut::<_, Rgb565Pixel>(self.dma_buf.as_mut().unwrap().as_mut_slice())
}
}
#[embassy_executor::task]
async fn run_lcd_task(st7701s: St7701s<'static, Blocking>, framebuffer: &'static mut Framebuffer) {
run_lcd(st7701s, framebuffer).await
}
async fn run_lcd(mut st7701s: St7701s<'static, Blocking>, framebuffer: &'static mut Framebuffer) {
loop {
// Timer::after(Duration::from_millis(100)).await;
// yield_now().await;
SIGNAL_LCD_SUBMIT.wait().await;
// TODO: Use bounce buffers:
// https://docs.espressif.com/projects/esp-idf/en/v5.0/esp32s3/api-reference/peripherals/lcd.html#bounce-buffer-with-single-psram-frame-buffer
// This can be implemented as a `DmaTxBuffer`.
let transfer = match st7701s.dpi.send(false, framebuffer.dma_buf.take().unwrap()) {
Err((error, result_dpi, result_dma_buf)) => {
error!(
"An error occurred while initiating transfer of the framebuffer to the LCD display: {error:?}"
);
st7701s.dpi = result_dpi;
framebuffer.dma_buf = Some(result_dma_buf);
continue;
}
Ok(transfer) => transfer,
};
// This could be used to allow other tasks to be executed on the first core, but that causes
// the flash to be accessed, which interferes with the framebuffer transfer.
// For that reason, it is disabled, and this task blocks the first core, until the transfer
// is complete.
#[cfg(not(feature = "limit-fps"))]
while !transfer.is_done() {
// Timer::after_millis(1).await;
rmk::embassy_futures::yield_now().await;
}
let result;
let dma_buf;
(result, st7701s.dpi, dma_buf) = transfer.wait();
framebuffer.dma_buf = Some(dma_buf);
SIGNAL_UI_RENDER.signal(());
if let Err(error) = result {
error!(
"An error occurred while transferring framebuffer to the LCD display: {error:?}"
);
}
}
}
// // TODO: Not needed currently. If it is ever enabled, don't forget to register it in Io. // // TODO: Not needed currently. If it is ever enabled, don't forget to register it in Io.
// #[handler] // #[handler]
// #[ram] // Improves performance. // #[ram] // TODO: Is this necessary?
// fn interrupt_handler() { // fn interrupt_handler() {
// // esp_println::println!( // // esp_println::println!(
// // "GPIO Interrupt with priority {}", // // "GPIO Interrupt with priority {}",

File diff suppressed because it is too large Load diff

View file

@ -1,405 +0,0 @@
use tinyvec::ArrayVec;
use crate::peripherals::st7701s::commands::*;
pub enum InitSequenceAction {
// TODO: Each command takes up 17 bytes. Consider storing compressed.
Command(ArrayCommand<16>),
SleepMs(u64),
}
macro_rules! make_init_action {
(command: $command:expr$(,)?) => {
InitSequenceAction::Command({
let command = $command;
let args_slice = command.args_slice();
let mut args_array = [0; _];
let mut args_len = 0;
while args_len < args_slice.len() {
args_array[args_len] = args_slice[args_len];
args_len += 1;
}
ArrayCommand {
address: command.address_const(),
args: match ArrayVec::try_from_array_len(args_array, args_len) {
Ok(args) => args,
Err(_) => panic!("too many command args"),
},
}
})
};
(sleep_ms: $expr:expr) => {
InitSequenceAction::SleepMs($expr)
};
}
macro_rules! make_init_sequence {
($({ $($tt:tt)* }),*$(,)?) => {
[
$( make_init_action!($($tt)*) ),*
]
};
}
pub const INIT_SEQUENCE_COMMANDS: [InitSequenceAction; 46] = make_init_sequence! [
{ command: CmdCn2bkxsel(
CmdCn2bkxselArg0::new(),
CmdCn2bkxselArg1::new(),
CmdCn2bkxselArg2::new(),
CmdCn2bkxselArg3::new(),
CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true),
) },
{ command: CustomCommand {
address: 0xEF,
args: &[0x08],
} },
{ command: CmdCn2bkxsel(
CmdCn2bkxselArg0::new(),
CmdCn2bkxselArg1::new(),
CmdCn2bkxselArg2::new(),
CmdCn2bkxselArg3::new(),
CmdCn2bkxselArg4::new().with_bksel(0x0).with_cn2(true),
) },
{ command: CmdLneset(
CmdLnesetArg0::new().with_bar(119).with_lde_en(false),
CmdLnesetArg1::new().with_line_delta(0),
) },
{ command: CmdPorctrl(
CmdPorctrlArg0::new().with_vbp(17),
CmdPorctrlArg1::new().with_vfp(12),
) },
{ command: CmdInvsel(
CmdInvselArg0::new().with_nlinv(0b111),
CmdInvselArg1::new().with_rtni(2),
) },
{ command: CustomCommand {
address: 0xCC,
args: &[0x30],
} },
{ command: CmdPvgamctrl(
CmdPvgamctrlArg0::new().with_vc0p(6).with_aj0p(0),
CmdPvgamctrlArg1::new().with_vc4p(15).with_aj1p(3),
CmdPvgamctrlArg2::new().with_vc8p(14).with_aj2p(0),
CmdPvgamctrlArg3::new().with_vc16p(12),
CmdPvgamctrlArg4::new().with_vc24p(15).with_aj3p(0),
CmdPvgamctrlArg5::new().with_vc52p(3),
CmdPvgamctrlArg6::new().with_vc80p(0),
CmdPvgamctrlArg7::new().with_vc108p(10),
CmdPvgamctrlArg8::new().with_vc147p(7),
CmdPvgamctrlArg9::new().with_vc175p(27),
CmdPvgamctrlArg10::new().with_vc203p(3),
CmdPvgamctrlArg11::new().with_vc231p(18).with_aj4p(0),
CmdPvgamctrlArg12::new().with_vc239p(16),
CmdPvgamctrlArg13::new().with_vc247p(37).with_aj5p(0),
CmdPvgamctrlArg14::new().with_vc251p(54).with_aj6p(0),
CmdPvgamctrlArg15::new().with_vc255p(30).with_aj7p(0),
) },
{ command: CmdNvgamctrl(
CmdNvgamctrlArg0::new().with_vc0n(12).with_aj0n(0),
CmdNvgamctrlArg1::new().with_vc4n(14).with_aj1n(3),
CmdNvgamctrlArg2::new().with_vc8n(18).with_aj2n(0),
CmdNvgamctrlArg3::new().with_vc16n(12),
CmdNvgamctrlArg4::new().with_vc24n(14).with_aj3n(0),
CmdNvgamctrlArg5::new().with_vc52n(6),
CmdNvgamctrlArg6::new().with_vc80n(3),
CmdNvgamctrlArg7::new().with_vc108n(6),
CmdNvgamctrlArg8::new().with_vc147n(8),
CmdNvgamctrlArg9::new().with_vc175n(35),
CmdNvgamctrlArg10::new().with_vc203n(6),
CmdNvgamctrlArg11::new().with_vc231n(18).with_aj4n(0),
CmdNvgamctrlArg12::new().with_vc239n(16),
CmdNvgamctrlArg13::new().with_vc247n(48).with_aj5n(0),
CmdNvgamctrlArg14::new().with_vc251n(47).with_aj6n(0),
CmdNvgamctrlArg15::new().with_vc255n(31).with_aj7n(0),
) },
{ command: CmdCn2bkxsel(
CmdCn2bkxselArg0::new(),
CmdCn2bkxselArg1::new(),
CmdCn2bkxselArg2::new(),
CmdCn2bkxselArg3::new(),
CmdCn2bkxselArg4::new().with_bksel(0x1).with_cn2(true),
) },
{ command: CmdVrhs(
CmdVrhsArg0::new().with_vrha(115),
) },
{ command: CmdVcoms(
CmdVcomsArg0::new().with_vcom(124),
) },
{ command: CmdVghss(
// The first bit is set to 1 in the original init code, but not here.
CmdVghssArg0::new().with_vghss(0x3), // 13 V
) },
{ command: CmdTescmd(
CmdTescmdArg0::new(),
) },
{ command: CmdVgls(
CmdVglsArg0::new().with_vgls(0x9) // -10.17 V
) },
{ command: CmdPwctrl1(
CmdPwctrl1Arg0::new()
.with_apos(0x3) // Max
.with_apis(0x1) // Min
.with_ap(0x2) // Middle
) },
{ command: CmdPwctrl2(
CmdPwctrl2Arg0::new()
.with_avcl(0x3) // -5 V
.with_avdd(0x3) // 6.8 V
) },
{ command: CmdPwctrl3(
CmdPwctrl3Arg0::new()
.with_svno_pum(0) // Cell setting 4
.with_svpo_pum(0x1) // Cell setting 5
) },
{ command: CmdPclks2(
CmdPclks2Arg0::new().with_sbstcks(0x3)
) },
{ command: CmdPdr1(
CmdPdr1Arg0::new().with_t2d(8) // 1.6 us
) },
{ command: CmdPdr2(
CmdPdr2Arg0::new().with_t3d(8) // 6.4 us
) },
{ command: CmdMipiset1(
CmdMipiset1Arg0::new().with_err_sel(0).with_eotp_en(true),
) },
{ command: CustomCommand {
address: 0xE0,
args: &[
0x00,
0x00,
0x02,
0x00,
0x00,
0x0C,
]
} },
{ command: CustomCommand {
address: 0xE1,
args: &[
0x05,
0x96,
0x07,
0x96,
0x06,
0x96,
0x08,
0x96,
0x00,
0x44,
0x44,
]
} },
{ command: CustomCommand {
address: 0xE2,
args: &[
0x00,
0x00,
0x03,
0x03,
0x00,
0x00,
0x02,
0x00,
0x00,
0x00,
0x02,
0x00,
]
} },
{ command: CustomCommand {
address: 0xE3,
args: &[
0x00,
0x00,
0x33,
0x33,
]
} },
{ command: CustomCommand {
address: 0xE4,
args: &[
0x44,
0x44,
]
} },
{ command: CustomCommand {
address: 0xE5,
args: &[
0x0D,
0xD4,
0x28,
0x8C,
0x0F,
0xD6,
0x28,
0x8C,
0x09,
0xD0,
0x28,
0x8C,
0x0B,
0xD2,
0x28,
0x8C,
]
} },
{ command: CustomCommand {
address: 0xE6,
args: &[
0x00,
0x00,
0x33,
0x33,
]
} },
{ command: CustomCommand {
address: 0xE7,
args: &[
0x44,
0x44,
]
} },
{ command: CustomCommand {
address: 0xE8,
args: &[
0x0E,
0xD5,
0x28,
0x8C,
0x10,
0xD7,
0x28,
0x8C,
0x0A,
0xD1,
0x28,
0x8C,
0x0C,
0xD3,
0x28,
0x8C,
]
} },
{ command: CustomCommand {
address: 0xEB,
args: &[
0x00,
0x01,
0xE4,
0xE4,
0x44,
0x00,
]
} },
{ command: CustomCommand {
address: 0xED,
args: &[
0xF3,
0xC1,
0xBA,
0x0F,
0x66,
0x77,
0x44,
0x55,
0x55,
0x44,
0x77,
0x66,
0xF0,
0xAB,
0x1C,
0x3F,
]
} },
{ command: CustomCommand {
address: 0xEF,
args: &[
0x10,
0x0D,
0x04,
0x08,
0x3F,
0x1F,
]
} },
{ command: CmdCn2bkxsel(
CmdCn2bkxselArg0::new(),
CmdCn2bkxselArg1::new(),
CmdCn2bkxselArg2::new(),
CmdCn2bkxselArg3::new(),
CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true),
) },
{ command: CustomCommand {
address: 0xE8,
args: &[
0x00,
0x0E,
],
} },
{ command: CmdSlpout() },
{ sleep_ms: 120 },
{ command: CustomCommand {
address: 0xE8,
args: &[
0x00,
0x0C,
],
} },
{ sleep_ms: 10 },
{ command: CustomCommand {
address: 0xE8,
args: &[
0x40,
0x00,
],
} },
{ command: CmdCn2bkxsel(
CmdCn2bkxselArg0::new(),
CmdCn2bkxselArg1::new(),
CmdCn2bkxselArg2::new(),
CmdCn2bkxselArg3::new(),
CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(false),
) },
{ command: CmdMadctl(
CmdMadctlArg0::new().with_bgr(false).with_ml(false),
) },
{ command: CmdColmod(
CmdColmodArg0::new().with_vipf(6) // 18-bit pixel
) },
{ command: CmdDispon() },
{ sleep_ms: 20 },
// { command: CmdCn2bkxsel(
// CmdCn2bkxselArg0::new(),
// CmdCn2bkxselArg1::new(),
// CmdCn2bkxselArg2::new(),
// CmdCn2bkxselArg3::new(),
// CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(true),
// ) },
// { command: CmdPorctrl(
// CmdPorctrlArg0::new().with_vbp(0xFF),
// CmdPorctrlArg1::new().with_vfp(0x0),
// ) },
// // { command: CmdRgbctrl(
// // CmdRgbctrlArg0::new()
// // .with_ep(false)
// // .with_dp(false)
// // .with_hsp(false)
// // .with_vsp(false)
// // .with_de_hv(true),
// // CmdRgbctrlArg1::new()
// // .with_hbp_hvrgb(16),
// // CmdRgbctrlArg2::new()
// // .with_vbp_hvrgb(8),
// // ) },
// { command: CmdCn2bkxsel(
// CmdCn2bkxselArg0::new(),
// CmdCn2bkxselArg1::new(),
// CmdCn2bkxselArg2::new(),
// CmdCn2bkxselArg3::new(),
// CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(false),
// ) },
// { sleep_ms: 20 },
];

View file

@ -0,0 +1,489 @@
use alloc::{boxed::Box, vec, vec::Vec};
use core::iter::once;
use embassy_time::{Duration, Timer};
use esp_hal::gpio::{Flex, Level, Output};
use lazy_static::lazy_static;
use crate::peripherals::st7701s::Command;
async fn spi_delay() {
Timer::after(Duration::from_micros(1)).await;
}
async fn spi_dummy_bit(sck: &mut Output<'_>) {
sck.set_low();
spi_delay().await;
sck.set_high();
spi_delay().await;
}
async fn spi_write_bit(bit: bool, mosi: &mut Flex<'_>, sck: &mut Output<'_>) {
mosi.set_level(if bit { Level::High } else { Level::Low });
sck.set_low();
spi_delay().await;
sck.set_high();
spi_delay().await;
}
async fn spi_read_bit(mosi: &mut Flex<'_>, sck: &mut Output<'_>) -> bool {
sck.set_low();
spi_delay().await;
sck.set_high();
spi_delay().await;
mosi.is_high()
}
async fn spi_write_bits(
bits: impl Iterator<Item = bool>,
mosi: &mut Flex<'_>,
sck: &mut Output<'_>,
) {
for bit in bits {
spi_write_bit(bit, mosi, sck).await;
}
}
async fn spi_write_word(is_param: bool, data: u8, mosi: &mut Flex<'_>, sck: &mut Output<'_>) {
assert!(sck.is_set_high());
spi_write_bits(
once(is_param).chain((0..8).map(|i| (data >> i) & 1 != 0).rev()),
mosi,
sck,
)
.await;
}
pub async fn spi_write(
command: u8,
args: impl IntoIterator<Item = u8>,
mosi: &mut Flex<'_>,
sck: &mut Output<'_>,
cs: &mut Output<'_>,
) {
cs.set_low();
spi_write_word(false, command, mosi, sck).await;
for arg in args {
spi_write_word(true, arg, mosi, sck).await;
}
cs.set_high();
}
#[expect(unused)]
pub async fn spi_read(
command: u8,
dummy_cycle: bool,
mosi: &mut Flex<'_>,
sck: &mut Output<'_>,
cs: &mut Output<'_>,
output_buffer: &mut [u8],
) {
output_buffer.fill(0);
cs.set_low();
spi_write_word(false, command, mosi, sck).await;
mosi.set_output_enable(false);
mosi.set_input_enable(true);
if dummy_cycle {
spi_dummy_bit(sck).await;
}
for output_byte in output_buffer {
for i in (0..8).rev() {
if spi_read_bit(mosi, sck).await {
*output_byte |= 1 << i;
}
}
}
mosi.set_input_enable(false);
mosi.set_output_enable(true);
cs.set_high();
}
use crate::peripherals::st7701s::*;
// struct InitSequenceAction {
// command: ArrayCommand<8>,
// sleep: u64,
// }
// pub const INIT_SEQUENCE: [InitSequenceAction; _] = [CmdCn2bkxsel(
// CmdCn2bkxselArg0::new(),
// CmdCn2bkxselArg1::new(),
// CmdCn2bkxselArg2::new(),
// CmdCn2bkxselArg3::new(),
// CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true),
// )];
lazy_static! {
pub static ref INIT_SEQUENCE_COMMANDS: Vec<(Vec<Box<dyn Command + Send + Sync>>, u64)> = vec![
(vec![
Box::new(CmdCn2bkxsel(
CmdCn2bkxselArg0::new(),
CmdCn2bkxselArg1::new(),
CmdCn2bkxselArg2::new(),
CmdCn2bkxselArg3::new(),
CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true),
)),
Box::new(CustomCommand {
address: 0xEF,
args: &[0x08],
}),
Box::new(CmdCn2bkxsel(
CmdCn2bkxselArg0::new(),
CmdCn2bkxselArg1::new(),
CmdCn2bkxselArg2::new(),
CmdCn2bkxselArg3::new(),
CmdCn2bkxselArg4::new().with_bksel(0x0).with_cn2(true),
)),
Box::new(CmdLneset(
CmdLnesetArg0::new().with_bar(119).with_lde_en(false),
CmdLnesetArg1::new().with_line_delta(0),
)),
Box::new(CmdPorctrl(
CmdPorctrlArg0::new().with_vbp(17),
CmdPorctrlArg1::new().with_vfp(12),
)),
Box::new(CmdInvsel(
CmdInvselArg0::new().with_nlinv(0b111),
CmdInvselArg1::new().with_rtni(2),
)),
Box::new(CustomCommand {
address: 0xCC,
args: &[0x30],
}),
Box::new(CmdPvgamctrl(
CmdPvgamctrlArg0::new().with_vc0p(6).with_aj0p(0),
CmdPvgamctrlArg1::new().with_vc4p(15).with_aj1p(3),
CmdPvgamctrlArg2::new().with_vc8p(14).with_aj2p(0),
CmdPvgamctrlArg3::new().with_vc16p(12),
CmdPvgamctrlArg4::new().with_vc24p(15).with_aj3p(0),
CmdPvgamctrlArg5::new().with_vc52p(3),
CmdPvgamctrlArg6::new().with_vc80p(0),
CmdPvgamctrlArg7::new().with_vc108p(10),
CmdPvgamctrlArg8::new().with_vc147p(7),
CmdPvgamctrlArg9::new().with_vc175p(27),
CmdPvgamctrlArg10::new().with_vc203p(3),
CmdPvgamctrlArg11::new().with_vc231p(18).with_aj4p(0),
CmdPvgamctrlArg12::new().with_vc239p(16),
CmdPvgamctrlArg13::new().with_vc247p(37).with_aj5p(0),
CmdPvgamctrlArg14::new().with_vc251p(54).with_aj6p(0),
CmdPvgamctrlArg15::new().with_vc255p(30).with_aj7p(0),
)),
Box::new(CmdNvgamctrl(
CmdNvgamctrlArg0::new().with_vc0n(12).with_aj0n(0),
CmdNvgamctrlArg1::new().with_vc4n(14).with_aj1n(3),
CmdNvgamctrlArg2::new().with_vc8n(18).with_aj2n(0),
CmdNvgamctrlArg3::new().with_vc16n(12),
CmdNvgamctrlArg4::new().with_vc24n(14).with_aj3n(0),
CmdNvgamctrlArg5::new().with_vc52n(6),
CmdNvgamctrlArg6::new().with_vc80n(3),
CmdNvgamctrlArg7::new().with_vc108n(6),
CmdNvgamctrlArg8::new().with_vc147n(8),
CmdNvgamctrlArg9::new().with_vc175n(35),
CmdNvgamctrlArg10::new().with_vc203n(6),
CmdNvgamctrlArg11::new().with_vc231n(18).with_aj4n(0),
CmdNvgamctrlArg12::new().with_vc239n(16),
CmdNvgamctrlArg13::new().with_vc247n(48).with_aj5n(0),
CmdNvgamctrlArg14::new().with_vc251n(47).with_aj6n(0),
CmdNvgamctrlArg15::new().with_vc255n(31).with_aj7n(0),
)),
Box::new(CmdCn2bkxsel(
CmdCn2bkxselArg0::new(),
CmdCn2bkxselArg1::new(),
CmdCn2bkxselArg2::new(),
CmdCn2bkxselArg3::new(),
CmdCn2bkxselArg4::new().with_bksel(0x1).with_cn2(true),
)),
Box::new(CmdVrhs(
CmdVrhsArg0::new().with_vrha(115),
)),
Box::new(CmdVcoms(
CmdVcomsArg0::new().with_vcom(124),
)),
Box::new(CmdVghss(
// The first bit is set to 1 in the original init code, but not here.
CmdVghssArg0::new().with_vghss(0x3), // 13 V
)),
Box::new(CmdTescmd(
CmdTescmdArg0::new(),
)),
Box::new(CmdVgls(
CmdVglsArg0::new().with_vgls(0x9) // -10.17 V
)),
Box::new(CmdPwctrl1(
CmdPwctrl1Arg0::new()
.with_apos(0x3) // Max
.with_apis(0x1) // Min
.with_ap(0x2) // Middle
)),
Box::new(CmdPwctrl2(
CmdPwctrl2Arg0::new()
.with_avcl(0x3) // -5 V
.with_avdd(0x3) // 6.8 V
)),
Box::new(CmdPwctrl3(
CmdPwctrl3Arg0::new()
.with_svno_pum(0) // Cell setting 4
.with_svpo_pum(0x1) // Cell setting 5
)),
Box::new(CmdPclks2(
CmdPclks2Arg0::new().with_sbstcks(0x3)
)),
Box::new(CmdPdr1(
CmdPdr1Arg0::new().with_t2d(8) // 1.6 us
)),
Box::new(CmdPdr2(
CmdPdr2Arg0::new().with_t3d(8) // 6.4 us
)),
Box::new(CmdMipiset1(
CmdMipiset1Arg0::new().with_err_sel(0).with_eotp_en(true),
)),
Box::new(CustomCommand {
address: 0xE0,
args: &[
0x00,
0x00,
0x02,
0x00,
0x00,
0x0C,
]
}),
Box::new(CustomCommand {
address: 0xE1,
args: &[
0x05,
0x96,
0x07,
0x96,
0x06,
0x96,
0x08,
0x96,
0x00,
0x44,
0x44,
]
}),
Box::new(CustomCommand {
address: 0xE2,
args: &[
0x00,
0x00,
0x03,
0x03,
0x00,
0x00,
0x02,
0x00,
0x00,
0x00,
0x02,
0x00,
]
}),
Box::new(CustomCommand {
address: 0xE3,
args: &[
0x00,
0x00,
0x33,
0x33,
]
}),
Box::new(CustomCommand {
address: 0xE4,
args: &[
0x44,
0x44,
]
}),
Box::new(CustomCommand {
address: 0xE5,
args: &[
0x0D,
0xD4,
0x28,
0x8C,
0x0F,
0xD6,
0x28,
0x8C,
0x09,
0xD0,
0x28,
0x8C,
0x0B,
0xD2,
0x28,
0x8C,
]
}),
Box::new(CustomCommand {
address: 0xE6,
args: &[
0x00,
0x00,
0x33,
0x33,
]
}),
Box::new(CustomCommand {
address: 0xE7,
args: &[
0x44,
0x44,
]
}),
Box::new(CustomCommand {
address: 0xE8,
args: &[
0x0E,
0xD5,
0x28,
0x8C,
0x10,
0xD7,
0x28,
0x8C,
0x0A,
0xD1,
0x28,
0x8C,
0x0C,
0xD3,
0x28,
0x8C,
]
}),
Box::new(CustomCommand {
address: 0xEB,
args: &[
0x00,
0x01,
0xE4,
0xE4,
0x44,
0x00,
]
}),
Box::new(CustomCommand {
address: 0xED,
args: &[
0xF3,
0xC1,
0xBA,
0x0F,
0x66,
0x77,
0x44,
0x55,
0x55,
0x44,
0x77,
0x66,
0xF0,
0xAB,
0x1C,
0x3F,
]
}),
Box::new(CustomCommand {
address: 0xEF,
args: &[
0x10,
0x0D,
0x04,
0x08,
0x3F,
0x1F,
]
}),
Box::new(CmdCn2bkxsel(
CmdCn2bkxselArg0::new(),
CmdCn2bkxselArg1::new(),
CmdCn2bkxselArg2::new(),
CmdCn2bkxselArg3::new(),
CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true),
)),
Box::new(CustomCommand {
address: 0xE8,
args: &[
0x00,
0x0E,
],
}),
Box::new(CmdSlpout()),
], 120),
(vec![
Box::new(CustomCommand {
address: 0xE8,
args: &[
0x00,
0x0C,
],
}),
], 10),
(vec![
Box::new(CustomCommand {
address: 0xE8,
args: &[
0x40,
0x00,
],
}),
Box::new(CmdCn2bkxsel(
CmdCn2bkxselArg0::new(),
CmdCn2bkxselArg1::new(),
CmdCn2bkxselArg2::new(),
CmdCn2bkxselArg3::new(),
CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(false),
)),
Box::new(CmdMadctl(
CmdMadctlArg0::new().with_bgr(false).with_ml(false),
)),
Box::new(CmdColmod(
CmdColmodArg0::new().with_vipf(6) // 18-bit pixel
)),
Box::new(CmdDispon()),
], 20),
// (vec![
// Box::new(CmdCn2bkxsel(
// CmdCn2bkxselArg0::new(),
// CmdCn2bkxselArg1::new(),
// CmdCn2bkxselArg2::new(),
// CmdCn2bkxselArg3::new(),
// CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(true),
// )),
// Box::new(CmdPorctrl(
// CmdPorctrlArg0::new().with_vbp(0xFF),
// CmdPorctrlArg1::new().with_vfp(0x0),
// )),
// // Box::new(CmdRgbctrl(
// // CmdRgbctrlArg0::new()
// // .with_ep(false)
// // .with_dp(false)
// // .with_hsp(false)
// // .with_vsp(false)
// // .with_de_hv(true),
// // CmdRgbctrlArg1::new()
// // .with_hbp_hvrgb(16),
// // CmdRgbctrlArg2::new()
// // .with_vbp_hvrgb(8),
// // )),
// Box::new(CmdCn2bkxsel(
// CmdCn2bkxselArg0::new(),
// CmdCn2bkxselArg1::new(),
// CmdCn2bkxselArg2::new(),
// CmdCn2bkxselArg3::new(),
// CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(false),
// )),
// ], 20),
];
}

File diff suppressed because it is too large Load diff

View file

@ -1,102 +0,0 @@
use core::iter::once;
use embassy_time::{Duration, Timer};
use esp_hal::gpio::{Flex, Level, Output};
async fn spi_delay() {
Timer::after(Duration::from_micros(1)).await;
}
async fn spi_dummy_bit(sck: &mut Output<'_>) {
sck.set_low();
spi_delay().await;
sck.set_high();
spi_delay().await;
}
async fn spi_write_bit(bit: bool, mosi: &mut Flex<'_>, sck: &mut Output<'_>) {
mosi.set_level(if bit { Level::High } else { Level::Low });
sck.set_low();
spi_delay().await;
sck.set_high();
spi_delay().await;
}
async fn spi_read_bit(mosi: &mut Flex<'_>, sck: &mut Output<'_>) -> bool {
sck.set_low();
spi_delay().await;
sck.set_high();
spi_delay().await;
mosi.is_high()
}
async fn spi_write_bits(
bits: impl Iterator<Item = bool>,
mosi: &mut Flex<'_>,
sck: &mut Output<'_>,
) {
for bit in bits {
spi_write_bit(bit, mosi, sck).await;
}
}
async fn spi_write_word(is_param: bool, data: u8, mosi: &mut Flex<'_>, sck: &mut Output<'_>) {
assert!(sck.is_set_high());
spi_write_bits(
once(is_param).chain((0..8).map(|i| (data >> i) & 1 != 0).rev()),
mosi,
sck,
)
.await;
}
pub async fn spi_write(
command: u8,
args: impl IntoIterator<Item = u8>,
mosi: &mut Flex<'_>,
sck: &mut Output<'_>,
cs: &mut Output<'_>,
) {
cs.set_low();
spi_write_word(false, command, mosi, sck).await;
for arg in args {
spi_write_word(true, arg, mosi, sck).await;
}
cs.set_high();
}
#[expect(unused)]
pub async fn spi_read(
command: u8,
dummy_cycle: bool,
mosi: &mut Flex<'_>,
sck: &mut Output<'_>,
cs: &mut Output<'_>,
output_buffer: &mut [u8],
) {
output_buffer.fill(0);
cs.set_low();
spi_write_word(false, command, mosi, sck).await;
mosi.set_output_enable(false);
mosi.set_input_enable(true);
if dummy_cycle {
spi_dummy_bit(sck).await;
}
for output_byte in output_buffer {
for i in (0..8).rev() {
if spi_read_bit(mosi, sck).await {
*output_byte |= 1 << i;
}
}
}
mosi.set_input_enable(false);
mosi.set_output_enable(true);
cs.set_high();
}

View file

@ -11,7 +11,7 @@ use alloc::vec::Vec;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::channel::Channel; use embassy_sync::channel::Channel;
use embassy_time::Instant; use embassy_time::Instant;
use esp_alloc::MemoryCapability; use esp_alloc::{EspHeap, MemoryCapability};
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use rmk::descriptor::KeyboardReport; use rmk::descriptor::KeyboardReport;
use rmk::hid::Report; use rmk::hid::Report;
@ -19,7 +19,6 @@ use rmk::{heapless, join_all};
use slint::platform::Key; use slint::platform::Key;
use xkbcommon::xkb::{self, FeedResult, KeyDirection, Keysym, ModMask, Status}; use xkbcommon::xkb::{self, FeedResult, KeyDirection, Keysym, ModMask, Status};
use crate::ram::PsramAllocator;
use crate::util::{DurationExt, get_file_name}; use crate::util::{DurationExt, get_file_name};
use crate::{KEYBOARD_REPORT_PROXY, PSRAM_ALLOCATOR}; use crate::{KEYBOARD_REPORT_PROXY, PSRAM_ALLOCATOR};
@ -402,8 +401,8 @@ struct KeysymEntry {
mask: xkb::ModMask, mask: xkb::ModMask,
} }
type KeysymEntries = Vec<KeysymEntry, PsramAllocator>; type KeysymEntries = Vec<KeysymEntry, &'static EspHeap>;
type KeysymMap = BTreeMap<Keysym, KeysymEntries, PsramAllocator>; type KeysymMap = BTreeMap<Keysym, KeysymEntries, &'static EspHeap>;
/// Based on https://github.com/xkbcommon/libxkbcommon/blob/6c67e3d41d3215ab1edd4406de215c7bf1f20c74/tools/how-to-type.c#L434 /// Based on https://github.com/xkbcommon/libxkbcommon/blob/6c67e3d41d3215ab1edd4406de215c7bf1f20c74/tools/how-to-type.c#L434
fn lookup_keysym_entries( fn lookup_keysym_entries(
@ -428,7 +427,7 @@ pub fn string_to_hid_keycodes(
keymap: &xkb::Keymap, keymap: &xkb::Keymap,
_compose_table: &xkb::compose::Table, _compose_table: &xkb::compose::Table,
string: &str, string: &str,
) -> Result<Vec<HidKeycodeWithMods, PsramAllocator>, char> { ) -> Result<Vec<HidKeycodeWithMods, &'static EspHeap>, char> {
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct KeycodeChoice { struct KeycodeChoice {
mod_mask: ModMask, mod_mask: ModMask,

View file

@ -1,373 +0,0 @@
use embassy_time::Timer;
use esp_alloc::{HeapRegion, MemoryCapability};
use esp_hal::ram;
use log::{info, warn};
// Memory allocation regions.
// These can be debugged using `xtensa-esp32s3-elf-size -A <path-to-binary>`.
// A panic such as `memory allocation of 3740121773 bytes failed` is caused by a heap overflow. The size is `DEEDBAAD` in hex.
//
// RAM usage of static variables can be diagnosed with:
// ```sh
// xtensa-esp32s3-elf-nm.exe -S --size-sort -t d ../target/xtensa-esp32s3-none-elf/release/acid-firmware | grep -iE ' [dbv] ' | tail -n 10 | tac
// ```
/// Total heap size
pub const HEAP_SIZE: usize = 112 * 1024;
/// Size of the app core's stack
pub const STACK_SIZE_CORE_APP: usize = 80 * 1024;
pub static PSRAM_ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();
pub type PsramAllocator = &'static esp_alloc::EspHeap;
pub fn initialize(psram_peripheral: esp_hal::peripherals::PSRAM) {
// Use the internal DRAM as the heap.
// Memory reclaimed from the esp-idf bootloader.
const HEAP_SIZE_RECLAIMED: usize = const {
let range = esp_metadata_generated::memory_range!("DRAM2_UNINIT");
range.end - range.start
};
esp_alloc::heap_allocator!(#[ram(reclaimed)] size: HEAP_SIZE_RECLAIMED);
esp_alloc::heap_allocator!(size: HEAP_SIZE - HEAP_SIZE_RECLAIMED);
#[cfg(not(feature = "no-alloc-tracing"))]
alloc_tracing::install_ram_allocator_proxy();
info!(
"Internal RAM heap initialized!\n{}",
esp_alloc::HEAP.stats()
);
// Initialize the PSRAM allocator.
{
let (psram_offset, psram_size) = esp_hal::psram::psram_raw_parts(&psram_peripheral);
unsafe {
PSRAM_ALLOCATOR.add_region(HeapRegion::new(
psram_offset,
psram_size,
MemoryCapability::External.into(),
));
}
info!(
"External PSRAM heap initialized with capacity of {} MiB!\n{}",
psram_size / 1024 / 1024,
PSRAM_ALLOCATOR.stats(),
);
}
}
pub async fn run_alloc_stats_reporter() {
let mut psram_used_prev = 0;
let mut heap_used_prev = 0;
loop {
let psram_stats = PSRAM_ALLOCATOR.stats();
let heap_stats = esp_alloc::HEAP.stats();
if psram_stats.current_usage != psram_used_prev {
let difference = psram_stats.current_usage as isize - psram_used_prev as isize;
psram_used_prev = psram_stats.current_usage;
warn!(
"External PSRAM heap usage changed: {}{}\n{psram_stats}",
if difference < 0 { '-' } else { '+' },
difference.abs()
);
}
if heap_stats.current_usage != heap_used_prev {
let difference = heap_stats.current_usage as isize - heap_used_prev as isize;
heap_used_prev = heap_stats.current_usage;
warn!(
"Internal RAM heap usage changed: {}{}\n{heap_stats}",
if difference < 0 { '-' } else { '+' },
difference.abs()
);
#[cfg(not(feature = "no-alloc-tracing"))]
alloc_tracing::report_stats();
}
Timer::after_secs(1).await;
}
}
#[cfg(not(feature = "no-alloc-tracing"))]
mod alloc_tracing {
use core::{
alloc::GlobalAlloc,
cell::RefCell,
cmp::{Ordering, Reverse},
fmt::Display,
};
use alloc::collections::btree_map::BTreeMap;
use embassy_sync::blocking_mutex::{Mutex, raw::CriticalSectionRawMutex};
use esp_alloc::EspHeap;
use esp_backtrace::Backtrace;
use esp_sync::NonReentrantMutex;
use log::warn;
use tinyvec::ArrayVec;
use crate::ram::{PSRAM_ALLOCATOR, PsramAllocator};
#[derive(Default, Clone, PartialEq, Eq, Debug)]
struct Stats {
/// The total number of allocations.
allocations: usize,
/// The number of bytes allocated in total.
allocated_total: usize,
/// The number of bytes allocated minus the number of bytes deallocated.
allocated_current: usize,
}
impl Stats {
fn update(&mut self, allocations_delta: isize, bytes_delta: isize, bytes_new: usize) {
self.allocations = (self.allocations as isize + allocations_delta) as usize;
self.allocated_total += bytes_new;
self.allocated_current = (self.allocated_current as isize + bytes_delta) as usize;
}
}
impl PartialOrd for Stats {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Stats {
fn cmp(&self, other: &Self) -> Ordering {
self.allocated_current
.cmp(&other.allocated_current)
.then_with(|| self.allocated_total.cmp(&other.allocated_total).reverse())
.then_with(|| self.allocations.cmp(&other.allocations).reverse())
}
}
#[derive(Clone)]
struct BacktraceWrapper(Backtrace);
impl Display for BacktraceWrapper {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut it = self.0.frames().iter();
if let Some(frame) = it.next() {
write!(f, "0x{:08x}", frame.program_counter())?;
}
while let Some(frame) = it.next() {
write!(f, " 0x{:08x}", frame.program_counter())?;
}
Ok(())
}
}
impl PartialEq for BacktraceWrapper {
fn eq(&self, other: &Self) -> bool {
if self.0.frames().len() != other.0.frames().len() {
return false;
}
for (lhs_frame, rhs_frame) in self.0.frames().iter().zip(other.0.frames()) {
if lhs_frame.program_counter() != rhs_frame.program_counter() {
return false;
}
}
true
}
}
impl Eq for BacktraceWrapper {}
impl PartialOrd for BacktraceWrapper {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for BacktraceWrapper {
fn cmp(&self, other: &Self) -> Ordering {
let mut lhs_it = self.0.frames().iter().rev();
let mut rhs_it = other.0.frames().iter().rev();
loop {
match (lhs_it.next(), rhs_it.next()) {
(Some(lhs_frame), Some(rhs_frame)) => {
let ordering = lhs_frame
.program_counter()
.cmp(&rhs_frame.program_counter());
if ordering != Ordering::Equal {
return ordering;
}
}
(Some(_), None) => return Ordering::Greater,
(None, Some(_)) => return Ordering::Less,
(None, None) => return Ordering::Equal,
}
}
}
}
#[derive(Clone)]
struct AllocationTracer {
bt_to_stats: BTreeMap<BacktraceWrapper, Stats, PsramAllocator>,
ptr_to_bt: BTreeMap<*mut u8, BacktraceWrapper, PsramAllocator>,
}
unsafe impl Send for AllocationTracer {}
impl AllocationTracer {
const fn new() -> Self {
Self {
bt_to_stats: BTreeMap::new_in(&PSRAM_ALLOCATOR),
ptr_to_bt: BTreeMap::new_in(&PSRAM_ALLOCATOR),
}
}
fn alloc(&mut self, bt: Backtrace, ptr: *mut u8, bytes: usize) {
let bt = BacktraceWrapper(bt.clone());
self.ptr_to_bt.insert(ptr, bt.clone());
let stats = self
.bt_to_stats
.entry(bt)
.or_insert_with(|| Default::default());
stats.update(1, bytes as isize, bytes);
}
fn realloc(
&mut self,
ptr_old: *mut u8,
ptr_new: *mut u8,
bytes_old: usize,
bytes_new: usize,
) {
let bt = self.ptr_to_bt.remove(&ptr_old).unwrap();
self.ptr_to_bt.insert(ptr_new, bt.clone());
let stats = self.bt_to_stats.get_mut(&bt).unwrap();
stats.update(0, bytes_new as isize - bytes_old as isize, bytes_new);
}
fn dealloc(&mut self, ptr: *mut u8, bytes: usize) {
let bt = self.ptr_to_bt.remove(&ptr).unwrap();
let stats = self.bt_to_stats.get_mut(&bt).unwrap();
stats.update(-1, -(bytes as isize), 0);
}
}
struct TracingAllocator<T: GlobalAlloc + 'static> {
inner: NonReentrantMutex<Option<&'static T>>,
tracer: Mutex<CriticalSectionRawMutex, RefCell<AllocationTracer>>,
}
impl<T> TracingAllocator<T>
where
T: GlobalAlloc,
{
const fn new() -> Self {
Self {
inner: NonReentrantMutex::new(None),
tracer: Mutex::new(RefCell::new(AllocationTracer::new())),
}
}
fn with_inner<R>(&self, callback: impl FnOnce(&T) -> R) -> R {
self.inner.with(|inner| {
(callback)(
inner
.as_ref()
.expect("an allocator must be installed in the global allocator proxy"),
)
})
}
fn update_with<R>(&self, callback: impl FnOnce(&mut AllocationTracer) -> R) -> R {
self.tracer
.lock(|tracer| (callback)(&mut tracer.borrow_mut()))
}
}
unsafe impl<T> GlobalAlloc for TracingAllocator<T>
where
T: GlobalAlloc,
{
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
let bt = Backtrace::capture();
let ptr = self.with_inner(|inner| unsafe { inner.alloc(layout) });
self.update_with(|tracer| tracer.alloc(bt, ptr, layout.size()));
ptr
}
unsafe fn alloc_zeroed(&self, layout: core::alloc::Layout) -> *mut u8 {
let bt = Backtrace::capture();
let ptr = self.with_inner(|inner| unsafe { inner.alloc_zeroed(layout) });
self.update_with(|tracer| tracer.alloc(bt, ptr, layout.size()));
ptr
}
unsafe fn realloc(
&self,
ptr_old: *mut u8,
layout: core::alloc::Layout,
new_size: usize,
) -> *mut u8 {
let ptr_new =
self.with_inner(|inner| unsafe { inner.realloc(ptr_old, layout, new_size) });
self.update_with(|tracer| tracer.realloc(ptr_old, ptr_new, layout.size(), new_size));
ptr_new
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
self.update_with(|tracer| tracer.dealloc(ptr, layout.size()));
self.with_inner(|inner| unsafe { inner.dealloc(ptr, layout) });
}
}
#[global_allocator]
static PROXY: TracingAllocator<EspHeap> = TracingAllocator::new();
pub fn install_ram_allocator_proxy() {
PROXY.inner.with(|inner| {
*inner = Some(&esp_alloc::HEAP);
});
}
pub fn report_stats() {
let AllocationTracer {
bt_to_stats,
ptr_to_bt,
} = PROXY.tracer.lock(|tracer| tracer.borrow().clone());
// Wrapped in `Option` because of the `Default` requirement.
let mut sorted = ArrayVec::<[Option<(BacktraceWrapper, Reverse<Stats>)>; 5]>::new();
let bt_to_stats_len = bt_to_stats.len();
for (key, value) in bt_to_stats {
// Reverse ordering of stats because we are interested in the largest.
let value_rev = Reverse(value);
if let Some((_, value_last)) = sorted.last().and_then(|last| last.as_ref())
&& &value_rev >= value_last
&& sorted.len() >= sorted.capacity()
{
// This stat is not large enough to be inserted.
continue;
}
let index = match sorted.binary_search_by_key(&Some(&value_rev), |item| {
item.as_ref().map(|(_, current_value)| current_value)
}) {
Ok(index) => index,
Err(index) => index,
};
if sorted.len() >= sorted.capacity() {
assert!(
index < sorted.len(),
"the stat should be large enough to be inserted not at the end of the list"
);
let _ = sorted.pop().unwrap();
}
sorted.insert(index, Some((key, value_rev)));
}
warn!("Largest allocations in global allocator:");
for (index, (key, value)) in sorted.into_iter().map(Option::unwrap).enumerate() {
warn!("{}. {}\n{:#?}", index + 1, key, value.0);
}
warn!("bt_to_stats.len() = {}", bt_to_stats_len);
warn!("ptr_to_bt.len() = {}", ptr_to_bt.len());
}
}

View file

@ -7,9 +7,8 @@ use core::{
use alloc::{ use alloc::{
boxed::Box, collections::vec_deque::VecDeque, rc::Rc, string::ToString, sync::Arc, vec::Vec, boxed::Box, collections::vec_deque::VecDeque, rc::Rc, string::ToString, sync::Arc, vec::Vec,
}; };
use embassy_sync::blocking_mutex::{self, raw::CriticalSectionRawMutex}; use critical_section::Mutex;
use esp_hal::time::Instant; use esp_hal::time::Instant;
use esp_hal_bounce_buffers::SwapchainWriter;
use log::{debug, info}; use log::{debug, info};
use slint::{ use slint::{
EventLoopError, PhysicalSize, SharedString, WindowSize, EventLoopError, PhysicalSize, SharedString, WindowSize,
@ -24,22 +23,25 @@ use crate::proxy::{KEY_MESSAGE_CHANNEL, TryFromKeysym};
use super::window_adapter::SoftwareWindowAdapter; use super::window_adapter::SoftwareWindowAdapter;
pub struct FramebufferPtr(pub *mut [Rgb565Pixel]);
unsafe impl Send for FramebufferPtr {}
pub struct SlintBackend { pub struct SlintBackend {
pub window_size: [u32; 2], pub window_size: [u32; 2],
pub window: RefCell<Option<Rc<SoftwareWindowAdapter>>>, pub window: RefCell<Option<Rc<SoftwareWindowAdapter>>>,
pub swapchain: RefCell<SwapchainWriter>, pub framebuffer: FramebufferPtr,
pub quit_event_loop: Arc<AtomicBool>, pub quit_event_loop: Arc<AtomicBool>,
pub events: Arc< pub events: Arc<Mutex<RefCell<VecDeque<Box<dyn FnOnce() + Send>>>>>,
blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<VecDeque<Box<dyn FnOnce() + Send>>>>,
>,
} }
impl slint::platform::Platform for SlintBackend { impl slint::platform::Platform for SlintBackend {
fn create_window_adapter( fn create_window_adapter(
&self, &self,
) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> { ) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
let renderer = let renderer = SoftwareRenderer::new_with_repaint_buffer_type(
SoftwareRenderer::new_with_repaint_buffer_type(RepaintBufferType::SwappedBuffers); RepaintBufferType::ReusedBuffer, /* TODO: Implement a swapchain */
);
renderer.set_rendering_rotation(RenderingRotation::Rotate270); renderer.set_rendering_rotation(RenderingRotation::Rotate270);
let window = SoftwareWindowAdapter::new(renderer); let window = SoftwareWindowAdapter::new(renderer);
// window.set_scale_factor(4.0); // window.set_scale_factor(4.0);
@ -68,9 +70,13 @@ impl slint::platform::Platform for SlintBackend {
/* loop */ /* loop */
{ {
let drained_events = self let drained_events = critical_section::with(|cs| {
.events self.events
.lock(|events| events.borrow_mut().drain(..).collect::<Vec<_>>()); .borrow(cs)
.borrow_mut()
.drain(..)
.collect::<Vec<_>>()
});
for event in drained_events { for event in drained_events {
(event)(); (event)();
@ -128,10 +134,8 @@ impl slint::platform::Platform for SlintBackend {
} }
window.draw_if_needed(|renderer| { window.draw_if_needed(|renderer| {
// TODO: VSYNC support. // TODO: Proper synchronization.
let mut swapchain = self.swapchain.borrow_mut(); let framebuffer = unsafe { &mut *self.framebuffer.0 };
let mut write_guard = swapchain.write();
let framebuffer = bytemuck::cast_slice_mut::<u8, Rgb565Pixel>(&mut write_guard);
renderer.render(framebuffer, self.window_size[1] as usize); renderer.render(framebuffer, self.window_size[1] as usize);
info!("UI rendered."); info!("UI rendered.");
}); });
@ -144,9 +148,7 @@ impl slint::platform::Platform for SlintBackend {
struct AcidEventLoopProxy { struct AcidEventLoopProxy {
pub quit_event_loop: Arc<AtomicBool>, pub quit_event_loop: Arc<AtomicBool>,
pub events: Arc< pub events: Arc<Mutex<RefCell<VecDeque<Box<dyn FnOnce() + Send>>>>>,
blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<VecDeque<Box<dyn FnOnce() + Send>>>>,
>,
} }
impl EventLoopProxy for AcidEventLoopProxy { impl EventLoopProxy for AcidEventLoopProxy {
@ -159,8 +161,8 @@ impl EventLoopProxy for AcidEventLoopProxy {
&self, &self,
event: Box<dyn FnOnce() + Send>, event: Box<dyn FnOnce() + Send>,
) -> Result<(), EventLoopError> { ) -> Result<(), EventLoopError> {
self.events.lock(|events| { critical_section::with(|cs| {
events.borrow_mut().push_back(event); self.events.borrow(cs).borrow_mut().push_back(event);
}); });
Ok(()) Ok(())
} }

View file

@ -29,14 +29,13 @@ use spectre_api_sys::{
#[cfg(feature = "limit-fps")] #[cfg(feature = "limit-fps")]
use crate::FRAME_DURATION_MIN; use crate::FRAME_DURATION_MIN;
use crate::{ use crate::{
PSRAM_ALLOCATOR, PSRAM_ALLOCATOR, SIGNAL_LCD_SUBMIT, SIGNAL_UI_RENDER,
db::{ db::{
AcidDatabase, DbKey, DbPathSpectreUserSite, DbPathSpectreUserSites, DbPathSpectreUsers, AcidDatabase, DbKey, DbPathSpectreUserSite, DbPathSpectreUserSites, DbPathSpectreUsers,
PartitionAcid, ReadTransactionExt, PartitionAcid, ReadTransactionExt,
}, },
ffi::{alloc::__spre_free, crypto::ACTIVE_ENCRYPTED_USER_KEY}, ffi::{alloc::__spre_free, crypto::ACTIVE_ENCRYPTED_USER_KEY},
proxy::OUTPUT_STRING_CHANNEL, proxy::OUTPUT_STRING_CHANNEL,
ram::PsramAllocator,
ui::{ ui::{
backend::SlintBackend, backend::SlintBackend,
messages::{ messages::{
@ -61,8 +60,8 @@ fn spectre_derive_user_key(
encrypted_key: Option<Key>, encrypted_key: Option<Key>,
) -> SpectreUserKey { ) -> SpectreUserKey {
if let Some(encrypted_key) = encrypted_key { if let Some(encrypted_key) = encrypted_key {
ACTIVE_ENCRYPTED_USER_KEY.lock(|user_key| { critical_section::with(|cs| {
user_key.set(encrypted_key); ACTIVE_ENCRYPTED_USER_KEY.borrow(cs).set(encrypted_key);
}); });
} }
@ -131,7 +130,7 @@ pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: Partition
struct State { struct State {
window: AppWindow, window: AppWindow,
db: Rc<AcidDatabase, PsramAllocator>, db: Rc<AcidDatabase>,
users: SpectreUsersConfig, users: SpectreUsersConfig,
/// Currently active view. /// Currently active view.
view: AppState, view: AppState,
@ -143,23 +142,24 @@ struct State {
} }
impl State { impl State {
async fn new(db: AcidDatabase, main: AppWindow) -> Rc<RefCell<Self>, PsramAllocator> { async fn new(db: AcidDatabase, main: AppWindow) -> Rc<RefCell<Self>> {
let users = Self::load_users(&db).await; let users = {
let users = Self::load_users(&db).await;
warn!("Users: {users:#?}");
users
};
let usernames = users.users.clone().map(|user| user.username); let usernames = users.users.clone().map(|user| user.username);
let state = Rc::new_in( let state = Rc::new(RefCell::new(State {
RefCell::new(State { window: main.clone_strong(),
window: main.clone_strong(), users,
users, db: Rc::new(db),
db: Rc::new_in(db, &PSRAM_ALLOCATOR), view: AppState::Login,
view: AppState::Login, state_login: Default::default(),
state_login: Default::default(), state_users: Default::default(),
state_users: Default::default(), state_user_edit: Default::default(),
state_user_edit: Default::default(), state_user_sites: Default::default(),
state_user_sites: Default::default(), }));
}),
&PSRAM_ALLOCATOR,
);
main.on_enter_view({ main.on_enter_view({
let state = state.clone(); let state = state.clone();
@ -336,10 +336,7 @@ impl State {
} }
} }
fn process_callback_message( fn process_callback_message(state_rc: &Rc<RefCell<State>>, message: CallbackMessage) {
state_rc: &Rc<RefCell<State>, PsramAllocator>,
message: CallbackMessage,
) {
let view = state_rc.borrow().view; let view = state_rc.borrow().view;
match view { match view {
AppState::Login => StateLogin::process_callback_message(state_rc, message), AppState::Login => StateLogin::process_callback_message(state_rc, message),
@ -379,8 +376,10 @@ impl State {
loop { loop {
slint::run_event_loop().unwrap(); slint::run_event_loop().unwrap();
SIGNAL_LCD_SUBMIT.signal(());
#[cfg(feature = "limit-fps")] #[cfg(feature = "limit-fps")]
embassy_time::Timer::after(FRAME_DURATION_MIN).await; embassy_time::Timer::after(FRAME_DURATION_MIN).await;
SIGNAL_UI_RENDER.wait().await;
} }
#[expect(unreachable_code)] #[expect(unreachable_code)]
@ -389,21 +388,14 @@ impl State {
} }
trait AppViewTrait { trait AppViewTrait {
fn process_callback_message( fn process_callback_message(_state_rc: &Rc<RefCell<State>>, _message: CallbackMessage) {}
_state_rc: &Rc<RefCell<State>, PsramAllocator>,
_message: CallbackMessage,
) {
}
} }
#[derive(Default)] #[derive(Default)]
struct StateLogin {} struct StateLogin {}
impl AppViewTrait for StateLogin { impl AppViewTrait for StateLogin {
fn process_callback_message( fn process_callback_message(state_rc: &Rc<RefCell<State>>, message: CallbackMessage) {
state_rc: &Rc<RefCell<State>, PsramAllocator>,
message: CallbackMessage,
) {
let mut state = state_rc.borrow_mut(); let mut state = state_rc.borrow_mut();
match message { match message {
CallbackMessage::Login(CallbackMessageLogin::PwAccepted { CallbackMessage::Login(CallbackMessageLogin::PwAccepted {
@ -556,10 +548,7 @@ impl AppViewTrait for StateLogin {
struct StateUsers {} struct StateUsers {}
impl AppViewTrait for StateUsers { impl AppViewTrait for StateUsers {
fn process_callback_message( fn process_callback_message(state_rc: &Rc<RefCell<State>>, message: CallbackMessage) {
state_rc: &Rc<RefCell<State>, PsramAllocator>,
message: CallbackMessage,
) {
let mut state = state_rc.borrow_mut(); let mut state = state_rc.borrow_mut();
match message { match message {
CallbackMessage::Escape => { CallbackMessage::Escape => {
@ -589,10 +578,7 @@ struct StateUserEdit {
} }
impl AppViewTrait for StateUserEdit { impl AppViewTrait for StateUserEdit {
fn process_callback_message( fn process_callback_message(state_rc: &Rc<RefCell<State>>, message: CallbackMessage) {
state_rc: &Rc<RefCell<State>, PsramAllocator>,
message: CallbackMessage,
) {
let state = state_rc.clone(); let state = state_rc.clone();
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
match message { match message {
@ -742,10 +728,7 @@ struct StateUserSites {
} }
impl AppViewTrait for StateUserSites { impl AppViewTrait for StateUserSites {
fn process_callback_message( fn process_callback_message(state_rc: &Rc<RefCell<State>>, message: CallbackMessage) {
state_rc: &Rc<RefCell<State>, PsramAllocator>,
message: CallbackMessage,
) {
let state = state_rc.clone(); let state = state_rc.clone();
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
match message { match message {

View file

@ -1,9 +1,5 @@
use core::fmt::Display; use core::fmt::Display;
use embassy_sync::{
blocking_mutex::raw::RawMutex,
mutex::{Mutex, MutexGuard},
};
use embassy_time::Duration; use embassy_time::Duration;
pub struct FormattedDuration<'a>(&'a Duration); pub struct FormattedDuration<'a>(&'a Duration);
@ -50,37 +46,3 @@ pub const fn get_file_name(path: &str) -> &str {
Err(_) => panic!("Failed to extract the file name from a path."), Err(_) => panic!("Failed to extract the file name from a path."),
} }
} }
#[allow(unused)]
pub trait MutexExt<M, T> {
type Guard<'a>
where
M: 'a,
T: 'a,
Self: 'a;
/// Locks the mutex, while entering a critical section only during locking and unlocking.
///
/// # Safety:
/// * The guard must not be kept across async `yield` points.
/// * This must not be called from within interrupt handlers.
/// Otherwise, a deadlock might occur.
unsafe fn lock_blocking(&self) -> Self::Guard<'_>;
}
impl<M: RawMutex, T> MutexExt<M, T> for Mutex<M, T> {
type Guard<'a>
= MutexGuard<'a, M, T>
where
M: 'a,
T: 'a,
Self: 'a;
unsafe fn lock_blocking(&self) -> Self::Guard<'_> {
loop {
if let Ok(guard) = self.try_lock() {
return guard;
}
}
}
}

View file

@ -31,7 +31,7 @@ export component AppWindow inherits Window {
in property <string> dummy_identicon_symbols: "╔╚╰═█░▒▓☺☻╗╝╯═◈◎◐◑◒◓☀☁☂☃☄★☆☎☏⎈⌂☘☢☣☕⌚⌛⏰⚡⛄⛅☔♔♕♖♗♘♙♚♛♜♝♞♟♨♩♪♫⚐⚑⚔⚖⚙⚠⌘⏎✄✆✈✉✌"; in property <string> dummy_identicon_symbols: "╔╚╰═█░▒▓☺☻╗╝╯═◈◎◐◑◒◓☀☁☂☃☄★☆☎☏⎈⌂☘☢☣☕⌚⌛⏰⚡⛄⛅☔♔♕♖♗♘♙♚♛♜♝♞♟♨♩♪♫⚐⚑⚔⚖⚙⚠⌘⏎✄✆✈✉✌";
default-font-family: "IBM Plex Mono"; default-font-family: "IBM Plex Mono";
default-font-size: 16pt; default-font-size: 16pt;
height: 8px + 240px + 8px; height: 368px;
width: 960px; width: 960px;
forward-focus: focus-scope; forward-focus: focus-scope;
in property <AppState> app-state: AppState.login; in property <AppState> app-state: AppState.login;
@ -69,9 +69,9 @@ export component AppWindow inherits Window {
} }
vertical-box := VerticalBox { vertical-box := VerticalBox {
width: 960px; width: 960px;
height: 8px + 240px + 8px; height: 368px;
padding: 0px; padding: 0px;
padding-top: 8px; padding-top: 120px;
padding-bottom: 8px; padding-bottom: 8px;
Rectangle { Rectangle {
height: 240px; height: 240px;