diff --git a/eepy-example-app/build.sh b/eepy-example-app/build.sh index b4cbe8a..235df35 100755 --- a/eepy-example-app/build.sh +++ b/eepy-example-app/build.sh @@ -7,7 +7,7 @@ mkdir out/bins for slot in `seq -f "%02g" 31` do EPD_PROG_SLOT=$slot cargo build --release - elf2epb -i ../target/thumbv6m-none-eabi/release/eepy-example-app -o "out/bins/fw16-epd-example-app.s$slot.epb" + elf2epb -i ../target/thumbv6m-none-eabi/release/eepy-example-app -o "out/bins/eepy-example-app.s$slot.epb" done (cd out/bins && tar --zstd -cf ../eepy-example-app.epa *) \ No newline at end of file diff --git a/eepy-example-app/src/main.rs b/eepy-example-app/src/main.rs index d32d7ae..24f5b6a 100644 --- a/eepy-example-app/src/main.rs +++ b/eepy-example-app/src/main.rs @@ -3,6 +3,7 @@ extern crate panic_halt; +use core::arch::asm; use core::fmt::Write; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::Drawable; @@ -13,12 +14,9 @@ use heapless::String; use eepy_gui::draw_target::EpdDrawTarget; use eepy_gui::element::button::Button; use eepy_gui::element::{Gui, DEFAULT_TEXT_STYLE}; -use eepy_sys::{ProgramFunctionTable, RefreshBlockMode, SafeOption}; use eepy_sys::header::ProgramSlotHeader; - -extern "C" { - static _end: *const u8; -} +use eepy_sys::image::RefreshBlockMode; +use eepy_sys::input::{has_event, next_event, set_touch_enabled}; #[link_section = ".header"] #[used] @@ -28,15 +26,11 @@ static HEADER: ProgramSlotHeader = ProgramSlotHeader::partial( entry, ); -#[used] -static mut FOO: u32 = 10; - #[no_mangle] -pub extern "C" fn entry(pft: &ProgramFunctionTable) { +pub extern "C" fn entry() { + set_touch_enabled(true); - unsafe { (pft.set_touch_enabled)(true) }; - - let mut draw_target = EpdDrawTarget::new(pft.write_image, pft.refresh); + let mut draw_target = EpdDrawTarget::new(); let mut button = Button::with_default_style_auto_sized(Point::new(10, 40), "Click me", true); button.draw_init(&mut draw_target); @@ -45,7 +39,7 @@ pub extern "C" fn entry(pft: &ProgramFunctionTable) { let mut counter = 0; loop { - while let SafeOption::Some(ev) = (pft.next_event)() { + while let Some(ev) = next_event() { let mut needs_refresh = false; let response = button.tick(&mut draw_target, ev); @@ -67,5 +61,9 @@ pub extern "C" fn entry(pft: &ProgramFunctionTable) { draw_target.refresh(true, RefreshBlockMode::NonBlocking); } } + + if !has_event() { + unsafe { asm!("wfe", "wfe") }; + } } } \ No newline at end of file diff --git a/eepy-gui/src/draw_target.rs b/eepy-gui/src/draw_target.rs index 2ccad23..bdc00c1 100644 --- a/eepy-gui/src/draw_target.rs +++ b/eepy-gui/src/draw_target.rs @@ -4,12 +4,10 @@ use embedded_graphics::geometry::Dimensions; use embedded_graphics::Pixel; use embedded_graphics::pixelcolor::BinaryColor; use embedded_graphics::primitives::Rectangle; -use eepy_sys::RefreshBlockMode; +use eepy_sys::image::{refresh, write_image, RefreshBlockMode}; use tp370pgh01::{DIM_X, DIM_Y, IMAGE_BYTES}; pub struct EpdDrawTarget { - write_image: extern "C" fn(&[u8; IMAGE_BYTES]), - refresh: extern "C" fn(bool, RefreshBlockMode), buf: [u8; IMAGE_BYTES], } @@ -56,16 +54,14 @@ impl DrawTarget for EpdDrawTarget { } impl EpdDrawTarget { - pub const fn new(write_image: extern "C" fn(&[u8; IMAGE_BYTES]), refresh: extern "C" fn(bool, RefreshBlockMode)) -> Self { + pub const fn new() -> Self { Self { - write_image, - refresh, buf: [0; IMAGE_BYTES] } } pub fn refresh(&self, fast_refresh: bool, block_mode: RefreshBlockMode) { - (self.write_image)(&self.buf); - (self.refresh)(fast_refresh, block_mode); + write_image(&self.buf); + refresh(fast_refresh, block_mode); } } \ No newline at end of file diff --git a/eepy-gui/src/element/button.rs b/eepy-gui/src/element/button.rs index ff85d15..7decddd 100644 --- a/eepy-gui/src/element/button.rs +++ b/eepy-gui/src/element/button.rs @@ -4,7 +4,7 @@ use embedded_graphics::pixelcolor::BinaryColor; use embedded_graphics::primitives::{CornerRadii, PrimitiveStyle, Rectangle, RoundedRectangle}; use embedded_graphics::text::{Alignment, Baseline, Text, TextStyle, TextStyleBuilder}; use embedded_graphics::text::renderer::TextRenderer; -use eepy_sys::{Event, TouchEventType}; +use eepy_sys::input::{Event, TouchEventType}; use crate::draw_target::EpdDrawTarget; use crate::element::{Gui, DEFAULT_PRIMITIVE_STYLE, DEFAULT_TEXT_STYLE}; diff --git a/eepy-gui/src/element/mod.rs b/eepy-gui/src/element/mod.rs index b7939e3..8387609 100644 --- a/eepy-gui/src/element/mod.rs +++ b/eepy-gui/src/element/mod.rs @@ -6,7 +6,7 @@ use embedded_graphics::mono_font::MonoTextStyle; use embedded_graphics::pixelcolor::BinaryColor; use embedded_graphics::prelude::*; use embedded_graphics::primitives::{PrimitiveStyle, PrimitiveStyleBuilder, Rectangle}; -use eepy_sys::Event; +use eepy_sys::input::Event; use tp370pgh01::{DIM_X, DIM_Y}; use crate::draw_target::EpdDrawTarget; diff --git a/eepy-gui/src/element/slider.rs b/eepy-gui/src/element/slider.rs index f4b60ef..f60c415 100644 --- a/eepy-gui/src/element/slider.rs +++ b/eepy-gui/src/element/slider.rs @@ -2,7 +2,7 @@ use embedded_graphics::prelude::*; use embedded_graphics::geometry::Point; use embedded_graphics::pixelcolor::BinaryColor; use embedded_graphics::primitives::{Circle, Line, PrimitiveStyle, Rectangle}; -use eepy_sys::{Event, TouchEventType}; +use eepy_sys::input::{Event, TouchEventType}; use crate::draw_target::EpdDrawTarget; use crate::element::{Gui, DEFAULT_PRIMITIVE_STYLE}; diff --git a/eepy-sys/src/header.rs b/eepy-sys/src/header.rs index 623ab2e..ac190b1 100644 --- a/eepy-sys/src/header.rs +++ b/eepy-sys/src/header.rs @@ -1,5 +1,4 @@ use core::str::Utf8Error; -use crate::ProgramFunctionTable; pub const XIP_BASE: *const u8 = 0x10000000 as *const u8; pub const PROGRAM_RAM_AREA_BASE: *mut u8 = 0x20020000 as *mut u8; @@ -25,13 +24,13 @@ pub struct ProgramSlotHeader { pub version_len: usize, pub version_ptr: *const u8, - pub entry: unsafe extern "C" fn(&ProgramFunctionTable), + pub entry: unsafe extern "C" fn(), } unsafe impl Sync for ProgramSlotHeader {} impl ProgramSlotHeader { - pub const fn partial(name: &'static str, version: &'static str, entry: unsafe extern "C" fn(&ProgramFunctionTable)) -> Self { + pub const fn partial(name: &'static str, version: &'static str, entry: unsafe extern "C" fn()) -> Self { Self { block_erase_cycles: 0, crc: 0, diff --git a/eepy-sys/src/image.rs b/eepy-sys/src/image.rs new file mode 100644 index 0000000..a8bb5a3 --- /dev/null +++ b/eepy-sys/src/image.rs @@ -0,0 +1,66 @@ +use tp370pgh01::IMAGE_BYTES; +use crate::syscall; +use crate::syscall::SyscallNumber; + +#[repr(usize)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ImageSyscall { + WriteImage = 0, + Refresh = 1, +} + +impl TryFrom for ImageSyscall { + type Error = (); + + fn try_from(value: usize) -> Result { + match value { + x if x == ImageSyscall::WriteImage as usize => Ok(ImageSyscall::WriteImage), + x if x == ImageSyscall::Refresh as usize => Ok(ImageSyscall::Refresh), + _ => Err(()), + } + } +} + +#[repr(usize)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum RefreshBlockMode { + NonBlocking, + BlockAcknowledge, + BlockFinish, +} + +impl TryFrom for RefreshBlockMode { + type Error = (); + + fn try_from(value: usize) -> Result { + match value { + x if x == RefreshBlockMode::NonBlocking as usize => Ok(RefreshBlockMode::NonBlocking), + x if x == RefreshBlockMode::BlockAcknowledge as usize => Ok(RefreshBlockMode::BlockAcknowledge), + x if x == RefreshBlockMode::BlockFinish as usize => Ok(RefreshBlockMode::BlockFinish), + _ => Err(()), + } + } +} + +pub fn write_image(image: &[u8; IMAGE_BYTES]) { + unsafe { + syscall!( + SyscallNumber::Image, + in ImageSyscall::WriteImage, + in &raw const *image, + ); + } +} + +pub fn refresh(fast_refresh: bool, refresh_block_mode: RefreshBlockMode) { + unsafe { + syscall!( + SyscallNumber::Image, + in ImageSyscall::Refresh, + in fast_refresh, + in refresh_block_mode, + ); + } +} \ No newline at end of file diff --git a/eepy-sys/src/input.rs b/eepy-sys/src/input.rs new file mode 100644 index 0000000..97eb5d3 --- /dev/null +++ b/eepy-sys/src/input.rs @@ -0,0 +1,150 @@ +use core::fmt::{Display, Formatter}; +use crate::syscall; +use crate::syscall::SyscallNumber; + +#[repr(usize)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum InputSyscall { + NextEvent = 0, + SetTouchEnabled = 1, + HasEvent = 2, +} + +impl TryFrom for InputSyscall { + type Error = (); + + fn try_from(value: usize) -> Result { + match value { + x if x == InputSyscall::NextEvent as usize => Ok(InputSyscall::NextEvent), + x if x == InputSyscall::SetTouchEnabled as usize => Ok(InputSyscall::SetTouchEnabled), + x if x == InputSyscall::HasEvent as usize => Ok(InputSyscall::HasEvent), + _ => Err(()), + } + } +} + +#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] +pub enum Event { + Touch(TouchEvent), + RefreshFinished, +} + +#[repr(u8)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] +pub enum TouchEventType { + Down, + Up, + Move, +} + +#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct TouchEvent { + pub ev_type: TouchEventType, + pub x: u16, + pub y: u16, +} + +impl TouchEvent { + pub const fn new() -> Self { + Self { + ev_type: TouchEventType::Down, + x: u16::MAX, + y: u16::MAX, + } + } + + #[cfg(feature = "embedded-graphics")] + pub fn eg_point(&self) -> embedded_graphics::prelude::Point { + embedded_graphics::prelude::Point::new(self.x as i32, self.y as i32) + } +} + +impl Display for TouchEvent { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + let ty = match self.ev_type { + TouchEventType::Down => "Down", + TouchEventType::Up => "Up", + TouchEventType::Move => "Move", + }; + write!(f, "{ty} @ ({}, {})", self.x, self.y) + } +} + +#[repr(usize)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ScEventType { + NoEvent = 0, + TouchUp = 1, + TouchDown = 2, + TouchMove = 3, + RefreshFinished = 4, +} + +impl TryFrom for ScEventType { + type Error = (); + + fn try_from(value: usize) -> Result { + match value { + x if x == ScEventType::NoEvent as usize => Ok(ScEventType::NoEvent), + x if x == ScEventType::TouchUp as usize => Ok(ScEventType::TouchUp), + x if x == ScEventType::TouchDown as usize => Ok(ScEventType::TouchDown), + x if x == ScEventType::TouchMove as usize => Ok(ScEventType::TouchMove), + x if x == ScEventType::RefreshFinished as usize => Ok(ScEventType::RefreshFinished), + _ => Err(()), + } + } +} + +pub fn next_event() -> Option { + let mut ev_type: usize; + let mut x: u16; + let mut y: u16; + + unsafe { + syscall!( + SyscallNumber::Input, + out ev_type in InputSyscall::NextEvent, + out x, + out y, + ); + } + + match ScEventType::try_from(ev_type) { + Ok(ScEventType::NoEvent) => None, + Ok(ScEventType::TouchUp) => Some(Event::Touch(TouchEvent { ev_type: TouchEventType::Up, x, y })), + Ok(ScEventType::TouchDown) => Some(Event::Touch(TouchEvent { ev_type: TouchEventType::Down, x, y })), + Ok(ScEventType::TouchMove) => Some(Event::Touch(TouchEvent { ev_type: TouchEventType::Move, x, y })), + Ok(ScEventType::RefreshFinished) => Some(Event::RefreshFinished), + Err(_) => panic!("invalid touch event"), + } +} + +pub fn set_touch_enabled(enabled: bool) { + unsafe { + syscall!( + SyscallNumber::Input, + in InputSyscall::SetTouchEnabled, + in enabled, + ); + } +} + +pub fn has_event() -> bool { + let mut has_event: usize; + + unsafe { + syscall!( + SyscallNumber::Input, + out has_event in InputSyscall::HasEvent, + ); + } + + has_event != 0 +} \ No newline at end of file diff --git a/eepy-sys/src/lib.rs b/eepy-sys/src/lib.rs index 7f1a8b0..90b9586 100644 --- a/eepy-sys/src/lib.rs +++ b/eepy-sys/src/lib.rs @@ -2,108 +2,9 @@ pub mod header; pub mod syscall; +pub mod misc; +pub mod image; +pub mod input; +pub mod usb; -use core::fmt::{Display, Formatter}; -pub use tp370pgh01::IMAGE_BYTES; - -/// Option type with stable ABI. -#[repr(C)] -#[derive(Debug, serde::Serialize, serde::Deserialize)] -pub enum SafeOption { - None, - Some(T), -} - -impl From> for SafeOption { - fn from(value: Option) -> Self { - match value { - None => SafeOption::None, - Some(v) => SafeOption::Some(v), - } - } -} - -impl From> for Option { - fn from(value: SafeOption) -> Self { - match value { - SafeOption::None => None, - SafeOption::Some(v) => Some(v), - } - } -} - -#[repr(C)] -pub enum RefreshBlockMode { - NonBlocking, - BlockAcknowledge, - BlockFinish, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct ProgramFunctionTable { - pub write_image: extern "C" fn(&[u8; IMAGE_BYTES]), - pub refresh: extern "C" fn(bool, RefreshBlockMode), - pub next_event: extern "C" fn() -> SafeOption, - pub set_touch_enabled: unsafe extern "C" fn(bool), - pub serial_number: extern "C" fn() -> &'static [u8; 16] -} - -impl ProgramFunctionTable { - pub fn serial_number_str(&self) -> &'static str { - unsafe { core::str::from_utf8_unchecked((self.serial_number)()) } - } -} - -#[repr(C)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] -pub enum Event { - Null, - Touch(TouchEvent), - RefreshFinished, -} - -#[repr(u8)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] -pub enum TouchEventType { - Down, - Up, - Move, -} - -#[repr(C)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] -pub struct TouchEvent { - pub ev_type: TouchEventType, - pub x: u16, - pub y: u16, -} - -impl TouchEvent { - pub const fn new() -> Self { - Self { - ev_type: TouchEventType::Down, - x: u16::MAX, - y: u16::MAX, - } - } - - #[cfg(feature = "embedded-graphics")] - pub fn eg_point(&self) -> embedded_graphics::prelude::Point { - embedded_graphics::prelude::Point::new(self.x as i32, self.y as i32) - } -} - -impl Display for TouchEvent { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - let ty = match self.ev_type { - TouchEventType::Down => "Down", - TouchEventType::Up => "Up", - TouchEventType::Move => "Move", - }; - write!(f, "{ty} @ ({}, {})", self.x, self.y) - } -} \ No newline at end of file +pub use tp370pgh01::IMAGE_BYTES; \ No newline at end of file diff --git a/eepy-sys/src/misc.rs b/eepy-sys/src/misc.rs new file mode 100644 index 0000000..9d5076e --- /dev/null +++ b/eepy-sys/src/misc.rs @@ -0,0 +1,31 @@ +use crate::syscall; +use crate::syscall::SyscallNumber; + +#[repr(usize)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum MiscSyscall { + GetSerial = 0, +} + +impl TryFrom for MiscSyscall { + type Error = (); + + fn try_from(value: usize) -> Result { + match value { + x if x == MiscSyscall::GetSerial as usize => Ok(MiscSyscall::GetSerial), + _ => Err(()), + } + } +} + +pub fn get_serial() -> &'static str { + let mut ptr: *const [u8; 16]; + unsafe { + syscall!( + SyscallNumber::Misc, + out ptr in MiscSyscall::GetSerial, + ); + core::str::from_utf8_unchecked(&*ptr) + } +} \ No newline at end of file diff --git a/eepy-sys/src/syscall/mod.rs b/eepy-sys/src/syscall.rs similarity index 72% rename from eepy-sys/src/syscall/mod.rs rename to eepy-sys/src/syscall.rs index 96c22ca..18485e5 100644 --- a/eepy-sys/src/syscall/mod.rs +++ b/eepy-sys/src/syscall.rs @@ -22,25 +22,6 @@ impl TryFrom for SyscallNumber { } } -#[repr(usize)] -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum MiscSyscall { - GetSerial = 0, -} - -impl TryFrom for MiscSyscall { - type Error = (); - - fn try_from(value: usize) -> Result { - match value { - x if x == MiscSyscall::GetSerial as usize => Ok(MiscSyscall::GetSerial), - _ => Err(()), - } - } -} - - /// Perform a raw system call. #[macro_export] macro_rules! syscall { @@ -73,10 +54,4 @@ macro_rules! syscall { syscall_num = const $syscall_num as u8, ) } -} - -pub fn get_serial() -> &'static str { - let mut ptr: *const [u8; 16]; - unsafe { syscall!(SyscallNumber::Misc, out ptr in MiscSyscall::GetSerial) }; - unsafe { core::str::from_utf8_unchecked(&*ptr) } } \ No newline at end of file diff --git a/eepy-sys/src/usb.rs b/eepy-sys/src/usb.rs new file mode 100644 index 0000000..0ffdd02 --- /dev/null +++ b/eepy-sys/src/usb.rs @@ -0,0 +1 @@ +// TODO \ No newline at end of file diff --git a/eepy/src/gui.rs b/eepy/src/gui.rs index 23c5e27..b43ea70 100644 --- a/eepy/src/gui.rs +++ b/eepy/src/gui.rs @@ -7,8 +7,8 @@ use eepy_gui::draw_target::EpdDrawTarget; use eepy_gui::element::button::Button; use eepy_gui::element::Gui; use eepy_gui::element::slider::Slider; -use eepy_sys::{RefreshBlockMode, SafeOption, Event, TouchEventType}; -use crate::{next_event, set_touch_enabled}; +use eepy_sys::image::RefreshBlockMode; +use eepy_sys::input::{has_event, next_event, set_touch_enabled, Event, TouchEventType}; enum Page { MainPage, @@ -266,6 +266,7 @@ impl Gui for MainGui { } fn tick(&mut self, draw_target: &mut EpdDrawTarget, ev: Event) -> Self::Output { + debug!("gui tick"); if let Some(page) = self.get_current_page_mut().tick(draw_target, ev) { self.current_page = page; draw_target.clear(BinaryColor::Off).unwrap(); @@ -278,14 +279,21 @@ impl Gui for MainGui { pub(crate) fn gui_main(mut draw_target: EpdDrawTarget) -> ! { debug!("gui_main"); - unsafe { set_touch_enabled(true) }; + set_touch_enabled(true); let mut gui = MainGui::new(); gui.draw_init(&mut draw_target); draw_target.refresh(false, RefreshBlockMode::BlockAcknowledge); loop { - while let SafeOption::Some(ev) = next_event() { + while let Some(ev) = next_event() { gui.tick(&mut draw_target, ev); } + + if !has_event() { + // has_event() is a syscall. The SVCall exception is a WFE wakeup event, so we need two + // WFEs so we don't immediately wake up. + cortex_m::asm::wfe(); + cortex_m::asm::wfe(); + } } } \ No newline at end of file diff --git a/eepy/src/main.rs b/eepy/src/main.rs index 5fc6085..b941447 100644 --- a/eepy/src/main.rs +++ b/eepy/src/main.rs @@ -7,16 +7,12 @@ mod serial; mod ringbuffer; mod syscall; -use core::arch::asm; -#[allow(unused_imports)] -use panic_probe as _; -#[allow(unused_imports)] -use defmt_rtt as _; +extern crate panic_probe; +extern crate defmt_rtt; use core::cell::RefCell; use critical_section::Mutex; -use defmt::{debug, info, println, trace, warn}; -use embedded_hal::delay::DelayNs; +use defmt::{debug, info, trace, warn}; use embedded_hal::digital::OutputPin; use embedded_hal::i2c::I2c; use mcp9808::MCP9808; @@ -39,9 +35,11 @@ use fw16_epd_bsp::hal::timer::{Alarm, Alarm0}; use fw16_epd_bsp::pac::I2C0; use fw16_epd_bsp::pac::interrupt; use eepy_gui::draw_target::EpdDrawTarget; -use eepy_sys::{Event, RefreshBlockMode, SafeOption, TouchEvent, TouchEventType}; +use eepy_sys::input::{Event, TouchEvent, TouchEventType}; +use eepy_sys::misc::get_serial; use tp370pgh01::rp2040::{Rp2040PervasiveSpiDelays, IoPin}; use tp370pgh01::{Tp370pgh01, IMAGE_BYTES}; +use crate::programs::{slot, Programs}; use crate::ringbuffer::RingBuffer; //use crate::programs::Programs; @@ -74,39 +72,6 @@ static TOUCH_ENABLED: AtomicBool = AtomicBool::new(false); static FLASHING: AtomicBool = AtomicBool::new(false); static FLASHING_ACK: AtomicBool = AtomicBool::new(false); -extern "C" fn write_image(image: &[u8; IMAGE_BYTES]) { - critical_section::with(|cs| IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(image)); -} - -extern "C" fn refresh(fast_refresh: bool, block_mode: RefreshBlockMode) { - DO_REFRESH.store(true, Ordering::Relaxed); - FAST_REFRESH.store(fast_refresh, Ordering::Relaxed); - cortex_m::asm::sev(); - - if matches!(block_mode, RefreshBlockMode::BlockAcknowledge | RefreshBlockMode::BlockFinish) { - while DO_REFRESH.load(Ordering::Relaxed) {} - } - - if matches!(block_mode, RefreshBlockMode::BlockFinish) { - while REFRESHING.load(Ordering::Relaxed) {} - } -} - -extern "C" fn next_event() -> SafeOption { - critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).pop()).into() -} - -unsafe extern "C" fn set_touch_enabled(enable: bool) { - TOUCH_ENABLED.store(enable, Ordering::Relaxed); - if !enable { - critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).clear()); - } -} - -extern "C" fn serial_number() -> &'static [u8; 16] { - SERIAL_NUMBER.get().unwrap() -} - /// Function in RAM to be executed by core1 whilst flashing programs (core1 cannot be executing code /// from flash whilst writing/erasing flash) /// @@ -160,7 +125,8 @@ fn main() -> ! { SERIAL_NUMBER.set(serial).unwrap(); unsafe { cortex_m::interrupt::enable() }; - info!("Framework 16 EPD firmware version {}, serial no. {}", env!("CARGO_PKG_VERSION"), unsafe { core::str::from_utf8_unchecked(serial_number()) }); + info!("eepyOS version {} (c) arthomnix 2025", env!("CARGO_PKG_VERSION")); + info!("Serial number: {}", eepy_sys::misc::get_serial()); let mut sio = Sio::new(pac.SIO); let pins = Pins::new( @@ -219,7 +185,7 @@ fn main() -> ! { let mut timer = Timer::new(pac.TIMER, &mut pac.RESETS, &clocks); // make sure temperature sensor has read temperature - timer.delay_ms(35); + //timer.delay_ms(35); let mut alarm = timer.alarm_0().unwrap(); alarm.enable_interrupt(); critical_section::with(|cs| GLOBAL_ALARM0.borrow_ref_mut(cs).replace(alarm)); @@ -250,8 +216,8 @@ fn main() -> ! { let usb_device = UsbDeviceBuilder::new(bus_ref, UsbVidPid(0x2e8a, 0x000a)) .strings(&[StringDescriptors::default() .manufacturer("arthomnix") - .product("Touchscreen EPD Input Module for Framework 16") - .serial_number(unsafe { core::str::from_utf8_unchecked(serial_number()) }) + .product("Touchscreen EPD Input Module for Framework 16 [eepyOS]") + .serial_number(get_serial()) ]) .unwrap() .device_class(usbd_serial::USB_CLASS_CDC) @@ -299,9 +265,11 @@ fn main() -> ! { epd.soft_reset().unwrap(); epd.refresh(&image, TEMP.load(Ordering::Relaxed)).unwrap(); } + + critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).push(Event::RefreshFinished)); + cortex_m::asm::sev(); } - critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).push(Event::RefreshFinished)); REFRESHING.store(false, Ordering::Relaxed); } }).unwrap(); @@ -313,25 +281,15 @@ fn main() -> ! { pac::NVIC::unmask(interrupt::SW0_IRQ); }; - /*let program = Programs::new().next(); - if let Some(program) = program { - unsafe { - program.load(); - let pft = ProgramFunctionTable { - write_image, - refresh, - next_event, - set_touch_enabled, - serial_number, - }; - program.entry()(&pft); - } - }*/ - - // Test syscall - println!("{}", eepy_sys::syscall::get_serial()); + // testing program loading + unsafe { + let prog = slot(1); + debug!("{}", (*prog).name().unwrap()); + debug!("{}", (*prog).version().unwrap()); + ((*prog).entry)(); + } - let draw_target = EpdDrawTarget::new(write_image, refresh); + let draw_target = EpdDrawTarget::new(); gui::gui_main(draw_target); } diff --git a/eepy/src/programs.rs b/eepy/src/programs.rs index c1419d5..9ab8e1c 100644 --- a/eepy/src/programs.rs +++ b/eepy/src/programs.rs @@ -1,28 +1,26 @@ -use core::marker::PhantomData; use eepy_sys::header::*; -pub(crate) const unsafe fn slot<'a>(id: u8) -> &'a ProgramSlotHeader { +pub(crate) const unsafe fn slot(id: u8) -> *const ProgramSlotHeader { if id < 1 || id > 31 { panic!("slot ID must be between 1 and 31"); } - &*XIP_BASE.add(SLOT_SIZE * id as usize).cast::() + XIP_BASE.add(SLOT_SIZE * id as usize).cast::() } -pub(crate) struct Programs<'a> { +pub(crate) struct Programs { id: u8, - _phantom: PhantomData<&'a ProgramSlotHeader>, } -impl<'a> Programs<'a> { +impl Programs { pub(crate) fn new() -> Self { - Self { id: 0, _phantom: PhantomData } + Self { id: 0 } } } -impl<'a> Iterator for Programs<'a> { - type Item = &'a ProgramSlotHeader; +impl Iterator for Programs { + type Item = *const ProgramSlotHeader; fn next(&mut self) -> Option { return None; diff --git a/eepy/src/ringbuffer.rs b/eepy/src/ringbuffer.rs index ae12c5f..ad0f437 100644 --- a/eepy/src/ringbuffer.rs +++ b/eepy/src/ringbuffer.rs @@ -57,4 +57,8 @@ impl RingBuffer { self.read_index = 0; self.write_index = 0; } + + pub(crate) fn is_empty(&self) -> bool { + self.read_index == self.write_index + } } \ No newline at end of file diff --git a/eepy/src/serial.rs b/eepy/src/serial.rs index 60dda89..45fd7e1 100644 --- a/eepy/src/serial.rs +++ b/eepy/src/serial.rs @@ -5,10 +5,10 @@ use usbd_serial::SerialPort; use fw16_epd_bsp::hal::usb::UsbBus; use fw16_epd_bsp::pac; use eepy_sys::header::ProgramSlotHeader; -use eepy_sys::RefreshBlockMode; use eepy_serial::{Response, SerialCommand}; +use eepy_sys::image::{refresh, write_image, RefreshBlockMode}; use tp370pgh01::IMAGE_BYTES; -use crate::{refresh, write_image, GLOBAL_USB_DEVICE, GLOBAL_USB_SERIAL}; +use crate::{GLOBAL_USB_DEVICE, GLOBAL_USB_SERIAL}; #[derive(Copy, Clone, Debug, defmt::Format)] enum SerialState { diff --git a/eepy/src/syscall.rs b/eepy/src/syscall.rs index db3dd19..c3fa111 100644 --- a/eepy/src/syscall.rs +++ b/eepy/src/syscall.rs @@ -1,9 +1,12 @@ -use core::arch::{asm, global_asm}; -use cortex_m_rt::exception; -use defmt::debug; -use defmt::export::panic; -use eepy_sys::syscall::{MiscSyscall, SyscallNumber}; -use crate::SERIAL_NUMBER; +use core::arch::global_asm; +use core::sync::atomic::Ordering; +use defmt::trace; +use eepy_sys::image::{ImageSyscall, RefreshBlockMode}; +use eepy_sys::input::{Event, InputSyscall, ScEventType, TouchEvent, TouchEventType}; +use eepy_sys::misc::MiscSyscall; +use eepy_sys::syscall::SyscallNumber; +use tp370pgh01::IMAGE_BYTES; +use crate::{DO_REFRESH, EVENT_QUEUE, FAST_REFRESH, IMAGE_BUFFER, REFRESHING, SERIAL_NUMBER, TOUCH_ENABLED}; global_asm!(include_str!("syscall.s")); @@ -18,19 +21,121 @@ global_asm!(include_str!("syscall.s")); /// The pc value from the stack can be used to extract the literal operand from the SVC instruction /// which triggered the syscall. #[no_mangle] -extern "C" fn syscall(sp: *mut usize) { +extern "C" fn handle_syscall(sp: *mut usize) { // Stack contains R0, R1, R2, R3, R12, LR, ReturnAddress, xPSR let stack_values = unsafe { core::slice::from_raw_parts_mut(sp, 8) }; let syscall_num = unsafe { (stack_values[6] as *const u8).sub(2).read() }; + trace!( + "syscall: sp={} imm={:x} r0={:x} r1={:x} r2={:x} r3={:x} r12={:x} lr={:x} pc={:x} xpsr={:x}", + sp, + syscall_num, + stack_values[0], + stack_values[1], + stack_values[2], + stack_values[3], + stack_values[4], + stack_values[5], + stack_values[6], + stack_values[7], + ); + match SyscallNumber::try_from(syscall_num) { - Ok(SyscallNumber::Misc) => match MiscSyscall::try_from(stack_values[0]) { - Ok(MiscSyscall::GetSerial) => { - let serial = SERIAL_NUMBER.get().unwrap(); - stack_values[0] = (&raw const *serial) as usize; - }, - _ => panic!("illegal syscall"), - }, + Ok(SyscallNumber::Misc) => handle_misc(stack_values), + Ok(SyscallNumber::Image) => handle_image(stack_values), + Ok(SyscallNumber::Input) => handle_input(stack_values), + Ok(SyscallNumber::Usb) => todo!("usb syscalls"), + _ => panic!("illegal syscall"), + } +} + +fn handle_misc(stack_values: &mut [usize]) { + match MiscSyscall::try_from(stack_values[0]) { + Ok(MiscSyscall::GetSerial) => handle_get_serial(stack_values), + _ => panic!("illegal syscall"), + } +} + +fn handle_get_serial(stack_values: &mut [usize]) { + stack_values[0] = (&raw const *SERIAL_NUMBER.get().unwrap()) as usize; +} + +fn handle_image(stack_values: &mut [usize]) { + match ImageSyscall::try_from(stack_values[0]) { + Ok(ImageSyscall::WriteImage) => handle_write_image(stack_values), + Ok(ImageSyscall::Refresh) => handle_refresh(stack_values), + _ => panic!("illegal syscall"), + } +} + +fn handle_write_image(stack_values: &mut [usize]) { + let image: &[u8; IMAGE_BYTES] = unsafe { &*(stack_values[1] as *const [u8; IMAGE_BYTES]) }; + critical_section::with(|cs| IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(image)); +} + +fn handle_refresh(stack_values: &mut [usize]) { + let fast_refresh = stack_values[1] != 0; + let blocking_mode = RefreshBlockMode::try_from(stack_values[2]).expect("illegal refresh blocking mode"); + + DO_REFRESH.store(true, Ordering::Relaxed); + FAST_REFRESH.store(fast_refresh, Ordering::Relaxed); + cortex_m::asm::sev(); + + if matches!(blocking_mode, RefreshBlockMode::BlockAcknowledge | RefreshBlockMode::BlockFinish) { + while DO_REFRESH.load(Ordering::Relaxed) {} + } + + if matches!(blocking_mode, RefreshBlockMode::BlockFinish) { + while REFRESHING.load(Ordering::Relaxed) {} + } +} + +fn handle_input(stack_values: &mut [usize]) { + match InputSyscall::try_from(stack_values[0]) { + Ok(InputSyscall::NextEvent) => handle_next_event(stack_values), + Ok(InputSyscall::SetTouchEnabled) => handle_set_touch_enabled(stack_values), + Ok(InputSyscall::HasEvent) => handle_has_event(stack_values), _ => panic!("illegal syscall"), } +} + +fn handle_next_event(stack_values: &mut [usize]) { + let next_event = critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).pop()); + let mut x = 0; + let mut y = 0; + let sc_ev_type = match next_event { + None => ScEventType::NoEvent, + Some(Event::Touch(TouchEvent { ev_type: TouchEventType::Down, x: px, y: py })) => { + x = px; + y = py; + ScEventType::TouchDown + }, + Some(Event::Touch(TouchEvent { ev_type: TouchEventType::Up, x: px, y: py })) => { + x = px; + y = py; + ScEventType::TouchUp + }, + Some(Event::Touch(TouchEvent { ev_type: TouchEventType::Move, x: px, y: py })) => { + x = px; + y = py; + ScEventType::TouchMove + }, + Some(Event::RefreshFinished) => ScEventType::RefreshFinished, + }; + stack_values[0] = sc_ev_type as usize; + stack_values[1] = x as usize; + stack_values[2] = y as usize; +} + +fn handle_set_touch_enabled(stack_values: &mut [usize]) { + let enable = stack_values[1] != 0; + TOUCH_ENABLED.store(enable, Ordering::Relaxed); + if !enable { + critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).clear()); + } +} + +fn handle_has_event(stack_values: &mut [usize]) { + let empty = critical_section::with(|cs| EVENT_QUEUE.borrow_ref(cs).is_empty()); + stack_values[0] = (!empty) as usize; } \ No newline at end of file diff --git a/eepy/src/syscall.s b/eepy/src/syscall.s index c36c70f..8f671c5 100644 --- a/eepy/src/syscall.s +++ b/eepy/src/syscall.s @@ -7,9 +7,9 @@ _SVCall: tst r0, r1 beq .use_msp mrs r0, psp - ldr r1, =syscall + ldr r1, =handle_syscall bx r1 .use_msp: mrs r0, msp - ldr r1, =syscall + ldr r1, =handle_syscall bx r1 \ No newline at end of file diff --git a/elf2epb/src/main.rs b/elf2epb/src/main.rs index 709ce5d..0cce073 100644 --- a/elf2epb/src/main.rs +++ b/elf2epb/src/main.rs @@ -28,10 +28,26 @@ fn main() { let mut data_paddr = 0u32; let mut data_vaddr = 0u32; let mut data_len = 0u32; + let mut current_addr: Option = None; for phdr in elf.segments().expect("Failed to parse ELF file") { if phdr.p_type == PT_LOAD { - bin.extend_from_slice(elf.segment_data(&phdr).expect("Failed to parse ELF file")); + if current_addr.is_none() { + current_addr.replace(phdr.p_vaddr as u32); + } + + let current_addr = current_addr.as_mut().unwrap(); + + if *current_addr < phdr.p_vaddr as u32 { + let diff = phdr.p_vaddr as u32 - *current_addr; + for _ in 0..diff { + bin.push(0); + } + } + + let segment_data = elf.segment_data(&phdr).expect("Failed to parse ELF file"); + bin.extend_from_slice(segment_data); + *current_addr += segment_data.len() as u32; } // Assume that there is at most one segment that needs to be copied from flash to RAM