Перейти до змісту

Дисплей та кнопки

Почнемо з того, що видно одразу: напис на дисплеї й число, яке збільшується від кнопки A. Тут немає радіо та асинхронних задач, тільки blocking і RefCellBus.

Налаштування залежностей

Це справжній Cargo.toml прикладу з репозиторію:

[package]
name = "basics"
version = "0.1.0"
edition = "2024"
rust-version = "1.88"
publish = false

[dependencies]
lilkaoxide = { path = "../../sdk-hal", default-features = false, features = ["blocking", "RefCellBus", "alloc", "display_buffer_1024"] }
embedded-graphics = "0.8.2"
heapless = "0.9.1"
esp-alloc = { version = "0.10.0", features = ["defmt"] }
esp-bootloader-esp-idf = { version = "0.5.0", features = ["esp32s3", "defmt"] }
esp-println = { version = "0.16.1", features = ["esp32s3", "defmt-espflash"] }

path = "../../sdk-hal" працює всередині цього репозиторію. В окремому проєкті замість нього вкажіть шлях до своєї копії SDK або опубліковану version. Приклади з новими API можуть випереджати версію на crates.io.

Код

#![no_std]
#![no_main]

use core::fmt::Write;
use embedded_graphics::{
    mono_font::{MonoTextStyle, ascii::FONT_9X15_BOLD},
    pixelcolor::Rgb565,
    prelude::*,
    text::{Baseline, Text},
};
use esp_println::{self as _, println};
use lilkaoxide::prelude::*;

esp_bootloader_esp_idf::esp_app_desc!();

#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
    println!("PANIC: {}", info);
    loop {
        core::hint::spin_loop();
    }
}

#[esp_hal::main]
fn main() -> ! {
    esp_alloc::heap_allocator!(size: 16 * 1024);
    let mut config = Configuration::default();
    config.buzzer_config.duty_pct = 0;
    let lilka = Lilka::init(config).expect("Lilka init failed");
    let mut display = lilka.display;
    let controller = lilka.controller;
    let delay = esp_hal::delay::Delay::new();
    let mut count = 0u32;
    let mut was_pressed = false;
    let mut redraw = true;
    loop {
        let pressed = controller.a.is_low();
        if pressed && !was_pressed {
            count = count.wrapping_add(1);
            redraw = true;
        }
        was_pressed = pressed;
        if redraw {
            display.clear(Rgb565::BLACK).unwrap();
            let style = MonoTextStyle::new(&FONT_9X15_BOLD, Rgb565::CYAN);
            Text::with_baseline("Hello, Lilka!", Point::new(12, 24), style, Baseline::Top)
                .draw(&mut display)
                .unwrap();
            let mut line: heapless::String<32> = heapless::String::new();
            let _ = write!(line, "A pressed: {}", count);
            Text::with_baseline(&line, Point::new(12, 64), style, Baseline::Top)
                .draw(&mut display)
                .unwrap();
            redraw = false;
        }
        // This simple example samples every 30 ms; the editor has full debounce.
        delay.delay_millis(30);
    }
}

Що тут відбувається

Lilka::init(config) ініціалізує периферію та повертає об'єкт з дисплеєм, кнопками, SD й іншими частинами. Поля можна забирати окремо, тому дисплей залишається змінним, а контролер просто читається в циклі.

Штатне положення дисплея Lilka — 280×240, Rotation::Deg270. Сам контролер ST7789 працює з панеллю 240×280, але малюємо ми вже в повернутій системі координат. Точка (0, 0) — зверху зліва.

Кнопки мають pull-up, тому is_low() означає натиснуту кнопку. was_pressed потрібен, щоби утримання A не збільшувало число на кожній ітерації. Опитування раз на 30 мс тут спрощене; для справжнього debounce треба перевіряти незмінність стану протягом певного часу, а автоповтор робити окремим таймером.

redraw не дає перемальовувати дисплей без причини. Навіщо ганяти той самий текст по SPI сотні разів, якщо він не змінився?

Буфер дисплея — не framebuffer

display_buffer_1024 — це робочий SPI-буфер драйвера. Він не займає 280×240×2 байти під повний кадр. Якщо потрібен framebuffer, його треба створювати окремо, що займає додаткову RAM.

Для запуску: cd examples/basics, після цього cargo +esp run --release --locked. Для Keira — збереження .bin.

Що покласти в проєкт

Приклад повністю лежить у examples/basics/: Cargo.toml, build.rs, rust-toolchain.toml, .cargo/config.toml та src/main.rs. Тут немає пропущеного main чи прихованої ініціалізації.

cd examples/basics
cargo +esp build --release --locked

Після запуску має бути чорний фон, Hello, Lilka! та A pressed: 0. Натисніть і відпустіть A — лічильник збільшиться на одиницю. Утримання саме по собі число не накручує. Програмний debounce не реалізований, тому брязкіт контакту все ще може дати зайве натискання.

Якщо екран порожній, спочатку дивіться лог Lilka init failed або PANIC, а не змінюйте GPIO навмання. SDK вже знає розводку Lilka v2. Докладніше про перевірку — збірка та налагодження.