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

Периферія Lilka

Після Lilka::init ми отримуємо вже налаштовані частини консолі. Не треба створювати другий SPI для дисплея або вручну переводити кнопки в input. Але SDK не приховує самі драйвери, тому для складніших речей можна користуватись їхнім API.

Дисплей

Поле display має тип LilkaDisplay, який залежить від features. У blocking це mipidsi, в async — lcd-async.

Штатний поворот дає 280×240, точка (0, 0) зверху зліва. Для blocking звичайний текст і примітиви малюються через embedded-graphics. Координата тексту залежить від baseline: з Baseline::Top це верх рядка, а не нижня лінія символів.

Повний приклад дисплея й кнопок можна зібрати без дописування ініціалізації. Для async є окрема програма, яка малює в маленький RGB565-буфер і передає його на екран через .await.

Не очищуйте весь дисплей на кожному проході циклу без потреби. Запам'ятайте зміну стану, поставте redraw, а вже після малювання скиньте його. Це зменшує передачу по SPI і мерехтіння.

Кнопки

controller має поля up, down, left, right, a, b, c, d, select, start. Це GPIO з pull-up, тому is_low() означає натиснуто.

let controller = lilka.controller;
if controller.a.is_low() {
    esp_println::println!("A is held");
}

Це перевірка утримання, а не подія. Щоб отримати одне натискання, порівнюйте з попереднім станом. Для debounce приймайте новий стан тільки після того, як він певний час не змінювався. Для автоповтору потрібні ще затримка після першого натискання й інтервал повторів.

Не робіть довгий delay всередині обробки A, якщо одночасно очікуєте реакцію на B. Краще зберігати час наступної дії та далі опитувати решту кнопок.

microSD

sd — це Option. Відсутня або неініціалізована карта дає None, а дисплей може працювати далі. Не робіть lilka.sd.unwrap(), якщо програма має запускатись і без карти.

Драйвер працює з секторами по 512 байтів. Для файлів потрібен FAT-рівень; alias SdVolMgr у SDK попри назву не монтує файлову систему. Самодостатній приклад показує монтування, відкриття, читання і закриття файла; адаптер BlockDevice винесено в окремий модуль.

Дисплей і карта ділять SPI. Спроби звертатись до тієї ж blocking-шини з переривання під час активної позики — не спосіб пришвидшити читання.

Батарея

adc_battery вже налаштований під дільник напруги на платі:

let mut adc = lilka.adc_battery;
let millivolts = adc.read_voltage_mv();
esp_println::println!("Battery: {} mV", millivolts);

Це оцінка напруги, а не готовий точний відсоток заряду. ADC має похибку, напруга змінюється під навантаженням, а батарея не розряджається лінійно. Для індикатора корисно усереднювати вимірювання й не оновлювати цифру на кожному проході циклу.

Зумер

За замовчуванням у BuzzerConfig duty 50%, тому в навчальних прикладах до ініціалізації ставимо duty_pct = 0. Інакше можна несподівано почути постійний тон.

Короткий сигнал у blocking-коді після ініціалізації:

use lilkaoxide::hal::ledc::channel::ChannelIFace;

let mut buzzer = lilka.buzzer;
buzzer.channel.set_duty_hw(128);
lilka.delay.delay_millis(80);
buzzer.channel.set_duty_hw(0);

Значення 128 тут приблизно відповідає 50% при стандартному Duty8Bit. Після іншого timer_duty діапазон буде інший. Частота задається в buzzer_config.frequency, це окрема річ від гучності через duty.

В async замініть очікування на Timer::after(...).await, щоби не блокувати executor на час сигналу.

I2S та звук

i2s_tx — передавач I2S0 з DMA. Це не те саме, що зумер: він передає цифрові аудіосемпли зовнішньому DAC. Стандартна конфігурація — 44100 Гц, стерео, 16-бітні семпли, Philips I2S.

i2s_tx_config дає змогу змінити ці параметри до Lilka::init. Дані повинні відповідати обраному формату: WAV-заголовок або стиснений MP3 не можна просто передати як PCM. Декодування, черга буферів і повторне заповнення DMA залишаються на застосунку. Blocking і async режими мають різні методи передачі; конкретні сигнатури дивіться в Rustdoc типу I2sTx та використовуваної версії esp-hal.

RTC, Wi-Fi та BLE

Для збереженого часу є окремий приклад keira-rtc. Правдоподібний timestamp не доводить, що годинник синхронізований саме сьогодні; SDK не вміє визначити це без зовнішнього джерела.

Для Wi-Fi/BLE потрібен feature radio. Після ініціалізації доступні відповідні поля у peripherals, але мережеві з'єднання й протоколи треба створювати окремо. Не викликайте повторно esp_hal::init: використовуйте периферію, яку повернув SDK, і не забирайте GPIO, вже зайняті дисплеєм, SD чи звуком.