led and backlight

This commit is contained in:
2026-09-07 18:35:36 -05:00
commit 60e073a706
7 changed files with 1651 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
[build]
target = "riscv32imac-unknown-none-elf"
[target.riscv32imac-unknown-none-elf]
runner = "espflash flash --monitor"
rustflags = ["-C", "link-arg=-Tlinkall.x"]
+1
View File
@@ -0,0 +1 @@
/target
Generated
+1463
View File
File diff suppressed because it is too large Load Diff
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "espboard"
version = "0.1.0"
edition = "2024"
[dependencies]
esp-backtrace = { version = "0.20.0", features = ["esp32c6", "println", "panic-handler"] }
esp-bootloader-esp-idf = { version = "0.6.0", features = ["esp32c6"] }
esp-hal = { version = "1.2.0", features = ["esp32c6", "unstable"] }
esp-hal-smartled = { version = "0.18", default-features = false, features = ["esp32c6"] }
esp-println = { version = "0.18.0", default-features = false, features = ["esp32c6", "jtag-serial"] }
smart-leds = "0.4"
smart-leds-trait = "0.3"
[profile.dev]
codegen-units = 1
incremental = false
[profile.release]
codegen-units = 1
+50
View File
@@ -0,0 +1,50 @@
use esp_hal::gpio::interconnect::PeripheralOutput;
use esp_hal::gpio::DriveMode;
use esp_hal::ledc::channel::{self, Channel, ChannelIFace};
use esp_hal::ledc::timer::TimerIFace;
use esp_hal::ledc::LowSpeed;
/// Maximum duty cycle percentage. The board documentation warns against
/// running the display above 50% brightness, so `level = 1.0` maps to this.
const MAX_DUTY_PCT: u8 = 50;
#[derive(Debug)]
pub enum Error {
Channel(channel::Error),
}
impl From<channel::Error> for Error {
fn from(e: channel::Error) -> Self {
Error::Channel(e)
}
}
/// Backlight PWM driver. The channel holds a reference to the caller-owned
/// timer, so this type's lifetime is bounded by the timer's lifetime.
pub struct LcdBacklight<'a> {
channel: Channel<'a, LowSpeed>,
}
impl<'a> LcdBacklight<'a> {
pub fn new(
timer: &'a dyn TimerIFace<LowSpeed>,
pin: impl PeripheralOutput<'a>,
) -> Result<Self, Error> {
let mut channel = Channel::new(channel::Number::Channel0, pin);
channel.configure(channel::config::Config {
timer,
duty_pct: 0,
drive_mode: DriveMode::PushPull,
})?;
Ok(Self { channel })
}
/// Set the backlight level from `0.0` (off) to `1.0` (maximum).
///
/// `1.0` is scaled to a 50% duty cycle, the maximum brightness the board
/// documentation recommends.
pub fn set_level(&mut self, level: f32) -> Result<(), Error> {
let duty_pct = ((level.clamp(0.0, 1.0) * MAX_DUTY_PCT as f32) + 0.5) as u8;
self.channel.set_duty(duty_pct).map_err(Error::Channel)
}
}
+42
View File
@@ -0,0 +1,42 @@
use esp_hal::gpio::interconnect::PeripheralOutput;
use esp_hal::rmt::TxChannelCreator;
use esp_hal::time::Rate;
use esp_hal::Blocking;
use esp_hal_smartled::{RmtSmartLeds, WS2812_TIMING, buffer_size, color_order};
use smart_leds_trait::{RGB8, SmartLedsWrite};
const RMT_FREQ: Rate = Rate::from_mhz(80);
type SmartLed = RmtSmartLeds<'static, { buffer_size::<RGB8>(1) }, Blocking, RGB8, color_order::Grb>;
pub struct RgbLed {
led: SmartLed,
}
impl RgbLed {
pub fn new(
channel: impl TxChannelCreator<'static, Blocking>,
pin: impl PeripheralOutput<'static>,
) -> Result<Self, esp_hal_smartled::Error> {
Ok(Self {
led: SmartLed::new_with_memsize(WS2812_TIMING, channel, pin, 2, RMT_FREQ)?,
})
}
pub fn set_color(
&mut self,
r: u8,
g: u8,
b: u8,
) -> Result<(), esp_hal_smartled::AdapterError> {
self.led.write([RGB8 { r, g, b }])
}
pub fn set_rgb(&mut self, rgb: RGB8) -> Result<(), esp_hal_smartled::AdapterError> {
self.led.write([rgb])
}
pub fn off(&mut self) -> Result<(), esp_hal_smartled::AdapterError> {
self.set_color(0, 0, 0)
}
}
+69
View File
@@ -0,0 +1,69 @@
#![no_std]
#![no_main]
mod backlight;
mod led;
use esp_backtrace as _;
use esp_bootloader_esp_idf::esp_app_desc;
use esp_hal::ledc::timer::{self, TimerIFace};
use esp_hal::ledc::{LSGlobalClkSource, Ledc, LowSpeed};
use esp_hal::{init, rmt::Rmt, time::Rate, Config};
use esp_println::println;
use smart_leds::hsv::{hsv2rgb, Hsv};
use smart_leds::{brightness, gamma};
esp_app_desc!();
#[esp_hal::main]
fn main() -> ! {
let peripherals = init(Config::default());
let mut led = {
let rmt = Rmt::new(peripherals.RMT, Rate::from_mhz(80)).expect("Failed to init RMT");
led::RgbLed::new(rmt.channel0, peripherals.GPIO8).expect("Failed to init RGB LED")
};
let mut ledc = Ledc::new(peripherals.LEDC);
ledc.set_global_slow_clock(LSGlobalClkSource::APBClk);
let mut timer = ledc.timer::<LowSpeed>(timer::Number::Timer0);
timer
.configure(timer::config::Config {
duty: timer::config::Duty::Duty8Bit,
clock_source: timer::LSClockSource::APBClk,
frequency: Rate::from_khz(5),
})
.expect("Failed to configure backlight timer");
let mut backlight =
backlight::LcdBacklight::new(&timer, peripherals.GPIO22).expect("Failed to init backlight");
let delay = esp_hal::delay::Delay::new();
let levels = [1.0f32, 0.75, 0.5, 0.25, 0.0, 0.5];
let mut level_idx = 0;
loop {
for hue in 0..=255 {
let rgb = hsv2rgb(Hsv {
hue,
sat: 255,
val: 255,
});
let lit = brightness(gamma(core::iter::once(rgb)), 40)
.next()
.unwrap();
led.set_rgb(lit).unwrap();
if hue % 32 == 0 {
println!("hue {hue}: ({}, {}, {})", lit.r, lit.g, lit.b);
}
delay.delay_millis(10);
}
let level = levels[level_idx % levels.len()];
backlight.set_level(level).unwrap();
println!("backlight level: {level}");
level_idx += 1;
}
}