Skip to content
Snippets Groups Projects
Commit b7f919cd authored by Stephen D's avatar Stephen D
Browse files

buffered display that uses way too much RAM

parent 569015f4
No related branches found
No related tags found
No related merge requests found
// Uses too much RAM to be useful ):
// 320 * 240 * 2 * 2 bytes/pixel = 307KB of RAM
use core::convert::Infallible;
use embedded_graphics::{
draw_target::DrawTarget,
geometry::{Dimensions, Point},
pixelcolor::PixelColor,
Pixel,
};
pub struct BufferedDisplay<const L: usize, C: 'static, E, DT: DrawTarget<Color = C, Error = E>> {
target: DT,
width: usize,
height: usize,
last: &'static mut [C; L],
cur: &'static mut [C; L],
}
impl<const L: usize, C: PixelColor, E, DT: DrawTarget<Color = C, Error = E>>
BufferedDisplay<L, C, E, DT>
{
pub fn new(
mut target: DT,
color: C,
last: &'static mut [C; L],
cur: &'static mut [C; L],
) -> Result<Self, E> {
let size = target.bounding_box().size;
let width = size.width.try_into().unwrap();
let height = size.height.try_into().unwrap();
assert_eq!(L, width * height);
target.fill_solid(&target.bounding_box(), color)?;
Ok(Self {
target,
width,
height,
last,
cur,
})
}
pub fn flush(&mut self) -> Result<(), E> {
let iter = BufferedDisplayIter::new(self.width, self.height, self.last, self.cur);
self.target.draw_iter(iter)?;
Ok(())
}
#[inline(always)]
fn index<X: TryInto<usize>, Y: TryInto<usize>>(&self, x: X, y: Y) -> Option<usize> {
let x: usize = x.try_into().ok()?;
let y: usize = y.try_into().ok()?;
if y >= self.height || x >= self.width {
return None;
}
Some(x + y * self.width)
}
}
impl<const L: usize, C: PixelColor, E, DT: DrawTarget<Color = C, Error = E>> Dimensions
for BufferedDisplay<L, C, E, DT>
{
fn bounding_box(&self) -> embedded_graphics::primitives::Rectangle {
self.target.bounding_box()
}
}
impl<const L: usize, C: PixelColor, E, DT: DrawTarget<Color = C, Error = E>> DrawTarget
for BufferedDisplay<L, C, E, DT>
{
type Color = C;
type Error = Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = embedded_graphics::prelude::Pixel<Self::Color>>,
{
for Pixel(p, c) in pixels {
if let Some(idx) = self.index(p.x, p.y) {
self.cur[idx] = c;
}
}
Ok(())
}
}
struct BufferedDisplayIter<'a, C: PixelColor> {
width: usize,
height: usize,
last: &'a mut [C],
cur: &'a mut [C],
i: usize,
}
impl<'a, C: PixelColor> BufferedDisplayIter<'a, C> {
fn new(width: usize, height: usize, last: &'a mut [C], cur: &'a mut [C]) -> Self {
Self {
width,
height,
last,
cur,
i: 0,
}
}
#[inline(always)]
fn get_xy(&self, idx: usize) -> (i32, i32) {
let y = (idx / self.width).try_into().unwrap();
let x = (idx % self.width).try_into().unwrap();
(x, y)
}
}
impl<'a, C: PixelColor> Iterator for BufferedDisplayIter<'a, C> {
type Item = Pixel<C>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.i >= self.cur.len() {
return None;
}
let c = self.cur[self.i];
if self.last[self.i] != c {
self.last[self.i] = c;
let (x, y) = self.get_xy(self.i);
return Some(Pixel(Point::new(x, y), c));
}
self.i += 1;
}
}
}
......@@ -37,7 +37,7 @@ const KEYMAP: [[char; 8]; 6] = [
],
];
enum KeyCode {
pub enum KeyCode {
CallStart,
Menu,
Touchpad,
......@@ -110,6 +110,7 @@ pub struct Keyboard<
row_ser: RSER,
row_ld: RLD,
last_state: u64,
cur_key: Option<KeyCode>,
}
impl<
......@@ -142,6 +143,7 @@ impl<
row_ld,
last_state: 0,
cur_key: None,
}
}
......@@ -175,7 +177,7 @@ impl<
is_alt_pressed(state),
is_shift_pressed(state),
);
rprint!("{}", keycode);
self.cur_key = Some(keycode);
}
}
}
......@@ -185,6 +187,10 @@ impl<
state
}
pub fn pushed_key(&mut self) -> Option<KeyCode> {
self.cur_key.take()
}
// 1s in val are low
// 0s are high
// indexes from furthest pin (QH on second shift register)
......
#![no_std]
#![no_main]
mod buffered_display;
mod keyboard;
mod touchpad;
......@@ -15,16 +16,18 @@ pub static BOOT_LOADER: [u8; 256] = rp2040_boot2::BOOT_LOADER_GD25Q64CS;
dispatchers = [TIMER_IRQ_1, TIMER_IRQ_2]
)]
mod app {
use crate::{keyboard::Keyboard, touchpad::Touchpad};
use core::fmt::Write;
use cortex_m::delay::Delay;
use crate::{
buffered_display::BufferedDisplay,
keyboard::{KeyCode, Keyboard},
touchpad::Touchpad,
};
use cortex_m::{delay::Delay, singleton};
use display_interface_spi::SPIInterface;
use embedded_graphics::{
draw_target::DrawTarget,
mono_font::{ascii::FONT_10X20, MonoTextStyleBuilder},
pixelcolor::{Rgb565, RgbColor},
prelude::{Point, Primitive},
primitives::{Circle, PrimitiveStyle, Triangle},
prelude::Point,
text::{Baseline, Text},
Drawable,
};
......@@ -41,16 +44,18 @@ mod app {
},
pac::{I2C1, SPI1},
pwm::{self, Channel, FreeRunning, Pwm4, Pwm5, Slice},
reset, spi,
spi,
timer::{monotonic::Monotonic, Timer},
Clock, I2C,
};
use heapless::String;
use mipidsi::{models::ST7789, Builder};
use mipidsi::{models::ST7789, Builder, Orientation};
use rp2040_hal::{self as hal, timer::Alarm0};
use rtic_monotonics::rp2040::*;
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
const WIDTH: usize = 320;
const HEIGHT: usize = 240;
type KeyboardBl = Channel<Slice<Pwm4, FreeRunning>, pwm::B>;
......@@ -99,7 +104,9 @@ mod app {
type MyMono = Monotonic<Alarm0>;
#[shared]
struct Shared {}
struct Shared {
text: String<1024>,
}
#[local]
struct Local {
......@@ -179,6 +186,7 @@ mod app {
let mut display_bl = pwm.channel_a;
display_bl.output_to(pins.gpio10);
display_bl.set_inverted();
display_bl.set_duty(display_bl.get_max_duty());
let tp_reset = pins.gpio3.into_push_pull_output();
let mut disp_reset = pins.gpio4.into_push_pull_output_in_state(PinState::Low);
......@@ -236,7 +244,8 @@ mod app {
let spi_cs = SPIInterface::new(spi, display_dc, display_cs);
let display = Builder::st7789(spi_cs)
.with_display_size(320, 240)
.with_display_size(HEIGHT.try_into().unwrap(), WIDTH.try_into().unwrap())
.with_orientation(Orientation::LandscapeInverted(true))
.init(&mut delay, None)
.unwrap();
......@@ -246,7 +255,9 @@ mod app {
let mono = Monotonic::new(timer, timer.alarm_0().unwrap());
(
Shared {},
Shared {
text: String::new(),
},
Local {
keyboard_bl,
touchpad,
......@@ -261,7 +272,7 @@ mod app {
)
}
#[task(priority = 2, local = [display_bl, keyboard_bl, touchpad, keyboard])]
#[task(priority = 2, local = [display_bl, keyboard_bl, touchpad, keyboard], shared = [text])]
fn keyboard_update(ctx: keyboard_update::Context) {
//while usb_dev.poll(&mut [&mut serial]) {}
......@@ -275,13 +286,30 @@ mod app {
let max_duty = keyboard_bl.get_max_duty();
let mut cur_duty = display_bl.get_duty();
let mut buf: String<64> = String::new();
let state = keyboard.read_state();
if state > 0 {
write!(&mut buf, "KB: {state:x}\r\n").unwrap();
//serial.write(&buf.as_bytes());
keyboard_bl.set_duty(max_duty);
if let Some(k) = keyboard.pushed_key() {
let mut text = ctx.shared.text;
match k {
KeyCode::Backspace => {
text.lock(|f| f.pop());
}
KeyCode::Char(c) => {
text.lock(|f| {
let _ = f.push(c);
});
}
KeyCode::Enter => {
text.lock(|f| {
let _ = f.push_str("\r\n");
});
}
_ => {}
}
}
} else {
keyboard_bl.set_duty(0);
}
......@@ -303,37 +331,34 @@ mod app {
keyboard_update::spawn_after(5.millis()).unwrap();
}
#[task(local = [display, r, g, b])]
fn display_update(ctx: display_update::Context) {
#[task(local = [display, r, g, b], shared = [text])]
fn display_update(mut ctx: display_update::Context) {
let display_update::LocalResources { display, r, g, b } = ctx.local;
*r += 1;
*g += 3;
*b += 5;
*r %= 255;
*g %= 255;
*b %= 255;
/*
*r += 1;
*g += 3;
*b += 5;
*r %= 255;
*g %= 255;
*b %= 255;
*/
display.clear(Rgb565::new(*r, *g, *b)).unwrap();
// Draw a smiley face with white eyes and a red mouth
draw_smiley(display).unwrap();
// text!
let text_style = MonoTextStyleBuilder::new()
.font(&FONT_10X20)
.text_color(Rgb565::YELLOW)
.build();
Text::with_baseline(
"Hello world!",
Point::new(16, 128),
text_style,
Baseline::Top,
)
.draw(display)
.unwrap();
ctx.shared.text.lock(|text| {
let _ = text.push('_');
Text::with_baseline(text, Point::new(8, 8), text_style, Baseline::Top)
.draw(display)
.unwrap();
text.pop();
});
display_update::spawn_after(100.millis()).unwrap();
display_update::spawn_after(10.millis()).unwrap();
}
#[idle]
......@@ -342,36 +367,4 @@ mod app {
cortex_m::asm::wfi();
}
}
fn draw_smiley<T: DrawTarget<Color = Rgb565>>(display: &mut T) -> Result<(), T::Error> {
// Draw the left eye as a circle located at (50, 100), with a diameter of 40, filled with white
Circle::new(Point::new(50, 100), 40)
.into_styled(PrimitiveStyle::with_fill(Rgb565::WHITE))
.draw(display)?;
// Draw the right eye as a circle located at (50, 200), with a diameter of 40, filled with white
Circle::new(Point::new(50, 200), 40)
.into_styled(PrimitiveStyle::with_fill(Rgb565::WHITE))
.draw(display)?;
// Draw an upside down red triangle to represent a smiling mouth
Triangle::new(
Point::new(130, 140),
Point::new(130, 200),
Point::new(160, 170),
)
.into_styled(PrimitiveStyle::with_fill(Rgb565::RED))
.draw(display)?;
// Cover the top part of the mouth with a black triangle so it looks closed instead of open
Triangle::new(
Point::new(130, 150),
Point::new(130, 190),
Point::new(150, 170),
)
.into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK))
.draw(display)?;
Ok(())
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment