lcd driver
This commit is contained in:
Generated
+68
@@ -8,6 +8,12 @@ version = "1.5.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "az"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitfield"
|
name = "bitfield"
|
||||||
version = "0.19.5"
|
version = "0.19.5"
|
||||||
@@ -326,6 +332,29 @@ dependencies = [
|
|||||||
"nb 1.1.0",
|
"nb 1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-graphics"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e8da660bb0c829b34a56a965490597f82a55e767b91f9543be80ce8ccb416fe"
|
||||||
|
dependencies = [
|
||||||
|
"az",
|
||||||
|
"byteorder",
|
||||||
|
"embedded-graphics-core",
|
||||||
|
"float-cmp",
|
||||||
|
"micromath",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-graphics-core"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95743bef3ff70fcba3930246c4e6872882bbea0dcc6da2ca860112e0cd4bd09f"
|
||||||
|
dependencies = [
|
||||||
|
"az",
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embedded-hal"
|
name = "embedded-hal"
|
||||||
version = "0.2.7"
|
version = "0.2.7"
|
||||||
@@ -351,6 +380,16 @@ dependencies = [
|
|||||||
"embedded-hal 1.0.0",
|
"embedded-hal 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-hal-bus"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "513e0b3a8fb7d3013a8ae17a834283f170deaf7d0eeab0a7c1a36ad4dd356d22"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
"embedded-hal 1.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embedded-io"
|
name = "embedded-io"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
@@ -730,15 +769,27 @@ dependencies = [
|
|||||||
name = "espboard"
|
name = "espboard"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"embedded-graphics",
|
||||||
|
"embedded-hal-bus",
|
||||||
"esp-backtrace",
|
"esp-backtrace",
|
||||||
"esp-bootloader-esp-idf",
|
"esp-bootloader-esp-idf",
|
||||||
"esp-hal",
|
"esp-hal",
|
||||||
"esp-hal-smartled",
|
"esp-hal-smartled",
|
||||||
"esp-println",
|
"esp-println",
|
||||||
|
"mipidsi",
|
||||||
"smart-leds",
|
"smart-leds",
|
||||||
"smart-leds-trait",
|
"smart-leds-trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "float-cmp"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@@ -953,6 +1004,23 @@ version = "2.8.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "micromath"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mipidsi"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "790ebd28bd67addbccf41b1c0c188c26bb9f5bdcd91d4d6da9bd558e20d97a1d"
|
||||||
|
dependencies = [
|
||||||
|
"embedded-graphics-core",
|
||||||
|
"embedded-hal 1.0.0",
|
||||||
|
"heapless 0.8.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nb"
|
name = "nb"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
|||||||
@@ -4,11 +4,14 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
embedded-graphics = "0.8"
|
||||||
|
embedded-hal-bus = "0.3"
|
||||||
esp-backtrace = { version = "0.20.0", features = ["esp32c6", "println", "panic-handler"] }
|
esp-backtrace = { version = "0.20.0", features = ["esp32c6", "println", "panic-handler"] }
|
||||||
esp-bootloader-esp-idf = { version = "0.6.0", features = ["esp32c6"] }
|
esp-bootloader-esp-idf = { version = "0.6.0", features = ["esp32c6"] }
|
||||||
esp-hal = { version = "1.2.0", features = ["esp32c6", "unstable"] }
|
esp-hal = { version = "1.2.0", features = ["esp32c6", "unstable"] }
|
||||||
esp-hal-smartled = { version = "0.18", default-features = false, features = ["esp32c6"] }
|
esp-hal-smartled = { version = "0.18", default-features = false, features = ["esp32c6"] }
|
||||||
esp-println = { version = "0.18.0", default-features = false, features = ["esp32c6", "jtag-serial"] }
|
esp-println = { version = "0.18.0", default-features = false, features = ["esp32c6", "jtag-serial"] }
|
||||||
|
mipidsi = "0.10"
|
||||||
smart-leds = "0.4"
|
smart-leds = "0.4"
|
||||||
smart-leds-trait = "0.3"
|
smart-leds-trait = "0.3"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};
|
||||||
|
use esp_hal::delay::Delay;
|
||||||
|
use esp_hal::gpio::{Level, Output, OutputConfig};
|
||||||
|
use esp_hal::peripherals::{GPIO14, GPIO15, GPIO21, GPIO6, GPIO7, SPI2};
|
||||||
|
use esp_hal::spi::master::{Config as SpiConfig, Spi};
|
||||||
|
use esp_hal::spi::Mode;
|
||||||
|
use esp_hal::time::Rate;
|
||||||
|
use esp_hal::Blocking;
|
||||||
|
use mipidsi::interface::SpiInterface;
|
||||||
|
use mipidsi::models::ST7789;
|
||||||
|
use mipidsi::options::{ColorInversion, ColorOrder, Orientation, Rotation};
|
||||||
|
use mipidsi::{Builder, Display};
|
||||||
|
|
||||||
|
pub const WIDTH: u32 = 172;
|
||||||
|
pub const HEIGHT: u32 = 320;
|
||||||
|
|
||||||
|
const SPI_FREQ: Rate = Rate::from_mhz(40);
|
||||||
|
|
||||||
|
/// Column offset of the 172-px-wide panel within the ST7789's 240-px framebuffer.
|
||||||
|
const COLUMN_OFFSET: u16 = 34;
|
||||||
|
|
||||||
|
type Device = ExclusiveDevice<Spi<'static, Blocking>, Output<'static>, NoDelay>;
|
||||||
|
type Interface<'a> = SpiInterface<'a, Device, Output<'static>>;
|
||||||
|
|
||||||
|
/// The mipidsi [`Display`], which implements embedded-graphics' [`DrawTarget`].
|
||||||
|
///
|
||||||
|
/// The lifetime is the borrow of the pixel buffer passed to [`init`].
|
||||||
|
pub type Lcd<'a> = Display<Interface<'a>, ST7789, Output<'static>>;
|
||||||
|
|
||||||
|
pub fn init<'a>(
|
||||||
|
spi2: SPI2<'static>,
|
||||||
|
sck: GPIO7<'static>,
|
||||||
|
mosi: GPIO6<'static>,
|
||||||
|
dc: GPIO15<'static>,
|
||||||
|
cs: GPIO14<'static>,
|
||||||
|
rst: GPIO21<'static>,
|
||||||
|
buffer: &'a mut [u8],
|
||||||
|
) -> Lcd<'a> {
|
||||||
|
let spi = Spi::new(spi2, SpiConfig::default().with_frequency(SPI_FREQ).with_mode(Mode::_0))
|
||||||
|
.expect("Failed to configure SPI")
|
||||||
|
.with_sck(sck)
|
||||||
|
.with_mosi(mosi);
|
||||||
|
|
||||||
|
let cs_pin = Output::new(cs, Level::High, OutputConfig::default());
|
||||||
|
let device = ExclusiveDevice::new_no_delay(spi, cs_pin).expect("Failed to create SPI device");
|
||||||
|
|
||||||
|
let dc_pin = Output::new(dc, Level::Low, OutputConfig::default());
|
||||||
|
let interface = SpiInterface::new(device, dc_pin, buffer);
|
||||||
|
|
||||||
|
let mut rst_pin = Output::new(rst, Level::High, OutputConfig::default());
|
||||||
|
rst_pin.set_high();
|
||||||
|
|
||||||
|
let mut delay = Delay::new();
|
||||||
|
|
||||||
|
Builder::new(ST7789, interface)
|
||||||
|
.display_size(WIDTH as u16, HEIGHT as u16)
|
||||||
|
.display_offset(COLUMN_OFFSET, 0)
|
||||||
|
.orientation(Orientation::new().rotate(Rotation::Deg0))
|
||||||
|
.color_order(ColorOrder::Rgb)
|
||||||
|
.invert_colors(ColorInversion::Inverted)
|
||||||
|
.reset_pin(rst_pin)
|
||||||
|
.init(&mut delay)
|
||||||
|
.expect("Failed to init display")
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
use embedded_graphics::draw_target::DrawTarget;
|
||||||
|
use embedded_graphics::geometry::{OriginDimensions, Size};
|
||||||
|
use embedded_graphics::pixelcolor::{Rgb565, RgbColor};
|
||||||
|
use embedded_graphics::Pixel;
|
||||||
|
|
||||||
|
use crate::display::{HEIGHT, WIDTH};
|
||||||
|
|
||||||
|
const PIXEL_COUNT: usize = (WIDTH * HEIGHT) as usize;
|
||||||
|
|
||||||
|
/// The full-screen back buffer (172 × 320 × 2 bytes ≈ 110 KB).
|
||||||
|
static mut PIXELS: [Rgb565; PIXEL_COUNT] = [Rgb565::BLACK; PIXEL_COUNT];
|
||||||
|
|
||||||
|
/// A full-frame framebuffer that implements embedded-graphics' [`DrawTarget`].
|
||||||
|
///
|
||||||
|
/// Draw primitives into it, then blit the whole thing to the display once per
|
||||||
|
/// frame to avoid flicker. The backing memory is a `static`, so this is only
|
||||||
|
/// safe to use from a single thread (which is all this application has).
|
||||||
|
pub struct Framebuffer;
|
||||||
|
|
||||||
|
impl Framebuffer {
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
// SAFETY: only used from the single main loop.
|
||||||
|
unsafe { &mut *core::ptr::addr_of_mut!(PIXELS) }.fill(Rgb565::BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The raw pixels, ready to be sent to the display.
|
||||||
|
pub fn pixels(&self) -> &'static [Rgb565] {
|
||||||
|
// SAFETY: the buffer lives for the whole program and is only borrowed
|
||||||
|
// for the duration of the (blocking) blit.
|
||||||
|
unsafe { &*core::ptr::addr_of!(PIXELS) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrawTarget for Framebuffer {
|
||||||
|
type Color = Rgb565;
|
||||||
|
type Error = core::convert::Infallible;
|
||||||
|
|
||||||
|
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = Pixel<Self::Color>>,
|
||||||
|
{
|
||||||
|
// SAFETY: only used from the single main loop.
|
||||||
|
let buffer = unsafe { &mut *core::ptr::addr_of_mut!(PIXELS) };
|
||||||
|
|
||||||
|
for pixel in pixels {
|
||||||
|
let (x, y) = (pixel.0.x, pixel.0.y);
|
||||||
|
if x >= 0 && y >= 0 && x < WIDTH as i32 && y < HEIGHT as i32 {
|
||||||
|
buffer[(y as usize) * WIDTH as usize + (x as usize)] = pixel.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OriginDimensions for Framebuffer {
|
||||||
|
fn size(&self) -> Size {
|
||||||
|
Size::new(WIDTH, HEIGHT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Framebuffer {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
+74
-19
@@ -2,14 +2,23 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
mod backlight;
|
mod backlight;
|
||||||
|
mod display;
|
||||||
|
mod framebuffer;
|
||||||
mod led;
|
mod led;
|
||||||
|
|
||||||
|
use embedded_graphics::draw_target::DrawTarget;
|
||||||
|
use embedded_graphics::pixelcolor::Rgb565;
|
||||||
|
use embedded_graphics::prelude::*;
|
||||||
|
use embedded_graphics::primitives::{PrimitiveStyle, Rectangle, Triangle};
|
||||||
use esp_backtrace as _;
|
use esp_backtrace as _;
|
||||||
use esp_bootloader_esp_idf::esp_app_desc;
|
use esp_bootloader_esp_idf::esp_app_desc;
|
||||||
use esp_hal::{init, rmt::Rmt, time::Rate, Config};
|
use esp_hal::{init, rmt::Rmt, time::Rate, Config};
|
||||||
use esp_println::println;
|
use esp_println::println;
|
||||||
use smart_leds::hsv::{hsv2rgb, Hsv};
|
use smart_leds::hsv::{hsv2rgb, Hsv};
|
||||||
use smart_leds::{brightness, gamma};
|
use smart_leds::RGB8;
|
||||||
|
|
||||||
|
const SAT: u8 = 200;
|
||||||
|
const VAL: u8 = 200;
|
||||||
|
|
||||||
esp_app_desc!();
|
esp_app_desc!();
|
||||||
|
|
||||||
@@ -26,34 +35,80 @@ fn main() -> ! {
|
|||||||
backlight::setup_timer(peripherals.LEDC).expect("Failed to init backlight timer");
|
backlight::setup_timer(peripherals.LEDC).expect("Failed to init backlight timer");
|
||||||
let mut backlight =
|
let mut backlight =
|
||||||
backlight::LcdBacklight::new(&timer, peripherals.GPIO22).expect("Failed to init backlight");
|
backlight::LcdBacklight::new(&timer, peripherals.GPIO22).expect("Failed to init backlight");
|
||||||
|
backlight.set_level(0.5).unwrap();
|
||||||
|
|
||||||
|
let mut buffer = [0u8; 4096];
|
||||||
|
let mut lcd = display::init(
|
||||||
|
peripherals.SPI2,
|
||||||
|
peripherals.GPIO7,
|
||||||
|
peripherals.GPIO6,
|
||||||
|
peripherals.GPIO15,
|
||||||
|
peripherals.GPIO14,
|
||||||
|
peripherals.GPIO21,
|
||||||
|
&mut buffer,
|
||||||
|
);
|
||||||
|
|
||||||
let delay = esp_hal::delay::Delay::new();
|
let delay = esp_hal::delay::Delay::new();
|
||||||
|
|
||||||
let levels = [1.0f32, 0.75, 0.5, 0.25, 0.0, 0.5];
|
println!("LCD {}x{} initialized", display::WIDTH, display::HEIGHT);
|
||||||
let mut level_idx = 0;
|
lcd.clear(Rgb565::BLACK).unwrap();
|
||||||
|
|
||||||
|
let mut fb = framebuffer::Framebuffer;
|
||||||
|
let screen = Rectangle::new(Point::zero(), Size::new(display::WIDTH, display::HEIGHT));
|
||||||
|
|
||||||
|
let mut pts = [
|
||||||
|
Point::new(86, 20),
|
||||||
|
Point::new(61, 50),
|
||||||
|
Point::new(111, 50),
|
||||||
|
];
|
||||||
|
let mut vx: i32 = 3;
|
||||||
|
let mut vy: i32 = 2;
|
||||||
|
let mut frame = 0u32;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
for hue in 0..=255 {
|
let hue = (frame % 256) as u8;
|
||||||
let rgb = hsv2rgb(Hsv {
|
let lit = hsv2rgb(Hsv {
|
||||||
hue,
|
hue,
|
||||||
sat: 255,
|
sat: SAT,
|
||||||
val: 255,
|
val: VAL,
|
||||||
});
|
});
|
||||||
let lit = brightness(gamma(core::iter::once(rgb)), 40)
|
led.set_rgb(RGB8::new(0,0,0)).unwrap();
|
||||||
.next()
|
let accent = Rgb565::new(lit.r / 8, lit.g / 4, lit.b / 8);
|
||||||
|
|
||||||
|
for p in &mut pts {
|
||||||
|
p.x += vx;
|
||||||
|
p.y += vy;
|
||||||
|
}
|
||||||
|
|
||||||
|
let min_x = pts.iter().map(|p| p.x).min().unwrap();
|
||||||
|
let max_x = pts.iter().map(|p| p.x).max().unwrap();
|
||||||
|
let min_y = pts.iter().map(|p| p.y).min().unwrap();
|
||||||
|
let max_y = pts.iter().map(|p| p.y).max().unwrap();
|
||||||
|
|
||||||
|
if min_x <= 0 || max_x >= display::WIDTH as i32 {
|
||||||
|
vx = -vx;
|
||||||
|
}
|
||||||
|
if min_y <= 0 || max_y >= display::HEIGHT as i32 {
|
||||||
|
vy = -vy;
|
||||||
|
}
|
||||||
|
|
||||||
|
fb.clear();
|
||||||
|
Triangle::new(pts[0], pts[1], pts[2])
|
||||||
|
.into_styled(PrimitiveStyle::with_fill(accent))
|
||||||
|
.draw(&mut fb)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
led.set_rgb(lit).unwrap();
|
|
||||||
|
|
||||||
if hue % 32 == 0 {
|
lcd.fill_contiguous(&screen, fb.pixels().iter().cloned())
|
||||||
println!("hue {hue}: ({}, {}, {})", lit.r, lit.g, lit.b);
|
.unwrap();
|
||||||
|
|
||||||
|
if frame % 32 == 0 {
|
||||||
|
println!(
|
||||||
|
"frame {frame}: apex=({}, {}), v=({}, {}), hue={hue}",
|
||||||
|
pts[0].x, pts[0].y, vx, vy
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
delay.delay_millis(10);
|
frame = frame.wrapping_add(1);
|
||||||
}
|
//delay.delay_millis(30);
|
||||||
|
|
||||||
let level = levels[level_idx % levels.len()];
|
|
||||||
backlight.set_level(level).unwrap();
|
|
||||||
println!("backlight level: {level}");
|
|
||||||
level_idx += 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user