Working Diag & artnet proto with imported led driver smi lib C
This commit is contained in:
Vendored
+3
-3
@@ -15,9 +15,9 @@
|
|||||||
"processCreateCommands": [
|
"processCreateCommands": [
|
||||||
"gdb-remote raspberrypi.local:17777"
|
"gdb-remote raspberrypi.local:17777"
|
||||||
],
|
],
|
||||||
"env": {
|
// "env": {
|
||||||
"RUST_LOG": "debug"
|
// "RUST_LOG": "trace"
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Vendored
+3
-1
@@ -1,3 +1,5 @@
|
|||||||
{
|
{
|
||||||
"rust-analyzer.cargo.target": "armv7-unknown-linux-gnueabihf"
|
"rust-analyzer.cargo.target": "armv7-unknown-linux-gnueabihf",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.formatOnPaste": true
|
||||||
}
|
}
|
||||||
Generated
+627
-2
@@ -2,6 +2,21 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "addr2line"
|
||||||
|
version = "0.24.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||||
|
dependencies = [
|
||||||
|
"gimli",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler2"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.3"
|
version = "1.1.3"
|
||||||
@@ -11,6 +26,21 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android-tzdata"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.18"
|
version = "0.6.18"
|
||||||
@@ -47,7 +77,7 @@ version = "1.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -58,7 +88,103 @@ checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"once_cell_polyfill",
|
"once_cell_polyfill",
|
||||||
"windows-sys",
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "artnet_protocol"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f1ca8f664c611598975a2af1084042d2e44c0532db8b72a7b551153f4753627"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.1",
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace"
|
||||||
|
version = "0.3.75"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
|
||||||
|
dependencies = [
|
||||||
|
"addr2line",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"miniz_oxide",
|
||||||
|
"object",
|
||||||
|
"rustc-demangle",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg_aliases"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.41"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
|
||||||
|
dependencies = [
|
||||||
|
"android-tzdata",
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-traits",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -67,6 +193,78 @@ version = "1.0.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-queue",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-queue"
|
||||||
|
version = "0.3.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctrlc"
|
||||||
|
version = "3.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73"
|
||||||
|
dependencies = [
|
||||||
|
"nix 0.30.1",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_filter"
|
name = "env_filter"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@@ -90,6 +288,36 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gimli"
|
||||||
|
version = "0.31.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.63"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is_terminal_polyfill"
|
name = "is_terminal_polyfill"
|
||||||
version = "1.70.1"
|
version = "1.70.1"
|
||||||
@@ -120,6 +348,16 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.172"
|
version = "0.2.172"
|
||||||
@@ -130,11 +368,26 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
|||||||
name = "lightsabre_backend"
|
name = "lightsabre_backend"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"artnet_protocol",
|
||||||
|
"crossbeam",
|
||||||
|
"ctrlc",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"log",
|
"log",
|
||||||
|
"nix 0.30.1",
|
||||||
|
"rpi-mailbox",
|
||||||
"rppal",
|
"rppal",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.27"
|
version = "0.4.27"
|
||||||
@@ -147,12 +400,125 @@ version = "2.7.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.8.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
|
||||||
|
dependencies = [
|
||||||
|
"adler2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.26.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"memoffset",
|
||||||
|
"pin-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.30.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.1",
|
||||||
|
"cfg-if",
|
||||||
|
"cfg_aliases",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "object"
|
||||||
|
version = "0.36.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell_polyfill"
|
name = "once_cell_polyfill"
|
||||||
version = "1.70.1"
|
version = "1.70.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "portable-atomic"
|
name = "portable-atomic"
|
||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
@@ -186,6 +552,15 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.11.1"
|
version = "1.11.1"
|
||||||
@@ -215,6 +590,19 @@ version = "0.8.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rpi-mailbox"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "161a8b2c05deee778809bf165433051db0e057fc32b4999ca0247924c705386e"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.1",
|
||||||
|
"chrono",
|
||||||
|
"log",
|
||||||
|
"nix 0.26.4",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rppal"
|
name = "rppal"
|
||||||
version = "0.22.1"
|
version = "0.22.1"
|
||||||
@@ -224,6 +612,31 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust_sandbox"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-demangle"
|
||||||
|
version = "0.1.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.219"
|
version = "1.0.219"
|
||||||
@@ -244,6 +657,37 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.5.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.101"
|
version = "2.0.101"
|
||||||
@@ -255,6 +699,55 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio"
|
||||||
|
version = "1.45.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace",
|
||||||
|
"bytes",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"parking_lot",
|
||||||
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
|
"socket2",
|
||||||
|
"tokio-macros",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
@@ -267,6 +760,138 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
||||||
|
dependencies = [
|
||||||
|
"windows-implement",
|
||||||
|
"windows-interface",
|
||||||
|
"windows-link",
|
||||||
|
"windows-result",
|
||||||
|
"windows-strings",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-implement"
|
||||||
|
version = "0.60.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-interface"
|
||||||
|
version = "0.59.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-strings"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.59.0"
|
version = "0.59.0"
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["lightsabre_backend"]
|
members = ["lightsabre_backend", "rust_sandbox"]
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ LightSabre is a project designed to control WS281x RGB LEDs using the Secondary
|
|||||||
|
|
||||||
- **WS281x LED Control:** High-performance driving of addressable RGB LEDs via the Raspberry Pi SMI.
|
- **WS281x LED Control:** High-performance driving of addressable RGB LEDs via the Raspberry Pi SMI.
|
||||||
- **ArtNet Support:** Receives color data over the ArtNet protocol, allowing the Raspberry Pi to function as a DMX controller.
|
- **ArtNet Support:** Receives color data over the ArtNet protocol, allowing the Raspberry Pi to function as a DMX controller.
|
||||||
- **Autonomous Mode:** Analyzes audio input from an I2S microphone, performing spectral decomposition to generate dynamic lighting effects.
|
- **Standalone Mode:** Analyzes audio input from an I2S microphone, performing spectral decomposition to generate dynamic lighting effects.
|
||||||
- **Web Interface:** Local web server for configuring and tweaking autonomous behavior.
|
- **Web Interface:** Local web server for configuring and tweaking standalone behavior.
|
||||||
- **MIDI Integration:** Supports real-time control and parameter adjustment via MIDI controllers.
|
- **MIDI Integration:** Supports real-time control and parameter adjustment via MIDI controllers.
|
||||||
|
|
||||||
## Technology Stack
|
## Technology Stack
|
||||||
@@ -26,18 +26,46 @@ LightSabre is a project designed to control WS281x RGB LEDs using the Secondary
|
|||||||
1. **Hardware Requirements**
|
1. **Hardware Requirements**
|
||||||
- Raspberry Pi (any model with SMI support)
|
- Raspberry Pi (any model with SMI support)
|
||||||
- WS281x-compatible RGB LED strip
|
- WS281x-compatible RGB LED strip
|
||||||
- I2S microphone (for autonomous mode)
|
- I2S microphone (for standalone mode)
|
||||||
- Optional: MIDI controller
|
- Optional: MIDI controller
|
||||||
|
|
||||||
2. **Software Setup**
|
### System install
|
||||||
- Install dependencies (see `requirements.txt`)
|
|
||||||
- Configure network for ArtNet or connect MIDI controller
|
The project is design for running on top of [Raspberry Pi OS Lite 32bits](https://www.raspberrypi.com/software/) (formerly Raspbian).
|
||||||
- Access the web interface via your browser
|
|
||||||
|
Raspberry Pi OS Lite provides device access through file system, networking tools for network interface management, POSIX sockets, native threading, and process control, covering all system features needed by LightSabre.
|
||||||
|
|
||||||
|
What about Realtime ?
|
||||||
|
|
||||||
|
#### Network AP / infra
|
||||||
|
|
||||||
|
`sudo raspi-config`
|
||||||
|
|
||||||
|
See. `nmcli`
|
||||||
|
`nmcli c up preconfigured`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### Rust
|
||||||
|
After installing Raspberry Pi OS we need to install Rust toolchain:
|
||||||
|
```
|
||||||
|
sudo apt update
|
||||||
|
sudo apt full upgrade -y
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||||
|
```
|
||||||
|
|
||||||
|
For more details : https://rustup.rs/
|
||||||
|
|
||||||
|
#### I2S Microphone
|
||||||
|
|
||||||
|
[I2S microphone](https://learn.adafruit.com/adafruit-i2s-mems-microphone-breakout/raspberry-pi-wiring-test)
|
||||||
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- [ArtNet Protocol](https://art-net.org.uk/)
|
- [ArtNet Protocol](https://art-net.org.uk/)
|
||||||
- [WS281x LEDs](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf)
|
- [WS281x LEDs](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf)
|
||||||
|
- [I2S microphone](https://learn.adafruit.com/adafruit-i2s-mems-microphone-breakout/raspberry-pi-wiring-test)
|
||||||
- [Raspberry Pi SMI](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html)
|
- [Raspberry Pi SMI](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html)
|
||||||
- [MIDI Protocol](https://www.midi.org/)
|
- [MIDI Protocol](https://www.midi.org/)
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,15 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
artnet_protocol = "0.4.3"
|
||||||
|
crossbeam = "0.8.4"
|
||||||
|
ctrlc = { version = "3.4.7", features = ["termination"] }
|
||||||
env_logger = "0.11.8"
|
env_logger = "0.11.8"
|
||||||
log = "0.4.27"
|
log = "0.4.27"
|
||||||
|
nix = "0.30.1"
|
||||||
|
rpi-mailbox = "0.3.0"
|
||||||
rppal = "0.22.1"
|
rppal = "0.22.1"
|
||||||
|
# tokio = { version = "1.45.1", features = ["full"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["rpizero2", "16channel"]
|
default = ["rpizero2", "16channel"]
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
fn main() {
|
||||||
|
println!("cargo::rustc-link-search=/workspaces/LightSabre/lightsabre_backend/drivers/lib/");
|
||||||
|
// println!("cargo:rustc-link-search=/workspaces/LightSabre/lightsabre_backend/drivers/lib");
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2020 rxi
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the MIT license. See `log.c` for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LOG_H
|
||||||
|
#define LOG_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define LOG_VERSION "0.1.0"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
va_list ap;
|
||||||
|
const char *fmt;
|
||||||
|
const char *file;
|
||||||
|
struct tm *time;
|
||||||
|
void *udata;
|
||||||
|
int line;
|
||||||
|
int level;
|
||||||
|
} log_Event;
|
||||||
|
|
||||||
|
typedef void (*log_LogFn)(log_Event *ev);
|
||||||
|
typedef void (*log_LockFn)(bool lock, void *udata);
|
||||||
|
|
||||||
|
enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL };
|
||||||
|
|
||||||
|
#define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
|
||||||
|
const char* log_level_string(int level);
|
||||||
|
void log_set_lock(log_LockFn fn, void *udata);
|
||||||
|
void log_set_level(int level);
|
||||||
|
void log_set_quiet(bool enable);
|
||||||
|
int log_add_callback(log_LogFn fn, void *udata, int level);
|
||||||
|
int log_add_fp(FILE *fp, int level);
|
||||||
|
|
||||||
|
void log_log(int level, const char *file, int line, const char *fmt, ...);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.12)
|
||||||
|
|
||||||
|
# set the project name
|
||||||
|
project(RpiLedBars VERSION 0.5 LANGUAGES C)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 99)
|
||||||
|
|
||||||
|
add_subdirectory(libs)
|
||||||
|
|
||||||
|
# add the executable
|
||||||
|
add_library(${PROJECT_NAME}_drivers
|
||||||
|
common.c
|
||||||
|
dma/rpi_dma.c
|
||||||
|
dma/rpi_videocore.c
|
||||||
|
gpio/rpi_gpio.c
|
||||||
|
leddriver/rpi_leddriver.c
|
||||||
|
smi/rpi_smi.c)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME}_drivers PRIVATE wiringPi)
|
||||||
|
target_link_libraries(${PROJECT_NAME}_drivers PRIVATE logc)
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT_NAME}_drivers PUBLIC dma gpio leddriver smi)
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
// Use mmap to obtain virtual address, given physical
|
||||||
|
void *map_periph(MEM_MAP *mp, void *phys, int size) {
|
||||||
|
mp->phys = phys;
|
||||||
|
mp->size = PAGE_ROUNDUP(size);
|
||||||
|
mp->bus = (void *)((uint32_t)phys - PHYS_REG_BASE + BUS_REG_BASE);
|
||||||
|
mp->virt = map_segment(phys, mp->size);
|
||||||
|
return (mp->virt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free mapped peripheral or memory
|
||||||
|
void unmap_periph_mem(MEM_MAP *mp) {
|
||||||
|
if (mp) {
|
||||||
|
unmap_segment(mp->virt, mp->size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- VIRTUAL MEMORY -----
|
||||||
|
|
||||||
|
// Get virtual memory segment for peripheral regs or physical mem
|
||||||
|
void *map_segment(void *addr, int size) {
|
||||||
|
int fd;
|
||||||
|
void *mem;
|
||||||
|
|
||||||
|
size = PAGE_ROUNDUP(size);
|
||||||
|
if ((fd = open("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC)) < 0) {
|
||||||
|
log_fatal("can't open /dev/mem, run using sudo");
|
||||||
|
exit(-EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
mem = mmap(0, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, (uint32_t)addr);
|
||||||
|
close(fd);
|
||||||
|
log_info("Map %p -> %p", (void *)addr, mem);
|
||||||
|
if (mem == MAP_FAILED) {
|
||||||
|
log_fatal("can't map memory");
|
||||||
|
exit(-EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
return (mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free mapped memory
|
||||||
|
void unmap_segment(void *mem, int size) {
|
||||||
|
if (mem) {
|
||||||
|
munmap(mem, PAGE_ROUNDUP(size));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#if !defined(__COMMON_H__)
|
||||||
|
#define __COMMON_H__
|
||||||
|
|
||||||
|
// Location of peripheral registers in physical memory
|
||||||
|
#define PHYS_REG_BASE PI_23_REG_BASE
|
||||||
|
#define PI_01_REG_BASE 0x20000000 // Pi Zero or 1
|
||||||
|
#define PI_23_REG_BASE 0x3F000000 // Pi 2 or 3
|
||||||
|
#define PI_4_REG_BASE 0xFE000000 // Pi 4
|
||||||
|
|
||||||
|
#define CLOCK_HZ 250000000 // Pi 2 - 4
|
||||||
|
//#define CLOCK_HZ 400000000 // Pi Zero
|
||||||
|
|
||||||
|
// Location of peripheral registers in bus memory
|
||||||
|
#define BUS_REG_BASE 0x7E000000
|
||||||
|
|
||||||
|
// Get virtual 8 and 32-bit pointers to register
|
||||||
|
#define REG8(m, x) ((volatile uint8_t *)((uint32_t)(m.virt) + (uint32_t)(x)))
|
||||||
|
#define REG32(m, x) ((volatile uint32_t *)((uint32_t)(m.virt) + (uint32_t)(x)))
|
||||||
|
// Get bus address of register
|
||||||
|
#define REG_BUS_ADDR(m, x) ((uint32_t)(m.bus) + (uint32_t)(x))
|
||||||
|
// Convert uncached memory virtual address to bus address
|
||||||
|
#define MEM_BUS_ADDR(mp, a) ((uint32_t)a - (uint32_t)mp->virt + (uint32_t)mp->bus)
|
||||||
|
// Convert bus address to physical address (for mmap)
|
||||||
|
#define BUS_PHYS_ADDR(a) ((void *)((uint32_t)(a) & ~0xC0000000))
|
||||||
|
|
||||||
|
// Size of memory page
|
||||||
|
#define PAGE_SIZE 0x1000
|
||||||
|
// Round up to nearest page
|
||||||
|
#define PAGE_ROUNDUP(n) ((n) % PAGE_SIZE == 0 ? (n) : ((n) + PAGE_SIZE) & ~(PAGE_SIZE - 1))
|
||||||
|
|
||||||
|
// Structure for mapped peripheral or memory
|
||||||
|
typedef struct {
|
||||||
|
int fd, // File descriptor
|
||||||
|
h, // Memory handle
|
||||||
|
size; // Memory size
|
||||||
|
void *bus, // Bus address
|
||||||
|
*virt, // Virtual address
|
||||||
|
*phys; // Physical address
|
||||||
|
} MEM_MAP;
|
||||||
|
|
||||||
|
// Use mmap to obtain virtual address, given physical
|
||||||
|
void *map_periph(MEM_MAP *mp, void *phys, int size);
|
||||||
|
|
||||||
|
// Free mapped peripheral or memory
|
||||||
|
void unmap_periph_mem(MEM_MAP *mp);
|
||||||
|
|
||||||
|
void *map_segment(void *addr, int size);
|
||||||
|
void unmap_segment(void *addr, int size);
|
||||||
|
|
||||||
|
#endif // __COMMON_H__
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#include "rpi_dma.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "rpi_videocore.h"
|
||||||
|
|
||||||
|
// DMA channels and data requests
|
||||||
|
#define DMA_SMI_DREQ 4
|
||||||
|
#define DMA_PWM_DREQ 5
|
||||||
|
#define DMA_SPI_TX_DREQ 6
|
||||||
|
#define DMA_SPI_RX_DREQ 7
|
||||||
|
#define DMA_BASE (PHYS_REG_BASE + 0x007000)
|
||||||
|
// DMA register addresses offset by 0x100 * chan_num
|
||||||
|
#define DMA_CS 0x00
|
||||||
|
#define DMA_CONBLK_AD 0x04
|
||||||
|
#define DMA_TI 0x08
|
||||||
|
#define DMA_SRCE_AD 0x0c
|
||||||
|
#define DMA_DEST_AD 0x10
|
||||||
|
#define DMA_TXFR_LEN 0x14
|
||||||
|
#define DMA_STRIDE 0x18
|
||||||
|
#define DMA_NEXTCONBK 0x1c
|
||||||
|
#define DMA_DEBUG 0x20
|
||||||
|
#define DMA_REG(ch, r) ((r) == DMA_ENABLE ? DMA_ENABLE : (ch)*0x100 + (r))
|
||||||
|
#define DMA_ENABLE 0xff0
|
||||||
|
// DMA register values
|
||||||
|
#define DMA_WAIT_RESP (1 << 3)
|
||||||
|
#define DMA_CB_DEST_INC (1 << 4)
|
||||||
|
#define DMA_DEST_DREQ (1 << 6)
|
||||||
|
#define DMA_CB_SRCE_INC (1 << 8)
|
||||||
|
#define DMA_SRCE_DREQ (1 << 10)
|
||||||
|
#define DMA_PRIORITY(n) ((n) << 16)
|
||||||
|
|
||||||
|
// Virtual memory pointers to acceess GPIO, DMA and PWM from user space
|
||||||
|
MEM_MAP dma_regs;
|
||||||
|
|
||||||
|
char *dma_regstrs[] = {"DMA CS", "CB_AD", "TI", "SRCE_AD", "DEST_AD",
|
||||||
|
"TFR_LEN", "STRIDE", "NEXT_CB", "DEBUG", ""};
|
||||||
|
|
||||||
|
void dma_setup(MEM_MAP *mp, int chan, int nsamp, uint8_t **txdata, int offset, uint32_t dest_ad) {
|
||||||
|
map_periph(&dma_regs, (void *)DMA_BASE, PAGE_SIZE);
|
||||||
|
|
||||||
|
DMA_CB *cbs = mp->virt;
|
||||||
|
|
||||||
|
*txdata = (uint8_t *)(cbs + offset);
|
||||||
|
enable_dma(chan);
|
||||||
|
cbs[0].ti = DMA_DEST_DREQ | (DMA_SMI_DREQ << 16) | DMA_CB_SRCE_INC | DMA_WAIT_RESP;
|
||||||
|
cbs[0].tfr_len = nsamp;
|
||||||
|
cbs[0].srce_ad = MEM_BUS_ADDR(mp, *txdata);
|
||||||
|
cbs[0].dest_ad = dest_ad;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dma_close() { unmap_periph_mem(&dma_regs); }
|
||||||
|
|
||||||
|
// Enable and reset DMA
|
||||||
|
void enable_dma(int chan) {
|
||||||
|
*REG32(dma_regs, DMA_ENABLE) |= (1 << chan);
|
||||||
|
*REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start DMA, given first control block
|
||||||
|
void start_dma(MEM_MAP *mp, int chan, DMA_CB *cbp, uint32_t csval) {
|
||||||
|
*REG32(dma_regs, DMA_REG(chan, DMA_CONBLK_AD)) = MEM_BUS_ADDR(mp, cbp);
|
||||||
|
*REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 2; // Clear 'end' flag
|
||||||
|
*REG32(dma_regs, DMA_REG(chan, DMA_DEBUG)) = 7; // Clear error bits
|
||||||
|
*REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 | csval; // Start DMA
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return remaining transfer length
|
||||||
|
uint32_t dma_transfer_len(int chan) { return (*REG32(dma_regs, DMA_REG(chan, DMA_TXFR_LEN))); }
|
||||||
|
|
||||||
|
// Check if DMA is active
|
||||||
|
uint32_t dma_active(int chan) { return ((*REG32(dma_regs, DMA_REG(chan, DMA_CS))) & 1); }
|
||||||
|
|
||||||
|
// Halt current DMA operation by resetting controller
|
||||||
|
void stop_dma(int chan) {
|
||||||
|
if (dma_regs.virt)
|
||||||
|
*REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display DMA registers
|
||||||
|
void disp_dma(int chan) {
|
||||||
|
volatile uint32_t *p = REG32(dma_regs, DMA_REG(chan, DMA_CS));
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (dma_regstrs[i][0]) {
|
||||||
|
printf("%-7s %08X ", dma_regstrs[i++], *p++);
|
||||||
|
if (i % 5 == 0 || dma_regstrs[i][0] == 0)
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#if !defined(__RPI_DMA_H__)
|
||||||
|
#define __RPI_DMA_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
#define DMA_CHAN_A 10
|
||||||
|
#define DMA_CHAN_B 11
|
||||||
|
|
||||||
|
// DMA control block (must be 32-byte aligned)
|
||||||
|
typedef struct {
|
||||||
|
uint32_t ti, // Transfer info
|
||||||
|
srce_ad, // Source address
|
||||||
|
dest_ad, // Destination address
|
||||||
|
tfr_len, // Transfer length
|
||||||
|
stride, // Transfer stride
|
||||||
|
next_cb, // Next control block
|
||||||
|
debug, // Debug register, zero in control block
|
||||||
|
unused;
|
||||||
|
} DMA_CB __attribute__((aligned(32)));
|
||||||
|
|
||||||
|
void dma_setup(MEM_MAP *mp, int chan, int nsamp, uint8_t **txdata, int offset, uint32_t dest_ad);
|
||||||
|
void dma_close();
|
||||||
|
|
||||||
|
void enable_dma(int chan);
|
||||||
|
void start_dma(MEM_MAP *mp, int chan, DMA_CB *cbp, uint32_t csval);
|
||||||
|
uint32_t dma_transfer_len(int chan);
|
||||||
|
uint32_t dma_active(int chan);
|
||||||
|
void stop_dma(int chan);
|
||||||
|
void disp_dma(int chan);
|
||||||
|
|
||||||
|
#endif // __RPI_DMA_H__
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
#include "rpi_videocore.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
// Mailbox command/response structure
|
||||||
|
typedef struct {
|
||||||
|
uint32_t len, // Overall length (bytes)
|
||||||
|
req, // Zero for request, 1<<31 for response
|
||||||
|
tag, // Command number
|
||||||
|
blen, // Buffer length (bytes)
|
||||||
|
dlen; // Data length (bytes)
|
||||||
|
uint32_t uints[32 - 5]; // Data (108 bytes maximum)
|
||||||
|
} VC_MSG __attribute__((aligned(16)));
|
||||||
|
|
||||||
|
void disp_vc_msg(VC_MSG *msgp);
|
||||||
|
|
||||||
|
int open_mbox(void);
|
||||||
|
void close_mbox(int fd);
|
||||||
|
uint32_t msg_mbox(int fd, VC_MSG *msgp);
|
||||||
|
void *map_uncached_mem(MEM_MAP *mp, int size);
|
||||||
|
|
||||||
|
void videocore_setup(MEM_MAP *mp, int size) { map_uncached_mem(mp, size); }
|
||||||
|
void videocore_close(MEM_MAP *mp) {
|
||||||
|
unmap_periph_mem(mp);
|
||||||
|
if (mp->fd) {
|
||||||
|
unlock_vc_mem(mp->fd, mp->h);
|
||||||
|
free_vc_mem(mp->fd, mp->h);
|
||||||
|
close_mbox(mp->fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate uncached memory, get bus & phys addresses
|
||||||
|
void *map_uncached_mem(MEM_MAP *mp, int size) {
|
||||||
|
void *ret;
|
||||||
|
mp->size = PAGE_ROUNDUP(size);
|
||||||
|
mp->fd = open_mbox();
|
||||||
|
ret = (mp->h = alloc_vc_mem(mp->fd, mp->size, DMA_MEM_FLAGS)) > 0 &&
|
||||||
|
(mp->bus = lock_vc_mem(mp->fd, mp->h)) != 0 &&
|
||||||
|
(mp->virt = map_segment(BUS_PHYS_ADDR(mp->bus), mp->size)) != 0
|
||||||
|
? mp->virt
|
||||||
|
: 0;
|
||||||
|
log_info("VC mem handle %u, phys %p, virt %p", mp->h, mp->bus, mp->virt);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate memory on PAGE_SIZE boundary, return handle
|
||||||
|
uint32_t alloc_vc_mem(int fd, uint32_t size, VC_ALLOC_FLAGS flags) {
|
||||||
|
VC_MSG msg = {
|
||||||
|
.tag = 0x3000c, .blen = 12, .dlen = 12, .uints = {PAGE_ROUNDUP(size), PAGE_SIZE, flags}};
|
||||||
|
return (msg_mbox(fd, &msg));
|
||||||
|
}
|
||||||
|
// Lock allocated memory, return bus address
|
||||||
|
void *lock_vc_mem(int fd, int h) {
|
||||||
|
VC_MSG msg = {.tag = 0x3000d, .blen = 4, .dlen = 4, .uints = {h}};
|
||||||
|
return (h ? (void *)msg_mbox(fd, &msg) : 0);
|
||||||
|
}
|
||||||
|
// Unlock allocated memory
|
||||||
|
uint32_t unlock_vc_mem(int fd, int h) {
|
||||||
|
VC_MSG msg = {.tag = 0x3000e, .blen = 4, .dlen = 4, .uints = {h}};
|
||||||
|
return (h ? msg_mbox(fd, &msg) : 0);
|
||||||
|
}
|
||||||
|
// Free memory
|
||||||
|
uint32_t free_vc_mem(int fd, int h) {
|
||||||
|
VC_MSG msg = {.tag = 0x3000f, .blen = 4, .dlen = 4, .uints = {h}};
|
||||||
|
return (h ? msg_mbox(fd, &msg) : 0);
|
||||||
|
}
|
||||||
|
uint32_t set_vc_clock(int fd, int id, uint32_t freq) {
|
||||||
|
VC_MSG msg1 = {.tag = 0x38001, .blen = 8, .dlen = 8, .uints = {id, 1}};
|
||||||
|
VC_MSG msg2 = {.tag = 0x38002, .blen = 12, .dlen = 12, .uints = {id, freq, 0}};
|
||||||
|
msg_mbox(fd, &msg1);
|
||||||
|
disp_vc_msg(&msg1);
|
||||||
|
msg_mbox(fd, &msg2);
|
||||||
|
disp_vc_msg(&msg2);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display mailbox message
|
||||||
|
void disp_vc_msg(VC_MSG *msgp) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
printf("VC msg len=%X, req=%X, tag=%X, blen=%x, dlen=%x, data ", msgp->len, msgp->req, msgp->tag,
|
||||||
|
msgp->blen, msgp->dlen);
|
||||||
|
for (i = 0; i < msgp->blen / 4; i++)
|
||||||
|
printf("%08X ", msgp->uints[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open mailbox interface, return file descriptor
|
||||||
|
int open_mbox(void) {
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if ((fd = open("/dev/vcio", 0)) < 0)
|
||||||
|
log_error("can't open VC mailbox");
|
||||||
|
return (fd);
|
||||||
|
}
|
||||||
|
// Close mailbox interface
|
||||||
|
void close_mbox(int fd) {
|
||||||
|
if (fd >= 0)
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send message to mailbox, return first response int, 0 if error
|
||||||
|
uint32_t msg_mbox(int fd, VC_MSG *msgp) {
|
||||||
|
uint32_t ret = 0, i;
|
||||||
|
|
||||||
|
for (i = msgp->dlen / 4; i <= msgp->blen / 4; i += 4)
|
||||||
|
msgp->uints[i++] = 0;
|
||||||
|
msgp->len = (msgp->blen + 6) * 4;
|
||||||
|
msgp->req = 0;
|
||||||
|
if (ioctl(fd, _IOWR(100, 0, void *), msgp) < 0) {
|
||||||
|
log_error("VC IOCTL failed");
|
||||||
|
} else if ((msgp->req & 0x80000000) == 0) {
|
||||||
|
log_error("VC IOCTL error");
|
||||||
|
} else if (msgp->req == 0x80000001) {
|
||||||
|
log_error("VC IOCTL partial error");
|
||||||
|
} else {
|
||||||
|
ret = msgp->uints[0];
|
||||||
|
}
|
||||||
|
#if DEBUG
|
||||||
|
disp_vc_msg(msgp);
|
||||||
|
#endif
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#if !defined(__RPI_VIDEOCORE_H__)
|
||||||
|
#define __RPI_VIDEOCORE_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
// Videocore mailbox memory allocation flags, see:
|
||||||
|
// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
|
||||||
|
typedef enum {
|
||||||
|
MEM_FLAG_DISCARDABLE = 1 << 0, // can be resized to 0 at any time. Use for cached data
|
||||||
|
MEM_FLAG_NORMAL = 0 << 2, // normal allocating alias. Don't use from ARM
|
||||||
|
MEM_FLAG_DIRECT = 1 << 2, // 0xC alias uncached
|
||||||
|
MEM_FLAG_COHERENT = 2 << 2, // 0x8 alias. Non-allocating in L2 but coherent
|
||||||
|
MEM_FLAG_ZERO = 1 << 4, // initialise buffer to all zeros
|
||||||
|
MEM_FLAG_NO_INIT = 1 << 5, // don't initialise (default is initialise to all ones)
|
||||||
|
MEM_FLAG_HINT_PERMALOCK = 1 << 6, // Likely to be locked for long periods of time
|
||||||
|
MEM_FLAG_L1_NONALLOCATING = (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT) // Allocating in L2
|
||||||
|
} VC_ALLOC_FLAGS;
|
||||||
|
|
||||||
|
// VC flags for unchached DMA memory
|
||||||
|
#define DMA_MEM_FLAGS (MEM_FLAG_DIRECT | MEM_FLAG_ZERO)
|
||||||
|
|
||||||
|
void videocore_setup(MEM_MAP *mp, int size);
|
||||||
|
void videocore_close(MEM_MAP *mp);
|
||||||
|
|
||||||
|
uint32_t alloc_vc_mem(int fd, uint32_t size, VC_ALLOC_FLAGS flags);
|
||||||
|
void *lock_vc_mem(int fd, int h);
|
||||||
|
uint32_t unlock_vc_mem(int fd, int h);
|
||||||
|
uint32_t free_vc_mem(int fd, int h);
|
||||||
|
uint32_t set_vc_clock(int fd, int id, uint32_t freq);
|
||||||
|
|
||||||
|
#endif // __RPI_VIDEOCORE_H__
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
#include "rpi_gpio.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
// GPIO register definitions
|
||||||
|
#define GPIO_BASE (PHYS_REG_BASE + 0x200000)
|
||||||
|
#define GPIO_MODE0 0x00
|
||||||
|
#define GPIO_SET0 0x1c
|
||||||
|
#define GPIO_CLR0 0x28
|
||||||
|
#define GPIO_LEV0 0x34
|
||||||
|
#define GPIO_GPPUD 0x94
|
||||||
|
#define GPIO_GPPUDCLK0 0x98
|
||||||
|
|
||||||
|
#define GPIO_MODE_STRS "IN", "OUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3"
|
||||||
|
|
||||||
|
bool isInitialized = false;
|
||||||
|
|
||||||
|
// Virtual memory pointers to acceess GPIO, DMA and PWM from user space
|
||||||
|
MEM_MAP gpio_regs;
|
||||||
|
|
||||||
|
char *gpio_mode_strs[] = {GPIO_MODE_STRS};
|
||||||
|
|
||||||
|
// definitions
|
||||||
|
|
||||||
|
void gpio_setup() {
|
||||||
|
if (!isInitialized) {
|
||||||
|
map_periph(&gpio_regs, (void *)GPIO_BASE, PAGE_SIZE);
|
||||||
|
isInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpio_close() {
|
||||||
|
if (isInitialized) {
|
||||||
|
unmap_periph_mem(&gpio_regs);
|
||||||
|
isInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set input or output with pullups
|
||||||
|
void gpio_set(int pin, int mode, int pull) {
|
||||||
|
gpio_mode(pin, mode);
|
||||||
|
gpio_pull(pin, pull);
|
||||||
|
}
|
||||||
|
// Set I/P pullup or pulldown
|
||||||
|
void gpio_pull(int pin, int pull) {
|
||||||
|
volatile uint32_t *reg = REG32(gpio_regs, GPIO_GPPUDCLK0) + pin / 32;
|
||||||
|
|
||||||
|
*REG32(gpio_regs, GPIO_GPPUD) = pull;
|
||||||
|
usleep(2);
|
||||||
|
*reg = pin << (pin % 32);
|
||||||
|
usleep(2);
|
||||||
|
*REG32(gpio_regs, GPIO_GPPUD) = 0;
|
||||||
|
*reg = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set input or output
|
||||||
|
void gpio_mode(int pin, int mode) {
|
||||||
|
if (gpio_regs.virt) {
|
||||||
|
volatile uint32_t *reg = REG32(gpio_regs, GPIO_MODE0) + pin / 10, shift = (pin % 10) * 3;
|
||||||
|
*reg = (*reg & ~(7 << shift)) | (mode << shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set an O/P pin
|
||||||
|
void gpio_out(int pin, int val) {
|
||||||
|
volatile uint32_t *reg = REG32(gpio_regs, val ? GPIO_SET0 : GPIO_CLR0) + pin / 32;
|
||||||
|
*reg = 1 << (pin % 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an I/P pin value
|
||||||
|
uint8_t gpio_in(int pin) {
|
||||||
|
volatile uint32_t *reg = REG32(gpio_regs, GPIO_LEV0) + pin / 32;
|
||||||
|
return (((*reg) >> (pin % 32)) & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display the values in a GPIO mode register
|
||||||
|
void disp_mode_vals(uint32_t mode) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 10; i++)
|
||||||
|
printf("%u:%-4s ", i, gpio_mode_strs[(mode >> (i * 3)) & 7]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#if !defined(__RPI_GPIO_H__)
|
||||||
|
#define __RPI_GPIO_H__
|
||||||
|
|
||||||
|
// GPIO I/O definitions
|
||||||
|
#define GPIO_IN 0
|
||||||
|
#define GPIO_OUT 1
|
||||||
|
#define GPIO_ALT0 4
|
||||||
|
#define GPIO_ALT1 5
|
||||||
|
#define GPIO_ALT2 6
|
||||||
|
#define GPIO_ALT3 7
|
||||||
|
#define GPIO_ALT4 3
|
||||||
|
#define GPIO_ALT5 2
|
||||||
|
|
||||||
|
#define GPIO_NOPULL 0
|
||||||
|
#define GPIO_PULLDN 1
|
||||||
|
#define GPIO_PULLUP 2
|
||||||
|
|
||||||
|
void gpio_setup();
|
||||||
|
|
||||||
|
void gpio_close();
|
||||||
|
|
||||||
|
void gpio_pull(int pin, int pull);
|
||||||
|
|
||||||
|
void gpio_mode(int pin, int mode);
|
||||||
|
|
||||||
|
#endif // __RPI_GPIO_H__
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
#include "rpi_leddriver.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "../../rpi_param.h"
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
#include "../dma/rpi_dma.h"
|
||||||
|
#include "../dma/rpi_videocore.h"
|
||||||
|
#include "../gpio/rpi_gpio.h"
|
||||||
|
#include "../smi/rpi_smi.h"
|
||||||
|
|
||||||
|
#if PHYS_REG_BASE == PI_4_REG_BASE // Timings for RPi v4 (1.5 GHz)
|
||||||
|
#define SMI_TIMING 10, 15, 30, 15 // 400 ns cycle time
|
||||||
|
#else // Timings for RPi v0-3 (1 GHz)
|
||||||
|
#define SMI_TIMING 10, 10, 20, 10 // 400 ns cycle time
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TX_TEST 0 // If non-zero, use dummy Tx data
|
||||||
|
#define LED_NBITS 24 // Number of data bits per LED
|
||||||
|
#define LED_PREBITS 4 // Number of zero bits before LED data
|
||||||
|
#define LED_POSTBITS 4 // Number of zero bits after LED data
|
||||||
|
#define BIT_NPULSES 3 // Number of O/P pulses per LED bit
|
||||||
|
|
||||||
|
// Length of data for 1 row (1 LED on each channel)
|
||||||
|
#define LED_DLEN (LED_NBITS * BIT_NPULSES)
|
||||||
|
|
||||||
|
// Transmit data type, 8 or 16 bits
|
||||||
|
#if LED_NCHANS > 8
|
||||||
|
#define TXDATA_T uint16_t
|
||||||
|
#else
|
||||||
|
#define TXDATA_T uint8_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Ofset into Tx data buffer, given LED number in chan
|
||||||
|
#define LED_TX_OSET(n) (LED_PREBITS + (LED_DLEN * (n)))
|
||||||
|
|
||||||
|
// Size of data buffers & NV memory, given number of LEDs per chan
|
||||||
|
#define TX_BUFF_LEN(n) (LED_TX_OSET(n) + LED_POSTBITS)
|
||||||
|
#define TX_BUFF_SIZE(n) (TX_BUFF_LEN(n) * sizeof(TXDATA_T))
|
||||||
|
#define VC_MEM_SIZE (PAGE_SIZE + TX_BUFF_SIZE(CHAN_MAXLEDS))
|
||||||
|
|
||||||
|
/* Global */
|
||||||
|
MEM_MAP vc_mem;
|
||||||
|
TXDATA_T *txdata;
|
||||||
|
TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)];
|
||||||
|
|
||||||
|
void swap_bytes();
|
||||||
|
|
||||||
|
void leddriver_setup() {
|
||||||
|
videocore_setup(&vc_mem, VC_MEM_SIZE);
|
||||||
|
gpio_setup();
|
||||||
|
smi_setup(LED_NCHANS, SMI_TIMING, &vc_mem, TX_BUFF_LEN(CHAN_MAXLEDS), &txdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
void leddriver_close() {
|
||||||
|
videocore_close(&vc_mem);
|
||||||
|
smi_close(LED_NCHANS);
|
||||||
|
gpio_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_color(uint32_t rgb, int index) {
|
||||||
|
int msk;
|
||||||
|
TXDATA_T *txd = &(tx_buffer[LED_TX_OSET(index)]);
|
||||||
|
|
||||||
|
// For each bit of the 24-bit RGB values..
|
||||||
|
for (size_t n = 0; n < LED_NBITS; n++) {
|
||||||
|
// Mask to convert RGB to GRB, M.S bit first
|
||||||
|
msk = n == 0 ? 0x800000 : n == 8 ? 0x8000 : n == 16 ? 0x80 : msk >> 1;
|
||||||
|
// 1st byte or word is a high pulse on all lines
|
||||||
|
txd[0] = (TXDATA_T)0xffff;
|
||||||
|
// 2nd has high or low bits from data
|
||||||
|
// 3rd is a low pulse
|
||||||
|
txd[1] = txd[2] = 0;
|
||||||
|
if (rgb & msk) {
|
||||||
|
txd[1] = (TXDATA_T)0xffff;
|
||||||
|
}
|
||||||
|
txd += BIT_NPULSES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Tx data for 8 or 16 chans, 1 LED per chan, given 1 RGB val per chan
|
||||||
|
// Logic 1 is 0.8us high, 0.4 us low, logic 0 is 0.4us high, 0.8us low
|
||||||
|
void rgb_txdata(int *rgbs, int index) {
|
||||||
|
int i, n, msk;
|
||||||
|
TXDATA_T *txd = &(tx_buffer[LED_TX_OSET(index)]);
|
||||||
|
|
||||||
|
// For each bit of the 24-bit RGB values..
|
||||||
|
for (n = 0; n < LED_NBITS; n++) {
|
||||||
|
// Mask to convert RGB to GRB, M.S bit first
|
||||||
|
msk = n == 0 ? 0x800000 : n == 8 ? 0x8000 : n == 16 ? 0x80 : msk >> 1;
|
||||||
|
// 1st byte or word is a high pulse on all lines
|
||||||
|
txd[0] = (TXDATA_T)0xffff;
|
||||||
|
// 2nd has high or low bits from data
|
||||||
|
// 3rd is a low pulse
|
||||||
|
txd[1] = txd[2] = 0;
|
||||||
|
for (i = 0; i < LED_NCHANS; i++) {
|
||||||
|
if (rgbs[i] & msk)
|
||||||
|
txd[1] |= (1 << i);
|
||||||
|
}
|
||||||
|
txd += BIT_NPULSES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void leddriver_refresh() {
|
||||||
|
#if LED_NCHANS <= 8
|
||||||
|
swap_bytes();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (dma_active(DMA_CHAN_A)) {
|
||||||
|
usleep(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(txdata, tx_buffer, TX_BUFF_SIZE(CHAN_MAXLEDS));
|
||||||
|
enable_dma(DMA_CHAN_A);
|
||||||
|
start_smi(&vc_mem);
|
||||||
|
usleep(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap adjacent bytes in transmit data
|
||||||
|
void swap_bytes() {
|
||||||
|
uint16_t *wp = (uint16_t *)tx_buffer;
|
||||||
|
int len = TX_BUFF_SIZE(CHAN_MAXLEDS);
|
||||||
|
|
||||||
|
len = (len + 1) / 2;
|
||||||
|
while (len-- > 0) {
|
||||||
|
*wp = __builtin_bswap16(*wp);
|
||||||
|
wp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* source :
|
||||||
|
* https://github.com/adafruit/Adafruit_NeoPixel/blob/216ccdbff399750f5b02d4cc804c598399e39713/Adafruit_NeoPixel.cpp#L2414
|
||||||
|
*/
|
||||||
|
uint32_t ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) {
|
||||||
|
|
||||||
|
uint8_t r, g, b;
|
||||||
|
|
||||||
|
// Remap 0-65535 to 0-1529. Pure red is CENTERED on the 64K rollover;
|
||||||
|
// 0 is not the start of pure red, but the midpoint...a few values above
|
||||||
|
// zero and a few below 65536 all yield pure red (similarly, 32768 is the
|
||||||
|
// midpoint, not start, of pure cyan). The 8-bit RGB hexcone (256 values
|
||||||
|
// each for red, green, blue) really only allows for 1530 distinct hues
|
||||||
|
// (not 1536, more on that below), but the full unsigned 16-bit type was
|
||||||
|
// chosen for hue so that one's code can easily handle a contiguous color
|
||||||
|
// wheel by allowing hue to roll over in either direction.
|
||||||
|
hue = (hue * 1530L + 32768) / 65536;
|
||||||
|
// Because red is centered on the rollover point (the +32768 above,
|
||||||
|
// essentially a fixed-point +0.5), the above actually yields 0 to 1530,
|
||||||
|
// where 0 and 1530 would yield the same thing. Rather than apply a
|
||||||
|
// costly modulo operator, 1530 is handled as a special case below.
|
||||||
|
|
||||||
|
// So you'd think that the color "hexcone" (the thing that ramps from
|
||||||
|
// pure red, to pure yellow, to pure green and so forth back to red,
|
||||||
|
// yielding six slices), and with each color component having 256
|
||||||
|
// possible values (0-255), might have 1536 possible items (6*256),
|
||||||
|
// but in reality there's 1530. This is because the last element in
|
||||||
|
// each 256-element slice is equal to the first element of the next
|
||||||
|
// slice, and keeping those in there this would create small
|
||||||
|
// discontinuities in the color wheel. So the last element of each
|
||||||
|
// slice is dropped...we regard only elements 0-254, with item 255
|
||||||
|
// being picked up as element 0 of the next slice. Like this:
|
||||||
|
// Red to not-quite-pure-yellow is: 255, 0, 0 to 255, 254, 0
|
||||||
|
// Pure yellow to not-quite-pure-green is: 255, 255, 0 to 1, 255, 0
|
||||||
|
// Pure green to not-quite-pure-cyan is: 0, 255, 0 to 0, 255, 254
|
||||||
|
// and so forth. Hence, 1530 distinct hues (0 to 1529), and hence why
|
||||||
|
// the constants below are not the multiples of 256 you might expect.
|
||||||
|
|
||||||
|
// Convert hue to R,G,B (nested ifs faster than divide+mod+switch):
|
||||||
|
if (hue < 510) { // Red to Green-1
|
||||||
|
b = 0;
|
||||||
|
if (hue < 255) { // Red to Yellow-1
|
||||||
|
r = 255;
|
||||||
|
g = hue; // g = 0 to 254
|
||||||
|
} else { // Yellow to Green-1
|
||||||
|
r = 510 - hue; // r = 255 to 1
|
||||||
|
g = 255;
|
||||||
|
}
|
||||||
|
} else if (hue < 1020) { // Green to Blue-1
|
||||||
|
r = 0;
|
||||||
|
if (hue < 765) { // Green to Cyan-1
|
||||||
|
g = 255;
|
||||||
|
b = hue - 510; // b = 0 to 254
|
||||||
|
} else { // Cyan to Blue-1
|
||||||
|
g = 1020 - hue; // g = 255 to 1
|
||||||
|
b = 255;
|
||||||
|
}
|
||||||
|
} else if (hue < 1530) { // Blue to Red-1
|
||||||
|
g = 0;
|
||||||
|
if (hue < 1275) { // Blue to Magenta-1
|
||||||
|
r = hue - 1020; // r = 0 to 254
|
||||||
|
b = 255;
|
||||||
|
} else { // Magenta to Red-1
|
||||||
|
r = 255;
|
||||||
|
b = 1530 - hue; // b = 255 to 1
|
||||||
|
}
|
||||||
|
} else { // Last 0.5 Red (quicker than % operator)
|
||||||
|
r = 255;
|
||||||
|
g = b = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply saturation and value to R,G,B, pack into 32-bit result:
|
||||||
|
uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255
|
||||||
|
uint16_t s1 = 1 + sat; // 1 to 256; same reason
|
||||||
|
uint8_t s2 = 255 - sat; // 255 to 0
|
||||||
|
return ((((((r * s1) >> 8) + s2) * v1) & 0xff00) << 8) |
|
||||||
|
(((((g * s1) >> 8) + s2) * v1) & 0xff00) | (((((b * s1) >> 8) + s2) * v1) >> 8);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#if !defined(__RPI_LEDDRIVER_H__)
|
||||||
|
#define __RPI_LEDDRIVER_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void leddriver_setup();
|
||||||
|
|
||||||
|
void leddriver_close();
|
||||||
|
|
||||||
|
void set_color(uint32_t rgb, int index);
|
||||||
|
|
||||||
|
void rgb_txdata(int *rgbs, int index);
|
||||||
|
|
||||||
|
void leddriver_refresh();
|
||||||
|
|
||||||
|
uint32_t ColorHSV(uint16_t hue, uint8_t sat, uint8_t val);
|
||||||
|
|
||||||
|
#endif // __RPI_LEDDRIVER_H__
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
#set(FETCHCONTENT_FULLY_DISCONNECTED ON)
|
||||||
|
|
||||||
|
#set(BUILD_SHARED_LIBS off)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
logc
|
||||||
|
GIT_REPOSITORY "https://github.com/Tropicananass/log.c.git"
|
||||||
|
)
|
||||||
|
|
||||||
|
# add the log.c
|
||||||
|
FetchContent_MakeAvailable(logc)
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
#include "rpi_smi.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "../dma/rpi_dma.h"
|
||||||
|
#include "../gpio/rpi_gpio.h"
|
||||||
|
|
||||||
|
// GPIO first pin
|
||||||
|
#define SMI_SD0_PIN 8
|
||||||
|
|
||||||
|
// Data widths
|
||||||
|
#define SMI_8_BITS 0
|
||||||
|
#define SMI_16_BITS 1
|
||||||
|
#define SMI_18_BITS 2
|
||||||
|
#define SMI_9_BITS 3
|
||||||
|
|
||||||
|
// Clock registers and values
|
||||||
|
#define CLK_BASE (PHYS_REG_BASE + 0x101000)
|
||||||
|
// #define CLK_PWM_CTL 0xa0
|
||||||
|
// #define CLK_PWM_DIV 0xa4
|
||||||
|
#define CLK_SMI_CTL 0xb0
|
||||||
|
#define CLK_SMI_DIV 0xb4
|
||||||
|
#define CLK_PASSWD 0x5a000000
|
||||||
|
#define PWM_CLOCK_ID 0xa
|
||||||
|
|
||||||
|
// DMA request threshold
|
||||||
|
#define REQUEST_THRESH 2
|
||||||
|
|
||||||
|
// Register definitions
|
||||||
|
#define SMI_BASE (PHYS_REG_BASE + 0x600000)
|
||||||
|
#define SMI_CS 0x00 // Control & status
|
||||||
|
#define SMI_L 0x04 // Transfer length
|
||||||
|
#define SMI_A 0x08 // Address
|
||||||
|
#define SMI_D 0x0c // Data
|
||||||
|
#define SMI_DSR0 0x10 // Read settings device 0
|
||||||
|
#define SMI_DSW0 0x14 // Write settings device 0
|
||||||
|
#define SMI_DSR1 0x18 // Read settings device 1
|
||||||
|
#define SMI_DSW1 0x1c // Write settings device 1
|
||||||
|
#define SMI_DSR2 0x20 // Read settings device 2
|
||||||
|
#define SMI_DSW2 0x24 // Write settings device 2
|
||||||
|
#define SMI_DSR3 0x28 // Read settings device 3
|
||||||
|
#define SMI_DSW3 0x2c // Write settings device 3
|
||||||
|
#define SMI_DMC 0x30 // DMA control
|
||||||
|
#define SMI_DCS 0x34 // Direct control/status
|
||||||
|
#define SMI_DCA 0x38 // Direct address
|
||||||
|
#define SMI_DCD 0x3c // Direct data
|
||||||
|
#define SMI_FD 0x40 // FIFO debug
|
||||||
|
#define SMI_REGLEN (SMI_FD * 4)
|
||||||
|
|
||||||
|
// Union of 32-bit value with register bitfields
|
||||||
|
#define REG_DEF(name, fields) \
|
||||||
|
typedef union { \
|
||||||
|
struct { \
|
||||||
|
volatile uint32_t fields; \
|
||||||
|
}; \
|
||||||
|
volatile uint32_t value; \
|
||||||
|
} name
|
||||||
|
|
||||||
|
// Control and status register
|
||||||
|
#define SMI_CS_FIELDS \
|
||||||
|
enable: \
|
||||||
|
1, done : 1, active : 1, start : 1, clear : 1, write : 1, _x1 : 2, teen : 1, intd : 1, intt : 1, \
|
||||||
|
intr : 1, pvmode : 1, seterr : 1, pxldat : 1, edreq : 1, _x2 : 8, _x3 : 1, aferr : 1, \
|
||||||
|
txw : 1, rxr : 1, txd : 1, rxd : 1, txe : 1, rxf : 1
|
||||||
|
REG_DEF(SMI_CS_REG, SMI_CS_FIELDS);
|
||||||
|
|
||||||
|
// Data length register
|
||||||
|
#define SMI_L_FIELDS \
|
||||||
|
len: \
|
||||||
|
32
|
||||||
|
REG_DEF(SMI_L_REG, SMI_L_FIELDS);
|
||||||
|
|
||||||
|
// Address & device number
|
||||||
|
#define SMI_A_FIELDS \
|
||||||
|
addr: \
|
||||||
|
6, _x1 : 2, dev : 2
|
||||||
|
REG_DEF(SMI_A_REG, SMI_A_FIELDS);
|
||||||
|
|
||||||
|
// Data FIFO
|
||||||
|
#define SMI_D_FIELDS \
|
||||||
|
data: \
|
||||||
|
32
|
||||||
|
REG_DEF(SMI_D_REG, SMI_D_FIELDS);
|
||||||
|
|
||||||
|
// DMA control register
|
||||||
|
#define SMI_DMC_FIELDS \
|
||||||
|
reqw: \
|
||||||
|
6, reqr : 6, panicw : 6, panicr : 6, dmap : 1, _x1 : 3, dmaen : 1
|
||||||
|
REG_DEF(SMI_DMC_REG, SMI_DMC_FIELDS);
|
||||||
|
|
||||||
|
// Device settings: read (1 of 4)
|
||||||
|
#define SMI_DSR_FIELDS \
|
||||||
|
rstrobe: \
|
||||||
|
7, rdreq : 1, rpace : 7, rpaceall : 1, rhold : 6, fsetup : 1, mode68 : 1, rsetup : 6, rwidth : 2
|
||||||
|
REG_DEF(SMI_DSR_REG, SMI_DSR_FIELDS);
|
||||||
|
|
||||||
|
// Device settings: write (1 of 4)
|
||||||
|
#define SMI_DSW_FIELDS \
|
||||||
|
wstrobe: \
|
||||||
|
7, wdreq : 1, wpace : 7, wpaceall : 1, whold : 6, wswap : 1, wformat : 1, wsetup : 6, wwidth : 2
|
||||||
|
REG_DEF(SMI_DSW_REG, SMI_DSW_FIELDS);
|
||||||
|
|
||||||
|
// Direct control register
|
||||||
|
#define SMI_DCS_FIELDS \
|
||||||
|
enable: \
|
||||||
|
1, start : 1, done : 1, write : 1
|
||||||
|
REG_DEF(SMI_DCS_REG, SMI_DCS_FIELDS);
|
||||||
|
|
||||||
|
// Direct control address & device number
|
||||||
|
#define SMI_DCA_FIELDS \
|
||||||
|
addr: \
|
||||||
|
6, _x1 : 2, dev : 2
|
||||||
|
REG_DEF(SMI_DCA_REG, SMI_DCA_FIELDS);
|
||||||
|
|
||||||
|
// Direct control data
|
||||||
|
#define SMI_DCD_FIELDS \
|
||||||
|
data: \
|
||||||
|
32
|
||||||
|
REG_DEF(SMI_DCD_REG, SMI_DCD_FIELDS);
|
||||||
|
|
||||||
|
// Debug register
|
||||||
|
#define SMI_FLVL_FIELDS \
|
||||||
|
fcnt: \
|
||||||
|
6, _x1 : 2, flvl : 6
|
||||||
|
REG_DEF(SMI_FLVL_REG, SMI_FLVL_FIELDS);
|
||||||
|
|
||||||
|
// Pointers to SMI registers
|
||||||
|
volatile SMI_CS_REG *smi_cs;
|
||||||
|
volatile SMI_L_REG *smi_l;
|
||||||
|
volatile SMI_A_REG *smi_a;
|
||||||
|
volatile SMI_D_REG *smi_d;
|
||||||
|
volatile SMI_DMC_REG *smi_dmc;
|
||||||
|
volatile SMI_DSR_REG *smi_dsr;
|
||||||
|
volatile SMI_DSW_REG *smi_dsw;
|
||||||
|
volatile SMI_DCS_REG *smi_dcs;
|
||||||
|
volatile SMI_DCA_REG *smi_dca;
|
||||||
|
volatile SMI_DCD_REG *smi_dcd;
|
||||||
|
|
||||||
|
MEM_MAP smi_regs, clk_regs;
|
||||||
|
|
||||||
|
void setup_smi_dma(MEM_MAP *mp, int nsamp, uint8_t **txdata, int len);
|
||||||
|
|
||||||
|
void smi_setup(int channels, int ns, int setup, int strobe, int hold, MEM_MAP *mp, int nsamp,
|
||||||
|
uint8_t **txdata) {
|
||||||
|
map_periph(&smi_regs, (void *)SMI_BASE, PAGE_SIZE);
|
||||||
|
map_periph(&clk_regs, (void *)CLK_BASE, PAGE_SIZE);
|
||||||
|
|
||||||
|
int width = channels > 8 ? SMI_16_BITS : SMI_8_BITS;
|
||||||
|
int i, divi = ns / 2;
|
||||||
|
|
||||||
|
smi_cs = (SMI_CS_REG *)REG32(smi_regs, SMI_CS);
|
||||||
|
smi_l = (SMI_L_REG *)REG32(smi_regs, SMI_L);
|
||||||
|
smi_a = (SMI_A_REG *)REG32(smi_regs, SMI_A);
|
||||||
|
smi_d = (SMI_D_REG *)REG32(smi_regs, SMI_D);
|
||||||
|
smi_dmc = (SMI_DMC_REG *)REG32(smi_regs, SMI_DMC);
|
||||||
|
smi_dsr = (SMI_DSR_REG *)REG32(smi_regs, SMI_DSR0);
|
||||||
|
smi_dsw = (SMI_DSW_REG *)REG32(smi_regs, SMI_DSW0);
|
||||||
|
smi_dcs = (SMI_DCS_REG *)REG32(smi_regs, SMI_DCS);
|
||||||
|
smi_dca = (SMI_DCA_REG *)REG32(smi_regs, SMI_DCA);
|
||||||
|
smi_dcd = (SMI_DCD_REG *)REG32(smi_regs, SMI_DCD);
|
||||||
|
smi_cs->value = smi_l->value = smi_a->value = 0;
|
||||||
|
smi_dsr->value = smi_dsw->value = smi_dcs->value = smi_dca->value = 0;
|
||||||
|
if (*REG32(clk_regs, CLK_SMI_DIV) != divi << 12) {
|
||||||
|
*REG32(clk_regs, CLK_SMI_CTL) = CLK_PASSWD | (1 << 5);
|
||||||
|
usleep(10);
|
||||||
|
while (*REG32(clk_regs, CLK_SMI_CTL) & (1 << 7))
|
||||||
|
;
|
||||||
|
usleep(10);
|
||||||
|
*REG32(clk_regs, CLK_SMI_DIV) = CLK_PASSWD | (divi << 12);
|
||||||
|
usleep(10);
|
||||||
|
*REG32(clk_regs, CLK_SMI_CTL) = CLK_PASSWD | 6 | (1 << 4);
|
||||||
|
usleep(10);
|
||||||
|
while ((*REG32(clk_regs, CLK_SMI_CTL) & (1 << 7)) == 0)
|
||||||
|
;
|
||||||
|
usleep(100);
|
||||||
|
}
|
||||||
|
if (smi_cs->seterr)
|
||||||
|
smi_cs->seterr = 1;
|
||||||
|
smi_dsr->rsetup = smi_dsw->wsetup = setup;
|
||||||
|
smi_dsr->rstrobe = smi_dsw->wstrobe = strobe;
|
||||||
|
smi_dsr->rhold = smi_dsw->whold = hold;
|
||||||
|
smi_dmc->panicr = smi_dmc->panicw = 8;
|
||||||
|
smi_dmc->reqr = smi_dmc->reqw = REQUEST_THRESH;
|
||||||
|
smi_dsr->rwidth = smi_dsw->wwidth = width;
|
||||||
|
for (i = 0; i < channels; i++)
|
||||||
|
gpio_mode(SMI_SD0_PIN + i, GPIO_ALT1);
|
||||||
|
|
||||||
|
setup_smi_dma(mp, nsamp, txdata, width + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void smi_close(int channels) {
|
||||||
|
for (size_t i = 0; i < channels; ++i)
|
||||||
|
gpio_mode(SMI_SD0_PIN + i, GPIO_IN);
|
||||||
|
if (smi_regs.virt) {
|
||||||
|
*REG32(smi_regs, SMI_CS) = 0;
|
||||||
|
}
|
||||||
|
stop_dma(DMA_CHAN_A);
|
||||||
|
unmap_periph_mem(&clk_regs);
|
||||||
|
unmap_periph_mem(&smi_regs);
|
||||||
|
dma_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start SMI DMA transfers
|
||||||
|
void start_smi(MEM_MAP *mp) {
|
||||||
|
DMA_CB *cbs = mp->virt;
|
||||||
|
|
||||||
|
start_dma(mp, DMA_CHAN_A, &cbs[0], 0);
|
||||||
|
smi_cs->start = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
|
||||||
|
// Set up SMI transfers using DMA
|
||||||
|
void setup_smi_dma(MEM_MAP *mp, int nsamp, uint8_t **txdata, int len) {
|
||||||
|
smi_dmc->dmaen = 1;
|
||||||
|
smi_cs->enable = 1;
|
||||||
|
smi_cs->clear = 1;
|
||||||
|
smi_cs->pxldat = 1;
|
||||||
|
smi_l->len = nsamp * len;
|
||||||
|
smi_cs->write = 1;
|
||||||
|
|
||||||
|
dma_setup(mp, DMA_CHAN_A, nsamp, txdata, len, REG_BUS_ADDR(smi_regs, SMI_D));
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#if !defined(__RPI_SMI_H__)
|
||||||
|
#define __RPI_SMI_H__
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void smi_setup(int channels, int ns, int setup, int strobe, int hold, MEM_MAP *mp, int nsamp,
|
||||||
|
uint8_t **txdata);
|
||||||
|
|
||||||
|
void smi_close(int channels);
|
||||||
|
|
||||||
|
void start_smi(MEM_MAP *mp);
|
||||||
|
|
||||||
|
#endif // __RPI_SMI_H__
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
use crate::cputasks::modes::AppMode;
|
||||||
|
|
||||||
|
pub enum Message {
|
||||||
|
ModeChanged { mode: AppMode },
|
||||||
|
// Other messages...
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod modes;
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
pub mod artnet;
|
||||||
|
pub mod diagnostics;
|
||||||
|
pub mod manual;
|
||||||
|
pub mod standalone;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crossbeam::channel::Receiver;
|
||||||
|
|
||||||
|
use crate::channels::Message;
|
||||||
|
use crate::devices::led_driver::LedDriver;
|
||||||
|
|
||||||
|
pub trait AppModeHandler {
|
||||||
|
fn enter(&mut self);
|
||||||
|
fn run(&mut self, led_driver: &mut LedDriver);
|
||||||
|
fn exit(&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum AppMode {
|
||||||
|
Diagnostics,
|
||||||
|
ArtNet,
|
||||||
|
Standalone,
|
||||||
|
Manual,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppMode {
|
||||||
|
pub fn for_each<F: FnMut(AppMode)>(mut f: F) {
|
||||||
|
f(AppMode::Diagnostics);
|
||||||
|
f(AppMode::ArtNet);
|
||||||
|
f(AppMode::Standalone);
|
||||||
|
f(AppMode::Manual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<usize> for AppMode {
|
||||||
|
fn from(value: usize) -> Self {
|
||||||
|
match value {
|
||||||
|
1 => AppMode::ArtNet,
|
||||||
|
2 => AppMode::Standalone,
|
||||||
|
3 => AppMode::Manual,
|
||||||
|
_ => AppMode::Diagnostics,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ModeManager {
|
||||||
|
mode_handler_map: HashMap<AppMode, Box<dyn AppModeHandler + Send>>,
|
||||||
|
mode: Arc<Mutex<AppMode>>,
|
||||||
|
mode_rx: Receiver<Message>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModeManager {
|
||||||
|
pub fn new(mode_rx: Receiver<Message>) -> Self {
|
||||||
|
let mut handlers: HashMap<AppMode, Box<dyn AppModeHandler + Send>> = HashMap::new();
|
||||||
|
handlers.insert(AppMode::Manual, Box::new(manual::ManualMode::new()));
|
||||||
|
handlers.insert(
|
||||||
|
AppMode::Standalone,
|
||||||
|
Box::new(standalone::StandaloneMode::new()),
|
||||||
|
);
|
||||||
|
handlers.insert(
|
||||||
|
AppMode::Diagnostics,
|
||||||
|
Box::new(diagnostics::DiagnosticsMode::new()),
|
||||||
|
);
|
||||||
|
handlers.insert(AppMode::ArtNet, Box::new(artnet::ArtNetMode::new()));
|
||||||
|
|
||||||
|
let mode_manager = ModeManager {
|
||||||
|
mode_handler_map: handlers,
|
||||||
|
mode: Arc::new(Mutex::new(AppMode::Diagnostics)),
|
||||||
|
mode_rx: mode_rx.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
mode_manager
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self, led_driver: &mut LedDriver) {
|
||||||
|
if let Ok(Message::ModeChanged { mode: next }) = self.mode_rx.try_recv() {
|
||||||
|
let current = self.get_current_mode();
|
||||||
|
if current != next {
|
||||||
|
log::info!(" Changing mode from {:?} to {:?}", current, next);
|
||||||
|
self.get_handler(Some(current)).exit();
|
||||||
|
self.get_handler(Some(next)).enter();
|
||||||
|
self.set_current_mode(next);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.get_handler(None).run(led_driver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_current_mode(&self) -> AppMode {
|
||||||
|
*self.mode.lock().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_current_mode(&self, mode: AppMode) {
|
||||||
|
*self.mode.lock().unwrap() = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_handler(&mut self, mode: Option<AppMode>) -> &mut Box<dyn AppModeHandler + Send> {
|
||||||
|
let mode = &mode.unwrap_or_else(|| self.get_current_mode());
|
||||||
|
self.mode_handler_map
|
||||||
|
.get_mut(mode)
|
||||||
|
.expect(&format!("No handler found for mode: {:?}", mode))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
use artnet_protocol::{ArtCommand, PollReply};
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::net::{Ipv4Addr, UdpSocket};
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use crate::cputasks::modes::AppModeHandler;
|
||||||
|
use crate::devices::led_driver::LedDriver;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct ArtNetMode {
|
||||||
|
socket: Option<UdpSocket>,
|
||||||
|
last_frame_time: Option<Instant>,
|
||||||
|
statistics: ArtNetModeStatistics,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArtNetMode {
|
||||||
|
const _SHORT: &str = "LightSabre\0";
|
||||||
|
const _SHORT_PAD: [u8; 18 - Self::_SHORT.len()] = [0; 7];
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
ArtNetMode::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppModeHandler for ArtNetMode {
|
||||||
|
fn enter(&mut self) {
|
||||||
|
log::debug!("[ArtNet] Entering ArtNet Mode");
|
||||||
|
|
||||||
|
let mut attempts = 0_usize;
|
||||||
|
let socket = loop {
|
||||||
|
match UdpSocket::bind(("0.0.0.0", 6454)) {
|
||||||
|
Ok(socket) => break socket,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("[ArtNet] Failed to bind ArtNet socket: {e}, retrying...");
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attempts += 1;
|
||||||
|
if attempts > 10 {
|
||||||
|
panic!("[ArtNet] Failed to bind ArtNet socket after multiple attempts");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
socket.set_broadcast(true).unwrap();
|
||||||
|
socket.set_nonblocking(true).unwrap();
|
||||||
|
self.socket = Some(socket);
|
||||||
|
log::debug!("[ArtNet] ArtNet Mode initialized and listening on port 6454");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self, led_driver: &mut LedDriver) {
|
||||||
|
log::trace!("[ArtNet] Running...");
|
||||||
|
let buf = &mut [0; 530];
|
||||||
|
let mut is_first_data_frame = true;
|
||||||
|
loop {
|
||||||
|
match self
|
||||||
|
.socket
|
||||||
|
.as_mut()
|
||||||
|
.expect("ArtNet socket not initialized")
|
||||||
|
.recv_from(buf)
|
||||||
|
{
|
||||||
|
Ok((num_bytes_read, from)) => {
|
||||||
|
log::trace!("[ArtNet] Received {} bytes from {}", num_bytes_read, from);
|
||||||
|
|
||||||
|
let command =
|
||||||
|
ArtCommand::from_buffer(buf).expect("Failed to parse ArtNet command");
|
||||||
|
match command {
|
||||||
|
ArtCommand::Poll(_) => {
|
||||||
|
log::trace!("[ArtNet] Received Poll command, responding...");
|
||||||
|
let poll_reply: PollReply = PollReply {
|
||||||
|
address: Ipv4Addr::from_bits(0),
|
||||||
|
port: 6454,
|
||||||
|
version: [0, 1],
|
||||||
|
port_address: [0; 2],
|
||||||
|
oem: [0x01, 0x90],
|
||||||
|
ubea_version: 0,
|
||||||
|
status_1: 0,
|
||||||
|
esta_code: 0,
|
||||||
|
short_name: b"LightSabre\0\0\0\0\0\0\0\0".to_owned(),
|
||||||
|
long_name: b"LightSabre Artnet Node\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0".to_owned(),
|
||||||
|
node_report: [0; 64],
|
||||||
|
num_ports: [0, 1],
|
||||||
|
port_types: [0x80, 0, 0, 0],
|
||||||
|
good_input: [0; 4],
|
||||||
|
good_output: [0; 4],
|
||||||
|
swin: [0; 4],
|
||||||
|
swout: [0; 4],
|
||||||
|
sw_video: 0,
|
||||||
|
sw_macro: 0,
|
||||||
|
sw_remote: 0,
|
||||||
|
spare: [0; 3],
|
||||||
|
style: 0,
|
||||||
|
mac: [0; 6],
|
||||||
|
bind_ip: [0; 4],
|
||||||
|
bind_index: 0,
|
||||||
|
status_2: 0,
|
||||||
|
filler: [0; 26],
|
||||||
|
};
|
||||||
|
let response = ArtCommand::PollReply(Box::new(poll_reply))
|
||||||
|
.write_to_buffer()
|
||||||
|
.unwrap();
|
||||||
|
self.socket
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.send_to(&response, from)
|
||||||
|
.expect("Failed to send Poll response");
|
||||||
|
}
|
||||||
|
ArtCommand::Output(output) => {
|
||||||
|
log::trace!("[ArtNet] Received Output command with data: {:?}", output);
|
||||||
|
/* compute statistics */
|
||||||
|
if is_first_data_frame {
|
||||||
|
self.statistics.update(self.last_frame_time);
|
||||||
|
self.last_frame_time = Some(Instant::now());
|
||||||
|
is_first_data_frame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//output.port_address
|
||||||
|
for i in 0..led_driver.get_led_per_strips() {
|
||||||
|
let data_index = i * 3;
|
||||||
|
let g = *output.data.as_ref().get(data_index).unwrap_or(&0);
|
||||||
|
let r = *output.data.as_ref().get(data_index + 1).unwrap_or(&0);
|
||||||
|
let b = *output.data.as_ref().get(data_index + 2).unwrap_or(&0);
|
||||||
|
// let color = (r as u32) << 16 + (g as u32) << 8 + b;
|
||||||
|
let color: u32 =
|
||||||
|
((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
|
||||||
|
led_driver.set_color(color, i);
|
||||||
|
}
|
||||||
|
led_driver.refresh();
|
||||||
|
// Here you would typically handle the output data, e.g., send it to the LED driver
|
||||||
|
// For now, we just log it
|
||||||
|
}
|
||||||
|
ArtCommand::PollReply(_) => {
|
||||||
|
log::trace!("[ArtNet] Received PollReply command, ignoring");
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
log::warn!("[ArtNet] Received unhandled command: {:?}", command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
||||||
|
// No data received, continue running
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(e) => panic!("encountered IO error: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit(&mut self) {
|
||||||
|
log::debug!("[ArtNet] Exiting ArtNet Mode");
|
||||||
|
log::info!("[ArtNet] Statistics: {}", self.statistics);
|
||||||
|
self.socket = None;
|
||||||
|
self.last_frame_time = None;
|
||||||
|
self.statistics = ArtNetModeStatistics::default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct ArtNetModeStatistics {
|
||||||
|
frame_count: u32,
|
||||||
|
min_time: Option<Duration>,
|
||||||
|
max_time: Option<Duration>,
|
||||||
|
total_time: Duration,
|
||||||
|
avg_delta: Option<Duration>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArtNetModeStatistics {
|
||||||
|
fn update(&mut self, last_frame_time: Option<Instant>) {
|
||||||
|
self.frame_count += 1;
|
||||||
|
if let Some(last_frame_time) = last_frame_time {
|
||||||
|
let elapsed = last_frame_time.elapsed();
|
||||||
|
if self.frame_count > 30 {
|
||||||
|
let avg = self.total_time / self.frame_count;
|
||||||
|
if elapsed > avg * 2 {
|
||||||
|
log::debug!("[ArtNet] Frame took too long: {:?}ms", elapsed.as_millis());
|
||||||
|
}
|
||||||
|
if elapsed < avg / 2 {
|
||||||
|
log::debug!("[ArtNet] Frame took too short: {:?}ms", elapsed.as_millis());
|
||||||
|
}
|
||||||
|
let delta = elapsed.abs_diff(avg);
|
||||||
|
self.avg_delta = if let Some(avg_delta) = self.avg_delta {
|
||||||
|
Some((avg_delta * (self.frame_count - 30) + delta) / (self.frame_count - 29))
|
||||||
|
} else {
|
||||||
|
Some(delta)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
self.total_time += elapsed;
|
||||||
|
self.max_time = Some(match self.max_time {
|
||||||
|
Some(max_time) => std::cmp::max(max_time, elapsed),
|
||||||
|
None => elapsed,
|
||||||
|
});
|
||||||
|
self.min_time = Some(match self.min_time {
|
||||||
|
Some(min_time) => std::cmp::min(min_time, elapsed),
|
||||||
|
None => elapsed,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ArtNetModeStatistics {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let average_time = if self.frame_count > 0 {
|
||||||
|
self.total_time / self.frame_count as u32
|
||||||
|
} else {
|
||||||
|
Duration::default()
|
||||||
|
};
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"\n======================================================\n ArtNet Statistics\n"
|
||||||
|
)?;
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"frame_count: {}, total_time: {:?}, avg_delta: {}µs\n",
|
||||||
|
self.frame_count,
|
||||||
|
self.total_time,
|
||||||
|
self.avg_delta
|
||||||
|
.unwrap_or_else(|| Duration::default())
|
||||||
|
.as_micros()
|
||||||
|
)?;
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"min_time: {}, average_time: {}, max_time: {}\n",
|
||||||
|
self.min_time
|
||||||
|
.unwrap_or_else(|| Duration::default())
|
||||||
|
.as_micros() as f64
|
||||||
|
/ 1000_f64,
|
||||||
|
average_time.as_micros() as f64 / 1000_f64,
|
||||||
|
self.max_time
|
||||||
|
.unwrap_or_else(|| Duration::default())
|
||||||
|
.as_micros() as f64
|
||||||
|
/ 1000_f64
|
||||||
|
)?;
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"min_framerate: {}, average_framerate: {}, max_framerate: {}\n",
|
||||||
|
1_f64
|
||||||
|
/ self
|
||||||
|
.max_time
|
||||||
|
.unwrap_or_else(|| Duration::from_secs(0))
|
||||||
|
.as_secs_f64(),
|
||||||
|
1_f64 / average_time.as_secs_f64(),
|
||||||
|
1_f64
|
||||||
|
/ self
|
||||||
|
.min_time
|
||||||
|
.unwrap_or_else(|| Duration::from_secs(0))
|
||||||
|
.as_secs_f64()
|
||||||
|
)?;
|
||||||
|
write!(f, "==================================================")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
use crate::cputasks::modes::AppModeHandler;
|
||||||
|
|
||||||
|
use crate::devices::led_driver::LedDriver;
|
||||||
|
use std::iter::Peekable;
|
||||||
|
use std::ops::Range;
|
||||||
|
use std::slice::Iter;
|
||||||
|
|
||||||
|
pub struct DiagnosticsMode {
|
||||||
|
cycle_count: usize,
|
||||||
|
color_iterator: Peekable<Iter<'static, u32>>,
|
||||||
|
led_iterator: Range<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiagnosticsMode {
|
||||||
|
const TEST_COLORS: [u32; 7] = [
|
||||||
|
0x1f0000, 0x001f00, 0x00001f, 0x5f5f00, 0x1f001f, 0x001f1f, 0x1f1f1f,
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
DiagnosticsMode {
|
||||||
|
cycle_count: 0,
|
||||||
|
color_iterator: Self::color_iterator(),
|
||||||
|
led_iterator: 0..0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn color_iterator() -> Peekable<Iter<'static, u32>> {
|
||||||
|
Self::TEST_COLORS.iter().peekable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_led_iterator(led_driver: &LedDriver) -> Range<usize> {
|
||||||
|
// Initialize the LED iterator to cover all LEDs
|
||||||
|
0..led_driver.get_led_per_strips()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppModeHandler for DiagnosticsMode {
|
||||||
|
fn enter(&mut self) {
|
||||||
|
log::debug!("[Diagnostics] Entering Diagnostics Mode");
|
||||||
|
self.cycle_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self, led_driver: &mut LedDriver) {
|
||||||
|
log::trace!("[Diagnostics] Running...");
|
||||||
|
if self.cycle_count % 50 == 0 {
|
||||||
|
let (led, color) = match self.led_iterator.next() {
|
||||||
|
Some(led) => {
|
||||||
|
led_driver.set_color(**self.color_iterator.peek().unwrap(), led);
|
||||||
|
(led, *self.color_iterator.peek().unwrap())
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Reset the LED iterator if it reaches the end
|
||||||
|
self.led_iterator = DiagnosticsMode::init_led_iterator(led_driver);
|
||||||
|
let led = self.led_iterator.next().unwrap();
|
||||||
|
self.color_iterator.next();
|
||||||
|
(
|
||||||
|
led,
|
||||||
|
match self.color_iterator.peek() {
|
||||||
|
Some(color) => {
|
||||||
|
led_driver.set_color(**color, led);
|
||||||
|
*color
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Reset the color iterator if it reaches the end
|
||||||
|
self.color_iterator = DiagnosticsMode::color_iterator();
|
||||||
|
let color = self.color_iterator.peek().unwrap();
|
||||||
|
led_driver.set_color(**color, led);
|
||||||
|
*color
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
log::trace!("Setting LED {} to color {:06x}", led, color);
|
||||||
|
led_driver.refresh();
|
||||||
|
}
|
||||||
|
self.cycle_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit(&mut self) {
|
||||||
|
log::debug!("[Diagnostics] Exiting Diagnostics Mode");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
use crate::cputasks::modes::AppModeHandler;
|
||||||
|
use crate::devices::led_driver::LedDriver;
|
||||||
|
|
||||||
|
pub struct ManualMode;
|
||||||
|
|
||||||
|
impl ManualMode {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
ManualMode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppModeHandler for ManualMode {
|
||||||
|
fn enter(&mut self) {
|
||||||
|
log::debug!("[Manual] Entering Manual Mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self, _: &mut LedDriver) {
|
||||||
|
log::trace!("[Manual] Running...");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit(&mut self) {
|
||||||
|
log::debug!("[Manual] Exiting Manual Mode");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
use rppal::gpio::Gpio;
|
||||||
|
|
||||||
|
use crate::cputasks::modes::AppModeHandler;
|
||||||
|
use crate::devices::led_driver::LedDriver;
|
||||||
|
|
||||||
|
pub struct StandaloneMode {
|
||||||
|
_i2s_mic_pin: Vec<rppal::gpio::IoPin>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StandaloneMode {
|
||||||
|
const I2S_PINS: [u8; 3] = [20, 19, 18];
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let gpio = Gpio::new().expect("Failed to initialize GPIO");
|
||||||
|
let i2s_mic_pin: Vec<rppal::gpio::IoPin> = Self::I2S_PINS
|
||||||
|
.iter()
|
||||||
|
.map(|&pin| {
|
||||||
|
gpio.get(pin)
|
||||||
|
.expect(&format!("Failed to get GPIO pin {}", pin))
|
||||||
|
.into_io(rppal::gpio::Mode::Alt0)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
StandaloneMode {
|
||||||
|
_i2s_mic_pin: i2s_mic_pin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppModeHandler for StandaloneMode {
|
||||||
|
fn enter(&mut self) {
|
||||||
|
log::debug!("[Standalone] Entering Standalone Mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self, _: &mut LedDriver) {
|
||||||
|
log::trace!("[Standalone] Running...");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit(&mut self) {
|
||||||
|
log::debug!("[Standalone] Exiting Standalone Mode");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod led_driver;
|
||||||
|
pub mod selector;
|
||||||
@@ -1,59 +1,61 @@
|
|||||||
// Constants
|
#[link(name = "logc", kind = "static")]
|
||||||
const LED_NBITS: usize = 24; // Number of data bits per LED
|
unsafe extern "C" {
|
||||||
const LED_PREBITS: usize = 4; // Number of zero bits before LED data
|
unsafe fn log_log(level: u32, file: *const u8, line: i32, fmt: *const u8, ...);
|
||||||
const LED_POSTBITS: usize = 4; // Number of zero bits after LED data
|
|
||||||
const BIT_NPULSES: usize = 3; // Number of O/P pulses per LED bit
|
|
||||||
|
|
||||||
#[cfg(feature = "rpi4")] // Timings for RPi v4 (1.5 GHz)
|
|
||||||
const SMI_TIMING: [u32; 4] = [10, 15, 30, 15]; // 400 ns cycle time
|
|
||||||
#[cfg(not(feature = "rpi4"))] // Timings for RPi v0-3 (1 GHz)
|
|
||||||
const SMI_TIMING: [u32; 4] = [10, 10, 20, 10]; // 400 ns cycle time
|
|
||||||
|
|
||||||
// Use 16-bit data type for TxDataT
|
|
||||||
#[cfg(feature = "16channel")]
|
|
||||||
type TxDataT = u16;
|
|
||||||
// Use 8-bit data type for TxDataT
|
|
||||||
#[cfg(not(feature = "16channel"))]
|
|
||||||
type TxDataT = u8;
|
|
||||||
|
|
||||||
// Helper macros as functions
|
|
||||||
#[inline]
|
|
||||||
const fn led_dlen() -> usize {
|
|
||||||
LED_NBITS * BIT_NPULSES
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[link(name = "RpiLedBars_drivers", kind = "static")]
|
||||||
const fn led_tx_oset(n: usize) -> usize {
|
unsafe extern "C" {
|
||||||
LED_PREBITS + (led_dlen() * n)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
unsafe fn leddriver_setup();
|
||||||
const fn tx_buff_len(n: usize) -> usize {
|
|
||||||
led_tx_oset(n) + LED_POSTBITS
|
unsafe fn leddriver_close();
|
||||||
|
|
||||||
|
unsafe fn set_color(rgb: u32, index: usize);
|
||||||
|
|
||||||
|
// unsafe fn rgb_txdata(rgbs: *mut u32, index: usize);
|
||||||
|
|
||||||
|
unsafe fn leddriver_refresh();
|
||||||
|
|
||||||
|
// unsafe fn ColorHSV(hue: u16, sat: u8, val: u8) -> u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LedDriver {
|
pub struct LedDriver {
|
||||||
// Placeholder for LED driver state
|
led_per_strips: usize,
|
||||||
tx_buffer: [TxDataT; tx_buff_len(30)],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LedDriver {
|
impl LedDriver {
|
||||||
pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
|
pub fn new(led_per_strips: usize) -> LedDriver {
|
||||||
// Initialize the LED driver
|
unsafe {
|
||||||
Ok(LedDriver {
|
log_log(
|
||||||
tx_buffer: [TxDataT::default(); 4856],
|
0,
|
||||||
})
|
"coucou".as_bytes().as_ptr(),
|
||||||
|
13,
|
||||||
|
"hello".as_bytes().as_ptr(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
unsafe { leddriver_setup() };
|
||||||
|
LedDriver { led_per_strips }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_color(&self, rgb: u32, index: usize) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn get_led_per_strips(&self) -> usize {
|
||||||
// Set the color for the LED at the specified index
|
self.led_per_strips
|
||||||
// Placeholder for actual implementation
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh(&self) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn set_color(&self, rgb: u32, index: usize) {
|
||||||
// Refresh the LED states
|
unsafe { set_color(rgb, index) };
|
||||||
// Placeholder for actual implementation
|
}
|
||||||
Ok(())
|
|
||||||
|
pub fn refresh(&self) {
|
||||||
|
unsafe { leddriver_refresh() };
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn color_hsv(&self, hue: u16, sat: u8, val: u8) -> u32 {
|
||||||
|
// unsafe { ColorHSV(hue, sat, val) }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for LedDriver {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { leddriver_close() };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// Constants
|
||||||
|
const LED_NBITS: usize = 24; // Number of data bits per LED
|
||||||
|
const LED_PREBITS: usize = 4; // Number of zero bits before LED data
|
||||||
|
const LED_POSTBITS: usize = 4; // Number of zero bits after LED data
|
||||||
|
const BIT_NPULSES: usize = 3; // Number of O/P pulses per LED bit
|
||||||
|
|
||||||
|
#[cfg(feature = "rpi4")] // Timings for RPi v4 (1.5 GHz)
|
||||||
|
const SMI_TIMING: [u32; 4] = [10, 15, 30, 15]; // 400 ns cycle time
|
||||||
|
#[cfg(not(feature = "rpi4"))] // Timings for RPi v0-3 (1 GHz)
|
||||||
|
const SMI_TIMING: [u32; 4] = [10, 10, 20, 10]; // 400 ns cycle time
|
||||||
|
|
||||||
|
// Use 16-bit data type for TxDataT
|
||||||
|
#[cfg(feature = "16channel")]
|
||||||
|
type TxDataT = u16;
|
||||||
|
// Use 8-bit data type for TxDataT
|
||||||
|
#[cfg(not(feature = "16channel"))]
|
||||||
|
type TxDataT = u8;
|
||||||
|
|
||||||
|
// Helper macros as functions
|
||||||
|
// Length of data for 1 row (1 LED on each channel)
|
||||||
|
#[inline]
|
||||||
|
const fn led_dlen() -> usize {
|
||||||
|
LED_NBITS * BIT_NPULSES
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset into Tx data buffer, given LED number in chan
|
||||||
|
#[inline]
|
||||||
|
const fn led_tx_offset(n: usize) -> usize {
|
||||||
|
LED_PREBITS + (led_dlen() * n)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
const fn tx_buff_len(n: usize) -> usize {
|
||||||
|
led_tx_offset(n) + LED_POSTBITS
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LedDriver {
|
||||||
|
// Placeholder for LED driver state
|
||||||
|
tx_buffer: [TxDataT; tx_buff_len(30)],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LedDriver {
|
||||||
|
pub fn new() -> LedDriver {
|
||||||
|
// Initialize the LED driver
|
||||||
|
LedDriver {
|
||||||
|
tx_buffer: [TxDataT::default(); tx_buff_len(30)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_color(&self, rgb: u32, index: usize) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// Set the color for the LED at the specified index
|
||||||
|
// Placeholder for actual implementation
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// Refresh the LED states
|
||||||
|
// Placeholder for actual implementation
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io;
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::ptr;
|
||||||
|
use libc::{mmap, munmap, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE, O_RDWR, O_SYNC, O_CLOEXEC};
|
||||||
|
use log::{info};
|
||||||
|
|
||||||
|
// Location of peripheral registers in physical memory
|
||||||
|
#[cfg(feature = "rpi")]
|
||||||
|
const PHYS_REG_BASE: usize = 0x2000_0000; // Pi Zero or 1
|
||||||
|
#[cfg(any(feature = "rpi2", feature = "rpi3"))]
|
||||||
|
const PHYS_REG_BASE: usize = 0x3F00_0000; // Pi 2 or 3 or Zero 2
|
||||||
|
#[cfg(feature = "rpi4")]
|
||||||
|
const PHYS_REG_BASE: usize = 0xFE00_0000; // Pi 4
|
||||||
|
|
||||||
|
// Clock frequency
|
||||||
|
#[cfg(feature = "rpi")]
|
||||||
|
const CLOCK_HZ: usize = 400_000_000; // Pi Zero
|
||||||
|
#[cfg(any(feature = "rpi2", feature = "rpi3", feature = "rpi4"))]
|
||||||
|
const CLOCK_HZ: usize = 250_000_000; // Pi 2 - 4
|
||||||
|
|
||||||
|
// Location of peripheral registers in bus memory
|
||||||
|
const BUS_REG_BASE: usize = 0x7E00_0000;
|
||||||
|
|
||||||
|
const MEM_DEVICE_FILE: &str = "/dev/mem"; // Memory device file
|
||||||
|
|
||||||
|
|
||||||
|
// Get virtual 8 and 32-bit pointers to register
|
||||||
|
#[inline]
|
||||||
|
const unsafe fn reg8(m: &MemMap, x: usize) -> *mut u8 {
|
||||||
|
(m.virt as usize + x) as *mut u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
const unsafe fn reg32(m: &MemMap, x: usize) -> *mut u32 {
|
||||||
|
(m.virt as usize + x) as *mut u32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get bus address of register
|
||||||
|
#[inline]
|
||||||
|
const fn reg_bus_addr(m: &MemMap, x: usize) -> usize {
|
||||||
|
m.bus as usize + x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert uncached memory virtual address to bus address
|
||||||
|
#[inline]
|
||||||
|
const fn mem_bus_addr(mp: &MemMap, a: usize) -> usize {
|
||||||
|
a - mp.virt as usize + mp.bus as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert bus address to physical address (for mmap)
|
||||||
|
#[inline]
|
||||||
|
const fn bus_phys_addr(a: usize) -> usize {
|
||||||
|
a & !0xC000_0000
|
||||||
|
}
|
||||||
|
|
||||||
|
const PAGE_SIZE: usize = 0x1000; // 4 KiB page size
|
||||||
|
const fn page_roundup(size: usize) -> usize {
|
||||||
|
(size + PAGE_SIZE - 1) & !(PAGE_SIZE - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Structure for mapped peripheral or memory
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MemMap {
|
||||||
|
pub fd: i32, // File descriptor
|
||||||
|
pub h: i32, // Memory handle
|
||||||
|
pub size: usize, // Memory size
|
||||||
|
pub bus: *mut libc::c_void, // Bus address
|
||||||
|
pub virt: *mut libc::c_void, // Virtual address
|
||||||
|
pub phys: *mut libc::c_void, // Physical address
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemMap {
|
||||||
|
// Create a new memory map
|
||||||
|
pub fn new(size: usize, phys: *mut libc::c_void) -> io::Result<MemMap> {
|
||||||
|
let size = page_roundup(size);
|
||||||
|
let bus_addr = phys as usize - PHYS_REG_BASE + BUS_REG_BASE;
|
||||||
|
let virt_addr = unsafe { map_segment(phys as usize, size) };
|
||||||
|
|
||||||
|
if virt_addr.is_null() {
|
||||||
|
return Err(io::Error::new(io::ErrorKind::Other, "Memory mapping failed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(MemMap {
|
||||||
|
fd: -1,
|
||||||
|
h: -1,
|
||||||
|
size: size,
|
||||||
|
bus: virt_addr,
|
||||||
|
virt: virt_addr,
|
||||||
|
phys: phys,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement Drop to automatically unmap memory when MemMap goes out of scope
|
||||||
|
impl Drop for MemMap {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
unmap_segment(self.virt, self.size);
|
||||||
|
}
|
||||||
|
self.virt = ptr::null_mut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsafe fn map_segment(addr: usize, size: usize) -> *mut libc::c_void {
|
||||||
|
let size = page_roundup(size);
|
||||||
|
|
||||||
|
let file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.custom_flags(O_SYNC | O_CLOEXEC)
|
||||||
|
.open("/dev/mem")
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
panic!("can't open /dev/mem, run using sudo");
|
||||||
|
});
|
||||||
|
|
||||||
|
let fd = file.as_raw_fd();
|
||||||
|
|
||||||
|
let mem = mmap(
|
||||||
|
ptr::null_mut(),
|
||||||
|
size,
|
||||||
|
PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED,
|
||||||
|
fd,
|
||||||
|
addr as libc::off_t,
|
||||||
|
);
|
||||||
|
drop(file);
|
||||||
|
|
||||||
|
info("Map {:p} -> {:p}", addr as *const (), mem);
|
||||||
|
|
||||||
|
if mem == MAP_FAILED {
|
||||||
|
panic!("Memory mapping {:p} -> {:p} failed", addr as *const (), mem);
|
||||||
|
}
|
||||||
|
mem
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free mapped memory
|
||||||
|
unsafe fn unmap_segment(mem: *mut libc::c_void, size: usize) {
|
||||||
|
if !mem.is_null() {
|
||||||
|
munmap(mem, page_roundup(size));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
|
use std::io;
|
||||||
|
use std::mem;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
pub struct VCAllocFlags: u32 {
|
||||||
|
const MEM_FLAG_DISCARDABLE = 1 << 0; // can be resized to 0 at any time. Use for cached data
|
||||||
|
const MEM_FLAG_NORMAL = 0 << 2; // normal allocating alias. Don't use from ARM
|
||||||
|
const MEM_FLAG_DIRECT = 1 << 2; // 0xC alias uncached
|
||||||
|
const MEM_FLAG_COHERENT = 2 << 2; // 0x8 alias. Non-allocating in L2 but coherent
|
||||||
|
const MEM_FLAG_ZERO = 1 << 4; // initialise buffer to all zeros
|
||||||
|
const MEM_FLAG_NO_INIT = 1 << 5; // don't initialise (default is initialise to all ones)
|
||||||
|
const MEM_FLAG_HINT_PERMALOCK = 1 << 6; // Likely to be locked for long periods of time
|
||||||
|
const MEM_FLAG_L1_NONALLOCATING = Self::MEM_FLAG_DIRECT.bits | Self::MEM_FLAG_COHERENT.bits; // Allocating in L2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const DMA_MEM_FLAGS: VCAllocFlags = VCAllocFlags::MEM_FLAG_DIRECT | VCAllocFlags::MEM_FLAG_ZERO;
|
||||||
|
|
||||||
|
#[repr(C, align(16))]
|
||||||
|
pub struct VcMsg {
|
||||||
|
pub len: u32, // Overall length (bytes)
|
||||||
|
pub req: u32, // Zero for request, 1<<31 for response
|
||||||
|
pub tag: u32, // Command number
|
||||||
|
pub blen: u32, // Buffer length (bytes)
|
||||||
|
pub dlen: u32, // Data length (bytes)
|
||||||
|
pub uints: [u32; 27], // Data (108 bytes maximum)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_mbox() -> io::Result<RawFd> {
|
||||||
|
let file = OpenOptions::new().read(true).open("/dev/vcio");
|
||||||
|
match file {
|
||||||
|
Ok(f) => Ok(f.as_raw_fd()),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("can't open VC mailbox: {}", e);
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close_mbox(fd: RawFd) {
|
||||||
|
if fd >= 0 {
|
||||||
|
// SAFETY: closing a valid file descriptor
|
||||||
|
unsafe { libc::close(fd) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const VC_IOC_MAGIC: u8 = 100;
|
||||||
|
const VC_IOC_MBOX: u64 = nix::request_code_readwrite!(VC_IOC_MAGIC, 0, mem::size_of::<VcMsg>());
|
||||||
|
|
||||||
|
pub fn msg_mbox(fd: RawFd, msg: &mut VcMsg) -> u32 {
|
||||||
|
// Zero out unused message buffer
|
||||||
|
let dlen_words = (msg.dlen / 4) as usize;
|
||||||
|
let blen_words = (msg.blen / 4) as usize;
|
||||||
|
for i in dlen_words..=blen_words {
|
||||||
|
if i < msg.uints.len() {
|
||||||
|
msg.uints[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg.len = ((msg.blen + 6) * 4) as u32;
|
||||||
|
msg.req = 0;
|
||||||
|
|
||||||
|
let ret = ioctl_write_ptr!(fd, VC_IOC_MAGIC, 0, VcMsg, msg);
|
||||||
|
|
||||||
|
if ret < 0 {
|
||||||
|
log::error!("VC IOCTL failed");
|
||||||
|
0
|
||||||
|
} else if (msg.req & 0x8000_0000) == 0 {
|
||||||
|
log::error!("VC IOCTL error");
|
||||||
|
0
|
||||||
|
} else if msg.req == 0x8000_0001 {
|
||||||
|
log::error!("VC IOCTL partial error");
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
msg.uints[0]
|
||||||
|
}
|
||||||
|
disp_vc_msg(msgp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disp_vc_msg(msg: &VcMsg) {
|
||||||
|
print!(
|
||||||
|
"VC msg len={:X}, req={:X}, tag={:X}, blen={:x}, dlen={:x}, data ",
|
||||||
|
msg.len, msg.req, msg.tag, msg.blen, msg.dlen
|
||||||
|
);
|
||||||
|
let blen_words = (msg.blen / 4) as usize;
|
||||||
|
for i in 0..blen_words.min(msg.uints.len()) {
|
||||||
|
print!("{:08X} ", msg.uints[i]);
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
pub mod led_driver;
|
|
||||||
pub mod selector;
|
|
||||||
|
|
||||||
pub trait Device {
|
|
||||||
fn read(&self) -> Vec<bool>;
|
|
||||||
}
|
|
||||||
@@ -1,31 +1,48 @@
|
|||||||
use rppal::gpio::{Gpio, InputPin};
|
use rppal::gpio::{Event, Gpio, InputPin};
|
||||||
|
|
||||||
use super::Device;
|
|
||||||
use rppal::gpio::Error;
|
use rppal::gpio::Error;
|
||||||
|
// GPIO pin number for the LED
|
||||||
const SELECTOR_PINS: [u8; 4] = [27, 5, 6, 26]; // GPIO pin number for the LED
|
|
||||||
|
|
||||||
pub struct Selector {
|
pub struct Selector {
|
||||||
selector_pins: Vec<InputPin>,
|
selector_pins: Vec<InputPin>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Selector {
|
impl Selector {
|
||||||
|
const SELECTOR_PINS: [u8; 4] = [27, 5, 6, 26];
|
||||||
|
|
||||||
pub fn new() -> Result<Selector, Error> {
|
pub fn new() -> Result<Selector, Error> {
|
||||||
let gpio = Gpio::new()?;
|
let gpio = Gpio::new()?;
|
||||||
// Set up the GPIO pins for the selector, use pull-down resistors to ensure a known state when not pressed
|
// Set up the GPIO pins for the selector, use pull-down resistors to ensure a known state when not pressed
|
||||||
let selector_pins: Vec<InputPin> = SELECTOR_PINS
|
log::debug!(
|
||||||
|
"Setting up selector with pins {:?} as input_pullup",
|
||||||
|
Self::SELECTOR_PINS
|
||||||
|
);
|
||||||
|
let selector_pins: Vec<InputPin> = Self::SELECTOR_PINS
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&pin| gpio.get(pin).unwrap().into_input_pulldown())
|
.map(|&pin| gpio.get(pin).unwrap().into_input_pullup())
|
||||||
.collect();
|
.collect();
|
||||||
Ok(Selector { selector_pins })
|
Ok(Selector { selector_pins })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_callback<F>(&mut self, index: usize, callback: F)
|
||||||
|
where
|
||||||
|
F: FnMut(Event) + Send + 'static,
|
||||||
|
{
|
||||||
|
log::debug!("Setting callback for selector pin at index {}", index);
|
||||||
|
assert!(index < self.selector_pins.len(), "Index out of bounds");
|
||||||
|
self.selector_pins[index]
|
||||||
|
.set_async_interrupt(
|
||||||
|
rppal::gpio::Trigger::FallingEdge,
|
||||||
|
Some(std::time::Duration::from_millis(10)),
|
||||||
|
callback,
|
||||||
|
)
|
||||||
|
.expect("Failed to set interrupt");
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device for Selector {
|
pub fn get_current_index(&self) -> usize {
|
||||||
fn read(&self) -> Vec<bool> {
|
|
||||||
self.selector_pins
|
self.selector_pins
|
||||||
.iter()
|
.iter()
|
||||||
.map(|pin| pin.read() == rppal::gpio::Level::High)
|
.position(|pin| pin.is_low())
|
||||||
.collect()
|
.unwrap_or(0) // Default to 0 if no pin is low
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod selector;
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
use crossbeam::channel::Sender;
|
||||||
|
use rppal::gpio::Event;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use crate::channels::Message;
|
||||||
|
use crate::cputasks::modes::AppMode;
|
||||||
|
use crate::devices::selector::Selector as SelectorDevice;
|
||||||
|
|
||||||
|
pub struct SelectorTask {
|
||||||
|
_selector_device: SelectorDevice,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SelectorTask {
|
||||||
|
pub fn new(tx: Sender<Message>) -> Result<SelectorTask, Box<dyn Error>> {
|
||||||
|
log::debug!("Setting up selector task");
|
||||||
|
let mut selector_device: SelectorDevice = SelectorDevice::new()?;
|
||||||
|
let tx: Arc<Mutex<Sender<Message>>> = Arc::new(Mutex::new(tx));
|
||||||
|
AppMode::for_each(|mode| {
|
||||||
|
log::debug!(
|
||||||
|
"Setting up selector callback for mode: ({}){:?}",
|
||||||
|
mode as usize,
|
||||||
|
mode
|
||||||
|
);
|
||||||
|
let tx = tx.clone();
|
||||||
|
selector_device.set_callback(mode as usize, get_mode_callback(mode, tx));
|
||||||
|
});
|
||||||
|
get_mode_callback(selector_device.get_current_index().into(), tx)(Event::default());
|
||||||
|
Ok(SelectorTask {
|
||||||
|
_selector_device: selector_device,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mode_callback(
|
||||||
|
mode: AppMode,
|
||||||
|
tx: Arc<Mutex<Sender<Message>>>,
|
||||||
|
) -> impl Fn(Event) + Send + 'static {
|
||||||
|
move |_| {
|
||||||
|
log::trace!("Selector mode changed: {:?}", mode);
|
||||||
|
let tx: std::sync::MutexGuard<'_, Sender<Message>> = tx.lock().unwrap();
|
||||||
|
let _ = tx.send(Message::ModeChanged { mode });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
mod channels;
|
||||||
|
mod config;
|
||||||
|
mod cputasks;
|
||||||
|
mod devices;
|
||||||
|
mod iotasks;
|
||||||
|
|
||||||
|
use crate::cputasks::modes::ModeManager;
|
||||||
|
use crate::devices::led_driver::LedDriver;
|
||||||
|
use crate::iotasks::selector::SelectorTask;
|
||||||
|
|
||||||
|
use crossbeam::channel::unbounded;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
struct GlobalContext {
|
||||||
|
mode_manager: ModeManager,
|
||||||
|
_selector_task: SelectorTask,
|
||||||
|
led_driver: LedDriver,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LightSabre;
|
||||||
|
pub struct LightSabreIntitialized {
|
||||||
|
ctx: GlobalContext,
|
||||||
|
}
|
||||||
|
pub struct LightSabreRunning {
|
||||||
|
join_handle: std::thread::JoinHandle<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup() -> GlobalContext {
|
||||||
|
log::info!("Setting up LightSabre...");
|
||||||
|
ctrlc::set_handler(|| {
|
||||||
|
cleanup();
|
||||||
|
std::process::exit(0);
|
||||||
|
})
|
||||||
|
.expect("Error setting SIGINT/SIGTERM/SIGHUP handler");
|
||||||
|
let led_driver = LedDriver::new(5);
|
||||||
|
|
||||||
|
let (tx, rx) = unbounded();
|
||||||
|
let mode_manager = ModeManager::new(rx);
|
||||||
|
let selector_task = SelectorTask::new(tx.clone()).expect("Failed to create selector task");
|
||||||
|
|
||||||
|
// Initialization code here
|
||||||
|
log::info!("Setup complete.");
|
||||||
|
GlobalContext {
|
||||||
|
mode_manager,
|
||||||
|
_selector_task: selector_task,
|
||||||
|
led_driver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(ctx: &mut GlobalContext) {
|
||||||
|
// let period = Duration::from_secs(2);
|
||||||
|
let period = Duration::from_millis(10);
|
||||||
|
loop {
|
||||||
|
let start = Instant::now();
|
||||||
|
ctx.mode_manager.run(&mut ctx.led_driver);
|
||||||
|
let elapsed = start.elapsed();
|
||||||
|
|
||||||
|
if elapsed > period {
|
||||||
|
log::warn!(
|
||||||
|
"Mode {:?} execution took too long: {:?}/{:?}ms",
|
||||||
|
ctx.mode_manager.get_current_mode(),
|
||||||
|
elapsed,
|
||||||
|
period
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread::sleep(period.saturating_sub(elapsed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup() {
|
||||||
|
log::info!("Cleaning up before quitting...");
|
||||||
|
// Perform cleanup here
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightSabre {
|
||||||
|
pub fn setup(self) -> LightSabreIntitialized {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightSabreIntitialized {
|
||||||
|
pub fn run(self) -> LightSabreRunning {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightSabreRunning {
|
||||||
|
pub fn wait(self) -> LightSabre {
|
||||||
|
// Wait for the running state to finish
|
||||||
|
// This is a placeholder; actual waiting logic would depend on the application
|
||||||
|
self.join_handle.join().expect("Thread panicked");
|
||||||
|
LightSabre
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(self) -> LightSabre {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LightSabre> for LightSabreIntitialized {
|
||||||
|
fn from(_: LightSabre) -> Self {
|
||||||
|
let ctx = setup();
|
||||||
|
LightSabreIntitialized { ctx }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LightSabreIntitialized> for LightSabreRunning {
|
||||||
|
fn from(mut value: LightSabreIntitialized) -> Self {
|
||||||
|
LightSabreRunning {
|
||||||
|
join_handle: std::thread::spawn(move || run(&mut value.ctx)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LightSabreRunning> for LightSabre {
|
||||||
|
fn from(_: LightSabreRunning) -> Self {
|
||||||
|
cleanup();
|
||||||
|
LightSabre
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,26 +1,10 @@
|
|||||||
use devices::Device;
|
use lightsabre_backend::LightSabre;
|
||||||
use devices::led_driver::LedDriver;
|
|
||||||
use devices::selector::Selector;
|
|
||||||
use log::{debug, info};
|
|
||||||
use rppal::system::DeviceInfo;
|
|
||||||
|
|
||||||
mod devices;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
info!("Executing on device: {}", DeviceInfo::new()?.model());
|
// let mut scheduler = statemachine::Scheduler::new(statemachine::ExecModeTest::new())?;
|
||||||
|
// scheduler.start()
|
||||||
// Initialize GPIO
|
let ls = LightSabre;
|
||||||
let selector = Selector::new()?;
|
ls.setup().run().wait();
|
||||||
let ledDriver = devices::led_driver::LedDriver::new()?;
|
Ok(())
|
||||||
debug!("Selector initialized.");
|
|
||||||
// Main loop
|
|
||||||
loop {
|
|
||||||
// Read the selector state
|
|
||||||
let selector_state = selector.read();
|
|
||||||
debug!("Selector state: {:?}", selector_state);
|
|
||||||
|
|
||||||
// Sleep for a short duration to avoid busy-waiting
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::ops::Index;
|
||||||
|
|
||||||
|
use env_logger::fmt::style::Color;
|
||||||
|
// If 'devices' is an external crate, add it to Cargo.toml and use:
|
||||||
|
// use devices::led_driver::LedDriver;
|
||||||
|
// use devices::selector::Selector;
|
||||||
|
use log::{debug, info, trace};
|
||||||
|
use rppal::system::DeviceInfo;
|
||||||
|
|
||||||
|
use crate::devices::{self, Device, led_driver};
|
||||||
|
use devices::led_driver::LedDriver;
|
||||||
|
use devices::selector::Selector;
|
||||||
|
|
||||||
|
pub struct Scheduler {
|
||||||
|
mode: Box<dyn Mode>,
|
||||||
|
led_driver: LedDriver,
|
||||||
|
selector: Selector,
|
||||||
|
}
|
||||||
|
|
||||||
|
// trait UserCode {
|
||||||
|
// fn setup(&self);
|
||||||
|
// fn execute(&self);
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl Scheduler {
|
||||||
|
pub fn new(startup_mode: T) -> Result<Scheduler<T>, Box<dyn Error>> {
|
||||||
|
//setup devices
|
||||||
|
info!("Executing on device: {}", DeviceInfo::new()?.model());
|
||||||
|
// Initialize GPIO
|
||||||
|
let selector = Selector::new()?;
|
||||||
|
// #[allow(unused_variables)]
|
||||||
|
let led_driver = LedDriver::new();
|
||||||
|
debug!("Selector initialized.");
|
||||||
|
// Main loop
|
||||||
|
Ok(Scheduler {
|
||||||
|
mode: startup_mode,
|
||||||
|
led_driver,
|
||||||
|
selector,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
pub async fn start(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
|
loop {
|
||||||
|
self.mode.execute(&mut self.led_driver).await;
|
||||||
|
// Read the selector state
|
||||||
|
let selector_state = self.selector.read();
|
||||||
|
trace!("Selector state: {:?}", selector_state);
|
||||||
|
match selector_state.iter().position(|x| *x) {
|
||||||
|
Some(index) => {
|
||||||
|
// If a button is pressed, change the mode based on the index
|
||||||
|
match index {
|
||||||
|
0 => {
|
||||||
|
info!("Switching to Test Mode");
|
||||||
|
self.set_mode(ExecModeTest::new());
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
info!("Switching to ArtNet Mode");
|
||||||
|
self.set_mode(ExecModeArtNet);
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
info!("Switching to Standalone Mode");
|
||||||
|
self.set_mode(ExecModeStandalone);
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
info!("Switching to Manual Mode");
|
||||||
|
self.set_mode(ExecModeManual);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
warn!("Unknown selector index: {}", index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// If no button is pressed, continue with the current mode
|
||||||
|
warning!("No selector input is it connected, continuing in current mode.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_mode(&mut self, next_mode: T) {
|
||||||
|
self.mode = next_mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait Mode {
|
||||||
|
async fn execute(&mut self, led_driver: &mut LedDriver);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ExecModeTest<'a> {
|
||||||
|
color_iterator: std::iter::Peekable<std::slice::Iter<'a, u32>>,
|
||||||
|
led_iterator: std::ops::Range<usize>,
|
||||||
|
}
|
||||||
|
pub struct ExecModeArtNet;
|
||||||
|
pub struct ExecModeStandalone;
|
||||||
|
pub struct ExecModeManual;
|
||||||
|
|
||||||
|
impl ExecModeTest<'_> {
|
||||||
|
const TEST_COLORS: [u32; 7] = [
|
||||||
|
0x1f0000, 0x001f00, 0x00001f, 0x5f5f00, 0x1f001f, 0x001f1f, 0x1f1f1f,
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
ExecModeTest {
|
||||||
|
color_iterator: ExecModeTest::TEST_COLORS.iter().peekable(),
|
||||||
|
led_iterator: 0..5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mode for ExecModeTest<'_> {
|
||||||
|
async fn execute(&mut self, led_driver: &mut LedDriver) {
|
||||||
|
let (led, color) = match self.led_iterator.next() {
|
||||||
|
Some(led) => {
|
||||||
|
led_driver.set_color(**self.color_iterator.peek().unwrap(), led);
|
||||||
|
(led, *self.color_iterator.peek().unwrap())
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Reset the LED iterator if it reaches the end
|
||||||
|
self.led_iterator = 0..5;
|
||||||
|
let led = self.led_iterator.next().unwrap();
|
||||||
|
self.color_iterator.next();
|
||||||
|
(
|
||||||
|
led,
|
||||||
|
match self.color_iterator.peek() {
|
||||||
|
Some(color) => {
|
||||||
|
led_driver.set_color(**color, led);
|
||||||
|
*color
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Reset the color iterator if it reaches the end
|
||||||
|
self.color_iterator = ExecModeTest::TEST_COLORS.iter().peekable();
|
||||||
|
let color = self.color_iterator.peek().unwrap();
|
||||||
|
led_driver.set_color(**color, led);
|
||||||
|
*color
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
trace!("Setting LED {} to color {:06x}", led, color);
|
||||||
|
led_driver.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mode for ExecModeArtNet {
|
||||||
|
async fn execute(&mut self, _led_driver: &mut LedDriver) {
|
||||||
|
info!("Executing ArtNet mode");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "rust_sandbox"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tokio = { version = "1.45.1", features = ["full"] }
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
use tokio::{
|
||||||
|
spawn,
|
||||||
|
time::{Duration, sleep},
|
||||||
|
};
|
||||||
|
|
||||||
|
async fn say_hello() {
|
||||||
|
// Wait for a while before printing to make it a more interesting race.
|
||||||
|
sleep(Duration::from_millis(100)).await;
|
||||||
|
print!("hello ");
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn say_world() {
|
||||||
|
sleep(Duration::from_millis(100)).await;
|
||||||
|
print!("world ");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
loop {
|
||||||
|
let handle1 = spawn(say_hello());
|
||||||
|
let handle2 = spawn(say_world());
|
||||||
|
|
||||||
|
let _ = handle1.await;
|
||||||
|
let _ = handle2.await;
|
||||||
|
|
||||||
|
println!("!");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ TARGET_USER="pi"
|
|||||||
TARGET_BIN_FILE="/tmp/${APP}"
|
TARGET_BIN_FILE="/tmp/${APP}"
|
||||||
TARGET_CWD="/tmp"
|
TARGET_CWD="/tmp"
|
||||||
|
|
||||||
ssh "${TARGET_USER}@${SSH_REMOTE}" "killall lldb-server ${APP}"
|
ssh "${TARGET_USER}@${SSH_REMOTE}" "sudo killall lldb-server ${APP}"
|
||||||
|
|
||||||
if ! rsync -avz "${BUILD_BIN_FILE}" "${TARGET_USER}@${SSH_REMOTE}:${TARGET_BIN_FILE}"; then
|
if ! rsync -avz "${BUILD_BIN_FILE}" "${TARGET_USER}@${SSH_REMOTE}:${TARGET_BIN_FILE}"; then
|
||||||
# If rsync doesn't work, it may not be available on target. Fallback to trying SSH copy.
|
# If rsync doesn't work, it may not be available on target. Fallback to trying SSH copy.
|
||||||
@@ -20,4 +20,4 @@ if ! rsync -avz "${BUILD_BIN_FILE}" "${TARGET_USER}@${SSH_REMOTE}:${TARGET_BIN_F
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ssh -f "${TARGET_USER}@${SSH_REMOTE}" "sh -c 'cd ${TARGET_CWD}; RUST_LOG=debug nohup lldb-server g *:${GDBPORT} ${TARGET_BIN_FILE} > /dev/null 2>&1 &'"
|
ssh -f "${TARGET_USER}@${SSH_REMOTE}" "sh -c 'cd ${TARGET_CWD}; sudo RUST_LOG=trace nohup lldb-server g *:${GDBPORT} ${TARGET_BIN_FILE} > /dev/null 2>&1 &'"
|
||||||
Executable
+30
@@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
VSCODE_WS="$(pwd)"
|
||||||
|
SSH_REMOTE="raspberrypi.local"
|
||||||
|
|
||||||
|
APP="lightsabre_backend"
|
||||||
|
TARGET_ARCH="armv7-unknown-linux-gnueabihf"
|
||||||
|
BUILD_BIN_FILE="${VSCODE_WS}/target/${TARGET_ARCH}/debug/${APP}"
|
||||||
|
TARGET_USER="pi"
|
||||||
|
TARGET_BIN_FILE="/tmp/${APP}"
|
||||||
|
TARGET_CWD="/tmp"
|
||||||
|
|
||||||
|
cargo build
|
||||||
|
|
||||||
|
ssh "${TARGET_USER}@${SSH_REMOTE}" "sudo killall lldb-server ${APP}"
|
||||||
|
|
||||||
|
if ! rsync -avz "${BUILD_BIN_FILE}" "${TARGET_USER}@${SSH_REMOTE}:${TARGET_BIN_FILE}"; then
|
||||||
|
# If rsync doesn't work, it may not be available on target. Fallback to trying SSH copy.
|
||||||
|
if ! scp "${BUILD_BIN_FILE}" "${TARGET_USER}@${SSH_REMOTE}:${TARGET_BIN_FILE}"; then
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$1" == "--debug" ]]; then
|
||||||
|
echo "Running in debug mode"
|
||||||
|
ssh "${TARGET_USER}@${SSH_REMOTE}" "sh -c 'cd ${TARGET_CWD}; sudo RUST_LOG=debug lldb -o run ${TARGET_BIN_FILE}'"
|
||||||
|
else
|
||||||
|
echo "Running in release mode"
|
||||||
|
ssh "${TARGET_USER}@${SSH_REMOTE}" "sh -c 'cd ${TARGET_CWD}; sudo RUST_LOG=info ${TARGET_BIN_FILE}'"
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user