【rust/esp32】初识slint ui框架并在st7789 lcd上显示

2023-11-05 10:15

本文主要是介绍【rust/esp32】初识slint ui框架并在st7789 lcd上显示,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 说在前面
  • 关于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自动编译ui代码
slint::include_modules!();
#[entry]
fn main() -> ! {init_heap();// slint 设置默认backendslint::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();// 由于我的lcd不支持触屏 这里模拟了下按钮点击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;// 初始化spilet 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();// spi interfacelet di = SPIInterfaceNoCS::new(spi, dc);// st7789 驱动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();// 这里的大致流程是:// slint会计算出当前帧需要变化的像素// 结果会暂时存放在buffer_provider// 然后将buffer_provider中的数据传给spiif 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::LineBufferProviderfor &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);// We send empty data just to get the device in the right windowself.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();}
}

结果

  • 还是有卡顿的感觉
    在这里插入图片描述

参考

  • slint mcu

这篇关于【rust/esp32】初识slint ui框架并在st7789 lcd上显示的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/349164

相关文章

修改若依框架Token的过期时间问题

《修改若依框架Token的过期时间问题》本文介绍了如何修改若依框架中Token的过期时间,通过修改`application.yml`文件中的配置来实现,默认单位为分钟,希望此经验对大家有所帮助,也欢迎... 目录修改若依框架Token的过期时间修改Token的过期时间关闭Token的过期时js间总结修改若依

在Rust中要用Struct和Enum组织数据的原因解析

《在Rust中要用Struct和Enum组织数据的原因解析》在Rust中,Struct和Enum是组织数据的核心工具,Struct用于将相关字段封装为单一实体,便于管理和扩展,Enum用于明确定义所有... 目录为什么在Rust中要用Struct和Enum组织数据?一、使用struct组织数据:将相关字段绑

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

Vue项目中Element UI组件未注册的问题原因及解决方法

《Vue项目中ElementUI组件未注册的问题原因及解决方法》在Vue项目中使用ElementUI组件库时,开发者可能会遇到一些常见问题,例如组件未正确注册导致的警告或错误,本文将详细探讨这些问题... 目录引言一、问题背景1.1 错误信息分析1.2 问题原因二、解决方法2.1 全局引入 Element

如何设置vim永久显示行号

《如何设置vim永久显示行号》在Linux环境下,vim默认不显示行号,这在程序编译出错时定位错误语句非常不便,通过修改vim配置文件vimrc,可以在每次打开vim时永久显示行号... 目录设置vim永久显示行号1.临时显示行号2.永www.chinasem.cn久显示行号总结设置vim永久显示行号在li

Rust 数据类型详解

《Rust数据类型详解》本文介绍了Rust编程语言中的标量类型和复合类型,标量类型包括整数、浮点数、布尔和字符,而复合类型则包括元组和数组,标量类型用于表示单个值,具有不同的表示和范围,本文介绍的非... 目录一、标量类型(Scalar Types)1. 整数类型(Integer Types)1.1 整数字

Python中的可视化设计与UI界面实现

《Python中的可视化设计与UI界面实现》本文介绍了如何使用Python创建用户界面(UI),包括使用Tkinter、PyQt、Kivy等库进行基本窗口、动态图表和动画效果的实现,通过示例代码,展示... 目录从像素到界面:python带你玩转UI设计示例:使用Tkinter创建一个简单的窗口绘图魔法:用

element-ui下拉输入框+resetFields无法回显的问题解决

《element-ui下拉输入框+resetFields无法回显的问题解决》本文主要介绍了在使用ElementUI的下拉输入框时,点击重置按钮后输入框无法回显数据的问题,具有一定的参考价值,感兴趣的... 目录描述原因问题重现解决方案方法一方法二总结描述第一次进入页面,不做任何操作,点击重置按钮,再进行下

Rust中的Option枚举快速入门教程

《Rust中的Option枚举快速入门教程》Rust中的Option枚举用于表示可能不存在的值,提供了多种方法来处理这些值,避免了空指针异常,文章介绍了Option的定义、常见方法、使用场景以及注意事... 目录引言Option介绍Option的常见方法Option使用场景场景一:函数返回可能不存在的值场景

电脑显示hdmi无信号怎么办? 电脑显示器无信号的终极解决指南

《电脑显示hdmi无信号怎么办?电脑显示器无信号的终极解决指南》HDMI无信号的问题却让人头疼不已,遇到这种情况该怎么办?针对这种情况,我们可以采取一系列步骤来逐一排查并解决问题,以下是详细的方法... 无论你是试图为笔记本电脑设置多个显示器还是使用外部显示器,都可能会弹出“无HDMI信号”错误。此消息可能