diff --git a/Pedestal/MCDU/README.md b/Pedestal/MCDU/README.md new file mode 100644 index 0000000..b0f0389 --- /dev/null +++ b/Pedestal/MCDU/README.md @@ -0,0 +1,10 @@ +# USB HID +## Output +One byte output, they match to indications as follows +* 0x01: FM +* 0x02: FAIL +* 0x04: FM2 +* 0x08: RDY +* 0x0F: MENU +* 0x10: IND +* 0x20: FM1 diff --git a/Pedestal/MCDU/firmware/.cargo/config b/Pedestal/MCDU/firmware/.cargo/config new file mode 100644 index 0000000..af1f29b --- /dev/null +++ b/Pedestal/MCDU/firmware/.cargo/config @@ -0,0 +1,11 @@ +[target.thumbv7m-none-eabi] +runner = 'arm-none-eabi-gdb' +rustflags = [ + "-C", "link-arg=-Tlink.x", +] + +[build] +target = "thumbv7m-none-eabi" + +[unstable] +build-std = ["core", "alloc"] diff --git a/Pedestal/MCDU/firmware/.gdbinit b/Pedestal/MCDU/firmware/.gdbinit new file mode 100644 index 0000000..098ff6f --- /dev/null +++ b/Pedestal/MCDU/firmware/.gdbinit @@ -0,0 +1,18 @@ +target remote :3333 + +monitor arm semihosting enable + +# # send captured ITM to the file itm.fifo +# # (the microcontroller SWO pin must be connected to the programmer SWO pin) +# # 8000000 must match the core clock frequency +# monitor tpiu config internal itm.fifo uart off 8000000 + +# # OR: make the microcontroller SWO pin output compatible with UART (8N1) +# # 2000000 is the frequency of the SWO pin +# monitor tpiu config external uart off 8000000 2000000 + +# # enable ITM port 0 +# monitor itm port 0 on + +load +step diff --git a/Pedestal/MCDU/firmware/Cargo.lock b/Pedestal/MCDU/firmware/Cargo.lock new file mode 100644 index 0000000..c97ec9e --- /dev/null +++ b/Pedestal/MCDU/firmware/Cargo.lock @@ -0,0 +1,526 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bare-metal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[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 = "bxcan" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ac3d0c0a542d0ab5521211f873f62706a7136df415676f676d347e5a41dd80" +dependencies = [ + "bitflags", + "embedded-hal", + "nb 1.1.0", + "vcell", +] + +[[package]] +name = "bytemuck" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.55", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal 0.2.5", + "bitfield", + "embedded-hal", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cortex-m-semihosting" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c23234600452033cc77e4b761e740e02d2c4168e11dbf36ab14a0f58973592b0" +dependencies = [ + "cortex-m", +] + +[[package]] +name = "embedded-dma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "firmware" +version = "0.1.0" +dependencies = [ + "bytemuck", + "cortex-m", + "cortex-m-rt", + "embedded-hal", + "fugit", + "nb 1.1.0", + "packed_struct", + "panic-halt", + "panic-semihosting", + "ryu", + "stm32f1xx-hal", + "usb-device", + "usbd-human-interface-device", + "usbd-serial", +] + +[[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.55", +] + +[[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.55", +] + +[[package]] +name = "fugit" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" +dependencies = [ + "gcd", +] + +[[package]] +name = "fugit-timer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9607bfc4c388f9d629704f56ede4a007546cad417b3bcd6fc7c87dc7edce04a" +dependencies = [ + "fugit", + "nb 1.1.0", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive", +] + +[[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.55", +] + +[[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 = "panic-halt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" + +[[package]] +name = "panic-semihosting" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8a3e1233d9073d76a870223512ce4eeea43c067a94a445c13bd6d792d7b1ab" +dependencies = [ + "cortex-m", + "cortex-m-semihosting", +] + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stm32-usbd" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c94998f166d66b210a164648a0b7866428d8f1e0740bf8a4c5edd89d4750c1" +dependencies = [ + "cortex-m", + "usb-device", + "vcell", +] + +[[package]] +name = "stm32f1" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dc80735831c28fe85384e1e28428fb6d201f67c696e369a239ed9c5eba369d" +dependencies = [ + "bare-metal 1.0.0", + "cortex-m", + "cortex-m-rt", + "vcell", +] + +[[package]] +name = "stm32f1xx-hal" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30845662b9ce46a2ec04da97666a2b32458bee5032bb0452d0caf1536a96a542" +dependencies = [ + "bitflags", + "bxcan", + "cortex-m", + "cortex-m-rt", + "embedded-dma", + "embedded-hal", + "fugit", + "fugit-timer", + "nb 1.1.0", + "stm32-usbd", + "stm32f1", + "void", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "usb-device" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508" + +[[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", + "num_enum", + "option-block", + "packed_struct", + "usb-device", +] + +[[package]] +name = "usbd-serial" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db75519b86287f12dcf0d171c7cf4ecc839149fe9f3b720ac4cfce52959e1dfe" +dependencies = [ + "embedded-hal", + "nb 0.1.3", + "usb-device", +] + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] diff --git a/Pedestal/MCDU/firmware/Cargo.toml b/Pedestal/MCDU/firmware/Cargo.toml new file mode 100644 index 0000000..95c6bbb --- /dev/null +++ b/Pedestal/MCDU/firmware/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "firmware" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +embedded-hal = "0.2.7" +nb = "1" +cortex-m = "0.7.6" +cortex-m-rt = "0.7.1" +# Panic behaviour, see https://crates.io/keywords/panic-impl for alternatives +panic-halt = "0.2.0" +#stm32-usbd = "0.6.0" +usb-device = "0.2.9" +usbd-serial = "0.1.1" +ryu = "1.0.16" +panic-semihosting = "0.6.0" +usbd-human-interface-device = "0.4.5" +packed_struct = { version = "0.10.1", default-features = false } +fugit = "0.3.7" +bytemuck = { version = "1.15.0", features = ["derive"] } + +[dependencies.stm32f1xx-hal] +version = "0.10.0" +features = ["rt", "stm32f103"] + +[profile.dev] +codegen-units = 1 +opt-level = "z" + +[profile.dev.package."*"] +codegen-units = 1 +opt-level = "z" + +[profile.release] +# or "z" +opt-level = "z" diff --git a/Pedestal/MCDU/firmware/descriptor.wara b/Pedestal/MCDU/firmware/descriptor.wara new file mode 100644 index 0000000..ac7ecb2 --- /dev/null +++ b/Pedestal/MCDU/firmware/descriptor.wara @@ -0,0 +1,14 @@ +[[applicationCollection]] +usage = ['Generic Desktop', 'Joystick'] + +[[applicationCollection.inputReport]] + +[[applicationCollection.inputReport.variableItem]] +usageRange = ['Button', 'Button 1', 'Button 96'] +logicalValueRange = [0, 1] + +[[applicationCollection.outputReport]] + +[[applicationCollection.outputReport.variableItem]] +usage = ['Haptics', 'Manual Trigger'] +logicalValueRange = [0, 127] diff --git a/Pedestal/MCDU/firmware/hidtools.sh b/Pedestal/MCDU/firmware/hidtools.sh new file mode 100755 index 0000000..12d02cc --- /dev/null +++ b/Pedestal/MCDU/firmware/hidtools.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +nix run github:feathecutie/hidtools -- -s descriptor.wara +cat descriptor.h |grep " 0x" diff --git a/Pedestal/MCDU/firmware/memory.x b/Pedestal/MCDU/firmware/memory.x new file mode 100644 index 0000000..eea8b4f --- /dev/null +++ b/Pedestal/MCDU/firmware/memory.x @@ -0,0 +1,7 @@ +/* Linker script for the STM32F103C8T6 */ +MEMORY +{ + /* We only use 63KiB so we have 1KiB free for calibration data */ + FLASH : ORIGIN = 0x08000000, LENGTH = 63K + RAM : ORIGIN = 0x20000000, LENGTH = 20K +} diff --git a/Pedestal/MCDU/firmware/openocd.cfg b/Pedestal/MCDU/firmware/openocd.cfg new file mode 100644 index 0000000..0f1a070 --- /dev/null +++ b/Pedestal/MCDU/firmware/openocd.cfg @@ -0,0 +1,12 @@ +# Sample OpenOCD configuration for the STM32F3DISCOVERY development board + +# Depending on the hardware revision you got you'll have to pick ONE of these +# interfaces. At any time only one interface should be commented out. + +# Revision C (newer revision) +source [find interface/jlink.cfg] + +# Revision A and B (older revisions) +# source [find interface/stlink-v2.cfg] + +source [find target/stm32f1x.cfg] diff --git a/Pedestal/MCDU/firmware/openocd.sh b/Pedestal/MCDU/firmware/openocd.sh new file mode 100755 index 0000000..d312b75 --- /dev/null +++ b/Pedestal/MCDU/firmware/openocd.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +openocd -f interface/jlink.cfg -f ./swd.cfg -f target/stm32f1x.cfg diff --git a/Pedestal/MCDU/firmware/src/device.rs b/Pedestal/MCDU/firmware/src/device.rs new file mode 100644 index 0000000..7c32924 --- /dev/null +++ b/Pedestal/MCDU/firmware/src/device.rs @@ -0,0 +1,134 @@ +use core::default::Default; +use fugit::ExtU32; +use packed_struct::prelude::*; +use usb_device::bus::UsbBus; +use usb_device::class_prelude::UsbBusAllocator; +use usbd_human_interface_device::{ + descriptor::InterfaceProtocol, + device::DeviceClass, + interface::{ + InBytes64, Interface, InterfaceBuilder, InterfaceConfig, OutBytes64, ReportSingle, + UsbAllocatable, + }, + UsbHidError, +}; + +// Generated using Waratah +#[rustfmt::skip] +pub const CUSTOM_DESCRIPTOR: &[u8] = &[ + 0x05, 0x01, // UsagePage(Generic Desktop[0x0001]) + 0x09, 0x04, // UsageId(Joystick[0x0004]) + 0xA1, 0x01, // Collection(Application) + 0x85, 0x01, // ReportId(1) + 0x05, 0x09, // UsagePage(Button[0x0009]) + 0x19, 0x01, // UsageIdMin(Button 1[0x0001]) + 0x29, 0x60, // UsageIdMax(Button 96[0x0060]) + 0x15, 0x00, // LogicalMinimum(0) + 0x25, 0x01, // LogicalMaximum(1) + 0x95, 0x60, // ReportCount(96) + 0x75, 0x01, // ReportSize(1) + 0x81, 0x02, // Input(Data, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, BitField) + 0x05, 0x0E, // UsagePage(Haptics[0x000E]) + 0x09, 0x21, // UsageId(Manual Trigger[0x0021]) + 0x25, 0x7F, // LogicalMaximum(127) + 0x95, 0x01, // ReportCount(1) + 0x75, 0x07, // ReportSize(7) + 0x91, 0x02, // Output(Data, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, NonVolatile, BitField) + 0x75, 0x01, // ReportSize(1) + 0x91, 0x03, // Output(Constant, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, NonVolatile, BitField) + 0xC0, // EndCollection() +]; + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Default, PackedStruct)] +#[packed_struct(endian = "lsb", size_bytes = "13")] // MUST be <= 64 else we get problem cause InBytes64 or OutBytes64 +pub struct CustomInputReport { + #[packed_field] + pub report_id: u8, + #[packed_field] + pub buttons1: u64, + #[packed_field] + pub buttons2: u32, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Default, PackedStruct)] +#[packed_struct(endian = "lsb", size_bytes = "2")] +pub struct CustomOutputReport { + #[packed_field] + pub report_id: u8, + #[packed_field] + pub leds: u8, +} + +pub struct CustomDevice<'a, B: UsbBus> { + interface: Interface<'a, B, InBytes64, OutBytes64, ReportSingle>, +} + +impl<'a, B: UsbBus> CustomDevice<'a, B> { + pub fn write_report(&mut self, report: &CustomInputReport) -> Result<(), UsbHidError> { + let data = report.pack().map_err(|_| UsbHidError::SerializationError)?; + self.interface + .write_report(&data) + .map(|_| ()) + .map_err(UsbHidError::from) + } + + pub fn read_report(&mut self) -> Result { + let mut data = [0; 2]; + self.interface + .read_report(&mut data[..]) + .map(|_| CustomOutputReport::unpack(&data).unwrap()) + .map_err(UsbHidError::from) + } +} + +impl<'a, B: UsbBus> DeviceClass<'a> for CustomDevice<'a, B> { + type I = Interface<'a, B, InBytes64, OutBytes64, ReportSingle>; + + fn interface(&mut self) -> &mut Self::I { + &mut self.interface + } + + fn reset(&mut self) {} + + fn tick(&mut self) -> Result<(), UsbHidError> { + Ok(()) + } +} + +pub struct CustomConfig<'a> { + interface: InterfaceConfig<'a, InBytes64, OutBytes64, ReportSingle>, +} + +impl<'a> Default for CustomConfig<'a> { + #[must_use] + fn default() -> Self { + Self::new( + InterfaceBuilder::new(CUSTOM_DESCRIPTOR) + .unwrap() + .boot_device(InterfaceProtocol::None) + .description(" CustomDevice") + .in_endpoint(10.millis()) + .unwrap() + .with_out_endpoint(10.millis()) + .unwrap() + .build(), + ) + } +} + +impl<'a> CustomConfig<'a> { + #[must_use] + pub fn new(interface: InterfaceConfig<'a, InBytes64, OutBytes64, ReportSingle>) -> Self { + Self { interface } + } +} + +impl<'a, B: UsbBus + 'a> UsbAllocatable<'a, B> for CustomConfig<'a> { + type Allocated = CustomDevice<'a, B>; + + fn allocate(self, usb_alloc: &'a UsbBusAllocator) -> Self::Allocated { + Self::Allocated { + interface: Interface::new(usb_alloc, self.interface), + } + } +} diff --git a/Pedestal/MCDU/firmware/src/main.rs b/Pedestal/MCDU/firmware/src/main.rs new file mode 100644 index 0000000..15401bc --- /dev/null +++ b/Pedestal/MCDU/firmware/src/main.rs @@ -0,0 +1,246 @@ +#![deny(unsafe_code)] +#![no_main] +#![no_std] + +// Silence certain clippy warnings +#![allow(non_upper_case_globals)] +#![allow(clippy::needless_late_init)] +#![allow(clippy::needless_return)] + +mod device; + +use panic_halt as _; + +use cortex_m::asm::delay; +use cortex_m_rt::entry; +use stm32f1xx_hal::{ + gpio::{Pin, Input, PullDown, Output, PushPull}, + pac, + prelude::*, +// timer::{Channel}, + usb::{Peripheral, UsbBus}, +}; + +use usb_device::prelude::*; +use usbd_human_interface_device::prelude::*; + +use crate::device::{CustomConfig, CustomInputReport}; + +// TODO compiler throws error when initializing pa15 and pb4 +struct MyPins { + // Input Col select + pa4: Pin<'A', 4, Output>, + pa5: Pin<'A', 5, Output>, +// pa15: Pin<'A', 15, Output>, + pb6: Pin<'B', 6, Output>, + pb7: Pin<'B', 7, Output>, + pb15: Pin<'B', 15, Output>, + pc0: Pin<'C', 0, Output>, + pc1: Pin<'C', 1, Output>, + pc2: Pin<'C', 2, Output>, + pc3: Pin<'C', 3, Output>, + pc9: Pin<'C', 9, Output>, + + // Input read row + pa0: Pin<'A', 0, Input>, + pa1: Pin<'A', 1, Input>, + pa2: Pin<'A', 2, Input>, + pa8: Pin<'A', 8, Input>, +// pb4: Pin<'B', 4, Input>, + pb5: Pin<'B', 5, Input>, + pb12: Pin<'B', 12, Input>, + pb13: Pin<'B', 13, Input>, + pb14: Pin<'B', 14, Input>, + pc6: Pin<'C', 6, Input>, + pc7: Pin<'C', 7, Input>, + pc8: Pin<'C', 8, Input>, + pc10: Pin<'C', 10, Input>, + pc11: Pin<'C', 11, Input>, + pc15: Pin<'C', 15, Input>, + + // Outputs + pa9: Pin<'A', 9, Output>, + pa10: Pin<'A', 10, Output>, + pb8: Pin<'B', 8, Output>, + pb9: Pin<'B', 9, Output>, + pc12: Pin<'C', 12, Output>, + pc13: Pin<'C', 13, Output>, + pc14: Pin<'C', 14, Output>, +} + +#[entry] +fn main() -> ! { + // ====================== general setup ================= + // Acquire peripherals + let p = pac::Peripherals::take().unwrap(); + let mut flash = p.FLASH.constrain(); + let rcc = p.RCC.constrain(); + + // Setup GPIOA + let mut gpioa = p.GPIOA.split(); + let mut gpiob = p.GPIOB.split(); + let mut gpioc = p.GPIOC.split(); + + // configure clock + let clocks = rcc + .cfgr + .use_hse(16.MHz()) + .sysclk(48.MHz()) + .pclk1(24.MHz()) + .freeze(&mut flash.acr); + + // ====================== USB setup ================= + assert!(clocks.usbclk_valid()); + let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh); + usb_dp.set_low(); + delay(clocks.sysclk().raw() / 100); + + let usb = Peripheral { + usb: p.USB, + pin_dm: gpioa.pa11, + pin_dp: usb_dp.into_floating_input(&mut gpioa.crh), + }; + let usb_bus = UsbBus::new(usb); + + let mut consumer = UsbHidClassBuilder::new() + .add_device(CustomConfig::default()) + .build(&usb_bus); + + + let mut usb_dev = if cfg!(FO) { + UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27db)) + .manufacturer("FLC Meow") + .product("MCDU First Officer") + .serial_number("01189998819991197252") + .build() + } else if cfg!(third) { + UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dc)) + .manufacturer("FLC Meow") + .product("MCDU 3rd Occupant") + .serial_number("01189998819991197251") + .build() + } else { + UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27da)) + .manufacturer("FLC Meow") + .product("MCDU Captain") + .serial_number("01189998819991197253") + .build() + }; + + // ====================== Pin setup ================= + let mut input_pins = MyPins { + // Inputs Col Select + pa4: gpioa.pa4.into_push_pull_output(&mut gpioa.crl), + pa5: gpioa.pa5.into_push_pull_output(&mut gpioa.crl), +// pa15: gpioa.pa15.into_push_pull_output(&mut gpioa.crh), + pb6: gpiob.pb6.into_push_pull_output(&mut gpiob.crl), + pb7: gpiob.pb7.into_push_pull_output(&mut gpiob.crl), + pb15: gpiob.pb15.into_push_pull_output(&mut gpiob.crh), + pc0: gpioc.pc0.into_push_pull_output(&mut gpioc.crl), + pc1: gpioc.pc1.into_push_pull_output(&mut gpioc.crl), + pc2: gpioc.pc2.into_push_pull_output(&mut gpioc.crl), + pc3: gpioc.pc3.into_push_pull_output(&mut gpioc.crl), + pc9: gpioc.pc9.into_push_pull_output(&mut gpioc.crh), + + // Inputs Row Read + pa0: gpioa.pa0.into_pull_down_input(&mut gpioa.crl), + pa1: gpioa.pa1.into_pull_down_input(&mut gpioa.crl), + pa2: gpioa.pa2.into_pull_down_input(&mut gpioa.crl), + pa8: gpioa.pa8.into_pull_down_input(&mut gpioa.crh), +// pb4: gpiob.pb4.into_pull_down_input(&mut gpiob.crl), + pb5: gpiob.pb5.into_pull_down_input(&mut gpiob.crl), + pb12: gpiob.pb12.into_pull_down_input(&mut gpiob.crh), + pb13: gpiob.pb13.into_pull_down_input(&mut gpiob.crh), + pb14: gpiob.pb14.into_pull_down_input(&mut gpiob.crh), + pc6: gpioc.pc6.into_pull_down_input(&mut gpioc.crl), + pc7: gpioc.pc7.into_pull_down_input(&mut gpioc.crl), + pc8: gpioc.pc8.into_pull_down_input(&mut gpioc.crh), + pc10: gpioc.pc10.into_pull_down_input(&mut gpioc.crh), + pc11: gpioc.pc11.into_pull_down_input(&mut gpioc.crh), + pc15: gpioc.pc15.into_pull_down_input(&mut gpioc.crh), + + // Outputs + pa9: gpioa.pa9.into_push_pull_output(&mut gpioa.crh), // FM + pa10: gpioa.pa10.into_push_pull_output(&mut gpioa.crh), // FAIL + pb8: gpiob.pb8.into_push_pull_output(&mut gpiob.crh), // FM2 + pb9: gpiob.pb9.into_push_pull_output(&mut gpiob.crh), // RDY + pc12: gpioc.pc12.into_push_pull_output(&mut gpioc.crh), // MENU + pc13: gpioc.pc13.into_push_pull_output(&mut gpioc.crh), // IND + pc14: gpioc.pc14.into_push_pull_output(&mut gpioc.crh), // FM1 + }; + +// let mut last = get_report(&mut input_pins); + + // ====================== Timer setup =============== +// let timer = Instant; +// let mut last_report_sent = timer.elapsed(); + + // ====================== Main loop ================= + loop { + let report = get_report(&mut input_pins); +// TODO figure out timer and only send in like 1ms intervals or on change +// if report != last { + match consumer.device().write_report(&report) { + Err(UsbHidError::WouldBlock) => {} + Err(UsbHidError::UsbError(usb_device::UsbError::BufferOverflow)) => { + core::panic!("Failed to write consumer report, report is too big") + } + Ok(_) => { +// last = report; + } + Err(e) => { + input_pins.pa10.set_high(); // set as indicator that this has happened + core::panic!("Failed to write consumer report: {:?}", e) + } + } +// } + + if usb_dev.poll(&mut [&mut consumer]) { + match consumer.device().read_report() { + Err(UsbHidError::WouldBlock) => {} + Ok(output) => { + // LED outputs + // FM + if output.leds & 0x1 == 0x1 { + input_pins.pa9.set_high(); + } + else { + input_pins.pa9.set_low(); + } + // TODO rest of LEDs + } + Err(e) => { + core::panic!("Failed to write consumer report: {:?}", e) + } + } + } + + } +} + +// Returns a CustomInputReport from the inputs given +fn get_report(pins: &mut MyPins) -> CustomInputReport { + // TODO keyboard matrix + let mut buttons1: u64 = 0; + let mut buttons2: u32 = 0; + + // Buttons +// if pins.pb0.is_high() { +// buttons += 0x01; +// } +// if pins.pc1.is_high() { +// buttons1 += 0x02; +// } +// if pins.pc3.is_high() { +// buttons += 0x04; +// } + if pins.pc15.is_high() { + buttons1 += 0x08; + } + + CustomInputReport { + report_id: 1, + buttons1, + buttons2, + } +} diff --git a/Pedestal/MCDU/firmware/swd.cfg b/Pedestal/MCDU/firmware/swd.cfg new file mode 100644 index 0000000..b263065 --- /dev/null +++ b/Pedestal/MCDU/firmware/swd.cfg @@ -0,0 +1 @@ +transport select swd