#![no_std]
use crate::cfg::Configuration;
use esp_hal::Config;
#[cfg(feature = "async")]
use esp_hal::interrupt::software::SoftwareInterruptControl;
use esp_hal::peripherals::Peripherals;
#[cfg(feature = "async")]
use esp_hal::timer::timg::TimerGroup;
use esp_println as _;
mod adcbattery;
mod buzzer;
mod cfg;
mod controller;
mod display;
mod i2s;
#[cfg(feature = "radio")]
mod radio;
mod sd;
mod spi;
pub use adcbattery::{AdcCfg, Adcbattery};
pub use buzzer::{Buzzer, BuzzerInitError};
pub use cfg::{BuzzerConfig, Configuration as LilkaConfiguration};
pub use controller::ControllerState;
pub use display::DisplayInitError;
pub use display::LilkaDisplay;
pub use i2s::{I2sInitError, I2sTx, I2sTxConfig};
pub use sd::{SdInitError, SdVolMgr};
pub use spi::{
InnerDelay as LilkaDelay, SpiBusInner as LilkaSpiBus, SpiBusWrapper, SpiDev, SpiInitError,
init_bus, init_spi_master,
};
use crate::sd::SDC;
pub use defmt;
#[cfg(feature = "async")]
pub use embassy_time;
pub use embedded_sdmmc;
pub use esp_hal as hal;
#[cfg(feature = "async")]
pub use esp_rtos;
pub use mipidsi;
pub mod prelude {
#[cfg(feature = "async")]
pub use crate::RtosRuntime;
pub use crate::cfg::{BuzzerConfig, Configuration};
pub use crate::{Lilka, LilkaConfiguration};
#[cfg(feature = "async")]
pub use embassy_executor;
#[cfg(feature = "async")]
pub use embassy_time;
pub use embedded_sdmmc;
pub use esp_hal;
#[cfg(feature = "async")]
pub use esp_rtos;
pub use mipidsi;
}
pub struct Lilka {
pub peripherals: Peripherals,
#[cfg(feature = "async")]
pub rtos: RtosRuntime,
pub delay: LilkaDelay,
pub controller: ControllerState,
pub adc_battery: Adcbattery<'static>,
pub display: LilkaDisplay,
pub sd: SDC<'static>,
pub i2s_tx: I2sTx<'static>,
pub buzzer: Buzzer,
}
#[cfg(feature = "async")]
pub struct RtosRuntime {
started: bool,
}
#[cfg(feature = "async")]
impl RtosRuntime {
fn start(
timg0: esp_hal::peripherals::TIMG0<'static>,
sw_interrupt: esp_hal::peripherals::SW_INTERRUPT<'static>,
) -> Self {
let timg0 = TimerGroup::new(timg0);
let sw_interrupt = SoftwareInterruptControl::new(sw_interrupt);
esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0);
Self { started: true }
}
pub const fn is_started(&self) -> bool {
self.started
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum InitError {
Spi(SpiInitError),
Display(DisplayInitError),
Sd(SdInitError),
I2s(I2sInitError),
Buzzer(BuzzerInitError),
}
#[derive(Clone, Copy)]
enum StepStatus {
Ok,
Error(&'static str),
Skipped,
}
impl StepStatus {
fn log(self, step: &'static str) {
match self {
Self::Ok => defmt::info!("[OK] {=str}", step),
Self::Error(reason) => defmt::error!("[ERR] {=str}: {=str}", step, reason),
Self::Skipped => defmt::warn!("[SKIP] {=str}", step),
}
}
}
#[derive(Clone, Copy)]
struct InitStatusLog {
spi: StepStatus,
display: StepStatus,
sd: StepStatus,
controller: StepStatus,
adc_battery: StepStatus,
i2s_tx: StepStatus,
buzzer: StepStatus,
}
impl Default for InitStatusLog {
fn default() -> Self {
Self {
spi: StepStatus::Skipped,
display: StepStatus::Skipped,
sd: StepStatus::Skipped,
controller: StepStatus::Skipped,
adc_battery: StepStatus::Skipped,
i2s_tx: StepStatus::Skipped,
buzzer: StepStatus::Skipped,
}
}
}
fn log_init_status(status: &InitStatusLog) {
defmt::info!("Init status summary:");
status.spi.log("SPI");
status.display.log("Display");
status.sd.log("SD");
status.controller.log("Controller");
status.adc_battery.log("ADC battery");
status.i2s_tx.log("I2S TX");
status.buzzer.log("Buzzer");
}
impl Lilka {
pub fn init(config: Configuration) -> Result<Self, InitError> {
let mut status = InitStatusLog::default();
let Configuration {
cpu_clock,
spi_frequency,
display_refresh_order,
display_rotation,
display_tearing,
adc_cfg,
i2s_tx_config,
buzzer_config,
} = config;
let peripherals = esp_hal::init(Config::default().with_cpu_clock(cpu_clock));
#[cfg(feature = "async")]
let rtos = RtosRuntime::start(peripherals.TIMG0, peripherals.SW_INTERRUPT);
let spi = match init_spi_master(
peripherals.SPI2,
peripherals.GPIO18,
peripherals.GPIO17,
peripherals.GPIO8,
spi_frequency,
) {
Ok(spi) => {
status.spi = StepStatus::Ok;
spi
}
Err(err) => {
status.spi = StepStatus::Error(err.as_str());
log_init_status(&status);
return Err(InitError::Spi(err));
}
};
#[cfg(feature = "blocking")]
let delay = spi::InnerDelay::new();
#[cfg(feature = "async")]
let delay = embassy_time::Delay;
let spi_bus = init_bus(spi);
let display = match display::init_display(
peripherals.GPIO46,
peripherals.GPIO15,
peripherals.GPIO7,
spi_bus,
delay.clone(),
display_refresh_order,
display_rotation,
display_tearing,
) {
Ok(display) => {
status.display = StepStatus::Ok;
display
}
Err(err) => {
status.display = StepStatus::Error(err.as_str());
log_init_status(&status);
return Err(InitError::Display(err));
}
};
let sd = match sd::init_sd(spi_bus, peripherals.GPIO16, delay.clone()) {
Ok(sd) => {
status.sd = StepStatus::Ok;
sd
}
Err(err) => {
status.sd = StepStatus::Error(err.as_str());
log_init_status(&status);
return Err(InitError::Sd(err));
}
};
let controller = ControllerState::init_controller(
peripherals.GPIO5,
peripherals.GPIO6,
peripherals.GPIO10,
peripherals.GPIO9,
peripherals.GPIO38,
peripherals.GPIO41,
peripherals.GPIO39,
peripherals.GPIO40,
peripherals.GPIO0,
peripherals.GPIO4,
);
status.controller = StepStatus::Ok;
let adc_battery = adcbattery::init_adcbattery(peripherals.GPIO3, adc_cfg, peripherals.ADC1);
status.adc_battery = StepStatus::Ok;
let i2s_tx = match i2s::init_i2s_tx(
i2s_tx_config,
peripherals.GPIO42,
peripherals.GPIO2,
peripherals.GPIO1,
peripherals.I2S0,
peripherals.DMA_CH0,
) {
Ok(i2s_tx) => {
status.i2s_tx = StepStatus::Ok;
i2s_tx
}
Err(err) => {
status.i2s_tx = StepStatus::Error(err.as_str());
log_init_status(&status);
return Err(InitError::I2s(err));
}
};
let buzzer = match buzzer::init_buzzer(buzzer_config, peripherals.LEDC, peripherals.GPIO11)
{
Ok(buzzer) => {
status.buzzer = StepStatus::Ok;
buzzer
}
Err(err) => {
status.buzzer = StepStatus::Error(err.as_str());
log_init_status(&status);
return Err(InitError::Buzzer(err));
}
};
log_init_status(&status);
Ok(Self {
peripherals: unsafe { Peripherals::steal() },
#[cfg(feature = "async")]
rtos,
delay,
controller,
adc_battery,
display,
sd,
i2s_tx,
buzzer,
})
}
}