use embassy_sync::once_lock::OnceLock;
use esp_hal::gpio::DriveMode;
use esp_hal::ledc::channel::{self, Channel, ChannelHW, ChannelIFace};
use esp_hal::ledc::timer::{self, LSClockSource, Timer, TimerIFace};
use esp_hal::ledc::{LSGlobalClkSource, Ledc, LowSpeed};
use esp_hal::peripherals::{GPIO38, LEDC};
use esp_hal::time::Rate;
pub const PWM_FREQUENCY: Rate = Rate::from_khz(2);
#[allow(missing_docs)]
pub struct BacklightResources<'d> {
pub ledc: LEDC<'d>,
pub pin: GPIO38<'d>,
}
reborrow_group!(BacklightResources { ledc, pin });
struct SharedTimer(Timer<'static, LowSpeed>);
unsafe impl Sync for SharedTimer {}
fn timer() -> &'static Timer<'static, LowSpeed> {
static TIMER: OnceLock<SharedTimer> = OnceLock::new();
&TIMER
.get_or_init(|| {
let mut ledc = Ledc::new(unsafe { LEDC::steal() });
ledc.set_global_slow_clock(LSGlobalClkSource::APBClk);
let mut t = ledc.timer::<LowSpeed>(timer::Number::Timer0);
t.configure(timer::config::Config {
duty: timer::config::Duty::Duty8Bit,
clock_source: LSClockSource::APBClk,
frequency: PWM_FREQUENCY,
})
.expect("2 kHz at 8 bits is reachable from APB");
SharedTimer(t)
})
.0
}
impl<'d> BacklightResources<'d> {
pub fn init(&mut self) -> Backlight<'_> {
let mut ch = Channel::<LowSpeed>::new(channel::Number::Channel0, self.pin.reborrow());
ch.configure(channel::config::Config {
timer: timer(),
duty_pct: 100,
drive_mode: DriveMode::PushPull,
})
.expect("valid LEDC channel configuration");
let mut backlight = Backlight {
ch,
level: Backlight::MAX,
};
backlight.set(Backlight::MAX);
backlight
}
}
pub struct Backlight<'d> {
ch: Channel<'d, LowSpeed>,
level: u8,
}
impl Backlight<'_> {
pub const MAX: u8 = u8::MAX;
pub fn set(&mut self, level: u8) {
let duty = if level == Self::MAX {
1 << 8
} else {
u32::from(level)
};
self.ch.set_duty_hw(duty);
self.level = level;
}
pub fn level(&self) -> u8 {
self.level
}
}