Compare commits

...

4 Commits

Author SHA1 Message Date
d91b903817 refactor(app): reformat cargo.toml 2024-11-18 16:35:28 +01:00
cb6b214e4d feat: add slint based stuff 2024-11-18 16:35:01 +01:00
a33cb94a35 feat: add lvgl based impl 2024-11-18 16:34:52 +01:00
6446310023 feat(profiler-display): add std feature 2024-11-18 16:34:44 +01:00
24 changed files with 7179 additions and 15 deletions

View File

@ -22,29 +22,33 @@ path = "src/bin/calibrate.rs"
[lib]
[dependencies]
bit_field = "0.10.2"
display-interface = "0.5.0"
bit_field = "0.10.2"
display-interface = "0.5.0"
display-interface-spi = "0.5.0"
embassy-embedded-hal = "0.2.0"
embassy-embedded-hal = "0.2.0"
# defmt = "0.3.8"
# defmt-rtt = "0.4.1"
embassy-executor = { version = "0.6.0", features = ["log"] }
embassy-executor = { version = "0.6.0", features = ["log"] }
embassy-sync = "0.6.0"
embassy-time = { version = "0.3.1", features = ["generic-queue-8"] }
embassy-time = { version = "0.3.1", features = ["generic-queue-8"] }
embedded-graphics = "0.8.1"
embedded-graphics-profiler-display = { version = "0.1.0", path = "../embedded-graphics-profiler-display" }
embedded-hal = "1.0.0"
embedded-sdmmc = "0.8.0"
esp-backtrace = { version = "0.14.1", features = ["esp32", "println", "exception-handler", "panic-handler"] }
esp-hal = { version = "0.20.1", features = ["esp32", "log", "async"] }
esp-hal-embassy = { version = "0.3.0", features = ["esp32", "log"] }
esp-backtrace = { version = "0.14.1", features = [
"esp32",
"exception-handler",
"panic-handler",
"println",
] }
esp-hal = { version = "0.20.1", features = ["async", "esp32", "log"] }
esp-hal-embassy = { version = "0.3.0", features = ["esp32", "log"] }
esp-println = { version = "0.11.0", features = ["esp32", "log"] }
format_no_std = "1.2.0"
heapless = { version = "0.8.0", features = ["serde"] }
kolibri-embedded-gui = { git = "https://github.com/Yandrik/kolibri.git", version = "0.0.0-alpha.1", branch = "optimizations" }
mipidsi = "0.8.0"
profont = "0.7.0"
static_cell = { version = "2.1.0", features = ["nightly"] }
static_cell = { version = "2.1.0", features = ["nightly"] }
ufmt = "0.2.0"
xpt2046 = { git = "https://github.com/Yandrik/xpt2046.git", version = "0.3.1" }

View File

@ -3,6 +3,9 @@ name = "embedded-graphics-profiler-display"
version = "0.1.0"
edition = "2021"
[features]
std = []
[dependencies]
atomic = "0.6.0"
embassy-time = "0.3.2"

View File

@ -1,4 +1,4 @@
#![no_std]
#![cfg_attr(not(feature = "std"), no_std)]
#![no_main]
mod profiler;

View File

@ -1,4 +1,7 @@
use atomic::Atomic;
#[cfg(feature = "std")]
use std::time::{Duration, Instant};
#[cfg(not(feature = "std"))]
use embassy_time::{Duration, Instant};
use embedded_graphics::draw_target::DrawTarget;
use embedded_graphics::{Drawable, Pixel};

View File

