文章目录
- 说在前面
- 关于slint
- 关于no-std
- 关于dma
- 准备工作
- 相关依赖
- 代码
- 结果
- 参考
说在前面
- esp32版本:s3
- 运行环境:no-std
- 开发环境:wsl2
- LCD模块:ST7789V2 240*280 LCD
- Slint版本:master分支
- github地址:这里
关于slint
- 官网
- 为啥不用
lvgl
?
只能说rust的生态还是不太行,lvgl的rust binding似乎还在开发中,已经有仓库了,但是还在开发中。
slint
目前比较完善,但是相关资料也少。
反正已经在折腾rust
了,也不在乎再多折腾个小众点的。
关于no-std
- 上一篇还是
std
环境,怎么就变成no-std
了?
std
环境下也折腾了slint
,但是fps就是不怎么高 (可能哪里写的不对) ,然后就试了下no-std
,稍微丝滑点,并且编译也快。
关于dma
rust
生态下dma
的资料更是少的可怜,找了好久也没啥进展,所以本文不涉及dma
准备工作
- 引脚连接见上篇
- 开发环境部分,由于不需要
esp idf
,简单很多,cargo
直接搞定
相关依赖
- Cargo.toml
[dependencies]
hal = { package = "esp32s3-hal", version = "0.13.0"}
esp-backtrace = { version = "0.9.0", features = ["esp32s3", "panic-handler", "exception-handler", "print-uart"] }
esp-println = { version = "0.7.0", features = ["esp32s3","log"] }
log = { version = "0.4.18" }
esp-alloc = { version = "0.3.0" }
embedded-hal = "0.2.7"
embedded-graphics-core = "0.4.0"
embedded-graphics = "0.8.1"
embedded-graphics-framebuf = "0.5.0"
display-interface = "0.4"
display-interface-spi = "0.4"
mipidsi = "0.7.1"
slint = { git = "https://githubfast.com/slint-ui/slint", default-features = false, features = ["compat-1-2","unsafe-single-threaded","libm", "renderer-software"] }
[build-dependencies]
slint-build = { git = "https://githubfast.com/slint-ui/slint" }
代码
#![no_std]
#![no_main]
extern crate alloc;
use alloc::boxed::Box;
use alloc::rc::Rc;
use rs_esp32s3_no_std_st7789_demo::dma::DmaBackend;
use core::cell::RefCell;
use core::mem::MaybeUninit;
use display_interface_spi::SPIInterfaceNoCS;
use embedded_graphics_core::prelude::{DrawTarget, Point, RgbColor, Size};
use embedded_graphics_core::{pixelcolor::raw::RawU16, primitives::Rectangle};
use esp_backtrace as _;
use esp_println::println;
use hal::spi::master::{Spi, dma};
use hal::{
clock::{ClockControl, CpuClock},
peripherals::Peripherals,
prelude::*,
spi::SpiMode,
systimer::SystemTimer,
timer::TimerGroup,
Delay, Rtc, IO,
};
use mipidsi::Display;
#[global_allocator]
static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();
fn init_heap() {
const HEAP_SIZE: usize = 250 * 1024;
static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = MaybeUninit::uninit();
unsafe {
ALLOCATOR.init(HEAP.as_mut_ptr() as *mut u8, HEAP_SIZE);
}
}
slint::include_modules!();
#[entry]
fn main() -> ! {
init_heap();
slint::platform::set_platform(Box::new(EspBackend::default()))
.expect("backend already initialized");
let main_window = Recipe::new().unwrap();
let strong = main_window.clone_strong();
let timer = slint::Timer::default();
timer.start(
slint::TimerMode::Repeated,
core::time::Duration::from_millis(1000),
move || {
if strong.get_counter() <= 0 {
strong.set_counter(25);
} else {
strong.set_counter(0);
}
},
);
main_window.run().unwrap();
panic!("The MCU demo should not quit");
}
#[derive(Default)]
pub 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 {
core::time::Duration::from_millis(
SystemTimer::now() / (SystemTimer::TICKS_PER_SECOND / 1000),
)
}
fn run_event_loop(&self) -> Result<(), slint::PlatformError> {
let peripherals = Peripherals::take();
let mut system = peripherals.SYSTEM.split();
let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock240MHz).freeze();
let mut rtc = Rtc::new(peripherals.RTC_CNTL);
let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
let mut wdt0 = timer_group0.wdt;
let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks);
let mut wdt1 = timer_group1.wdt;
rtc.rwdt.disable();
wdt0.disable();
wdt1.disable();
let mut delay = Delay::new(&clocks);
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
let clk = io.pins.gpio18;
let sdo = io.pins.gpio17;
let cs = io.pins.gpio14;
let spi = Spi::new_no_miso(
peripherals.SPI2,
clk,
sdo,
cs,
60u32.MHz(),
SpiMode::Mode0,
&clocks,
);
println!("spi init.");
let dc = io.pins.gpio15.into_push_pull_output();
let rst = io.pins.gpio16.into_push_pull_output();
let di = SPIInterfaceNoCS::new(spi, dc);
let mut display = mipidsi::Builder::st7789(di)
.with_display_size(240, 280)
.with_window_offset_handler(|_| (0, 20))
.with_framebuffer_size(240, 280)
.with_invert_colors( mipidsi::ColorInversion::Inverted)
.init(&mut delay, Some(rst))
.unwrap();
println!("display init.");
let mut bl = io.pins.gpio13.into_push_pull_output();
bl.set_high().unwrap();
let size = slint::PhysicalSize::new(240, 280);
self.window.borrow().as_ref().unwrap().set_size(size);
let mut buffer_provider = DrawBuffer {
display,
buffer: &mut [slint::platform::software_renderer::Rgb565Pixel::default(); 240],
};
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;
}
}
}
}
fn debug_log(&self, arguments: core::fmt::Arguments) {
println!("{}", arguments);
}
}
struct DrawBuffer<'a, Display> {
display: Display,
buffer: &'a mut [slint::platform::software_renderer::Rgb565Pixel],
}
impl<DI: display_interface::WriteOnlyDataCommand, RST: embedded_hal::digital::v2::OutputPin>
slint::platform::software_renderer::LineBufferProvider
for &mut DrawBuffer<'_, Display<DI, mipidsi::models::ST7789, 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);
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();
}
}
结果
参考