diff --git a/.woodpecker.yml b/.woodpecker.yml deleted file mode 100644 index 943053b..0000000 --- a/.woodpecker.yml +++ /dev/null @@ -1,28 +0,0 @@ -pipeline: - fmt: - group: default - image: rust_avr - commands: - - cd firmware/rust/ - - cargo fmt --all -- --check - - clippy: - group: default - image: rust_avr - commands: - - cd firmware/rust/ - - cargo clippy --all-features - - doc: - group: default - image: rust_avr - commands: - - cd firmware/rust/ - - cargo doc --all-features - - build: - group: default - image: rust_avr - commands: - - cd firmware/rust/ - - cargo build --all-features diff --git a/README.md b/README.md index 2d51b42..1b02b01 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ -# Clock Generator [![status-badge](https://ci.onders.org/api/badges/finga/clock_generator/status.svg)](https://ci.onders.org/finga/clock_generator) +# Clock Generator -A simple board to control an Si5351 clock generator board with the -TWI. Though, this is not limited to the Si5351 board as it is designed -to be universal usable. +A simple board operating another Si5351 clock generator board. -## Board -The board is populated with an ATmega328(p), a display and its -backlight driver, a switchable rotary encoder and powered by a mini -USB port. All unneeded MCU pins are accessible via pin headers. +## Motherboard +The board is populated by an ATmega328(p), powered by a mini USB port, +a display and its backlight driver as well as a switchable rotary +encoder. The board is designed to be used not only for this project, +but also for similar projects where peripherals such as a display and +a rotary encoder are needed. All free mcu pins are accessible via pin +headers. diff --git a/firmware/rust/.cargo/config.toml b/firmware/rust/.cargo/config.toml deleted file mode 100644 index bdea107..0000000 --- a/firmware/rust/.cargo/config.toml +++ /dev/null @@ -1,6 +0,0 @@ -[build] -target = "avr-specs/avr-atmega328p.json" - -[unstable] -build-std = ["core"] -build-std-features = ["compiler-builtins-mangled-names"] diff --git a/firmware/rust/.gitignore b/firmware/rust/.gitignore deleted file mode 100644 index ea8c4bf..0000000 --- a/firmware/rust/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/firmware/rust/Cargo.lock b/firmware/rust/Cargo.lock deleted file mode 100644 index 74b8068..0000000 --- a/firmware/rust/Cargo.lock +++ /dev/null @@ -1,233 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "atmega-hal" -version = "0.1.0" -source = "git+https://github.com/rahix/avr-hal?branch=main#e897783816437a677aa577ddfdaa34e9a1e86d96" -dependencies = [ - "avr-device", - "avr-hal-generic", -] - -[[package]] -name = "avr-device" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb440a38bef22cef233b3eb46ef13e588eb8dcc70a1bdce3f00ebc6bf6b48dd" -dependencies = [ - "avr-device-macros", - "bare-metal", - "cfg-if", - "vcell", -] - -[[package]] -name = "avr-device-macros" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c62997db86a92e73b68ab491d4379e1ddb12e94a9eb542180bc6b60f0e09f8b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "avr-hal-generic" -version = "0.1.0" -source = "git+https://github.com/rahix/avr-hal?branch=main#e897783816437a677aa577ddfdaa34e9a1e86d96" -dependencies = [ - "avr-device", - "cfg-if", - "embedded-hal", - "nb 0.1.3", - "paste", - "ufmt", - "void", -] - -[[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 = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "clock-generator" -version = "0.1.0-dev" -dependencies = [ - "atmega-hal", - "avr-device", - "embedded-hal", - "nb 1.0.0", - "panic-halt", - "si5351", -] - -[[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 = "nb" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" -dependencies = [ - "nb 1.0.0", -] - -[[package]] -name = "nb" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" - -[[package]] -name = "panic-halt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" - -[[package]] -name = "paste" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" - -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - -[[package]] -name = "proc-macro2" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[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 = "si5351" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06c1f1f280165963ce5fdff953a5303ac186b2b723aa1ed33916db70ceabfb8" -dependencies = [ - "bitflags", - "embedded-hal", -] - -[[package]] -name = "syn" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "ufmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7ecea7ef79d3f8f878eee614afdf5256475c63ad76139d4da6125617c784a0" -dependencies = [ - "proc-macro-hack", - "ufmt-macros", - "ufmt-write", -] - -[[package]] -name = "ufmt-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed813e34a2bfa9dc58ee2ed8c8314d25e6d70c911486d64b8085cb695cfac069" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "ufmt-write" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[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" diff --git a/firmware/rust/Cargo.toml b/firmware/rust/Cargo.toml deleted file mode 100644 index 50a1fe9..0000000 --- a/firmware/rust/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -name = "clock-generator" -version = "0.1.0-dev" -authors = ["finga "] -edition = "2018" -license = "GPL-3.0-or-later" - -[[bin]] -name = "clock-generator" -test = false -bench = false - -[dependencies] -panic-halt = "0.2" -nb = "1.0" -embedded-hal = "0.2" -avr-device = { version = "0.3", features = ["atmega328p"] } -si5351 = "0.2" - -[dependencies.atmega-hal] -git = "https://github.com/rahix/avr-hal" -branch = "main" -features = ["atmega328p", "rt"] - -[profile.dev] -panic = "abort" -lto = true -opt-level = "s" - -[profile.release] -panic = "abort" -codegen-units = 1 -debug = true -lto = true -opt-level = "s" diff --git a/firmware/rust/Makefile.toml b/firmware/rust/Makefile.toml deleted file mode 100644 index e47cb7d..0000000 --- a/firmware/rust/Makefile.toml +++ /dev/null @@ -1,45 +0,0 @@ -[env] -MCU = "atmega328p" -PROGRAMMER = "usbasp" -EXTENDED_FUSE = "0xFF" -HIGH_FUSE = "0xD6" -LOW_FUSE = "0xE2" - -[tasks.size] -description = "Print usage of memory segments" -dependencies = ["build"] -command = "avr-size" -args = ["--format=avr", "--mcu=${MCU}", "target/avr-atmega328p/debug/clock-generator.elf"] - -[tasks.copy_flash] -description = "Extract the flash" -dependencies = ["build"] -command = "avr-objcopy" -args = ["-O", "ihex", "-j", ".text", "-j", ".data", "target/avr-atmega328p/debug/clock-generator.elf", "target/avr-atmega328p/debug/clock-generator.hex"] - -[tasks.flash] -description = "Flash the firmware" -dependencies = ["copy_flash", "size"] -command = "avrdude" -args = ["-p", "${MCU}", "-c", "${PROGRAMMER}", "-U", "flash:w:target/avr-atmega328p/debug/clock-generator.hex:a"] - -[tasks.copy_eeprom] -description = "Extract the EEPROM" -dependencies = ["build"] -command = "avr-objcopy" -args = ["--change-section-lma", ".eeprom=0", "-O", "ihex", "-j", ".eeprom", "target/avr-atmega328p/debug/clock-generator.elf", "target/avr-atmega328p/debug/clock-generator.eep"] - -[tasks.eeprom] -description = "Flash the eeprom" -dependencies = ["copy_eeprom", "size"] -command = "avrdude" -args = ["-p", "${MCU}", "-c", "${PROGRAMMER}", "-U", "eeprom:w:target/avr-atmega328p/debug/clock-generator.eep:a"] - -[tasks.fuses] -description = "Burn the fuses" -command = "avrdude" -args = ["-p", "${MCU}", "-c", "${PROGRAMMER}", "-U", "efuse:w:${EXTENDED_FUSE}:m", "-U", "hfuse:w:${HIGH_FUSE}:m", "-U", "lfuse:w:${LOW_FUSE}:m"] - -[tasks.all] -description = "Execute all tasks" -run_task = { name = ["fuses", "eeprom", "flash"] } diff --git a/firmware/rust/avr-specs/avr-atmega328p.json b/firmware/rust/avr-specs/avr-atmega328p.json deleted file mode 100644 index e236b08..0000000 --- a/firmware/rust/avr-specs/avr-atmega328p.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "arch": "avr", - "atomic-cas": false, - "cpu": "atmega328p", - "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8", - "eh-frame-header": false, - "exe-suffix": ".elf", - "executables": true, - "late-link-args": { - "gcc": [ - "-lgcc" - ] - }, - "linker": "avr-gcc", - "linker-is-gnu": true, - "llvm-target": "avr-unknown-unknown", - "max-atomic-width": 8, - "no-default-libraries": false, - "pre-link-args": { - "gcc": [ - "-mmcu=atmega328p", - "-Wl,--as-needed" - ] - }, - "target-c-int-width": "16", - "target-pointer-width": "16" -} diff --git a/firmware/rust/rust-toolchain.toml b/firmware/rust/rust-toolchain.toml deleted file mode 100644 index 6616dd3..0000000 --- a/firmware/rust/rust-toolchain.toml +++ /dev/null @@ -1,4 +0,0 @@ -[toolchain] -channel = "nightly-2021-01-07" -components = [ "rust-src" ] -profile = "minimal" diff --git a/firmware/rust/src/assets.rs b/firmware/rust/src/assets.rs deleted file mode 100644 index 3874534..0000000 --- a/firmware/rust/src/assets.rs +++ /dev/null @@ -1,294 +0,0 @@ -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -pub const SACRED_CHAO: [[u8; 40]; 5] = [ - [ - 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF0, 0xF8, 0xFC, 0xFC, 0xFE, 0xFE, 0xFE, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFC, 0xFC, - 0xF8, 0xF0, 0xF0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, - ], - [ - 0x80, 0xF0, 0xFC, 0xFE, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, 0x3F, 0x3F, 0x7F, 0x7F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x03, 0xC3, - 0xE3, 0x73, 0x37, 0x17, 0x07, 0x0F, 0x1E, 0x3C, 0xF0, 0x80, - ], - [ - 0xFF, 0xFF, 0xFF, 0xE7, 0xC3, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0xFC, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0x30, 0x00, 0x00, 0xFF, - ], - [ - 0x01, 0x0F, 0x3F, 0x4F, 0x9F, 0x3F, 0x3E, 0x3C, 0x7C, 0x7C, 0x7C, 0x7C, 0x3E, 0x3E, 0x3E, - 0x1F, 0x1F, 0x0F, 0x07, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x07, 0x0F, 0x0F, 0x0F, - 0x0F, 0x0F, 0x07, 0x07, 0x03, 0x81, 0x40, 0x30, 0x0E, 0x01, - ], - [ - 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x08, 0x10, 0x20, 0x20, 0x40, 0x40, 0x40, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0x40, 0x40, 0x20, 0x20, - 0x10, 0x08, 0x08, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, - ], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -pub const ONDERS_ORG: [[u8; 48]; 2] = [ - [ - 0xE0, 0x60, 0xE0, 0x00, 0x00, 0xE0, 0x60, 0xE0, 0x00, 0x00, 0xE0, 0x60, 0xF8, 0x00, 0x00, - 0xE0, 0xA0, 0xE0, 0x00, 0x00, 0xE0, 0x60, 0xE0, 0x00, 0x00, 0xE0, 0xA0, 0xA0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x60, 0xE0, 0x00, 0x00, 0xE0, 0x60, 0xE0, 0x00, 0x00, - 0xE0, 0x60, 0xE0, - ], - [ - 0x03, 0x02, 0x03, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x03, 0x02, 0x03, 0x00, 0x00, - 0x03, 0x02, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x0B, 0x0A, 0x0F, - ], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -pub const ON: [u8; 11] = [ - 0x00, 0x7E, 0x66, 0x7E, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00, -]; - -pub const OFF: [u8; 11] = [ - 0x7E, 0x66, 0x7E, 0x00, 0x7E, 0x16, 0x16, 0x00, 0x7E, 0x16, 0x16, -]; - -pub const PLL_A: [u8; 6] = [0x00, 0x7C, 0x12, 0x12, 0x7C, 0x00]; - -pub const PLL_B: [u8; 6] = [0x00, 0x7E, 0x4A, 0x4A, 0x74, 0x00]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_DOT: [&[u8]; 2] = [&[0x00, 0x00], &[0x30, 0x30]]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_0: [&[u8]; 2] = [ - &[0xF8, 0xFC, 0x0C, 0xFC, 0xF8], - &[0x1F, 0x3F, 0x30, 0x3F, 0x1F], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_1: [&[u8]; 2] = [ - &[0x30, 0x30, 0xFC, 0xFC, 0x00], - &[0x30, 0x30, 0x3F, 0x3F, 0x30], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_2: [&[u8]; 2] = [ - &[0x18, 0x1C, 0x8C, 0xFC, 0xF8], - &[0x38, 0x3E, 0x3F, 0x33, 0x30], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_3: [&[u8]; 2] = [ - &[0x18, 0x9C, 0x8C, 0xFC, 0x78], - &[0x18, 0x39, 0x31, 0x3F, 0x1E], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_4: [&[u8]; 2] = [ - &[0x80, 0xE0, 0x78, 0xFC, 0xFC], - &[0x07, 0x07, 0x06, 0x3F, 0x3F], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_5: [&[u8]; 2] = [ - &[0xFC, 0xFC, 0x8C, 0x8C, 0x0C], - &[0x1C, 0x3D, 0x31, 0x3F, 0x1F], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_6: [&[u8]; 2] = [ - &[0xF8, 0xFC, 0x8C, 0xBC, 0x38], - &[0x1F, 0x3F, 0x31, 0x3F, 0x1F], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_7: [&[u8]; 2] = [ - &[0x0C, 0x0C, 0xEC, 0xFC, 0x1C], - &[0x00, 0x3E, 0x3F, 0x01, 0x00], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_8: [&[u8]; 2] = [ - &[0x78, 0xFC, 0x8C, 0xFC, 0x78], - &[0x1E, 0x3F, 0x31, 0x3F, 0x1E], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_9: [&[u8]; 2] = [ - &[0xF8, 0xFC, 0x8C, 0xFC, 0xF8], - &[0x1C, 0x3D, 0x31, 0x3F, 0x1F], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_COLON: [&[u8]; 2] = [&[0x30, 0x30], &[0x0C, 0x0C]]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_A: [&[u8]; 2] = [ - &[0xC0, 0xF0, 0x3C, 0x3C, 0xF0, 0xC0], - &[0x3F, 0x3F, 0x06, 0x06, 0x3F, 0x3F], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_B: [&[u8]; 2] = [ - &[0xFC, 0xFC, 0x8C, 0x8C, 0xFC, 0x78], - &[0x3F, 0x3F, 0x31, 0x31, 0x3F, 0x1E], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_C: [&[u8]; 2] = [ - &[0xF8, 0xFC, 0x0C, 0x1C, 0x18], - &[0x1F, 0x3F, 0x30, 0x38, 0x18], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_E: [&[u8]; 2] = [ - &[0xFC, 0xFC, 0x8C, 0x8C, 0x0C, 0x0C], - &[0x3F, 0x3F, 0x31, 0x31, 0x30, 0x30], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_F: [&[u8]; 2] = [ - &[0xFC, 0xFC, 0x8C, 0x8C, 0x0C, 0x0C], - &[0x3F, 0x3F, 0x01, 0x01, 0x00, 0x00], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_G: [&[u8]; 2] = [ - &[0xF8, 0xFC, 0x0C, 0x0C, 0x3C, 0x38], - &[0x1F, 0x3F, 0x30, 0x33, 0x3F, 0x1F], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_H: [&[u8]; 2] = [ - &[0xFC, 0xFC, 0x80, 0x80, 0xFC, 0xFC], - &[0x3F, 0x3F, 0x01, 0x01, 0x3F, 0x3F], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_I: [&[u8]; 2] = [&[0x0C, 0xFC, 0xFC, 0x0C], &[0x30, 0x3F, 0x3F, 0x30]]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_K: [&[u8]; 2] = [ - &[0xFC, 0xFC, 0xC0, 0xF0, 0x7C, 0x1C], - &[0x3F, 0x3F, 0x03, 0x0F, 0x3E, 0x38], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_L: [&[u8]; 2] = [ - &[0xFC, 0xFC, 0x00, 0x00, 0x00], - &[0x3F, 0x3F, 0x30, 0x30, 0x30], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_N: [&[u8]; 2] = [ - &[0xFC, 0xFC, 0xF0, 0xC0, 0x00, 0xFC, 0xFC], - &[0x3F, 0x3F, 0x00, 0x03, 0x0F, 0x3F, 0x3F], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_O: [&[u8]; 2] = [ - &[0xF8, 0xFC, 0x0C, 0x0C, 0xFC, 0xF8], - &[0x1F, 0x3F, 0x30, 0x30, 0x3F, 0x1F], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_P: [&[u8]; 2] = [ - &[0xFC, 0xFC, 0x8C, 0x8C, 0xFC, 0xF8], - &[0x3F, 0x3F, 0x01, 0x01, 0x01, 0x00], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_Q: [&[u8]; 2] = [ - &[0xF8, 0xFC, 0x0C, 0x0C, 0xFC, 0xF8], - &[0x1F, 0x3F, 0x30, 0x38, 0x7F, 0x6F], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_R: [&[u8]; 2] = [ - &[0xFC, 0xFC, 0x8C, 0x8C, 0xFC, 0xF8], - &[0x3F, 0x3F, 0x01, 0x03, 0x3F, 0x3E], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_S: [&[u8]; 2] = [ - &[0xF8, 0xFC, 0x8C, 0x8C, 0x9C, 0x18], - &[0x18, 0x39, 0x31, 0x31, 0x3F, 0x1F], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_T: [&[u8]; 2] = [ - &[0x0C, 0x0C, 0xFC, 0xFC, 0x0C, 0x0C], - &[0x00, 0x00, 0x3F, 0x3F, 0x00, 0x00], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_U: [&[u8]; 2] = [ - &[0xFC, 0xFC, 0x00, 0x00, 0xFC, 0xFC], - &[0x1F, 0x3F, 0x30, 0x30, 0x3F, 0x1F], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_UNDERSCORE: [&[u8]; 2] = [ - &[0x00, 0x00, 0x00, 0x00, 0x00], - &[0x30, 0x30, 0x30, 0x30, 0x30], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -const SYM_INVALID: [&[u8]; 2] = [ - &[0x80, 0xE0, 0x98, 0xCC, 0x4C, 0x18, 0xE0, 0x80], - &[0x01, 0x07, 0x1F, 0x24, 0x25, 0x1F, 0x07, 0x01], -]; - -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -pub const SYMBOL_TABLE: [&[&[u8]; 2]; 50] = [ - &SYM_DOT, // '.' - &SYM_INVALID, // '/' - &SYM_0, // '0' - &SYM_1, // '1' - &SYM_2, // '2' - &SYM_3, // '3' - &SYM_4, // '4' - &SYM_5, // '5' - &SYM_6, // '6' - &SYM_7, // '7' - &SYM_8, // '8' - &SYM_9, // '9' - &SYM_COLON, // ':' - &SYM_INVALID, // ';' - &SYM_INVALID, // '<' - &SYM_INVALID, // '=' - &SYM_INVALID, // '>' - &SYM_INVALID, // '?' - &SYM_INVALID, // '@' - &SYM_A, // 'A' - &SYM_B, // 'B' - &SYM_C, // 'C' - &SYM_INVALID, // 'D' - &SYM_E, // 'E' - &SYM_F, // 'F' - &SYM_G, // 'G' - &SYM_H, // 'H' - &SYM_I, // 'I' - &SYM_INVALID, // 'J' - &SYM_K, // 'K' - &SYM_L, // 'L' - &SYM_INVALID, // 'M' - &SYM_N, // 'N' - &SYM_O, // 'O' - &SYM_P, // 'P' - &SYM_Q, // 'Q' - &SYM_R, // 'R' - &SYM_S, // 'S' - &SYM_T, // 'T' - &SYM_U, // 'U' - &SYM_INVALID, // 'V' - &SYM_INVALID, // 'W' - &SYM_INVALID, // 'X' - &SYM_INVALID, // 'Y' - &SYM_INVALID, // 'Z' - &SYM_INVALID, // '[' - &SYM_INVALID, // '\' - &SYM_INVALID, // ']' - &SYM_INVALID, // '^' - &SYM_UNDERSCORE, // '_' -]; diff --git a/firmware/rust/src/eeprom.rs b/firmware/rust/src/eeprom.rs deleted file mode 100644 index dad5600..0000000 --- a/firmware/rust/src/eeprom.rs +++ /dev/null @@ -1,48 +0,0 @@ -use atmega_hal::Peripherals; -use avr_device::interrupt; -use core::convert::Infallible; -use nb::Error::WouldBlock; - -pub fn read_byte(p_addr: *const u8) -> nb::Result { - let dp = unsafe { Peripherals::steal() }; - let eeprom = dp.EEPROM; - - // Wait for completion of previous access - if eeprom.eecr.read().eepe().bit_is_set() { - return Err(WouldBlock); - } - - interrupt::free(|_cs| { - // Write address into EEPROM address register - eeprom.eear.write(|w| unsafe { w.bits(p_addr as u16) }); - // Start to read from EEPROM by setting EERE - eeprom.eecr.write(|w| w.eere().set_bit()); - }); - - // Return data from EEPROM data register - Ok(eeprom.eedr.read().bits()) -} - -pub fn write_byte(p_addr: *const u8, data: u8) -> nb::Result<(), Infallible> { - let dp = unsafe { Peripherals::steal() }; - let eeprom = dp.EEPROM; - - // Wait for completion of previous access - if eeprom.eecr.read().eepe().bit_is_set() { - return Err(WouldBlock); - } - - interrupt::free(|_cs| { - // Write address into EEPROM address register - eeprom.eear.write(|w| unsafe { w.bits(p_addr as u16) }); - // Write data into EEPROM data register - eeprom.eedr.write(|w| unsafe { w.bits(data) }); - // Enable writing to the EEPROM by setting EEMPE - eeprom.eecr.write(|w| w.eempe().set_bit()); - // Start to write to EEPROM by setting EEPE - eeprom.eecr.write(|w| w.eepe().set_bit()); - }); - - // Return data from EEPROM data register - Ok(()) -} diff --git a/firmware/rust/src/lcd.rs b/firmware/rust/src/lcd.rs deleted file mode 100644 index 4d25a18..0000000 --- a/firmware/rust/src/lcd.rs +++ /dev/null @@ -1,359 +0,0 @@ -use atmega_hal::{ - delay::Delay, - port::{mode::Output, Pin, PB0, PB1}, - Spi, -}; -use core::convert::TryInto; -use embedded_hal::{blocking::delay::DelayMs, spi::FullDuplex}; -use nb::block; - -use crate::{assets::SYMBOL_TABLE, eeprom, DefaultClock, CONTRAST}; - -// TODO: Make `cd` and `rst` pins generic pins -pub struct Lcd { - pub spi: Spi, - pub cd: Pin, - rst: Pin, -} - -impl Lcd { - pub fn new(spi: Spi, cd: Pin, rst: Pin) -> Self { - Self { spi, cd, rst } - } - - pub fn init(&mut self) { - let mut delay = Delay::::new(); - - // TODO: Test if delay is really needed - delay.delay_ms(1_u8); - self.rst.set_high(); - // TODO: Try to reduce delay to a minimum - delay.delay_ms(1_u8); - - let init_sequence = [ - 0x40, // (6) Set Scroll Line: Display start line 0 - 0xA1, // (13) Set SEG direction: SEG reverse - 0xC0, // (14) Set COM direction: Normal COM0 - COM63 - 0xA6, // (11) Set Inverse Display: Display inverse off - 0xA2, // (17) Set LCD Bias Ratio: Set Bias 1/9 (Duty 1/65) - 0x2F, // (5) Set Power Control: Booster, Regulator and Follower on - 0x27, // (8) Set VLCD Resistor Ratio: Set Contrast - 0xEE, // (18) Reset Cursor Update Mode - 0x81, // (9) Set Electronic Volume: Set Contrast - nb::block!(eeprom::read_byte(&CONTRAST)).unwrap(), // (9) Set Electronic Volume: Set Contrast - 0xAF, // (12) Set Display Enable: Display on - ]; - - for i in init_sequence.iter() { - block!(self.spi.send(*i)).unwrap(); - } - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - } - - pub fn set_contrast(&mut self, contrast: u8) { - assert!(contrast <= 63); - - block!(self.spi.send(0x81)).unwrap(); // (9) Set Electronic Volume: Set Contrast - block!(self.spi.send(contrast)).unwrap(); // (9) Set Electronic Volume: Set Contrast - } - - pub fn move_cursor(&mut self, segment: u8, page: u8) { - assert!(segment < 102); - assert!(page < 8); - - block!(self.spi.send(0x0F & segment)).unwrap(); - block!(self.spi.send(0x10 + (segment >> 4))).unwrap(); - block!(self.spi.send(0xB0 + page)).unwrap(); - } - - fn fill(&mut self, segment: u8, page: u8, width: u8, data: u8) { - assert!(segment + width <= 102); - let mut delay = Delay::::new(); - - self.move_cursor(segment, page); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_high(); - - for _ in 0..width { - block!(self.spi.send(data)).unwrap(); - } - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_low(); - } - - pub fn fill_area(&mut self, segment: u8, page: u8, width: u8, height: u8, data: u8) { - assert!(page + height <= 8); - - for i in 0..height { - self.fill(segment, page + i, width, data); - } - } - - pub fn print(&mut self, segment: u8, page: u8, string: &str) { - let mut delay = Delay::::new(); - - for i in 0..2 { - self.move_cursor(segment, page + i); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_high(); - - block!(self.spi.send(0x00)).unwrap(); - block!(self.spi.send(0x00)).unwrap(); - for c in string.chars() { - for segment in SYMBOL_TABLE[c as usize - 46][i as usize] { - block!(self.spi.send(*segment)).unwrap(); - } - block!(self.spi.send(0x00)).unwrap(); - } - block!(self.spi.send(0x00)).unwrap(); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_low(); - } - } - - pub fn print_inverted(&mut self, segment: u8, page: u8, string: &str) { - let mut delay = Delay::::new(); - - for i in 0..2 { - self.move_cursor(segment, page + i); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_high(); - - block!(self.spi.send(0xFF)).unwrap(); - block!(self.spi.send(0xFF)).unwrap(); - for c in string.chars() { - for segment in SYMBOL_TABLE[c as usize - 46][i as usize] { - block!(self.spi.send(!*segment)).unwrap(); - } - block!(self.spi.send(0xFF)).unwrap(); - } - block!(self.spi.send(0xFF)).unwrap(); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_low(); - } - } - - pub fn print_u8(&mut self, segment: u8, page: u8, digits: u8, data: u8) { - assert!(digits <= 3); - let mut delay = Delay::::new(); - - let mut array = [0_usize; 3]; - for (i, item) in array.iter_mut().enumerate() { - *item = (((data / 10_u8.pow(i.try_into().unwrap())) % 10) + 2).into(); - } - array.reverse(); - - for i in 0..2 { - self.move_cursor(segment, page + i); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_high(); - - block!(self.spi.send(0x00)).unwrap(); - block!(self.spi.send(0x00)).unwrap(); - for j in 3 - digits..3 { - for segment in SYMBOL_TABLE[array[j as usize]][i as usize] { - block!(self.spi.send(*segment)).unwrap(); - } - block!(self.spi.send(0x00)).unwrap(); - } - block!(self.spi.send(0x00)).unwrap(); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_low(); - } - } - - pub fn print_u8_inverted(&mut self, segment: u8, page: u8, digits: u8, data: u8) { - assert!(digits <= 3); - let mut delay = Delay::::new(); - - let mut array = [0usize; 3]; - for (i, item) in array.iter_mut().enumerate() { - *item = (((data / 10_u8.pow(i.try_into().unwrap())) % 10) + 2).into(); - } - array.reverse(); - - for i in 0..2 { - self.move_cursor(segment, page + i); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_high(); - - block!(self.spi.send(0xFF)).unwrap(); - block!(self.spi.send(0xFF)).unwrap(); - for j in 3 - digits..3 { - for segment in SYMBOL_TABLE[array[j as usize]][i as usize] { - block!(self.spi.send(!*segment)).unwrap(); - } - block!(self.spi.send(0xFF)).unwrap(); - } - block!(self.spi.send(0xFF)).unwrap(); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_low(); - } - } - - pub fn print_freq(&mut self, segment: u8, page: u8, data: u32) { - let mut delay = Delay::::new(); - - let mut array = [0usize; 9]; - for (i, item) in array.iter_mut().enumerate() { - *item = (((data / 10_u32.pow(i.try_into().unwrap())) % 10) + 2) - .try_into() - .unwrap(); - } - array.reverse(); - - for i in 0..2 { - self.move_cursor(segment, page + i); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_high(); - - block!(self.spi.send(0x00)).unwrap(); - block!(self.spi.send(0x00)).unwrap(); - for j in 0..3 { - for segment in SYMBOL_TABLE[array[j as usize]][i as usize] { - block!(self.spi.send(*segment)).unwrap(); - } - block!(self.spi.send(0x00)).unwrap(); - } - for segment in SYMBOL_TABLE[0][i as usize] { - block!(self.spi.send(*segment)).unwrap(); - } - block!(self.spi.send(0x00)).unwrap(); - for j in 3..6 { - for segment in SYMBOL_TABLE[array[j as usize]][i as usize] { - block!(self.spi.send(*segment)).unwrap(); - } - block!(self.spi.send(0x00)).unwrap(); - } - for segment in SYMBOL_TABLE[0][i as usize] { - block!(self.spi.send(*segment)).unwrap(); - } - block!(self.spi.send(0x00)).unwrap(); - for j in 6..9 { - for segment in SYMBOL_TABLE[array[j as usize]][i as usize] { - block!(self.spi.send(*segment)).unwrap(); - } - block!(self.spi.send(0x00)).unwrap(); - } - block!(self.spi.send(0x00)).unwrap(); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_low(); - } - } - - pub fn print_freq_digit(&mut self, segment: u8, page: u8, data: u32, digit: u8) { - let mut delay = Delay::::new(); - - let mut array = [0usize; 9]; - for (i, item) in array.iter_mut().enumerate() { - *item = (((data / 10_u32.pow(i.try_into().unwrap())) % 10) + 2) - .try_into() - .unwrap(); - } - array.reverse(); - let digit = 8 - digit; - - for i in 0..2 { - self.move_cursor(segment, page + i); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_high(); - - block!(self.spi.send(0x00)).unwrap(); - block!(self.spi.send(0x00)).unwrap(); - for j in 0..3 { - if j == digit { - for segment in SYMBOL_TABLE[array[j as usize]][i as usize] { - block!(self.spi.send(!*segment)).unwrap(); - } - } else { - for segment in SYMBOL_TABLE[array[j as usize]][i as usize] { - block!(self.spi.send(*segment)).unwrap(); - } - } - block!(self.spi.send(0x00)).unwrap(); - } - for segment in SYMBOL_TABLE[0][i as usize] { - block!(self.spi.send(*segment)).unwrap(); - } - block!(self.spi.send(0x00)).unwrap(); - for j in 3..6 { - if j == digit { - for segment in SYMBOL_TABLE[array[j as usize]][i as usize] { - block!(self.spi.send(!*segment)).unwrap(); - } - } else { - for segment in SYMBOL_TABLE[array[j as usize]][i as usize] { - block!(self.spi.send(*segment)).unwrap(); - } - } - block!(self.spi.send(0x00)).unwrap(); - } - for segment in SYMBOL_TABLE[0][i as usize] { - block!(self.spi.send(*segment)).unwrap(); - } - block!(self.spi.send(0x00)).unwrap(); - for j in 6..9 { - if j == digit { - for segment in SYMBOL_TABLE[array[j as usize]][i as usize] { - block!(self.spi.send(!*segment)).unwrap(); - } - } else { - for segment in SYMBOL_TABLE[array[j as usize]][i as usize] { - block!(self.spi.send(*segment)).unwrap(); - } - } - block!(self.spi.send(0x00)).unwrap(); - } - block!(self.spi.send(0x00)).unwrap(); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_low(); - } - } - - pub fn print_icon(&mut self, segment: u8, page: u8, symbol: &[u8]) { - let mut delay = Delay::::new(); - self.move_cursor(segment, page); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_high(); - - for c in symbol { - block!(self.spi.send(*c)).unwrap(); - } - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_low(); - } -} diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs deleted file mode 100644 index 18fe0d4..0000000 --- a/firmware/rust/src/main.rs +++ /dev/null @@ -1,207 +0,0 @@ -#![feature(abi_avr_interrupt)] -#![no_std] -#![no_main] - -use atmega_hal::{ - clock::MHz8, - delay::Delay, - pac::{EXINT, TC1}, - pins, - port::{self, mode::PullUp, Pin, PB6, PB7, PC0}, - spi::{DataOrder, SerialClockRate, Settings, Spi}, - Peripherals, -}; -use avr_device::interrupt; -use core::sync::atomic::{AtomicBool, Ordering}; -use embedded_hal::{ - blocking::delay::DelayMs, - spi::{Mode, Phase, Polarity}, -}; -use panic_halt as _; - -mod assets; -mod eeprom; -mod lcd; -mod rotary; -mod screen; - -use rotary::{Direction, Rotary}; -use screen::{Screen, Screens}; - -pub type DefaultClock = MHz8; -pub type I2c = atmega_hal::i2c::I2c; - -#[used] -#[link_section = ".eeprom"] -static BACKLIGHT: u8 = 0; - -#[used] -#[link_section = ".eeprom"] -static CONTRAST: u8 = 8; - -pub enum Input { - Next, - Previous, - Select, - Back, -} - -struct ClockGenerator { - screen: Screen, - tc1: TC1, - exint: EXINT, - encoder: Rotary, PB6>, Pin, PB7>>, - button: Pin, PC0>, - delay: Delay, -} - -impl ClockGenerator { - fn new() -> Self { - let dp = Peripherals::take().unwrap(); - let pins = pins!(dp); - - // Init SPI - let (spi, _) = Spi::new( - dp.SPI, - pins.pb5.into_output(), - pins.pb3.into_output(), - pins.pb4.into_pull_up_input(), - pins.pb2.into_output(), - Settings { - data_order: DataOrder::MostSignificantFirst, - clock: SerialClockRate::OscfOver2, - mode: Mode { - polarity: Polarity::IdleLow, - phase: Phase::CaptureOnFirstTransition, - }, - }, - ); - - Self { - screen: Screen::new( - dp.TC0, - spi, - pins.pd5.into_output(), - pins.pb0.into_output(), - pins.pb1.into_output(), - I2c::new( - dp.TWI, - pins.pc4.into_pull_up_input(), - pins.pc5.into_pull_up_input(), - 50000, - ), - ), - tc1: dp.TC1, - exint: dp.EXINT, - delay: Delay::::new(), - encoder: Rotary::new(pins.pb6.into_pull_up_input(), pins.pb7.into_pull_up_input()), - button: pins.pc0.into_pull_up_input(), - } - } - - fn init(&mut self) { - // Init Timer/Counter1 - self.tc1.ocr1a.write(|w| unsafe { w.bits(65535) }); - - // Enable interrupts for encoder, button and timer - self.exint.pcicr.write(|w| w.pcie().bits(0b0000_0011)); - self.exint.pcmsk0.write(|w| w.pcint().bits(0b1100_0000)); - self.exint.pcmsk1.write(|w| w.pcint().bits(0b0000_0001)); - self.tc1.timsk1.write(|w| w.ocie1a().set_bit()); - - // Init screen - self.screen.init(); - - // Show splash screen for a moment - self.delay.delay_ms(2000_u16); - - // Set home screen - self.screen.change(Screens::Home(screen::Home::new())); - - // Enable interrupts globally - unsafe { interrupt::enable() }; - } - - fn update(&mut self) { - if UPDATE_ENCODER.load(Ordering::SeqCst) { - interrupt::free(|_cs| { - self.delay.delay_ms(3_u8); - - match self.encoder.update() { - Direction::Clockwise => { - self.screen.input(&Input::Next); - } - Direction::CounterClockwise => { - self.screen.input(&Input::Previous); - } - Direction::None => {} - } - UPDATE_ENCODER.store(false, Ordering::SeqCst); - }) - } - - if UPDATE_TIMER.load(Ordering::SeqCst) { - self.tc1.tccr1b.write(|w| w.cs1().no_clock()); - self.screen.input(&Input::Back); - UPDATE_TIMER.store(false, Ordering::SeqCst); - } - - if UPDATE_BUTTON.load(Ordering::SeqCst) { - self.delay.delay_ms(2_u8); - - if self.button.is_low() { - // Press - if self.tc1.tccr1b.read().cs1().is_no_clock() { - self.tc1.tcnt1.write(|w| unsafe { w.bits(0) }); - self.tc1.tccr1b.write(|w| w.cs1().prescale_64()); - } - } else { - // Release - if self.tc1.tccr1b.read().cs1().is_prescale_64() { - self.tc1.tccr1b.write(|w| w.cs1().no_clock()); - self.screen.input(&Input::Select); - self.delay.delay_ms(3_u8); - } - } - UPDATE_BUTTON.store(false, Ordering::SeqCst); - } - } -} - -static UPDATE_ENCODER: AtomicBool = AtomicBool::new(false); -static UPDATE_BUTTON: AtomicBool = AtomicBool::new(false); -static UPDATE_TIMER: AtomicBool = AtomicBool::new(false); - -#[interrupt(atmega328p)] -#[allow(non_snake_case)] -fn PCINT0() { - interrupt::free(|_cs| { - UPDATE_ENCODER.store(true, Ordering::SeqCst); - }) -} - -#[interrupt(atmega328p)] -#[allow(non_snake_case)] -fn PCINT1() { - interrupt::free(|_cs| { - UPDATE_BUTTON.store(true, Ordering::SeqCst); - }) -} - -#[interrupt(atmega328p)] -#[allow(non_snake_case)] -fn TIMER1_COMPA() { - interrupt::free(|_cs| { - UPDATE_TIMER.store(true, Ordering::SeqCst); - }) -} - -#[atmega_hal::entry] -fn main() -> ! { - let mut cg = ClockGenerator::new(); - cg.init(); - - loop { - cg.update(); - } -} diff --git a/firmware/rust/src/rotary.rs b/firmware/rust/src/rotary.rs deleted file mode 100644 index 21919dc..0000000 --- a/firmware/rust/src/rotary.rs +++ /dev/null @@ -1,51 +0,0 @@ -use embedded_hal::digital::v2::InputPin; - -pub struct Rotary { - pin_a: A, - pin_b: B, - state: u8, -} - -pub enum Direction { - Clockwise, - CounterClockwise, - None, -} - -impl Rotary -where - A: InputPin, - B: InputPin, -{ - pub fn new(pin_a: A, pin_b: B) -> Self { - Self { - pin_a, - pin_b, - state: 0, - } - } - - pub fn update(&mut self) -> Direction { - let a = self.pin_a.is_low(); - let b = self.pin_b.is_low(); - - let new_state = match (a, b) { - (Ok(true), Ok(true)) => 0b00, - (Ok(true), Ok(false)) => 0b01, - (Ok(false), Ok(true)) => 0b10, - (Ok(false), Ok(false)) => 0b11, - _ => return Direction::None, - }; - - let direction = match (self.state, new_state) { - (0b10, 0b11) => Direction::Clockwise, - (0b01, 0b00) => Direction::Clockwise, - (0b01, 0b11) => Direction::CounterClockwise, - (0b10, 0b00) => Direction::CounterClockwise, - _ => Direction::None, - }; - - self.state = new_state; - direction - } -} diff --git a/firmware/rust/src/screen/channel.rs b/firmware/rust/src/screen/channel.rs deleted file mode 100644 index 0ee5dc3..0000000 --- a/firmware/rust/src/screen/channel.rs +++ /dev/null @@ -1,191 +0,0 @@ -use super::{ClockChannel, Event, Home, Screens}; -use crate::{lcd::Lcd, Input}; -use si5351::{ClockOutput, PLL}; - -enum Selection { - Frequency, - FrequencyDigit(u8), - Digit(u8), - Enabled, - Pll, - Back, -} - -pub struct Channel { - active: Selection, - channel: ClockChannel, -} - -impl Channel { - pub fn new(channel: ClockChannel) -> Self { - Self { - active: Selection::Frequency, - channel, - } - } - - pub fn input(&mut self, input: &Input) -> Event { - self.active = match self.active { - Selection::Frequency => match input { - Input::Next => Selection::Enabled, - Input::Previous => Selection::Back, - Input::Select => Selection::FrequencyDigit(8), - Input::Back => return Event::Screen(Screens::Home(Home::new())), - }, - Selection::FrequencyDigit(digit) => match input { - Input::Next => { - if digit == u8::MIN { - Selection::FrequencyDigit(8) - } else { - Selection::FrequencyDigit(digit - 1) - } - } - Input::Previous => { - if digit >= 8 { - Selection::FrequencyDigit(u8::MIN) - } else { - Selection::FrequencyDigit(digit + 1) - } - } - Input::Select => Selection::Digit(digit), - Input::Back => Selection::Frequency, - }, - Selection::Digit(digit) => match input { - Input::Next => { - let new_freq = self.channel.freq + 10_u32.pow(digit.into()); - if new_freq < 162_000_000 { - self.channel.freq = new_freq; - } else { - self.channel.freq = 162_000_000 - } - return Event::Channel(self.channel); - } - Input::Previous => { - let difference = 10_u32.pow(digit.into()); - if self.channel.freq > difference { - let new_freq = self.channel.freq - difference; - if new_freq > 15_000 { - self.channel.freq = new_freq; - } else { - self.channel.freq = 15_000; - } - } else { - self.channel.freq = 15_000; - } - return Event::Channel(self.channel); - } - Input::Select => Selection::Frequency, - Input::Back => Selection::FrequencyDigit(digit), - }, - Selection::Enabled => match input { - Input::Next => Selection::Pll, - Input::Previous => Selection::Frequency, - Input::Select => { - self.channel.enabled = !self.channel.enabled; - return Event::Channel(self.channel); - } - Input::Back => return Event::Screen(Screens::Home(Home::new())), - }, - Selection::Pll => match input { - Input::Next => Selection::Back, - Input::Previous => Selection::Enabled, - Input::Select => { - self.channel.pll = match self.channel.pll { - PLL::A => PLL::B, - PLL::B => PLL::A, - }; - return Event::Channel(self.channel); - } - Input::Back => return Event::Screen(Screens::Home(Home::new())), - }, - Selection::Back => match input { - Input::Next => Selection::Frequency, - Input::Previous => Selection::Pll, - _ => return Event::Screen(Screens::Home(Home::new())), - }, - }; - - Event::None - } - - fn draw_enabled(&self, lcd: &mut Lcd, inverted: bool) { - if inverted { - match self.channel.enabled { - false => lcd.print_inverted(13, 4, "OFF"), - true => lcd.print_inverted(16, 4, "ON"), - } - } else { - match self.channel.enabled { - false => lcd.print(13, 4, "OFF"), - true => lcd.print(16, 4, "ON"), - } - } - } - - pub fn draw(&self, lcd: &mut Lcd) { - lcd.fill_area(0, 0, 19, 2, 0xFF); - match self.channel.output { - ClockOutput::Clk0 => lcd.print_inverted(19, 0, "CHANNEL_1"), - ClockOutput::Clk1 => lcd.print_inverted(19, 0, "CHANNEL_2"), - ClockOutput::Clk2 => lcd.print_inverted(19, 0, "CHANNEL_3"), - _ => unimplemented!(), - } - lcd.fill_area(83, 0, 19, 2, 0xFF); - - match &self.active { - Selection::Frequency => { - lcd.print_inverted(0, 2, "FREQ:"); - lcd.print_freq(39, 2, self.channel.freq); - self.draw_enabled(lcd, false); - match self.channel.pll { - PLL::A => lcd.print(59, 4, "PLL_A"), - PLL::B => lcd.print(59, 4, "PLL_B"), - } - lcd.print(36, 6, "BACK"); - } - Selection::FrequencyDigit(digit) | Selection::Digit(digit) => { - lcd.print(0, 2, "FREQ:"); - lcd.print_freq_digit(39, 2, self.channel.freq, *digit); - self.draw_enabled(lcd, false); - match self.channel.pll { - PLL::A => lcd.print(59, 4, "PLL_A"), - PLL::B => lcd.print(59, 4, "PLL_B"), - } - lcd.print(36, 6, "BACK"); - } - Selection::Enabled => { - lcd.print(0, 2, "FREQ:"); - lcd.print_freq(39, 2, self.channel.freq); - self.draw_enabled(lcd, true); - match self.channel.pll { - PLL::A => lcd.print(59, 4, "PLL_A"), - PLL::B => lcd.print(59, 4, "PLL_B"), - } - lcd.print(36, 6, "BACK"); - } - Selection::Pll => { - lcd.print(0, 2, "FREQ:"); - lcd.print_freq(39, 2, self.channel.freq); - self.draw_enabled(lcd, false); - match self.channel.pll { - PLL::A => lcd.print_inverted(59, 4, "PLL_A"), - PLL::B => lcd.print_inverted(59, 4, "PLL_B"), - } - lcd.print(36, 6, "BACK"); - } - Selection::Back => { - lcd.print(0, 2, "FREQ:"); - lcd.print_freq(39, 2, self.channel.freq); - match self.channel.enabled { - false => lcd.print(13, 4, "OFF"), - true => lcd.print(16, 4, "ON"), - } - match self.channel.pll { - PLL::A => lcd.print(59, 4, "PLL_A"), - PLL::B => lcd.print(59, 4, "PLL_B"), - } - lcd.print_inverted(36, 6, "BACK"); - } - } - } -} diff --git a/firmware/rust/src/screen/home.rs b/firmware/rust/src/screen/home.rs deleted file mode 100644 index 68c064d..0000000 --- a/firmware/rust/src/screen/home.rs +++ /dev/null @@ -1,93 +0,0 @@ -use super::{Channel, Event, Screens, Setup}; -use crate::{lcd::Lcd, screen::ClockChannel, Input}; - -enum Selection { - Ch1, - Ch2, - Ch3, - Setup, -} - -pub struct Home { - active: Selection, -} - -impl Home { - pub fn new() -> Self { - Self { - active: Selection::Ch1, - } - } - - pub fn input(&mut self, input: &Input, channels: [ClockChannel; 3]) -> Event { - self.active = match self.active { - Selection::Ch1 => match input { - Input::Next => Selection::Ch2, - Input::Previous => Selection::Setup, - Input::Select => return Event::Screen(Screens::Channel(Channel::new(channels[0]))), - Input::Back => Selection::Ch1, - }, - Selection::Ch2 => match input { - Input::Next => Selection::Ch3, - Input::Previous => Selection::Ch1, - Input::Select => return Event::Screen(Screens::Channel(Channel::new(channels[1]))), - Input::Back => Selection::Ch2, - }, - Selection::Ch3 => match input { - Input::Next => Selection::Setup, - Input::Previous => Selection::Ch2, - Input::Select => return Event::Screen(Screens::Channel(Channel::new(channels[2]))), - Input::Back => Selection::Ch3, - }, - Selection::Setup => match input { - Input::Next => Selection::Ch1, - Input::Previous => Selection::Ch3, - Input::Select => return Event::Screen(Screens::Setup(Setup::new())), - Input::Back => Selection::Setup, - }, - }; - - Event::None - } - - pub fn draw(&self, lcd: &mut Lcd, channels: [ClockChannel; 3]) { - match &self.active { - Selection::Ch1 => { - lcd.print_inverted(0, 0, "CH1"); - channels[0].print(lcd, 0); - lcd.print(0, 2, "CH2"); - channels[1].print(lcd, 2); - lcd.print(0, 4, "CH3"); - channels[2].print(lcd, 4); - lcd.print(33, 6, "SETUP"); - } - Selection::Ch2 => { - lcd.print(0, 0, "CH1"); - channels[0].print(lcd, 0); - lcd.print_inverted(0, 2, "CH2"); - channels[1].print(lcd, 2); - lcd.print(0, 4, "CH3"); - channels[2].print(lcd, 4); - lcd.print(33, 6, "SETUP"); - } - Selection::Ch3 => { - lcd.print(0, 0, "CH1"); - channels[0].print(lcd, 0); - lcd.print(0, 2, "CH2"); - channels[1].print(lcd, 2); - lcd.print_inverted(0, 4, "CH3"); - channels[2].print(lcd, 4); - lcd.print(33, 6, "SETUP"); - } - Selection::Setup => { - lcd.print(0, 0, "CH1"); - channels[0].print(lcd, 0); - lcd.print(0, 2, "CH2"); - channels[1].print(lcd, 2); - lcd.print(0, 4, "CH3"); - channels[2].print(lcd, 4); - lcd.print_inverted(33, 6, "SETUP"); - } - } - } -} diff --git a/firmware/rust/src/screen/mod.rs b/firmware/rust/src/screen/mod.rs deleted file mode 100644 index 622f761..0000000 --- a/firmware/rust/src/screen/mod.rs +++ /dev/null @@ -1,206 +0,0 @@ -use atmega_hal::{ - pac::TC0, - port::{mode::Output, Pin, PB0, PB1, PD5}, - Spi, -}; -use si5351::{ClockOutput, Si5351, Si5351Device, PLL}; - -mod channel; -mod home; -mod setup; -mod splash; - -use crate::{ - assets::{OFF, ON, PLL_A, PLL_B}, - eeprom, - lcd::Lcd, - I2c, Input, BACKLIGHT, -}; -pub use channel::Channel; -pub use home::Home; -pub use setup::Setup; -pub use splash::Splash; - -// TODO: Only update changes instead of whole screen - -pub enum Event { - Screen(Screens), - Backlight(u8), - Contrast(u8), - Channel(ClockChannel), - None, -} - -#[derive(Clone, Copy)] -pub struct ClockChannel { - output: ClockOutput, - freq: u32, - enabled: bool, - pll: PLL, -} - -impl ClockChannel { - fn print(&self, lcd: &mut Lcd, page: u8) { - lcd.print_freq(25, page, self.freq); - lcd.print_icon(91, page, if self.enabled { &ON } else { &OFF }); - lcd.print_icon( - 94, - page + 1, - match self.pll { - PLL::A => &PLL_A, - PLL::B => &PLL_B, - }, - ); - } - - fn update(&self, si5351: &mut Si5351Device) { - si5351 - .set_frequency(self.pll, self.output, self.freq) - .unwrap(); - si5351.set_clock_enabled(self.output, self.enabled); - si5351.flush_output_enabled().unwrap(); - } -} - -pub enum Screens { - Splash(Splash), - Home(Home), - Setup(Setup), - Channel(Channel), -} - -impl Screens { - pub fn input(&mut self, input: &Input, channels: [ClockChannel; 3]) -> Event { - match self { - Screens::Splash(_) => Event::None, - Screens::Home(home) => home.input(input, channels), - Screens::Setup(setup) => setup.input(input), - Screens::Channel(channel) => channel.input(input), - } - } -} - -pub struct Screen { - lcd: Lcd, - tc0: TC0, - pwm: Pin, - screen: Screens, - si5351: Si5351Device, - channels: [ClockChannel; 3], -} - -impl Screen { - pub fn new( - tc0: TC0, - spi: Spi, - pwm: Pin, - cd: Pin, - rst: Pin, - i2c: I2c, - ) -> Self { - Self { - lcd: Lcd::new(spi, cd, rst), - tc0, - pwm, - screen: Screens::Splash(Splash), - si5351: Si5351Device::new_adafruit_module(i2c), - channels: [ - ClockChannel { - output: ClockOutput::Clk0, - freq: 1_000_000, - enabled: false, - pll: PLL::A, - }, - ClockChannel { - output: ClockOutput::Clk1, - freq: 1_000_000, - enabled: false, - pll: PLL::A, - }, - ClockChannel { - output: ClockOutput::Clk2, - freq: 1_000_000, - enabled: false, - pll: PLL::A, - }, - ], - } - } - - pub fn init(&mut self) { - // Init display backlight - self.tc0.ocr0a.write(|w| unsafe { w.bits(255) }); - self.tc0.tccr0a.write(|w| { - w.wgm0().pwm_fast(); - w.com0b().match_clear() - }); - self.set_backlight(nb::block!(eeprom::read_byte(&BACKLIGHT)).unwrap()); - - // Init lcd display - self.lcd.init(); - self.draw(); - - // Init Si5351 - self.si5351.init_adafruit_module().unwrap(); - } - - fn set_backlight(&mut self, backlight: u8) { - match backlight { - 0 => { - self.tc0.tccr0b.write(|w| w.cs0().no_clock()); - self.pwm.set_low(); - } - 1..=5 => { - self.tc0.tccr0b.write(|w| { - w.wgm02().set_bit(); - w.cs0().prescale_256() - }); - self.tc0.ocr0b.write(|w| unsafe { w.bits(backlight - 1) }); - } - _ => { - self.tc0.tccr0b.write(|w| { - w.wgm02().set_bit(); - w.cs0().prescale_64() - }); - self.tc0.ocr0b.write(|w| unsafe { w.bits(backlight - 6) }); - } - } - } - - pub fn draw(&mut self) { - self.lcd.fill_area(0, 0, 102, 8, 0x00); - - match &self.screen { - Screens::Splash(splash) => splash.draw(&mut self.lcd), - Screens::Home(home) => home.draw(&mut self.lcd, self.channels), - Screens::Setup(setup) => setup.draw(&mut self.lcd), - Screens::Channel(channel) => channel.draw(&mut self.lcd), - } - } - - pub fn input(&mut self, input: &Input) { - match self.screen.input(input, self.channels) { - Event::Screen(screen) => self.screen = screen, - Event::Backlight(backlight) => self.set_backlight(backlight), - Event::Contrast(contrast) => self.lcd.set_contrast(contrast), - Event::Channel(channel) => { - match channel.output { - ClockOutput::Clk0 => self.channels[0] = channel, - ClockOutput::Clk1 => self.channels[1] = channel, - ClockOutput::Clk2 => self.channels[2] = channel, - _ => unimplemented!(), - } - - channel.update(&mut self.si5351); - } - Event::None => {} - } - - self.draw(); - } - - pub fn change(&mut self, screen: Screens) { - self.screen = screen; - self.draw(); - } -} diff --git a/firmware/rust/src/screen/setup.rs b/firmware/rust/src/screen/setup.rs deleted file mode 100644 index a06ee70..0000000 --- a/firmware/rust/src/screen/setup.rs +++ /dev/null @@ -1,152 +0,0 @@ -use super::{Event, Home, Screens}; -use crate::{eeprom, lcd::Lcd, Input, BACKLIGHT, CONTRAST}; - -enum Selection { - Backlight, - BacklightEdit, - Contrast, - ContrastEdit, - Back, -} - -pub struct Setup { - active: Selection, - backlight: u8, - contrast: u8, -} - -impl Setup { - pub fn new() -> Self { - Self { - active: Selection::Backlight, - backlight: nb::block!(eeprom::read_byte(&BACKLIGHT)).unwrap(), - contrast: nb::block!(eeprom::read_byte(&CONTRAST)).unwrap(), - } - } - - pub fn input(&mut self, input: &Input) -> Event { - self.active = match self.active { - Selection::Backlight => match input { - Input::Next => Selection::Contrast, - Input::Previous => Selection::Back, - Input::Select => Selection::BacklightEdit, - Input::Back => return Event::Screen(Screens::Home(Home::new())), - }, - Selection::BacklightEdit => match input { - Input::Next => { - self.backlight = if self.backlight == u8::MAX { - u8::MIN - } else { - self.backlight + 1 - }; - - return Event::Backlight(self.backlight); - } - Input::Previous => { - self.backlight = if self.backlight == u8::MIN { - u8::MAX - } else { - self.backlight - 1 - }; - - return Event::Backlight(self.backlight); - } - Input::Select => { - nb::block!(eeprom::write_byte(&BACKLIGHT, self.backlight)).unwrap(); - Selection::Backlight - } - Input::Back => { - self.active = Selection::Backlight; - self.backlight = nb::block!(eeprom::read_byte(&BACKLIGHT)).unwrap(); - return Event::Backlight(self.backlight); - } - }, - Selection::Contrast => match input { - Input::Next => Selection::Back, - Input::Previous => Selection::Backlight, - Input::Select => Selection::ContrastEdit, - Input::Back => return Event::Screen(Screens::Home(Home::new())), - }, - Selection::ContrastEdit => match input { - Input::Next => { - self.contrast = if self.contrast >= 63 { - u8::MIN - } else { - self.contrast + 1 - }; - - return Event::Contrast(self.contrast); - } - Input::Previous => { - self.contrast = if self.contrast == u8::MIN { - 63 - } else { - self.contrast - 1 - }; - - return Event::Contrast(self.contrast); - } - Input::Select => { - nb::block!(eeprom::write_byte(&CONTRAST, self.contrast)).unwrap(); - Selection::Contrast - } - Input::Back => { - self.active = Selection::Contrast; - self.contrast = nb::block!(eeprom::read_byte(&CONTRAST)).unwrap(); - return Event::Contrast(self.contrast); - } - }, - Selection::Back => match input { - Input::Next => Selection::Backlight, - Input::Previous => Selection::Contrast, - _ => return Event::Screen(Screens::Home(Home::new())), - }, - }; - - Event::None - } - - pub fn draw(&self, lcd: &mut Lcd) { - lcd.fill_area(0, 0, 33, 2, 0xFF); - lcd.print_inverted(33, 0, "SETUP"); - lcd.fill_area(69, 0, 33, 2, 0xFF); - - match &self.active { - Selection::Contrast => { - lcd.print(0, 2, "BACKLIGHT:"); - lcd.print_u8(81, 2, 3, self.backlight); - lcd.print_inverted(0, 4, "CONTRAST:"); - lcd.print_u8(87, 4, 2, self.contrast); - lcd.print(36, 6, "BACK"); - } - Selection::ContrastEdit => { - lcd.print(0, 2, "BACKLIGHT:"); - lcd.print_u8(81, 2, 3, self.backlight); - lcd.print(0, 4, "CONTRAST:"); - lcd.print_u8_inverted(87, 4, 2, self.contrast); - lcd.print(36, 6, "BACK"); - } - Selection::Backlight => { - lcd.print_inverted(0, 2, "BACKLIGHT:"); - lcd.print_u8(81, 2, 3, self.backlight); - lcd.print(0, 4, "CONTRAST:"); - lcd.print_u8(87, 4, 2, self.contrast); - lcd.print(36, 6, "BACK"); - } - Selection::BacklightEdit => { - lcd.print(0, 2, "BACKLIGHT:"); - lcd.print_u8_inverted(81, 2, 3, self.backlight); - lcd.print(0, 4, "CONTRAST:"); - lcd.print_u8(87, 4, 2, self.contrast); - lcd.print(36, 6, "BACK"); - } - Selection::Back => { - lcd.print(0, 2, "BACKLIGHT:"); - lcd.print_u8(81, 2, 3, self.backlight); - lcd.print(0, 4, "CONTRAST:"); - lcd.print_u8(87, 4, 2, self.contrast); - lcd.print_inverted(36, 6, "BACK"); - } - } - } -} diff --git a/firmware/rust/src/screen/splash.rs b/firmware/rust/src/screen/splash.rs deleted file mode 100644 index 1f95039..0000000 --- a/firmware/rust/src/screen/splash.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::{ - assets::{ONDERS_ORG, SACRED_CHAO}, - lcd::Lcd, - DefaultClock, -}; -use atmega_hal::delay::Delay; -use embedded_hal::{blocking::delay::DelayMs, spi::FullDuplex}; -use nb::block; - -pub struct Splash; - -impl Splash { - pub fn draw(&self, lcd: &mut Lcd) { - let mut delay = Delay::::new(); - - for (i, page) in SACRED_CHAO.iter().enumerate() { - lcd.move_cursor(31, 1 + i as u8); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - lcd.cd.set_high(); - - for segment in page { - block!(lcd.spi.send(*segment)).unwrap(); - } - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - lcd.cd.set_low(); - } - - for (i, page) in ONDERS_ORG.iter().enumerate() { - lcd.move_cursor(27, 6 + i as u8); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - lcd.cd.set_high(); - - for segment in page { - block!(lcd.spi.send(*segment)).unwrap(); - } - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - lcd.cd.set_low(); - } - } -} diff --git a/knob/knob.scad b/knob/knob.scad deleted file mode 100644 index 943fac8..0000000 --- a/knob/knob.scad +++ /dev/null @@ -1,21 +0,0 @@ -$fn = 200; - -module shaft() { - difference() { - cylinder(h = 5, d = 6); - translate([2, -2.5, 0]) - cube(5); - } - - translate([0, 0, -5]) - cylinder(h = 5, d = 8); -} - -difference() { - cylinder(h = 14, d1 = 18, d2 = 16); - translate([0, 0, 5]) - shaft(); -} - -translate([0, 0, 14]) - cylinder(h = 2, d1 = 16, d2 = 12);