@ -4,7 +4,7 @@ target = "xtensa-esp32-espidf"
[target.xtensa-esp32-espidf]
linker = "ldproxy"
# runner = "espflash --monitor" # Select this runner for espflash v1.x.x
runner = "espflash flash --monitor" # Select this runner for espflash v2.x.x
runner = "espflash flash --monitor --baud 921600" # Select this runner for espflash v2.x.x
rustflags = [
# Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110
"--cfg",
@ -31,3 +31,6 @@ CROSS_COMPILE = "xtensa-esp32-elf"
# Directory for custom fonts (written in C) that Lvgl can use
LVGL_FONTS_DIR = {relative = true, value = "custom-fonts"}
PATH="/usr/lib64/ccache:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/var/lib/snapd/snap/bin:/home/yannik/.rustup/toolchains/esp/xtensa-esp32s2-elf/esp-2021r2-patch5-8_4_0/xtensa-esp32s2-elf/bin:/home/yannik/.rustup/toolchains/esp/xtensa-esp32s3-elf/esp-2021r2-patch5-8_4_0/xtensa-esp32s3-elf/bin:/home/yannik/.rustup/toolchains/esp/xtensa-esp32-elf/esp-2021r2-patch5-8_4_0/xtensa-esp32-elf/bin:/home/yannik/.rustup/toolchains/esp/riscv32-esp-elf/esp-2021r2-patch5-8_4_0/riscv32-esp-elf/bin:"
LIBCLANG_PATH="/home/yannik/.rustup/toolchains/esp/xtensa-esp32-elf-clang/esp-15.0.0-20221201/esp-clang/lib"

View File

@ -15,6 +15,12 @@ opt-level = "s"
debug = true # Symbols are nice and they don't increase the size on Flash
opt-level = "z"
[[bin]]
name = "starter"
[[bin]]
name = "timer"
[features]
default = ["embassy", "esp-idf-svc/native", "std"]
@ -50,6 +56,12 @@ display-interface-spi = "0.5.0"
mipidsi = "0.8.0"
static_cell = "2.1.0"
xpt2046 = { git = "https://github.com/Yandrik/xpt2046.git", version = "0.3.1" }
embedded-graphics-profiler-display = { version = "0.1.0", path = "../embedded-graphics-profiler-display", features = ["std"] }
[build-dependencies]
embuild = "0.32.0"

View File

@ -1,6 +1,6 @@
# Rust often needs a bit of an extra main task stack size compared to C (the default is 3K)
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8000
# CONFIG_ESP_MAIN_TASK_STACK_SIZE=16000
CONFIG_ESP_MAIN_TASK_STACK_SIZE=48000
# Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default).
# This allows to use 1 ms granuality for thread sleeps (10 ms by default).

View File

@ -0,0 +1,266 @@
use std::{
cell::RefCell,
sync::mpsc::channel,
thread,
time::{Duration, Instant},
};
use std::cmp::min;
use cstr_core::CString;
use display_interface_spi::SPIInterface;
use embedded_graphics_core::{draw_target::DrawTarget, prelude::Point};
use esp_idf_hal::spi::SpiSingleDeviceDriver;
use esp_idf_hal::{
delay::{self, Delay},
gpio::*,
peripherals::Peripherals,
spi::{config::DriverConfig, Dma, SpiConfig, SpiDeviceDriver},
units::FromValueType, // for converting 26MHz to value
};
use lvgl::{
font::Font,
input_device::{
pointer::{Pointer, PointerInputData},
InputDriver,
},
style::Style,
widgets::{Btn, Label},
Align,
Color,
Display,
DrawBuffer,
LvError,
Part,
TextAlign,
Widget,
};
use mipidsi::{
models::ILI9486Rgb565,
options::{ColorInversion, ColorOrder, Orientation, Rotation},
Builder,
};
use xpt2046::Xpt2046;
use embedded_graphics_profiler_display::ProfilerDisplay;
fn main() -> Result<(), LvError> {
const HOR_RES: u32 = 320;
const VER_RES: u32 = 240;
const LINES: u32 = 20;
// It is necessary to call this function once. Otherwise some patches to the
// runtime implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
esp_idf_svc::sys::link_patches();
// Initialize lvgl
lvgl::init();
// Bind the log crate to the ESP Logging facilities
esp_idf_svc::log::EspLogger::initialize_default();
log::info!("================= Starting App =======================");
let peripherals = Peripherals::take().unwrap();
// #[allow(unused)]
let pins = peripherals.pins;
let sclk = pins.gpio14;
let miso = pins.gpio12;
let mosi = pins.gpio13;
let cs = pins.gpio15;
let dc = pins.gpio2;
let spi = SpiDeviceDriver::new_single(
peripherals.spi2,
sclk, // sclk
mosi, // sdo
Some(miso), // sdi
Some(cs), // cs
&DriverConfig::new().dma(Dma::Channel1(4096)),
&SpiConfig::new().write_only(true).baudrate(10.MHz().into()),
)
.unwrap();
// let rst = PinDriver::output(pins.gpio33).unwrap();
let dc = PinDriver::output(dc).unwrap();
let di = SPIInterface::new(spi, dc);
// Turn backlight on
let mut bklt = PinDriver::output(pins.gpio21).unwrap();
bklt.set_high().unwrap();
// Configuration for M5Stack Core Development Kit V1.0
// Puts display in landscape mode with the three buttons at the bottom of screen
// let mut m5stack_display = Builder::ili9342c_rgb565(di)
// .with_display_size(320, 240)
// .with_color_order(ColorOrder::Bgr)
// .with_orientation(Orientation::Portrait(false))
// .with_invert_colors(mipidsi::ColorInversion::Inverted)
// .init(&mut delay::Ets, Some(rst))
// .unwrap();
// 0.6.0
// let mut raw_display = Builder::ili9342c_rgb565(di)
// .with_orientation(Orientation::Portrait(false))
// .with_color_order(ColorOrder::Bgr)
// .with_invert_colors(true)
// .init(&mut Delay::new_default(), None::<PinDriver<AnyOutputPin, Output>>)
// .unwrap();
let raw_display = Builder::new(ILI9486Rgb565, di)
.orientation(Orientation {
rotation: Rotation::Deg90,
mirrored: true,
})
.color_order(ColorOrder::Bgr)
.invert_colors(ColorInversion::Inverted)
.init(&mut Delay::new_default())
.unwrap();
let mut raw_display = ProfilerDisplay::new(raw_display);
// Stack size value - 20,000 for 10 lines, 40,000 for 20 lines
// let (touch_send, touch_recv) = channel();
let touch_irq = pins.gpio36;
let touch_mosi = pins.gpio32;
let touch_miso = pins.gpio39;
let touch_clk = pins.gpio25;
let touch_cs = pins.gpio33;
let mut touch_driver = Xpt2046::new(
SpiDeviceDriver::new_single(
peripherals.spi3,
touch_clk,
touch_mosi,
Some(touch_miso),
Some(touch_cs),
&DriverConfig::new(),
&SpiConfig::new().write_only(true).baudrate(2.MHz().into()),
)
.unwrap(),
PinDriver::input(touch_irq).unwrap(),
xpt2046::Orientation::LandscapeFlipped,
);
touch_driver.set_num_samples(1);
touch_driver.init(&mut Delay::new_default()).unwrap();
let touch_driver = RefCell::new(touch_driver);
let _lvgl_thread = thread::Builder::new()
.stack_size(40_000)
.spawn(move || {
println!("thread started");
let buffer = DrawBuffer::<{ (HOR_RES * LINES) as usize }>::default();
let display = Display::register(buffer, HOR_RES, VER_RES, |refresh| {
raw_display.draw_iter(refresh.as_pixels()).unwrap();
})
.unwrap();
// Register a new input device that's capable of reading the current state of
// the input
// let mut last_touch = RefCell::new(None);
let _touch_screen = Pointer::register(
|| {
let mut td_ref = touch_driver.borrow_mut();
td_ref.run().expect("Running Touch driver failed");
if td_ref.is_touched() {
let point = td_ref.get_touch_point();
PointerInputData::Touch(point).pressed().once()
} else {
PointerInputData::Touch(Point::new(0, 0)).released().once()
}
},
&display,
);
// Create screen and widgets
let mut screen = display.get_scr_act().unwrap();
let mut screen_style = Style::default();
screen_style.set_bg_color(Color::from_rgb((0, 0, 0)));
screen_style.set_radius(0);
screen.add_style(Part::Main, &mut screen_style);
let mut time = Label::new().unwrap();
let mut style_time = Style::default();
style_time.set_text_color(Color::from_rgb((255, 255, 255))); // white
style_time.set_text_align(TextAlign::Center);
// Custom font requires lvgl-sys in Cargo.toml and 'use lvgl_sys' in this file
style_time.set_text_font(unsafe { Font::new_raw(lvgl_sys::gotham_bold_80) });
time.add_style(Part::Main, &mut style_time);
// Time text will be centered in screen
time.set_align(Align::Center, 0, 0);
let mut button = Btn::create(&mut screen).unwrap();
button.set_align(Align::LeftMid, 30, 0);
button.set_size(180, 80);
let mut btn_lbl = Label::create(&mut button).unwrap();
btn_lbl.set_text(CString::new("Click me!").unwrap().as_c_str());
let mut btn_state = false;
button.on_event(|_btn, event| {
// println!("Button received event: {:?}", event);
if let lvgl::Event::Clicked = event {
if btn_state {
let nt = CString::new("Click me!").unwrap();
btn_lbl.set_text(nt.as_c_str()).unwrap();
} else {
let nt = CString::new("Clicked!").unwrap();
btn_lbl.set_text(nt.as_c_str()).unwrap();
}
btn_state = !btn_state;
}
});
let mut i = 0;
loop {
let start_time = Instant::now();
if i > 59 {
i = 0;
}
let val = CString::new(format!("21:{:02}", i)).unwrap();
time.set_text(&val).unwrap();
i += 1;
let start_draw_time = Instant::now();
lvgl::task_handler();
// Simulate clock - so sleep for one second so time text is incremented in
// seconds
delay::FreeRtos::delay_ms(10);
lvgl::tick_inc(Instant::now().duration_since(start_time));
let end_time = Instant::now();
let draw_time = raw_display.get_time();
let prep_time = start_draw_time - start_time;
let proc_time = end_time - start_draw_time;
let proc_time = proc_time - min(draw_time, proc_time);
if draw_time.as_micros() > 0 {
println!(
"draw time: {}.{:03}ms | prep time: {}.{:03}ms | proc time: {}.{:03}ms | total time: {}.{:03}ms",
draw_time.as_millis(),
draw_time.as_micros() % 100,
prep_time.as_millis(),
prep_time.as_micros() % 100,
proc_time.as_millis(),
proc_time.as_micros() % 100,
(draw_time + prep_time + proc_time).as_millis(),
(draw_time + prep_time + proc_time).as_micros() % 100, );
}
raw_display.reset_time();
}
})
.unwrap();
loop {
// Don't exit application
delay::FreeRtos::delay_ms(1_000_000);
}
}

432
lvgl-based/src/bin/timer.rs Normal file
View File

@ -0,0 +1,432 @@
use std::{
cell::RefCell,
cmp::min,
sync::mpsc::channel,
thread,
time::{Duration, Instant},
};
use cstr_core::CString;
use display_interface_spi::SPIInterface;
use embedded_graphics_core::{draw_target::DrawTarget, prelude::Point};
use embedded_graphics_profiler_display::ProfilerDisplay;
use esp_idf_hal::spi::SpiSingleDeviceDriver;
use esp_idf_hal::{
delay::{self, Delay},
gpio::*,
peripherals::Peripherals,
spi::{config::DriverConfig, Dma, SpiConfig, SpiDeviceDriver},
units::FromValueType, // for converting 26MHz to value
};
use lvgl::{
font::Font,
input_device::{
pointer::{Pointer, PointerInputData},
InputDriver,
},
style::Style,
widgets::{Btn, Label},
Align,
Color,
Display,
DrawBuffer,
Event,
LvError,
Part,
TextAlign,
Widget,
};
use mipidsi::{
models::ILI9486Rgb565,
options::{ColorInversion, ColorOrder, Orientation, Rotation},
Builder,
};
use xpt2046::Xpt2046;
struct AppData {
timer_start: Instant,
timer_set_duration: Duration,
timer_remaining_duration: Duration,
timer_running: bool,
timer_paused: bool,
}
impl AppData {
fn new() -> Self {
Self {
timer_start: Instant::now(),
timer_set_duration: Duration::from_secs(10),
timer_remaining_duration: Duration::from_secs(10),
timer_running: false,
timer_paused: false,
}
}
fn set_timer_duration(&mut self, duration: Duration) {
self.timer_set_duration = duration;
}
fn add_secs(&mut self, secs: u64) {
self.set_timer_duration(
self.timer_set_duration
.checked_add(Duration::from_secs(secs))
.unwrap_or(Duration::from_secs(5999))
.min(Duration::from_secs(5999)),
);
}
fn sub_secs(&mut self, secs: u64) {
self.set_timer_duration(
self.timer_set_duration
.checked_sub(Duration::from_secs(secs))
.unwrap_or(Duration::from_secs(10))
.max(Duration::from_secs(10)),
);
}
fn start_timer(&mut self) {
if !self.timer_paused {
self.timer_remaining_duration = self.timer_set_duration;
}
self.timer_start = Instant::now();
self.timer_running = true;
self.timer_paused = false;
}
fn pause_timer(&mut self) {
self.timer_paused = true;
self.timer_remaining_duration = self
.timer_remaining_duration
.checked_sub(self.timer_start.elapsed())
.unwrap_or(Duration::from_secs(0));
}
fn reset_timer(&mut self) {
self.timer_start = Instant::now();
self.timer_paused = false;
self.timer_running = false;
self.timer_remaining_duration = self.timer_set_duration;
}
fn remaining(&self) -> Duration {
if self.timer_stopped() {
self.timer_set_duration
} else if self.timer_paused() {
self.timer_remaining_duration
} else {
self.timer_remaining_duration
.checked_sub(self.timer_start.elapsed())
.unwrap_or(Duration::from_secs(0))
}
}
fn timer_stopped(&self) -> bool {
!self.timer_running
}
fn timer_paused(&self) -> bool {
self.timer_running && self.timer_paused
}
fn timer_running(&self) -> bool {
self.timer_running && !self.timer_paused
}
fn timer_finished(&self) -> bool {
self.timer_running && self.remaining() == Duration::from_secs(0)
}
}
fn main() -> Result<(), LvError> {
const HOR_RES: u32 = 320;
const VER_RES: u32 = 240;
const LINES: u32 = 20;
// It is necessary to call this function once. Otherwise some patches to the
// runtime implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
esp_idf_svc::sys::link_patches();
// Initialize lvgl
lvgl::init();
// Bind the log crate to the ESP Logging facilities
esp_idf_svc::log::EspLogger::initialize_default();
log::info!("================= Starting App =======================");
let peripherals = Peripherals::take().unwrap();
// #[allow(unused)]
let pins = peripherals.pins;
let sclk = pins.gpio14;
let miso = pins.gpio12;
let mosi = pins.gpio13;
let cs = pins.gpio15;
let dc = pins.gpio2;
let spi = SpiDeviceDriver::new_single(
peripherals.spi2,
sclk, // sclk
mosi, // sdo
Some(miso), // sdi
Some(cs), // cs
&DriverConfig::new().dma(Dma::Channel1(4096)),
&SpiConfig::new().write_only(true).baudrate(10.MHz().into()),
)
.unwrap();
// let rst = PinDriver::output(pins.gpio33).unwrap();
let dc = PinDriver::output(dc).unwrap();
let di = SPIInterface::new(spi, dc);
// Turn backlight on
let mut bklt = PinDriver::output(pins.gpio21).unwrap();
bklt.set_high().unwrap();
// Configuration for M5Stack Core Development Kit V1.0
// Puts display in landscape mode with the three buttons at the bottom of screen
// let mut m5stack_display = Builder::ili9342c_rgb565(di)
// .with_display_size(320, 240)
// .with_color_order(ColorOrder::Bgr)
// .with_orientation(Orientation::Portrait(false))
// .with_invert_colors(mipidsi::ColorInversion::Inverted)
// .init(&mut delay::Ets, Some(rst))
// .unwrap();
// 0.6.0
// let mut raw_display = Builder::ili9342c_rgb565(di)
// .with_orientation(Orientation::Portrait(false))
// .with_color_order(ColorOrder::Bgr)
// .with_invert_colors(true)
// .init(&mut Delay::new_default(), None::<PinDriver<AnyOutputPin, Output>>)
// .unwrap();
let raw_display = Builder::new(ILI9486Rgb565, di)
.orientation(Orientation {
rotation: Rotation::Deg90,
mirrored: true,
})
.color_order(ColorOrder::Bgr)
.invert_colors(ColorInversion::Inverted)
.init(&mut Delay::new_default())
.unwrap();
let mut raw_display = ProfilerDisplay::new(raw_display);
// Stack size value - 20,000 for 10 lines, 40,000 for 20 lines
// let (touch_send, touch_recv) = channel();
let touch_irq = pins.gpio36;
let touch_mosi = pins.gpio32;
let touch_miso = pins.gpio39;
let touch_clk = pins.gpio25;
let touch_cs = pins.gpio33;
let mut touch_driver = Xpt2046::new(
SpiDeviceDriver::new_single(
peripherals.spi3,
touch_clk,
touch_mosi,
Some(touch_miso),
Some(touch_cs),
&DriverConfig::new(),
&SpiConfig::new().write_only(true).baudrate(2.MHz().into()),
)
.unwrap(),
PinDriver::input(touch_irq).unwrap(),
xpt2046::Orientation::LandscapeFlipped,
);
touch_driver.set_num_samples(1);
touch_driver.init(&mut Delay::new_default()).unwrap();
let touch_driver = RefCell::new(touch_driver);
let _lvgl_thread = thread::Builder::new()
.stack_size(40_000)
.spawn(move || {
println!("thread started");
let mut appdata = AppData::new();
let buffer = DrawBuffer::<{ (HOR_RES * LINES) as usize }>::default();
let display = Display::register(buffer, HOR_RES, VER_RES, |refresh| {
raw_display.draw_iter(refresh.as_pixels()).unwrap();
})
.unwrap();
// Register a new input device that's capable of reading the current state of
// the input
// let mut last_touch = RefCell::new(None);
let _touch_screen = Pointer::register(
|| {
let mut td_ref = touch_driver.borrow_mut();
td_ref.run().expect("Running Touch driver failed");
if td_ref.is_touched() {
let point = td_ref.get_touch_point();
// println!("touched {:?}", point);
PointerInputData::Touch(Point::new(point.x + 20, 240 - point.y)).pressed().once()
} else {
// println!("untouched");
PointerInputData::Touch(Point::new(0, 0)).released().once()
}
},
&display,
);
// Create screen and widgets
let mut screen = display.get_scr_act().unwrap();
let mut screen_style = Style::default();
screen_style.set_bg_color(Color::from_rgb((0, 0, 0)));
screen_style.set_radius(0);
screen.add_style(Part::Main, &mut screen_style);
let mut time = Label::new().unwrap();
let mut style_time = Style::default();
style_time.set_text_color(Color::from_rgb((255, 255, 255))); // white
style_time.set_text_align(TextAlign::Center);
// Custom font requires lvgl-sys in Cargo.toml and 'use lvgl_sys' in this file
time.add_style(Part::Main, &mut style_time);
// Time text will be centered in screen
time.set_align(Align::Center, 0, 0);
let mut button_add = Btn::create(&mut screen).unwrap();
// button_add.set_align(Align::Center, -50, -100);
button_add.set_pos(130, 150);
button_add.set_size(30, 30);
let mut btn_lbl1 = Label::create(&mut button_add).unwrap();
btn_lbl1.set_text(CString::new(b"+").unwrap().as_c_str());
button_add.on_event(|_btn, event| {
if let Event::Pressed = event {
println!("pressed");
if appdata.timer_stopped() {
appdata.add_secs(10);
}
}
// println!("Button received event: {:?}", event);
});
let mut button_sub = Btn::create(&mut screen).unwrap();
button_sub.set_pos(170, 150);
button_sub.set_size(30, 30);
let mut btn_lbl2 = Label::create(&mut button_sub).unwrap();
btn_lbl2.set_text(CString::new(b"-").unwrap().as_c_str());
button_sub.on_event(|_btn, event| {
if let Event::Pressed = event {
if appdata.timer_stopped() {
appdata.sub_secs(10);
}
}
});
let mut button_reset = Btn::create(&mut screen).unwrap();
button_reset.set_pos(123, 190);
button_reset.set_size(35, 35);
let mut btn_lbl3 = Label::create(&mut button_reset).unwrap();
btn_lbl3.set_text(CString::new(b"\xEF\x80\xA1").unwrap().as_c_str());
const PLAY: &'static [u8; 3] = b"\xEF\x81\x8B";
const PAUSE: &'static [u8; 3] = b"\xEF\x81\x8C";
const STOP: &'static [u8; 3] = b"\xEF\x81\x8D";
let mut button_start_stop = Btn::create(&mut screen).unwrap();
button_start_stop.set_pos(173, 190);
button_start_stop.set_size(35, 35);
let mut btn_lbl4 = Label::create(&mut button_start_stop).unwrap();
btn_lbl4.set_text(CString::new(b"\xEF\x81\x8B").unwrap().as_c_str());
button_reset.on_event(|_btn, event| {
if let Event::Pressed = event {
appdata.reset_timer();
btn_lbl4.set_text(CString::new(PLAY).unwrap().as_c_str());
}
});
button_start_stop.on_event(|_btn, event| {
if let Event::Pressed = event {
if appdata.timer_finished() {
// println!("Resetting finished timer");
appdata.reset_timer();
btn_lbl4.set_text(CString::new(PLAY).unwrap().as_c_str());
} else if appdata.timer_running() {
// println!("Pausing timer");
appdata.pause_timer();
btn_lbl4.set_text(CString::new(PLAY).unwrap().as_c_str());
} else {
// println!("Starting timer");
appdata.start_timer();
if appdata.timer_finished() {
// println!("Timer already finished");
btn_lbl4.set_text(CString::new(STOP).unwrap().as_c_str());
} else {
// println!("Timer running");
btn_lbl4.set_text(CString::new(PAUSE).unwrap().as_c_str());
}
}
}
});
let mut was_finished = false;
loop {
let start_time = Instant::now();
let rem_time = appdata.remaining();
let val = CString::new(format!("{:02}:{:02}:{:03}",
rem_time.as_secs() / 60,
rem_time.as_secs() % 60,
rem_time.as_millis() % 1000)).unwrap();
time.set_text(&val).unwrap();
if !was_finished && appdata.timer_finished() {
was_finished = true;
btn_lbl4.set_text(CString::new(STOP).unwrap().as_c_str());
}
was_finished = appdata.timer_finished();
let start_draw_time = Instant::now();
lvgl::task_handler();
// Simulate clock - so sleep for one second so time text is incremented in
// seconds
// delay::FreeRtos::delay_ms(1);
lvgl::tick_inc(Instant::now().duration_since(start_time));
let end_time = Instant::now();
let draw_time = raw_display.get_time();
let prep_time = start_draw_time - start_time;
let proc_time = end_time - start_draw_time;
let proc_time = proc_time - min(draw_time, proc_time);
if draw_time.as_micros() > 0 {
println!(
"draw time: {}.{:03}ms | prep time: {}.{:03}ms | proc time: {}.{:03}ms | total time: {}.{:03}ms",
draw_time.as_millis(),
draw_time.as_micros() % 100,
prep_time.as_millis(),
prep_time.as_micros() % 100,
proc_time.as_millis(),
proc_time.as_micros() % 100,
(draw_time + prep_time + proc_time).as_millis(),
(draw_time + prep_time + proc_time).as_micros() % 100, );
}
raw_display.reset_time();
delay::FreeRtos::delay_ms(1);
}
})
.unwrap();
loop {
// Don't exit application
delay::FreeRtos::delay_ms(1_000_000);
}
}

View File

@ -125,7 +125,6 @@ fn main() -> Result<(), LvError> {
style_time.set_text_align(TextAlign::Center);
// Custom font requires lvgl-sys in Cargo.toml and 'use lvgl_sys' in this file
style_time.set_text_font(unsafe { Font::new_raw(lvgl_sys::gotham_bold_80) });
time.add_style(Part::Main, &mut style_time);
@ -148,7 +147,7 @@ fn main() -> Result<(), LvError> {
lvgl::task_handler();
// Simulate clock - so sleep for one second so time text is incremented in seconds
delay::FreeRtos::delay_ms(1000);
delay::FreeRtos::delay_ms(1);
lvgl::tick_inc(Instant::now().duration_since(start));
}

322
lvgl-based/tree.text Normal file
View File

@ -0,0 +1,322 @@
rust-m5stack-lvgl-demo v0.1.0 (/home/yannik/repos/kolibri-cyd-tester-app-embassy/lvgl-based)
├── cstr_core v0.2.6
│ ├── cty v0.2.2
│ └── memchr v2.7.4
├── display-interface-spi v0.5.0
│ ├── byte-slice-cast v1.2.2
│ ├── display-interface v0.5.0
│ ├── embedded-hal v1.0.0
│ └── embedded-hal-async v1.0.0
│ └── embedded-hal v1.0.0
├── embedded-graphics-core v0.4.0
│ ├── az v1.2.1
│ └── byteorder v1.5.0
├── esp-idf-hal v0.44.1
│ ├── atomic-waker v1.1.2
│ ├── critical-section v1.2.0
│ ├── embassy-sync v0.6.0
│ │ ├── cfg-if v1.0.0
│ │ ├── critical-section v1.2.0
│ │ ├── embedded-io-async v0.6.1
│ │ │ └── embedded-io v0.6.1
│ │ ├── futures-util v0.3.31
│ │ │ ├── futures-core v0.3.31
│ │ │ ├── futures-task v0.3.31
│ │ │ ├── pin-project-lite v0.2.15
│ │ │ └── pin-utils v0.1.0
│ │ └── heapless v0.8.0
│ │ ├── hash32 v0.3.1
│ │ │ └── byteorder v1.5.0
│ │ └── stable_deref_trait v1.2.0
│ ├── embedded-can v0.4.1
│ │ └── nb v1.1.0
│ ├── embedded-hal v0.2.7
│ │ ├── nb v0.1.3
│ │ │ └── nb v1.1.0
│ │ └── void v1.0.2
│ ├── embedded-hal v1.0.0
│ ├── embedded-hal-async v1.0.0 (*)
│ ├── embedded-hal-nb v1.0.0
│ │ ├── embedded-hal v1.0.0
│ │ └── nb v1.1.0
│ ├── embedded-io v0.6.1
│ ├── embedded-io-async v0.6.1 (*)
│ ├── enumset v1.1.5
│ │ └── enumset_derive v0.10.0 (proc-macro)
│ │ ├── darling v0.20.10
│ │ │ ├── darling_core v0.20.10
│ │ │ │ ├── fnv v1.0.7
│ │ │ │ ├── ident_case v1.0.1
│ │ │ │ ├── proc-macro2 v1.0.89
│ │ │ │ │ └── unicode-ident v1.0.13
│ │ │ │ ├── quote v1.0.37
│ │ │ │ │ └── proc-macro2 v1.0.89 (*)
│ │ │ │ └── syn v2.0.87
│ │ │ │ ├── proc-macro2 v1.0.89 (*)
│ │ │ │ ├── quote v1.0.37 (*)
│ │ │ │ └── unicode-ident v1.0.13
│ │ │ └── darling_macro v0.20.10 (proc-macro)
│ │ │ ├── darling_core v0.20.10 (*)
│ │ │ ├── quote v1.0.37 (*)
│ │ │ └── syn v2.0.87 (*)
│ │ ├── proc-macro2 v1.0.89 (*)
│ │ ├── quote v1.0.37 (*)
│ │ └── syn v2.0.87 (*)
│ ├── esp-idf-sys v0.35.0
│ │ ├── build-time v0.1.3 (proc-macro)
│ │ │ ├── chrono v0.4.38
│ │ │ │ ├── iana-time-zone v0.1.61
│ │ │ │ └── num-traits v0.2.19
│ │ │ │ [build-dependencies]
│ │ │ │ └── autocfg v1.4.0
│ │ │ ├── once_cell v1.20.2
│ │ │ ├── proc-macro2 v1.0.89 (*)
│ │ │ ├── quote v1.0.37 (*)
│ │ │ └── syn v2.0.87 (*)
│ │ ├── const_format v0.2.33
│ │ │ └── const_format_proc_macros v0.2.33 (proc-macro)
│ │ │ ├── proc-macro2 v1.0.89 (*)
│ │ │ ├── quote v1.0.37 (*)
│ │ │ └── unicode-xid v0.2.6
│ │ └── libc v0.2.162
│ │ [build-dependencies]
│ │ ├── anyhow v1.0.93
│ │ ├── bindgen v0.69.5
│ │ │ ├── bitflags v2.6.0
│ │ │ ├── cexpr v0.6.0
│ │ │ │ └── nom v7.1.3
│ │ │ │ ├── memchr v2.7.4
│ │ │ │ └── minimal-lexical v0.2.1
│ │ │ ├── clang-sys v1.8.1
│ │ │ │ ├── glob v0.3.1
│ │ │ │ ├── libc v0.2.162
│ │ │ │ └── libloading v0.8.5
│ │ │ │ └── cfg-if v1.0.0
│ │ │ │ [build-dependencies]
│ │ │ │ └── glob v0.3.1
│ │ │ ├── itertools v0.12.1
│ │ │ │ └── either v1.13.0
│ │ │ ├── lazy_static v1.5.0
│ │ │ ├── lazycell v1.3.0
│ │ │ ├── log v0.4.22
│ │ │ ├── prettyplease v0.2.25
│ │ │ │ ├── proc-macro2 v1.0.89 (*)
│ │ │ │ └── syn v2.0.87 (*)
│ │ │ ├── proc-macro2 v1.0.89 (*)
│ │ │ ├── quote v1.0.37 (*)
│ │ │ ├── regex v1.11.1
│ │ │ │ ├── aho-corasick v1.1.3
│ │ │ │ │ └── memchr v2.7.4
│ │ │ │ ├── memchr v2.7.4
│ │ │ │ ├── regex-automata v0.4.9
│ │ │ │ │ ├── aho-corasick v1.1.3 (*)
│ │ │ │ │ ├── memchr v2.7.4
│ │ │ │ │ └── regex-syntax v0.8.5
│ │ │ │ └── regex-syntax v0.8.5
│ │ │ ├── rustc-hash v1.1.0
│ │ │ ├── shlex v1.3.0
│ │ │ ├── syn v2.0.87 (*)
│ │ │ └── which v4.4.2
│ │ │ ├── either v1.13.0
│ │ │ ├── home v0.5.9
│ │ │ └── rustix v0.38.40
│ │ │ ├── bitflags v2.6.0
│ │ │ └── linux-raw-sys v0.4.14
│ │ ├── cargo_metadata v0.18.1
│ │ │ ├── camino v1.1.9
│ │ │ │ └── serde v1.0.215
│ │ │ │ └── serde_derive v1.0.215 (proc-macro)
│ │ │ │ ├── proc-macro2 v1.0.89 (*)
│ │ │ │ ├── quote v1.0.37 (*)
│ │ │ │ └── syn v2.0.87 (*)
│ │ │ ├── cargo-platform v0.1.8
│ │ │ │ └── serde v1.0.215 (*)
│ │ │ ├── semver v1.0.23
│ │ │ │ └── serde v1.0.215 (*)
│ │ │ ├── serde v1.0.215 (*)
│ │ │ ├── serde_json v1.0.132
│ │ │ │ ├── itoa v1.0.11
│ │ │ │ ├── memchr v2.7.4
│ │ │ │ ├── ryu v1.0.18
│ │ │ │ └── serde v1.0.215 (*)
│ │ │ └── thiserror v1.0.69
│ │ │ └── thiserror-impl v1.0.69 (proc-macro)
│ │ │ ├── proc-macro2 v1.0.89 (*)
│ │ │ ├── quote v1.0.37 (*)
│ │ │ └── syn v2.0.87 (*)
│ │ ├── embuild v0.32.0
│ │ │ ├── anyhow v1.0.93
│ │ │ ├── bindgen v0.69.5 (*)
│ │ │ ├── bitflags v1.3.2
│ │ │ ├── cmake v0.1.51
│ │ │ │ └── cc v1.2.1
│ │ │ │ └── shlex v1.3.0
│ │ │ ├── filetime v0.2.25
│ │ │ │ ├── cfg-if v1.0.0
│ │ │ │ └── libc v0.2.162
│ │ │ ├── globwalk v0.8.1
│ │ │ │ ├── bitflags v1.3.2
│ │ │ │ ├── ignore v0.4.23
│ │ │ │ │ ├── crossbeam-deque v0.8.5
│ │ │ │ │ │ ├── crossbeam-epoch v0.9.18
│ │ │ │ │ │ │ └── crossbeam-utils v0.8.20
│ │ │ │ │ │ └── crossbeam-utils v0.8.20
│ │ │ │ │ ├── globset v0.4.15
│ │ │ │ │ │ ├── aho-corasick v1.1.3 (*)
│ │ │ │ │ │ ├── bstr v1.11.0
│ │ │ │ │ │ │ └── memchr v2.7.4
│ │ │ │ │ │ ├── log v0.4.22
│ │ │ │ │ │ ├── regex-automata v0.4.9 (*)
│ │ │ │ │ │ └── regex-syntax v0.8.5
│ │ │ │ │ ├── log v0.4.22
│ │ │ │ │ ├── memchr v2.7.4
│ │ │ │ │ ├── regex-automata v0.4.9 (*)
│ │ │ │ │ ├── same-file v1.0.6
│ │ │ │ │ └── walkdir v2.5.0
│ │ │ │ │ └── same-file v1.0.6
│ │ │ │ └── walkdir v2.5.0 (*)
│ │ │ ├── home v0.5.9
│ │ │ ├── log v0.4.22
│ │ │ ├── regex v1.11.1 (*)
│ │ │ ├── remove_dir_all v0.8.4
│ │ │ │ ├── cfg-if v1.0.0
│ │ │ │ ├── cvt v0.1.2
│ │ │ │ │ └── cfg-if v1.0.0
│ │ │ │ ├── fs_at v0.2.1
│ │ │ │ │ ├── cfg-if v1.0.0
│ │ │ │ │ ├── cvt v0.1.2 (*)
│ │ │ │ │ ├── libc v0.2.162
│ │ │ │ │ └── nix v0.29.0
│ │ │ │ │ ├── bitflags v2.6.0
│ │ │ │ │ ├── cfg-if v1.0.0
│ │ │ │ │ └── libc v0.2.162
│ │ │ │ │ [build-dependencies]
│ │ │ │ │ └── cfg_aliases v0.2.1
│ │ │ │ ├── libc v0.2.162
│ │ │ │ └── normpath v1.3.0
│ │ │ ├── serde v1.0.215 (*)
│ │ │ ├── serde_json v1.0.132 (*)
│ │ │ ├── shlex v1.3.0
│ │ │ ├── strum v0.24.1
│ │ │ │ └── strum_macros v0.24.3 (proc-macro)
│ │ │ │ ├── heck v0.4.1
│ │ │ │ ├── proc-macro2 v1.0.89 (*)
│ │ │ │ ├── quote v1.0.37 (*)
│ │ │ │ ├── rustversion v1.0.18 (proc-macro)
│ │ │ │ └── syn v1.0.109
│ │ │ │ ├── proc-macro2 v1.0.89 (*)
│ │ │ │ ├── quote v1.0.37 (*)
│ │ │ │ └── unicode-ident v1.0.13
│ │ │ ├── tempfile v3.14.0
│ │ │ │ ├── cfg-if v1.0.0
│ │ │ │ ├── fastrand v2.2.0
│ │ │ │ ├── once_cell v1.20.2
│ │ │ │ └── rustix v0.38.40 (*)
│ │ │ ├── thiserror v1.0.69 (*)
│ │ │ └── which v4.4.2 (*)
│ │ ├── envy v0.4.2
│ │ │ └── serde v1.0.215 (*)
│ │ ├── regex v1.11.1 (*)
│ │ ├── serde v1.0.215 (*)
│ │ ├── strum v0.24.1 (*)
│ │ └── which v4.4.2 (*)
│ ├── heapless v0.8.0 (*)
│ ├── log v0.4.22
│ ├── nb v1.1.0
│ └── num_enum v0.7.3
│ └── num_enum_derive v0.7.3 (proc-macro)
│ ├── proc-macro2 v1.0.89 (*)
│ ├── quote v1.0.37 (*)
│ └── syn v2.0.87 (*)
│ [build-dependencies]
│ └── embuild v0.32.0 (*)
├── esp-idf-svc v0.49.1
│ ├── embassy-futures v0.1.1
│ ├── embassy-time-driver v0.1.0
│ │ └── document-features v0.2.10 (proc-macro)
│ │ └── litrs v0.4.1
│ ├── embedded-hal-async v1.0.0 (*)
│ ├── embedded-svc v0.28.0
│ │ ├── embedded-io v0.6.1
│ │ ├── embedded-io-async v0.6.1 (*)
│ │ ├── enumset v1.1.5 (*)
│ │ ├── heapless v0.8.0 (*)
│ │ └── serde v1.0.215
│ │ └── serde_derive v1.0.215 (proc-macro) (*)
│ ├── enumset v1.1.5 (*)
│ ├── esp-idf-hal v0.44.1 (*)
│ ├── heapless v0.8.0 (*)
│ ├── log v0.4.22
│ ├── num_enum v0.7.3 (*)
│ └── uncased v0.9.10
│ [build-dependencies]
│ └── version_check v0.9.5
│ [build-dependencies]
│ └── embuild v0.32.0 (*)
├── esp-idf-sys v0.35.0 (*)
├── log v0.4.22
├── lvgl v0.6.2 (https://github.com/enelson1001/lv_binding_rust#723ad383)
│ ├── bitflags v2.6.0
│ ├── cstr_core v0.2.6 (*)
│ ├── ctor v0.2.8 (proc-macro)
│ │ ├── quote v1.0.37 (*)
│ │ └── syn v2.0.87 (*)
│ ├── cty v0.2.2
│ ├── embedded-graphics v0.8.1
│ │ ├── az v1.2.1
│ │ ├── byteorder v1.5.0
│ │ ├── embedded-graphics-core v0.4.0 (*)
│ │ ├── float-cmp v0.9.0
│ │ │ └── num-traits v0.2.19
│ │ │ [build-dependencies]
│ │ │ └── autocfg v1.4.0
│ │ └── micromath v2.1.0
│ ├── lvgl-sys v0.6.2 (https://github.com/enelson1001/lv_binding_rust#723ad383)
│ │ └── cty v0.2.2
│ │ [build-dependencies]
│ │ ├── bindgen v0.65.1
│ │ │ ├── bitflags v1.3.2
│ │ │ ├── cexpr v0.6.0 (*)
│ │ │ ├── clang-sys v1.8.1 (*)
│ │ │ ├── lazy_static v1.5.0
│ │ │ ├── lazycell v1.3.0
│ │ │ ├── log v0.4.22
│ │ │ ├── peeking_take_while v0.1.2
│ │ │ ├── prettyplease v0.2.25 (*)
│ │ │ ├── proc-macro2 v1.0.89 (*)
│ │ │ ├── quote v1.0.37 (*)
│ │ │ ├── regex v1.11.1 (*)
│ │ │ ├── rustc-hash v1.1.0
│ │ │ ├── shlex v1.3.0
│ │ │ ├── syn v2.0.87 (*)
│ │ │ └── which v4.4.2 (*)
│ │ └── cc v1.2.1 (*)
│ └── paste v1.0.15 (proc-macro)
│ [build-dependencies]
│ ├── lvgl-codegen v0.6.2 (https://github.com/enelson1001/lv_binding_rust#723ad383)
│ │ ├── Inflector v0.11.4
│ │ │ ├── lazy_static v1.5.0
│ │ │ └── regex v1.11.1 (*)
│ │ ├── lazy_static v1.5.0
│ │ ├── proc-macro2 v1.0.89 (*)
│ │ ├── quote v1.0.37 (*)
│ │ ├── regex v1.11.1 (*)
│ │ └── syn v2.0.87 (*)
│ ├── lvgl-sys v0.6.2 (https://github.com/enelson1001/lv_binding_rust#723ad383)
│ │ └── cty v0.2.2
│ │ [build-dependencies]
│ │ ├── bindgen v0.65.1 (*)
│ │ └── cc v1.2.1 (*)
│ ├── proc-macro2 v1.0.89 (*)
│ └── quote v1.0.37 (*)
├── lvgl-sys v0.6.2 (https://github.com/enelson1001/lv_binding_rust#723ad383) (*)
├── mipidsi v0.8.0
│ ├── display-interface v0.5.0
│ ├── embedded-graphics-core v0.4.0 (*)
│ ├── embedded-hal v1.0.0
│ ├── heapless v0.8.0 (*)
│ └── nb v1.1.0
└── static_cell v2.1.0
└── portable-atomic v1.9.0
[build-dependencies]
└── embuild v0.32.0 (*)

View File

@ -0,0 +1,42 @@
#
# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository.
#
# Copyright (c) The RP-RS Developers, 2021
#
# You might want to make a similar file in your own repository if you are
# writing programs for Raspberry Silicon microcontrollers.
#
# This file is MIT or Apache-2.0 as per the repository README.md file
#
[target.xtensa-esp32-none]
# runner = "espflash --monitor" # Select this runner for espflash v1.x.x
runner = "espflash flash --monitor --baud 921600" # Select this runner for espflash v2.x.x
# Target specific options
[target.thumbv6m-none-eabi]
# Pass some extra options to rustc, some of which get passed on to the linker.
#
# * linker argument --nmagic turns off page alignment of sections (which saves
# flash space)
# * linker argument -Tlink.x tells the linker to use link.x as the linker
# script. This is usually provided by the cortex-m-rt crate, and by default
# the version in that crate will include a file called `memory.x` which
# describes the particular memory layout for your specific chip.
# * inline-threshold=5 makes the compiler more aggressive and inlining functions
# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't
# have SIMD)
rustflags = [
"-C", "link-arg=--nmagic",
"-C", "link-arg=-Tlink.x",
"-C", "llvm-args=--inline-threshold=5",
"-C", "no-vectorize-loops",
]
# This runner will make a UF2 file and then copy it to a mounted RP2040 in USB
# Bootloader mode:
# runner = "elf2uf2-rs -d"
# This runner will find a supported SWD debug probe and flash your RP2040 over
# SWD:
# runner = "probe-run --chip RP2040"

6
slint-based/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# These are backup files generated by rustfmt
**/*.rs.bk

7
slint-based/.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"recommendations": [
"rust-lang.rust-analyzer",
"vadimcn.vscode-lldb",
"Slint.slint"
]
}

5186
slint-based/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

69
slint-based/Cargo.toml Normal file
View File

@ -0,0 +1,69 @@
[workspace]
[package]
name = "slint-mcu-rust-template"
version = "0.1.0"
edition = "2021"
build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
# [[bin]]
# name = "main"
[[bin]]
name = "starter"
[dependencies]
slint = { version = "1.8", default-features = false, features = ["compat-1-2", "renderer-software", "libm", "unsafe-single-threaded"] }
embassy-embedded-hal = "0.2.0"
embassy-executor = { version = "0.6.0", features = ["log"] }
embassy-sync = "0.6.0"
embassy-time = { version = "0.3.1", features = ["generic-queue-8"] }
esp-hal = { version = "0.20.1", features = ["async", "esp32", "log"] }
esp-hal-embassy = { version = "0.3.0", features = ["esp32", "log"] }
esp-println = { version = "0.12.0", features = ["esp32", "log"] }
cortex-m = { version = "0.7.6" }
cortex-m-rt = { version = "0.7.1" }
embedded-alloc = { version = "0.6.0" }
shared-bus = { version = "0.3.1" }
panic-halt = { version = "1.0.0" }
display-interface-spi = { version = "0.5.0" }
mipidsi = "0.8.0"
embedded-graphics-core = { version = "0.4.0" }
embedded-hal = { version = "1.0.0" }
fugit = { version = "0.3.6" }
esp-alloc = "0.5.0"
embedded-graphics-profiler-display = { version = "0.1.0", path = "../embedded-graphics-profiler-display" }
xpt2046 = { git = "https://github.com/Yandrik/xpt2046.git", version = "0.3.1" }
esp-backtrace = { version = "0.14.2", features = ["esp32", "println"] }
static_cell = { version = "2.1.0", features = ["nightly"] }
embedded-hal-bus = "0.2.0"
[build-dependencies]
slint-build = { version = "1.8" }
[features]
simulator = ["slint/backend-winit"]
# pico = [
# "slint/unsafe-single-threaded",
# "slint/libm",
# "cortex-m",
# "cortex-m-rt",
# "embedded-alloc",
# "embedded-hal",
# "fugit",
# "display-interface-spi",
# "embedded-graphics-core",
# "shared-bus",
# "panic-halt"
# ]

45
slint-based/README.md Normal file
View File

@ -0,0 +1,45 @@
# Slint Bare Metal Microcontroller Rust Template
A template for a Rust Microcontroller(MCU) application that's using [Slint](https://slint-ui.com) for the user interface.
## About
This template helps you get started developing a bare metal MCU Rust application with Slint as toolkit for the user interface.
It shows how to implement the `slint::platform::Platform` trait, and displays a simple `.slint` design on the screen.
For a template about using Slint with an operating system (Desktop, or Embedded Linux), check out the
classic template at https://github.com/slint-ui/slint-rust-template.
## Usage
1. Download and extract the [ZIP archive of this repository](https://github.com/slint-ui/slint-mcu-rust-template/archive/refs/heads/main.zip).
2. Rename the extracted directory and change into it:
```
mv slint-mcu-rust-template-main my-project
cd my-project
```
3. Run on the Desktop (Simulator)
```
cargo run --features simulator
```
4. If you have a [RaspberryPi Pico](https://www.raspberrypi.com/products/raspberry-pi-pico/) with a [2.8 inch Waveshare Touch Screen](https://www.waveshare.com/pico-restouch-lcd-2.8.htm):
a. Install the cargo extension to create UF2 images for the RP2040 USB Bootloader
```
cargo install elf2uf2-rs --locked
```
b. Run on the device
```
cargo run --target=thumbv6m-none-eabi --features=pico --release
```
In order to port to your device, you will have to replace all the code that is specific to the RaspberryPi Pico.
See also the instructions on https://slint-ui.com/snapshots/master/docs/rust/slint/docs/mcu/index.html
## Next Steps
We hope that this template helps you get started and you enjoy exploring making user interfaces with Slint. To learn more
about the Slint APIs and the `.slint` markup language check out our [online documentation](https://slint-ui.com/docs/rust/slint/).
Don't forget to edit this readme to replace it by yours, and edit the `name =` field in `Cargo.toml` to match the name of your project.

8
slint-based/build.rs Normal file
View File

@ -0,0 +1,8 @@
fn main() {
slint_build::compile_with_config(
"ui/app-window.slint",
slint_build::CompilerConfiguration::new()
.embed_resources(slint_build::EmbedResourcesKind::EmbedForSoftwareRenderer),
)
.unwrap();
}

View File

@ -0,0 +1,2 @@
[toolchain]
channel = "esp"

1
slint-based/rustfmt.toml Normal file
View File

@ -0,0 +1 @@
use_small_heuristics = "Max"

View File

@ -0,0 +1,450 @@
#![no_std]
#![no_main]
extern crate alloc;
use alloc::boxed::Box;
use alloc::rc::Rc;
use core::mem::MaybeUninit;
use core::{cell::RefCell, cmp::min, fmt};
use display_interface_spi::SPIInterface;
use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice;
use embassy_executor::Spawner;
use embassy_sync::{
blocking_mutex::{raw::NoopRawMutex, NoopMutex},
signal::Signal,
};
use embassy_time::Delay;
use embassy_time::{Duration, Instant, Timer};
use embedded_graphics_core::pixelcolor::Rgb565;
use embedded_graphics_core::prelude::*;
use embedded_graphics_core::primitives::Rectangle;
use embedded_graphics_profiler_display::ProfilerDisplay;
use embedded_hal::digital::OutputPin;
use embedded_hal_bus::spi::ExclusiveDevice;
use esp_backtrace as _;
use esp_hal::interrupt::Priority;
use esp_hal::{self, prelude::*};
use esp_hal::{
clock::ClockControl,
gpio::{GpioPin, Input, Io, Level, Output, Pull, NO_PIN},
peripherals::{Peripherals, SPI2, SPI3},
prelude::*,
rtc_cntl::Rtc,
spi::{master::Spi, FullDuplexMode, SpiMode},
system::SystemControl,
timer::timg::TimerGroup,
};
use esp_hal_embassy::InterruptExecutor;
use esp_println::println;
use mipidsi::{
models::ILI9486Rgb565,
options::{ColorInversion, ColorOrder, Orientation, Rotation},
Builder, Display,
};
use panic_halt as _;
use slint::platform::software_renderer::MinimalSoftwareWindow;
use slint::platform::{Platform, PointerEventButton, WindowEvent};
use slint::private_unstable_api::re_exports::LogicalPoint;
use slint::LogicalPosition;
use static_cell::StaticCell;
use xpt2046::Xpt2046;
use esp_alloc as _;
// slint::slint!{ export MyUI := Window {} }
/*
slint::include_modules!();
# */
slint::include_modules!();
fn init_heap() {
const HEAP_SIZE: usize = 32 * 1024;
static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = MaybeUninit::uninit();
unsafe {
esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new(
HEAP.as_mut_ptr() as *mut u8,
HEAP_SIZE,
esp_alloc::MemoryCapability::Internal.into(),
));
}
}
#[embassy_executor::task]
async fn touch_task(
touch_irq: GpioPin<36>,
spi: ExclusiveDevice<
Spi<'static, SPI3, FullDuplexMode>,
Output<'static, GpioPin<33>>,
&'static mut Delay,
>,
touch_signal: &'static Signal<NoopRawMutex, Option<Point>>,
) -> ! {
let mut touch_driver =
Xpt2046::new(spi, Input::new(touch_irq, Pull::Up), xpt2046::Orientation::LandscapeFlipped);
touch_driver.set_num_samples(16);
touch_driver.init(&mut embassy_time::Delay).unwrap();
esp_println::println!("touch task");
loop {
touch_driver.run().expect("Running Touch driver failed");
if touch_driver.is_touched() {
let point = touch_driver.get_touch_point();
touch_signal.signal(Some(Point::new(point.x + 25, 240 - point.y)));
} else {
touch_signal.signal(None);
}
Timer::after(Duration::from_millis(1)).await; // 100 a second
// Your touch handling logic here
}
}
fn point_to_logical_pos(point: Point) -> LogicalPosition {
LogicalPosition::new(point.x as f32, point.y as f32)
}
struct CYDPlatform {
window: Rc<slint::platform::software_renderer::MinimalSoftwareWindow>,
}
impl Platform for CYDPlatform {
fn create_window_adapter(
&self,
) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
Ok(self.window.clone())
}
fn duration_since_start(&self) -> core::time::Duration {
embassy_time::Instant::from_millis(0).elapsed().into()
}
//noinspection DuplicatedCode
fn run_event_loop(&self) -> Result<(), slint::PlatformError> {
todo!();
}
}
struct AppData {
timer_start: Instant,
timer_set_duration: Duration,
timer_remaining_duration: Duration,
timer_running: bool,
timer_paused: bool,
}
impl AppData {
fn new() -> Self {
Self {
timer_start: Instant::now(),
timer_set_duration: Duration::from_secs(10),
timer_remaining_duration: Duration::from_secs(10),
timer_running: false,
timer_paused: false,
}
}
fn set_timer_duration(&mut self, duration: Duration) {
self.timer_set_duration = duration;
}
fn add_secs(&mut self, secs: u64) {
self.set_timer_duration(
self.timer_set_duration
.checked_add(Duration::from_secs(secs))
.unwrap_or(Duration::from_secs(5999))
.min(Duration::from_secs(5999)),
);
}
fn sub_secs(&mut self, secs: u64) {
self.set_timer_duration(
self.timer_set_duration
.checked_sub(Duration::from_secs(secs))
.unwrap_or(Duration::from_secs(10))
.max(Duration::from_secs(10)),
);
}
fn start_timer(&mut self) {
if !self.timer_paused {
self.timer_remaining_duration = self.timer_set_duration;
}
self.timer_start = Instant::now();
self.timer_running = true;
self.timer_paused = false;
}
fn pause_timer(&mut self) {
self.timer_paused = true;
self.timer_remaining_duration = self
.timer_remaining_duration
.checked_sub(self.timer_start.elapsed())
.unwrap_or(Duration::from_secs(0));
}
fn reset_timer(&mut self) {
self.timer_start = Instant::now();
self.timer_paused = false;
self.timer_running = false;
self.timer_remaining_duration = self.timer_set_duration;
}
fn remaining(&self) -> Duration {
if self.timer_stopped() {
self.timer_set_duration
} else if self.timer_paused() {
self.timer_remaining_duration
} else {
self.timer_remaining_duration
.checked_sub(self.timer_start.elapsed())
.unwrap_or(Duration::from_secs(0))
}
}
fn timer_stopped(&self) -> bool {
!self.timer_running
}
fn timer_paused(&self) -> bool {
self.timer_running && self.timer_paused
}
fn timer_running(&self) -> bool {
self.timer_running && !self.timer_paused
}
fn timer_finished(&self) -> bool {
self.timer_running && self.remaining() == Duration::from_secs(0)
}
}
#[main]
async fn main(spawner: Spawner) {
init_heap();
let peripherals = Peripherals::take();
let system = SystemControl::new(peripherals.SYSTEM);
let mut clocks = ClockControl::boot_defaults(system.clock_control).freeze();
let mut rtc = Rtc::new(peripherals.LPWR);
rtc.rwdt.disable();
let mut timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
timer_group0.wdt.disable();
let mut timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks);
timer_group1.wdt.disable();
esp_hal_embassy::init(&clocks, timer_group0.timer0);
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
let touch_irq = io.pins.gpio36;
let touch_mosi = io.pins.gpio32;
let touch_miso = io.pins.gpio39;
let touch_clk = io.pins.gpio25;
let touch_cs = io.pins.gpio33;
// 2MHz is the MAX! DO NOT DECREASE! This is really important.
let mut touch_spi = Spi::new(peripherals.SPI3, 2.MHz(), SpiMode::Mode0, &mut clocks).with_pins(
Some(touch_clk),
Some(touch_mosi),
Some(touch_miso),
NO_PIN,
);
static TOUCH_DELAY_STATICCELL: StaticCell<Delay> = StaticCell::new();
let mut delay = TOUCH_DELAY_STATICCELL.init(Delay);
let touch_spi =
ExclusiveDevice::new(touch_spi, Output::new(touch_cs, Level::Low), delay).unwrap();
let touch_signal = Signal::new();
static TOUCH_SIGNAL: StaticCell<Signal<NoopRawMutex, Option<Point>>> = StaticCell::new();
let touch_signal = &*TOUCH_SIGNAL.init(touch_signal);
// let sw_int = system.software_interrupt_control.software_interrupt2;
// static EXECUTOR: StaticCell<InterruptExecutor<2>> = StaticCell::new();
// let executor = InterruptExecutor::<2>::new(sw_int);
// let executor = EXECUTOR.init(executor);
spawner.spawn(touch_task(touch_irq, touch_spi, touch_signal)).unwrap();
// executor.start(Priority::Priority1);
// Display setup
let sclk = io.pins.gpio14;
let miso = io.pins.gpio12;
let mosi = io.pins.gpio13;
let cs = io.pins.gpio15;
let dc = io.pins.gpio2;
let mut backlight = Output::new(io.pins.gpio21, Level::Low);
let mut spi = Spi::new(peripherals.SPI2, 10u32.MHz(), SpiMode::Mode0, &clocks).with_pins(
Some(sclk),
Some(mosi),
Some(miso),
esp_hal::gpio::NO_PIN,
);
// static DISP_SPI_BUS: StaticCell<NoopMutex<RefCell<Spi<SPI2, FullDuplexMode>>>> = StaticCell::new();
// let spi_bus = NoopMutex::new(RefCell::new(spi));
// let spi_bus = DISP_SPI_BUS.init(spi_bus);
static spi_delay_staticcell: StaticCell<Delay> = StaticCell::new();
let mut delay = spi_delay_staticcell.init(Delay);
let spi = ExclusiveDevice::new(spi, Output::new(cs, Level::Low), delay).unwrap();
let di = SPIInterface::new(spi, Output::new(dc, Level::Low));
let display = Builder::new(ILI9486Rgb565, di)
.orientation(Orientation { rotation: Rotation::Deg90, mirrored: true })
.color_order(ColorOrder::Bgr)
.invert_colors(ColorInversion::Inverted)
.init(&mut embassy_time::Delay)
.unwrap();
let mut display = ProfilerDisplay::new(display);
backlight.set_high();
let size = display.bounding_box().size;
let size = slint::PhysicalSize::new(size.width as u32, size.height as u32);
let window = MinimalSoftwareWindow::new(Default::default());
slint::platform::set_platform(Box::new(CYDPlatform { window: window.clone() })).unwrap();
let ui = create_slint_app();
window.set_size(slint::PhysicalSize::new(320, 240));
let mut buffer_provider = DrawBuffer {
display,
buffer: &mut [slint::platform::software_renderer::Rgb565Pixel(0); 320],
};
let mut last_touch = None;
loop {
let start_time = Instant::now();
if let Some(touch) = touch_signal.try_take() {
// println!("touch: {:?}, last_touch: {:?}", touch, last_touch);
let button = PointerEventButton::Left;
let interact = match (touch, last_touch) {
(Some(point), Some(_)) => {
Some(WindowEvent::PointerMoved { position: point_to_logical_pos(point) })
}
(Some(point), None) => Some(WindowEvent::PointerPressed {
position: point_to_logical_pos(point),
button,
}),
(None, Some(point)) => Some(WindowEvent::PointerReleased {
position: point_to_logical_pos(point),
button,
}),
(None, None) => None,
};
if let Some(event) = interact {
// println!("event: {:?}", event);
window.dispatch_event(event);
}
last_touch = touch;
}
slint::platform::update_timers_and_animations();
// let window = window.clone();
let start_draw_time = Instant::now();
window.draw_if_needed(|renderer| {
renderer.render_by_line(&mut buffer_provider);
});
let button = PointerEventButton::Left;
if window.has_active_animations() {
continue;
}
let display = &mut buffer_provider.display;
let end_time = embassy_time::Instant::now();
let draw_time = display.get_time();
let prep_time = start_draw_time - start_time;
let proc_time = end_time - start_draw_time;
let proc_time = proc_time - min(draw_time, proc_time);
rtc.rwdt.feed();
if draw_time.as_micros() > 0 {
println!(
"draw time: {}.{:03}ms | prep time: {}.{:03}ms | proc time: {}.{:03}ms | total time: {}.{:03}ms",
draw_time.as_millis(),
draw_time.as_micros() % 100,
prep_time.as_millis(),
prep_time.as_micros() % 100,
proc_time.as_millis(),
proc_time.as_micros() % 100,
(draw_time + prep_time + proc_time).as_millis(),
(draw_time + prep_time + proc_time).as_micros() % 100, );
}
display.reset_time();
Timer::after(Duration::from_millis(1)).await; // 60 a second
}
}
fn create_slint_app() -> AppWindow {
let ui = AppWindow::new().unwrap();
let ui_handle = ui.as_weak();
ui.on_request_increase_value(move || {
let ui = ui_handle.unwrap();
ui.set_counter(ui.get_counter() + 1);
});
ui
}
struct DrawBuffer<'a, DT> {
display: DT,
buffer: &'a mut [slint::platform::software_renderer::Rgb565Pixel],
}
impl<
// DI: display_interface_spi::WriteOnlyDataCommand,
E: core::fmt::Debug,
DT: DrawTarget<Color = Rgb565, Error = E>,
// RST: OutputPin<Error = core::convert::Infallible>,
> slint::platform::software_renderer::LineBufferProvider for &mut DrawBuffer<'_, DT>
{
type TargetPixel = slint::platform::software_renderer::Rgb565Pixel;
fn process_line(
&mut self,
line: usize,
range: core::ops::Range<usize>,
render_fn: impl FnOnce(&mut [slint::platform::software_renderer::Rgb565Pixel]),
) {
let buffer = &mut self.buffer[range.clone()];
render_fn(buffer);
// We send empty data just to get the device in the right window
self.display
.fill_contiguous(
&Rectangle::new(
Point::new(range.start as i32, line as i32),
Size::new((range.end - range.start) as u32, 1),
),
// range.start as u16,
// line as _,
// range.end as u16,
// line as u16,
buffer
.iter()
.map(|x| embedded_graphics_core::pixelcolor::raw::RawU16::new(x.0).into()),
)
.unwrap();
}
}

260
slint-based/src/cyd.rs Normal file
View File

@ -0,0 +1,260 @@
use alloc::boxed::Box;
use alloc::rc::Rc;
use core::cell::RefCell;
use core::mem::MaybeUninit;
use display_interface_spi::SPIInterface;
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_sync::signal::Signal;
use embassy_time::{Duration, Timer};
use embedded_graphics_core::geometry::{OriginDimensions, Point};
use embedded_graphics_profiler_display::ProfilerDisplay;
use embedded_hal::digital::OutputPin;
use embedded_hal_bus::spi::ExclusiveDevice;
use esp_hal::clock::ClockControl;
use esp_hal::delay::Delay;
pub use esp_hal::entry;
use esp_hal::gpio::{GpioPin, Input, Io, Level, Output, Pull, NO_PIN};
use esp_hal::interrupt::Priority;
use esp_hal::peripherals::{Peripherals, SPI3};
use esp_hal::prelude::*;
use esp_hal::rtc_cntl::Rtc;
use esp_hal::spi::{master::Spi, FullDuplexMode, SpiMode};
use esp_hal::system::SystemControl;
use esp_hal::timer::timg::TimerGroup;
use esp_hal_embassy::InterruptExecutor;
use mipidsi::models::ILI9486Rgb565;
use mipidsi::options::{ColorInversion, ColorOrder, Rotation};
use mipidsi::{options::Orientation, Builder, Display};
use xpt2046::Xpt2046;
#[embassy_executor::task]
async fn touch_task(
touch_irq: GpioPin<36>,
spi: ExclusiveDevice<
Spi<'static, SPI3, FullDuplexMode>,
Output<'static, GpioPin<33>>,
&'static mut Delay,
>,
touch_signal: &'static Signal<NoopRawMutex, Option<Point>>,
) -> ! {
let mut touch_driver = Xpt2046::new(
spi,
Input::new(touch_irq, Pull::Up),
xpt2046::Orientation::LandscapeFlipped,
);
touch_driver.set_num_samples(16);
touch_driver.init(&mut embassy_time::Delay).unwrap();
esp_println::println!("touch task");
loop {
touch_driver.run().expect("Running Touch driver failed");
if touch_driver.is_touched() {
let point = touch_driver.get_touch_point();
touch_signal.signal(Some(Point::new(point.x + 25, 240 - point.y)));
} else {
touch_signal.signal(None);
}
Timer::after(Duration::from_millis(1)).await; // 100 a second
// Your touch handling logic here
}
}
pub fn init() {
const HEAP_SIZE: usize = 250 * 1024;
static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = MaybeUninit::uninit();
unsafe {
esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new(
HEAP.as_mut_ptr() as *mut u8,
HEAP_SIZE,
esp_alloc::MemoryCapability::Internal.into(),
));
}
slint::platform::set_platform(Box::new(EspBackend::default()))
.expect("backend already initialized");
}
#[derive(Default)]
struct EspBackend {
window: RefCell<Option<Rc<slint::platform::software_renderer::MinimalSoftwareWindow>>>,
}
impl slint::platform::Platform for EspBackend {
fn create_window_adapter(
&self,
) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
let window = slint::platform::software_renderer::MinimalSoftwareWindow::new(
slint::platform::software_renderer::RepaintBufferType::ReusedBuffer,
);
self.window.replace(Some(window.clone()));
Ok(window)
}
fn duration_since_start(&self) -> core::time::Duration {
embassy_time::Instant::from_millis(0).elapsed().into()
}
//noinspection DuplicatedCode
fn run_event_loop(&self) -> Result<(), slint::PlatformError> {
use display_interface_spi::SPIInterface;
use embedded_hal_bus::spi::ExclusiveDevice;
use esp_hal::delay::Delay;
use esp_hal::gpio::{Io, Level, Output};
use esp_hal::rtc_cntl::Rtc;
use esp_hal::spi::{master::Spi, SpiMode};
use esp_hal::timer::timg::TimerGroup;
use esp_hal::{self, prelude::*};
use mipidsi::{
options::{ColorInversion, ColorOrder, Orientation, Rotation},
Builder,
};
use static_cell::StaticCell;
let peripherals = Peripherals::take();
let system = SystemControl::new(peripherals.SYSTEM);
let mut clocks = ClockControl::boot_defaults(system.clock_control).freeze();
let mut rtc = Rtc::new(peripherals.LPWR);
rtc.rwdt.disable();
let mut timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
timer_group0.wdt.disable();
let mut timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks);
timer_group1.wdt.disable();
esp_hal_embassy::init(&clocks, timer_group0.timer0);
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
let touch_irq = io.pins.gpio36;
let touch_mosi = io.pins.gpio32;
let touch_miso = io.pins.gpio39;
let touch_clk = io.pins.gpio25;
let touch_cs = io.pins.gpio33;
// 2MHz is the MAX! DO NOT DECREASE! This is really important.
let mut touch_spi = Spi::new(peripherals.SPI3, 2.MHz(), SpiMode::Mode0, &mut clocks)
.with_pins(Some(touch_clk), Some(touch_mosi), Some(touch_miso), NO_PIN);
let touch_spi =
ExclusiveDevice::new(touch_spi, Output::new(touch_cs, Level::Low), &mut Delay).unwrap();
let touch_signal = Signal::new();
static TOUCH_SIGNAL: StaticCell<Signal<NoopRawMutex, Option<Point>>> = StaticCell::new();
let touch_signal = &*TOUCH_SIGNAL.init(touch_signal);
let sw_int = system.software_interrupt_control.software_interrupt2;
static EXECUTOR: StaticCell<InterruptExecutor<2>> = StaticCell::new();
let executor = InterruptExecutor::<2>::new(sw_int);
let executor = EXECUTOR.init(executor);
executor.spawner().unwrap()
.spawn(touch_task(touch_irq, touch_spi, touch_signal))
.unwrap();
executor.start(Priority::Priority1);
// Display setup
let sclk = io.pins.gpio14;
let miso = io.pins.gpio12;
let mosi = io.pins.gpio13;
let cs = io.pins.gpio15;
let dc = io.pins.gpio2;
let mut backlight = Output::new(io.pins.gpio21, Level::Low);
let mut spi = Spi::new(peripherals.SPI2, 10u32.MHz(), SpiMode::Mode0, &clocks).with_pins(
Some(sclk),
Some(mosi),
Some(miso),
esp_hal::gpio::NO_PIN,
);
// static DISP_SPI_BUS: StaticCell<NoopMutex<RefCell<Spi<SPI2, FullDuplexMode>>>> = StaticCell::new();
// let spi_bus = NoopMutex::new(RefCell::new(spi));
// let spi_bus = DISP_SPI_BUS.init(spi_bus);
let spi = ExclusiveDevice::new(spi, Output::new(cs, Level::Low), &mut Delay).unwrap();
let di = SPIInterface::new(spi, Output::new(dc, Level::Low));
let display = Builder::new(ILI9486Rgb565, di)
.orientation(Orientation { rotation: Rotation::Deg90, mirrored: true })
.color_order(ColorOrder::Bgr)
.invert_colors(ColorInversion::Inverted)
.init(&mut embassy_time::Delay)
.unwrap();
let mut display = ProfilerDisplay::new(display);
backlight.set_high().unwrap();
let size = display.size();
let size = slint::PhysicalSize::new(size.width as u32, size.height as u32);
self.window.borrow().as_ref().unwrap().set_size(size);
let mut buffer_provider = DrawBuffer {
display,
buffer: &mut [slint::platform::software_renderer::Rgb565Pixel(0); 320],
};
loop {
slint::platform::update_timers_and_animations();
if let Some(window) = self.window.borrow().clone() {
window.draw_if_needed(|renderer| {
renderer.render_by_line(&mut buffer_provider);
});
if window.has_active_animations() {
continue;
}
}
// TODO: Implement touch handling
}
}
fn debug_log(&self, arguments: core::fmt::Arguments) {
esp_println::println!("{}", arguments);
}
}
struct DrawBuffer<'a, Display> {
display: Display,
buffer: &'a mut [slint::platform::software_renderer::Rgb565Pixel],
}
impl<
DI: display_interface_spi::WriteOnlyDataCommand,
RST: OutputPin<Error = core::convert::Infallible>,
> slint::platform::software_renderer::LineBufferProvider
for &mut DrawBuffer<'_, Display<DI, mipidsi::models::ILI9342CRgb565, RST>>
{
type TargetPixel = slint::platform::software_renderer::Rgb565Pixel;
fn process_line(
&mut self,
line: usize,
range: core::ops::Range<usize>,
render_fn: impl FnOnce(&mut [slint::platform::software_renderer::Rgb565Pixel]),
) {
let buffer = &mut self.buffer[range.clone()];
render_fn(buffer);
// We send empty data just to get the device in the right window
self.display
.set_pixels(
range.start as u16,
line as _,
range.end as u16,
line as u16,
buffer
.iter()
.map(|x| embedded_graphics_core::pixelcolor::raw::RawU16::new(x.0).into()),
)
.unwrap();
}
}

8
slint-based/src/lib.rs Normal file
View File

@ -0,0 +1,8 @@
#![no_std]
#![no_main]
extern crate alloc;
// mod cyd;
// pub use cyd::*;

View File

@ -0,0 +1,36 @@
import { Button, VerticalBox , AboutSlint } from "std-widgets.slint";
export component AppWindow inherits Window {
width: 320px;
height: 240px;
in-out property <int> counter: 42;
callback request-increase-value();
VerticalBox {
alignment: start;
Text {
text: "Hello World!";
font-size: 24px;
horizontal-alignment: center;
}
}
Text {
text: "Counter App (Slint)";
}
VerticalBox {
Text {
text: "Counter: \{root.counter}";
}
Button {
text: "Increase value";
clicked => {
root.request-increase-value();
}
}
// AboutSlint { }
}
}