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

news2024/12/29 19:03:05

文章目录

  • 说在前面
  • 关于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 设置默认backend
    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();
    // 由于我的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;

		// 初始化spi
        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();

		// spi interface
        let 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中的数据传给spi
            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);

        // 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();
    }
}

结果

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

参考

  • slint mcu

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

asp.net docker-compose添加dapr配置

docker-compose.yml添加配置 webapplication1-dapr:image: "daprio/daprd:1.9.6"network_mode: "service:webapplication1"depends_on:- webapplication1 docker-compose.override.yml中添加 dapr-placement:command: ["./placement", "-po…

Nacos 注册中心如何做到每秒百万级别的服务注册?

1. Nacos 注册中心如何做到每秒百万级别的服务注册 &#xff1f; 在 nacos.io 官方文档里边有这样一个性能测试报告&#xff1a; PS&#xff1a;TPS 是指每秒能够处理的事物数 / 任务数 从图中可以看出当有 4 台机器&#xff0c;并发数为 100 的时候&#xff0c;注册实例的测…

SpringSecurity全家桶 (一) —— 简介

1. 概述 Spring Security 是一个框架&#xff0c;提供针对常见攻击的身份验证、授权和保护。 它为保护命令式和响应式应用程序提供了一流的支持&#xff0c;是保护基于 Spring 的应用程序的事实标准。 2. 了解 shiro&#xff1a; 在之前SSM框架盛行的时代&#xff0c;项目的…

在maven官网中如何下载低版本的maven

链接&#xff1a;https://archive.apache.org/dist/maven/maven-3/

万宾科技管网水位监测助力智慧城市的排水系统

以往如果要了解城市地下排水管网的水位变化&#xff0c;需要依靠人工巡检或者排查的方式&#xff0c;这不仅加大了人员的工作量&#xff0c;而且也为市政府带来了更多的工作难题。比如人员监管监测不到位或无法远程监控等情况&#xff0c;都会降低市政府对排水管网的管理能力&a…

汇编语言(举个栗子)

汇编语言&#xff08;Assembly Language&#xff09;是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言&#xff0c;亦称为符号语言。在汇编语言中&#xff0c;用助记符代替机器指令的操作码&#xff0c;用地址符号或标号代替指令或操作数的地址。在不同…

物理机安装黑群晖

物理机安装黑群晖 黑群晖系统: DSM 6.2.0–6.2.3 引导版本: 1.04b (首选推荐版本) 针对机型: DS918 引导方式: 引导方式&#xff1a;传统BIOS和UEFI&#xff08;可选&#xff09; 点击此处链接&#xff0c;引导下载地址 点此处为&#xff1a;DS918 DSM 6.2.3-25426 Update2 系…

【LeetCode:421. 数组中两个数的最大异或值 | 位运算 贪心】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

【漏洞复现】Drupal XSS漏洞复现

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 复现环境&#xff1a;Vulhub 环境启动后&#xff0c;访问 http://192.168.80.141:8080/ 将会看到drupal的安装页面&#xff0c;一路默认配置下一步安装。因为没有mysql环境&#xff0c;…

JavaEE平台技术——预备知识(Web、Sevlet、Tomcat)

JavaEE平台技术——预备知识&#xff08;Web、Sevlet、Tomcat&#xff09; 1. Web基础知识2. Servlet3. Tomcat并发原理 1. Web基础知识 &#x1f192;&#x1f192;上个CSDN我们讲的是JavaEE的这个渊源&#xff0c;实际上讲了两个小时的历史课&#xff0c;给大家梳理了一下&a…

鲁棒性的含义以及如何提高模型的鲁棒性

1、含义 鲁棒是Robust的音译&#xff0c;也就是健壮和强壮的意思。它也是在异常和危险情况下系统生存的能力。比如说&#xff0c;计算机软件在输入错误、磁盘故障、网络过载或有意攻击情况下&#xff0c;能否不死机、不崩溃&#xff0c;就是该软件的鲁棒性。所谓“鲁棒性”&am…

蓝桥杯官网填空题(方格填数)

题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 在 2 行 5 列的格子中填入 1 到 10 的数字。 要求&#xff1a; 相邻的格子中的数&#xff0c;右边的大于左边的&#xff0c;下边的大于上边的。 如下图所示的 …

项目实战:在首页上添加分页按钮

1、在index.html添加div_pagination <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><link rel"stylesheet" href"style/index.css"><script src&qu…

设置IDEA快捷生成方法头,类头注释

1.File->settings->editor->live templates进入Live Template界面进行设置&#xff1a; 下一步&#xff1a; 下一步&#xff1a; /*** Title: $title$* author: sunyanzeng* date: $datatime$*/在需要添加文件头的地方打出“aa”&#xff0c;回车&#xff0c;会自…

算法——多数相和

三数 15. 三数之和 - 力扣&#xff08;LeetCode&#xff09; 所以代码实现应该是 vector<vector<int>> threeSum(vector<int>& nums) {int n nums.size();sort(nums.begin(), nums.end()); // 对数组进行排序&#xff0c;以便后续操作vector<vector…

springboot--外部环境配置

外部环境配置 前言1、配置优先级配置文件优先级如下&#xff08;后面的覆盖前面的&#xff09;测试 2、外部配置3、导入配置4、属性占位符 前言 场景&#xff1a;线上应用如何快速修改配置&#xff0c;并引用最新配置&#xff1f; springBoot 使用配置优先级外部配置 简化配置…

黑客入门 15 个必杀技能!

互联网新兴技术不断涌现&#xff0c;在给人类带来巨大财富和便捷的同时&#xff0c;也带来了非常严峻的网络安全问题。 侵害个人隐私、侵犯知识产权、网络犯罪等时有发生&#xff0c;网络监听、网络攻击、网络恐怖主义活动等成为**全球公害。 习主席在全国网络安全和信息化工…

基于SSM的物流仓储管理系统

项目名称 基于SSM的物流仓储管理系统 演示视频 基于SSM的物流仓储管理系统&#xff08;包远程安装配置和代码讲解&#xff09;_哔哩哔哩_bilibili 一、项目简介 本项目是一套基于SSM的物流仓储管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习…

【Java-框架-Mybatis】(01) 使用Mybatis框架操作MySQL数据库,快速上手

前言 使用"Mybatis"框架操作"MySQL"数据库&#xff0c;快速上手&#xff1b; 实操一 【说明】 通过"IntelliJ IDEA"软件来创建"Maven"项目&#xff1b;通过"Mybatis"框架完成"MySQL"数据库操作&#xff1b; 【环…