【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();}
}
结果
- 还是有卡顿的感觉

- 对比
clvgl+dma,加权fps有157,差距太大了

参考
- slint mcu
相关文章:
【rust/esp32】初识slint ui框架并在st7789 lcd上显示
文章目录 说在前面关于slint关于no-std关于dma准备工作相关依赖代码结果参考 说在前面 esp32版本:s3运行环境:no-std开发环境:wsl2LCD模块:ST7789V2 240*280 LCDSlint版本:master分支github地址:这里 关于s…...
精通Nginx(05)-http工作机制、指令和内置变量
http服务是Nginx最原始的服务,搞清楚其工作机制非常有利于弄懂nginx是如何工作的。 Nginx核心模块为ngx_http_core_module。 目录 http工作机制 配置结构 工作机制 http常用指令 http server listen server_name location 优先级 "/"的特殊用法 root/a…...
用于 GaN-HEMT 功率器件仿真的 TCAD 方法论
目录 标题:TCAD Methodology for Simulation of GaN-HEMT Power Devices来源:Proceedings of the 26th International Symposium on Power Semiconductor Devices & ICs(14年 ISPSD)GaN-HEMT仿真面临的挑战文章研究了什么文章的创新点文章的研究方法…...
Web3公链之Cosmos生态的项目Celestia
文章目录 Web3公链之Cosmos生态的项目:模块化区块链Celestia什么是CelestiaCelestia网络架构数据可用性问题有哪些可用的解决方案? 发展历史运行节点参考 Web3公链之Cosmos生态的项目:模块化区块链Celestia 什么是Celestia 官网:…...
vue+prismjs 网页代码高亮插件
最近在使用wangEditor的过程中发现编辑器中代码块展示没有问题,但是预览编辑器中的内容样式丢失,看过wangEditor的文档后发现用到了Prism.js,现将使用的经验分享。 使用步骤 1、安装prismjs插件 // 1. 安装prismjs 插件 npm install prismj…...
【软件测试】其实远远不止需求文档这么简单
我们都知道,软件测试是一门依赖性很强的综合技术,软件测试工程师在施行自己的工作时,总是要依赖其他团队的产出。 比如,我们要依赖着需求团队给出的需求分析说明书来确定测试的方向,又要依赖开发团队产出的实际代码产品…...
SAP-PP-常用TCODE
PP主数据管理MM01/MM02物料主数据维护/修改 MM17物料主数据部分字段批量修改 /sapapo/mat1PPDS查看物料主数据 /sapapo/Res01PPDS查看资源主数据 BOM管理CS01/CS02维护/修改/删除BOM 超级BOM涉及到物料分类类型001 ,CT04 创建特性,CL01 创建类 工作中…...
第六章认识Node.js服务器开发
目录 Node.js同步和异步编程 基本概念 执行方式 获取异步API的返回值 网页基础扩展 项目 Node.js同步和异步编程 基本概念 同步API(应用程序编程接口)是指只有当前API执行完毕后才能继续执行下一个API。形象的说同步模式就是一个服务员在某一个时间段内只服务一个客人…...
Ubuntu 增加服务 比如openfire
在Ubuntu上,可以使用systemd来管理和配置服务。下面是将命令添加为服务的一般步骤: 创建一个.service文件,该文件描述了您要添加的服务。打开终端,并使用以下命令创建一个新的服务文件: sudo nano /etc/systemd/syst…...
海康Visionmaster-全局变量:全局变量关联流程中具体 模块结果的方法
将视觉流程中模板匹配算法模块运行的结果数据:特征匹配点 X 关联全局变量 MatchResultX。 在流程运行的主界面中,按照下面 1,2,3,4 步骤操作,第一步选中算法模块,第二步择模块结果 Tab 页&#…...
Eureka介绍和使用
Eureka介绍和使用 一、基本介绍1. Eureka是什么?2. Eureka的作用3. 常用使用场景4. Eureka的工作原理5. Eureka的优点6. 使用Eureka的注意事项 二、eureka配置项解释1. eureka.instance.hostname2. eureka.instance.appname3. eureka.instance.instance-id4. eureka.client.se…...
Incremental Object Detection via Meta-Learning【论文解析】
Incremental Object Detection via Meta-Learning 摘要1 介绍2 相关工作3 方法3.1 问题描述3.2元学习梯度预处理3.3增量式目标检测器摘要 摘要:在真实世界的情境中,目标检测器可能会不断遇到来自新类别的物体实例。当现有的目标检测器应用于这种情景时,它们对旧类别的性能会…...
AI大模型时代网络安全攻防对抗升级,瑞数信息变革“下一代应用与数据安全”
AI与大模型技术加速普及,安全领域也在以创新视角聚焦下一代应用安全WAAP变革,拓展新一代数据安全领域。近日瑞数信息重磅发布了瑞数全新API扫描器、API安全审计、数据安全检测与应急响应系统及分布式数据库备份系统四大新品。此次发布在延续瑞数信息Bot自…...
后端接口接收对象和文件集合,formdata传递数组对象
0 问题 后端接口需要接收前端传递过来的对象和文件集合;对象中存在数组对象 1 前端和后端 前端只能使用formdata来传递参数,后端不使用RequestBody注解 2 formdata传递数组对象 2.1 多个参数对象数组 addForm: {contactInfo: [{contactPerson: ,…...
python json包
当前大语言模型比较火热,很多数据是以json格式进行数据传递的。python包中的json包就是一个处理Json格式数专业包。 本文主要介绍这个包中的四个函数,dump,dumps,load,loads 序列化为Json dump:将Python对象序列化为Json文件 案例 我们有如…...
基于 NGram 分词,优化 Es 搜索逻辑,并深入理解了 matchPhraseQuery 与 termQuery
基于 NGram 分词,优化 Es 搜索逻辑,并深入理解了 matchPhraseQuery 与 termQuery 前言问题描述排查索引库分词(发现问题)如何去解决这个问题?IK 分词器NGram 分词器使用替换 NGram 分词器后进行测试matchPhraseQuery 查…...
PivotNet:Vectorized Pivot Learning for End-to-end HD Map Construction
参考代码:BeMapNet。PS:代码暂未放出,关注该仓库动态 动机和主要贡献 在MapTR系列的算法中将单个车道线建模为固定数量的有序点集(对应下图Evenly-based),这样的方式对于普通道路场景具备一定适应性。但是…...
阿里云安全恶意程序检测
阿里云安全恶意程序检测 赛题理解赛题介绍赛题说明数据说明评测指标 赛题分析数据特征解题思路 数据探索数据特征类型数据分布箱型图 变量取值分布缺失值异常值分析训练集的tid特征标签分布测试集数据探索同上 数据集联合分析file_id分析API分析 特征工程与基线模型构造特征与特…...
Xcode中如何操作Git
👨🏻💻 热爱摄影的程序员 👨🏻🎨 喜欢编码的设计师 🧕🏻 擅长设计的剪辑师 🧑🏻🏫 一位高冷无情的编码爱好者 大家好,我是全栈工…...
浅述边缘计算场景下的云边端协同融合架构的应用场景示例
云计算正在向一种更加全局化的分布式节点组合形态进阶,而边缘计算是云计算能力向边缘侧分布式拓展的新触角。随着城市建设进程加快,海量设备产生的数据,若上传到云端进行处理,会对云端造成巨大压力。如果利用边缘计算来让云端的能…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
MySQL 主从同步异常处理
阅读原文:https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主,遇到的这个错误: Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一,通常表示ÿ…...
