firmware for my Touchscreen E-Paper Input Module for Framework Laptop 16
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fully replace function pointer interface with syscalls

+459 -253
+1 -1
eepy-example-app/build.sh
··· 7 7 for slot in `seq -f "%02g" 31` 8 8 do 9 9 EPD_PROG_SLOT=$slot cargo build --release 10 - elf2epb -i ../target/thumbv6m-none-eabi/release/eepy-example-app -o "out/bins/fw16-epd-example-app.s$slot.epb" 10 + elf2epb -i ../target/thumbv6m-none-eabi/release/eepy-example-app -o "out/bins/eepy-example-app.s$slot.epb" 11 11 done 12 12 13 13 (cd out/bins && tar --zstd -cf ../eepy-example-app.epa *)
+11 -13
eepy-example-app/src/main.rs
··· 3 3 4 4 extern crate panic_halt; 5 5 6 + use core::arch::asm; 6 7 use core::fmt::Write; 7 8 use embedded_graphics::draw_target::DrawTarget; 8 9 use embedded_graphics::Drawable; ··· 13 14 use eepy_gui::draw_target::EpdDrawTarget; 14 15 use eepy_gui::element::button::Button; 15 16 use eepy_gui::element::{Gui, DEFAULT_TEXT_STYLE}; 16 - use eepy_sys::{ProgramFunctionTable, RefreshBlockMode, SafeOption}; 17 17 use eepy_sys::header::ProgramSlotHeader; 18 - 19 - extern "C" { 20 - static _end: *const u8; 21 - } 18 + use eepy_sys::image::RefreshBlockMode; 19 + use eepy_sys::input::{has_event, next_event, set_touch_enabled}; 22 20 23 21 #[link_section = ".header"] 24 22 #[used] ··· 28 26 entry, 29 27 ); 30 28 31 - #[used] 32 - static mut FOO: u32 = 10; 33 - 34 29 #[no_mangle] 35 - pub extern "C" fn entry(pft: &ProgramFunctionTable) { 36 - 37 - unsafe { (pft.set_touch_enabled)(true) }; 30 + pub extern "C" fn entry() { 31 + set_touch_enabled(true); 38 32 39 - let mut draw_target = EpdDrawTarget::new(pft.write_image, pft.refresh); 33 + let mut draw_target = EpdDrawTarget::new(); 40 34 41 35 let mut button = Button::with_default_style_auto_sized(Point::new(10, 40), "Click me", true); 42 36 button.draw_init(&mut draw_target); ··· 45 39 let mut counter = 0; 46 40 47 41 loop { 48 - while let SafeOption::Some(ev) = (pft.next_event)() { 42 + while let Some(ev) = next_event() { 49 43 let mut needs_refresh = false; 50 44 51 45 let response = button.tick(&mut draw_target, ev); ··· 66 60 if needs_refresh { 67 61 draw_target.refresh(true, RefreshBlockMode::NonBlocking); 68 62 } 63 + } 64 + 65 + if !has_event() { 66 + unsafe { asm!("wfe", "wfe") }; 69 67 } 70 68 } 71 69 }
+4 -8
eepy-gui/src/draw_target.rs
··· 4 4 use embedded_graphics::Pixel; 5 5 use embedded_graphics::pixelcolor::BinaryColor; 6 6 use embedded_graphics::primitives::Rectangle; 7 - use eepy_sys::RefreshBlockMode; 7 + use eepy_sys::image::{refresh, write_image, RefreshBlockMode}; 8 8 use tp370pgh01::{DIM_X, DIM_Y, IMAGE_BYTES}; 9 9 10 10 pub struct EpdDrawTarget { 11 - write_image: extern "C" fn(&[u8; IMAGE_BYTES]), 12 - refresh: extern "C" fn(bool, RefreshBlockMode), 13 11 buf: [u8; IMAGE_BYTES], 14 12 } 15 13 ··· 56 54 } 57 55 58 56 impl EpdDrawTarget { 59 - pub const fn new(write_image: extern "C" fn(&[u8; IMAGE_BYTES]), refresh: extern "C" fn(bool, RefreshBlockMode)) -> Self { 57 + pub const fn new() -> Self { 60 58 Self { 61 - write_image, 62 - refresh, 63 59 buf: [0; IMAGE_BYTES] 64 60 } 65 61 } 66 62 67 63 pub fn refresh(&self, fast_refresh: bool, block_mode: RefreshBlockMode) { 68 - (self.write_image)(&self.buf); 69 - (self.refresh)(fast_refresh, block_mode); 64 + write_image(&self.buf); 65 + refresh(fast_refresh, block_mode); 70 66 } 71 67 }
+1 -1
eepy-gui/src/element/button.rs
··· 4 4 use embedded_graphics::primitives::{CornerRadii, PrimitiveStyle, Rectangle, RoundedRectangle}; 5 5 use embedded_graphics::text::{Alignment, Baseline, Text, TextStyle, TextStyleBuilder}; 6 6 use embedded_graphics::text::renderer::TextRenderer; 7 - use eepy_sys::{Event, TouchEventType}; 7 + use eepy_sys::input::{Event, TouchEventType}; 8 8 use crate::draw_target::EpdDrawTarget; 9 9 use crate::element::{Gui, DEFAULT_PRIMITIVE_STYLE, DEFAULT_TEXT_STYLE}; 10 10
+1 -1
eepy-gui/src/element/mod.rs
··· 6 6 use embedded_graphics::pixelcolor::BinaryColor; 7 7 use embedded_graphics::prelude::*; 8 8 use embedded_graphics::primitives::{PrimitiveStyle, PrimitiveStyleBuilder, Rectangle}; 9 - use eepy_sys::Event; 9 + use eepy_sys::input::Event; 10 10 use tp370pgh01::{DIM_X, DIM_Y}; 11 11 use crate::draw_target::EpdDrawTarget; 12 12
+1 -1
eepy-gui/src/element/slider.rs
··· 2 2 use embedded_graphics::geometry::Point; 3 3 use embedded_graphics::pixelcolor::BinaryColor; 4 4 use embedded_graphics::primitives::{Circle, Line, PrimitiveStyle, Rectangle}; 5 - use eepy_sys::{Event, TouchEventType}; 5 + use eepy_sys::input::{Event, TouchEventType}; 6 6 use crate::draw_target::EpdDrawTarget; 7 7 use crate::element::{Gui, DEFAULT_PRIMITIVE_STYLE}; 8 8
+2 -3
eepy-sys/src/header.rs
··· 1 1 use core::str::Utf8Error; 2 - use crate::ProgramFunctionTable; 3 2 4 3 pub const XIP_BASE: *const u8 = 0x10000000 as *const u8; 5 4 pub const PROGRAM_RAM_AREA_BASE: *mut u8 = 0x20020000 as *mut u8; ··· 25 24 pub version_len: usize, 26 25 pub version_ptr: *const u8, 27 26 28 - pub entry: unsafe extern "C" fn(&ProgramFunctionTable), 27 + pub entry: unsafe extern "C" fn(), 29 28 } 30 29 31 30 unsafe impl Sync for ProgramSlotHeader {} 32 31 33 32 impl ProgramSlotHeader { 34 - pub const fn partial(name: &'static str, version: &'static str, entry: unsafe extern "C" fn(&ProgramFunctionTable)) -> Self { 33 + pub const fn partial(name: &'static str, version: &'static str, entry: unsafe extern "C" fn()) -> Self { 35 34 Self { 36 35 block_erase_cycles: 0, 37 36 crc: 0,
+66
eepy-sys/src/image.rs
··· 1 + use tp370pgh01::IMAGE_BYTES; 2 + use crate::syscall; 3 + use crate::syscall::SyscallNumber; 4 + 5 + #[repr(usize)] 6 + #[derive(Copy, Clone, Debug, Eq, PartialEq)] 7 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 8 + pub enum ImageSyscall { 9 + WriteImage = 0, 10 + Refresh = 1, 11 + } 12 + 13 + impl TryFrom<usize> for ImageSyscall { 14 + type Error = (); 15 + 16 + fn try_from(value: usize) -> Result<Self, Self::Error> { 17 + match value { 18 + x if x == ImageSyscall::WriteImage as usize => Ok(ImageSyscall::WriteImage), 19 + x if x == ImageSyscall::Refresh as usize => Ok(ImageSyscall::Refresh), 20 + _ => Err(()), 21 + } 22 + } 23 + } 24 + 25 + #[repr(usize)] 26 + #[derive(Copy, Clone, Debug, Eq, PartialEq)] 27 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 28 + pub enum RefreshBlockMode { 29 + NonBlocking, 30 + BlockAcknowledge, 31 + BlockFinish, 32 + } 33 + 34 + impl TryFrom<usize> for RefreshBlockMode { 35 + type Error = (); 36 + 37 + fn try_from(value: usize) -> Result<Self, Self::Error> { 38 + match value { 39 + x if x == RefreshBlockMode::NonBlocking as usize => Ok(RefreshBlockMode::NonBlocking), 40 + x if x == RefreshBlockMode::BlockAcknowledge as usize => Ok(RefreshBlockMode::BlockAcknowledge), 41 + x if x == RefreshBlockMode::BlockFinish as usize => Ok(RefreshBlockMode::BlockFinish), 42 + _ => Err(()), 43 + } 44 + } 45 + } 46 + 47 + pub fn write_image(image: &[u8; IMAGE_BYTES]) { 48 + unsafe { 49 + syscall!( 50 + SyscallNumber::Image, 51 + in ImageSyscall::WriteImage, 52 + in &raw const *image, 53 + ); 54 + } 55 + } 56 + 57 + pub fn refresh(fast_refresh: bool, refresh_block_mode: RefreshBlockMode) { 58 + unsafe { 59 + syscall!( 60 + SyscallNumber::Image, 61 + in ImageSyscall::Refresh, 62 + in fast_refresh, 63 + in refresh_block_mode, 64 + ); 65 + } 66 + }
+150
eepy-sys/src/input.rs
··· 1 + use core::fmt::{Display, Formatter}; 2 + use crate::syscall; 3 + use crate::syscall::SyscallNumber; 4 + 5 + #[repr(usize)] 6 + #[derive(Copy, Clone, Debug, Eq, PartialEq)] 7 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 8 + pub enum InputSyscall { 9 + NextEvent = 0, 10 + SetTouchEnabled = 1, 11 + HasEvent = 2, 12 + } 13 + 14 + impl TryFrom<usize> for InputSyscall { 15 + type Error = (); 16 + 17 + fn try_from(value: usize) -> Result<Self, Self::Error> { 18 + match value { 19 + x if x == InputSyscall::NextEvent as usize => Ok(InputSyscall::NextEvent), 20 + x if x == InputSyscall::SetTouchEnabled as usize => Ok(InputSyscall::SetTouchEnabled), 21 + x if x == InputSyscall::HasEvent as usize => Ok(InputSyscall::HasEvent), 22 + _ => Err(()), 23 + } 24 + } 25 + } 26 + 27 + #[repr(C)] 28 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 29 + #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 30 + pub enum Event { 31 + Touch(TouchEvent), 32 + RefreshFinished, 33 + } 34 + 35 + #[repr(u8)] 36 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 37 + #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 38 + pub enum TouchEventType { 39 + Down, 40 + Up, 41 + Move, 42 + } 43 + 44 + #[repr(C)] 45 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 46 + #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 47 + pub struct TouchEvent { 48 + pub ev_type: TouchEventType, 49 + pub x: u16, 50 + pub y: u16, 51 + } 52 + 53 + impl TouchEvent { 54 + pub const fn new() -> Self { 55 + Self { 56 + ev_type: TouchEventType::Down, 57 + x: u16::MAX, 58 + y: u16::MAX, 59 + } 60 + } 61 + 62 + #[cfg(feature = "embedded-graphics")] 63 + pub fn eg_point(&self) -> embedded_graphics::prelude::Point { 64 + embedded_graphics::prelude::Point::new(self.x as i32, self.y as i32) 65 + } 66 + } 67 + 68 + impl Display for TouchEvent { 69 + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 70 + let ty = match self.ev_type { 71 + TouchEventType::Down => "Down", 72 + TouchEventType::Up => "Up", 73 + TouchEventType::Move => "Move", 74 + }; 75 + write!(f, "{ty} @ ({}, {})", self.x, self.y) 76 + } 77 + } 78 + 79 + #[repr(usize)] 80 + #[derive(Copy, Clone, Debug, Eq, PartialEq)] 81 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 82 + pub enum ScEventType { 83 + NoEvent = 0, 84 + TouchUp = 1, 85 + TouchDown = 2, 86 + TouchMove = 3, 87 + RefreshFinished = 4, 88 + } 89 + 90 + impl TryFrom<usize> for ScEventType { 91 + type Error = (); 92 + 93 + fn try_from(value: usize) -> Result<Self, Self::Error> { 94 + match value { 95 + x if x == ScEventType::NoEvent as usize => Ok(ScEventType::NoEvent), 96 + x if x == ScEventType::TouchUp as usize => Ok(ScEventType::TouchUp), 97 + x if x == ScEventType::TouchDown as usize => Ok(ScEventType::TouchDown), 98 + x if x == ScEventType::TouchMove as usize => Ok(ScEventType::TouchMove), 99 + x if x == ScEventType::RefreshFinished as usize => Ok(ScEventType::RefreshFinished), 100 + _ => Err(()), 101 + } 102 + } 103 + } 104 + 105 + pub fn next_event() -> Option<Event> { 106 + let mut ev_type: usize; 107 + let mut x: u16; 108 + let mut y: u16; 109 + 110 + unsafe { 111 + syscall!( 112 + SyscallNumber::Input, 113 + out ev_type in InputSyscall::NextEvent, 114 + out x, 115 + out y, 116 + ); 117 + } 118 + 119 + match ScEventType::try_from(ev_type) { 120 + Ok(ScEventType::NoEvent) => None, 121 + Ok(ScEventType::TouchUp) => Some(Event::Touch(TouchEvent { ev_type: TouchEventType::Up, x, y })), 122 + Ok(ScEventType::TouchDown) => Some(Event::Touch(TouchEvent { ev_type: TouchEventType::Down, x, y })), 123 + Ok(ScEventType::TouchMove) => Some(Event::Touch(TouchEvent { ev_type: TouchEventType::Move, x, y })), 124 + Ok(ScEventType::RefreshFinished) => Some(Event::RefreshFinished), 125 + Err(_) => panic!("invalid touch event"), 126 + } 127 + } 128 + 129 + pub fn set_touch_enabled(enabled: bool) { 130 + unsafe { 131 + syscall!( 132 + SyscallNumber::Input, 133 + in InputSyscall::SetTouchEnabled, 134 + in enabled, 135 + ); 136 + } 137 + } 138 + 139 + pub fn has_event() -> bool { 140 + let mut has_event: usize; 141 + 142 + unsafe { 143 + syscall!( 144 + SyscallNumber::Input, 145 + out has_event in InputSyscall::HasEvent, 146 + ); 147 + } 148 + 149 + has_event != 0 150 + }
+5 -104
eepy-sys/src/lib.rs
··· 2 2 3 3 pub mod header; 4 4 pub mod syscall; 5 - 6 - use core::fmt::{Display, Formatter}; 7 - pub use tp370pgh01::IMAGE_BYTES; 8 - 9 - /// Option type with stable ABI. 10 - #[repr(C)] 11 - #[derive(Debug, serde::Serialize, serde::Deserialize)] 12 - pub enum SafeOption<T> { 13 - None, 14 - Some(T), 15 - } 16 - 17 - impl<T> From<Option<T>> for SafeOption<T> { 18 - fn from(value: Option<T>) -> Self { 19 - match value { 20 - None => SafeOption::None, 21 - Some(v) => SafeOption::Some(v), 22 - } 23 - } 24 - } 5 + pub mod misc; 6 + pub mod image; 7 + pub mod input; 8 + pub mod usb; 25 9 26 - impl<T> From<SafeOption<T>> for Option<T> { 27 - fn from(value: SafeOption<T>) -> Self { 28 - match value { 29 - SafeOption::None => None, 30 - SafeOption::Some(v) => Some(v), 31 - } 32 - } 33 - } 34 - 35 - #[repr(C)] 36 - pub enum RefreshBlockMode { 37 - NonBlocking, 38 - BlockAcknowledge, 39 - BlockFinish, 40 - } 41 - 42 - #[repr(C)] 43 - #[derive(Copy, Clone)] 44 - pub struct ProgramFunctionTable { 45 - pub write_image: extern "C" fn(&[u8; IMAGE_BYTES]), 46 - pub refresh: extern "C" fn(bool, RefreshBlockMode), 47 - pub next_event: extern "C" fn() -> SafeOption<Event>, 48 - pub set_touch_enabled: unsafe extern "C" fn(bool), 49 - pub serial_number: extern "C" fn() -> &'static [u8; 16] 50 - } 51 - 52 - impl ProgramFunctionTable { 53 - pub fn serial_number_str(&self) -> &'static str { 54 - unsafe { core::str::from_utf8_unchecked((self.serial_number)()) } 55 - } 56 - } 57 - 58 - #[repr(C)] 59 - #[cfg_attr(feature = "defmt", derive(defmt::Format))] 60 - #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 61 - pub enum Event { 62 - Null, 63 - Touch(TouchEvent), 64 - RefreshFinished, 65 - } 66 - 67 - #[repr(u8)] 68 - #[cfg_attr(feature = "defmt", derive(defmt::Format))] 69 - #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 70 - pub enum TouchEventType { 71 - Down, 72 - Up, 73 - Move, 74 - } 75 - 76 - #[repr(C)] 77 - #[cfg_attr(feature = "defmt", derive(defmt::Format))] 78 - #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 79 - pub struct TouchEvent { 80 - pub ev_type: TouchEventType, 81 - pub x: u16, 82 - pub y: u16, 83 - } 84 - 85 - impl TouchEvent { 86 - pub const fn new() -> Self { 87 - Self { 88 - ev_type: TouchEventType::Down, 89 - x: u16::MAX, 90 - y: u16::MAX, 91 - } 92 - } 93 - 94 - #[cfg(feature = "embedded-graphics")] 95 - pub fn eg_point(&self) -> embedded_graphics::prelude::Point { 96 - embedded_graphics::prelude::Point::new(self.x as i32, self.y as i32) 97 - } 98 - } 99 - 100 - impl Display for TouchEvent { 101 - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 102 - let ty = match self.ev_type { 103 - TouchEventType::Down => "Down", 104 - TouchEventType::Up => "Up", 105 - TouchEventType::Move => "Move", 106 - }; 107 - write!(f, "{ty} @ ({}, {})", self.x, self.y) 108 - } 109 - } 10 + pub use tp370pgh01::IMAGE_BYTES;
+31
eepy-sys/src/misc.rs
··· 1 + use crate::syscall; 2 + use crate::syscall::SyscallNumber; 3 + 4 + #[repr(usize)] 5 + #[derive(Copy, Clone, Debug, Eq, PartialEq)] 6 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 7 + pub enum MiscSyscall { 8 + GetSerial = 0, 9 + } 10 + 11 + impl TryFrom<usize> for MiscSyscall { 12 + type Error = (); 13 + 14 + fn try_from(value: usize) -> Result<Self, Self::Error> { 15 + match value { 16 + x if x == MiscSyscall::GetSerial as usize => Ok(MiscSyscall::GetSerial), 17 + _ => Err(()), 18 + } 19 + } 20 + } 21 + 22 + pub fn get_serial() -> &'static str { 23 + let mut ptr: *const [u8; 16]; 24 + unsafe { 25 + syscall!( 26 + SyscallNumber::Misc, 27 + out ptr in MiscSyscall::GetSerial, 28 + ); 29 + core::str::from_utf8_unchecked(&*ptr) 30 + } 31 + }
-25
eepy-sys/src/syscall/mod.rs eepy-sys/src/syscall.rs
··· 22 22 } 23 23 } 24 24 25 - #[repr(usize)] 26 - #[derive(Copy, Clone, Debug, Eq, PartialEq)] 27 - #[cfg_attr(feature = "defmt", derive(defmt::Format))] 28 - pub enum MiscSyscall { 29 - GetSerial = 0, 30 - } 31 - 32 - impl TryFrom<usize> for MiscSyscall { 33 - type Error = (); 34 - 35 - fn try_from(value: usize) -> Result<Self, Self::Error> { 36 - match value { 37 - x if x == MiscSyscall::GetSerial as usize => Ok(MiscSyscall::GetSerial), 38 - _ => Err(()), 39 - } 40 - } 41 - } 42 - 43 - 44 25 /// Perform a raw system call. 45 26 #[macro_export] 46 27 macro_rules! syscall { ··· 73 54 syscall_num = const $syscall_num as u8, 74 55 ) 75 56 } 76 - } 77 - 78 - pub fn get_serial() -> &'static str { 79 - let mut ptr: *const [u8; 16]; 80 - unsafe { syscall!(SyscallNumber::Misc, out ptr in MiscSyscall::GetSerial) }; 81 - unsafe { core::str::from_utf8_unchecked(&*ptr) } 82 57 }
+1
eepy-sys/src/usb.rs
··· 1 + // TODO
+12 -4
eepy/src/gui.rs
··· 7 7 use eepy_gui::element::button::Button; 8 8 use eepy_gui::element::Gui; 9 9 use eepy_gui::element::slider::Slider; 10 - use eepy_sys::{RefreshBlockMode, SafeOption, Event, TouchEventType}; 11 - use crate::{next_event, set_touch_enabled}; 10 + use eepy_sys::image::RefreshBlockMode; 11 + use eepy_sys::input::{has_event, next_event, set_touch_enabled, Event, TouchEventType}; 12 12 13 13 enum Page { 14 14 MainPage, ··· 266 266 } 267 267 268 268 fn tick(&mut self, draw_target: &mut EpdDrawTarget, ev: Event) -> Self::Output { 269 + debug!("gui tick"); 269 270 if let Some(page) = self.get_current_page_mut().tick(draw_target, ev) { 270 271 self.current_page = page; 271 272 draw_target.clear(BinaryColor::Off).unwrap(); ··· 278 279 pub(crate) fn gui_main(mut draw_target: EpdDrawTarget) -> ! { 279 280 debug!("gui_main"); 280 281 281 - unsafe { set_touch_enabled(true) }; 282 + set_touch_enabled(true); 282 283 let mut gui = MainGui::new(); 283 284 gui.draw_init(&mut draw_target); 284 285 draw_target.refresh(false, RefreshBlockMode::BlockAcknowledge); 285 286 286 287 loop { 287 - while let SafeOption::Some(ev) = next_event() { 288 + while let Some(ev) = next_event() { 288 289 gui.tick(&mut draw_target, ev); 290 + } 291 + 292 + if !has_event() { 293 + // has_event() is a syscall. The SVCall exception is a WFE wakeup event, so we need two 294 + // WFEs so we don't immediately wake up. 295 + cortex_m::asm::wfe(); 296 + cortex_m::asm::wfe(); 289 297 } 290 298 } 291 299 }
+22 -64
eepy/src/main.rs
··· 7 7 mod ringbuffer; 8 8 mod syscall; 9 9 10 - use core::arch::asm; 11 - #[allow(unused_imports)] 12 - use panic_probe as _; 13 - #[allow(unused_imports)] 14 - use defmt_rtt as _; 10 + extern crate panic_probe; 11 + extern crate defmt_rtt; 15 12 16 13 use core::cell::RefCell; 17 14 use critical_section::Mutex; 18 - use defmt::{debug, info, println, trace, warn}; 19 - use embedded_hal::delay::DelayNs; 15 + use defmt::{debug, info, trace, warn}; 20 16 use embedded_hal::digital::OutputPin; 21 17 use embedded_hal::i2c::I2c; 22 18 use mcp9808::MCP9808; ··· 39 35 use fw16_epd_bsp::pac::I2C0; 40 36 use fw16_epd_bsp::pac::interrupt; 41 37 use eepy_gui::draw_target::EpdDrawTarget; 42 - use eepy_sys::{Event, RefreshBlockMode, SafeOption, TouchEvent, TouchEventType}; 38 + use eepy_sys::input::{Event, TouchEvent, TouchEventType}; 39 + use eepy_sys::misc::get_serial; 43 40 use tp370pgh01::rp2040::{Rp2040PervasiveSpiDelays, IoPin}; 44 41 use tp370pgh01::{Tp370pgh01, IMAGE_BYTES}; 42 + use crate::programs::{slot, Programs}; 45 43 use crate::ringbuffer::RingBuffer; 46 44 //use crate::programs::Programs; 47 45 ··· 74 72 static FLASHING: AtomicBool = AtomicBool::new(false); 75 73 static FLASHING_ACK: AtomicBool = AtomicBool::new(false); 76 74 77 - extern "C" fn write_image(image: &[u8; IMAGE_BYTES]) { 78 - critical_section::with(|cs| IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(image)); 79 - } 80 - 81 - extern "C" fn refresh(fast_refresh: bool, block_mode: RefreshBlockMode) { 82 - DO_REFRESH.store(true, Ordering::Relaxed); 83 - FAST_REFRESH.store(fast_refresh, Ordering::Relaxed); 84 - cortex_m::asm::sev(); 85 - 86 - if matches!(block_mode, RefreshBlockMode::BlockAcknowledge | RefreshBlockMode::BlockFinish) { 87 - while DO_REFRESH.load(Ordering::Relaxed) {} 88 - } 89 - 90 - if matches!(block_mode, RefreshBlockMode::BlockFinish) { 91 - while REFRESHING.load(Ordering::Relaxed) {} 92 - } 93 - } 94 - 95 - extern "C" fn next_event() -> SafeOption<Event> { 96 - critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).pop()).into() 97 - } 98 - 99 - unsafe extern "C" fn set_touch_enabled(enable: bool) { 100 - TOUCH_ENABLED.store(enable, Ordering::Relaxed); 101 - if !enable { 102 - critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).clear()); 103 - } 104 - } 105 - 106 - extern "C" fn serial_number() -> &'static [u8; 16] { 107 - SERIAL_NUMBER.get().unwrap() 108 - } 109 - 110 75 /// Function in RAM to be executed by core1 whilst flashing programs (core1 cannot be executing code 111 76 /// from flash whilst writing/erasing flash) 112 77 /// ··· 160 125 SERIAL_NUMBER.set(serial).unwrap(); 161 126 unsafe { cortex_m::interrupt::enable() }; 162 127 163 - info!("Framework 16 EPD firmware version {}, serial no. {}", env!("CARGO_PKG_VERSION"), unsafe { core::str::from_utf8_unchecked(serial_number()) }); 128 + info!("eepyOS version {} (c) arthomnix 2025", env!("CARGO_PKG_VERSION")); 129 + info!("Serial number: {}", eepy_sys::misc::get_serial()); 164 130 165 131 let mut sio = Sio::new(pac.SIO); 166 132 let pins = Pins::new( ··· 219 185 220 186 let mut timer = Timer::new(pac.TIMER, &mut pac.RESETS, &clocks); 221 187 // make sure temperature sensor has read temperature 222 - timer.delay_ms(35); 188 + //timer.delay_ms(35); 223 189 let mut alarm = timer.alarm_0().unwrap(); 224 190 alarm.enable_interrupt(); 225 191 critical_section::with(|cs| GLOBAL_ALARM0.borrow_ref_mut(cs).replace(alarm)); ··· 250 216 let usb_device = UsbDeviceBuilder::new(bus_ref, UsbVidPid(0x2e8a, 0x000a)) 251 217 .strings(&[StringDescriptors::default() 252 218 .manufacturer("arthomnix") 253 - .product("Touchscreen EPD Input Module for Framework 16") 254 - .serial_number(unsafe { core::str::from_utf8_unchecked(serial_number()) }) 219 + .product("Touchscreen EPD Input Module for Framework 16 [eepyOS]") 220 + .serial_number(get_serial()) 255 221 ]) 256 222 .unwrap() 257 223 .device_class(usbd_serial::USB_CLASS_CDC) ··· 299 265 epd.soft_reset().unwrap(); 300 266 epd.refresh(&image, TEMP.load(Ordering::Relaxed)).unwrap(); 301 267 } 268 + 269 + critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).push(Event::RefreshFinished)); 270 + cortex_m::asm::sev(); 302 271 } 303 272 304 - critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).push(Event::RefreshFinished)); 305 273 REFRESHING.store(false, Ordering::Relaxed); 306 274 } 307 275 }).unwrap(); ··· 313 281 pac::NVIC::unmask(interrupt::SW0_IRQ); 314 282 }; 315 283 316 - /*let program = Programs::new().next(); 317 - if let Some(program) = program { 318 - unsafe { 319 - program.load(); 320 - let pft = ProgramFunctionTable { 321 - write_image, 322 - refresh, 323 - next_event, 324 - set_touch_enabled, 325 - serial_number, 326 - }; 327 - program.entry()(&pft); 328 - } 329 - }*/ 284 + // testing program loading 285 + unsafe { 286 + let prog = slot(1); 287 + debug!("{}", (*prog).name().unwrap()); 288 + debug!("{}", (*prog).version().unwrap()); 289 + ((*prog).entry)(); 290 + } 330 291 331 - // Test syscall 332 - println!("{}", eepy_sys::syscall::get_serial()); 333 - 334 - let draw_target = EpdDrawTarget::new(write_image, refresh); 292 + let draw_target = EpdDrawTarget::new(); 335 293 gui::gui_main(draw_target); 336 294 } 337 295
+7 -9
eepy/src/programs.rs
··· 1 - use core::marker::PhantomData; 2 1 use eepy_sys::header::*; 3 2 4 - pub(crate) const unsafe fn slot<'a>(id: u8) -> &'a ProgramSlotHeader { 3 + pub(crate) const unsafe fn slot(id: u8) -> *const ProgramSlotHeader { 5 4 if id < 1 || id > 31 { 6 5 panic!("slot ID must be between 1 and 31"); 7 6 } 8 7 9 - &*XIP_BASE.add(SLOT_SIZE * id as usize).cast::<ProgramSlotHeader>() 8 + XIP_BASE.add(SLOT_SIZE * id as usize).cast::<ProgramSlotHeader>() 10 9 } 11 10 12 11 13 - pub(crate) struct Programs<'a> { 12 + pub(crate) struct Programs { 14 13 id: u8, 15 - _phantom: PhantomData<&'a ProgramSlotHeader>, 16 14 } 17 15 18 - impl<'a> Programs<'a> { 16 + impl Programs { 19 17 pub(crate) fn new() -> Self { 20 - Self { id: 0, _phantom: PhantomData } 18 + Self { id: 0 } 21 19 } 22 20 } 23 21 24 - impl<'a> Iterator for Programs<'a> { 25 - type Item = &'a ProgramSlotHeader; 22 + impl Iterator for Programs { 23 + type Item = *const ProgramSlotHeader; 26 24 27 25 fn next(&mut self) -> Option<Self::Item> { 28 26 return None;
+4
eepy/src/ringbuffer.rs
··· 57 57 self.read_index = 0; 58 58 self.write_index = 0; 59 59 } 60 + 61 + pub(crate) fn is_empty(&self) -> bool { 62 + self.read_index == self.write_index 63 + } 60 64 }
+2 -2
eepy/src/serial.rs
··· 5 5 use fw16_epd_bsp::hal::usb::UsbBus; 6 6 use fw16_epd_bsp::pac; 7 7 use eepy_sys::header::ProgramSlotHeader; 8 - use eepy_sys::RefreshBlockMode; 9 8 use eepy_serial::{Response, SerialCommand}; 9 + use eepy_sys::image::{refresh, write_image, RefreshBlockMode}; 10 10 use tp370pgh01::IMAGE_BYTES; 11 - use crate::{refresh, write_image, GLOBAL_USB_DEVICE, GLOBAL_USB_SERIAL}; 11 + use crate::{GLOBAL_USB_DEVICE, GLOBAL_USB_SERIAL}; 12 12 13 13 #[derive(Copy, Clone, Debug, defmt::Format)] 14 14 enum SerialState {
+119 -14
eepy/src/syscall.rs
··· 1 - use core::arch::{asm, global_asm}; 2 - use cortex_m_rt::exception; 3 - use defmt::debug; 4 - use defmt::export::panic; 5 - use eepy_sys::syscall::{MiscSyscall, SyscallNumber}; 6 - use crate::SERIAL_NUMBER; 1 + use core::arch::global_asm; 2 + use core::sync::atomic::Ordering; 3 + use defmt::trace; 4 + use eepy_sys::image::{ImageSyscall, RefreshBlockMode}; 5 + use eepy_sys::input::{Event, InputSyscall, ScEventType, TouchEvent, TouchEventType}; 6 + use eepy_sys::misc::MiscSyscall; 7 + use eepy_sys::syscall::SyscallNumber; 8 + use tp370pgh01::IMAGE_BYTES; 9 + use crate::{DO_REFRESH, EVENT_QUEUE, FAST_REFRESH, IMAGE_BUFFER, REFRESHING, SERIAL_NUMBER, TOUCH_ENABLED}; 7 10 8 11 global_asm!(include_str!("syscall.s")); 9 12 ··· 18 21 /// The pc value from the stack can be used to extract the literal operand from the SVC instruction 19 22 /// which triggered the syscall. 20 23 #[no_mangle] 21 - extern "C" fn syscall(sp: *mut usize) { 24 + extern "C" fn handle_syscall(sp: *mut usize) { 22 25 // Stack contains R0, R1, R2, R3, R12, LR, ReturnAddress, xPSR 23 26 let stack_values = unsafe { core::slice::from_raw_parts_mut(sp, 8) }; 24 27 let syscall_num = unsafe { (stack_values[6] as *const u8).sub(2).read() }; 25 28 29 + trace!( 30 + "syscall: sp={} imm={:x} r0={:x} r1={:x} r2={:x} r3={:x} r12={:x} lr={:x} pc={:x} xpsr={:x}", 31 + sp, 32 + syscall_num, 33 + stack_values[0], 34 + stack_values[1], 35 + stack_values[2], 36 + stack_values[3], 37 + stack_values[4], 38 + stack_values[5], 39 + stack_values[6], 40 + stack_values[7], 41 + ); 42 + 26 43 match SyscallNumber::try_from(syscall_num) { 27 - Ok(SyscallNumber::Misc) => match MiscSyscall::try_from(stack_values[0]) { 28 - Ok(MiscSyscall::GetSerial) => { 29 - let serial = SERIAL_NUMBER.get().unwrap(); 30 - stack_values[0] = (&raw const *serial) as usize; 31 - }, 32 - _ => panic!("illegal syscall"), 33 - }, 44 + Ok(SyscallNumber::Misc) => handle_misc(stack_values), 45 + Ok(SyscallNumber::Image) => handle_image(stack_values), 46 + Ok(SyscallNumber::Input) => handle_input(stack_values), 47 + Ok(SyscallNumber::Usb) => todo!("usb syscalls"), 48 + _ => panic!("illegal syscall"), 49 + } 50 + } 51 + 52 + fn handle_misc(stack_values: &mut [usize]) { 53 + match MiscSyscall::try_from(stack_values[0]) { 54 + Ok(MiscSyscall::GetSerial) => handle_get_serial(stack_values), 55 + _ => panic!("illegal syscall"), 56 + } 57 + } 58 + 59 + fn handle_get_serial(stack_values: &mut [usize]) { 60 + stack_values[0] = (&raw const *SERIAL_NUMBER.get().unwrap()) as usize; 61 + } 62 + 63 + fn handle_image(stack_values: &mut [usize]) { 64 + match ImageSyscall::try_from(stack_values[0]) { 65 + Ok(ImageSyscall::WriteImage) => handle_write_image(stack_values), 66 + Ok(ImageSyscall::Refresh) => handle_refresh(stack_values), 67 + _ => panic!("illegal syscall"), 68 + } 69 + } 70 + 71 + fn handle_write_image(stack_values: &mut [usize]) { 72 + let image: &[u8; IMAGE_BYTES] = unsafe { &*(stack_values[1] as *const [u8; IMAGE_BYTES]) }; 73 + critical_section::with(|cs| IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(image)); 74 + } 75 + 76 + fn handle_refresh(stack_values: &mut [usize]) { 77 + let fast_refresh = stack_values[1] != 0; 78 + let blocking_mode = RefreshBlockMode::try_from(stack_values[2]).expect("illegal refresh blocking mode"); 79 + 80 + DO_REFRESH.store(true, Ordering::Relaxed); 81 + FAST_REFRESH.store(fast_refresh, Ordering::Relaxed); 82 + cortex_m::asm::sev(); 83 + 84 + if matches!(blocking_mode, RefreshBlockMode::BlockAcknowledge | RefreshBlockMode::BlockFinish) { 85 + while DO_REFRESH.load(Ordering::Relaxed) {} 86 + } 87 + 88 + if matches!(blocking_mode, RefreshBlockMode::BlockFinish) { 89 + while REFRESHING.load(Ordering::Relaxed) {} 90 + } 91 + } 92 + 93 + fn handle_input(stack_values: &mut [usize]) { 94 + match InputSyscall::try_from(stack_values[0]) { 95 + Ok(InputSyscall::NextEvent) => handle_next_event(stack_values), 96 + Ok(InputSyscall::SetTouchEnabled) => handle_set_touch_enabled(stack_values), 97 + Ok(InputSyscall::HasEvent) => handle_has_event(stack_values), 34 98 _ => panic!("illegal syscall"), 35 99 } 100 + } 101 + 102 + fn handle_next_event(stack_values: &mut [usize]) { 103 + let next_event = critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).pop()); 104 + let mut x = 0; 105 + let mut y = 0; 106 + let sc_ev_type = match next_event { 107 + None => ScEventType::NoEvent, 108 + Some(Event::Touch(TouchEvent { ev_type: TouchEventType::Down, x: px, y: py })) => { 109 + x = px; 110 + y = py; 111 + ScEventType::TouchDown 112 + }, 113 + Some(Event::Touch(TouchEvent { ev_type: TouchEventType::Up, x: px, y: py })) => { 114 + x = px; 115 + y = py; 116 + ScEventType::TouchUp 117 + }, 118 + Some(Event::Touch(TouchEvent { ev_type: TouchEventType::Move, x: px, y: py })) => { 119 + x = px; 120 + y = py; 121 + ScEventType::TouchMove 122 + }, 123 + Some(Event::RefreshFinished) => ScEventType::RefreshFinished, 124 + }; 125 + stack_values[0] = sc_ev_type as usize; 126 + stack_values[1] = x as usize; 127 + stack_values[2] = y as usize; 128 + } 129 + 130 + fn handle_set_touch_enabled(stack_values: &mut [usize]) { 131 + let enable = stack_values[1] != 0; 132 + TOUCH_ENABLED.store(enable, Ordering::Relaxed); 133 + if !enable { 134 + critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).clear()); 135 + } 136 + } 137 + 138 + fn handle_has_event(stack_values: &mut [usize]) { 139 + let empty = critical_section::with(|cs| EVENT_QUEUE.borrow_ref(cs).is_empty()); 140 + stack_values[0] = (!empty) as usize; 36 141 }
+2 -2
eepy/src/syscall.s
··· 7 7 tst r0, r1 8 8 beq .use_msp 9 9 mrs r0, psp 10 - ldr r1, =syscall 10 + ldr r1, =handle_syscall 11 11 bx r1 12 12 .use_msp: 13 13 mrs r0, msp 14 - ldr r1, =syscall 14 + ldr r1, =handle_syscall 15 15 bx r1
+17 -1
elf2epb/src/main.rs
··· 28 28 let mut data_paddr = 0u32; 29 29 let mut data_vaddr = 0u32; 30 30 let mut data_len = 0u32; 31 + let mut current_addr: Option<u32> = None; 31 32 32 33 for phdr in elf.segments().expect("Failed to parse ELF file") { 33 34 if phdr.p_type == PT_LOAD { 34 - bin.extend_from_slice(elf.segment_data(&phdr).expect("Failed to parse ELF file")); 35 + if current_addr.is_none() { 36 + current_addr.replace(phdr.p_vaddr as u32); 37 + } 38 + 39 + let current_addr = current_addr.as_mut().unwrap(); 40 + 41 + if *current_addr < phdr.p_vaddr as u32 { 42 + let diff = phdr.p_vaddr as u32 - *current_addr; 43 + for _ in 0..diff { 44 + bin.push(0); 45 + } 46 + } 47 + 48 + let segment_data = elf.segment_data(&phdr).expect("Failed to parse ELF file"); 49 + bin.extend_from_slice(segment_data); 50 + *current_addr += segment_data.len() as u32; 35 51 } 36 52 37 53 // Assume that there is at most one segment that needs to be copied from flash to RAM