mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-30 22:01:13 +00:00
MCU: attempt to support the xpt2046 touch screen
Still not very well callibrated, but somehow works
This commit is contained in:
parent
a746cefb18
commit
d9622a9795
3 changed files with 190 additions and 15 deletions
|
@ -19,7 +19,7 @@ path = "lib.rs"
|
||||||
simulator = ["winit", "glutin", "femtovg", "embedded-graphics-simulator", "std", "imgref", "scoped-tls-hkt"]
|
simulator = ["winit", "glutin", "femtovg", "embedded-graphics-simulator", "std", "imgref", "scoped-tls-hkt"]
|
||||||
default = ["simulator"]
|
default = ["simulator"]
|
||||||
|
|
||||||
pico-st7789 = ["unsafe_single_core", "rp-pico", "embedded-hal", "cortex-m-rt", "alloc-cortex-m", "embedded-time", "cortex-m", "display-interface-spi", "st7789", "defmt", "defmt-rtt", "panic-probe", "sixtyfps-corelib/defmt" ]
|
pico-st7789 = ["unsafe_single_core", "rp-pico", "embedded-hal", "cortex-m-rt", "alloc-cortex-m", "embedded-time", "cortex-m", "display-interface-spi", "st7789", "defmt", "defmt-rtt", "panic-probe", "sixtyfps-corelib/defmt", "shared-bus" ]
|
||||||
|
|
||||||
unsafe_single_core = ["sixtyfps-corelib/unsafe_single_core"]
|
unsafe_single_core = ["sixtyfps-corelib/unsafe_single_core"]
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ embedded-time = { version = "0.12.0", optional = true }
|
||||||
cortex-m = { version = "0.7.2", optional = true }
|
cortex-m = { version = "0.7.2", optional = true }
|
||||||
display-interface-spi = { version = "0.4.1", optional = true }
|
display-interface-spi = { version = "0.4.1", optional = true }
|
||||||
st7789 = { version = "0.6.1", optional = true }
|
st7789 = { version = "0.6.1", optional = true }
|
||||||
|
shared-bus = { version = "0.2", optional = true }
|
||||||
|
|
||||||
defmt = { version = "0.3.0", optional = true }
|
defmt = { version = "0.3.0", optional = true }
|
||||||
defmt-rtt = { version = "0.3.0", optional = true }
|
defmt-rtt = { version = "0.3.0", optional = true }
|
||||||
|
|
|
@ -40,7 +40,13 @@ mod renderer;
|
||||||
pub trait Devices {
|
pub trait Devices {
|
||||||
fn screen_size(&self) -> IntSize;
|
fn screen_size(&self) -> IntSize;
|
||||||
fn fill_region(&mut self, region: IntRect, pixels: &[Rgb888]);
|
fn fill_region(&mut self, region: IntRect, pixels: &[Rgb888]);
|
||||||
|
fn read_touch_event(&mut self) -> Option<sixtyfps_corelib::input::MouseEvent> {
|
||||||
|
None
|
||||||
|
}
|
||||||
fn debug(&mut self, _: &str);
|
fn debug(&mut self, _: &str);
|
||||||
|
fn time(&mut self) -> core::time::Duration {
|
||||||
|
core::time::Duration::ZERO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: embedded_graphics::draw_target::DrawTarget> crate::Devices for T
|
impl<T: embedded_graphics::draw_target::DrawTarget> crate::Devices for T
|
||||||
|
@ -68,12 +74,10 @@ where
|
||||||
fn debug(&mut self, text: &str) {
|
fn debug(&mut self, text: &str) {
|
||||||
use embedded_graphics::{
|
use embedded_graphics::{
|
||||||
mono_font::{ascii::FONT_6X10, MonoTextStyle},
|
mono_font::{ascii::FONT_6X10, MonoTextStyle},
|
||||||
text::{Alignment, Text},
|
text::Text,
|
||||||
};
|
};
|
||||||
let style = MonoTextStyle::new(&FONT_6X10, Rgb888::RED.into());
|
let style = MonoTextStyle::new(&FONT_6X10, Rgb888::RED.into());
|
||||||
Text::with_alignment(text, Point::new(20, 30), style, Alignment::Center)
|
Text::new(text, Point::new(20, 30), style).draw(self).unwrap();
|
||||||
.draw(self)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +214,7 @@ mod the_backend {
|
||||||
DEVICES.with(|devices| {
|
DEVICES.with(|devices| {
|
||||||
let mut devices = devices.borrow_mut();
|
let mut devices = devices.borrow_mut();
|
||||||
let devices = devices.as_mut().unwrap();
|
let devices = devices.as_mut().unwrap();
|
||||||
let size = devices.screen_size();
|
let size = devices.screen_size().to_f32() / runtime_window.scale_factor();
|
||||||
runtime_window.set_window_item_geometry(size.width as _, size.height as _);
|
runtime_window.set_window_item_geometry(size.width as _, size.height as _);
|
||||||
let background =
|
let background =
|
||||||
crate::renderer::to_rgb888_color_discard_alpha(window.background_color.get());
|
crate::renderer::to_rgb888_color_discard_alpha(window.background_color.get());
|
||||||
|
@ -240,6 +244,7 @@ mod the_backend {
|
||||||
Some(McuEvent::Custom(e)) => e(),
|
Some(McuEvent::Custom(e)) => e(),
|
||||||
Some(McuEvent::Repaint) => {
|
Some(McuEvent::Repaint) => {
|
||||||
if let Some(window) = WINDOWS.with(|x| x.borrow().clone()) {
|
if let Some(window) = WINDOWS.with(|x| x.borrow().clone()) {
|
||||||
|
sixtyfps_corelib::animations::update_animations();
|
||||||
self.draw(window)
|
self.draw(window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,6 +252,26 @@ mod the_backend {
|
||||||
// TODO: sleep();
|
// TODO: sleep();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DEVICES.with(|devices| {
|
||||||
|
let e = devices.borrow_mut().as_mut().unwrap().read_touch_event();
|
||||||
|
//devices.borrow_mut().as_mut().unwrap().debug(&alloc::format!("EVENT: {:?}", e));
|
||||||
|
if let Some(mut event) = e {
|
||||||
|
if let Some(window) = WINDOWS.with(|x| x.borrow().clone()) {
|
||||||
|
let w = window.self_weak.upgrade().unwrap();
|
||||||
|
// scale the event by the scale factor:
|
||||||
|
if let Some(p) = event.pos() {
|
||||||
|
event.translate(p / w.scale_factor() - p);
|
||||||
|
}
|
||||||
|
w.process_mouse_input(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let t = devices.borrow_mut().as_mut().unwrap().time().as_secs();
|
||||||
|
devices.borrow_mut().as_mut().unwrap().debug(&alloc::format!("{}", t));
|
||||||
|
});
|
||||||
|
sixtyfps_corelib::animations::update_animations();
|
||||||
|
if let Some(window) = WINDOWS.with(|x| x.borrow().clone()) {
|
||||||
|
self.draw(window)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,6 +323,10 @@ mod the_backend {
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn duration_since_start(&'static self) -> core::time::Duration {
|
||||||
|
DEVICES.with(|devices| devices.borrow_mut().as_mut().unwrap().time())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,7 +343,7 @@ pub fn init_simulator() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_with_display<Display: Devices + 'static>(mut display: Display) {
|
pub fn init_with_display<Display: Devices + 'static>(display: Display) {
|
||||||
DEVICES.with(|d| *d.borrow_mut() = Some(Box::new(display)));
|
DEVICES.with(|d| *d.borrow_mut() = Some(Box::new(display)));
|
||||||
sixtyfps_corelib::backend::instance_or_init(|| {
|
sixtyfps_corelib::backend::instance_or_init(|| {
|
||||||
alloc::boxed::Box::new(the_backend::MCUBackend::default())
|
alloc::boxed::Box::new(the_backend::MCUBackend::default())
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
// Copyright © SixtyFPS GmbH <info@sixtyfps.io>
|
// Copyright © SixtyFPS GmbH <info@sixtyfps.io>
|
||||||
// SPDX-License-Identifier: (GPL-3.0-only OR LicenseRef-SixtyFPS-commercial)
|
// SPDX-License-Identifier: (GPL-3.0-only OR LicenseRef-SixtyFPS-commercial)
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
pub use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::blocking::spi::Transfer;
|
||||||
|
use embedded_hal::digital::v2::{InputPin, OutputPin};
|
||||||
use embedded_time::rate::*;
|
use embedded_time::rate::*;
|
||||||
use rp_pico::hal;
|
use rp_pico::hal;
|
||||||
use rp_pico::hal::pac;
|
use rp_pico::hal::pac;
|
||||||
use rp_pico::hal::prelude::*;
|
use rp_pico::hal::prelude::*;
|
||||||
|
|
||||||
use embedded_hal::digital::v2::OutputPin;
|
|
||||||
|
|
||||||
pub use cortex_m_rt::entry;
|
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
use defmt_rtt as _; // global logger
|
||||||
use panic_probe as _;
|
use panic_probe as _;
|
||||||
|
|
||||||
|
@ -18,6 +20,9 @@ fn oom(_: core::alloc::Layout) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
use alloc_cortex_m::CortexMHeap;
|
use alloc_cortex_m::CortexMHeap;
|
||||||
|
use rp_pico::hal::rtc::{DateTime, RealTimeClock};
|
||||||
|
|
||||||
|
use crate::Devices;
|
||||||
|
|
||||||
const HEAP_SIZE: usize = 128 * 1024;
|
const HEAP_SIZE: usize = 128 * 1024;
|
||||||
static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE];
|
static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE];
|
||||||
|
@ -45,6 +50,8 @@ pub fn init_board() {
|
||||||
|
|
||||||
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().integer());
|
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().integer());
|
||||||
|
|
||||||
|
unsafe { ALLOCATOR.init(&mut HEAP as *const u8 as usize, core::mem::size_of_val(&HEAP)) }
|
||||||
|
|
||||||
let sio = hal::sio::Sio::new(pac.SIO);
|
let sio = hal::sio::Sio::new(pac.SIO);
|
||||||
|
|
||||||
let pins = rp_pico::Pins::new(pac.IO_BANK0, pac.PADS_BANK0, sio.gpio_bank0, &mut pac.RESETS);
|
let pins = rp_pico::Pins::new(pac.IO_BANK0, pac.PADS_BANK0, sio.gpio_bank0, &mut pac.RESETS);
|
||||||
|
@ -61,12 +68,14 @@ pub fn init_board() {
|
||||||
4_000_000u32.Hz(),
|
4_000_000u32.Hz(),
|
||||||
&embedded_hal::spi::MODE_3,
|
&embedded_hal::spi::MODE_3,
|
||||||
);
|
);
|
||||||
|
// FIXME: a cleaner way to get a static reference, or be able to use non-static backend
|
||||||
|
let spi = Box::leak(Box::new(shared_bus::BusManagerSimple::new(spi)));
|
||||||
|
|
||||||
let rst = pins.gpio15.into_push_pull_output();
|
let rst = pins.gpio15.into_push_pull_output();
|
||||||
|
|
||||||
let dc = pins.gpio8.into_push_pull_output();
|
let dc = pins.gpio8.into_push_pull_output();
|
||||||
let cs = pins.gpio9.into_push_pull_output();
|
let cs = pins.gpio9.into_push_pull_output();
|
||||||
let di = display_interface_spi::SPIInterface::new(spi, dc, cs);
|
let di = display_interface_spi::SPIInterface::new(spi.acquire_spi(), dc, cs);
|
||||||
|
|
||||||
let mut display = st7789::ST7789::new(di, rst, 320, 240);
|
let mut display = st7789::ST7789::new(di, rst, 320, 240);
|
||||||
|
|
||||||
|
@ -78,9 +87,145 @@ pub fn init_board() {
|
||||||
bl.set_high().unwrap();
|
bl.set_high().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe { ALLOCATOR.init(&mut HEAP as *const u8 as usize, core::mem::size_of_val(&HEAP)) }
|
|
||||||
|
|
||||||
display.init(&mut delay).unwrap();
|
display.init(&mut delay).unwrap();
|
||||||
display.set_orientation(st7789::Orientation::Landscape).unwrap();
|
display.set_orientation(st7789::Orientation::Landscape).unwrap();
|
||||||
crate::init_with_display(display);
|
|
||||||
|
let touch = xpt2046::XPT2046::new(
|
||||||
|
pins.gpio17.into_pull_down_input(),
|
||||||
|
pins.gpio16.into_push_pull_output(),
|
||||||
|
spi.acquire_spi(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let clock = RealTimeClock::new(
|
||||||
|
pac.RTC,
|
||||||
|
clocks.rtc_clock,
|
||||||
|
&mut pac.RESETS,
|
||||||
|
DateTime {
|
||||||
|
year: 1970,
|
||||||
|
month: 1,
|
||||||
|
day: 1,
|
||||||
|
day_of_week: hal::rtc::DayOfWeek::Thursday,
|
||||||
|
hour: 0,
|
||||||
|
minute: 0,
|
||||||
|
second: 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
crate::init_with_display(PicoDevices { display, touch, last_touch: Default::default(), clock });
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PicoDevices<Display, Touch> {
|
||||||
|
display: Display,
|
||||||
|
touch: Touch,
|
||||||
|
last_touch: Option<sixtyfps_corelib::graphics::Point>,
|
||||||
|
clock: RealTimeClock,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Display: Devices, IRQ: InputPin, CS: OutputPin<Error = IRQ::Error>, SPI: Transfer<u8>> Devices
|
||||||
|
for PicoDevices<Display, xpt2046::XPT2046<IRQ, CS, SPI>>
|
||||||
|
{
|
||||||
|
fn screen_size(&self) -> sixtyfps_corelib::graphics::IntSize {
|
||||||
|
self.display.screen_size()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_region(
|
||||||
|
&mut self,
|
||||||
|
region: sixtyfps_corelib::graphics::IntRect,
|
||||||
|
pixels: &[embedded_graphics::pixelcolor::Rgb888],
|
||||||
|
) {
|
||||||
|
self.display.fill_region(region, pixels)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug(&mut self, text: &str) {
|
||||||
|
self.display.debug(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_touch_event(&mut self) -> Option<sixtyfps_corelib::input::MouseEvent> {
|
||||||
|
let button = sixtyfps_corelib::items::PointerEventButton::left;
|
||||||
|
self.touch
|
||||||
|
.read()
|
||||||
|
.map_err(|_| ())
|
||||||
|
.unwrap()
|
||||||
|
.map(|point| {
|
||||||
|
let point = point.to_f32() / (i16::MAX as f32);
|
||||||
|
let size = self.display.screen_size().to_f32();
|
||||||
|
let pos = euclid::point2(point.x * size.width, point.y * size.height);
|
||||||
|
match self.last_touch.replace(pos) {
|
||||||
|
Some(_) => sixtyfps_corelib::input::MouseEvent::MouseMoved { pos },
|
||||||
|
None => sixtyfps_corelib::input::MouseEvent::MousePressed { pos, button },
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
self.last_touch
|
||||||
|
.take()
|
||||||
|
.map(|pos| sixtyfps_corelib::input::MouseEvent::MouseReleased { pos, button })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn time(&mut self) -> core::time::Duration {
|
||||||
|
let time = self.clock.now();
|
||||||
|
match time {
|
||||||
|
// FIXME! milisecond
|
||||||
|
Ok(t) => core::time::Duration::from_secs(t.second as u64 + t.minute as u64 * 60),
|
||||||
|
Err(_) => core::time::Duration::ZERO,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod xpt2046 {
|
||||||
|
use embedded_hal::blocking::spi::Transfer;
|
||||||
|
use embedded_hal::digital::v2::{InputPin, OutputPin};
|
||||||
|
use euclid::default::Point2D;
|
||||||
|
|
||||||
|
pub struct XPT2046<IRQ: InputPin, CS: OutputPin, SPI: Transfer<u8>> {
|
||||||
|
irq: IRQ,
|
||||||
|
cs: CS,
|
||||||
|
spi: SPI,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PinE, IRQ: InputPin<Error = PinE>, CS: OutputPin<Error = PinE>, SPI: Transfer<u8>>
|
||||||
|
XPT2046<IRQ, CS, SPI>
|
||||||
|
{
|
||||||
|
pub fn new(irq: IRQ, mut cs: CS, spi: SPI) -> Result<Self, PinE> {
|
||||||
|
cs.set_high()?;
|
||||||
|
Ok(Self { irq, cs, spi })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self) -> Result<Option<Point2D<i16>>, Error<PinE, SPI::Error>> {
|
||||||
|
if self.irq.is_low().map_err(|e| Error::Pin(e))? {
|
||||||
|
self.cs.set_low().map_err(|e| Error::Pin(e))?;
|
||||||
|
const CMD_X_READ: u8 = 0b10010000;
|
||||||
|
const CMD_Y_READ: u8 = 0b11010000;
|
||||||
|
|
||||||
|
macro_rules! xchg {
|
||||||
|
($byte:expr) => {
|
||||||
|
match self.spi.transfer(&mut [$byte]).map_err(|e| Error::Transfer(e))? {
|
||||||
|
[x] => *x as i16,
|
||||||
|
_ => return Err(Error::InternalError),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
xchg!(CMD_X_READ);
|
||||||
|
let mut x = xchg!(0) << 8;
|
||||||
|
x += xchg!(CMD_Y_READ);
|
||||||
|
let mut y = xchg!(0) << 8;
|
||||||
|
y += xchg!(0);
|
||||||
|
|
||||||
|
self.cs.set_high().map_err(|e| Error::Pin(e))?;
|
||||||
|
|
||||||
|
Ok(Some(Point2D::new(i16::MAX - x, y)))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Error<PinE, TransferE> {
|
||||||
|
Pin(PinE),
|
||||||
|
Transfer(TransferE),
|
||||||
|
InternalError,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue