lilkaoxide/
cfg.rs

1use crate::adcbattery::AdcCfg;
2use crate::i2s::I2sTxConfig;
3use esp_hal::analog::adc::Attenuation;
4use esp_hal::clock::CpuClock;
5use esp_hal::ledc::timer::config::Duty;
6use esp_hal::time::Rate;
7use fugit::RateExtU32;
8#[cfg(feature = "blocking")]
9use mipidsi::options::{RefreshOrder, Rotation, TearingEffect};
10#[cfg(feature = "async")]
11use lcd_async::options::{RefreshOrder, Rotation, TearingEffect};
12
13/// Configuration for the on-board PWM buzzer (LEDC peripheral, GPIO11).
14///
15/// Controls the pitch and volume of the piezo-electric buzzer.
16/// All fields are plain data and can be changed at runtime by reconfiguring the
17/// LEDC channel via [`crate::Buzzer::channel`].
18///
19/// # Defaults
20///
21/// | Field | Default value | Notes |
22/// |---|---|---|
23/// | `frequency` | 4 000 Hz | Mid-range buzzer tone. |
24/// | `duty_pct` | 50 % | Square wave — maximum amplitude. |
25/// | `timer_duty` | `Duty8Bit` | 256 steps of duty resolution. |
26///
27/// # Example
28///
29/// ```rust,no_run
30/// use lilkaoxide::LilkaConfiguration;
31/// use lilkaoxide::cfg::BuzzerConfig;
32/// use esp_hal::time::Rate;
33/// use esp_hal::ledc::timer::config::Duty;
34///
35/// // High-pitched short beep
36/// let beep = BuzzerConfig {
37///     frequency: Rate::from_hz(2_000),
38///     duty_pct: 30,
39///     timer_duty: Duty::Duty8Bit,
40/// };
41///
42/// let config = LilkaConfiguration {
43///     buzzer_config: beep,
44///     ..LilkaConfiguration::default()
45/// };
46/// ```
47#[derive(Clone, Copy)]
48pub struct BuzzerConfig {
49    /// Buzzer frequency in Hz.  Audible range for the Lilka piezo: ~500 Hz – 8 000 Hz.
50    pub frequency: Rate,
51    /// PWM duty cycle 0–100 %.  Values above 100 are clamped to 100 internally.
52    pub duty_pct: u8,
53    /// LEDC timer bit-width.  `Duty8Bit` gives 256 steps and is sufficient for a buzzer.
54    pub timer_duty: Duty,
55}
56
57impl Default for BuzzerConfig {
58    fn default() -> Self {
59        Self {
60            frequency: Rate::from_hz(4_000),
61            duty_pct: 50,
62            timer_duty: Duty::Duty8Bit,
63        }
64    }
65}
66
67/// Top-level configuration passed to [`crate::Lilka::init`].
68///
69/// All fields have sensible defaults via [`Configuration::default`].
70/// Use struct-update syntax to override only what you need:
71///
72/// ```rust,no_run
73/// use lilkaoxide::prelude::*;
74///
75/// let config = LilkaConfiguration {
76///     display_rotation: mipidsi::options::Rotation::Deg90,
77///     ..LilkaConfiguration::default()
78/// };
79/// ```
80///
81/// # Field reference
82///
83/// | Field | Type | Default | Description |
84/// |---|---|---|---|
85/// | `cpu_clock` | [`CpuClock`] | system default | ESP32-S3 core frequency. |
86/// | `spi_frequency` | `HertzU32` | 60 MHz | SPI2 clock for display + SD. |
87/// | `display_refresh_order` | [`RefreshOrder`] | default | ST7789 line-scan order. |
88/// | `display_rotation` | [`Rotation`] | `Deg270` | Screen rotation (landscape). |
89/// | `display_tearing` | [`TearingEffect`] | `Off` | Tearing-effect output signal. |
90/// | `adc_cfg` | [`AdcCfg`] | 11 dB attenuation | Battery ADC channel config. |
91/// | `i2s_tx_config` | [`I2sTxConfig`] | 44 100 Hz stereo | I2S audio output config. |
92/// | `buzzer_config` | [`BuzzerConfig`] | 4 kHz / 50 % | PWM buzzer config. |
93pub struct Configuration {
94    /// CPU clock speed.  Higher speeds increase performance but also current draw.
95    pub cpu_clock: CpuClock,
96    /// SPI2 bus clock.  Both the display (ST7789) and the SD card share this bus.
97    /// The ST7789 supports up to ~80 MHz; SD cards typically max out at 25 MHz.
98    /// 60 MHz is a balanced default that works reliably with both devices.
99    pub spi_frequency: fugit::HertzU32,
100    /// Controls the order in which the ST7789 controller scans rows and columns
101    /// during a frame refresh.  Changing this can fix visual artifacts on some panels.
102    pub display_refresh_order: RefreshOrder,
103    /// Physical rotation of the display image.  The Lilka is held in landscape mode,
104    /// so the default `Deg270` maps the (0,0) origin to the top-left corner.
105    pub display_rotation: Rotation,
106    /// Enables the tearing-effect (TE) output signal on the ST7789.  Leave `Off`
107    /// unless you are implementing vsync-locked rendering.
108    pub display_tearing: TearingEffect,
109    /// Battery ADC channel configuration.  The default 11 dB attenuation allows
110    /// measurement up to ~3.1 V which covers a typical Li-Po discharge range.
111    pub adc_cfg: AdcCfg,
112    /// I2S transmitter configuration (sample rate, channel count, bit depth, TDM standard).
113    pub i2s_tx_config: I2sTxConfig,
114    /// PWM buzzer configuration (frequency, duty cycle, timer resolution).
115    pub buzzer_config: BuzzerConfig,
116}
117
118impl Default for Configuration {
119    fn default() -> Configuration {
120        Configuration {
121            cpu_clock: Default::default(),
122            spi_frequency: 60.MHz(),
123            display_refresh_order: RefreshOrder::default(),
124            display_rotation: Rotation::Deg270,
125            display_tearing: TearingEffect::Off,
126            adc_cfg: AdcCfg {
127                attenuation: Attenuation::_11dB,
128            },
129            i2s_tx_config: I2sTxConfig::default(),
130            buzzer_config: BuzzerConfig::default(),
131        }
132    }
133}
134
135impl Configuration {
136    /// Creates a fully-specified [`Configuration`].
137    ///
138    /// Prefer using struct-update syntax with [`Configuration::default`] when
139    /// you only want to override a few fields — it is more readable and less
140    /// error-prone than listing every argument.
141    ///
142    /// # Parameters
143    ///
144    /// * `spi_frequency` — SPI2 clock.  `60.MHz()` is a safe default.
145    /// * `cpu_clock` — CPU core frequency.  `CpuClock::default()` starts at 240 MHz.
146    /// * `display_refresh_order` — ST7789 pixel scan order.
147    /// * `display_rotation` — Screen rotation; use `Rotation::Deg270` for landscape.
148    /// * `display_tearing` — TE signal; use `TearingEffect::Off` unless you need vsync.
149    /// * `adc_cfg` — ADC1 attenuation for the battery pin.
150    /// * `i2s_tx_config` — Audio output format and sample rate.
151    /// * `buzzer_config` — Buzzer frequency and duty cycle.
152    ///
153    /// # Example
154    ///
155    /// ```rust,no_run
156    /// use lilkaoxide::prelude::*;
157    /// use lilkaoxide::{LilkaConfiguration, AdcCfg, I2sTxConfig};
158    /// use esp_hal::analog::adc::Attenuation;
159    /// use esp_hal::clock::CpuClock;
160    /// use fugit::RateExtU32;
161    ///
162    /// # #[cfg(feature = "blocking")]
163    /// # {
164    /// use mipidsi::options::{RefreshOrder, Rotation, TearingEffect};
165    ///
166    /// let config = LilkaConfiguration::new(
167    ///     80.MHz(),                     // spi_frequency
168    ///     CpuClock::default(),          // cpu_clock
169    ///     RefreshOrder::default(),      // display_refresh_order
170    ///     Rotation::Deg270,             // display_rotation
171    ///     TearingEffect::Off,           // display_tearing
172    ///     AdcCfg { attenuation: Attenuation::_11dB },
173    ///     I2sTxConfig::default(),
174    ///     BuzzerConfig::default(),
175    /// );
176    /// # }
177    /// ```
178    pub fn new(
179        spi_frequency: fugit::HertzU32,
180        cpu_clock: CpuClock,
181        display_refresh_order: RefreshOrder,
182        display_rotation: Rotation,
183        display_tearing: TearingEffect,
184        adc_cfg: AdcCfg,
185        i2s_tx_config: I2sTxConfig,
186        buzzer_config: BuzzerConfig,
187    ) -> Configuration {
188        Configuration {
189            cpu_clock,
190            spi_frequency,
191            display_refresh_order,
192            display_rotation,
193            display_tearing,
194            adc_cfg,
195            i2s_tx_config,
196            buzzer_config,
197        }
198    }
199}