LED matrix demo
This commit is contained in:
parent
40ce8dba2c
commit
0bab1d52a5
3 changed files with 127 additions and 14 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -766,6 +766,7 @@ dependencies = [
|
||||||
"heapless",
|
"heapless",
|
||||||
"mipidsi",
|
"mipidsi",
|
||||||
"panic-probe",
|
"panic-probe",
|
||||||
|
"rgb",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1083,6 +1084,9 @@ name = "rgb"
|
||||||
version = "0.8.52"
|
version = "0.8.52"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
|
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rp-pac"
|
name = "rp-pac"
|
||||||
|
|
|
||||||
|
|
@ -47,3 +47,4 @@ mipidsi = "0.9.0"
|
||||||
embedded-hal-bus = "0.3.0"
|
embedded-hal-bus = "0.3.0"
|
||||||
embassy-futures = "0.1.2"
|
embassy-futures = "0.1.2"
|
||||||
heapless = "0.8.0"
|
heapless = "0.8.0"
|
||||||
|
rgb = "0.8.52"
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,61 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
|
use core::convert::Infallible;
|
||||||
|
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_futures::select::select_array;
|
use embassy_futures::select::select_array;
|
||||||
|
use embassy_rp::bind_interrupts;
|
||||||
use embassy_rp::gpio::{Input, Level, Output, Pull};
|
use embassy_rp::gpio::{Input, Level, Output, Pull};
|
||||||
|
use embassy_rp::peripherals::PIO0;
|
||||||
|
use embassy_rp::pio::Pio;
|
||||||
|
use embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program};
|
||||||
use embassy_rp::pwm::{self, Pwm};
|
use embassy_rp::pwm::{self, Pwm};
|
||||||
use embassy_rp::rom_data::reset_to_usb_boot;
|
use embassy_rp::rom_data::reset_to_usb_boot;
|
||||||
use embassy_time::Delay;
|
use embassy_time::Delay;
|
||||||
|
use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};
|
||||||
use embedded_hal_bus::spi::ExclusiveDevice;
|
use embedded_hal_bus::spi::ExclusiveDevice;
|
||||||
use heapless::String;
|
use heapless::String;
|
||||||
use mipidsi::interface::SpiInterface;
|
use mipidsi::interface::SpiInterface;
|
||||||
use mipidsi::models::ST7789;
|
use mipidsi::models::ST7789;
|
||||||
use mipidsi::options::Orientation;
|
use mipidsi::options::Orientation;
|
||||||
|
use rgb::RGB8;
|
||||||
|
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
use embassy_rp::spi;
|
use embassy_rp::spi::{self, Blocking, Spi};
|
||||||
use embassy_rp::spi::{Blocking, Spi};
|
|
||||||
use embedded_graphics::image::{Image, ImageRawLE};
|
use embedded_graphics::image::{Image, ImageRawLE};
|
||||||
use embedded_graphics::mono_font::MonoTextStyle;
|
use embedded_graphics::mono_font::MonoTextStyle;
|
||||||
use embedded_graphics::mono_font::ascii::FONT_10X20;
|
use embedded_graphics::mono_font::ascii::FONT_10X20;
|
||||||
use embedded_graphics::pixelcolor::Rgb565;
|
use embedded_graphics::pixelcolor::{Rgb565, Rgb888};
|
||||||
use embedded_graphics::prelude::*;
|
use embedded_graphics::prelude::*;
|
||||||
use embedded_graphics::text::Text;
|
use embedded_graphics::text::Text;
|
||||||
|
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
PIO0_IRQ_0 => embassy_rp::pio::InterruptHandler<PIO0>;
|
||||||
|
});
|
||||||
|
|
||||||
const DISPLAY_FREQ: u32 = 64_000_000;
|
const DISPLAY_FREQ: u32 = 64_000_000;
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(spawner: Spawner) -> ! {
|
async fn main(spawner: Spawner) -> ! {
|
||||||
let p = embassy_rp::init(Default::default());
|
let p = embassy_rp::init(Default::default());
|
||||||
|
|
||||||
|
// Setup buttons
|
||||||
|
let _btn_a = Input::new(p.PIN_12, Pull::Up);
|
||||||
|
let _btn_b = Input::new(p.PIN_13, Pull::Up);
|
||||||
|
// let _btn_x = Input::new(p.PIN_14, Pull::Up);
|
||||||
|
let btn_y = Input::new(p.PIN_15, Pull::Up);
|
||||||
|
|
||||||
|
let mut btn_f1 = Input::new(p.PIN_11, Pull::Up);
|
||||||
|
let mut btn_f2 = Input::new(p.PIN_14, Pull::Up);
|
||||||
|
let mut btn_f3 = Input::new(p.PIN_9, Pull::Up);
|
||||||
|
let mut btn_f4 = Input::new(p.PIN_3, Pull::Up);
|
||||||
|
|
||||||
|
spawner.spawn(dfu_button(btn_y)).ok();
|
||||||
|
|
||||||
// display SPI
|
// display SPI
|
||||||
|
|
||||||
let mut display_buffer = [0u8; 512];
|
let mut display_buffer = [0u8; 512];
|
||||||
|
|
@ -87,16 +112,33 @@ async fn main(spawner: Spawner) -> ! {
|
||||||
b_pwm.set_config(&b_config);
|
b_pwm.set_config(&b_config);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Setup buttons
|
let mut led_pio = Pio::new(p.PIO0, Irqs);
|
||||||
let _btn_a = Input::new(p.PIN_12, Pull::Up);
|
let led_program = PioWs2812Program::new(&mut led_pio.common);
|
||||||
let _btn_b = Input::new(p.PIN_13, Pull::Up);
|
|
||||||
// let _btn_x = Input::new(p.PIN_14, Pull::Up);
|
|
||||||
let btn_y = Input::new(p.PIN_15, Pull::Up);
|
|
||||||
|
|
||||||
let mut btn_f1 = Input::new(p.PIN_11, Pull::Up);
|
let mut led_surface = LedMatrix::new(
|
||||||
let mut btn_f2 = Input::new(p.PIN_14, Pull::Up);
|
Serpentine {
|
||||||
let mut btn_f3 = Input::new(p.PIN_9, Pull::Up);
|
size: Size::new(40, 8),
|
||||||
let mut btn_f4 = Input::new(p.PIN_3, Pull::Up);
|
},
|
||||||
|
PioWs2812::new(
|
||||||
|
&mut led_pio.common,
|
||||||
|
led_pio.sm0,
|
||||||
|
p.DMA_CH0,
|
||||||
|
p.PIN_10,
|
||||||
|
&led_program,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
led_surface.clear(Rgb888::BLACK).ok();
|
||||||
|
Rectangle::new(Point::zero(), led_surface.size())
|
||||||
|
.into_styled(
|
||||||
|
PrimitiveStyleBuilder::new()
|
||||||
|
.stroke_color(Rgb888::RED)
|
||||||
|
.stroke_width(1)
|
||||||
|
.fill_color(Rgb888::CSS_DARK_BLUE)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.draw(&mut led_surface)
|
||||||
|
.ok();
|
||||||
|
led_surface.sync().await;
|
||||||
|
|
||||||
let mut render = |f_keys: [bool; 4]| {
|
let mut render = |f_keys: [bool; 4]| {
|
||||||
display.clear(Rgb565::BLACK).ok();
|
display.clear(Rgb565::BLACK).ok();
|
||||||
|
|
@ -115,8 +157,6 @@ async fn main(spawner: Spawner) -> ! {
|
||||||
|
|
||||||
let mut last_state = None;
|
let mut last_state = None;
|
||||||
|
|
||||||
spawner.spawn(dfu_button(btn_y)).ok();
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let state = [
|
let state = [
|
||||||
btn_f1.is_low(),
|
btn_f1.is_low(),
|
||||||
|
|
@ -144,3 +184,71 @@ async fn dfu_button(mut btn_y: Input<'static>) {
|
||||||
btn_y.wait_for_falling_edge().await;
|
btn_y.wait_for_falling_edge().await;
|
||||||
reset_to_usb_boot(0, 0);
|
reset_to_usb_boot(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Serpentine {
|
||||||
|
size: Size,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serpentine {
|
||||||
|
fn map(&self, mut p: Point) -> Option<usize> {
|
||||||
|
if p.x < 0 || p.x as u32 >= self.size.width || p.y < 0 || p.y as u32 >= self.size.height {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.y % 2 == 0 {
|
||||||
|
p.x = self.size.width as i32 - 1 - p.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(p.y as usize * self.size.width as usize + p.x as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LedMatrix<'d> {
|
||||||
|
buffer: [RGB8; 320],
|
||||||
|
layout: Serpentine,
|
||||||
|
pio: PioWs2812<'d, PIO0, 0, 320>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> LedMatrix<'d> {
|
||||||
|
fn new(layout: Serpentine, pio: PioWs2812<'d, PIO0, 0, 320>) -> Self {
|
||||||
|
Self {
|
||||||
|
buffer: [RGB8::default(); 320],
|
||||||
|
layout,
|
||||||
|
pio,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn sync(&mut self) {
|
||||||
|
self.pio.write(&self.buffer).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> DrawTarget for LedMatrix<'d> {
|
||||||
|
type Color = Rgb888;
|
||||||
|
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = Pixel<Self::Color>>,
|
||||||
|
{
|
||||||
|
for Pixel(point, color) in pixels {
|
||||||
|
if let Some(x) = self.layout.map(point).and_then(|i| self.buffer.get_mut(i)) {
|
||||||
|
// Map to 0-64 for brightness limiting
|
||||||
|
*x = RGB8::new(
|
||||||
|
color.r().div_ceil(4),
|
||||||
|
color.g().div_ceil(4),
|
||||||
|
color.b().div_ceil(4),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> OriginDimensions for LedMatrix<'d> {
|
||||||
|
fn size(&self) -> Size {
|
||||||
|
self.layout.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue