From aa75712418fa8cdfac516d4fda25ccdad6c2313d Mon Sep 17 00:00:00 2001 From: finga Date: Tue, 1 Mar 2022 21:34:36 +0100 Subject: [PATCH 01/37] fw-rust: Minimal hello world --- firmware/rust/.cargo/config.toml | 5 + firmware/rust/.gitignore | 1 + firmware/rust/Cargo.lock | 215 ++++++++++++++++++++ firmware/rust/Cargo.toml | 33 +++ firmware/rust/avr-specs/avr-atmega328p.json | 27 +++ firmware/rust/rust-toolchain.toml | 4 + firmware/rust/src/main.rs | 20 ++ 7 files changed, 305 insertions(+) create mode 100644 firmware/rust/.cargo/config.toml create mode 100644 firmware/rust/.gitignore create mode 100644 firmware/rust/Cargo.lock create mode 100644 firmware/rust/Cargo.toml create mode 100644 firmware/rust/avr-specs/avr-atmega328p.json create mode 100644 firmware/rust/rust-toolchain.toml create mode 100644 firmware/rust/src/main.rs diff --git a/firmware/rust/.cargo/config.toml b/firmware/rust/.cargo/config.toml new file mode 100644 index 0000000..e04df30 --- /dev/null +++ b/firmware/rust/.cargo/config.toml @@ -0,0 +1,5 @@ +[build] +target = "avr-specs/avr-atmega328p.json" + +[unstable] +build-std = ["core"] diff --git a/firmware/rust/.gitignore b/firmware/rust/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/firmware/rust/.gitignore @@ -0,0 +1 @@ +/target diff --git a/firmware/rust/Cargo.lock b/firmware/rust/Cargo.lock new file mode 100644 index 0000000..068b887 --- /dev/null +++ b/firmware/rust/Cargo.lock @@ -0,0 +1,215 @@ +# 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 = "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", + "embedded-hal", + "nb 1.0.0", + "panic-halt", +] + +[[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 = "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 new file mode 100644 index 0000000..d86a122 --- /dev/null +++ b/firmware/rust/Cargo.toml @@ -0,0 +1,33 @@ +[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" + +[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/avr-specs/avr-atmega328p.json b/firmware/rust/avr-specs/avr-atmega328p.json new file mode 100644 index 0000000..e236b08 --- /dev/null +++ b/firmware/rust/avr-specs/avr-atmega328p.json @@ -0,0 +1,27 @@ +{ + "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 new file mode 100644 index 0000000..6616dd3 --- /dev/null +++ b/firmware/rust/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly-2021-01-07" +components = [ "rust-src" ] +profile = "minimal" diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs new file mode 100644 index 0000000..cc4d159 --- /dev/null +++ b/firmware/rust/src/main.rs @@ -0,0 +1,20 @@ +#![no_std] +#![no_main] + +use atmega_hal::{clock::MHz8, delay::Delay, pins, Peripherals}; +use embedded_hal::blocking::delay::DelayMs; +use panic_halt as _; + +#[atmega_hal::entry] +fn main() -> ! { + let dp = Peripherals::take().unwrap(); + let pins = pins!(dp); + + let mut led = pins.pd5.into_output(); + let mut delay = Delay::::new(); + + loop { + led.toggle(); + delay.delay_ms(255_u8); + } +} From d2772291bfcbec4eb9f0fa4f0a4f5e012ec038a0 Mon Sep 17 00:00:00 2001 From: finga Date: Thu, 3 Mar 2022 03:18:52 +0100 Subject: [PATCH 02/37] fw-rust: Add a makefile for cargo-make It has to be decided what kind of tooling should be used, for now cargo-make is used. This will probably be changed to use the tools provided by [probe.rs](https://probe.rs). --- firmware/rust/Makefile.toml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 firmware/rust/Makefile.toml diff --git a/firmware/rust/Makefile.toml b/firmware/rust/Makefile.toml new file mode 100644 index 0000000..166b05a --- /dev/null +++ b/firmware/rust/Makefile.toml @@ -0,0 +1,29 @@ +[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 = "Copy 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.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"] From aa59bc302dedfcb2904510b6381ee89250e86374 Mon Sep 17 00:00:00 2001 From: finga Date: Fri, 4 Mar 2022 12:59:51 +0100 Subject: [PATCH 03/37] fw-rust: Create a PWM signal for the backlight To have a dimmable backlight, generate a variable duty-cycle PWM signal which is consumed by the backlight driver circuit. --- firmware/rust/Cargo.lock | 1 + firmware/rust/Cargo.toml | 1 + firmware/rust/src/main.rs | 35 +++++++++++++++++++++++++++-------- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/firmware/rust/Cargo.lock b/firmware/rust/Cargo.lock index 068b887..c856d14 100644 --- a/firmware/rust/Cargo.lock +++ b/firmware/rust/Cargo.lock @@ -66,6 +66,7 @@ name = "clock-generator" version = "0.1.0-dev" dependencies = [ "atmega-hal", + "avr-device", "embedded-hal", "nb 1.0.0", "panic-halt", diff --git a/firmware/rust/Cargo.toml b/firmware/rust/Cargo.toml index d86a122..aee0597 100644 --- a/firmware/rust/Cargo.toml +++ b/firmware/rust/Cargo.toml @@ -14,6 +14,7 @@ bench = false panic-halt = "0.2" nb = "1.0" embedded-hal = "0.2" +avr-device = { version = "0.3", features = ["atmega328p"] } [dependencies.atmega-hal] git = "https://github.com/rahix/avr-hal" diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index cc4d159..88c8d5f 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -1,20 +1,39 @@ #![no_std] #![no_main] -use atmega_hal::{clock::MHz8, delay::Delay, pins, Peripherals}; -use embedded_hal::blocking::delay::DelayMs; +use atmega_hal::{pins, Peripherals}; +use avr_device::interrupt; use panic_halt as _; #[atmega_hal::entry] fn main() -> ! { + // Disable interrupts when initializing + interrupt::disable(); + + // Get peripherals and pins let dp = Peripherals::take().unwrap(); let pins = pins!(dp); - let mut led = pins.pd5.into_output(); - let mut delay = Delay::::new(); + // Init display backlight + let tc0 = dp.TC0; + tc0.tccr0a.write(|w| { + w.wgm0().pwm_fast(); + w.com0b().match_clear(); + w + }); + tc0.tccr0b.write(|w| { + w.wgm02().set_bit(); + w.cs0().prescale_64(); + w + }); + tc0.ocr0a.write(|w| unsafe { w.bits(255) }); + // TODO: Use EEPROM + tc0.ocr0b.write(|w| unsafe { w.bits(0) }); + pins.pd5.into_output(); - loop { - led.toggle(); - delay.delay_ms(255_u8); - } + // Enable interrupts + unsafe { interrupt::enable() }; + + #[allow(clippy::empty_loop)] + loop {} } From 643e5f1af4e957aec29a9e102e89a95b33c09ac4 Mon Sep 17 00:00:00 2001 From: finga Date: Fri, 4 Mar 2022 13:11:45 +0100 Subject: [PATCH 04/37] fw-rust: Create a SPI interface for the display In order to, later on, send data to the display create a SPI interface. --- firmware/rust/src/main.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index 88c8d5f..bf1f68b 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -1,8 +1,13 @@ #![no_std] #![no_main] -use atmega_hal::{pins, Peripherals}; +use atmega_hal::{ + pins, + spi::{DataOrder, SerialClockRate, Settings, Spi}, + Peripherals, +}; use avr_device::interrupt; +use embedded_hal::spi::{Mode, Phase, Polarity}; use panic_halt as _; #[atmega_hal::entry] @@ -31,6 +36,23 @@ fn main() -> ! { tc0.ocr0b.write(|w| unsafe { w.bits(0) }); pins.pd5.into_output(); + // Init SPI + let (_, _) = 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, + }, + }, + ); + // Enable interrupts unsafe { interrupt::enable() }; From 817776d7358e48836532fc1477e01857fe76de97 Mon Sep 17 00:00:00 2001 From: finga Date: Fri, 4 Mar 2022 13:17:18 +0100 Subject: [PATCH 05/37] fw-rust: Initialize the LCD display Send commands via SPI to the LCD display to initialize and configure it. --- firmware/rust/src/main.rs | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index bf1f68b..38569cb 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -2,12 +2,18 @@ #![no_main] use atmega_hal::{ + clock::MHz8, + delay::Delay, pins, spi::{DataOrder, SerialClockRate, Settings, Spi}, Peripherals, }; use avr_device::interrupt; -use embedded_hal::spi::{Mode, Phase, Polarity}; +use embedded_hal::{ + blocking::delay::DelayMs, + spi::{FullDuplex, Mode, Phase, Polarity}, +}; +use nb::block; use panic_halt as _; #[atmega_hal::entry] @@ -15,9 +21,10 @@ fn main() -> ! { // Disable interrupts when initializing interrupt::disable(); - // Get peripherals and pins + // Get peripherals, pins and delay let dp = Peripherals::take().unwrap(); let pins = pins!(dp); + let mut delay = Delay::::new(); // Init display backlight let tc0 = dp.TC0; @@ -37,7 +44,7 @@ fn main() -> ! { pins.pd5.into_output(); // Init SPI - let (_, _) = Spi::new( + let (mut spi, _) = Spi::new( dp.SPI, pins.pb5.into_output(), pins.pb3.into_output(), @@ -53,6 +60,26 @@ fn main() -> ! { }, ); + // Init LCD + let _lcd_cd = pins.pb0.into_output(); + let mut lcd_rst = pins.pb1.into_output(); + // TODO: Test if delay is really needed + delay.delay_ms(1_u8); + lcd_rst.set_high(); + // TODO: Try to reduce delay to a minimum + delay.delay_ms(5_u8); + + block!(spi.send(0x40)).unwrap(); // (6) Set Scroll Line: Display start line 0 + block!(spi.send(0xA1)).unwrap(); // (13) Set SEG direction: SEG reverse + block!(spi.send(0xC0)).unwrap(); // (14) Set COM direction: Normal COM0 - COM63 + block!(spi.send(0xA6)).unwrap(); // (11) Set Inverse Display: Display inverse off + block!(spi.send(0xA2)).unwrap(); // (17) Set LCD Bias Ratio: Set Bias 1/9 (Duty 1/65) + block!(spi.send(0x2F)).unwrap(); // (5) Set Power Control: Booster, Regulator and Follower on + block!(spi.send(0x27)).unwrap(); // (8) Set VLCD Resistor Ratio: Set Contrast + block!(spi.send(0x81)).unwrap(); // (9) Set Electronic Volume: Set Contrast + block!(spi.send(0x08)).unwrap(); // (9) Set Electronic Volume: Set Contrast // TODO: Use EEPROM + block!(spi.send(0xAF)).unwrap(); // (12) Set Display Enable: Display on + // Enable interrupts unsafe { interrupt::enable() }; From c42b4595c76d3de513b2a36261e8ed77208643ae Mon Sep 17 00:00:00 2001 From: finga Date: Fri, 4 Mar 2022 13:20:56 +0100 Subject: [PATCH 06/37] fw-rust: Clear the screen After initializing the LCD display, clear the screen. --- firmware/rust/src/main.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index 38569cb..f7e5bda 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -61,7 +61,7 @@ fn main() -> ! { ); // Init LCD - let _lcd_cd = pins.pb0.into_output(); + let mut lcd_cd = pins.pb0.into_output(); let mut lcd_rst = pins.pb1.into_output(); // TODO: Test if delay is really needed delay.delay_ms(1_u8); @@ -80,6 +80,25 @@ fn main() -> ! { block!(spi.send(0x08)).unwrap(); // (9) Set Electronic Volume: Set Contrast // TODO: Use EEPROM block!(spi.send(0xAF)).unwrap(); // (12) Set Display Enable: Display on + // Clear screen + for page in 0..8 { + block!(spi.send(0x00)).unwrap(); + block!(spi.send(0x10)).unwrap(); + block!(spi.send(0xB0 + page)).unwrap(); + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + lcd_cd.set_high(); + + for _ in 0..102 { + block!(spi.send(0x00)).unwrap(); + } + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + lcd_cd.set_low(); + } + // Enable interrupts unsafe { interrupt::enable() }; From a688d4c5eda6fcbd0008febe0370f8674254af61 Mon Sep 17 00:00:00 2001 From: finga Date: Sat, 5 Mar 2022 21:55:07 +0100 Subject: [PATCH 07/37] fw-rust: Show splash screen Put assets for the splash screen into the data segment and print it. --- firmware/rust/src/main.rs | 83 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index f7e5bda..a5abd86 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -16,6 +16,51 @@ use embedded_hal::{ use nb::block; use panic_halt as _; +// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized +static 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 +static 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, 0x20, 0x60, 0x00, 0x00, 0xE0, 0xA0, 0xA0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x60, 0xE0, 0x00, 0x00, 0xE0, 0x20, 0x60, 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, + ], +]; + #[atmega_hal::entry] fn main() -> ! { // Disable interrupts when initializing @@ -99,6 +144,44 @@ fn main() -> ! { lcd_cd.set_low(); } + // Draw sacred chao + for (i, page) in SACRED_CHAO.iter().enumerate() { + block!(spi.send(0x0F)).unwrap(); + block!(spi.send(0x11)).unwrap(); + block!(spi.send(0xB1 + i as u8)).unwrap(); + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + lcd_cd.set_high(); + + for segment in page { + block!(spi.send(*segment)).unwrap(); + } + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + lcd_cd.set_low(); + } + + // Draw onders.org + for (i, page) in ONDERS_ORG.iter().enumerate() { + block!(spi.send(0x0A)).unwrap(); + block!(spi.send(0x11)).unwrap(); + block!(spi.send(0xB6 + i as u8)).unwrap(); + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + lcd_cd.set_high(); + + for segment in page { + block!(spi.send(*segment)).unwrap(); + } + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + lcd_cd.set_low(); + } + // Enable interrupts unsafe { interrupt::enable() }; From 8315560daa5833a3d2dd6be06d96e39b6a726fea Mon Sep 17 00:00:00 2001 From: finga Date: Sun, 6 Mar 2022 16:54:48 +0100 Subject: [PATCH 08/37] fw-rust: Use chip-generic interrupt utilities Instead of manually enabling and disabling interrupts use the chip-generic interrupt utilities. --- firmware/rust/src/main.rs | 202 +++++++++++++++++++------------------- 1 file changed, 99 insertions(+), 103 deletions(-) diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index a5abd86..431bdd9 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -63,127 +63,123 @@ static ONDERS_ORG: [[u8; 48]; 2] = [ #[atmega_hal::entry] fn main() -> ! { - // Disable interrupts when initializing - interrupt::disable(); + interrupt::free(|_cs| { + // Get peripherals, pins and delay + let dp = Peripherals::take().unwrap(); + let pins = pins!(dp); + let mut delay = Delay::::new(); - // Get peripherals, pins and delay - let dp = Peripherals::take().unwrap(); - let pins = pins!(dp); - let mut delay = Delay::::new(); + // Init display backlight + let tc0 = dp.TC0; + tc0.tccr0a.write(|w| { + w.wgm0().pwm_fast(); + w.com0b().match_clear(); + w + }); + tc0.tccr0b.write(|w| { + w.wgm02().set_bit(); + w.cs0().prescale_64(); + w + }); + tc0.ocr0a.write(|w| unsafe { w.bits(255) }); + // TODO: Use EEPROM + tc0.ocr0b.write(|w| unsafe { w.bits(0) }); + pins.pd5.into_output(); - // Init display backlight - let tc0 = dp.TC0; - tc0.tccr0a.write(|w| { - w.wgm0().pwm_fast(); - w.com0b().match_clear(); - w - }); - tc0.tccr0b.write(|w| { - w.wgm02().set_bit(); - w.cs0().prescale_64(); - w - }); - tc0.ocr0a.write(|w| unsafe { w.bits(255) }); - // TODO: Use EEPROM - tc0.ocr0b.write(|w| unsafe { w.bits(0) }); - pins.pd5.into_output(); - - // Init SPI - let (mut 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, + // Init SPI + let (mut 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, + }, }, - }, - ); + ); - // Init LCD - let mut lcd_cd = pins.pb0.into_output(); - let mut lcd_rst = pins.pb1.into_output(); - // TODO: Test if delay is really needed - delay.delay_ms(1_u8); - lcd_rst.set_high(); - // TODO: Try to reduce delay to a minimum - delay.delay_ms(5_u8); - - block!(spi.send(0x40)).unwrap(); // (6) Set Scroll Line: Display start line 0 - block!(spi.send(0xA1)).unwrap(); // (13) Set SEG direction: SEG reverse - block!(spi.send(0xC0)).unwrap(); // (14) Set COM direction: Normal COM0 - COM63 - block!(spi.send(0xA6)).unwrap(); // (11) Set Inverse Display: Display inverse off - block!(spi.send(0xA2)).unwrap(); // (17) Set LCD Bias Ratio: Set Bias 1/9 (Duty 1/65) - block!(spi.send(0x2F)).unwrap(); // (5) Set Power Control: Booster, Regulator and Follower on - block!(spi.send(0x27)).unwrap(); // (8) Set VLCD Resistor Ratio: Set Contrast - block!(spi.send(0x81)).unwrap(); // (9) Set Electronic Volume: Set Contrast - block!(spi.send(0x08)).unwrap(); // (9) Set Electronic Volume: Set Contrast // TODO: Use EEPROM - block!(spi.send(0xAF)).unwrap(); // (12) Set Display Enable: Display on - - // Clear screen - for page in 0..8 { - block!(spi.send(0x00)).unwrap(); - block!(spi.send(0x10)).unwrap(); - block!(spi.send(0xB0 + page)).unwrap(); - - // TODO: This delay fixes issues, try find a better solution + // Init LCD + let mut lcd_cd = pins.pb0.into_output(); + let mut lcd_rst = pins.pb1.into_output(); + // TODO: Test if delay is really needed delay.delay_ms(1_u8); - lcd_cd.set_high(); + lcd_rst.set_high(); + // TODO: Try to reduce delay to a minimum + delay.delay_ms(5_u8); - for _ in 0..102 { + block!(spi.send(0x40)).unwrap(); // (6) Set Scroll Line: Display start line 0 + block!(spi.send(0xA1)).unwrap(); // (13) Set SEG direction: SEG reverse + block!(spi.send(0xC0)).unwrap(); // (14) Set COM direction: Normal COM0 - COM63 + block!(spi.send(0xA6)).unwrap(); // (11) Set Inverse Display: Display inverse off + block!(spi.send(0xA2)).unwrap(); // (17) Set LCD Bias Ratio: Set Bias 1/9 (Duty 1/65) + block!(spi.send(0x2F)).unwrap(); // (5) Set Power Control: Booster, Regulator and Follower on + block!(spi.send(0x27)).unwrap(); // (8) Set VLCD Resistor Ratio: Set Contrast + block!(spi.send(0x81)).unwrap(); // (9) Set Electronic Volume: Set Contrast + block!(spi.send(0x08)).unwrap(); // (9) Set Electronic Volume: Set Contrast // TODO: Use EEPROM + block!(spi.send(0xAF)).unwrap(); // (12) Set Display Enable: Display on + + // Clear screen + for page in 0..8 { block!(spi.send(0x00)).unwrap(); + block!(spi.send(0x10)).unwrap(); + block!(spi.send(0xB0 + page)).unwrap(); + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + lcd_cd.set_high(); + + for _ in 0..102 { + block!(spi.send(0x00)).unwrap(); + } + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + lcd_cd.set_low(); } - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - lcd_cd.set_low(); - } + // Draw sacred chao + for (i, page) in SACRED_CHAO.iter().enumerate() { + block!(spi.send(0x0F)).unwrap(); + block!(spi.send(0x11)).unwrap(); + block!(spi.send(0xB1 + i as u8)).unwrap(); - // Draw sacred chao - for (i, page) in SACRED_CHAO.iter().enumerate() { - block!(spi.send(0x0F)).unwrap(); - block!(spi.send(0x11)).unwrap(); - block!(spi.send(0xB1 + i as u8)).unwrap(); + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + lcd_cd.set_high(); - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - lcd_cd.set_high(); + for segment in page { + block!(spi.send(*segment)).unwrap(); + } - for segment in page { - block!(spi.send(*segment)).unwrap(); + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + lcd_cd.set_low(); } - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - lcd_cd.set_low(); - } + // Draw onders.org + for (i, page) in ONDERS_ORG.iter().enumerate() { + block!(spi.send(0x0A)).unwrap(); + block!(spi.send(0x11)).unwrap(); + block!(spi.send(0xB6 + i as u8)).unwrap(); - // Draw onders.org - for (i, page) in ONDERS_ORG.iter().enumerate() { - block!(spi.send(0x0A)).unwrap(); - block!(spi.send(0x11)).unwrap(); - block!(spi.send(0xB6 + i as u8)).unwrap(); + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + lcd_cd.set_high(); - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - lcd_cd.set_high(); + for segment in page { + block!(spi.send(*segment)).unwrap(); + } - for segment in page { - block!(spi.send(*segment)).unwrap(); + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + lcd_cd.set_low(); } - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - lcd_cd.set_low(); - } - - // Enable interrupts - unsafe { interrupt::enable() }; + }); #[allow(clippy::empty_loop)] loop {} From 4bc3e84bddbae207cf9931f5482b0183f158745e Mon Sep 17 00:00:00 2001 From: finga Date: Sun, 6 Mar 2022 18:18:09 +0100 Subject: [PATCH 09/37] fw-rust, readme: Add CI config --- .woodpecker.yml | 28 ++++++++++++++++++++++++++++ README.md | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .woodpecker.yml diff --git a/.woodpecker.yml b/.woodpecker.yml new file mode 100644 index 0000000..943053b --- /dev/null +++ b/.woodpecker.yml @@ -0,0 +1,28 @@ +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 1b02b01..9cef8b6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Clock Generator +# Clock Generator [![status-badge](https://ci.onders.org/api/badges/finga/clock_generator/status.svg)](https://ci.onders.org/finga/clock_generator) A simple board operating another Si5351 clock generator board. From b34b2810dbee02cf5fd6136654f5ff2ce3028718 Mon Sep 17 00:00:00 2001 From: finga Date: Tue, 8 Mar 2022 14:42:04 +0100 Subject: [PATCH 10/37] fw-rust: Handle data residing in the EEPROM --- firmware/rust/Makefile.toml | 14 +++++++++++++- firmware/rust/src/main.rs | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/firmware/rust/Makefile.toml b/firmware/rust/Makefile.toml index 166b05a..59d36fa 100644 --- a/firmware/rust/Makefile.toml +++ b/firmware/rust/Makefile.toml @@ -12,7 +12,7 @@ command = "avr-size" args = ["--format=avr", "--mcu=${MCU}", "target/avr-atmega328p/debug/clock-generator.elf"] [tasks.copy_flash] -description = "Copy the 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"] @@ -23,6 +23,18 @@ 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" diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index 431bdd9..8b9338d 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -16,6 +16,14 @@ use embedded_hal::{ use nb::block; use panic_halt as _; +#[used] +#[link_section = ".eeprom"] +static CONTRAST: u8 = 8; + +#[used] +#[link_section = ".eeprom"] +static BACKLIGHT: u8 = 1; + // TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized static SACRED_CHAO: [[u8; 40]; 5] = [ [ @@ -64,11 +72,35 @@ static ONDERS_ORG: [[u8; 48]; 2] = [ #[atmega_hal::entry] fn main() -> ! { interrupt::free(|_cs| { - // Get peripherals, pins and delay + // Get peripherals, pins, eeprom and delay let dp = Peripherals::take().unwrap(); let pins = pins!(dp); + let eeprom = dp.EEPROM; let mut delay = Delay::::new(); + // TODO: Wait for completion of previous write + // Write address into eeprom address register + eeprom + .eear + .write(|w| unsafe { w.bits(&CONTRAST as *const u8 as u16) }); + // Start to read from eeprom by setting EERE + eeprom.eecr.write(|w| w.eere().set_bit()); + // Return data from eeprom data register + let _contrast = eeprom.eedr.read().bits(); + + // TODO: Remove delay when waiting for completion of revious write is done + delay.delay_ms(1_u8); + + // TODO: Wait for completion of previous write + // Write address into eeprom address register + eeprom + .eear + .write(|w| unsafe { w.bits(&BACKLIGHT as *const u8 as u16) }); + // Start to read from eeprom by setting EERE + eeprom.eecr.write(|w| w.eere().set_bit()); + // Return data from eeprom data register + let _backlight = eeprom.eedr.read().bits(); + // Init display backlight let tc0 = dp.TC0; tc0.tccr0a.write(|w| { From 8b9d21a0123c64856b3460eac9b80363d34a83de Mon Sep 17 00:00:00 2001 From: finga Date: Tue, 8 Mar 2022 19:05:40 +0100 Subject: [PATCH 11/37] fw-rust: Start to use the avr-eeprom crate Start to use the avr-eeprom crate to handle values residing inside the EEPROM. --- firmware/rust/Cargo.lock | 6 ++++++ firmware/rust/Cargo.toml | 1 + firmware/rust/src/main.rs | 16 +++++++--------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/firmware/rust/Cargo.lock b/firmware/rust/Cargo.lock index c856d14..ecc84d6 100644 --- a/firmware/rust/Cargo.lock +++ b/firmware/rust/Cargo.lock @@ -32,6 +32,11 @@ dependencies = [ "syn", ] +[[package]] +name = "avr-eeprom" +version = "0.1.0-dev" +source = "git+https://git.onders.org/finga/avr-eeprom-rs.git?branch=main#d7653567fb2f7ca7bf5100a44c233015a6e03e51" + [[package]] name = "avr-hal-generic" version = "0.1.0" @@ -67,6 +72,7 @@ version = "0.1.0-dev" dependencies = [ "atmega-hal", "avr-device", + "avr-eeprom", "embedded-hal", "nb 1.0.0", "panic-halt", diff --git a/firmware/rust/Cargo.toml b/firmware/rust/Cargo.toml index aee0597..9a1d6e9 100644 --- a/firmware/rust/Cargo.toml +++ b/firmware/rust/Cargo.toml @@ -15,6 +15,7 @@ panic-halt = "0.2" nb = "1.0" embedded-hal = "0.2" avr-device = { version = "0.3", features = ["atmega328p"] } +avr-eeprom = { git = "https://git.onders.org/finga/avr-eeprom-rs.git", branch = "main" } [dependencies.atmega-hal] git = "https://github.com/rahix/avr-hal" diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index 8b9338d..87042c3 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -9,6 +9,7 @@ use atmega_hal::{ Peripherals, }; use avr_device::interrupt; +use avr_eeprom::eeprom; use embedded_hal::{ blocking::delay::DelayMs, spi::{FullDuplex, Mode, Phase, Polarity}, @@ -16,13 +17,10 @@ use embedded_hal::{ use nb::block; use panic_halt as _; -#[used] -#[link_section = ".eeprom"] -static CONTRAST: u8 = 8; - -#[used] -#[link_section = ".eeprom"] -static BACKLIGHT: u8 = 1; +eeprom! { + static eeprom CONTRAST: u8 = 8; + static eeprom BACKLIGHT: u8 = 1; +} // TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized static SACRED_CHAO: [[u8; 40]; 5] = [ @@ -82,7 +80,7 @@ fn main() -> ! { // Write address into eeprom address register eeprom .eear - .write(|w| unsafe { w.bits(&CONTRAST as *const u8 as u16) }); + .write(|w| unsafe { w.bits(CONTRAST.ptr() as u16) }); // Start to read from eeprom by setting EERE eeprom.eecr.write(|w| w.eere().set_bit()); // Return data from eeprom data register @@ -95,7 +93,7 @@ fn main() -> ! { // Write address into eeprom address register eeprom .eear - .write(|w| unsafe { w.bits(&BACKLIGHT as *const u8 as u16) }); + .write(|w| unsafe { w.bits(BACKLIGHT.ptr() as u16) }); // Start to read from eeprom by setting EERE eeprom.eecr.write(|w| w.eere().set_bit()); // Return data from eeprom data register From 36be6d27bc555f78c5d4b2a89af33c108b5ea10b Mon Sep 17 00:00:00 2001 From: finga Date: Wed, 9 Mar 2022 19:42:10 +0100 Subject: [PATCH 12/37] fw-rust: Create a function to clear the screen Create a utility function to clear the complete screen. --- firmware/rust/src/main.rs | 41 +++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index 87042c3..af812a2 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -5,6 +5,7 @@ use atmega_hal::{ clock::MHz8, delay::Delay, pins, + port::{mode::Output, Pin, PB0}, spi::{DataOrder, SerialClockRate, Settings, Spi}, Peripherals, }; @@ -67,6 +68,28 @@ static ONDERS_ORG: [[u8; 48]; 2] = [ ], ]; +fn clear_screen(spi: &mut Spi, cd: &mut Pin) { + let mut delay = Delay::::new(); + + for page in 0..8 { + block!(spi.send(0x00)).unwrap(); + block!(spi.send(0x10)).unwrap(); + block!(spi.send(0xB0 + page)).unwrap(); + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + cd.set_high(); + + for _ in 0..102 { + block!(spi.send(0x00)).unwrap(); + } + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + cd.set_low(); + } +} + #[atmega_hal::entry] fn main() -> ! { interrupt::free(|_cs| { @@ -154,23 +177,7 @@ fn main() -> ! { block!(spi.send(0xAF)).unwrap(); // (12) Set Display Enable: Display on // Clear screen - for page in 0..8 { - block!(spi.send(0x00)).unwrap(); - block!(spi.send(0x10)).unwrap(); - block!(spi.send(0xB0 + page)).unwrap(); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - lcd_cd.set_high(); - - for _ in 0..102 { - block!(spi.send(0x00)).unwrap(); - } - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - lcd_cd.set_low(); - } + clear_screen(&mut spi, &mut lcd_cd); // Draw sacred chao for (i, page) in SACRED_CHAO.iter().enumerate() { From 9425093391d63fd2ff0d9c2e5ef230d1d3eb0170 Mon Sep 17 00:00:00 2001 From: finga Date: Fri, 11 Mar 2022 02:30:13 +0100 Subject: [PATCH 13/37] fw-rust: Draw the splash screen via function Make the splash screen assets `const` and draw the splash screen with the new `draw_splash()` function. --- firmware/rust/src/main.rs | 88 ++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index af812a2..8f8feff 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -24,7 +24,7 @@ eeprom! { } // TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -static SACRED_CHAO: [[u8; 40]; 5] = [ +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, @@ -53,7 +53,7 @@ static SACRED_CHAO: [[u8; 40]; 5] = [ ]; // TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -static ONDERS_ORG: [[u8; 48]; 2] = [ +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, 0x20, 0x60, 0x00, 0x00, 0xE0, 0xA0, 0xA0, 0x00, 0x00, @@ -90,6 +90,48 @@ fn clear_screen(spi: &mut Spi, cd: &mut Pin) { } } +fn draw_splash(spi: &mut Spi, cd: &mut Pin) { + clear_screen(spi, cd); + + let mut delay = Delay::::new(); + + for (i, page) in SACRED_CHAO.iter().enumerate() { + block!(spi.send(0x0F)).unwrap(); + block!(spi.send(0x11)).unwrap(); + block!(spi.send(0xB1 + i as u8)).unwrap(); + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + cd.set_high(); + + for segment in page { + block!(spi.send(*segment)).unwrap(); + } + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + cd.set_low(); + } + + for (i, page) in ONDERS_ORG.iter().enumerate() { + block!(spi.send(0x0A)).unwrap(); + block!(spi.send(0x11)).unwrap(); + block!(spi.send(0xB6 + i as u8)).unwrap(); + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + cd.set_high(); + + for segment in page { + block!(spi.send(*segment)).unwrap(); + } + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + cd.set_low(); + } +} + #[atmega_hal::entry] fn main() -> ! { interrupt::free(|_cs| { @@ -176,46 +218,8 @@ fn main() -> ! { block!(spi.send(0x08)).unwrap(); // (9) Set Electronic Volume: Set Contrast // TODO: Use EEPROM block!(spi.send(0xAF)).unwrap(); // (12) Set Display Enable: Display on - // Clear screen - clear_screen(&mut spi, &mut lcd_cd); - - // Draw sacred chao - for (i, page) in SACRED_CHAO.iter().enumerate() { - block!(spi.send(0x0F)).unwrap(); - block!(spi.send(0x11)).unwrap(); - block!(spi.send(0xB1 + i as u8)).unwrap(); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - lcd_cd.set_high(); - - for segment in page { - block!(spi.send(*segment)).unwrap(); - } - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - lcd_cd.set_low(); - } - - // Draw onders.org - for (i, page) in ONDERS_ORG.iter().enumerate() { - block!(spi.send(0x0A)).unwrap(); - block!(spi.send(0x11)).unwrap(); - block!(spi.send(0xB6 + i as u8)).unwrap(); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - lcd_cd.set_high(); - - for segment in page { - block!(spi.send(*segment)).unwrap(); - } - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - lcd_cd.set_low(); - } + // Draw splash screen + draw_splash(&mut spi, &mut lcd_cd); }); #[allow(clippy::empty_loop)] From 518f113cc07eb7ff2e2c0a5d1045107fe57b0600 Mon Sep 17 00:00:00 2001 From: finga Date: Sat, 12 Mar 2022 17:20:53 +0100 Subject: [PATCH 14/37] fw-rust: Use avr-eeprom create to read from EEPROM Use the `avr-eeprom` crate when reading from EEPROM. --- firmware/rust/Cargo.lock | 6 +++++- firmware/rust/src/main.rs | 34 +++++++--------------------------- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/firmware/rust/Cargo.lock b/firmware/rust/Cargo.lock index ecc84d6..cb6f692 100644 --- a/firmware/rust/Cargo.lock +++ b/firmware/rust/Cargo.lock @@ -35,7 +35,11 @@ dependencies = [ [[package]] name = "avr-eeprom" version = "0.1.0-dev" -source = "git+https://git.onders.org/finga/avr-eeprom-rs.git?branch=main#d7653567fb2f7ca7bf5100a44c233015a6e03e51" +source = "git+https://git.onders.org/finga/avr-eeprom-rs.git?branch=main#0177da7268e91fa3c0cf545150589b2e33aa2101" +dependencies = [ + "avr-device", + "nb 1.0.0", +] [[package]] name = "avr-hal-generic" diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index 8f8feff..d16b397 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -10,7 +10,7 @@ use atmega_hal::{ Peripherals, }; use avr_device::interrupt; -use avr_eeprom::eeprom; +use avr_eeprom::{eeprom, Eeprom}; use embedded_hal::{ blocking::delay::DelayMs, spi::{FullDuplex, Mode, Phase, Polarity}, @@ -138,31 +138,12 @@ fn main() -> ! { // Get peripherals, pins, eeprom and delay let dp = Peripherals::take().unwrap(); let pins = pins!(dp); - let eeprom = dp.EEPROM; + let eeprom = Eeprom::new(dp.EEPROM); let mut delay = Delay::::new(); - // TODO: Wait for completion of previous write - // Write address into eeprom address register - eeprom - .eear - .write(|w| unsafe { w.bits(CONTRAST.ptr() as u16) }); - // Start to read from eeprom by setting EERE - eeprom.eecr.write(|w| w.eere().set_bit()); - // Return data from eeprom data register - let _contrast = eeprom.eedr.read().bits(); - - // TODO: Remove delay when waiting for completion of revious write is done - delay.delay_ms(1_u8); - - // TODO: Wait for completion of previous write - // Write address into eeprom address register - eeprom - .eear - .write(|w| unsafe { w.bits(BACKLIGHT.ptr() as u16) }); - // Start to read from eeprom by setting EERE - eeprom.eecr.write(|w| w.eere().set_bit()); - // Return data from eeprom data register - let _backlight = eeprom.eedr.read().bits(); + // Get contrast and backlight from EEPROM + let contrast = eeprom.read_value(&CONTRAST); + let backlight = eeprom.read_value(&BACKLIGHT); // Init display backlight let tc0 = dp.TC0; @@ -177,8 +158,7 @@ fn main() -> ! { w }); tc0.ocr0a.write(|w| unsafe { w.bits(255) }); - // TODO: Use EEPROM - tc0.ocr0b.write(|w| unsafe { w.bits(0) }); + tc0.ocr0b.write(|w| unsafe { w.bits(backlight) }); pins.pd5.into_output(); // Init SPI @@ -215,7 +195,7 @@ fn main() -> ! { block!(spi.send(0x2F)).unwrap(); // (5) Set Power Control: Booster, Regulator and Follower on block!(spi.send(0x27)).unwrap(); // (8) Set VLCD Resistor Ratio: Set Contrast block!(spi.send(0x81)).unwrap(); // (9) Set Electronic Volume: Set Contrast - block!(spi.send(0x08)).unwrap(); // (9) Set Electronic Volume: Set Contrast // TODO: Use EEPROM + block!(spi.send(contrast)).unwrap(); // (9) Set Electronic Volume: Set Contrast block!(spi.send(0xAF)).unwrap(); // (12) Set Display Enable: Display on // Draw splash screen From de8f789e63e7092889732fb389e0a7c5c69706e5 Mon Sep 17 00:00:00 2001 From: finga Date: Sat, 12 Mar 2022 18:11:58 +0100 Subject: [PATCH 15/37] fw-rust: Create `Lcd` struct to handle the screen To improve screen handling create the `Lcd` struct and implement some first helper functions. --- firmware/rust/src/main.rs | 174 ++++++++++++++++++++++++-------------- 1 file changed, 111 insertions(+), 63 deletions(-) diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index d16b397..ef10494 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -5,7 +5,7 @@ use atmega_hal::{ clock::MHz8, delay::Delay, pins, - port::{mode::Output, Pin, PB0}, + port::{mode::Output, Pin, PB0, PB1}, spi::{DataOrder, SerialClockRate, Settings, Spi}, Peripherals, }; @@ -68,78 +68,134 @@ const ONDERS_ORG: [[u8; 48]; 2] = [ ], ]; -fn clear_screen(spi: &mut Spi, cd: &mut Pin) { - let mut delay = Delay::::new(); +pub struct Lcd { + spi: Spi, + cd: Pin, + rst: Pin, +} - for page in 0..8 { - block!(spi.send(0x00)).unwrap(); - block!(spi.send(0x10)).unwrap(); - block!(spi.send(0xB0 + page)).unwrap(); +impl Lcd { + fn init(&mut self, contrast: u8) { + 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); + + block!(self.spi.send(0x40)).unwrap(); // (6) Set Scroll Line: Display start line 0 + block!(self.spi.send(0xA1)).unwrap(); // (13) Set SEG direction: SEG reverse + block!(self.spi.send(0xC0)).unwrap(); // (14) Set COM direction: Normal COM0 - COM63 + // block!(spi.send(0xA4)).unwrap(); // (10) Set All Pixel On: Disable -> Set All Pixel to ON */ + block!(self.spi.send(0xA6)).unwrap(); // (11) Set Inverse Display: Display inverse off + block!(self.spi.send(0xA2)).unwrap(); // (17) Set LCD Bias Ratio: Set Bias 1/9 (Duty 1/65) + block!(self.spi.send(0x2F)).unwrap(); // (5) Set Power Control: Booster, Regulator and Follower on + block!(self.spi.send(0x27)).unwrap(); // (8) Set VLCD Resistor Ratio: Set Contrast + // block!(spi.send(0xEE)).unwrap(); // (18) Reset Cursor Update Mode + block!(self.spi.send(0x81)).unwrap(); // (9) Set Electronic Volume: Set Contrast + block!(self.spi.send(contrast)).unwrap(); // (9) Set Electronic Volume: Set Contrast + // block!(spi.send(0xFA)).unwrap(); // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C + // block!(spi.send(0x90)).unwrap(); // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C + block!(self.spi.send(0xAF)).unwrap(); // (12) Set Display Enable: Display on // TODO: This delay fixes issues, try find a better solution delay.delay_ms(1_u8); - cd.set_high(); + } - for _ in 0..102 { - block!(spi.send(0x00)).unwrap(); + 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 clear(&mut self) { + let mut delay = Delay::::new(); + + for page in 0..8 { + self.move_cursor(0, page as u8); + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + self.cd.set_high(); + + for _ in 0..102 { + 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(); } + } - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - cd.set_low(); + fn screen(&mut self, screen: &Screens) { + self.clear(); + + match screen { + Screens::Splash(splash) => splash.draw(self), + } } } -fn draw_splash(spi: &mut Spi, cd: &mut Pin) { - clear_screen(spi, cd); +pub trait Draw { + fn draw(&self, lcd: &mut Lcd); +} - let mut delay = Delay::::new(); +struct SplashScreen; - for (i, page) in SACRED_CHAO.iter().enumerate() { - block!(spi.send(0x0F)).unwrap(); - block!(spi.send(0x11)).unwrap(); - block!(spi.send(0xB1 + i as u8)).unwrap(); +impl Draw for SplashScreen { + fn draw(&self, lcd: &mut Lcd) { + let mut delay = Delay::::new(); - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - cd.set_high(); + for (i, page) in SACRED_CHAO.iter().enumerate() { + lcd.move_cursor(31, 1 + i as u8); - for segment in page { - block!(spi.send(*segment)).unwrap(); + // 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(); } - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - cd.set_low(); - } + for (i, page) in ONDERS_ORG.iter().enumerate() { + lcd.move_cursor(27, 6 + i as u8); - for (i, page) in ONDERS_ORG.iter().enumerate() { - block!(spi.send(0x0A)).unwrap(); - block!(spi.send(0x11)).unwrap(); - block!(spi.send(0xB6 + i as u8)).unwrap(); + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + lcd.cd.set_high(); - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - cd.set_high(); + for segment in page { + block!(lcd.spi.send(*segment)).unwrap(); + } - for segment in page { - block!(spi.send(*segment)).unwrap(); + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + lcd.cd.set_low(); } - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - cd.set_low(); } } +enum Screens { + Splash(SplashScreen), +} + #[atmega_hal::entry] fn main() -> ! { interrupt::free(|_cs| { - // Get peripherals, pins, eeprom and delay + // Get peripherals, pins and eeprom let dp = Peripherals::take().unwrap(); let pins = pins!(dp); let eeprom = Eeprom::new(dp.EEPROM); - let mut delay = Delay::::new(); // Get contrast and backlight from EEPROM let contrast = eeprom.read_value(&CONTRAST); @@ -162,7 +218,7 @@ fn main() -> ! { pins.pd5.into_output(); // Init SPI - let (mut spi, _) = Spi::new( + let (spi, _) = Spi::new( dp.SPI, pins.pb5.into_output(), pins.pb3.into_output(), @@ -179,27 +235,19 @@ fn main() -> ! { ); // Init LCD - let mut lcd_cd = pins.pb0.into_output(); - let mut lcd_rst = pins.pb1.into_output(); - // TODO: Test if delay is really needed - delay.delay_ms(1_u8); - lcd_rst.set_high(); - // TODO: Try to reduce delay to a minimum - delay.delay_ms(5_u8); + let mut lcd = Lcd { + spi, + cd: pins.pb0.into_output(), + rst: pins.pb1.into_output(), + }; + lcd.init(contrast); - block!(spi.send(0x40)).unwrap(); // (6) Set Scroll Line: Display start line 0 - block!(spi.send(0xA1)).unwrap(); // (13) Set SEG direction: SEG reverse - block!(spi.send(0xC0)).unwrap(); // (14) Set COM direction: Normal COM0 - COM63 - block!(spi.send(0xA6)).unwrap(); // (11) Set Inverse Display: Display inverse off - block!(spi.send(0xA2)).unwrap(); // (17) Set LCD Bias Ratio: Set Bias 1/9 (Duty 1/65) - block!(spi.send(0x2F)).unwrap(); // (5) Set Power Control: Booster, Regulator and Follower on - block!(spi.send(0x27)).unwrap(); // (8) Set VLCD Resistor Ratio: Set Contrast - block!(spi.send(0x81)).unwrap(); // (9) Set Electronic Volume: Set Contrast - block!(spi.send(contrast)).unwrap(); // (9) Set Electronic Volume: Set Contrast - block!(spi.send(0xAF)).unwrap(); // (12) Set Display Enable: Display on + // Create screen + // TODO: This should use volatile and probably be somehow globally accessible + let screen = Screens::Splash(SplashScreen); // Draw splash screen - draw_splash(&mut spi, &mut lcd_cd); + lcd.screen(&screen); }); #[allow(clippy::empty_loop)] From 1688bb868ebeda51d13b242ae2c50a8133f1f346 Mon Sep 17 00:00:00 2001 From: finga Date: Sat, 12 Mar 2022 23:53:13 +0100 Subject: [PATCH 16/37] fw-rust: Add symbols and screens Add several symbols and a symbol table in an ascii order. Add the `print()` and `print_inverted()` functions to be able to draw strings, "black on white" as well as "white on black" onto the display. Add a `HomeScreen` which also saves the state of which selection is active. To save the screen state and also make it accessible globally use a `RefCell` contained in a `Mutex`. Also update the build dependencies. --- firmware/rust/Cargo.lock | 2 +- firmware/rust/src/main.rs | 330 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 319 insertions(+), 13 deletions(-) diff --git a/firmware/rust/Cargo.lock b/firmware/rust/Cargo.lock index cb6f692..04ba3b3 100644 --- a/firmware/rust/Cargo.lock +++ b/firmware/rust/Cargo.lock @@ -35,7 +35,7 @@ dependencies = [ [[package]] name = "avr-eeprom" version = "0.1.0-dev" -source = "git+https://git.onders.org/finga/avr-eeprom-rs.git?branch=main#0177da7268e91fa3c0cf545150589b2e33aa2101" +source = "git+https://git.onders.org/finga/avr-eeprom-rs.git?branch=main#96397b5fbbaaa8531842db301127c1346f6e05f9" dependencies = [ "avr-device", "nb 1.0.0", diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index ef10494..159e421 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -9,8 +9,9 @@ use atmega_hal::{ spi::{DataOrder, SerialClockRate, Settings, Spi}, Peripherals, }; -use avr_device::interrupt; +use avr_device::interrupt::{self, Mutex}; use avr_eeprom::{eeprom, Eeprom}; +use core::cell::RefCell; use embedded_hal::{ blocking::delay::DelayMs, spi::{FullDuplex, Mode, Phase, Polarity}, @@ -68,6 +69,166 @@ const ONDERS_ORG: [[u8; 48]; 2] = [ ], ]; +// 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_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_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_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_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_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 +const SYMBOL_TABLE: [&[&[u8]; 2]; 48] = [ + &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_INVALID, // ':' + &SYM_INVALID, // ';' + &SYM_INVALID, // '<' + &SYM_INVALID, // '=' + &SYM_INVALID, // '>' + &SYM_INVALID, // '?' + &SYM_INVALID, // '@' + &SYM_INVALID, // 'A' + &SYM_INVALID, // 'B' + &SYM_C, // 'C' + &SYM_INVALID, // 'D' + &SYM_E, // 'E' + &SYM_INVALID, // 'F' + &SYM_INVALID, // 'G' + &SYM_H, // 'H' + &SYM_INVALID, // 'I' + &SYM_INVALID, // 'J' + &SYM_INVALID, // 'K' + &SYM_INVALID, // 'L' + &SYM_INVALID, // 'M' + &SYM_INVALID, // 'N' + &SYM_INVALID, // 'O' + &SYM_P, // 'P' + &SYM_INVALID, // 'Q' + &SYM_INVALID, // '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_INVALID, // '_' +]; + pub struct Lcd { spi: Spi, cd: Pin, @@ -132,12 +293,69 @@ impl Lcd { } } - fn screen(&mut self, screen: &Screens) { + 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 - 48][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(); + } + } + + 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 - 48][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(); + } + } + + fn screen(&mut self) { self.clear(); - match screen { - Screens::Splash(splash) => splash.draw(self), - } + interrupt::free(|cs| match &*SCREEN.borrow(cs).borrow() { + Screens::Splash(splash) => { + splash.draw(self); + } + Screens::Home(home) => { + home.draw(self); + } + }) } } @@ -185,17 +403,64 @@ impl Draw for SplashScreen { } } +enum HomeSelection { + Ch1, + Ch2, + Ch3, + Setup, +} + +struct HomeScreen { + active: HomeSelection, +} + +impl Draw for HomeScreen { + fn draw(&self, lcd: &mut Lcd) { + match &self.active { + HomeSelection::Ch1 => { + lcd.print_inverted(0, 0, "CH1"); + lcd.print(0, 2, "CH2"); + lcd.print(0, 4, "CH3"); + lcd.print(33, 6, "SETUP"); + } + HomeSelection::Ch2 => { + lcd.print(0, 0, "CH1"); + lcd.print_inverted(0, 2, "CH2"); + lcd.print(0, 4, "CH3"); + lcd.print(33, 6, "SETUP"); + } + HomeSelection::Ch3 => { + lcd.print(0, 0, "CH1"); + lcd.print(0, 2, "CH2"); + lcd.print_inverted(0, 4, "CH3"); + lcd.print(33, 6, "SETUP"); + } + HomeSelection::Setup => { + lcd.print(0, 0, "CH1"); + lcd.print(0, 2, "CH2"); + lcd.print(0, 4, "CH3"); + lcd.print_inverted(33, 6, "SETUP"); + } + } + } +} + enum Screens { Splash(SplashScreen), + Home(HomeScreen), } +// TODO: Investigate if this should also be volatile +static SCREEN: Mutex> = Mutex::new(RefCell::new(Screens::Splash(SplashScreen))); + #[atmega_hal::entry] fn main() -> ! { - interrupt::free(|_cs| { - // Get peripherals, pins and eeprom + interrupt::free(|cs| { + // Get peripherals, pins, eeprom and delay let dp = Peripherals::take().unwrap(); let pins = pins!(dp); let eeprom = Eeprom::new(dp.EEPROM); + let mut delay = Delay::::new(); // Get contrast and backlight from EEPROM let contrast = eeprom.read_value(&CONTRAST); @@ -242,12 +507,53 @@ fn main() -> ! { }; lcd.init(contrast); - // Create screen - // TODO: This should use volatile and probably be somehow globally accessible - let screen = Screens::Splash(SplashScreen); - // Draw splash screen - lcd.screen(&screen); + lcd.screen(); + + // Show splash screen for a moment + delay.delay_ms(2000_u16); + + // Set home screen + let screen = SCREEN.borrow(cs); + *screen.borrow_mut() = Screens::Home(HomeScreen { + active: HomeSelection::Ch1, + }); + + // Draw screen + lcd.screen(); + + // Show splash screen for a moment + delay.delay_ms(1000_u16); + + // Demo select CH2 + *screen.borrow_mut() = Screens::Home(HomeScreen { + active: HomeSelection::Ch2, + }); + + // Draw screen + lcd.screen(); + + // Show splash screen for a moment + delay.delay_ms(1000_u16); + + // Demo select CH3 + *screen.borrow_mut() = Screens::Home(HomeScreen { + active: HomeSelection::Ch3, + }); + + // Draw screen + lcd.screen(); + + // Show splash screen for a moment + delay.delay_ms(1000_u16); + + // Demo select SETUP + *screen.borrow_mut() = Screens::Home(HomeScreen { + active: HomeSelection::Setup, + }); + + // Draw screen + lcd.screen(); }); #[allow(clippy::empty_loop)] From 4a33986e8dfa692f1f561c6c5d2c6e0176887f7f Mon Sep 17 00:00:00 2001 From: finga Date: Mon, 14 Mar 2022 22:39:12 +0100 Subject: [PATCH 17/37] fw-rust: Improve URL asset --- firmware/rust/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index 159e421..0a7ee59 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -57,8 +57,8 @@ const SACRED_CHAO: [[u8; 40]; 5] = [ 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, 0x20, 0x60, 0x00, 0x00, 0xE0, 0xA0, 0xA0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x60, 0xE0, 0x00, 0x00, 0xE0, 0x20, 0x60, 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, ], [ From 2ec8d1aeb92349efe8c02946e5e0179ca178b5f6 Mon Sep 17 00:00:00 2001 From: finga Date: Mon, 14 Mar 2022 22:51:35 +0100 Subject: [PATCH 18/37] fw-rust: Handle screens differently, use encoder Handle the screens differently and implement rotary encoder support. --- firmware/rust/src/main.rs | 270 ++++++++++++++++++++++-------------- firmware/rust/src/rotary.rs | 51 +++++++ 2 files changed, 219 insertions(+), 102 deletions(-) create mode 100644 firmware/rust/src/rotary.rs diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index 0a7ee59..1bb4ff1 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -1,3 +1,4 @@ +#![feature(abi_avr_interrupt)] #![no_std] #![no_main] @@ -9,9 +10,9 @@ use atmega_hal::{ spi::{DataOrder, SerialClockRate, Settings, Spi}, Peripherals, }; -use avr_device::interrupt::{self, Mutex}; +use avr_device::interrupt; use avr_eeprom::{eeprom, Eeprom}; -use core::cell::RefCell; +use core::sync::atomic::{AtomicBool, Ordering}; use embedded_hal::{ blocking::delay::DelayMs, spi::{FullDuplex, Mode, Phase, Polarity}, @@ -19,6 +20,9 @@ use embedded_hal::{ use nb::block; use panic_halt as _; +mod rotary; +use rotary::{Direction, Rotary}; + eeprom! { static eeprom CONTRAST: u8 = 8; static eeprom BACKLIGHT: u8 = 1; @@ -345,20 +349,27 @@ impl Lcd { } } - fn screen(&mut self) { - self.clear(); + fn draw(&mut self, screen: &Screens) { + interrupt::free(|_cs| { + self.clear(); - interrupt::free(|cs| match &*SCREEN.borrow(cs).borrow() { - Screens::Splash(splash) => { - splash.draw(self); + match screen { + Screens::Splash(splash) => { + splash.draw(self); + } + Screens::Home(home) => { + home.draw(self); + } } - Screens::Home(home) => { - home.draw(self); - } - }) + }); } } +enum Input { + Next, + Previous, +} + pub trait Draw { fn draw(&self, lcd: &mut Lcd); } @@ -414,6 +425,41 @@ struct HomeScreen { active: HomeSelection, } +impl HomeScreen { + fn input(&self, input: &Input) -> Screens { + match input { + Input::Next => match self.active { + HomeSelection::Ch1 => Screens::Home(HomeScreen { + active: HomeSelection::Ch2, + }), + HomeSelection::Ch2 => Screens::Home(HomeScreen { + active: HomeSelection::Ch3, + }), + HomeSelection::Ch3 => Screens::Home(HomeScreen { + active: HomeSelection::Setup, + }), + HomeSelection::Setup => Screens::Home(HomeScreen { + active: HomeSelection::Ch1, + }), + }, + Input::Previous => match self.active { + HomeSelection::Ch1 => Screens::Home(HomeScreen { + active: HomeSelection::Setup, + }), + HomeSelection::Ch2 => Screens::Home(HomeScreen { + active: HomeSelection::Ch1, + }), + HomeSelection::Ch3 => Screens::Home(HomeScreen { + active: HomeSelection::Ch2, + }), + HomeSelection::Setup => Screens::Home(HomeScreen { + active: HomeSelection::Ch3, + }), + }, + } + } +} + impl Draw for HomeScreen { fn draw(&self, lcd: &mut Lcd) { match &self.active { @@ -450,112 +496,132 @@ enum Screens { Home(HomeScreen), } -// TODO: Investigate if this should also be volatile -static SCREEN: Mutex> = Mutex::new(RefCell::new(Screens::Splash(SplashScreen))); +impl Screens { + fn input(&mut self, input: &Input) { + match self { + Screens::Splash(_) => {} + Screens::Home(home) => *self = home.input(input), + } + } +} + +static UPDATE_ENCODER: AtomicBool = AtomicBool::new(false); + +#[interrupt(atmega328p)] +#[allow(non_snake_case)] +fn PCINT0() { + interrupt::free(|_cs| { + UPDATE_ENCODER.store(true, Ordering::SeqCst); + }) +} #[atmega_hal::entry] fn main() -> ! { - interrupt::free(|cs| { - // Get peripherals, pins, eeprom and delay - let dp = Peripherals::take().unwrap(); - let pins = pins!(dp); - let eeprom = Eeprom::new(dp.EEPROM); - let mut delay = Delay::::new(); + // Get peripherals, pins, eeprom and delay + let dp = Peripherals::take().unwrap(); + let pins = pins!(dp); + let eeprom = Eeprom::new(dp.EEPROM); + let mut delay = Delay::::new(); - // Get contrast and backlight from EEPROM - let contrast = eeprom.read_value(&CONTRAST); - let backlight = eeprom.read_value(&BACKLIGHT); + // Get contrast and backlight from EEPROM + let contrast = eeprom.read_value(&CONTRAST); + let backlight = eeprom.read_value(&BACKLIGHT); - // Init display backlight - let tc0 = dp.TC0; - tc0.tccr0a.write(|w| { - w.wgm0().pwm_fast(); - w.com0b().match_clear(); - w - }); - tc0.tccr0b.write(|w| { - w.wgm02().set_bit(); - w.cs0().prescale_64(); - w - }); - tc0.ocr0a.write(|w| unsafe { w.bits(255) }); - tc0.ocr0b.write(|w| unsafe { w.bits(backlight) }); - pins.pd5.into_output(); + // Init display backlight + let tc0 = dp.TC0; + tc0.tccr0a.write(|w| { + w.wgm0().pwm_fast(); + w.com0b().match_clear(); + w + }); + tc0.tccr0b.write(|w| { + w.wgm02().set_bit(); + w.cs0().prescale_64(); + w + }); + tc0.ocr0a.write(|w| unsafe { w.bits(255) }); + tc0.ocr0b.write(|w| unsafe { w.bits(backlight) }); + pins.pd5.into_output(); - // 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, - }, + // 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, }, - ); + }, + ); - // Init LCD - let mut lcd = Lcd { - spi, - cd: pins.pb0.into_output(), - rst: pins.pb1.into_output(), - }; - lcd.init(contrast); + // Init LCD + let mut lcd = Lcd { + spi, + cd: pins.pb0.into_output(), + rst: pins.pb1.into_output(), + }; + lcd.init(contrast); - // Draw splash screen - lcd.screen(); + // Init encoder + let mut encoder = Rotary::new(pins.pb6.into_pull_up_input(), pins.pb7.into_pull_up_input()); + encoder.update(); - // Show splash screen for a moment - delay.delay_ms(2000_u16); + // Enable interrupts for encoder // TODO: and switch + dp.EXINT.pcicr.write(|w| w.pcie().bits(0b0000_0001)); + dp.EXINT.pcmsk0.write(|w| w.pcint().bits(0b1100_0000)); - // Set home screen - let screen = SCREEN.borrow(cs); - *screen.borrow_mut() = Screens::Home(HomeScreen { - active: HomeSelection::Ch1, - }); + // Create screen + let mut screen = Screens::Splash(SplashScreen); - // Draw screen - lcd.screen(); + // Draw splash screen + lcd.draw(&screen); - // Show splash screen for a moment - delay.delay_ms(1000_u16); + // Show splash screen for a moment + delay.delay_ms(2000_u16); - // Demo select CH2 - *screen.borrow_mut() = Screens::Home(HomeScreen { - active: HomeSelection::Ch2, - }); - - // Draw screen - lcd.screen(); - - // Show splash screen for a moment - delay.delay_ms(1000_u16); - - // Demo select CH3 - *screen.borrow_mut() = Screens::Home(HomeScreen { - active: HomeSelection::Ch3, - }); - - // Draw screen - lcd.screen(); - - // Show splash screen for a moment - delay.delay_ms(1000_u16); - - // Demo select SETUP - *screen.borrow_mut() = Screens::Home(HomeScreen { - active: HomeSelection::Setup, - }); - - // Draw screen - lcd.screen(); + // Set home screen + screen = Screens::Home(HomeScreen { + active: HomeSelection::Ch1, }); + // Draw screen + lcd.draw(&screen); + + let mut update_screen = false; + + // Enable interrupts globally + unsafe { interrupt::enable() }; + #[allow(clippy::empty_loop)] - loop {} + loop { + if UPDATE_ENCODER.load(Ordering::SeqCst) { + interrupt::free(|_cs| { + delay.delay_ms(3_u8); + + match encoder.update() { + Direction::Clockwise => { + screen.input(&Input::Next); + update_screen = true; + } + Direction::CounterClockwise => { + screen.input(&Input::Previous); + update_screen = true; + } + Direction::None => {} + } + UPDATE_ENCODER.store(false, Ordering::SeqCst); + }) + } + + if update_screen { + lcd.draw(&screen); + update_screen = false; + } + } } diff --git a/firmware/rust/src/rotary.rs b/firmware/rust/src/rotary.rs new file mode 100644 index 0000000..21919dc --- /dev/null +++ b/firmware/rust/src/rotary.rs @@ -0,0 +1,51 @@ +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 + } +} From 000371740831b8a44d0b1e644dac878da5ead812 Mon Sep 17 00:00:00 2001 From: finga Date: Tue, 15 Mar 2022 19:03:36 +0100 Subject: [PATCH 19/37] fw-rust: Refactor home screen Add new function for easier instantiation and refactor the input function. --- firmware/rust/src/main.rs | 60 ++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index 1bb4ff1..ddea84b 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -426,38 +426,34 @@ struct HomeScreen { } impl HomeScreen { - fn input(&self, input: &Input) -> Screens { - match input { - Input::Next => match self.active { - HomeSelection::Ch1 => Screens::Home(HomeScreen { - active: HomeSelection::Ch2, - }), - HomeSelection::Ch2 => Screens::Home(HomeScreen { - active: HomeSelection::Ch3, - }), - HomeSelection::Ch3 => Screens::Home(HomeScreen { - active: HomeSelection::Setup, - }), - HomeSelection::Setup => Screens::Home(HomeScreen { - active: HomeSelection::Ch1, - }), - }, - Input::Previous => match self.active { - HomeSelection::Ch1 => Screens::Home(HomeScreen { - active: HomeSelection::Setup, - }), - HomeSelection::Ch2 => Screens::Home(HomeScreen { - active: HomeSelection::Ch1, - }), - HomeSelection::Ch3 => Screens::Home(HomeScreen { - active: HomeSelection::Ch2, - }), - HomeSelection::Setup => Screens::Home(HomeScreen { - active: HomeSelection::Ch3, - }), - }, + fn new() -> Self { + Self { + active: HomeSelection::Ch1, } } + + fn input(&self, input: &Input) -> Screens { + Screens::Home(Self { + active: match self.active { + HomeSelection::Ch1 => match input { + Input::Next => HomeSelection::Ch2, + Input::Previous => HomeSelection::Setup, + }, + HomeSelection::Ch2 => match input { + Input::Next => HomeSelection::Ch3, + Input::Previous => HomeSelection::Ch1, + }, + HomeSelection::Ch3 => match input { + Input::Next => HomeSelection::Setup, + Input::Previous => HomeSelection::Ch2, + }, + HomeSelection::Setup => match input { + Input::Next => HomeSelection::Ch1, + Input::Previous => HomeSelection::Ch3, + }, + }, + }) + } } impl Draw for HomeScreen { @@ -586,9 +582,7 @@ fn main() -> ! { delay.delay_ms(2000_u16); // Set home screen - screen = Screens::Home(HomeScreen { - active: HomeSelection::Ch1, - }); + screen = Screens::Home(HomeScreen::new()); // Draw screen lcd.draw(&screen); From 91e120b25858b7e74a9a53aadf6b0f34442509f5 Mon Sep 17 00:00:00 2001 From: finga Date: Wed, 16 Mar 2022 22:17:23 +0100 Subject: [PATCH 20/37] fw-rust: Add setup screen and handle button input To create a setup screen more symbols are created and added to the symbol table. Helper functions are added to draw homogenous areas onto the display. Add two new fields `Click` and `Back` to the `Input` enum. Adapt functions already handling input to those new kinds of input. Add the setup screen construct. Add global atomics to handle the button and the timer for handling the back input when the button is pressed until the timer one overflows. Button press and the timer are handled via interrupts. --- firmware/rust/src/main.rs | 248 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 236 insertions(+), 12 deletions(-) diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index ddea84b..dcbae03 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -133,6 +133,21 @@ const SYM_9: [&[u8]; 2] = [ &[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], @@ -145,18 +160,57 @@ const SYM_E: [&[u8]; 2] = [ &[0x3F, 0x3F, 0x31, 0x31, 0x30, 0x30], ]; +// 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_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], @@ -193,31 +247,31 @@ const SYMBOL_TABLE: [&[&[u8]; 2]; 48] = [ &SYM_7, // '7' &SYM_8, // '8' &SYM_9, // '9' - &SYM_INVALID, // ':' + &SYM_COLON, // ':' &SYM_INVALID, // ';' &SYM_INVALID, // '<' &SYM_INVALID, // '=' &SYM_INVALID, // '>' &SYM_INVALID, // '?' &SYM_INVALID, // '@' - &SYM_INVALID, // 'A' - &SYM_INVALID, // 'B' + &SYM_A, // 'A' + &SYM_B, // 'B' &SYM_C, // 'C' &SYM_INVALID, // 'D' &SYM_E, // 'E' &SYM_INVALID, // 'F' - &SYM_INVALID, // 'G' + &SYM_G, // 'G' &SYM_H, // 'H' - &SYM_INVALID, // 'I' + &SYM_I, // 'I' &SYM_INVALID, // 'J' - &SYM_INVALID, // 'K' - &SYM_INVALID, // 'L' + &SYM_K, // 'K' + &SYM_L, // 'L' &SYM_INVALID, // 'M' - &SYM_INVALID, // 'N' - &SYM_INVALID, // 'O' + &SYM_N, // 'N' + &SYM_O, // 'O' &SYM_P, // 'P' &SYM_INVALID, // 'Q' - &SYM_INVALID, // 'R' + &SYM_R, // 'R' &SYM_S, // 'S' &SYM_T, // 'T' &SYM_U, // 'U' @@ -297,6 +351,33 @@ impl Lcd { } } + 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(); + } + + 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); + } + } + fn print(&mut self, segment: u8, page: u8, string: &str) { let mut delay = Delay::::new(); @@ -360,6 +441,9 @@ impl Lcd { Screens::Home(home) => { home.draw(self); } + Screens::Setup(setup) => { + setup.draw(self); + } } }); } @@ -368,6 +452,8 @@ impl Lcd { enum Input { Next, Previous, + Select, + Back, } pub trait Draw { @@ -438,18 +524,26 @@ impl HomeScreen { HomeSelection::Ch1 => match input { Input::Next => HomeSelection::Ch2, Input::Previous => HomeSelection::Setup, + Input::Select => return Screens::Splash(SplashScreen), + Input::Back => HomeSelection::Ch1, }, HomeSelection::Ch2 => match input { Input::Next => HomeSelection::Ch3, Input::Previous => HomeSelection::Ch1, + Input::Select => return Screens::Splash(SplashScreen), + Input::Back => HomeSelection::Ch2, }, HomeSelection::Ch3 => match input { Input::Next => HomeSelection::Setup, Input::Previous => HomeSelection::Ch2, + Input::Select => return Screens::Splash(SplashScreen), + Input::Back => HomeSelection::Ch3, }, HomeSelection::Setup => match input { Input::Next => HomeSelection::Ch1, Input::Previous => HomeSelection::Ch3, + Input::Select => return Screens::Setup(SetupScreen::new()), + Input::Back => HomeSelection::Setup, }, }, }) @@ -487,9 +581,84 @@ impl Draw for HomeScreen { } } +enum SetupSelection { + Contrast, + Backlight, + Back, +} + +struct SetupScreen { + active: SetupSelection, +} + +impl SetupScreen { + fn new() -> Self { + Self { + active: SetupSelection::Contrast, + } + } + + fn input(&self, input: &Input) -> Screens { + Screens::Setup(Self { + active: match self.active { + SetupSelection::Contrast => match input { + Input::Next => SetupSelection::Backlight, + Input::Previous => SetupSelection::Back, + Input::Select => return Screens::Splash(SplashScreen), + Input::Back => return Screens::Home(HomeScreen::new()), + }, + SetupSelection::Backlight => match input { + Input::Next => SetupSelection::Back, + Input::Previous => SetupSelection::Contrast, + Input::Select => return Screens::Splash(SplashScreen), + Input::Back => return Screens::Home(HomeScreen::new()), + }, + SetupSelection::Back => match input { + Input::Next => SetupSelection::Contrast, + Input::Previous => SetupSelection::Backlight, + Input::Select => return Screens::Home(HomeScreen::new()), + Input::Back => return Screens::Home(HomeScreen::new()), + }, + }, + }) + } +} + +impl Draw for SetupScreen { + fn draw(&self, lcd: &mut Lcd) { + match &self.active { + SetupSelection::Contrast => { + lcd.fill_area(0, 0, 33, 2, 0xFF); + lcd.print_inverted(33, 0, "SETUP"); + lcd.fill_area(69, 0, 33, 2, 0xFF); + lcd.print_inverted(0, 2, "CONTRAST:"); + lcd.print(0, 4, "BACKLIGHT:"); + lcd.print(36, 6, "BACK"); + } + SetupSelection::Backlight => { + lcd.fill_area(0, 0, 33, 2, 0xFF); + lcd.print_inverted(33, 0, "SETUP"); + lcd.fill_area(69, 0, 33, 2, 0xFF); + lcd.print(0, 2, "CONTRAST:"); + lcd.print_inverted(0, 4, "BACKLIGHT:"); + lcd.print(36, 6, "BACK"); + } + SetupSelection::Back => { + lcd.fill_area(0, 0, 33, 2, 0xFF); + lcd.print_inverted(33, 0, "SETUP"); + lcd.fill_area(69, 0, 33, 2, 0xFF); + lcd.print(0, 2, "CONTRAST:"); + lcd.print(0, 4, "BACKLIGHT:"); + lcd.print_inverted(36, 6, "BACK"); + } + } + } +} + enum Screens { Splash(SplashScreen), Home(HomeScreen), + Setup(SetupScreen), } impl Screens { @@ -497,11 +666,14 @@ impl Screens { match self { Screens::Splash(_) => {} Screens::Home(home) => *self = home.input(input), + Screens::Setup(setup) => *self = setup.input(input), } } } 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)] @@ -511,6 +683,22 @@ fn PCINT0() { }) } +#[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() -> ! { // Get peripherals, pins, eeprom and delay @@ -568,9 +756,18 @@ fn main() -> ! { let mut encoder = Rotary::new(pins.pb6.into_pull_up_input(), pins.pb7.into_pull_up_input()); encoder.update(); - // Enable interrupts for encoder // TODO: and switch - dp.EXINT.pcicr.write(|w| w.pcie().bits(0b0000_0001)); + // Init button + let button = pins.pc0.into_pull_up_input(); + + // Init Timer/Counter1 + let tc1 = dp.TC1; + tc1.ocr1a.write(|w| unsafe { w.bits(65535) }); + + // Enable interrupts for encoder, button and timer + dp.EXINT.pcicr.write(|w| w.pcie().bits(0b0000_0011)); dp.EXINT.pcmsk0.write(|w| w.pcint().bits(0b1100_0000)); + dp.EXINT.pcmsk1.write(|w| w.pcint().bits(0b0000_0001)); + tc1.timsk1.write(|w| w.ocie1a().set_bit()); // Create screen let mut screen = Screens::Splash(SplashScreen); @@ -613,6 +810,33 @@ fn main() -> ! { }) } + if UPDATE_TIMER.load(Ordering::SeqCst) { + tc1.tccr1b.write(|w| w.cs1().no_clock()); + screen.input(&Input::Back); + update_screen = true; + UPDATE_TIMER.store(false, Ordering::SeqCst); + } + + if UPDATE_BUTTON.load(Ordering::SeqCst) { + delay.delay_ms(2_u8); + + if button.is_low() { + // Press + if tc1.tccr1b.read().cs1().is_no_clock() { + tc1.tcnt1.write(|w| unsafe { w.bits(0) }); + tc1.tccr1b.write(|w| w.cs1().prescale_64()); + } + } else { + // Release + if tc1.tccr1b.read().cs1().is_prescale_64() { + tc1.tccr1b.write(|w| w.cs1().no_clock()); + screen.input(&Input::Select); + update_screen = true; + } + } + UPDATE_BUTTON.store(false, Ordering::SeqCst); + } + if update_screen { lcd.draw(&screen); update_screen = false; From c2920ea334ef70760b876d4cef452834f7bdc50d Mon Sep 17 00:00:00 2001 From: finga Date: Thu, 17 Mar 2022 12:03:22 +0100 Subject: [PATCH 21/37] fw-rust: Break everything down in multiple files To reduce length of `main.rs` and therefor improve readability create source files for: - Assets (`assets.rs`): Contains all graphical assets such as the splash screen assets, symbols and the symbol table. - LCD (`lcd.rs`): Contains all lower level things regarding the LCD such as the `Lcd` struct and its implementations. - Screen (`screen/mod.rs`): - Splash (`screen/splash.rs`) - Home (`screen/home.rs`) - Setup (`screen/setup.rs`) In the future it would probably make sense to move the LCD module into the screen module. --- firmware/rust/src/assets.rs | 258 +++++++++++ firmware/rust/src/lcd.rs | 181 ++++++++ firmware/rust/src/main.rs | 658 +---------------------------- firmware/rust/src/screen/home.rs | 83 ++++ firmware/rust/src/screen/mod.rs | 30 ++ firmware/rust/src/screen/setup.rs | 82 ++++ firmware/rust/src/screen/splash.rs | 48 +++ 7 files changed, 693 insertions(+), 647 deletions(-) create mode 100644 firmware/rust/src/assets.rs create mode 100644 firmware/rust/src/lcd.rs create mode 100644 firmware/rust/src/screen/home.rs create mode 100644 firmware/rust/src/screen/mod.rs create mode 100644 firmware/rust/src/screen/setup.rs create mode 100644 firmware/rust/src/screen/splash.rs diff --git a/firmware/rust/src/assets.rs b/firmware/rust/src/assets.rs new file mode 100644 index 0000000..9892e41 --- /dev/null +++ b/firmware/rust/src/assets.rs @@ -0,0 +1,258 @@ +// 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 +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_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_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_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]; 48] = [ + &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_INVALID, // '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_INVALID, // '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_INVALID, // '_' +]; diff --git a/firmware/rust/src/lcd.rs b/firmware/rust/src/lcd.rs new file mode 100644 index 0000000..9ac6564 --- /dev/null +++ b/firmware/rust/src/lcd.rs @@ -0,0 +1,181 @@ +use atmega_hal::{ + clock::MHz8, + delay::Delay, + port::{mode::Output, Pin, PB0, PB1}, + Spi, +}; +use avr_device::interrupt; +use embedded_hal::{blocking::delay::DelayMs, spi::FullDuplex}; +use nb::block; + +use crate::{ + assets::SYMBOL_TABLE, + screen::{Screen, Screens}, +}; + +// 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, contrast: u8) { + 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); + + block!(self.spi.send(0x40)).unwrap(); // (6) Set Scroll Line: Display start line 0 + block!(self.spi.send(0xA1)).unwrap(); // (13) Set SEG direction: SEG reverse + block!(self.spi.send(0xC0)).unwrap(); // (14) Set COM direction: Normal COM0 - COM63 + // block!(spi.send(0xA4)).unwrap(); // (10) Set All Pixel On: Disable -> Set All Pixel to ON */ + block!(self.spi.send(0xA6)).unwrap(); // (11) Set Inverse Display: Display inverse off + block!(self.spi.send(0xA2)).unwrap(); // (17) Set LCD Bias Ratio: Set Bias 1/9 (Duty 1/65) + block!(self.spi.send(0x2F)).unwrap(); // (5) Set Power Control: Booster, Regulator and Follower on + block!(self.spi.send(0x27)).unwrap(); // (8) Set VLCD Resistor Ratio: Set Contrast + // block!(spi.send(0xEE)).unwrap(); // (18) Reset Cursor Update Mode + block!(self.spi.send(0x81)).unwrap(); // (9) Set Electronic Volume: Set Contrast + block!(self.spi.send(contrast)).unwrap(); // (9) Set Electronic Volume: Set Contrast + // block!(spi.send(0xFA)).unwrap(); // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C + // block!(spi.send(0x90)).unwrap(); // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C + block!(self.spi.send(0xAF)).unwrap(); // (12) Set Display Enable: Display on + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + } + + 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 clear(&mut self) { + let mut delay = Delay::::new(); + + for page in 0..8 { + self.move_cursor(0, page as u8); + + // TODO: This delay fixes issues, try find a better solution + delay.delay_ms(1_u8); + self.cd.set_high(); + + for _ in 0..102 { + 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(); + } + } + + 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 - 48][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 - 48][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 draw(&mut self, screen: &Screens) { + interrupt::free(|_cs| { + self.clear(); + + match screen { + Screens::Splash(splash) => { + splash.draw(self); + } + Screens::Home(home) => { + home.draw(self); + } + Screens::Setup(setup) => { + setup.draw(self); + } + } + }); + } +} diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index dcbae03..cffdd0f 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -6,7 +6,6 @@ use atmega_hal::{ clock::MHz8, delay::Delay, pins, - port::{mode::Output, Pin, PB0, PB1}, spi::{DataOrder, SerialClockRate, Settings, Spi}, Peripherals, }; @@ -15,662 +14,31 @@ use avr_eeprom::{eeprom, Eeprom}; use core::sync::atomic::{AtomicBool, Ordering}; use embedded_hal::{ blocking::delay::DelayMs, - spi::{FullDuplex, Mode, Phase, Polarity}, + spi::{Mode, Phase, Polarity}, }; -use nb::block; use panic_halt as _; +mod assets; +mod lcd; mod rotary; +mod screen; + +use lcd::Lcd; use rotary::{Direction, Rotary}; +use screen::Screens; eeprom! { static eeprom CONTRAST: u8 = 8; static eeprom BACKLIGHT: u8 = 1; } -// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -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 -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 -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_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_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_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 -const SYMBOL_TABLE: [&[&[u8]; 2]; 48] = [ - &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_INVALID, // '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_INVALID, // '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_INVALID, // '_' -]; - -pub struct Lcd { - spi: Spi, - cd: Pin, - rst: Pin, -} - -impl Lcd { - fn init(&mut self, contrast: u8) { - 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); - - block!(self.spi.send(0x40)).unwrap(); // (6) Set Scroll Line: Display start line 0 - block!(self.spi.send(0xA1)).unwrap(); // (13) Set SEG direction: SEG reverse - block!(self.spi.send(0xC0)).unwrap(); // (14) Set COM direction: Normal COM0 - COM63 - // block!(spi.send(0xA4)).unwrap(); // (10) Set All Pixel On: Disable -> Set All Pixel to ON */ - block!(self.spi.send(0xA6)).unwrap(); // (11) Set Inverse Display: Display inverse off - block!(self.spi.send(0xA2)).unwrap(); // (17) Set LCD Bias Ratio: Set Bias 1/9 (Duty 1/65) - block!(self.spi.send(0x2F)).unwrap(); // (5) Set Power Control: Booster, Regulator and Follower on - block!(self.spi.send(0x27)).unwrap(); // (8) Set VLCD Resistor Ratio: Set Contrast - // block!(spi.send(0xEE)).unwrap(); // (18) Reset Cursor Update Mode - block!(self.spi.send(0x81)).unwrap(); // (9) Set Electronic Volume: Set Contrast - block!(self.spi.send(contrast)).unwrap(); // (9) Set Electronic Volume: Set Contrast - // block!(spi.send(0xFA)).unwrap(); // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C - // block!(spi.send(0x90)).unwrap(); // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C - block!(self.spi.send(0xAF)).unwrap(); // (12) Set Display Enable: Display on - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - } - - 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 clear(&mut self) { - let mut delay = Delay::::new(); - - for page in 0..8 { - self.move_cursor(0, page as u8); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_high(); - - for _ in 0..102 { - 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(); - } - } - - 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(); - } - - 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); - } - } - - 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 - 48][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(); - } - } - - 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 - 48][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(); - } - } - - fn draw(&mut self, screen: &Screens) { - interrupt::free(|_cs| { - self.clear(); - - match screen { - Screens::Splash(splash) => { - splash.draw(self); - } - Screens::Home(home) => { - home.draw(self); - } - Screens::Setup(setup) => { - setup.draw(self); - } - } - }); - } -} - -enum Input { +pub enum Input { Next, Previous, Select, Back, } -pub trait Draw { - fn draw(&self, lcd: &mut Lcd); -} - -struct SplashScreen; - -impl Draw for SplashScreen { - 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(); - } - } -} - -enum HomeSelection { - Ch1, - Ch2, - Ch3, - Setup, -} - -struct HomeScreen { - active: HomeSelection, -} - -impl HomeScreen { - fn new() -> Self { - Self { - active: HomeSelection::Ch1, - } - } - - fn input(&self, input: &Input) -> Screens { - Screens::Home(Self { - active: match self.active { - HomeSelection::Ch1 => match input { - Input::Next => HomeSelection::Ch2, - Input::Previous => HomeSelection::Setup, - Input::Select => return Screens::Splash(SplashScreen), - Input::Back => HomeSelection::Ch1, - }, - HomeSelection::Ch2 => match input { - Input::Next => HomeSelection::Ch3, - Input::Previous => HomeSelection::Ch1, - Input::Select => return Screens::Splash(SplashScreen), - Input::Back => HomeSelection::Ch2, - }, - HomeSelection::Ch3 => match input { - Input::Next => HomeSelection::Setup, - Input::Previous => HomeSelection::Ch2, - Input::Select => return Screens::Splash(SplashScreen), - Input::Back => HomeSelection::Ch3, - }, - HomeSelection::Setup => match input { - Input::Next => HomeSelection::Ch1, - Input::Previous => HomeSelection::Ch3, - Input::Select => return Screens::Setup(SetupScreen::new()), - Input::Back => HomeSelection::Setup, - }, - }, - }) - } -} - -impl Draw for HomeScreen { - fn draw(&self, lcd: &mut Lcd) { - match &self.active { - HomeSelection::Ch1 => { - lcd.print_inverted(0, 0, "CH1"); - lcd.print(0, 2, "CH2"); - lcd.print(0, 4, "CH3"); - lcd.print(33, 6, "SETUP"); - } - HomeSelection::Ch2 => { - lcd.print(0, 0, "CH1"); - lcd.print_inverted(0, 2, "CH2"); - lcd.print(0, 4, "CH3"); - lcd.print(33, 6, "SETUP"); - } - HomeSelection::Ch3 => { - lcd.print(0, 0, "CH1"); - lcd.print(0, 2, "CH2"); - lcd.print_inverted(0, 4, "CH3"); - lcd.print(33, 6, "SETUP"); - } - HomeSelection::Setup => { - lcd.print(0, 0, "CH1"); - lcd.print(0, 2, "CH2"); - lcd.print(0, 4, "CH3"); - lcd.print_inverted(33, 6, "SETUP"); - } - } - } -} - -enum SetupSelection { - Contrast, - Backlight, - Back, -} - -struct SetupScreen { - active: SetupSelection, -} - -impl SetupScreen { - fn new() -> Self { - Self { - active: SetupSelection::Contrast, - } - } - - fn input(&self, input: &Input) -> Screens { - Screens::Setup(Self { - active: match self.active { - SetupSelection::Contrast => match input { - Input::Next => SetupSelection::Backlight, - Input::Previous => SetupSelection::Back, - Input::Select => return Screens::Splash(SplashScreen), - Input::Back => return Screens::Home(HomeScreen::new()), - }, - SetupSelection::Backlight => match input { - Input::Next => SetupSelection::Back, - Input::Previous => SetupSelection::Contrast, - Input::Select => return Screens::Splash(SplashScreen), - Input::Back => return Screens::Home(HomeScreen::new()), - }, - SetupSelection::Back => match input { - Input::Next => SetupSelection::Contrast, - Input::Previous => SetupSelection::Backlight, - Input::Select => return Screens::Home(HomeScreen::new()), - Input::Back => return Screens::Home(HomeScreen::new()), - }, - }, - }) - } -} - -impl Draw for SetupScreen { - fn draw(&self, lcd: &mut Lcd) { - match &self.active { - SetupSelection::Contrast => { - lcd.fill_area(0, 0, 33, 2, 0xFF); - lcd.print_inverted(33, 0, "SETUP"); - lcd.fill_area(69, 0, 33, 2, 0xFF); - lcd.print_inverted(0, 2, "CONTRAST:"); - lcd.print(0, 4, "BACKLIGHT:"); - lcd.print(36, 6, "BACK"); - } - SetupSelection::Backlight => { - lcd.fill_area(0, 0, 33, 2, 0xFF); - lcd.print_inverted(33, 0, "SETUP"); - lcd.fill_area(69, 0, 33, 2, 0xFF); - lcd.print(0, 2, "CONTRAST:"); - lcd.print_inverted(0, 4, "BACKLIGHT:"); - lcd.print(36, 6, "BACK"); - } - SetupSelection::Back => { - lcd.fill_area(0, 0, 33, 2, 0xFF); - lcd.print_inverted(33, 0, "SETUP"); - lcd.fill_area(69, 0, 33, 2, 0xFF); - lcd.print(0, 2, "CONTRAST:"); - lcd.print(0, 4, "BACKLIGHT:"); - lcd.print_inverted(36, 6, "BACK"); - } - } - } -} - -enum Screens { - Splash(SplashScreen), - Home(HomeScreen), - Setup(SetupScreen), -} - -impl Screens { - fn input(&mut self, input: &Input) { - match self { - Screens::Splash(_) => {} - Screens::Home(home) => *self = home.input(input), - Screens::Setup(setup) => *self = setup.input(input), - } - } -} - static UPDATE_ENCODER: AtomicBool = AtomicBool::new(false); static UPDATE_BUTTON: AtomicBool = AtomicBool::new(false); static UPDATE_TIMER: AtomicBool = AtomicBool::new(false); @@ -745,11 +113,7 @@ fn main() -> ! { ); // Init LCD - let mut lcd = Lcd { - spi, - cd: pins.pb0.into_output(), - rst: pins.pb1.into_output(), - }; + let mut lcd = Lcd::new(spi, pins.pb0.into_output(), pins.pb1.into_output()); lcd.init(contrast); // Init encoder @@ -770,7 +134,7 @@ fn main() -> ! { tc1.timsk1.write(|w| w.ocie1a().set_bit()); // Create screen - let mut screen = Screens::Splash(SplashScreen); + let mut screen = Screens::Splash(screen::Splash); // Draw splash screen lcd.draw(&screen); @@ -779,7 +143,7 @@ fn main() -> ! { delay.delay_ms(2000_u16); // Set home screen - screen = Screens::Home(HomeScreen::new()); + screen = Screens::Home(screen::Home::new()); // Draw screen lcd.draw(&screen); diff --git a/firmware/rust/src/screen/home.rs b/firmware/rust/src/screen/home.rs new file mode 100644 index 0000000..749d42f --- /dev/null +++ b/firmware/rust/src/screen/home.rs @@ -0,0 +1,83 @@ +use super::{Screen, Screens, Setup, Splash}; +use crate::{lcd::Lcd, 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(&self, input: &Input) -> Screens { + Screens::Home(Self { + active: match self.active { + Selection::Ch1 => match input { + Input::Next => Selection::Ch2, + Input::Previous => Selection::Setup, + Input::Select => return Screens::Splash(Splash), + Input::Back => Selection::Ch1, + }, + Selection::Ch2 => match input { + Input::Next => Selection::Ch3, + Input::Previous => Selection::Ch1, + Input::Select => return Screens::Splash(Splash), + Input::Back => Selection::Ch2, + }, + Selection::Ch3 => match input { + Input::Next => Selection::Setup, + Input::Previous => Selection::Ch2, + Input::Select => return Screens::Splash(Splash), + Input::Back => Selection::Ch3, + }, + Selection::Setup => match input { + Input::Next => Selection::Ch1, + Input::Previous => Selection::Ch3, + Input::Select => return Screens::Setup(Setup::new()), + Input::Back => Selection::Setup, + }, + }, + }) + } +} + +impl Screen for Home { + fn draw(&self, lcd: &mut Lcd) { + match &self.active { + Selection::Ch1 => { + lcd.print_inverted(0, 0, "CH1"); + lcd.print(0, 2, "CH2"); + lcd.print(0, 4, "CH3"); + lcd.print(33, 6, "SETUP"); + } + Selection::Ch2 => { + lcd.print(0, 0, "CH1"); + lcd.print_inverted(0, 2, "CH2"); + lcd.print(0, 4, "CH3"); + lcd.print(33, 6, "SETUP"); + } + Selection::Ch3 => { + lcd.print(0, 0, "CH1"); + lcd.print(0, 2, "CH2"); + lcd.print_inverted(0, 4, "CH3"); + lcd.print(33, 6, "SETUP"); + } + Selection::Setup => { + lcd.print(0, 0, "CH1"); + lcd.print(0, 2, "CH2"); + lcd.print(0, 4, "CH3"); + lcd.print_inverted(33, 6, "SETUP"); + } + } + } +} diff --git a/firmware/rust/src/screen/mod.rs b/firmware/rust/src/screen/mod.rs new file mode 100644 index 0000000..09d46be --- /dev/null +++ b/firmware/rust/src/screen/mod.rs @@ -0,0 +1,30 @@ +mod home; +mod setup; +mod splash; + +use crate::{lcd::Lcd, Input}; +pub use home::Home; +pub use setup::Setup; +pub use splash::Splash; + +// TODO: Only update changes instead of whole screen + +pub trait Screen { + fn draw(&self, lcd: &mut Lcd); +} + +pub enum Screens { + Splash(Splash), + Home(Home), + Setup(Setup), +} + +impl Screens { + pub fn input(&mut self, input: &Input) { + match self { + Screens::Splash(_) => {} + Screens::Home(home) => *self = home.input(input), + Screens::Setup(setup) => *self = setup.input(input), + } + } +} diff --git a/firmware/rust/src/screen/setup.rs b/firmware/rust/src/screen/setup.rs new file mode 100644 index 0000000..5f3358d --- /dev/null +++ b/firmware/rust/src/screen/setup.rs @@ -0,0 +1,82 @@ +use super::{Home, Screen, Screens, Splash}; +use crate::{lcd::Lcd, Input}; + +enum Selection { + Contrast, + Backlight, + Back, +} + +pub struct Setup { + active: Selection, +} + +impl Setup { + pub fn new() -> Self { + Self { + active: Selection::Contrast, + } + } + + pub fn input(&self, input: &Input) -> Screens { + Screens::Setup(Self { + active: match self.active { + Selection::Contrast => match input { + Input::Next => Selection::Backlight, + Input::Previous => Selection::Back, + Input::Select => return Screens::Splash(Splash), + Input::Back => return Screens::Home(Home::new()), + }, + Selection::Backlight => match input { + Input::Next => Selection::Back, + Input::Previous => Selection::Contrast, + Input::Select => return Screens::Splash(Splash), + Input::Back => return Screens::Home(Home::new()), + }, + Selection::Back => match input { + Input::Next => Selection::Contrast, + Input::Previous => Selection::Backlight, + Input::Select => return Screens::Home(Home::new()), + Input::Back => return Screens::Home(Home::new()), + }, + }, + }) + } +} + +impl Screen for Setup { + fn draw(&self, lcd: &mut Lcd) { + match &self.active { + Selection::Contrast => { + lcd.fill_area(0, 0, 33, 2, 0xFF); + lcd.print_inverted(33, 0, "SETUP"); + lcd.fill_area(69, 0, 33, 2, 0xFF); + lcd.print_inverted(0, 2, "CONTRAST:"); + lcd.print(89, 2, "00"); + lcd.print(0, 4, "BACKLIGHT:"); + lcd.print(83, 4, "000"); + lcd.print(36, 6, "BACK"); + } + Selection::Backlight => { + lcd.fill_area(0, 0, 33, 2, 0xFF); + lcd.print_inverted(33, 0, "SETUP"); + lcd.fill_area(69, 0, 33, 2, 0xFF); + lcd.print(0, 2, "CONTRAST:"); + lcd.print(89, 2, "00"); + lcd.print_inverted(0, 4, "BACKLIGHT:"); + lcd.print(83, 4, "000"); + lcd.print(36, 6, "BACK"); + } + Selection::Back => { + lcd.fill_area(0, 0, 33, 2, 0xFF); + lcd.print_inverted(33, 0, "SETUP"); + lcd.fill_area(69, 0, 33, 2, 0xFF); + lcd.print(0, 2, "CONTRAST:"); + lcd.print(89, 2, "00"); + lcd.print(0, 4, "BACKLIGHT:"); + lcd.print(83, 4, "000"); + lcd.print_inverted(36, 6, "BACK"); + } + } + } +} diff --git a/firmware/rust/src/screen/splash.rs b/firmware/rust/src/screen/splash.rs new file mode 100644 index 0000000..219f314 --- /dev/null +++ b/firmware/rust/src/screen/splash.rs @@ -0,0 +1,48 @@ +use super::Screen; +use crate::{ + assets::{ONDERS_ORG, SACRED_CHAO}, + lcd::Lcd, +}; +use atmega_hal::{clock::MHz8, delay::Delay}; +use embedded_hal::{blocking::delay::DelayMs, spi::FullDuplex}; +use nb::block; + +pub struct Splash; + +impl Screen for Splash { + 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(); + } + } +} From 7d8f5f68700eba0650277ed0efa63f30b8b6425e Mon Sep 17 00:00:00 2001 From: finga Date: Sat, 19 Mar 2022 18:40:18 +0100 Subject: [PATCH 22/37] fw-rust: Create a print `u8` function for setup Create `print_u8()` helper function to print `u8` primitives to lcd. Refactor contrast and backlight variables to also keep them in a global `AtomicU8`. --- firmware/rust/src/lcd.rs | 34 +++++++++++++++++++++++++++++++ firmware/rust/src/main.rs | 17 +++++++++------- firmware/rust/src/screen/setup.rs | 18 +++++++++------- 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/firmware/rust/src/lcd.rs b/firmware/rust/src/lcd.rs index 9ac6564..55b8f86 100644 --- a/firmware/rust/src/lcd.rs +++ b/firmware/rust/src/lcd.rs @@ -5,6 +5,7 @@ use atmega_hal::{ Spi, }; use avr_device::interrupt; +use core::convert::TryInto; use embedded_hal::{blocking::delay::DelayMs, spi::FullDuplex}; use nb::block; @@ -161,6 +162,39 @@ impl Lcd { } } + 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 = [0usize; 3]; + for (i, item) in array.iter_mut().enumerate() { + *item = ((data / 10_u8.pow(i.try_into().unwrap())) % 10).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 draw(&mut self, screen: &Screens) { interrupt::free(|_cs| { self.clear(); diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index cffdd0f..a5ac1fc 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -11,7 +11,7 @@ use atmega_hal::{ }; use avr_device::interrupt; use avr_eeprom::{eeprom, Eeprom}; -use core::sync::atomic::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use embedded_hal::{ blocking::delay::DelayMs, spi::{Mode, Phase, Polarity}, @@ -28,8 +28,8 @@ use rotary::{Direction, Rotary}; use screen::Screens; eeprom! { - static eeprom CONTRAST: u8 = 8; - static eeprom BACKLIGHT: u8 = 1; + static eeprom EEPROM_CONTRAST: u8 = 8; + static eeprom EEPROM_BACKLIGHT: u8 = 1; } pub enum Input { @@ -42,6 +42,8 @@ pub enum Input { static UPDATE_ENCODER: AtomicBool = AtomicBool::new(false); static UPDATE_BUTTON: AtomicBool = AtomicBool::new(false); static UPDATE_TIMER: AtomicBool = AtomicBool::new(false); +static CONTRAST: AtomicU8 = AtomicU8::new(0); +static BACKLIGHT: AtomicU8 = AtomicU8::new(0); #[interrupt(atmega328p)] #[allow(non_snake_case)] @@ -76,8 +78,8 @@ fn main() -> ! { let mut delay = Delay::::new(); // Get contrast and backlight from EEPROM - let contrast = eeprom.read_value(&CONTRAST); - let backlight = eeprom.read_value(&BACKLIGHT); + CONTRAST.store(eeprom.read_value(&EEPROM_CONTRAST), Ordering::SeqCst); + BACKLIGHT.store(eeprom.read_value(&EEPROM_BACKLIGHT), Ordering::SeqCst); // Init display backlight let tc0 = dp.TC0; @@ -92,7 +94,8 @@ fn main() -> ! { w }); tc0.ocr0a.write(|w| unsafe { w.bits(255) }); - tc0.ocr0b.write(|w| unsafe { w.bits(backlight) }); + tc0.ocr0b + .write(|w| unsafe { w.bits(BACKLIGHT.load(Ordering::SeqCst)) }); pins.pd5.into_output(); // Init SPI @@ -114,7 +117,7 @@ fn main() -> ! { // Init LCD let mut lcd = Lcd::new(spi, pins.pb0.into_output(), pins.pb1.into_output()); - lcd.init(contrast); + lcd.init(CONTRAST.load(Ordering::SeqCst)); // Init encoder let mut encoder = Rotary::new(pins.pb6.into_pull_up_input(), pins.pb7.into_pull_up_input()); diff --git a/firmware/rust/src/screen/setup.rs b/firmware/rust/src/screen/setup.rs index 5f3358d..a3a61bc 100644 --- a/firmware/rust/src/screen/setup.rs +++ b/firmware/rust/src/screen/setup.rs @@ -1,5 +1,6 @@ use super::{Home, Screen, Screens, Splash}; -use crate::{lcd::Lcd, Input}; +use crate::{lcd::Lcd, Input, BACKLIGHT, CONTRAST}; +use core::sync::atomic::Ordering; enum Selection { Contrast, @@ -46,15 +47,18 @@ impl Setup { impl Screen for Setup { fn draw(&self, lcd: &mut Lcd) { + let contrast = CONTRAST.load(Ordering::SeqCst); + let backlight = BACKLIGHT.load(Ordering::SeqCst); + match &self.active { Selection::Contrast => { lcd.fill_area(0, 0, 33, 2, 0xFF); lcd.print_inverted(33, 0, "SETUP"); lcd.fill_area(69, 0, 33, 2, 0xFF); lcd.print_inverted(0, 2, "CONTRAST:"); - lcd.print(89, 2, "00"); + lcd.print_u8(89, 2, 2, contrast); lcd.print(0, 4, "BACKLIGHT:"); - lcd.print(83, 4, "000"); + lcd.print_u8(83, 2, 3, backlight); lcd.print(36, 6, "BACK"); } Selection::Backlight => { @@ -62,9 +66,9 @@ impl Screen for Setup { lcd.print_inverted(33, 0, "SETUP"); lcd.fill_area(69, 0, 33, 2, 0xFF); lcd.print(0, 2, "CONTRAST:"); - lcd.print(89, 2, "00"); + lcd.print_u8(89, 2, 2, contrast); lcd.print_inverted(0, 4, "BACKLIGHT:"); - lcd.print(83, 4, "000"); + lcd.print_u8(83, 2, 3, backlight); lcd.print(36, 6, "BACK"); } Selection::Back => { @@ -72,9 +76,9 @@ impl Screen for Setup { lcd.print_inverted(33, 0, "SETUP"); lcd.fill_area(69, 0, 33, 2, 0xFF); lcd.print(0, 2, "CONTRAST:"); - lcd.print(89, 2, "00"); + lcd.print_u8(89, 2, 2, contrast); lcd.print(0, 4, "BACKLIGHT:"); - lcd.print(83, 4, "000"); + lcd.print_u8(83, 2, 3, backlight); lcd.print_inverted(36, 6, "BACK"); } } From 08a371062d0f5e2397d17b7a1402869295872f48 Mon Sep 17 00:00:00 2001 From: finga Date: Sun, 20 Mar 2022 14:27:13 +0100 Subject: [PATCH 23/37] fw-rust: Refactor init function of the LCD Use an array for the init sequence commands and iterate over it. --- firmware/rust/src/lcd.rs | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/firmware/rust/src/lcd.rs b/firmware/rust/src/lcd.rs index 55b8f86..2d89a1d 100644 --- a/firmware/rust/src/lcd.rs +++ b/firmware/rust/src/lcd.rs @@ -35,20 +35,26 @@ impl Lcd { // TODO: Try to reduce delay to a minimum delay.delay_ms(1_u8); - block!(self.spi.send(0x40)).unwrap(); // (6) Set Scroll Line: Display start line 0 - block!(self.spi.send(0xA1)).unwrap(); // (13) Set SEG direction: SEG reverse - block!(self.spi.send(0xC0)).unwrap(); // (14) Set COM direction: Normal COM0 - COM63 - // block!(spi.send(0xA4)).unwrap(); // (10) Set All Pixel On: Disable -> Set All Pixel to ON */ - block!(self.spi.send(0xA6)).unwrap(); // (11) Set Inverse Display: Display inverse off - block!(self.spi.send(0xA2)).unwrap(); // (17) Set LCD Bias Ratio: Set Bias 1/9 (Duty 1/65) - block!(self.spi.send(0x2F)).unwrap(); // (5) Set Power Control: Booster, Regulator and Follower on - block!(self.spi.send(0x27)).unwrap(); // (8) Set VLCD Resistor Ratio: Set Contrast - // block!(spi.send(0xEE)).unwrap(); // (18) Reset Cursor Update Mode - block!(self.spi.send(0x81)).unwrap(); // (9) Set Electronic Volume: Set Contrast - block!(self.spi.send(contrast)).unwrap(); // (9) Set Electronic Volume: Set Contrast - // block!(spi.send(0xFA)).unwrap(); // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C - // block!(spi.send(0x90)).unwrap(); // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C - block!(self.spi.send(0xAF)).unwrap(); // (12) Set Display Enable: Display on + 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 + // 0xA4, // (10) Set All Pixel On: Disable -> Set All Pixel to ON + 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 + contrast, // (9) Set Electronic Volume: Set Contrast + // 0xFA, // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C + // 0x90, // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C + 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); From 63cc1c9d0fcad335b85ae6a747611e10bcecbdae Mon Sep 17 00:00:00 2001 From: finga Date: Tue, 22 Mar 2022 17:36:09 +0100 Subject: [PATCH 24/37] fw-rust: Remove `clear()` function for LCD Use `fill_area()` instead of `clear()` to clear the the display. --- firmware/rust/src/lcd.rs | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/firmware/rust/src/lcd.rs b/firmware/rust/src/lcd.rs index 2d89a1d..49adb06 100644 --- a/firmware/rust/src/lcd.rs +++ b/firmware/rust/src/lcd.rs @@ -69,26 +69,6 @@ impl Lcd { block!(self.spi.send(0xB0 + page)).unwrap(); } - fn clear(&mut self) { - let mut delay = Delay::::new(); - - for page in 0..8 { - self.move_cursor(0, page as u8); - - // TODO: This delay fixes issues, try find a better solution - delay.delay_ms(1_u8); - self.cd.set_high(); - - for _ in 0..102 { - 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(); - } - } - fn fill(&mut self, segment: u8, page: u8, width: u8, data: u8) { assert!(segment + width <= 102); let mut delay = Delay::::new(); @@ -203,7 +183,7 @@ impl Lcd { pub fn draw(&mut self, screen: &Screens) { interrupt::free(|_cs| { - self.clear(); + self.fill_area(0, 0, 102, 8, 0x00); match screen { Screens::Splash(splash) => { From 34d1623abbf9e6fac275d0d6ecf42dfeb3322a73 Mon Sep 17 00:00:00 2001 From: finga Date: Thu, 24 Mar 2022 20:28:28 +0100 Subject: [PATCH 25/37] knob: Add the encoder knob model [CI SKIP] --- knob/knob.scad | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 knob/knob.scad diff --git a/knob/knob.scad b/knob/knob.scad new file mode 100644 index 0000000..8b53ffa --- /dev/null +++ b/knob/knob.scad @@ -0,0 +1,18 @@ +$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 = 7); +} + +difference() { + cylinder(h = 16, d1 = 18, d2 = 15); + translate([0, 0, 5]) + shaft(); +} From 800d3ff8c319f894ab32a4bbddb0421a3ca8106d Mon Sep 17 00:00:00 2001 From: finga Date: Sun, 27 Mar 2022 22:10:07 +0200 Subject: [PATCH 26/37] fw-rust: Create "all" build task [CI SKIP] The "all" task configures a new mcu. It burns the fuses, configures the eeprom and flashs the firmware. --- firmware/rust/Makefile.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/firmware/rust/Makefile.toml b/firmware/rust/Makefile.toml index 59d36fa..e47cb7d 100644 --- a/firmware/rust/Makefile.toml +++ b/firmware/rust/Makefile.toml @@ -39,3 +39,7 @@ args = ["-p", "${MCU}", "-c", "${PROGRAMMER}", "-U", "eeprom:w:target/avr-atmega 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"] } From 6cd8df515ddc32714c5fbf4ca499fb6f63f30e80 Mon Sep 17 00:00:00 2001 From: finga Date: Tue, 29 Mar 2022 17:29:13 +0200 Subject: [PATCH 27/37] readme: Improve wording [CI SKIP] --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9cef8b6..2d51b42 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ # Clock Generator [![status-badge](https://ci.onders.org/api/badges/finga/clock_generator/status.svg)](https://ci.onders.org/finga/clock_generator) -A simple board operating another Si5351 clock generator board. +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. -## Motherboard +## 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. -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. From fdd1f4636d2d5dcbaca356a6e4b40f08a42a693a Mon Sep 17 00:00:00 2001 From: finga Date: Wed, 30 Mar 2022 19:29:32 +0200 Subject: [PATCH 28/37] fw-rust: Refactor everything Remove avr-eeprom dependency for now and introduce events. --- firmware/rust/Cargo.lock | 10 -- firmware/rust/Cargo.toml | 1 - firmware/rust/src/eeprom.rs | 48 +++++ firmware/rust/src/lcd.rs | 70 +++++--- firmware/rust/src/main.rs | 273 ++++++++++++++--------------- firmware/rust/src/screen/home.rs | 60 +++---- firmware/rust/src/screen/mod.rs | 90 +++++++++- firmware/rust/src/screen/setup.rs | 166 +++++++++++++----- firmware/rust/src/screen/splash.rs | 4 +- 9 files changed, 459 insertions(+), 263 deletions(-) create mode 100644 firmware/rust/src/eeprom.rs diff --git a/firmware/rust/Cargo.lock b/firmware/rust/Cargo.lock index 04ba3b3..c856d14 100644 --- a/firmware/rust/Cargo.lock +++ b/firmware/rust/Cargo.lock @@ -32,15 +32,6 @@ dependencies = [ "syn", ] -[[package]] -name = "avr-eeprom" -version = "0.1.0-dev" -source = "git+https://git.onders.org/finga/avr-eeprom-rs.git?branch=main#96397b5fbbaaa8531842db301127c1346f6e05f9" -dependencies = [ - "avr-device", - "nb 1.0.0", -] - [[package]] name = "avr-hal-generic" version = "0.1.0" @@ -76,7 +67,6 @@ version = "0.1.0-dev" dependencies = [ "atmega-hal", "avr-device", - "avr-eeprom", "embedded-hal", "nb 1.0.0", "panic-halt", diff --git a/firmware/rust/Cargo.toml b/firmware/rust/Cargo.toml index 9a1d6e9..aee0597 100644 --- a/firmware/rust/Cargo.toml +++ b/firmware/rust/Cargo.toml @@ -15,7 +15,6 @@ panic-halt = "0.2" nb = "1.0" embedded-hal = "0.2" avr-device = { version = "0.3", features = ["atmega328p"] } -avr-eeprom = { git = "https://git.onders.org/finga/avr-eeprom-rs.git", branch = "main" } [dependencies.atmega-hal] git = "https://github.com/rahix/avr-hal" diff --git a/firmware/rust/src/eeprom.rs b/firmware/rust/src/eeprom.rs new file mode 100644 index 0000000..dad5600 --- /dev/null +++ b/firmware/rust/src/eeprom.rs @@ -0,0 +1,48 @@ +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 index 49adb06..53dee58 100644 --- a/firmware/rust/src/lcd.rs +++ b/firmware/rust/src/lcd.rs @@ -4,15 +4,11 @@ use atmega_hal::{ port::{mode::Output, Pin, PB0, PB1}, Spi, }; -use avr_device::interrupt; use core::convert::TryInto; use embedded_hal::{blocking::delay::DelayMs, spi::FullDuplex}; use nb::block; -use crate::{ - assets::SYMBOL_TABLE, - screen::{Screen, Screens}, -}; +use crate::{assets::SYMBOL_TABLE, eeprom, CONTRAST}; // TODO: Make `cd` and `rst` pins generic pins pub struct Lcd { @@ -26,7 +22,7 @@ impl Lcd { Self { spi, cd, rst } } - pub fn init(&mut self, contrast: u8) { + pub fn init(&mut self) { let mut delay = Delay::::new(); // TODO: Test if delay is really needed @@ -40,13 +36,13 @@ impl Lcd { 0xA1, // (13) Set SEG direction: SEG reverse 0xC0, // (14) Set COM direction: Normal COM0 - COM63 // 0xA4, // (10) Set All Pixel On: Disable -> Set All Pixel to ON - 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 - contrast, // (9) Set Electronic Volume: Set Contrast + 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 // 0xFA, // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C // 0x90, // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C 0xAF, // (12) Set Display Enable: Display on @@ -60,6 +56,13 @@ impl Lcd { 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); @@ -181,21 +184,36 @@ impl Lcd { } } - pub fn draw(&mut self, screen: &Screens) { - interrupt::free(|_cs| { - self.fill_area(0, 0, 102, 8, 0x00); + pub fn print_u8_inverted(&mut self, segment: u8, page: u8, digits: u8, data: u8) { + assert!(digits <= 3); + let mut delay = Delay::::new(); - match screen { - Screens::Splash(splash) => { - splash.draw(self); - } - Screens::Home(home) => { - home.draw(self); - } - Screens::Setup(setup) => { - setup.draw(self); + let mut array = [0usize; 3]; + for (i, item) in array.iter_mut().enumerate() { + *item = ((data / 10_u8.pow(i.try_into().unwrap())) % 10).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(); + } } } diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index a5ac1fc..9e9eb49 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -5,13 +5,14 @@ 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 avr_eeprom::{eeprom, Eeprom}; -use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use core::sync::atomic::{AtomicBool, Ordering}; use embedded_hal::{ blocking::delay::DelayMs, spi::{Mode, Phase, Polarity}, @@ -19,18 +20,21 @@ use embedded_hal::{ use panic_halt as _; mod assets; +mod eeprom; mod lcd; mod rotary; mod screen; -use lcd::Lcd; use rotary::{Direction, Rotary}; -use screen::Screens; +use screen::{Screen, Screens}; -eeprom! { - static eeprom EEPROM_CONTRAST: u8 = 8; - static eeprom EEPROM_BACKLIGHT: u8 = 1; -} +#[used] +#[link_section = ".eeprom"] +static BACKLIGHT: u8 = 0; + +#[used] +#[link_section = ".eeprom"] +static CONTRAST: u8 = 8; pub enum Input { Next, @@ -39,11 +43,122 @@ pub enum Input { Back, } +struct ClockGenerator { + screen: Screen, + tc1: TC1, + exint: EXINT, + encoder: Rotary, PB6>, Pin, PB7>>, + button: Pin, PC0>, + delay: Delay, +} + +impl ClockGenerator { + fn new() -> Self { + // Get peripherals, pins and eeprom + let dp = Peripherals::take().unwrap(); + let pins = pins!(dp); + + // Set display PWM backlight pin as output + pins.pd5.into_output(); + + // 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.pb0.into_output(), pins.pb1.into_output()), + 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); + } + } + 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); -static CONTRAST: AtomicU8 = AtomicU8::new(0); -static BACKLIGHT: AtomicU8 = AtomicU8::new(0); #[interrupt(atmega328p)] #[allow(non_snake_case)] @@ -71,142 +186,10 @@ fn TIMER1_COMPA() { #[atmega_hal::entry] fn main() -> ! { - // Get peripherals, pins, eeprom and delay - let dp = Peripherals::take().unwrap(); - let pins = pins!(dp); - let eeprom = Eeprom::new(dp.EEPROM); - let mut delay = Delay::::new(); + let mut cg = ClockGenerator::new(); + cg.init(); - // Get contrast and backlight from EEPROM - CONTRAST.store(eeprom.read_value(&EEPROM_CONTRAST), Ordering::SeqCst); - BACKLIGHT.store(eeprom.read_value(&EEPROM_BACKLIGHT), Ordering::SeqCst); - - // Init display backlight - let tc0 = dp.TC0; - tc0.tccr0a.write(|w| { - w.wgm0().pwm_fast(); - w.com0b().match_clear(); - w - }); - tc0.tccr0b.write(|w| { - w.wgm02().set_bit(); - w.cs0().prescale_64(); - w - }); - tc0.ocr0a.write(|w| unsafe { w.bits(255) }); - tc0.ocr0b - .write(|w| unsafe { w.bits(BACKLIGHT.load(Ordering::SeqCst)) }); - pins.pd5.into_output(); - - // 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, - }, - }, - ); - - // Init LCD - let mut lcd = Lcd::new(spi, pins.pb0.into_output(), pins.pb1.into_output()); - lcd.init(CONTRAST.load(Ordering::SeqCst)); - - // Init encoder - let mut encoder = Rotary::new(pins.pb6.into_pull_up_input(), pins.pb7.into_pull_up_input()); - encoder.update(); - - // Init button - let button = pins.pc0.into_pull_up_input(); - - // Init Timer/Counter1 - let tc1 = dp.TC1; - tc1.ocr1a.write(|w| unsafe { w.bits(65535) }); - - // Enable interrupts for encoder, button and timer - dp.EXINT.pcicr.write(|w| w.pcie().bits(0b0000_0011)); - dp.EXINT.pcmsk0.write(|w| w.pcint().bits(0b1100_0000)); - dp.EXINT.pcmsk1.write(|w| w.pcint().bits(0b0000_0001)); - tc1.timsk1.write(|w| w.ocie1a().set_bit()); - - // Create screen - let mut screen = Screens::Splash(screen::Splash); - - // Draw splash screen - lcd.draw(&screen); - - // Show splash screen for a moment - delay.delay_ms(2000_u16); - - // Set home screen - screen = Screens::Home(screen::Home::new()); - - // Draw screen - lcd.draw(&screen); - - let mut update_screen = false; - - // Enable interrupts globally - unsafe { interrupt::enable() }; - - #[allow(clippy::empty_loop)] loop { - if UPDATE_ENCODER.load(Ordering::SeqCst) { - interrupt::free(|_cs| { - delay.delay_ms(3_u8); - - match encoder.update() { - Direction::Clockwise => { - screen.input(&Input::Next); - update_screen = true; - } - Direction::CounterClockwise => { - screen.input(&Input::Previous); - update_screen = true; - } - Direction::None => {} - } - UPDATE_ENCODER.store(false, Ordering::SeqCst); - }) - } - - if UPDATE_TIMER.load(Ordering::SeqCst) { - tc1.tccr1b.write(|w| w.cs1().no_clock()); - screen.input(&Input::Back); - update_screen = true; - UPDATE_TIMER.store(false, Ordering::SeqCst); - } - - if UPDATE_BUTTON.load(Ordering::SeqCst) { - delay.delay_ms(2_u8); - - if button.is_low() { - // Press - if tc1.tccr1b.read().cs1().is_no_clock() { - tc1.tcnt1.write(|w| unsafe { w.bits(0) }); - tc1.tccr1b.write(|w| w.cs1().prescale_64()); - } - } else { - // Release - if tc1.tccr1b.read().cs1().is_prescale_64() { - tc1.tccr1b.write(|w| w.cs1().no_clock()); - screen.input(&Input::Select); - update_screen = true; - } - } - UPDATE_BUTTON.store(false, Ordering::SeqCst); - } - - if update_screen { - lcd.draw(&screen); - update_screen = false; - } + cg.update(); } } diff --git a/firmware/rust/src/screen/home.rs b/firmware/rust/src/screen/home.rs index 749d42f..3fe84ba 100644 --- a/firmware/rust/src/screen/home.rs +++ b/firmware/rust/src/screen/home.rs @@ -1,4 +1,4 @@ -use super::{Screen, Screens, Setup, Splash}; +use super::{Draw, Event, Screens, Setup, Splash}; use crate::{lcd::Lcd, Input}; enum Selection { @@ -19,39 +19,39 @@ impl Home { } } - pub fn input(&self, input: &Input) -> Screens { - Screens::Home(Self { - active: match self.active { - Selection::Ch1 => match input { - Input::Next => Selection::Ch2, - Input::Previous => Selection::Setup, - Input::Select => return Screens::Splash(Splash), - Input::Back => Selection::Ch1, - }, - Selection::Ch2 => match input { - Input::Next => Selection::Ch3, - Input::Previous => Selection::Ch1, - Input::Select => return Screens::Splash(Splash), - Input::Back => Selection::Ch2, - }, - Selection::Ch3 => match input { - Input::Next => Selection::Setup, - Input::Previous => Selection::Ch2, - Input::Select => return Screens::Splash(Splash), - Input::Back => Selection::Ch3, - }, - Selection::Setup => match input { - Input::Next => Selection::Ch1, - Input::Previous => Selection::Ch3, - Input::Select => return Screens::Setup(Setup::new()), - Input::Back => Selection::Setup, - }, + pub fn input(&mut self, input: &Input) -> Event { + self.active = match self.active { + Selection::Ch1 => match input { + Input::Next => Selection::Ch2, + Input::Previous => Selection::Setup, + Input::Select => return Event::Screen(Screens::Splash(Splash)), + Input::Back => Selection::Ch1, }, - }) + Selection::Ch2 => match input { + Input::Next => Selection::Ch3, + Input::Previous => Selection::Ch1, + Input::Select => return Event::Screen(Screens::Splash(Splash)), + Input::Back => Selection::Ch2, + }, + Selection::Ch3 => match input { + Input::Next => Selection::Setup, + Input::Previous => Selection::Ch2, + Input::Select => return Event::Screen(Screens::Splash(Splash)), + 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 } } -impl Screen for Home { +impl Draw for Home { fn draw(&self, lcd: &mut Lcd) { match &self.active { Selection::Ch1 => { diff --git a/firmware/rust/src/screen/mod.rs b/firmware/rust/src/screen/mod.rs index 09d46be..fabedcc 100644 --- a/firmware/rust/src/screen/mod.rs +++ b/firmware/rust/src/screen/mod.rs @@ -1,15 +1,28 @@ +use atmega_hal::{ + pac::TC0, + port::{mode::Output, Pin, PB0, PB1}, + Spi, +}; + mod home; mod setup; mod splash; -use crate::{lcd::Lcd, Input}; +use crate::{eeprom, lcd::Lcd, Input, BACKLIGHT}; pub use home::Home; pub use setup::Setup; pub use splash::Splash; // TODO: Only update changes instead of whole screen -pub trait Screen { +pub enum Event { + Screen(Screens), + Backlight(u8), + Contrast(u8), + None, +} + +pub trait Draw { fn draw(&self, lcd: &mut Lcd); } @@ -20,11 +33,76 @@ pub enum Screens { } impl Screens { - pub fn input(&mut self, input: &Input) { + pub fn input(&mut self, input: &Input) -> Event { match self { - Screens::Splash(_) => {} - Screens::Home(home) => *self = home.input(input), - Screens::Setup(setup) => *self = setup.input(input), + Screens::Splash(_) => Event::None, + Screens::Home(home) => home.input(input), + Screens::Setup(setup) => setup.input(input), } } } + +pub struct Screen { + lcd: Lcd, + tc0: TC0, + screen: Screens, +} + +impl Screen { + pub fn new(tc0: TC0, spi: Spi, cd: Pin, rst: Pin) -> Self { + Self { + lcd: Lcd::new(spi, cd, rst), + tc0, + screen: Screens::Splash(Splash), + } + } + + pub fn init(&mut self) { + // Init display backlight + self.tc0.tccr0a.write(|w| { + w.wgm0().pwm_fast(); + w.com0b().match_clear(); + w + }); + self.tc0.tccr0b.write(|w| { + w.wgm02().set_bit(); + w.cs0().prescale_64(); + w + }); + self.tc0.ocr0a.write(|w| unsafe { w.bits(255) }); + self.tc0 + .ocr0b + .write(|w| unsafe { w.bits(nb::block!(eeprom::read_byte(&BACKLIGHT)).unwrap()) }); + + // Init lcd display + self.lcd.init(); + + self.draw(); + } + + 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), + Screens::Setup(setup) => setup.draw(&mut self.lcd), + } + } + + pub fn input(&mut self, input: &Input) { + match self.screen.input(input) { + Event::Screen(screen) => self.screen = screen, + Event::Backlight(backlight) => self.tc0.ocr0b.write(|w| unsafe { w.bits(backlight) }), + Event::Contrast(contrast) => self.lcd.set_contrast(contrast), + 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 index a3a61bc..dd38809 100644 --- a/firmware/rust/src/screen/setup.rs +++ b/firmware/rust/src/screen/setup.rs @@ -1,84 +1,164 @@ -use super::{Home, Screen, Screens, Splash}; -use crate::{lcd::Lcd, Input, BACKLIGHT, CONTRAST}; -use core::sync::atomic::Ordering; +use super::{Draw, Event, Home, Screens}; +use crate::{eeprom, lcd::Lcd, Input, BACKLIGHT, CONTRAST}; enum Selection { - Contrast, Backlight, + BacklightEdit, + Contrast, + ContrastEdit, Back, } pub struct Setup { active: Selection, + backlight: u8, + contrast: u8, } impl Setup { pub fn new() -> Self { Self { - active: Selection::Contrast, + active: Selection::Backlight, + backlight: nb::block!(eeprom::read_byte(&BACKLIGHT)).unwrap(), + contrast: nb::block!(eeprom::read_byte(&CONTRAST)).unwrap(), } } - pub fn input(&self, input: &Input) -> Screens { - Screens::Setup(Self { - active: match self.active { - Selection::Contrast => match input { - Input::Next => Selection::Backlight, - Input::Previous => Selection::Back, - Input::Select => return Screens::Splash(Splash), - Input::Back => return Screens::Home(Home::new()), - }, - Selection::Backlight => match input { - Input::Next => Selection::Back, - Input::Previous => Selection::Contrast, - Input::Select => return Screens::Splash(Splash), - Input::Back => return Screens::Home(Home::new()), - }, - Selection::Back => match input { - Input::Next => Selection::Contrast, - Input::Previous => Selection::Backlight, - Input::Select => return Screens::Home(Home::new()), - Input::Back => return Screens::Home(Home::new()), - }, + 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, + Input::Select => return Event::Screen(Screens::Home(Home::new())), + Input::Back => return Event::Screen(Screens::Home(Home::new())), + }, + }; + + Event::None } } -impl Screen for Setup { +impl Draw for Setup { fn draw(&self, lcd: &mut Lcd) { - let contrast = CONTRAST.load(Ordering::SeqCst); - let backlight = BACKLIGHT.load(Ordering::SeqCst); - match &self.active { Selection::Contrast => { lcd.fill_area(0, 0, 33, 2, 0xFF); lcd.print_inverted(33, 0, "SETUP"); lcd.fill_area(69, 0, 33, 2, 0xFF); - lcd.print_inverted(0, 2, "CONTRAST:"); - lcd.print_u8(89, 2, 2, contrast); - lcd.print(0, 4, "BACKLIGHT:"); - lcd.print_u8(83, 2, 3, backlight); + 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.fill_area(0, 0, 33, 2, 0xFF); + lcd.print_inverted(33, 0, "SETUP"); + lcd.fill_area(69, 0, 33, 2, 0xFF); + 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.fill_area(0, 0, 33, 2, 0xFF); lcd.print_inverted(33, 0, "SETUP"); lcd.fill_area(69, 0, 33, 2, 0xFF); - lcd.print(0, 2, "CONTRAST:"); - lcd.print_u8(89, 2, 2, contrast); - lcd.print_inverted(0, 4, "BACKLIGHT:"); - lcd.print_u8(83, 2, 3, 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.fill_area(0, 0, 33, 2, 0xFF); + lcd.print_inverted(33, 0, "SETUP"); + lcd.fill_area(69, 0, 33, 2, 0xFF); + 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.fill_area(0, 0, 33, 2, 0xFF); lcd.print_inverted(33, 0, "SETUP"); lcd.fill_area(69, 0, 33, 2, 0xFF); - lcd.print(0, 2, "CONTRAST:"); - lcd.print_u8(89, 2, 2, contrast); - lcd.print(0, 4, "BACKLIGHT:"); - lcd.print_u8(83, 2, 3, backlight); + 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 index 219f314..fcbbafa 100644 --- a/firmware/rust/src/screen/splash.rs +++ b/firmware/rust/src/screen/splash.rs @@ -1,4 +1,4 @@ -use super::Screen; +use super::Draw; use crate::{ assets::{ONDERS_ORG, SACRED_CHAO}, lcd::Lcd, @@ -9,7 +9,7 @@ use nb::block; pub struct Splash; -impl Screen for Splash { +impl Draw for Splash { fn draw(&self, lcd: &mut Lcd) { let mut delay = Delay::::new(); From 134db298f6b80fad7ab77d9ab3e23b634cbc546c Mon Sep 17 00:00:00 2001 From: finga Date: Wed, 30 Mar 2022 23:01:45 +0200 Subject: [PATCH 29/37] fw-rust: Move PWM handling into screen module Move the handling of the PWM signal which generates the dimmable backlight signal into the screen module. --- firmware/rust/src/main.rs | 11 ++++--- firmware/rust/src/screen/mod.rs | 51 ++++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index 9e9eb49..505fc8b 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -58,9 +58,6 @@ impl ClockGenerator { let dp = Peripherals::take().unwrap(); let pins = pins!(dp); - // Set display PWM backlight pin as output - pins.pd5.into_output(); - // Init SPI let (spi, _) = Spi::new( dp.SPI, @@ -79,7 +76,13 @@ impl ClockGenerator { ); Self { - screen: Screen::new(dp.TC0, spi, pins.pb0.into_output(), pins.pb1.into_output()), + screen: Screen::new( + dp.TC0, + spi, + pins.pd5.into_output(), + pins.pb0.into_output(), + pins.pb1.into_output(), + ), tc1: dp.TC1, exint: dp.EXINT, delay: Delay::::new(), diff --git a/firmware/rust/src/screen/mod.rs b/firmware/rust/src/screen/mod.rs index fabedcc..621ca51 100644 --- a/firmware/rust/src/screen/mod.rs +++ b/firmware/rust/src/screen/mod.rs @@ -1,6 +1,6 @@ use atmega_hal::{ pac::TC0, - port::{mode::Output, Pin, PB0, PB1}, + port::{mode::Output, Pin, PB0, PB1, PD5}, Spi, }; @@ -45,34 +45,34 @@ impl Screens { pub struct Screen { lcd: Lcd, tc0: TC0, + pwm: Pin, screen: Screens, } impl Screen { - pub fn new(tc0: TC0, spi: Spi, cd: Pin, rst: Pin) -> Self { + pub fn new( + tc0: TC0, + spi: Spi, + pwm: Pin, + cd: Pin, + rst: Pin, + ) -> Self { Self { lcd: Lcd::new(spi, cd, rst), tc0, + pwm, screen: Screens::Splash(Splash), } } 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(); - w + w.com0b().match_clear() }); - self.tc0.tccr0b.write(|w| { - w.wgm02().set_bit(); - w.cs0().prescale_64(); - w - }); - self.tc0.ocr0a.write(|w| unsafe { w.bits(255) }); - self.tc0 - .ocr0b - .write(|w| unsafe { w.bits(nb::block!(eeprom::read_byte(&BACKLIGHT)).unwrap()) }); + self.set_backlight(nb::block!(eeprom::read_byte(&BACKLIGHT)).unwrap()); // Init lcd display self.lcd.init(); @@ -80,6 +80,29 @@ impl Screen { self.draw(); } + 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); @@ -93,7 +116,7 @@ impl Screen { pub fn input(&mut self, input: &Input) { match self.screen.input(input) { Event::Screen(screen) => self.screen = screen, - Event::Backlight(backlight) => self.tc0.ocr0b.write(|w| unsafe { w.bits(backlight) }), + Event::Backlight(backlight) => self.set_backlight(backlight), Event::Contrast(contrast) => self.lcd.set_contrast(contrast), Event::None => {} } From 52bf0e6eec0d667304289e862825f4a4ef02a28b Mon Sep 17 00:00:00 2001 From: finga Date: Fri, 1 Apr 2022 01:12:51 +0200 Subject: [PATCH 30/37] fw-rust: Prepare to implement Si5351 commands Show settings of Si5351 channels on home screen. Initialize I2c and Si5351. --- firmware/rust/.cargo/config.toml | 1 + firmware/rust/Cargo.lock | 17 +++++++ firmware/rust/Cargo.toml | 1 + firmware/rust/src/assets.rs | 20 +++++++- firmware/rust/src/lcd.rs | 84 +++++++++++++++++++++++++++++--- firmware/rust/src/main.rs | 10 +++- firmware/rust/src/screen/home.rs | 58 +++++++++++++++++++++- firmware/rust/src/screen/mod.rs | 10 +++- 8 files changed, 188 insertions(+), 13 deletions(-) diff --git a/firmware/rust/.cargo/config.toml b/firmware/rust/.cargo/config.toml index e04df30..bdea107 100644 --- a/firmware/rust/.cargo/config.toml +++ b/firmware/rust/.cargo/config.toml @@ -3,3 +3,4 @@ target = "avr-specs/avr-atmega328p.json" [unstable] build-std = ["core"] +build-std-features = ["compiler-builtins-mangled-names"] diff --git a/firmware/rust/Cargo.lock b/firmware/rust/Cargo.lock index c856d14..74b8068 100644 --- a/firmware/rust/Cargo.lock +++ b/firmware/rust/Cargo.lock @@ -55,6 +55,12 @@ 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" @@ -70,6 +76,7 @@ dependencies = [ "embedded-hal", "nb 1.0.0", "panic-halt", + "si5351", ] [[package]] @@ -157,6 +164,16 @@ 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" diff --git a/firmware/rust/Cargo.toml b/firmware/rust/Cargo.toml index aee0597..50a1fe9 100644 --- a/firmware/rust/Cargo.toml +++ b/firmware/rust/Cargo.toml @@ -15,6 +15,7 @@ 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" diff --git a/firmware/rust/src/assets.rs b/firmware/rust/src/assets.rs index 9892e41..6be82da 100644 --- a/firmware/rust/src/assets.rs +++ b/firmware/rust/src/assets.rs @@ -43,6 +43,22 @@ pub const ONDERS_ORG: [[u8; 48]; 2] = [ ], ]; +// 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], @@ -206,7 +222,9 @@ const SYM_INVALID: [&[u8]; 2] = [ ]; // TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized -pub const SYMBOL_TABLE: [&[&[u8]; 2]; 48] = [ +pub const SYMBOL_TABLE: [&[&[u8]; 2]; 50] = [ + &SYM_DOT, // '.' + &SYM_INVALID, // '/' &SYM_0, // '0' &SYM_1, // '1' &SYM_2, // '2' diff --git a/firmware/rust/src/lcd.rs b/firmware/rust/src/lcd.rs index 53dee58..02bee27 100644 --- a/firmware/rust/src/lcd.rs +++ b/firmware/rust/src/lcd.rs @@ -32,10 +32,9 @@ impl Lcd { delay.delay_ms(1_u8); let init_sequence = [ - 0x40, // (6) Set Scroll Line: Display start line 0 + 0x40, // (6) Set Scroll Line: Display start line 0 0xA1, // (13) Set SEG direction: SEG reverse 0xC0, // (14) Set COM direction: Normal COM0 - COM63 - // 0xA4, // (10) Set All Pixel On: Disable -> Set All Pixel to ON 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 @@ -43,8 +42,6 @@ impl Lcd { 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 - // 0xFA, // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C - // 0x90, // (25) Set Adv. Program Control 0: Set Temperature compensation curve to -0.11%/°C 0xAF, // (12) Set Display Enable: Display on ]; @@ -112,7 +109,7 @@ impl Lcd { 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 - 48][i as usize] { + for segment in SYMBOL_TABLE[c as usize - 46][i as usize] { block!(self.spi.send(*segment)).unwrap(); } block!(self.spi.send(0x00)).unwrap(); @@ -138,7 +135,7 @@ impl Lcd { 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 - 48][i as usize] { + for segment in SYMBOL_TABLE[c as usize - 46][i as usize] { block!(self.spi.send(!*segment)).unwrap(); } block!(self.spi.send(0xFF)).unwrap(); @@ -157,7 +154,7 @@ impl Lcd { let mut array = [0usize; 3]; for (i, item) in array.iter_mut().enumerate() { - *item = ((data / 10_u8.pow(i.try_into().unwrap())) % 10).into(); + *item = (((data / 10_u8.pow(i.try_into().unwrap())) % 10) + 2).into(); } array.reverse(); @@ -190,7 +187,7 @@ impl Lcd { let mut array = [0usize; 3]; for (i, item) in array.iter_mut().enumerate() { - *item = ((data / 10_u8.pow(i.try_into().unwrap())) % 10).into(); + *item = (((data / 10_u8.pow(i.try_into().unwrap())) % 10) + 2).into(); } array.reverse(); @@ -216,4 +213,75 @@ impl Lcd { 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_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 index 505fc8b..8f377a5 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -28,6 +28,9 @@ 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; @@ -54,7 +57,6 @@ struct ClockGenerator { impl ClockGenerator { fn new() -> Self { - // Get peripherals, pins and eeprom let dp = Peripherals::take().unwrap(); let pins = pins!(dp); @@ -82,6 +84,12 @@ impl ClockGenerator { 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, diff --git a/firmware/rust/src/screen/home.rs b/firmware/rust/src/screen/home.rs index 3fe84ba..b54c704 100644 --- a/firmware/rust/src/screen/home.rs +++ b/firmware/rust/src/screen/home.rs @@ -1,5 +1,10 @@ use super::{Draw, Event, Screens, Setup, Splash}; -use crate::{lcd::Lcd, Input}; +use crate::{ + assets::{OFF, ON, PLL_A, PLL_B}, + lcd::Lcd, + Input, +}; +use si5351::PLL; enum Selection { Ch1, @@ -8,14 +13,53 @@ enum Selection { Setup, } +struct Channel { + freq: u32, + enabled: bool, + pll: PLL, +} + +impl Channel { + 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, + }, + ); + } +} + pub struct Home { active: Selection, + channels: [Channel; 3], } impl Home { pub fn new() -> Self { Self { active: Selection::Ch1, + channels: [ + Channel { + freq: 0, + enabled: false, + pll: PLL::A, + }, + Channel { + freq: 0, + enabled: false, + pll: PLL::A, + }, + Channel { + freq: 0, + enabled: false, + pll: PLL::A, + }, + ], } } @@ -56,26 +100,38 @@ impl Draw for Home { match &self.active { Selection::Ch1 => { lcd.print_inverted(0, 0, "CH1"); + self.channels[0].print(lcd, 0); lcd.print(0, 2, "CH2"); + self.channels[1].print(lcd, 2); lcd.print(0, 4, "CH3"); + self.channels[2].print(lcd, 4); lcd.print(33, 6, "SETUP"); } Selection::Ch2 => { lcd.print(0, 0, "CH1"); + self.channels[0].print(lcd, 0); lcd.print_inverted(0, 2, "CH2"); + self.channels[1].print(lcd, 2); lcd.print(0, 4, "CH3"); + self.channels[2].print(lcd, 4); lcd.print(33, 6, "SETUP"); } Selection::Ch3 => { lcd.print(0, 0, "CH1"); + self.channels[0].print(lcd, 0); lcd.print(0, 2, "CH2"); + self.channels[1].print(lcd, 2); lcd.print_inverted(0, 4, "CH3"); + self.channels[2].print(lcd, 4); lcd.print(33, 6, "SETUP"); } Selection::Setup => { lcd.print(0, 0, "CH1"); + self.channels[0].print(lcd, 0); lcd.print(0, 2, "CH2"); + self.channels[1].print(lcd, 2); lcd.print(0, 4, "CH3"); + self.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 index 621ca51..80a5181 100644 --- a/firmware/rust/src/screen/mod.rs +++ b/firmware/rust/src/screen/mod.rs @@ -3,12 +3,13 @@ use atmega_hal::{ port::{mode::Output, Pin, PB0, PB1, PD5}, Spi, }; +use si5351::{Si5351, Si5351Device}; mod home; mod setup; mod splash; -use crate::{eeprom, lcd::Lcd, Input, BACKLIGHT}; +use crate::{eeprom, lcd::Lcd, I2c, Input, BACKLIGHT}; pub use home::Home; pub use setup::Setup; pub use splash::Splash; @@ -47,6 +48,7 @@ pub struct Screen { tc0: TC0, pwm: Pin, screen: Screens, + si5351: Si5351Device, } impl Screen { @@ -56,12 +58,14 @@ impl Screen { 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), } } @@ -76,8 +80,10 @@ impl Screen { // Init lcd display self.lcd.init(); - self.draw(); + + // Init Si5351 + self.si5351.init_adafruit_module().unwrap(); } fn set_backlight(&mut self, backlight: u8) { From 4a03c7045ffea7a4895ea907c7bced4b4636fad8 Mon Sep 17 00:00:00 2001 From: finga Date: Sat, 2 Apr 2022 00:41:38 +0200 Subject: [PATCH 31/37] knob: Improve the design of the knob [CI SKIP] --- knob/knob.scad | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/knob/knob.scad b/knob/knob.scad index 8b53ffa..943fac8 100644 --- a/knob/knob.scad +++ b/knob/knob.scad @@ -8,11 +8,14 @@ module shaft() { } translate([0, 0, -5]) - cylinder(h = 5, d = 7); + cylinder(h = 5, d = 8); } difference() { - cylinder(h = 16, d1 = 18, d2 = 15); + cylinder(h = 14, d1 = 18, d2 = 16); translate([0, 0, 5]) shaft(); } + +translate([0, 0, 14]) + cylinder(h = 2, d1 = 16, d2 = 12); From 9994b1fc40afc164e9cbed78310cf19c04e10228 Mon Sep 17 00:00:00 2001 From: finga Date: Sat, 2 Apr 2022 18:24:31 +0200 Subject: [PATCH 32/37] fw-rust: Improve debouncing when going back When pressing the button until the timer1 creates an interrupt in order to create a "back" event, there were some debouncing problems. --- firmware/rust/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index 8f377a5..5436d92 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -160,6 +160,7 @@ impl ClockGenerator { 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); From 7f1497414649382bde3adaf6c1cdd3cb36780a63 Mon Sep 17 00:00:00 2001 From: finga Date: Sat, 2 Apr 2022 18:35:48 +0200 Subject: [PATCH 33/37] fw-rust: Remove unnecessary `Draw` trait --- firmware/rust/src/screen/home.rs | 6 ++---- firmware/rust/src/screen/mod.rs | 4 ---- firmware/rust/src/screen/setup.rs | 6 ++---- firmware/rust/src/screen/splash.rs | 5 ++--- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/firmware/rust/src/screen/home.rs b/firmware/rust/src/screen/home.rs index b54c704..03aa376 100644 --- a/firmware/rust/src/screen/home.rs +++ b/firmware/rust/src/screen/home.rs @@ -1,4 +1,4 @@ -use super::{Draw, Event, Screens, Setup, Splash}; +use super::{Event, Screens, Setup, Splash}; use crate::{ assets::{OFF, ON, PLL_A, PLL_B}, lcd::Lcd, @@ -93,10 +93,8 @@ impl Home { Event::None } -} -impl Draw for Home { - fn draw(&self, lcd: &mut Lcd) { + pub fn draw(&self, lcd: &mut Lcd) { match &self.active { Selection::Ch1 => { lcd.print_inverted(0, 0, "CH1"); diff --git a/firmware/rust/src/screen/mod.rs b/firmware/rust/src/screen/mod.rs index 80a5181..e8ab78c 100644 --- a/firmware/rust/src/screen/mod.rs +++ b/firmware/rust/src/screen/mod.rs @@ -23,10 +23,6 @@ pub enum Event { None, } -pub trait Draw { - fn draw(&self, lcd: &mut Lcd); -} - pub enum Screens { Splash(Splash), Home(Home), diff --git a/firmware/rust/src/screen/setup.rs b/firmware/rust/src/screen/setup.rs index dd38809..4de2cf8 100644 --- a/firmware/rust/src/screen/setup.rs +++ b/firmware/rust/src/screen/setup.rs @@ -1,4 +1,4 @@ -use super::{Draw, Event, Home, Screens}; +use super::{Event, Home, Screens}; use crate::{eeprom, lcd::Lcd, Input, BACKLIGHT, CONTRAST}; enum Selection { @@ -106,10 +106,8 @@ impl Setup { Event::None } -} -impl Draw for Setup { - fn draw(&self, lcd: &mut Lcd) { + pub fn draw(&self, lcd: &mut Lcd) { match &self.active { Selection::Contrast => { lcd.fill_area(0, 0, 33, 2, 0xFF); diff --git a/firmware/rust/src/screen/splash.rs b/firmware/rust/src/screen/splash.rs index fcbbafa..d31774d 100644 --- a/firmware/rust/src/screen/splash.rs +++ b/firmware/rust/src/screen/splash.rs @@ -1,4 +1,3 @@ -use super::Draw; use crate::{ assets::{ONDERS_ORG, SACRED_CHAO}, lcd::Lcd, @@ -9,8 +8,8 @@ use nb::block; pub struct Splash; -impl Draw for Splash { - fn draw(&self, lcd: &mut Lcd) { +impl Splash { + pub fn draw(&self, lcd: &mut Lcd) { let mut delay = Delay::::new(); for (i, page) in SACRED_CHAO.iter().enumerate() { From c36581e703e6547e358bac8679f5371b3a7a96e0 Mon Sep 17 00:00:00 2001 From: finga Date: Sat, 2 Apr 2022 18:38:46 +0200 Subject: [PATCH 34/37] fw-rust: Make Si5351 useable In order to make the Si5351 configurable add some needed assets, create the ability to print the frequency with selected digits and add a channel screen. --- firmware/rust/src/assets.rs | 118 +++++++++-------- firmware/rust/src/lcd.rs | 73 +++++++++++ firmware/rust/src/screen/channel.rs | 191 ++++++++++++++++++++++++++++ firmware/rust/src/screen/home.rs | 82 +++--------- firmware/rust/src/screen/mod.rs | 85 ++++++++++++- 5 files changed, 430 insertions(+), 119 deletions(-) create mode 100644 firmware/rust/src/screen/channel.rs diff --git a/firmware/rust/src/assets.rs b/firmware/rust/src/assets.rs index 6be82da..3874534 100644 --- a/firmware/rust/src/assets.rs +++ b/firmware/rust/src/assets.rs @@ -146,6 +146,12 @@ const SYM_E: [&[u8]; 2] = [ &[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], @@ -191,6 +197,12 @@ const SYM_P: [&[u8]; 2] = [ &[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], @@ -215,6 +227,12 @@ const SYM_U: [&[u8]; 2] = [ &[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], @@ -223,54 +241,54 @@ const SYM_INVALID: [&[u8]; 2] = [ // 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_INVALID, // '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_INVALID, // '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_INVALID, // '_' + &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/lcd.rs b/firmware/rust/src/lcd.rs index 02bee27..f360796 100644 --- a/firmware/rust/src/lcd.rs +++ b/firmware/rust/src/lcd.rs @@ -268,6 +268,79 @@ impl Lcd { } } + 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); diff --git a/firmware/rust/src/screen/channel.rs b/firmware/rust/src/screen/channel.rs new file mode 100644 index 0000000..0ee5dc3 --- /dev/null +++ b/firmware/rust/src/screen/channel.rs @@ -0,0 +1,191 @@ +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 index 03aa376..68c064d 100644 --- a/firmware/rust/src/screen/home.rs +++ b/firmware/rust/src/screen/home.rs @@ -1,10 +1,5 @@ -use super::{Event, Screens, Setup, Splash}; -use crate::{ - assets::{OFF, ON, PLL_A, PLL_B}, - lcd::Lcd, - Input, -}; -use si5351::PLL; +use super::{Channel, Event, Screens, Setup}; +use crate::{lcd::Lcd, screen::ClockChannel, Input}; enum Selection { Ch1, @@ -13,74 +8,35 @@ enum Selection { Setup, } -struct Channel { - freq: u32, - enabled: bool, - pll: PLL, -} - -impl Channel { - 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, - }, - ); - } -} - pub struct Home { active: Selection, - channels: [Channel; 3], } impl Home { pub fn new() -> Self { Self { active: Selection::Ch1, - channels: [ - Channel { - freq: 0, - enabled: false, - pll: PLL::A, - }, - Channel { - freq: 0, - enabled: false, - pll: PLL::A, - }, - Channel { - freq: 0, - enabled: false, - pll: PLL::A, - }, - ], } } - pub fn input(&mut self, input: &Input) -> Event { + 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::Splash(Splash)), + 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::Splash(Splash)), + 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::Splash(Splash)), + Input::Select => return Event::Screen(Screens::Channel(Channel::new(channels[2]))), Input::Back => Selection::Ch3, }, Selection::Setup => match input { @@ -94,42 +50,42 @@ impl Home { Event::None } - pub fn draw(&self, lcd: &mut Lcd) { + pub fn draw(&self, lcd: &mut Lcd, channels: [ClockChannel; 3]) { match &self.active { Selection::Ch1 => { lcd.print_inverted(0, 0, "CH1"); - self.channels[0].print(lcd, 0); + channels[0].print(lcd, 0); lcd.print(0, 2, "CH2"); - self.channels[1].print(lcd, 2); + channels[1].print(lcd, 2); lcd.print(0, 4, "CH3"); - self.channels[2].print(lcd, 4); + channels[2].print(lcd, 4); lcd.print(33, 6, "SETUP"); } Selection::Ch2 => { lcd.print(0, 0, "CH1"); - self.channels[0].print(lcd, 0); + channels[0].print(lcd, 0); lcd.print_inverted(0, 2, "CH2"); - self.channels[1].print(lcd, 2); + channels[1].print(lcd, 2); lcd.print(0, 4, "CH3"); - self.channels[2].print(lcd, 4); + channels[2].print(lcd, 4); lcd.print(33, 6, "SETUP"); } Selection::Ch3 => { lcd.print(0, 0, "CH1"); - self.channels[0].print(lcd, 0); + channels[0].print(lcd, 0); lcd.print(0, 2, "CH2"); - self.channels[1].print(lcd, 2); + channels[1].print(lcd, 2); lcd.print_inverted(0, 4, "CH3"); - self.channels[2].print(lcd, 4); + channels[2].print(lcd, 4); lcd.print(33, 6, "SETUP"); } Selection::Setup => { lcd.print(0, 0, "CH1"); - self.channels[0].print(lcd, 0); + channels[0].print(lcd, 0); lcd.print(0, 2, "CH2"); - self.channels[1].print(lcd, 2); + channels[1].print(lcd, 2); lcd.print(0, 4, "CH3"); - self.channels[2].print(lcd, 4); + 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 index e8ab78c..622f761 100644 --- a/firmware/rust/src/screen/mod.rs +++ b/firmware/rust/src/screen/mod.rs @@ -3,13 +3,20 @@ use atmega_hal::{ port::{mode::Output, Pin, PB0, PB1, PD5}, Spi, }; -use si5351::{Si5351, Si5351Device}; +use si5351::{ClockOutput, Si5351, Si5351Device, PLL}; +mod channel; mod home; mod setup; mod splash; -use crate::{eeprom, lcd::Lcd, I2c, Input, BACKLIGHT}; +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; @@ -20,21 +27,55 @@ 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) -> Event { + pub fn input(&mut self, input: &Input, channels: [ClockChannel; 3]) -> Event { match self { Screens::Splash(_) => Event::None, - Screens::Home(home) => home.input(input), + Screens::Home(home) => home.input(input, channels), Screens::Setup(setup) => setup.input(input), + Screens::Channel(channel) => channel.input(input), } } } @@ -45,6 +86,7 @@ pub struct Screen { pwm: Pin, screen: Screens, si5351: Si5351Device, + channels: [ClockChannel; 3], } impl Screen { @@ -62,6 +104,26 @@ impl Screen { 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, + }, + ], } } @@ -110,16 +172,27 @@ impl Screen { match &self.screen { Screens::Splash(splash) => splash.draw(&mut self.lcd), - Screens::Home(home) => home.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) { + 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 => {} } From b51511dbabddfea1a2afa8fa8c4fa8d03aeaec36 Mon Sep 17 00:00:00 2001 From: finga Date: Sat, 2 Apr 2022 18:42:52 +0200 Subject: [PATCH 35/37] fw-rust: Improve setup screen Remove redundant match arms when handling inputs and code when drawing the setup screen. --- firmware/rust/src/screen/setup.rs | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/firmware/rust/src/screen/setup.rs b/firmware/rust/src/screen/setup.rs index 4de2cf8..a06ee70 100644 --- a/firmware/rust/src/screen/setup.rs +++ b/firmware/rust/src/screen/setup.rs @@ -99,8 +99,7 @@ impl Setup { Selection::Back => match input { Input::Next => Selection::Backlight, Input::Previous => Selection::Contrast, - Input::Select => return Event::Screen(Screens::Home(Home::new())), - Input::Back => return Event::Screen(Screens::Home(Home::new())), + _ => return Event::Screen(Screens::Home(Home::new())), }, }; @@ -108,11 +107,12 @@ impl Setup { } 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.fill_area(0, 0, 33, 2, 0xFF); - lcd.print_inverted(33, 0, "SETUP"); - lcd.fill_area(69, 0, 33, 2, 0xFF); lcd.print(0, 2, "BACKLIGHT:"); lcd.print_u8(81, 2, 3, self.backlight); lcd.print_inverted(0, 4, "CONTRAST:"); @@ -120,9 +120,6 @@ impl Setup { lcd.print(36, 6, "BACK"); } Selection::ContrastEdit => { - lcd.fill_area(0, 0, 33, 2, 0xFF); - lcd.print_inverted(33, 0, "SETUP"); - lcd.fill_area(69, 0, 33, 2, 0xFF); lcd.print(0, 2, "BACKLIGHT:"); lcd.print_u8(81, 2, 3, self.backlight); lcd.print(0, 4, "CONTRAST:"); @@ -130,9 +127,6 @@ impl Setup { lcd.print(36, 6, "BACK"); } Selection::Backlight => { - lcd.fill_area(0, 0, 33, 2, 0xFF); - lcd.print_inverted(33, 0, "SETUP"); - lcd.fill_area(69, 0, 33, 2, 0xFF); lcd.print_inverted(0, 2, "BACKLIGHT:"); lcd.print_u8(81, 2, 3, self.backlight); lcd.print(0, 4, "CONTRAST:"); @@ -140,9 +134,6 @@ impl Setup { lcd.print(36, 6, "BACK"); } Selection::BacklightEdit => { - lcd.fill_area(0, 0, 33, 2, 0xFF); - lcd.print_inverted(33, 0, "SETUP"); - lcd.fill_area(69, 0, 33, 2, 0xFF); lcd.print(0, 2, "BACKLIGHT:"); lcd.print_u8_inverted(81, 2, 3, self.backlight); lcd.print(0, 4, "CONTRAST:"); @@ -150,9 +141,6 @@ impl Setup { lcd.print(36, 6, "BACK"); } Selection::Back => { - lcd.fill_area(0, 0, 33, 2, 0xFF); - lcd.print_inverted(33, 0, "SETUP"); - lcd.fill_area(69, 0, 33, 2, 0xFF); lcd.print(0, 2, "BACKLIGHT:"); lcd.print_u8(81, 2, 3, self.backlight); lcd.print(0, 4, "CONTRAST:"); From 791a0a0245918cc1035aaa6e057c50c889e3f838 Mon Sep 17 00:00:00 2001 From: finga Date: Sat, 2 Apr 2022 18:48:23 +0200 Subject: [PATCH 36/37] fw-rust: Use `DefaultClock` type alias everywhere --- firmware/rust/src/lcd.rs | 21 ++++++++++----------- firmware/rust/src/main.rs | 4 ++-- firmware/rust/src/screen/splash.rs | 5 +++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/firmware/rust/src/lcd.rs b/firmware/rust/src/lcd.rs index f360796..414c954 100644 --- a/firmware/rust/src/lcd.rs +++ b/firmware/rust/src/lcd.rs @@ -1,5 +1,4 @@ use atmega_hal::{ - clock::MHz8, delay::Delay, port::{mode::Output, Pin, PB0, PB1}, Spi, @@ -8,7 +7,7 @@ use core::convert::TryInto; use embedded_hal::{blocking::delay::DelayMs, spi::FullDuplex}; use nb::block; -use crate::{assets::SYMBOL_TABLE, eeprom, CONTRAST}; +use crate::{assets::SYMBOL_TABLE, eeprom, DefaultClock, CONTRAST}; // TODO: Make `cd` and `rst` pins generic pins pub struct Lcd { @@ -23,7 +22,7 @@ impl Lcd { } pub fn init(&mut self) { - let mut delay = Delay::::new(); + let mut delay = Delay::::new(); // TODO: Test if delay is really needed delay.delay_ms(1_u8); @@ -71,7 +70,7 @@ impl Lcd { fn fill(&mut self, segment: u8, page: u8, width: u8, data: u8) { assert!(segment + width <= 102); - let mut delay = Delay::::new(); + let mut delay = Delay::::new(); self.move_cursor(segment, page); @@ -97,7 +96,7 @@ impl Lcd { } pub fn print(&mut self, segment: u8, page: u8, string: &str) { - let mut delay = Delay::::new(); + let mut delay = Delay::::new(); for i in 0..2 { self.move_cursor(segment, page + i); @@ -123,7 +122,7 @@ impl Lcd { } pub fn print_inverted(&mut self, segment: u8, page: u8, string: &str) { - let mut delay = Delay::::new(); + let mut delay = Delay::::new(); for i in 0..2 { self.move_cursor(segment, page + i); @@ -150,7 +149,7 @@ impl Lcd { pub fn print_u8(&mut self, segment: u8, page: u8, digits: u8, data: u8) { assert!(digits <= 3); - let mut delay = Delay::::new(); + let mut delay = Delay::::new(); let mut array = [0usize; 3]; for (i, item) in array.iter_mut().enumerate() { @@ -183,7 +182,7 @@ impl Lcd { 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 delay = Delay::::new(); let mut array = [0usize; 3]; for (i, item) in array.iter_mut().enumerate() { @@ -215,7 +214,7 @@ impl Lcd { } pub fn print_freq(&mut self, segment: u8, page: u8, data: u32) { - let mut delay = Delay::::new(); + let mut delay = Delay::::new(); let mut array = [0usize; 9]; for (i, item) in array.iter_mut().enumerate() { @@ -269,7 +268,7 @@ impl Lcd { } pub fn print_freq_digit(&mut self, segment: u8, page: u8, data: u32, digit: u8) { - let mut delay = Delay::::new(); + let mut delay = Delay::::new(); let mut array = [0usize; 9]; for (i, item) in array.iter_mut().enumerate() { @@ -342,7 +341,7 @@ impl Lcd { } pub fn print_icon(&mut self, segment: u8, page: u8, symbol: &[u8]) { - let mut delay = Delay::::new(); + let mut delay = Delay::::new(); self.move_cursor(segment, page); // TODO: This delay fixes issues, try find a better solution diff --git a/firmware/rust/src/main.rs b/firmware/rust/src/main.rs index 5436d92..18fe0d4 100644 --- a/firmware/rust/src/main.rs +++ b/firmware/rust/src/main.rs @@ -52,7 +52,7 @@ struct ClockGenerator { exint: EXINT, encoder: Rotary, PB6>, Pin, PB7>>, button: Pin, PC0>, - delay: Delay, + delay: Delay, } impl ClockGenerator { @@ -93,7 +93,7 @@ impl ClockGenerator { ), tc1: dp.TC1, exint: dp.EXINT, - delay: Delay::::new(), + 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(), } diff --git a/firmware/rust/src/screen/splash.rs b/firmware/rust/src/screen/splash.rs index d31774d..1f95039 100644 --- a/firmware/rust/src/screen/splash.rs +++ b/firmware/rust/src/screen/splash.rs @@ -1,8 +1,9 @@ use crate::{ assets::{ONDERS_ORG, SACRED_CHAO}, lcd::Lcd, + DefaultClock, }; -use atmega_hal::{clock::MHz8, delay::Delay}; +use atmega_hal::delay::Delay; use embedded_hal::{blocking::delay::DelayMs, spi::FullDuplex}; use nb::block; @@ -10,7 +11,7 @@ pub struct Splash; impl Splash { pub fn draw(&self, lcd: &mut Lcd) { - let mut delay = Delay::::new(); + let mut delay = Delay::::new(); for (i, page) in SACRED_CHAO.iter().enumerate() { lcd.move_cursor(31, 1 + i as u8); From 9b7bfb0562b3615d4acca0ebe71a3575ece05328 Mon Sep 17 00:00:00 2001 From: finga Date: Sat, 2 Apr 2022 18:49:04 +0200 Subject: [PATCH 37/37] fw-rust: Improve readability --- firmware/rust/src/lcd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/rust/src/lcd.rs b/firmware/rust/src/lcd.rs index 414c954..4d25a18 100644 --- a/firmware/rust/src/lcd.rs +++ b/firmware/rust/src/lcd.rs @@ -151,7 +151,7 @@ impl Lcd { assert!(digits <= 3); let mut delay = Delay::::new(); - let mut array = [0usize; 3]; + 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(); }