diff --git a/.github/workflows/firmware.yml b/.github/workflows/firmware.yml index 64603a4f..96e915d5 100644 --- a/.github/workflows/firmware.yml +++ b/.github/workflows/firmware.yml @@ -34,11 +34,13 @@ jobs: - run: cargo install flip-link - run: cargo make --cwd b1display - run: cargo make --cwd c1minimal + - run: cargo make --cwd keyboard - run: cargo make --cwd ledmatrix build-release - run: cargo make --cwd ledmatrix build-release-10k - run: cargo make --cwd ledmatrix build-release-evt - run: cargo make --cwd b1display build-release - run: cargo make --cwd c1minimal build-release + - run: cargo make --cwd keyboard build-release - name: Convert to UF2 format run: | @@ -46,6 +48,7 @@ jobs: sudo apt-get install -y libudev-dev cargo make --cwd b1display uf2 cargo make --cwd c1minimal uf2 + cargo make --cwd keyboard uf2 cargo make --cwd ledmatrix build-release-10k-uf2 cargo make --cwd ledmatrix build-release-evt-uf2 cargo make --cwd ledmatrix uf2 @@ -56,6 +59,7 @@ jobs: sudo apt-get install -y llvm cargo make --cwd b1display bin cargo make --cwd c1minimal bin + cargo make --cwd keyboard bin cargo make --cwd ledmatrix bin - name: Upload ledmatrix files @@ -87,6 +91,14 @@ jobs: target/thumbv6m-none-eabi/release/c1minimal.bin target/thumbv6m-none-eabi/release/c1minimal.uf2 + - name: Upload keyboard files + uses: actions/upload-artifact@v3 + with: + name: c1minimal_fw_${{github.sha}} + path: | + target/thumbv6m-none-eabi/release/keyboard.bin + target/thumbv6m-none-eabi/release/keyboard.uf2 + linting: name: Linting runs-on: ubuntu-latest @@ -103,6 +115,7 @@ jobs: sudo apt-get install -y libudev-dev cargo make clippy --cwd b1display cargo make clippy --cwd c1minimal + cargo make clippy --cwd keyboard cargo make clippy --cwd ledmatrix # fl16-inputmodules/src/serialnum.rs @@ -114,6 +127,7 @@ jobs: cargo pkgid -p fl16-inputmodules | cut -d "#" -f2 >> versions.tmp cargo pkgid -p b1display | cut -d "#" -f2 >> versions.tmp cargo pkgid -p c1minimal | cut -d "#" -f2 >> versions.tmp + cargo pkgid -p keyboard | cut -d "#" -f2 >> versions.tmp cargo pkgid -p ledmatrix | cut -d "#" -f2 >> versions.tmp uniq -c versions.tmp | [ $(wc -l) -eq 1 ] @@ -129,5 +143,6 @@ jobs: - run: | cargo fmt -p b1display -- --check cargo fmt -p c1minimal -- --check + cargo fmt -p keyboard -- --check cargo fmt -p ledmatrix -- --check cargo fmt -p fl16-inputmodules -- --check diff --git a/.github/workflows/software.yml b/.github/workflows/software.yml index d65c8aee..537b8dcc 100644 --- a/.github/workflows/software.yml +++ b/.github/workflows/software.yml @@ -8,6 +8,7 @@ on: paths-ignore: - 'b1display/**' - 'c1minimal/**' + - 'keyboard/**' - 'fl16-inputmodules/**' - 'ledmatrix/**' pull_request: @@ -16,6 +17,7 @@ on: paths-ignore: - 'b1display/**' - 'c1minimal/**' + - 'keyboard/**' - 'fl16-inputmodules/**' - 'ledmatrix/**' diff --git a/.github/workflows/traditional-cargo.yml b/.github/workflows/traditional-cargo.yml index fcd96c49..baa162d9 100644 --- a/.github/workflows/traditional-cargo.yml +++ b/.github/workflows/traditional-cargo.yml @@ -36,6 +36,7 @@ jobs: - run: cargo build -p ledmatrix --features evt - run: cargo build -p b1display - run: cargo build -p c1minimal + - run: cargo build -p keyboard linux-software: name: Build Linux @@ -92,6 +93,7 @@ jobs: run: | cargo clippy -p b1display -- --deny=warnings cargo clippy -p c1minimal -- --deny=warnings + cargo clippy -p keyboard -- --deny=warnings cargo clippy -p ledmatrix -- --deny=warnings cargo clippy -p fl16-inputmodules -- --deny=warnings diff --git a/Cargo.lock b/Cargo.lock index 00663417..1396ee70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -207,7 +207,7 @@ dependencies = [ "embedded-hal", "fl16-inputmodules", "fugit", - "heapless", + "heapless 0.7.16", "rp2040-boot2", "rp2040-hal", "rp2040-panic-usb-boot", @@ -280,6 +280,18 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "bumpalo" version = "3.13.0" @@ -315,7 +327,7 @@ dependencies = [ "embedded-hal", "fl16-inputmodules", "fugit", - "heapless", + "heapless 0.7.16", "rp2040-boot2", "rp2040-hal", "rp2040-panic-usb-boot", @@ -784,7 +796,7 @@ dependencies = [ "embedded-graphics", "embedded-hal", "fugit", - "heapless", + "heapless 0.7.16", "is31fl3741", "num", "num-derive", @@ -820,6 +832,45 @@ dependencies = [ "num-traits", ] +[[package]] +name = "frunk" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a351b59e12f97b4176ee78497dff72e4276fb1ceb13e19056aca7fa0206287" +dependencies = [ + "frunk_core", + "frunk_derives", +] + +[[package]] +name = "frunk_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af2469fab0bd07e64ccf0ad57a1438f63160c69b2e57f04a439653d68eb558d6" + +[[package]] +name = "frunk_derives" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fa992f1656e1707946bbba340ad244f0814009ef8c0118eb7b658395f19a2e" +dependencies = [ + "frunk_proc_macro_helpers", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "frunk_proc_macro_helpers" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b54add839292b743aeda6ebedbd8b11e93404f902c56223e51b9ec18a13d2c" +dependencies = [ + "frunk_core", + "proc-macro2", + "quote", + "syn 2.0.18", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -835,6 +886,12 @@ dependencies = [ "gcd", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "gcd" version = "2.3.0" @@ -883,6 +940,15 @@ dependencies = [ "byteorder", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -896,12 +962,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" dependencies = [ "atomic-polyfill", - "hash32", + "hash32 0.2.1", "rustc_version 0.4.0", "spin", "stable_deref_trait", ] +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32 0.3.1", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.4.1" @@ -1021,8 +1097,19 @@ dependencies = [ [[package]] name = "is31fl3741" -version = "0.2.1" -source = "git+https://github.com/FrameworkComputer/is31fl3741-rs?branch=sw-enablement#fb88ad1baf28aa2a61bc85ff5db83c6e2b661ed5" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d02b7bcb58e9f5ef8562cec1914198f71a35aa8f90e23326b77d12502ab860" +dependencies = [ + "embedded-graphics-core", + "embedded-hal", +] + +[[package]] +name = "is31fl3743a" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884ca84ba6f4c9a42cf7116611fda1c94f1c735555ae768fbfccad8ccc491235" dependencies = [ "embedded-graphics-core", "embedded-hal", @@ -1089,6 +1176,29 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keyboard" +version = "0.1.7" +dependencies = [ + "cfg-if", + "cortex-m", + "cortex-m-rt", + "defmt", + "defmt-rtt", + "embedded-hal", + "fl16-inputmodules", + "fugit", + "heapless 0.7.16", + "is31fl3743a", + "rp2040-boot2", + "rp2040-hal", + "rp2040-panic-usb-boot", + "usb-device", + "usbd-hid", + "usbd-human-interface-device", + "usbd-serial", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1112,7 +1222,7 @@ dependencies = [ "embedded-hal", "fl16-inputmodules", "fugit", - "heapless", + "heapless 0.7.16", "is31fl3741", "rp2040-boot2", "rp2040-hal", @@ -1274,7 +1384,7 @@ dependencies = [ "bitflags 1.3.2", "jni-sys", "ndk-sys", - "num_enum", + "num_enum 0.5.11", "raw-window-handle", "thiserror", ] @@ -1407,7 +1517,16 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" dependencies = [ - "num_enum_derive", + "num_enum_derive 0.5.11", +] + +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive 0.7.2", ] [[package]] @@ -1422,6 +1541,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + [[package]] name = "object" version = "0.30.4" @@ -1469,6 +1599,33 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "option-block" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f2c5d345596a14d7c8b032a68f437955f0059f2eb9a5972371c84f7eef3227" + +[[package]] +name = "packed_struct" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36b29691432cc9eff8b282278473b63df73bea49bc3ec5e67f31a3ae9c3ec190" +dependencies = [ + "bitvec", + "packed_struct_codegen", +] + +[[package]] +name = "packed_struct_codegen" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd6706dfe50d53e0f6aa09e12c034c44faacd23e966ae5a209e8bdb8f179f98" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "parking_lot" version = "0.7.1" @@ -1534,7 +1691,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3" dependencies = [ "arrayvec", - "num_enum", + "num_enum 0.5.11", "paste", ] @@ -1624,6 +1781,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.6.5" @@ -1844,8 +2007,7 @@ dependencies = [ [[package]] name = "rp2040-hal" version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1369bb84862d7f69391a96606b2f29a00bfce7f29a749e23d5f01fc3f607ada0" +source = "git+https://github.com/FrameworkComputer/rp-hal?branch=adc-pull-down-up#ef80ceba220f0dc0f50cabeee4ed8fc3c597cf43" dependencies = [ "cortex-m", "critical-section", @@ -1867,10 +2029,8 @@ dependencies = [ [[package]] name = "rp2040-hal-macros" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86479063e497efe1ae81995ef9071f54fd1c7427e04d6c5b84cde545ff672a5e" +source = "git+https://github.com/FrameworkComputer/rp-hal?branch=adc-pull-down-up#ef80ceba220f0dc0f50cabeee4ed8fc3c597cf43" dependencies = [ - "cortex-m-rt", "proc-macro2", "quote", "syn 1.0.109", @@ -2148,6 +2308,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "termcolor" version = "1.2.0" @@ -2317,6 +2483,21 @@ dependencies = [ "usbd-hid-descriptors", ] +[[package]] +name = "usbd-human-interface-device" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69710303c06f23a1259d086bfb241212ae1ccfb5d582ebd596bb042d662ed73" +dependencies = [ + "frunk", + "fugit", + "heapless 0.8.0", + "num_enum 0.7.2", + "option-block", + "packed_struct", + "usb-device", +] + [[package]] name = "usbd-serial" version = "0.1.1" @@ -2682,3 +2863,12 @@ dependencies = [ "rp2040-hal", "smart-leds-trait", ] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] diff --git a/Cargo.toml b/Cargo.toml index e86f3e3b..06c3ebd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ resolver = "2" members = [ "b1display", "c1minimal", + "keyboard", "ledmatrix", "fl16-inputmodules", "inputmodule-control", @@ -19,6 +20,9 @@ members = [ # target. But we set the default target to thumbv6m-none-eabi default-members = ["fl16-inputmodules"] +[patch.crates-io] +rp2040-hal = { git = "https://github.com/FrameworkComputer/rp-hal", branch = "adc-pull-down-up" } + [workspace.dependencies] cortex-m = "0.7" cortex-m-rt = "0.7.3" @@ -36,7 +40,8 @@ usbd-serial = "0.1.1" usbd-hid = "0.6.1" fugit = "0.3.7" # LED Matrix -is31fl3741 = { git = "https://github.com/FrameworkComputer/is31fl3741-rs", branch = "sw-enablement" } +is31fl3741 = "0.3.0" +is31fl3743a = "0.1.0" # B1 Display st7306 = { git = "https://github.com/FrameworkComputer/st7306-rs", branch = "update-deps" } embedded-graphics = "0.8" @@ -57,6 +62,12 @@ incremental = true # To allow single-stepping through code use 0. Will cause timing issues, though opt-level = 3 +[profile.dev.package.keyboard] +codegen-units = 1 +incremental = true +# To allow single-stepping through code use 0. Will cause timing issues, though +opt-level = 3 + [profile.dev.package.b1display] codegen-units = 1 incremental = true diff --git a/fl16-inputmodules/Cargo.toml b/fl16-inputmodules/Cargo.toml index 9c28fd13..a91de0eb 100644 --- a/fl16-inputmodules/Cargo.toml +++ b/fl16-inputmodules/Cargo.toml @@ -47,3 +47,4 @@ default = [] ledmatrix = ["is31fl3741"] b1display = ["st7306", "embedded-graphics", "tinybmp"] c1minimal = ["smart-leds", "ws2812-pio"] +keyboard = [] diff --git a/fl16-inputmodules/src/keyboard_hal.rs b/fl16-inputmodules/src/keyboard_hal.rs new file mode 100644 index 00000000..beeb3ec1 --- /dev/null +++ b/fl16-inputmodules/src/keyboard_hal.rs @@ -0,0 +1,97 @@ +// Taken from rp_pico hal and adjusted + +pub extern crate rp2040_hal as hal; + +extern crate cortex_m_rt; +pub use hal::entry; + +/// The linker will place this boot block at the start of our program image. We +/// need this to help the ROM bootloader get our code up and running. +#[link_section = ".boot2"] +#[no_mangle] +#[used] +pub static BOOT2_FIRMWARE: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +pub use hal::pac; + +hal::bsp_pins!( + /// GPIO 0 is connected to the SLEEP# pin of the EC + Gpio0 { name: sleep }, + /// Mux selector A + Gpio1 { name: mux_a }, + /// Mux selector B + Gpio2 { name: mux_b }, + /// Mux selector C + Gpio3 { name: mux_c }, + /// Mux enable + Gpio4 { name: mux_enable }, + /// Pull low when firmware has started to turn off bootloader logic + Gpio5 { name: boot_done }, + /// Connected to KSI5 but unused, should use high-Z + Gpio6 { + name: ksi5_reserved + }, + /// Connected to KSI5 but unused, should use high-Z + Gpio7 { + name: ksi6_reserved + }, + /// Keyboard column drive + Gpio8 { name: kso0 }, + /// Keyboard column drive + Gpio9 { name: kso1 }, + /// Keyboard column drive + Gpio10 { name: kso2 }, + /// Keyboard column drive + Gpio11 { name: kso3 }, + /// Keyboard column drive + Gpio12 { name: kso4 }, + /// Keyboard column drive + Gpio13 { name: kso5 }, + /// Keyboard column drive + Gpio14 { name: kso6 }, + /// Keyboard column drive + Gpio15 { name: kso7 }, + /// Keyboard column drive + Gpio16 { name: kso13 }, + /// Keyboard column drive + Gpio17 { name: kso12 }, + /// Keyboard column drive + Gpio18 { name: kso11 }, + /// Keyboard column drive + Gpio19 { name: kso10 }, + /// Keyboard column drive + Gpio20 { name: kso9 }, + /// Keyboard column drive + Gpio21 { name: kso8 }, + /// Keyboard column drive + Gpio22 { name: kso15 }, + /// Keyboard column drive + Gpio23 { name: kso14 }, + /// Capslock LED + Gpio24 { name: caps_led }, + /// Single zone backlight (unused on RGB keyboard) + Gpio25 { name: backlight }, + /// GPIO 26 is connected to I2C SDA of the LED controller + Gpio26 { + name: gpio26, + aliases: { + /// I2C Function alias for pin [crate::Pins::gpio26]. + FunctionI2C: Gp26I2C1Sda + } + }, + /// GPIO 27 is connected to I2C SCL of the LED controller + Gpio27 { + name: gpio27, + aliases: { + /// I2C Function alias for pin [crate::Pins::gpio27]. + FunctionI2C: Gp27I2C1Scl + } + }, + /// Analog IN from mux + Gpio28 { name: analog_in }, + /// GPIO 29 is connected to the SDB pin of the LED controller + Gpio29 { name: sdb }, +); + +// External crystal frequency, same as Raspberry Pi Pico +pub const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; diff --git a/fl16-inputmodules/src/lib.rs b/fl16-inputmodules/src/lib.rs index e7fa8370..bfb278ec 100644 --- a/fl16-inputmodules/src/lib.rs +++ b/fl16-inputmodules/src/lib.rs @@ -5,8 +5,13 @@ all(feature = "ledmatrix", feature = "b1display"), all(feature = "ledmatrix", feature = "c1minimal"), all(feature = "b1display", feature = "c1minimal"), + all(feature = "ledmatrix", feature = "keyboard"), + all(feature = "b1display", feature = "keyboard"), + all(feature = "c1minimal", feature = "keyboard"), ))] -compile_error!("Features \"ledmatrix\", \"b1display\", and \"c1minimal\" are mutually exclusive"); +compile_error!( + "Features \"ledmatrix\", \"b1display\", \"c1minimal\", and \"keyboard\" are mutually exclusive" +); #[cfg(feature = "ledmatrix")] pub mod fl16; @@ -30,5 +35,8 @@ pub mod lcd_hal; #[cfg(feature = "c1minimal")] pub mod minimal_hal; +#[cfg(feature = "keyboard")] +pub mod keyboard_hal; + pub mod control; pub mod serialnum; diff --git a/keyboard/Cargo.toml b/keyboard/Cargo.toml new file mode 100644 index 00000000..6734e9b7 --- /dev/null +++ b/keyboard/Cargo.toml @@ -0,0 +1,44 @@ +[package] +edition = "2021" +name = "keyboard" +version = "0.1.7" + +[features] +default = ["macropad"] +cli = [] +ansi = [] +iso = [] +jis = [] +numpad = [] +macropad = [] + +[dependencies] +cortex-m.workspace = true +cortex-m-rt.workspace = true +embedded-hal.workspace = true + +defmt.workspace = true +defmt-rtt.workspace = true + +#panic-probe.workspace = true +rp2040-panic-usb-boot.workspace = true + +# Not using an external BSP, we've got the Framework Laptop 16 BSPs locally in this crate +rp2040-hal.workspace = true +rp2040-boot2.workspace = true + +# USB Serial +usb-device.workspace = true +heapless.workspace = true +usbd-serial.workspace = true +usbd-hid.workspace = true +fugit.workspace = true + +usbd-human-interface-device = "0.4.5" +is31fl3743a.workspace = true + +cfg-if = "1.0.0" + +[dependencies.fl16-inputmodules] +path = "../fl16-inputmodules" +features = ["keyboard"] diff --git a/keyboard/Makefile.toml b/keyboard/Makefile.toml new file mode 100644 index 00000000..38bc921b --- /dev/null +++ b/keyboard/Makefile.toml @@ -0,0 +1,19 @@ +extend = "../Makefile.toml" + +[tasks.uf2] +command = "elf2uf2-rs" +args = [ + "../target/thumbv6m-none-eabi/release/keyboard", + "../target/thumbv6m-none-eabi/release/keyboard.uf2", +] +dependencies = ["build-release"] +install_crate = "elf2uf2-rs" + +[tasks.bin] +command = "llvm-objcopy" +args = [ + "-Obinary", + "../target/thumbv6m-none-eabi/release/keyboard", + "../target/thumbv6m-none-eabi/release/keyboard.bin", +] +dependencies = ["build-release"] diff --git a/keyboard/README.md b/keyboard/README.md new file mode 100644 index 00000000..9beb06c0 --- /dev/null +++ b/keyboard/README.md @@ -0,0 +1,4 @@ +# Keyboard + +NOT THE OFFICIAL Keyboard firmware +Just experimental reference code for building keyboard firmare in Rust \ No newline at end of file diff --git a/keyboard/src/main.rs b/keyboard/src/main.rs new file mode 100644 index 00000000..44c3d93a --- /dev/null +++ b/keyboard/src/main.rs @@ -0,0 +1,602 @@ +//! NOT THE OFFICIAL Keyboard firmware +//! Just experimental reference code for building keyboard firmare in Rust +#![no_std] +#![no_main] +#![allow(clippy::needless_range_loop)] + +// TODO: +// - [x] Basic keyscan +// - [x] Send UP/LEFT/RIGHT/CAPS HID reports, DOWN to go into bootloader +// - [x] Can go into D2 (tested on Linux) +// - [x] Can wake host (remote wakeup) +// - [ ] Both serial and HID keyboard as composite device +// - [ ] Key Debouncing +// - [ ] 1-Zone PWM backlight +// - [x] RGB backlight (needs new/modified Rust driver) +// - [x] Working (all white) +// - [ ] Map all LEDs +// - [ ] Separate builds for different keyboard variants +// - [ ] Measure and optimize scan frequency +// - [ ] Implement full key matrix with all keys +// - [ ] Implement second layer for FN (including FN lock) +// - [ ] Persist brightness setting and FN lock through reset +// - [ ] Media keys + +use cortex_m::delay::Delay; +//use defmt::*; +use crate::rgb_matrix::{LedMatrix, DVT2_CALC_PIXEL}; +use defmt_rtt as _; +use embedded_hal::adc::OneShot; +use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin}; +use rp2040_hal::gpio::bank0::Gpio28; +use rp2040_hal::gpio::{self, Input, PullUp}; +use usbd_human_interface_device::page::Keyboard; +use usbd_human_interface_device::prelude::UsbHidClassBuilder; +use usbd_human_interface_device::prelude::*; +use usbd_human_interface_device::UsbHidError; + +mod rgb_matrix; + +use core::fmt::Display; +use core::fmt::{self, Formatter}; + +use rp2040_hal::{ + gpio::{ + bank0::{ + Gpio1, Gpio10, Gpio11, Gpio12, Gpio13, Gpio14, Gpio15, Gpio16, Gpio17, Gpio18, Gpio19, + Gpio2, Gpio20, Gpio21, Gpio22, Gpio23, Gpio3, Gpio8, Gpio9, + }, + Output, Pin, PinState, PushPull, + }, + Adc, Timer, +}; +//#[cfg(debug_assertions)] +//use panic_probe as _; +use rp2040_panic_usb_boot as _; + +/// List maximum current as 500mA in the USB descriptor +const MAX_CURRENT: usize = 500; + +// Provide an alias for our BSP so we can switch targets quickly. +// Uncomment the BSP you included in Cargo.toml, the rest of the code does not need to change. +use bsp::entry; +use fl16_inputmodules::keyboard_hal as bsp; +//use rp_pico as bsp; +// use sparkfun_pro_micro_rp2040 as bsp; + +use bsp::hal::{ + clocks::{init_clocks_and_plls, Clock}, + pac, + sio::Sio, + usb, + watchdog::Watchdog, +}; +use fugit::RateExtU32; + +// USB Device support +use usb_device::{class_prelude::*, prelude::*}; + +// USB Communications Class Device support +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +// Used to demonstrate writing formatted strings +use core::fmt::Write; +use heapless::String; + +use fl16_inputmodules::serialnum::device_release; + +const MATRIX_COLS: usize = 16; +const MATRIX_ROWS: usize = 8; +const ADC_THRESHOLD: usize = 2900; + +struct Mux { + a: Pin>, + b: Pin>, + c: Pin>, + // TODO + // x: Pin>, +} +impl Mux { + pub fn select_row(&mut self, row: usize) { + let index = match row { + 0 => 2, + 1 => 0, + 2 => 1, + _ => row, + }; + self.a.set_state(PinState::from(index & 0x01 != 0)).unwrap(); + self.b.set_state(PinState::from(index & 0x02 != 0)).unwrap(); + self.c.set_state(PinState::from(index & 0x04 != 0)).unwrap(); + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Default)] +pub struct Col(u8); +#[derive(Debug, PartialEq, Eq, Clone, Default)] +struct Matrix([Col; MATRIX_COLS]); + +impl Matrix { + pub fn set(&mut self, row: usize, col: usize, val: bool) { + let mask = 1 << row; + + self.0[col].0 = if val { + self.0[col].0 | mask + } else { + self.0[col].0 & !mask + }; + } +} + +impl Display for Col { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + for row in 0..MATRIX_ROWS { + let val = (self.0 & (1 << row)) >> row; + write!(f, "{:b}", val)? + } + Ok(()) + } +} + +type Kso = ( + Pin>, + Pin>, + Pin>, + Pin>, + Pin>, + Pin>, + Pin>, + Pin>, + Pin>, + Pin>, + Pin>, + Pin>, + Pin>, + Pin>, + Pin>, + Pin>, +); + +struct Scanner { + kso: Kso, + mux: Mux, + adc: (Adc, Pin>), +} +impl Scanner { + fn drive_col(&mut self, col: usize, state: PinState) { + match col { + 0 => self.kso.0.set_state(state).unwrap(), + 1 => self.kso.1.set_state(state).unwrap(), + 2 => self.kso.2.set_state(state).unwrap(), + 3 => self.kso.3.set_state(state).unwrap(), + 4 => self.kso.4.set_state(state).unwrap(), + 5 => self.kso.5.set_state(state).unwrap(), + 6 => self.kso.6.set_state(state).unwrap(), + 7 => self.kso.7.set_state(state).unwrap(), + 8 => self.kso.8.set_state(state).unwrap(), + 9 => self.kso.9.set_state(state).unwrap(), + 10 => self.kso.10.set_state(state).unwrap(), + 11 => self.kso.11.set_state(state).unwrap(), + 12 => self.kso.12.set_state(state).unwrap(), + 13 => self.kso.13.set_state(state).unwrap(), + 14 => self.kso.14.set_state(state).unwrap(), + 15 => self.kso.15.set_state(state).unwrap(), + _ => unreachable!(), + } + } + fn read_voltage(&mut self) -> usize { + let _adc_read: u16 = self.adc.0.read(&mut self.adc.1).unwrap(); + 33000 + } + pub fn measure_key(&mut self, row: usize, col: usize) -> (usize, usize) { + for col in 0..MATRIX_COLS { + self.drive_col(col, PinState::High); + } + self.drive_col(col, PinState::Low); + + self.mux.select_row(row); + // Let column and mux settle a bit + cortex_m::asm::delay(2000); + let adc_read: u16 = self.adc.0.read(&mut self.adc.1).unwrap(); + + self.drive_col(col, PinState::High); + + let voltage_10k = ((adc_read as usize) * 3300) / 4096; + (voltage_10k / 1_000, voltage_10k % 1_000) + } + pub fn scan(&mut self) -> Matrix { + let mut matrix = Matrix::default(); + + // Initialize all cols as high + for col in 0..MATRIX_COLS { + self.drive_col(col, PinState::High); + } + + for col in 0..MATRIX_COLS { + self.drive_col(col, PinState::Low); + + for row in 0..MATRIX_ROWS { + self.mux.select_row(row); + + if self.read_voltage() < ADC_THRESHOLD { + matrix.set(row, col, true); + } + } + + self.drive_col(col, PinState::High); + } + + matrix.set(3, 4, true); + matrix.set(0, 4, true); + matrix + } +} + +#[entry] +fn main() -> ! { + let mut pac = pac::Peripherals::take().unwrap(); + let core = pac::CorePeripherals::take().unwrap(); + let mut watchdog = Watchdog::new(pac.WATCHDOG); + let sio = Sio::new(pac.SIO); + + let clocks = init_clocks_and_plls( + bsp::XOSC_CRYSTAL_FREQ, + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut watchdog, + ) + .ok() + .unwrap(); + + let rosc = rp2040_hal::rosc::RingOscillator::new(pac.ROSC); + let _rosc = rosc.initialize(); + + let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()); + + let pins = bsp::Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + + // Set up the USB driver + let usb_bus = UsbBusAllocator::new(usb::UsbBus::new( + pac.USBCTRL_REGS, + pac.USBCTRL_DPRAM, + clocks.usb_clock, + true, + &mut pac.RESETS, + )); + + // Set up the USB Communications Class Device driver + //let mut serial = SerialPort::new(&usb_bus); + let mut keyboard_hid = UsbHidClassBuilder::new() + .add_device(usbd_human_interface_device::device::keyboard::BootKeyboardConfig::default()) + .build(&usb_bus); + + #[cfg(feature = "macropad")] + let pid = 0x013; + #[cfg(feature = "ansi")] + let pid = 0x012; + #[cfg(feature = "numpad")] + let pid = 0x014; + #[cfg(feature = "iso")] + let pid = 0x018; + #[cfg(feature = "jis")] + let pid = 0x019; + + #[cfg(feature = "macropad")] + let product = "Rust Macropad"; + #[cfg(feature = "ansi")] + let product = "Rust ANSI Keyboard"; + #[cfg(feature = "numpad")] + let product = "Rust Numpad"; + #[cfg(feature = "iso")] + let product = "Rust ISO Keyboard"; + #[cfg(feature = "jis")] + let product = "Rust JIS Keyboard"; + + let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x32ac, pid)) + .manufacturer("Framework Computer Inc") + .product(product) + .supports_remote_wakeup(true) + .device_class(0) + .device_sub_class(0) + .device_protocol(0) + .max_power(MAX_CURRENT) + .serial_number("testing") + .device_release(device_release()) + .build(); + + // Disable bootloader circuitry + let mut boot_done = pins.boot_done.into_push_pull_output(); + boot_done.set_low().unwrap(); + + // pins.gp26 // SDA + // pins.gp27 // SCL + + let mut caps_led = pins.caps_led.into_push_pull_output(); + let mut _backlight = pins.backlight.into_push_pull_output(); + + // Pull low to enable mux + let mut mux_enable = pins.mux_enable.into_push_pull_output(); + mux_enable.set_low().unwrap(); + let mux_a = pins.mux_a.into_push_pull_output(); + let mux_b = pins.mux_b.into_push_pull_output(); + let mux_c = pins.mux_c.into_push_pull_output(); + + // KS0 - KSO7 for Keyboard and Numpad + let kso0 = pins.kso0.into_push_pull_output(); + let kso1 = pins.kso1.into_push_pull_output(); + let kso2 = pins.kso2.into_push_pull_output(); + let kso3 = pins.kso3.into_push_pull_output(); + let kso4 = pins.kso4.into_push_pull_output(); + let kso5 = pins.kso5.into_push_pull_output(); + let kso6 = pins.kso6.into_push_pull_output(); + let kso7 = pins.kso7.into_push_pull_output(); + // KS08 - KS015 for Keyboard only + let kso8 = pins.kso8.into_push_pull_output(); + let kso9 = pins.kso9.into_push_pull_output(); + let kso10 = pins.kso10.into_push_pull_output(); + let kso11 = pins.kso11.into_push_pull_output(); + let kso12 = pins.kso12.into_push_pull_output(); + let kso13 = pins.kso13.into_push_pull_output(); + let kso14 = pins.kso14.into_push_pull_output(); + let kso15 = pins.kso15.into_push_pull_output(); + // Set unused pins to input to avoid interfering. They're hooked up to rows 5 and 6 + let _ = pins.ksi5_reserved.into_floating_input(); + let _ = pins.ksi6_reserved.into_floating_input(); + + let sleep = pins.sleep.into_floating_input(); + + // Enable LED controller + let mut led_enable = pins.sdb.into_push_pull_output(); + led_enable.set_high().unwrap(); + + let i2c = bsp::hal::I2C::i2c1( + pac.I2C1, + pins.gpio26.into_mode::(), + pins.gpio27.into_mode::(), + 1000.kHz(), + &mut pac.RESETS, + &clocks.peripheral_clock, + ); + + let mut matrix = LedMatrix::new(i2c, |_, _| 0x00); + + cfg_if::cfg_if! { + if #[cfg(any(feature = "ansi", feature = "macropad"))] { + matrix.set_address(0b0100000); + matrix + .setup(&mut delay) + .expect("failed to setup RGB controller"); + matrix.set_scaling(0xFF).expect("failed to set scaling"); + matrix.device.fill(0xFF); + } + } + + cfg_if::cfg_if! { + if #[cfg(feature = "ansi")] { + matrix.set_address(0b0100011); + matrix + .setup(&mut delay) + .expect("failed to setup RGB controller"); + matrix.set_scaling(0xFF).expect("failed to set scaling"); + matrix.device.fill(0xFF); + } + } + + let timer = Timer::new(pac.TIMER, &mut pac.RESETS); + let mut scan_timer = timer.get_counter().ticks(); + let mut capslock_timer = timer.get_counter().ticks(); + caps_led.set_high().unwrap(); + + let adc = Adc::new(pac.ADC, &mut pac.RESETS); + let adc_x = pins.analog_in.into_pull_up_input(); + + let mut scanner = Scanner { + kso: ( + kso0, kso1, kso2, kso3, kso4, kso5, kso6, kso7, kso8, kso9, kso10, kso11, kso12, kso13, + kso14, kso15, + ), + mux: Mux { + a: mux_a, + b: mux_b, + c: mux_c, + }, + adc: (adc, adc_x), + }; + + let mut usb_initialized; + let mut usb_suspended = false; + loop { + let _ = sleep.is_high(); + + // Turn back on when not suspended anymore + if !usb_suspended { + if caps_led.is_set_low().unwrap() { + caps_led.set_high().unwrap(); + #[cfg(any(feature = "ansi", feature = "macropad"))] + matrix.device.fill(0xFF); + } + } + + // Blink when in USB Suspend + // 500_000us = 500ms = 0.5s + if usb_suspended && timer.get_counter().ticks() > capslock_timer + 500_000 { + if caps_led.is_set_high().unwrap() { + #[cfg(any(feature = "ansi", feature = "macropad"))] + matrix.device.fill(0x00); + caps_led.set_low().unwrap(); + } else { + caps_led.set_high().unwrap(); + #[cfg(any(feature = "ansi", feature = "macropad"))] + matrix.device.fill(0xFF); + } + capslock_timer = timer.get_counter().ticks(); + } + + let mut keycode: Option = None; + if timer.get_counter().ticks() > scan_timer + 250_000 { + cfg_if::cfg_if! { + if #[cfg(any(feature = "ansi", feature = "iso", feature = "jis"))] { + let left = scanner.measure_key(6, 11); + let up = scanner.measure_key(1, 13); + let down = scanner.measure_key(1, 8); + let right = scanner.measure_key(2, 15); + let caps = scanner.measure_key(4, 4); + + let left_p = left.0 < 2 || (left.0 == 2 && left.1 < 290); + let right_p = right.0 < 2 || (right.0 == 2 && right.1 < 290); + let up_p = up.0 < 2 || (up.0 == 2 && up.1 < 290); + let down_p = down.0 < 2 || (down.0 == 2 && down.1 < 290); + let caps_p = caps.0 < 2 || (caps.0 == 2 && caps.1 < 290); + + if left_p { + keycode = Some(Keyboard::LeftArrow); + } else if right_p { + keycode = Some(Keyboard::RightArrow); + } else if up_p { + keycode = Some(Keyboard::UpArrow); + } else if down_p { + keycode = None; + rp2040_hal::rom_data::reset_to_usb_boot(0, 0); + } else if caps_p { + keycode = Some(Keyboard::CapsLock); + } else { + keycode = None; + } + } + } + + cfg_if::cfg_if! { + if #[cfg(any(feature = "numpad", feature = "macropad"))] { + let one = scanner.measure_key(0, 3); + let two = scanner.measure_key(0, 7); + let three = scanner.measure_key(1, 4); + let four = scanner.measure_key(2, 6); + + let one_p = one.0 < 2 || (one.0 == 2 && one.1 < 290); + let two_p = two.0 < 2 || (two.0 == 2 && two.1 < 290); + let three_p = three.0 < 2 || (three.0 == 2 && three.1 < 290); + let four_p = four.0 < 2 || (four.0 == 2 && four.1 < 290); + + if one_p { + keycode = Some(Keyboard::Keyboard1); + } else if two_p { + keycode = Some(Keyboard::Keyboard2); + } else if three_p { + keycode = Some(Keyboard::Keyboard3); + } else if four_p { + keycode = Some(Keyboard::Keyboard4); + } else { + keycode = None; + } + } + } + + scan_timer = timer.get_counter().ticks(); + } + + if !usb_suspended { + let _ = keyboard_hid.device().read_report(); + + // Setup the report for the control channel + let keycodes = if let Some(keycode) = keycode { + [keycode] + } else { + [Keyboard::NoEventIndicated] + }; + match keyboard_hid.device().write_report(keycodes) { + Err(UsbHidError::WouldBlock) | Err(UsbHidError::Duplicate) | Ok(_) => {} + Err(e) => panic!("Failed to write keyboard report: {:?}", e), + } + match keyboard_hid.tick() { + Err(UsbHidError::WouldBlock) | Ok(_) => {} + Err(e) => panic!("Failed to process keyboard tick: {:?}", e), + } + } + + // Wake the host. + if keycode.is_some() && usb_suspended && usb_dev.remote_wakeup_enabled() { + usb_dev.bus().remote_wakeup(); + } + + // Check for new data + if usb_dev.poll(&mut [&mut keyboard_hid]) { + match usb_dev.state() { + // Default: Device has just been created or reset + // Addressed: Device has received an address for the host + UsbDeviceState::Default | UsbDeviceState::Addressed => { + usb_initialized = false; + usb_suspended = false; + // Must not display anything or windows cannot enumerate properly + } + // Configured and is fully operational + UsbDeviceState::Configured => { + usb_initialized = true; + usb_suspended = false; + } + // Never occurs here. Only if poll() returns false + UsbDeviceState::Suspend => { + panic!("Never occurs here. Only if poll() returns false") + } + } + + // Ignore unused + let _ = usb_initialized; + let _ = usb_suspended; + + //let kb = Matrix::default(); + //let kb = scanner.scan(); + + keyboard_hid.poll(); + + // let mut buf = [0u8; 64]; + // match serial.read(&mut buf) { + // Err(_e) => { + // // Do nothing + // } + // Ok(0) => { + // // Do nothing + // } + // Ok(_count) => { + // match buf[0] { + // b'r' => rp2040_hal::rom_data::reset_to_usb_boot(0, 0), + // _ => (), + // } + // //let mut text: String<64> = String::new(); + // //write!(&mut text, " 01234567\r\n").unwrap(); + // //let _ = serial.write(text.as_bytes()); + + // //for col in 0..MATRIX_COLS { + // // let mut text: String<64> = String::new(); + // // write!(&mut text, "{:2}: {}\r\n", col, kb.0[col]).unwrap(); + // // let _ = serial.write(text.as_bytes()); + // //} + // } + // } + } else { + match usb_dev.state() { + // No new data + UsbDeviceState::Default | UsbDeviceState::Addressed => { + usb_initialized = false; + usb_suspended = false; + } + UsbDeviceState::Configured => { + usb_initialized = true; + usb_suspended = false; + } + UsbDeviceState::Suspend => { + usb_suspended = true; + } + } + // Ignore unused + let _ = usb_initialized; + let _ = usb_suspended; + } + } +} diff --git a/keyboard/src/rgb_matrix.rs b/keyboard/src/rgb_matrix.rs new file mode 100644 index 00000000..c750283a --- /dev/null +++ b/keyboard/src/rgb_matrix.rs @@ -0,0 +1,377 @@ +// #[cfg_attr(docsrs, doc(cfg(feature = "adafruit_rgb_13x9")))] +#[allow(unused_imports)] +use core::convert::TryFrom; +#[allow(unused_imports)] +use embedded_hal::blocking::delay::DelayMs; +use embedded_hal::blocking::i2c::Read; +use embedded_hal::blocking::i2c::Write; +#[allow(unused_imports)] +use is31fl3743a::{Error, IS31FL3743}; + +pub const DVT2_CALC_PIXEL: fn(x: u8, y: u8) -> (u8, u8) = |x: u8, y: u8| -> (u8, u8) { + // Generated by led-matrix.py + let lookup: [(u8, u8); 34 * 9] = [ + (0x00, 0), // x: 1, y: 1, sw: 1, cs: 1, id: 1 + (0x1e, 0), // x: 2, y: 1, sw: 2, cs: 1, id: 2 + (0x3c, 0), // x: 3, y: 1, sw: 3, cs: 1, id: 3 + (0x5a, 0), // x: 4, y: 1, sw: 4, cs: 1, id: 4 + (0x78, 0), // x: 5, y: 1, sw: 5, cs: 1, id: 5 + (0x96, 0), // x: 6, y: 1, sw: 6, cs: 1, id: 6 + (0x00, 1), // x: 7, y: 1, sw: 7, cs: 1, id: 7 + (0x1e, 1), // x: 8, y: 1, sw: 8, cs: 1, id: 8 + (0x5f, 1), // x: 9, y: 1, sw: 1, cs:36, id: 9 + (0x01, 0), // x: 1, y: 2, sw: 1, cs: 2, id: 10 + (0x1f, 0), // x: 2, y: 2, sw: 2, cs: 2, id: 11 + (0x3d, 0), // x: 3, y: 2, sw: 3, cs: 2, id: 12 + (0x5b, 0), // x: 4, y: 2, sw: 4, cs: 2, id: 13 + (0x79, 0), // x: 5, y: 2, sw: 5, cs: 2, id: 14 + (0x97, 0), // x: 6, y: 2, sw: 6, cs: 2, id: 15 + (0x01, 1), // x: 7, y: 2, sw: 7, cs: 2, id: 16 + (0x1f, 1), // x: 8, y: 2, sw: 8, cs: 2, id: 17 + (0x60, 1), // x: 9, y: 2, sw: 1, cs:37, id: 18 + (0x02, 0), // x: 1, y: 3, sw: 1, cs: 3, id: 19 + (0x20, 0), // x: 2, y: 3, sw: 2, cs: 3, id: 20 + (0x3e, 0), // x: 3, y: 3, sw: 3, cs: 3, id: 21 + (0x5c, 0), // x: 4, y: 3, sw: 4, cs: 3, id: 22 + (0x7a, 0), // x: 5, y: 3, sw: 5, cs: 3, id: 23 + (0x98, 0), // x: 6, y: 3, sw: 6, cs: 3, id: 24 + (0x02, 1), // x: 7, y: 3, sw: 7, cs: 3, id: 25 + (0x20, 1), // x: 8, y: 3, sw: 8, cs: 3, id: 26 + (0x61, 1), // x: 9, y: 3, sw: 1, cs:38, id: 27 + (0x03, 0), // x: 1, y: 4, sw: 1, cs: 4, id: 28 + (0x21, 0), // x: 2, y: 4, sw: 2, cs: 4, id: 29 + (0x3f, 0), // x: 3, y: 4, sw: 3, cs: 4, id: 30 + (0x5d, 0), // x: 4, y: 4, sw: 4, cs: 4, id: 31 + (0x7b, 0), // x: 5, y: 4, sw: 5, cs: 4, id: 32 + (0x99, 0), // x: 6, y: 4, sw: 6, cs: 4, id: 33 + (0x03, 1), // x: 7, y: 4, sw: 7, cs: 4, id: 34 + (0x21, 1), // x: 8, y: 4, sw: 8, cs: 4, id: 35 + (0x62, 1), // x: 9, y: 4, sw: 1, cs:39, id: 36 + (0x04, 0), // x: 1, y: 5, sw: 1, cs: 5, id: 37 + (0x22, 0), // x: 2, y: 5, sw: 2, cs: 5, id: 41 + (0x40, 0), // x: 3, y: 5, sw: 3, cs: 5, id: 45 + (0x5e, 0), // x: 4, y: 5, sw: 4, cs: 5, id: 49 + (0x7c, 0), // x: 5, y: 5, sw: 5, cs: 5, id: 53 + (0x9a, 0), // x: 6, y: 5, sw: 6, cs: 5, id: 57 + (0x04, 1), // x: 7, y: 5, sw: 7, cs: 5, id: 61 + (0x22, 1), // x: 8, y: 5, sw: 8, cs: 5, id: 65 + (0x5e, 1), // x: 9, y: 5, sw: 1, cs:35, id: 69 + (0x05, 0), // x: 1, y: 6, sw: 1, cs: 6, id: 38 + (0x23, 0), // x: 2, y: 6, sw: 2, cs: 6, id: 42 + (0x41, 0), // x: 3, y: 6, sw: 3, cs: 6, id: 46 + (0x5f, 0), // x: 4, y: 6, sw: 4, cs: 6, id: 50 + (0x7d, 0), // x: 5, y: 6, sw: 5, cs: 6, id: 54 + (0x9b, 0), // x: 6, y: 6, sw: 6, cs: 6, id: 58 + (0x05, 1), // x: 7, y: 6, sw: 7, cs: 6, id: 62 + (0x23, 1), // x: 8, y: 6, sw: 8, cs: 6, id: 66 + (0x68, 1), // x: 9, y: 6, sw: 2, cs:36, id: 70 + (0x06, 0), // x: 1, y: 7, sw: 1, cs: 7, id: 39 + (0x24, 0), // x: 2, y: 7, sw: 2, cs: 7, id: 43 + (0x42, 0), // x: 3, y: 7, sw: 3, cs: 7, id: 47 + (0x60, 0), // x: 4, y: 7, sw: 4, cs: 7, id: 51 + (0x7e, 0), // x: 5, y: 7, sw: 5, cs: 7, id: 55 + (0x9c, 0), // x: 6, y: 7, sw: 6, cs: 7, id: 59 + (0x06, 1), // x: 7, y: 7, sw: 7, cs: 7, id: 63 + (0x24, 1), // x: 8, y: 7, sw: 8, cs: 7, id: 67 + (0x69, 1), // x: 9, y: 7, sw: 2, cs:37, id: 71 + (0x07, 0), // x: 1, y: 8, sw: 1, cs: 8, id: 40 + (0x25, 0), // x: 2, y: 8, sw: 2, cs: 8, id: 44 + (0x43, 0), // x: 3, y: 8, sw: 3, cs: 8, id: 48 + (0x61, 0), // x: 4, y: 8, sw: 4, cs: 8, id: 52 + (0x7f, 0), // x: 5, y: 8, sw: 5, cs: 8, id: 56 + (0x9d, 0), // x: 6, y: 8, sw: 6, cs: 8, id: 60 + (0x07, 1), // x: 7, y: 8, sw: 7, cs: 8, id: 64 + (0x25, 1), // x: 8, y: 8, sw: 8, cs: 8, id: 68 + (0x6a, 1), // x: 9, y: 8, sw: 2, cs:38, id: 72 + (0x08, 0), // x: 1, y: 9, sw: 1, cs: 9, id: 73 + (0x26, 0), // x: 2, y: 9, sw: 2, cs: 9, id: 81 + (0x44, 0), // x: 3, y: 9, sw: 3, cs: 9, id: 89 + (0x62, 0), // x: 4, y: 9, sw: 4, cs: 9, id: 97 + (0x80, 0), // x: 5, y: 9, sw: 5, cs: 9, id:105 + (0x9e, 0), // x: 6, y: 9, sw: 6, cs: 9, id:113 + (0x08, 1), // x: 7, y: 9, sw: 7, cs: 9, id:121 + (0x26, 1), // x: 8, y: 9, sw: 8, cs: 9, id:129 + (0x6b, 1), // x: 9, y: 9, sw: 2, cs:39, id:137 + (0x09, 0), // x: 1, y:10, sw: 1, cs:10, id: 74 + (0x27, 0), // x: 2, y:10, sw: 2, cs:10, id: 82 + (0x45, 0), // x: 3, y:10, sw: 3, cs:10, id: 90 + (0x63, 0), // x: 4, y:10, sw: 4, cs:10, id: 98 + (0x81, 0), // x: 5, y:10, sw: 5, cs:10, id:106 + (0x9f, 0), // x: 6, y:10, sw: 6, cs:10, id:114 + (0x09, 1), // x: 7, y:10, sw: 7, cs:10, id:122 + (0x27, 1), // x: 8, y:10, sw: 8, cs:10, id:130 + (0x67, 1), // x: 9, y:10, sw: 2, cs:35, id:138 + (0x0a, 0), // x: 1, y:11, sw: 1, cs:11, id: 75 + (0x28, 0), // x: 2, y:11, sw: 2, cs:11, id: 83 + (0x46, 0), // x: 3, y:11, sw: 3, cs:11, id: 91 + (0x64, 0), // x: 4, y:11, sw: 4, cs:11, id: 99 + (0x82, 0), // x: 5, y:11, sw: 5, cs:11, id:107 + (0xa0, 0), // x: 6, y:11, sw: 6, cs:11, id:115 + (0x0a, 1), // x: 7, y:11, sw: 7, cs:11, id:123 + (0x28, 1), // x: 8, y:11, sw: 8, cs:11, id:131 + (0x71, 1), // x: 9, y:11, sw: 3, cs:36, id:139 + (0x0b, 0), // x: 1, y:12, sw: 1, cs:12, id: 76 + (0x29, 0), // x: 2, y:12, sw: 2, cs:12, id: 84 + (0x47, 0), // x: 3, y:12, sw: 3, cs:12, id: 92 + (0x65, 0), // x: 4, y:12, sw: 4, cs:12, id:100 + (0x83, 0), // x: 5, y:12, sw: 5, cs:12, id:108 + (0xa1, 0), // x: 6, y:12, sw: 6, cs:12, id:116 + (0x0b, 1), // x: 7, y:12, sw: 7, cs:12, id:124 + (0x29, 1), // x: 8, y:12, sw: 8, cs:12, id:132 + (0x72, 1), // x: 9, y:12, sw: 3, cs:37, id:140 + (0x0c, 0), // x: 1, y:13, sw: 1, cs:13, id: 77 + (0x2a, 0), // x: 2, y:13, sw: 2, cs:13, id: 85 + (0x48, 0), // x: 3, y:13, sw: 3, cs:13, id: 93 + (0x66, 0), // x: 4, y:13, sw: 4, cs:13, id:101 + (0x84, 0), // x: 5, y:13, sw: 5, cs:13, id:109 + (0xa2, 0), // x: 6, y:13, sw: 6, cs:13, id:117 + (0x0c, 1), // x: 7, y:13, sw: 7, cs:13, id:125 + (0x2a, 1), // x: 8, y:13, sw: 8, cs:13, id:133 + (0x73, 1), // x: 9, y:13, sw: 3, cs:38, id:141 + (0x0d, 0), // x: 1, y:14, sw: 1, cs:14, id: 78 + (0x2b, 0), // x: 2, y:14, sw: 2, cs:14, id: 86 + (0x49, 0), // x: 3, y:14, sw: 3, cs:14, id: 94 + (0x67, 0), // x: 4, y:14, sw: 4, cs:14, id:102 + (0x85, 0), // x: 5, y:14, sw: 5, cs:14, id:110 + (0xa3, 0), // x: 6, y:14, sw: 6, cs:14, id:118 + (0x0d, 1), // x: 7, y:14, sw: 7, cs:14, id:126 + (0x2b, 1), // x: 8, y:14, sw: 8, cs:14, id:134 + (0x70, 1), // x: 9, y:14, sw: 3, cs:35, id:142 + (0x0e, 0), // x: 1, y:15, sw: 1, cs:15, id: 79 + (0x2c, 0), // x: 2, y:15, sw: 2, cs:15, id: 87 + (0x4a, 0), // x: 3, y:15, sw: 3, cs:15, id: 95 + (0x68, 0), // x: 4, y:15, sw: 4, cs:15, id:103 + (0x86, 0), // x: 5, y:15, sw: 5, cs:15, id:111 + (0xa4, 0), // x: 6, y:15, sw: 6, cs:15, id:119 + (0x0e, 1), // x: 7, y:15, sw: 7, cs:15, id:127 + (0x2c, 1), // x: 8, y:15, sw: 8, cs:15, id:135 + (0x7a, 1), // x: 9, y:15, sw: 4, cs:36, id:143 + (0x0f, 0), // x: 1, y:16, sw: 1, cs:16, id: 80 + (0x2d, 0), // x: 2, y:16, sw: 2, cs:16, id: 88 + (0x4b, 0), // x: 3, y:16, sw: 3, cs:16, id: 96 + (0x69, 0), // x: 4, y:16, sw: 4, cs:16, id:104 + (0x87, 0), // x: 5, y:16, sw: 5, cs:16, id:112 + (0xa5, 0), // x: 6, y:16, sw: 6, cs:16, id:120 + (0x0f, 1), // x: 7, y:16, sw: 7, cs:16, id:128 + (0x2d, 1), // x: 8, y:16, sw: 8, cs:16, id:136 + (0x7b, 1), // x: 9, y:16, sw: 4, cs:37, id:144 + (0x10, 0), // x: 1, y:17, sw: 1, cs:17, id:145 + (0x2e, 0), // x: 2, y:17, sw: 2, cs:17, id:161 + (0x4c, 0), // x: 3, y:17, sw: 3, cs:17, id:177 + (0x6a, 0), // x: 4, y:17, sw: 4, cs:17, id:193 + (0x88, 0), // x: 5, y:17, sw: 5, cs:17, id:209 + (0xa6, 0), // x: 6, y:17, sw: 6, cs:17, id:225 + (0x10, 1), // x: 7, y:17, sw: 7, cs:17, id:241 + (0x2e, 1), // x: 8, y:17, sw: 8, cs:17, id:257 + (0x7c, 1), // x: 9, y:17, sw: 4, cs:38, id:273 + (0x11, 0), // x: 1, y:18, sw: 1, cs:18, id:146 + (0x2f, 0), // x: 2, y:18, sw: 2, cs:18, id:162 + (0x4d, 0), // x: 3, y:18, sw: 3, cs:18, id:178 + (0x6b, 0), // x: 4, y:18, sw: 4, cs:18, id:194 + (0x89, 0), // x: 5, y:18, sw: 5, cs:18, id:210 + (0xa7, 0), // x: 6, y:18, sw: 6, cs:18, id:226 + (0x11, 1), // x: 7, y:18, sw: 7, cs:18, id:242 + (0x2f, 1), // x: 8, y:18, sw: 8, cs:18, id:258 + (0x79, 1), // x: 9, y:18, sw: 4, cs:35, id:274 + (0x12, 0), // x: 1, y:19, sw: 1, cs:19, id:147 + (0x30, 0), // x: 2, y:19, sw: 2, cs:19, id:163 + (0x4e, 0), // x: 3, y:19, sw: 3, cs:19, id:179 + (0x6c, 0), // x: 4, y:19, sw: 4, cs:19, id:195 + (0x8a, 0), // x: 5, y:19, sw: 5, cs:19, id:211 + (0xa8, 0), // x: 6, y:19, sw: 6, cs:19, id:227 + (0x12, 1), // x: 7, y:19, sw: 7, cs:19, id:243 + (0x30, 1), // x: 8, y:19, sw: 8, cs:19, id:259 + (0x83, 1), // x: 9, y:19, sw: 5, cs:36, id:275 + (0x13, 0), // x: 1, y:20, sw: 1, cs:20, id:148 + (0x31, 0), // x: 2, y:20, sw: 2, cs:20, id:164 + (0x4f, 0), // x: 3, y:20, sw: 3, cs:20, id:180 + (0x6d, 0), // x: 4, y:20, sw: 4, cs:20, id:196 + (0x8b, 0), // x: 5, y:20, sw: 5, cs:20, id:212 + (0xa9, 0), // x: 6, y:20, sw: 6, cs:20, id:228 + (0x13, 1), // x: 7, y:20, sw: 7, cs:20, id:244 + (0x31, 1), // x: 8, y:20, sw: 8, cs:20, id:260 + (0x84, 1), // x: 9, y:20, sw: 5, cs:37, id:276 + (0x14, 0), // x: 1, y:21, sw: 1, cs:21, id:149 + (0x32, 0), // x: 2, y:21, sw: 2, cs:21, id:165 + (0x50, 0), // x: 3, y:21, sw: 3, cs:21, id:181 + (0x6e, 0), // x: 4, y:21, sw: 4, cs:21, id:197 + (0x8c, 0), // x: 5, y:21, sw: 5, cs:21, id:213 + (0xaa, 0), // x: 6, y:21, sw: 6, cs:21, id:229 + (0x14, 1), // x: 7, y:21, sw: 7, cs:21, id:245 + (0x32, 1), // x: 8, y:21, sw: 8, cs:21, id:261 + (0x85, 1), // x: 9, y:21, sw: 5, cs:38, id:277 + (0x15, 0), // x: 1, y:22, sw: 1, cs:22, id:150 + (0x33, 0), // x: 2, y:22, sw: 2, cs:22, id:166 + (0x51, 0), // x: 3, y:22, sw: 3, cs:22, id:182 + (0x6f, 0), // x: 4, y:22, sw: 4, cs:22, id:198 + (0x8d, 0), // x: 5, y:22, sw: 5, cs:22, id:214 + (0xab, 0), // x: 6, y:22, sw: 6, cs:22, id:230 + (0x15, 1), // x: 7, y:22, sw: 7, cs:22, id:246 + (0x33, 1), // x: 8, y:22, sw: 8, cs:22, id:262 + (0x82, 1), // x: 9, y:22, sw: 5, cs:35, id:278 + (0x16, 0), // x: 1, y:23, sw: 1, cs:23, id:151 + (0x34, 0), // x: 2, y:23, sw: 2, cs:23, id:167 + (0x52, 0), // x: 3, y:23, sw: 3, cs:23, id:183 + (0x70, 0), // x: 4, y:23, sw: 4, cs:23, id:199 + (0x8e, 0), // x: 5, y:23, sw: 5, cs:23, id:215 + (0xac, 0), // x: 6, y:23, sw: 6, cs:23, id:231 + (0x16, 1), // x: 7, y:23, sw: 7, cs:23, id:247 + (0x34, 1), // x: 8, y:23, sw: 8, cs:23, id:263 + (0x8c, 1), // x: 9, y:23, sw: 6, cs:36, id:279 + (0x17, 0), // x: 1, y:24, sw: 1, cs:24, id:152 + (0x35, 0), // x: 2, y:24, sw: 2, cs:24, id:168 + (0x53, 0), // x: 3, y:24, sw: 3, cs:24, id:184 + (0x71, 0), // x: 4, y:24, sw: 4, cs:24, id:200 + (0x8f, 0), // x: 5, y:24, sw: 5, cs:24, id:216 + (0xad, 0), // x: 6, y:24, sw: 6, cs:24, id:232 + (0x17, 1), // x: 7, y:24, sw: 7, cs:24, id:248 + (0x35, 1), // x: 8, y:24, sw: 8, cs:24, id:264 + (0x8d, 1), // x: 9, y:24, sw: 6, cs:37, id:280 + (0x18, 0), // x: 1, y:25, sw: 1, cs:25, id:153 + (0x36, 0), // x: 2, y:25, sw: 2, cs:25, id:169 + (0x54, 0), // x: 3, y:25, sw: 3, cs:25, id:185 + (0x72, 0), // x: 4, y:25, sw: 4, cs:25, id:201 + (0x90, 0), // x: 5, y:25, sw: 5, cs:25, id:217 + (0xae, 0), // x: 6, y:25, sw: 6, cs:25, id:233 + (0x18, 1), // x: 7, y:25, sw: 7, cs:25, id:249 + (0x36, 1), // x: 8, y:25, sw: 8, cs:25, id:265 + (0x8e, 1), // x: 9, y:25, sw: 6, cs:38, id:281 + (0x19, 0), // x: 1, y:26, sw: 1, cs:26, id:154 + (0x37, 0), // x: 2, y:26, sw: 2, cs:26, id:170 + (0x55, 0), // x: 3, y:26, sw: 3, cs:26, id:186 + (0x73, 0), // x: 4, y:26, sw: 4, cs:26, id:202 + (0x91, 0), // x: 5, y:26, sw: 5, cs:26, id:218 + (0xaf, 0), // x: 6, y:26, sw: 6, cs:26, id:234 + (0x19, 1), // x: 7, y:26, sw: 7, cs:26, id:250 + (0x37, 1), // x: 8, y:26, sw: 8, cs:26, id:266 + (0x8b, 1), // x: 9, y:26, sw: 6, cs:35, id:282 + (0x1a, 0), // x: 1, y:27, sw: 1, cs:27, id:155 + (0x38, 0), // x: 2, y:27, sw: 2, cs:27, id:171 + (0x56, 0), // x: 3, y:27, sw: 3, cs:27, id:187 + (0x74, 0), // x: 4, y:27, sw: 4, cs:27, id:203 + (0x92, 0), // x: 5, y:27, sw: 5, cs:27, id:219 + (0xb0, 0), // x: 6, y:27, sw: 6, cs:27, id:235 + (0x1a, 1), // x: 7, y:27, sw: 7, cs:27, id:251 + (0x38, 1), // x: 8, y:27, sw: 8, cs:27, id:267 + (0x95, 1), // x: 9, y:27, sw: 7, cs:36, id:283 + (0x1b, 0), // x: 1, y:28, sw: 1, cs:28, id:156 + (0x39, 0), // x: 2, y:28, sw: 2, cs:28, id:172 + (0x57, 0), // x: 3, y:28, sw: 3, cs:28, id:188 + (0x75, 0), // x: 4, y:28, sw: 4, cs:28, id:204 + (0x93, 0), // x: 5, y:28, sw: 5, cs:28, id:220 + (0xb1, 0), // x: 6, y:28, sw: 6, cs:28, id:236 + (0x1b, 1), // x: 7, y:28, sw: 7, cs:28, id:252 + (0x39, 1), // x: 8, y:28, sw: 8, cs:28, id:268 + (0x96, 1), // x: 9, y:28, sw: 7, cs:37, id:284 + (0x1c, 0), // x: 1, y:29, sw: 1, cs:29, id:157 + (0x3a, 0), // x: 2, y:29, sw: 2, cs:29, id:173 + (0x58, 0), // x: 3, y:29, sw: 3, cs:29, id:189 + (0x76, 0), // x: 4, y:29, sw: 4, cs:29, id:205 + (0x94, 0), // x: 5, y:29, sw: 5, cs:29, id:221 + (0xb2, 0), // x: 6, y:29, sw: 6, cs:29, id:237 + (0x1c, 1), // x: 7, y:29, sw: 7, cs:29, id:253 + (0x3a, 1), // x: 8, y:29, sw: 8, cs:29, id:269 + (0x97, 1), // x: 9, y:29, sw: 7, cs:38, id:285 + (0x1d, 0), // x: 1, y:30, sw: 1, cs:30, id:158 + (0x3b, 0), // x: 2, y:30, sw: 2, cs:30, id:174 + (0x59, 0), // x: 3, y:30, sw: 3, cs:30, id:190 + (0x77, 0), // x: 4, y:30, sw: 4, cs:30, id:206 + (0x95, 0), // x: 5, y:30, sw: 5, cs:30, id:222 + (0xb3, 0), // x: 6, y:30, sw: 6, cs:30, id:238 + (0x1d, 1), // x: 7, y:30, sw: 7, cs:30, id:254 + (0x3b, 1), // x: 8, y:30, sw: 8, cs:30, id:270 + (0x94, 1), // x: 9, y:30, sw: 7, cs:35, id:286 + (0x5a, 1), // x: 1, y:31, sw: 1, cs:31, id:159 + (0x63, 1), // x: 2, y:31, sw: 2, cs:31, id:175 + (0x6c, 1), // x: 3, y:31, sw: 3, cs:31, id:191 + (0x75, 1), // x: 4, y:31, sw: 4, cs:31, id:207 + (0x7e, 1), // x: 5, y:31, sw: 5, cs:31, id:223 + (0x87, 1), // x: 6, y:31, sw: 6, cs:31, id:239 + (0x90, 1), // x: 7, y:31, sw: 7, cs:31, id:255 + (0x99, 1), // x: 8, y:31, sw: 8, cs:31, id:271 + (0x9e, 1), // x: 9, y:31, sw: 8, cs:36, id:287 + (0x5b, 1), // x: 1, y:32, sw: 1, cs:32, id:160 + (0x64, 1), // x: 2, y:32, sw: 2, cs:32, id:176 + (0x6d, 1), // x: 3, y:32, sw: 3, cs:32, id:192 + (0x76, 1), // x: 4, y:32, sw: 4, cs:32, id:208 + (0x7f, 1), // x: 5, y:32, sw: 5, cs:32, id:224 + (0x88, 1), // x: 6, y:32, sw: 6, cs:32, id:240 + (0x91, 1), // x: 7, y:32, sw: 7, cs:32, id:256 + (0x9a, 1), // x: 8, y:32, sw: 8, cs:32, id:272 + (0x9f, 1), // x: 9, y:32, sw: 8, cs:37, id:288 + (0x5c, 1), // x: 1, y:33, sw: 1, cs:33, id:289 + (0x65, 1), // x: 2, y:33, sw: 2, cs:33, id:290 + (0x6e, 1), // x: 3, y:33, sw: 3, cs:33, id:291 + (0x77, 1), // x: 4, y:33, sw: 4, cs:33, id:292 + (0x80, 1), // x: 5, y:33, sw: 5, cs:33, id:293 + (0x89, 1), // x: 6, y:33, sw: 6, cs:33, id:294 + (0x92, 1), // x: 7, y:33, sw: 7, cs:33, id:295 + (0x9b, 1), // x: 8, y:33, sw: 8, cs:33, id:296 + (0xa0, 1), // x: 9, y:33, sw: 8, cs:38, id:297 + (0x5d, 1), // x: 1, y:34, sw: 1, cs:34, id:298 + (0x66, 1), // x: 2, y:34, sw: 2, cs:34, id:299 + (0x6f, 1), // x: 3, y:34, sw: 3, cs:34, id:300 + (0x78, 1), // x: 4, y:34, sw: 4, cs:34, id:301 + (0x81, 1), // x: 5, y:34, sw: 5, cs:34, id:302 + (0x8a, 1), // x: 6, y:34, sw: 6, cs:34, id:303 + (0x93, 1), // x: 7, y:34, sw: 7, cs:34, id:304 + (0x9c, 1), // x: 8, y:34, sw: 8, cs:34, id:305 + (0x9d, 1), // x: 9, y:34, sw: 8, cs:35, id:306 + ]; + let index: usize = (x as usize) + (y as usize) * 9; + if index < lookup.len() { + lookup[index] + } else { + (0x00, 0) + } +}; + +pub struct LedMatrix { + pub device: IS31FL3743, +} + +impl LedMatrix +where + I2C: Write, + I2C: Read, + I2cError: core::fmt::Debug, +{ + pub fn unwrap(self) -> I2C { + self.device.i2c + } + + // TODO: Maybe make this private and set it once in the constructor + pub fn set_scaling(&mut self, scale: u8) -> Result<(), I2cError> { + self.device.set_scaling(scale) + } + + pub fn set_address(&mut self, address: u8) { + self.device.address = address; + } + + pub fn new(i2c: I2C, calc_pixel: fn(x: u8, y: u8) -> u8) -> LedMatrix { + LedMatrix { + device: IS31FL3743 { + i2c, + address: 0b0100000, + width: 9, + height: 34, + calc_pixel, + }, + } + } + + pub fn setup>(&mut self, delay: &mut DEL) -> Result<(), Error> { + self.device.setup(delay)?; + Ok(()) + } + + pub fn fill_brightness(&mut self, brightness: u8) -> Result<(), Error> { + for x in 0..self.device.width { + for y in 0..self.device.height { + self.device.pixel(x, y, brightness)?; + } + } + Ok(()) + } +}