首页
苏兮影视
随笔记
壁纸
更多
直播
时光轴
友联
关于
统计
Search
1
软件添加id功能按钮
775 阅读
2
v2ray节点搭建
774 阅读
3
typecho非常有特色的模块
563 阅读
4
QQ扫码无法登录的解决方案
516 阅读
5
QQxml消息卡片生成源码
514 阅读
谈天说地
建站源码
经验教程
资源分享
动漫美图
登录
Search
标签搜索
java
rust
flutter
esp32c3
springboot
安卓
linux
vue
docker
joe
快捷键
git
fish shell
maven
redis
netty
dart
groovy
js
设计模式
尽意
累计撰写
109
篇文章
累计收到
39
条评论
首页
栏目
谈天说地
建站源码
经验教程
资源分享
动漫美图
页面
苏兮影视
随笔记
壁纸
直播
时光轴
友联
关于
统计
搜索到
2
篇与
的结果
2025-05-05
ESP32C3使用Slint渲染OLED屏幕
前言:说一下为什么要用slint,既然都选择用rust开发嵌入式了避免不了要折腾,也是为后续驱动st7789做铺垫吧。而其实对应OLED屏幕已经有一个很好用的库了embedded-graphics,几乎能满足OLED屏幕的全部需求了,但是slint有动画,定时器,声明式,这些优势,再嵌入式开发再合适不过了。在写这个的时候也遇到不少坑,所以记录一下吧创建项目引入库编译问题已经在 ESP32C3引入Slint 文章解决了先创建一个最基础的esp32c3的项目然后再Cargo.toml中添加依赖# 基础依赖 critical-section = "1.2.0" esp-hal = { version = "1.0.0-beta.0", features = ["esp32c3","unstable"] } esp-alloc = "0.7.0" esp-println = { version = "0.13.1",features = ["esp32c3","log"] } log = "0.4.27" esp-backtrace = { version = "0.15.1" ,features = ["esp32c3","println","panic-handler"]} # 图形相关 embedded-hal = "0.2.7" embedded-graphics-core = "0.4.0" embedded-graphics = "0.8.1" embedded-graphics-framebuf = "0.5.0" ssd1306 = { version = "0.10.0" ,features = ["graphics"]} # 异步 esp-hal-embassy = { version = "0.7.0", features = ["esp32c3"] } embassy-time = { version = "0.4.0", features = ["generic-queue-8"] } embassy-executor = { version = "0.7.0",features = ["task-arena-size-20480"] } static_cell = { version = "2.1.0", features = ["nightly"] } [dependencies.slint] git = "https://github.com/slint-ui/slint" rev = "29168bc89270798f6075a0a729c14a3f011ceb4f" # 修复提交的哈希 default-features = false features = [ "compat-1-2", "unsafe-single-threaded", # 启用单线程模式 "libm", "renderer-software", ] [build-dependencies] slint-build = {git = "https://github.com/slint-ui/slint",rev = "29168bc89270798f6075a0a729c14a3f011ceb4f"} 配置Slint我们使用slint的ui跟代码分离的模式,修改build.rs新增一个build_slint函数,然后在main函数中调用它 fn main(){ build_slint(); ... } fn build_slint(){ slint_build::compile_with_config( "ui/MainView.slint", slint_build::CompilerConfiguration::new() .embed_resources(slint_build::EmbedResourcesKind::EmbedForSoftwareRenderer) ).unwrap() }创建UI文件在项目根目录下创建 ui/MainView.slint 文件,定义 UI 界面// 由于是在no_std的环境下,没有默认字体,使用需要引入自定义的字体 import "./fonts/simhei.ttf"; // 这里定义了一个全局的状态,用于在rust代码里进行修改ui状态 export global MyState { in-out property <bool> is_show : true; } export component MainView inherits Window { default-font-family: "黑体"; width: 128px; height: 64px; Rectangle { x:0px; y:0px; width: 100%; height: 100%; background: transparent; border-color: black; border-width: 5px; } Rectangle { x:10px; y:6px; width: 50px; height: 50px; background: black; border-radius: 360px; visible: MyState.is_show; } property <bool> is_show : false; Rectangle { x:80px; y:6px; width: 20px; height: 20px; background: black; visible: is_show; } // 使用了slint的ui的定时器 timer:=Timer{ interval: 0.5s; running: true; triggered => { is_show = !is_show; } } Text { font-family: "SimHei"; text: "12345"; color: black; font-weight: 900; font-size: 40px; x:66px; } }编写main.rs代码因为是使用的no_std,没有多线程,而slint的run方法会调用invoke_from_event_loop方法,进而导致阻塞线程,所以需要改造,不使用run方法来渲染,而是手动处理渲染,在实现traitPlatform时,不去重写invoke_from_event_loop函数,我们在初始化代码块去使用异步发方式实现 slint::include_modules!(); #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { loop { error!("panic:{}",info); } } #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { // generator version: 0.3.1 // init_logger_from_env(); init_logger(LevelFilter::Debug); esp_alloc::heap_allocator!(size: 100 * 1024); info!("分配内存完成"); let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); let peripherals =esp_hal::init(config); // 配置异步 let timer0 = SystemTimer::new(peripherals.SYSTIMER); esp_hal_embassy::init(timer0.alarm0); let i2c = esp_hal::i2c::master::I2c::new(peripherals.I2C0, esp_hal::i2c::master::Config::default() .with_frequency(Rate::from_khz(400))) .unwrap() .with_sda(peripherals.GPIO8) .with_scl(peripherals.GPIO9); let display = Ssd1306::new( I2CDisplayInterface::new(i2c), DisplaySize128x64, DisplayRotation::Rotate0).into_buffered_graphics_mode(); info!("开启异步任务"); spawner.spawn(ui_run(display,spawner)).ok(); loop{ Timer::after_secs(2).await; } // for inspiration have a look at the examples at https://gitcahub.com/esp-rs/esp-hal/tree/esp-hal-v1.0.0-beta.0/examples/src/bin } #[embassy_executor::task] async fn ui_run(mut display: SSD,spawner: Spawner){ // 创建平台实例 let window = slint::platform::software_renderer::MinimalSoftwareWindow::new( slint::platform::software_renderer::RepaintBufferType::ReusedBuffer, ); window.set_size(PhysicalSize::new(128,64)); let platform = EspPlatform::new(window.clone()); slint::platform::set_platform(Box::new(platform)).unwrap(); // 创建ui let ui = MainView::new().unwrap(); ui.window().set_size(PhysicalSize::new(128,64)); let ui_weak = ui.as_weak(); spawner.spawn(ui_task(ui_weak)).ok(); info!("进入了loop run"); // 初始化显示 display.init().unwrap(); display.clear(BinaryColor::Off).unwrap(); display.flush().unwrap(); info!("清理屏幕 on"); // 创建行缓冲区 let display_width = 128; // 根据实际显示宽度设置 let mut the_frame_buffer = [Rgb565Pixel(0); 128 * 64]; // 128x64 // 这里是我们手动实现的invoke_from_event_loop loop { // 更新UI状态 slint::platform::update_timers_and_animations(); // 渲染UI window.draw_if_needed(|renderer| { info!("进入渲染回调"); renderer.render_by_line(FrameBuffer{ frame_buffer: &mut the_frame_buffer, stride: display_width,display:&mut display }); info!("退出渲染回调"); // 退出渲染回调,说明整个帧已经渲染完成,这时候刷新屏幕 display.flush().unwrap(); }); Timer::after_millis(100).await; } } #[embassy_executor::task] async fn ui_task(view:Weak<MainView>){ info!("ui_task 已经开启"); loop{ // 在这里用rust的代码更新slint的ui状态 let view = view.clone(); let view = view.upgrade().unwrap(); let show = view.global::<MyState>().get_is_show(); view.global::<MyState>().set_is_show(!show); info!("show的状态为:{}",show); Timer::after_secs(2).await; } } // 平台实现结构体 // 为了方便,先将OLED的屏幕生命周期设置为静态的 type SSD = Ssd1306<I2CInterface<I2c<'static,Blocking>>,DisplaySize128x64,BufferedGraphicsMode<DisplaySize128x64>>; struct EspPlatform{ window: Rc<slint::platform::software_renderer::MinimalSoftwareWindow>, } impl EspPlatform { fn new(window:Rc<MinimalSoftwareWindow>) -> Self { Self { window } } } /// 实现 Platform trait impl Platform for EspPlatform { fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, PlatformError> { info!("成功创建了window"); Ok(self.window.clone()) } fn duration_since_start(&self) -> core::time::Duration { let time = Instant::now().duration_since_epoch().as_millis(); // info!("time :{}",time); core::time::Duration::from_millis( time ) } } struct FrameBuffer<'a>{ frame_buffer: &'a mut [Rgb565Pixel], stride: usize,display:&'a mut SSD } impl<'a> LineBufferProvider for FrameBuffer<'a> { type TargetPixel = Rgb565Pixel; fn process_line( &mut self, line: usize, range: core::ops::Range<usize>, render_fn: impl FnOnce(&mut [Self::TargetPixel]), ) { let line_start = line * self.stride; let line_pixels = &mut self.frame_buffer[line_start..line_start + 128]; render_fn(&mut line_pixels[range.clone()]); // 规定黑色为亮,由于是在oled屏幕上渲染,只有亮或者不亮,为了方便我们规定一个颜色为亮色,其余的颜色都视为不亮,在ui页面也是,使用黑色作为渲染的区域 let black = Rgb565Pixel(0); for (x,row) in line_pixels.iter().enumerate(){ // 先将要更新的行的地方进行清屏 self.display.set_pixel(x as u32,line as u32,false); // 然后对应要渲染的像素点进行渲染 if row == &black{ self.display.set_pixel(x as u32, line as u32, true); } } } } 编译查看效果预览页面烧录的效果跟我们slint编写的ui页面一致
2025年05月05日
17 阅读
0 评论
0 点赞
ESP32C3引入Slint: 解决依赖冲突
为什么要折腾这个?想在 ESP32-C3 开发板上实现一个酷炫的 UI 界面,选了 Rust 生态的 Slint 框架。结果刚加上依赖就报错:error: 不能同时启用 critical-section 和 portable_atomic_unsafe_assume_single_core这两个特性不能同时存在解决问题过程第一步:创建最小测试项目# 生成纯净的 ESP32-C3 项目骨架 esp-generate --chip=esp32c3 slint_demo此时项目仅包含最基础的硬件驱动,编译一次成功:cargo build Compiling slint_demo v0.1.0 Finished dev [optimized] target(s)第二步:引入 Slint 引爆问题按官方文档添加依赖:cargo add slint@1.10.0 --no-default-features --features "compat-1-2 unsafe-single-threaded libm renderer-software"再次编译立刻报错:error: critical-section 与 portable_atomic_unsafe_assume_single_core 冲突第三步:定位问题注释 Slint 保留 ESP-HAL → 编译成功注释 ESP-HAL 保留 Slint → 依然报错Slint 的依赖树里藏了冲突的组件第四步:解决问题搜索 slint critical-section site:github.com锁定关键讨论:Issue #5057发现修复补丁:Commit 29168bc核心修改点:# Slint 内部配置调整 - portable-atomic = { features = ["critical-section"] } + portable-atomic = {} # 解除强制依赖最终配置方案关键依赖声明 (Cargo.toml)[dependencies] # 硬件抽象层 esp-hal = { version = "1.0.0-beta.0", default-features = false, features = ["esp32c3"] # 注意关闭默认特性 } # UI 框架 [dependencies.slint] git = "https://github.com/slint-ui/slint" rev = "29168bc89270798f6075a0a729c14a3f011ceb4f" # 锁定修复版本 default-features = false features = [ "compat-1-2", # 兼容旧版 API "unsafe-single-threaded", # 单核模式 "libm", # 数学库支持 "renderer-software" # 软件渲染 ] # 内存分配器 esp-alloc = "0.7.0"内存管理配置 (main.rs)// 初始化堆内存(72KB 用于 UI) #[main] fn main() -> ! { let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); let _peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(size: 72 * 1024); }经验总结依赖管理铁律用 cargo tree -e features 查看依赖关系通过 default-features = false 精准控制特性版本锁定技巧使用 rev = "commit_hash" 锁定特定修复版本定期检查 GitHub Issues 跟踪问题进展
2025年05月03日
10 阅读
0 评论
0 点赞