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}