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.

eepy-launcher: implement all serial commands eepytool: initial commit elf2epb: fix padding handling

+361 -135
+1
.idea/epd_firmware.iml
··· 15 15 <sourceFolder url="file://$MODULE_DIR$/eepy-sys/src" isTestSource="false" /> 16 16 <sourceFolder url="file://$MODULE_DIR$/eepy/src" isTestSource="false" /> 17 17 <sourceFolder url="file://$MODULE_DIR$/eepy-launcher/src" isTestSource="false" /> 18 + <sourceFolder url="file://$MODULE_DIR$/eepytool/src" isTestSource="false" /> 18 19 <excludeFolder url="file://$MODULE_DIR$/fw16_epd_bsp/target" /> 19 20 <excludeFolder url="file://$MODULE_DIR$/target" /> 20 21 <excludeFolder url="file://$MODULE_DIR$/fw16-epd-bsp/target" />
+8 -1
Cargo.toml
··· 10 10 "eepy-example-app", 11 11 "elf2epb", 12 12 "pervasive-spi", 13 - "tp370pgh01", "eepy-launcher", 13 + "tp370pgh01", 14 + "eepy-launcher", 15 + "eepytool", 14 16 ] 15 17 16 18 [workspace.dependencies] ··· 33 35 once_cell = { version = "1.20", default-features = false, features = ["critical-section"] } 34 36 postcard = "1.1" 35 37 serde = { version = "1.0", default-features = false, features = ["derive"] } 38 + elf = "0.7" 39 + clap = { version = "4.5", features = ["derive"] } 40 + serialport = "4.7" 41 + tar = "0.4" 42 + zstd = "0.13"
+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::input::{Event, TouchEventType}; 7 + use eepy_sys::input_common::{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::input::Event; 9 + use eepy_sys::input_common::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::input::{Event, TouchEventType}; 5 + use eepy_sys::input_common::{Event, TouchEventType}; 6 6 use crate::draw_target::EpdDrawTarget; 7 7 use crate::element::{Gui, DEFAULT_PRIMITIVE_STYLE}; 8 8
+1
eepy-launcher/Cargo.toml
··· 12 12 usbd-serial.workspace = true 13 13 portable-atomic.workspace = true 14 14 heapless.workspace = true 15 + postcard.workspace = true 15 16 panic-halt = "1.0"
+19 -15
eepy-launcher/src/main.rs
··· 18 18 use eepy_gui::element::slider::Slider; 19 19 use eepy_sys::exec::exec; 20 20 use eepy_sys::image::RefreshBlockMode; 21 - use eepy_sys::input::{has_event, next_event, set_touch_enabled, Event, TouchEventType}; 21 + use eepy_sys::input::{has_event, next_event, set_touch_enabled}; 22 + use eepy_sys::input_common::{Event, TouchEventType}; 22 23 use eepy_sys::header::{ProgramSlotHeader, Programs}; 23 - use eepy_sys::misc::{get_serial}; 24 + use eepy_sys::misc::get_serial; 24 25 use eepy_sys::usb; 25 26 use eepy_sys::usb::UsbBus; 26 27 use usb_device::prelude::*; 27 28 use usbd_serial::SerialPort; 28 - use crate::serial::NEEDS_REFRESH_PROGRAMS; 29 + use crate::serial::{HOST_APP, NEEDS_REFRESH, NEEDS_REFRESH_PROGRAMS}; 29 30 30 31 #[link_section = ".header"] 31 32 #[used] ··· 339 340 draw_target.refresh(false, RefreshBlockMode::BlockAcknowledge); 340 341 341 342 loop { 342 - while let Some(ev) = next_event() { 343 - gui.tick(&mut draw_target, ev); 344 - } 343 + if !HOST_APP.load(Ordering::Relaxed) { 344 + while let Some(ev) = next_event() { 345 + gui.tick(&mut draw_target, ev); 346 + } 345 347 346 - if !has_event() { 347 - // has_event() is a syscall. The SVCall exception is a WFE wakeup event, so we need two 348 - // WFEs so we don't immediately wake up. 349 - unsafe { asm!("wfe", "wfe") }; 350 - } 351 - 352 - if NEEDS_REFRESH_PROGRAMS.swap(false, Ordering::Relaxed) { 353 - gui.main_page.refresh_buttons(); 354 - if gui.current_page == Page::MainPage { 348 + if NEEDS_REFRESH_PROGRAMS.swap(false, Ordering::Relaxed) { 349 + gui.main_page.refresh_buttons(); 350 + if gui.current_page == Page::MainPage { 351 + gui.draw_init(&mut draw_target); 352 + draw_target.refresh(false, RefreshBlockMode::BlockAcknowledge); 353 + } 354 + } else if NEEDS_REFRESH.swap(false, Ordering::Relaxed) { 355 355 gui.draw_init(&mut draw_target); 356 356 draw_target.refresh(false, RefreshBlockMode::BlockAcknowledge); 357 + } else if !has_event() { 358 + // has_event() is a syscall. The SVCall exception is a WFE wakeup event, so we need two 359 + // WFEs so we don't immediately wake up. 360 + unsafe { asm!("wfe", "wfe") }; 357 361 } 358 362 } 359 363 }
+75 -10
eepy-launcher/src/serial.rs
··· 1 1 use core::sync::atomic::Ordering; 2 2 use core::fmt::Write; 3 - use portable_atomic::AtomicBool; 3 + use portable_atomic::{AtomicBool, AtomicU8}; 4 4 use usb_device::device::UsbDevice; 5 5 use usbd_serial::SerialPort; 6 6 use eepy_serial::{Response, SerialCommand}; 7 7 use eepy_sys::flash::{erase_and_program, invalidate_cache}; 8 8 use eepy_sys::image::{refresh, write_image, RefreshBlockMode}; 9 9 use eepy_sys::IMAGE_BYTES; 10 - use eepy_sys::misc::{debug, trace}; 10 + use eepy_sys::input::{next_event, set_touch_enabled}; 11 + use eepy_sys::misc::{debug, info, trace}; 11 12 use eepy_sys::usb::UsbBus; 12 13 use crate::{USB_DEVICE, USB_SERIAL}; 13 14 ··· 38 39 } 39 40 } 40 41 42 + pub(crate) static NEEDS_REFRESH: AtomicBool = AtomicBool::new(false); 41 43 pub(crate) static NEEDS_REFRESH_PROGRAMS: AtomicBool = AtomicBool::new(false); 44 + pub(crate) static HOST_APP: AtomicBool = AtomicBool::new(false); 45 + 46 + static PROG_SLOT: AtomicU8 = AtomicU8::new(0); 42 47 43 48 pub(crate) extern "C" fn usb_handler() { 44 49 trace("USB handler"); ··· 59 64 let buf = unsafe { &mut BUF }; 60 65 61 66 if dev.poll(&mut [serial]) { 67 + let mut s = heapless::String::<100>::new(); 68 + write!(s, "{state:?}").unwrap(); 69 + debug(&s); 70 + 62 71 match state { 63 72 SerialState::ReadyForCommand => { 64 73 let mut cmd_buf = [0u8]; ··· 67 76 return; 68 77 } 69 78 70 - match SerialCommand::try_from(cmd_buf[0]) { 71 - Ok(SerialCommand::RefreshNormal) => *state = SerialState::ReceivingImage { fast_refresh: false, index: 0 }, 72 - Ok(SerialCommand::RefreshFast) => *state = SerialState::ReceivingImage { fast_refresh: true, index: 0 }, 73 - Ok(SerialCommand::UploadProgram) => *state = SerialState::FlashingProgram { index: 0, page: 0, num_pages: None, remainder: None }, 74 - Ok(_) => write_all(serial, &[Response::UnknownCommand as u8]), 75 - Err(_) => write_all(serial, &[Response::UnknownCommand as u8]), 79 + if HOST_APP.load(Ordering::Relaxed) { 80 + match SerialCommand::try_from(cmd_buf[0]) { 81 + Ok(SerialCommand::RefreshNormal) => *state = SerialState::ReceivingImage { fast_refresh: false, index: 0 }, 82 + Ok(SerialCommand::RefreshFast) => *state = SerialState::ReceivingImage { fast_refresh: true, index: 0 }, 83 + Ok(SerialCommand::ExitHostApp) => { 84 + set_touch_enabled(true); 85 + HOST_APP.store(false, Ordering::Relaxed); 86 + NEEDS_REFRESH.store(true, Ordering::Relaxed); 87 + write_all(serial, &[Response::Ack as u8]); 88 + }, 89 + Ok(SerialCommand::NextEvent) => { 90 + write_all(serial, &[Response::Ack as u8]); 91 + write_all(serial, &postcard::to_vec::<_, 32>(&next_event()).unwrap()); 92 + }, 93 + Ok(SerialCommand::EnableTouch) => { 94 + set_touch_enabled(true); 95 + write_all(serial, &[Response::Ack as u8]); 96 + }, 97 + Ok(SerialCommand::DisableTouch) => { 98 + set_touch_enabled(false); 99 + write_all(serial, &[Response::Ack as u8]); 100 + }, 101 + Ok(SerialCommand::EnterHostApp | SerialCommand::GetProgramSlot | SerialCommand::UploadProgram) => { 102 + write_all(serial, &[Response::IncorrectMode as u8]); 103 + } 104 + Err(_) => write_all(serial, &[Response::UnknownCommand as u8]), 105 + } 106 + } else { 107 + match SerialCommand::try_from(cmd_buf[0]) { 108 + Ok(SerialCommand::GetProgramSlot) => { 109 + write_all(serial, &[Response::Ack as u8, 1]); 110 + PROG_SLOT.store(1, Ordering::Relaxed); 111 + }, 112 + Ok(SerialCommand::UploadProgram) => { 113 + if PROG_SLOT.load(Ordering::Relaxed) == 0 { 114 + write_all(serial, &[Response::NoProgramSlot as u8]); 115 + } else { 116 + *state = SerialState::FlashingProgram { index: 0, page: 0, num_pages: None, remainder: None }; 117 + write_all(serial, &[Response::Ack as u8]); 118 + } 119 + }, 120 + Ok(SerialCommand::EnterHostApp) => { 121 + HOST_APP.store(true, Ordering::Relaxed); 122 + write_image(&[0u8; IMAGE_BYTES]); 123 + refresh(false, RefreshBlockMode::NonBlocking); 124 + set_touch_enabled(false); 125 + write_all(serial, &[Response::Ack as u8]); 126 + }, 127 + Ok( 128 + SerialCommand::RefreshNormal 129 + | SerialCommand::RefreshFast 130 + | SerialCommand::ExitHostApp 131 + | SerialCommand::NextEvent 132 + | SerialCommand::DisableTouch 133 + | SerialCommand::EnableTouch 134 + ) => write_all(serial, &[Response::IncorrectMode as u8]), 135 + Err(_) => write_all(serial, &[Response::UnknownCommand as u8]), 136 + } 76 137 } 77 138 } 78 139 } ··· 127 188 // Actually write the flash page 128 189 // TODO: get next slot instead of always using slot 1 129 190 // TODO: wear levelling 191 + debug("writing page"); 130 192 unsafe { write_flash(&buf[4096..8192], 1, *page) }; 131 193 132 194 *page += 1; 133 195 // If this is the last page, also flash the first page which we didn't 134 196 // do at the start 135 197 if *page == num_pages { 198 + debug("finalising"); 136 199 unsafe { write_flash(&buf[0..4096], 1, 0) }; 137 200 201 + debug("invalidating XIP cache"); 138 202 // Invalidate the XIP cache, in case something from the flash area 139 203 // we just wrote is in there 140 - unsafe { invalidate_cache() } 204 + unsafe { invalidate_cache() }; 205 + debug("invalidated XIP cache"); 141 206 142 207 NEEDS_REFRESH_PROGRAMS.store(true, Ordering::Relaxed); 143 - debug("Finished writing program"); 208 + info("Finished writing program"); 144 209 145 210 *state = SerialState::ReadyForCommand; 146 211 }
+2 -1
eepy-serial/Cargo.toml
··· 3 3 version = "0.1.0" 4 4 edition = "2021" 5 5 6 - [dependencies] 6 + [dependencies] 7 + eepy-sys = { path = "../eepy-sys" }
+54 -2
eepy-serial/src/lib.rs
··· 1 1 #![no_std] 2 2 3 + pub use eepy_sys::input_common::Event; 4 + 5 + use core::fmt::{Display, Formatter}; 6 + 3 7 #[repr(u8)] 8 + #[derive(Copy, Clone, Debug, Eq, PartialEq)] 4 9 pub enum SerialCommand { 5 10 /// Refresh the screen. Must be followed by exactly 12480 bytes of image data. 6 11 RefreshNormal = 0, ··· 27 32 /// only available when not in Host App mode. 28 33 GetProgramSlot = 7, 29 34 /// Upload a program. The program will be stored in the slot returned by the previous 30 - /// GetProgramSlot call. The host program should 35 + /// GetProgramSlot call. The program uploaded must be linked correctly for the 36 + /// slot. Only available when not in Host App mode. 31 37 UploadProgram = 8, 32 38 } 33 39 ··· 52 58 } 53 59 54 60 #[repr(u8)] 61 + #[derive(Copy, Clone, Debug, Eq, PartialEq)] 55 62 pub enum Response { 56 63 UnknownCommand = 0x00, 57 64 IncorrectMode = 0x01, ··· 60 67 Ack = 0xff, 61 68 } 62 69 70 + impl From<Response> for Result<(), SerialError> { 71 + fn from(value: Response) -> Self { 72 + match value { 73 + Response::UnknownCommand => Err(SerialError::UnknownCommand), 74 + Response::IncorrectMode => Err(SerialError::IncorrectMode), 75 + Response::ProgramSlotsFull => Err(SerialError::ProgramSlotsFull), 76 + Response::NoProgramSlot => Err(SerialError::NoProgramSlot), 77 + Response::Ack => Ok(()), 78 + } 79 + } 80 + } 81 + 82 + impl Display for Response { 83 + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 84 + match self { 85 + Response::UnknownCommand => write!(f, "Invalid command"), 86 + Response::IncorrectMode => write!(f, "Incorrect mode"), 87 + Response::ProgramSlotsFull => write!(f, "No program slot is available"), 88 + Response::NoProgramSlot => write!(f, "Call GetProgramSlot before UploadProgram"), 89 + Response::Ack => write!(f, "Success"), 90 + } 91 + } 92 + } 93 + 63 94 impl TryFrom<u8> for Response { 64 95 type Error = (); 65 96 ··· 74 105 _ => Err(()), 75 106 } 76 107 } 77 - } 108 + } 109 + 110 + #[derive(Copy, Clone, Debug, Eq, PartialEq)] 111 + pub enum SerialError { 112 + UnknownCommand, 113 + IncorrectMode, 114 + ProgramSlotsFull, 115 + NoProgramSlot, 116 + } 117 + 118 + impl Display for SerialError { 119 + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 120 + match self { 121 + SerialError::UnknownCommand => write!(f, "Invalid command"), 122 + SerialError::IncorrectMode => write!(f, "Incorrect mode"), 123 + SerialError::ProgramSlotsFull => write!(f, "No program slot is available"), 124 + SerialError::NoProgramSlot => write!(f, "Call GetProgramSlot before UploadProgram"), 125 + } 126 + } 127 + } 128 + 129 + impl core::error::Error for SerialError {}
+1 -53
eepy-sys/src/input.rs
··· 1 - use core::fmt::{Display, Formatter}; 2 1 use core::mem::MaybeUninit; 3 2 use crate::{syscall, SafeOption}; 3 + use crate::input_common::Event; 4 4 use crate::syscall::SyscallNumber; 5 5 6 6 #[repr(usize)] ··· 22 22 x if x == InputSyscall::HasEvent as usize => Ok(InputSyscall::HasEvent), 23 23 _ => Err(()), 24 24 } 25 - } 26 - } 27 - 28 - #[repr(C)] 29 - #[cfg_attr(feature = "defmt", derive(defmt::Format))] 30 - #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 31 - pub enum Event { 32 - Touch(TouchEvent), 33 - RefreshFinished, 34 - } 35 - 36 - #[repr(u8)] 37 - #[cfg_attr(feature = "defmt", derive(defmt::Format))] 38 - #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 39 - pub enum TouchEventType { 40 - Down, 41 - Up, 42 - Move, 43 - } 44 - 45 - #[repr(C)] 46 - #[cfg_attr(feature = "defmt", derive(defmt::Format))] 47 - #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 48 - pub struct TouchEvent { 49 - pub ev_type: TouchEventType, 50 - pub x: u16, 51 - pub y: u16, 52 - } 53 - 54 - impl TouchEvent { 55 - pub const fn new() -> Self { 56 - Self { 57 - ev_type: TouchEventType::Down, 58 - x: u16::MAX, 59 - y: u16::MAX, 60 - } 61 - } 62 - 63 - #[cfg(feature = "embedded-graphics")] 64 - pub fn eg_point(&self) -> embedded_graphics::prelude::Point { 65 - embedded_graphics::prelude::Point::new(self.x as i32, self.y as i32) 66 - } 67 - } 68 - 69 - impl Display for TouchEvent { 70 - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 71 - let ty = match self.ev_type { 72 - TouchEventType::Down => "Down", 73 - TouchEventType::Up => "Up", 74 - TouchEventType::Move => "Move", 75 - }; 76 - write!(f, "{ty} @ ({}, {})", self.x, self.y) 77 25 } 78 26 } 79 27
+53
eepy-sys/src/input_common.rs
··· 1 + use core::fmt::{Display, Formatter}; 2 + 3 + #[repr(C)] 4 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 5 + #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 6 + pub enum Event { 7 + Touch(TouchEvent), 8 + RefreshFinished, 9 + } 10 + 11 + #[repr(u8)] 12 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 13 + #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 14 + pub enum TouchEventType { 15 + Down, 16 + Up, 17 + Move, 18 + } 19 + 20 + #[repr(C)] 21 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 22 + #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 23 + pub struct TouchEvent { 24 + pub ev_type: TouchEventType, 25 + pub x: u16, 26 + pub y: u16, 27 + } 28 + 29 + impl TouchEvent { 30 + pub const fn new() -> Self { 31 + Self { 32 + ev_type: TouchEventType::Down, 33 + x: u16::MAX, 34 + y: u16::MAX, 35 + } 36 + } 37 + 38 + #[cfg(feature = "embedded-graphics")] 39 + pub fn eg_point(&self) -> embedded_graphics::prelude::Point { 40 + embedded_graphics::prelude::Point::new(self.x as i32, self.y as i32) 41 + } 42 + } 43 + 44 + impl Display for TouchEvent { 45 + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 46 + let ty = match self.ev_type { 47 + TouchEventType::Down => "Down", 48 + TouchEventType::Up => "Up", 49 + TouchEventType::Move => "Move", 50 + }; 51 + write!(f, "{ty} @ ({}, {})", self.x, self.y) 52 + } 53 + }
+12
eepy-sys/src/lib.rs
··· 1 1 #![no_std] 2 2 3 + #[cfg(all(target_os = "none", target_arch = "arm"))] 3 4 pub mod header; 5 + #[cfg(all(target_os = "none", target_arch = "arm"))] 4 6 pub mod syscall; 7 + #[cfg(all(target_os = "none", target_arch = "arm"))] 5 8 pub mod misc; 9 + #[cfg(all(target_os = "none", target_arch = "arm"))] 6 10 pub mod image; 11 + #[cfg(all(target_os = "none", target_arch = "arm"))] 7 12 pub mod input; 13 + #[cfg(all(target_os = "none", target_arch = "arm"))] 8 14 pub mod usb; 15 + #[cfg(all(target_os = "none", target_arch = "arm"))] 9 16 pub mod exec; 17 + #[cfg(all(target_os = "none", target_arch = "arm"))] 10 18 pub mod critical_section; 19 + #[cfg(all(target_os = "none", target_arch = "arm"))] 11 20 pub mod flash; 21 + pub mod input_common; 22 + 12 23 13 24 #[cfg(feature = "critical-section-impl")] 25 + #[cfg(all(target_os = "none", target_arch = "arm"))] 14 26 mod critical_section_impl; 15 27 16 28 pub use tp370pgh01::IMAGE_BYTES;
+7
eepy-sys/src/syscall.rs
··· 30 30 31 31 /// Perform a raw system call. 32 32 #[macro_export] 33 + #[cfg(all(target_os = "none", target_arch = "arm"))] 33 34 macro_rules! syscall { 34 35 ( 35 36 $syscall_num:expr, ··· 60 61 syscall_num = const $syscall_num as u8, 61 62 ) 62 63 } 64 + } 65 + 66 + #[macro_export] 67 + #[cfg(not(all(target_os = "none", target_arch = "arm")))] 68 + macro_rules! syscall { 69 + ( $( $_foo:tt )* ) => { panic!("Cannot use eepyOS syscalls on non-eepyOS platforms") }; 63 70 }
+2 -46
eepy/src/main.rs
··· 35 35 use fw16_epd_bsp::hal::timer::{Alarm, Alarm0}; 36 36 use fw16_epd_bsp::pac::I2C0; 37 37 use fw16_epd_bsp::pac::interrupt; 38 - use eepy_sys::input::{Event, TouchEvent, TouchEventType}; 38 + use eepy_sys::input_common::{Event, TouchEvent, TouchEventType}; 39 39 use tp370pgh01::rp2040::{Rp2040PervasiveSpiDelays, IoPin}; 40 40 use tp370pgh01::{Tp370pgh01, IMAGE_BYTES}; 41 41 use crate::ringbuffer::RingBuffer; ··· 228 228 229 229 let mut timer = Timer::new(pac.TIMER, &mut pac.RESETS, &clocks); 230 230 // make sure temperature sensor has read temperature 231 - //timer.delay_ms(35); 231 + timer.delay_ms(35); 232 232 let mut alarm = timer.alarm_0().unwrap(); 233 233 alarm.enable_interrupt(); 234 234 critical_section::with(|cs| GLOBAL_ALARM0.borrow_ref_mut(cs).replace(alarm)); ··· 239 239 pac::NVIC::pend(interrupt::TIMER_IRQ_0); 240 240 241 241 usb::init_usb(pac.USBCTRL_REGS, pac.USBCTRL_DPRAM, clocks.usb_clock); 242 - 243 - /* 244 - let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new( 245 - pac.USBCTRL_REGS, 246 - pac.USBCTRL_DPRAM, 247 - clocks.usb_clock, 248 - true, 249 - &mut pac.RESETS, 250 - )); 251 - 252 - unsafe { 253 - GLOBAL_USB_BUS = Some(usb_bus); 254 - } 255 - 256 - // Safety: These are only accessed within this interrupt handler, or in main() before the 257 - // interrupt is enabled. 258 - #[allow(static_mut_refs)] 259 - let bus_ref = unsafe { GLOBAL_USB_BUS.as_ref().unwrap() }; 260 - 261 - let serial = SerialPort::new(bus_ref); 262 - let usb_device = UsbDeviceBuilder::new(bus_ref, UsbVidPid(0x2e8a, 0x000a)) 263 - .strings(&[StringDescriptors::default() 264 - .manufacturer("arthomnix") 265 - .product("Touchscreen EPD Input Module for Framework 16 [eepyOS]") 266 - .serial_number(get_serial()) 267 - ]) 268 - .unwrap() 269 - .device_class(usbd_serial::USB_CLASS_CDC) 270 - .build(); 271 - 272 - unsafe { 273 - GLOBAL_USB_SERIAL = Some(serial); 274 - GLOBAL_USB_DEVICE = Some(usb_device); 275 - } 276 - 277 - unsafe { 278 - core.NVIC.set_priority(interrupt::USBCTRL_IRQ, 0b11000000); 279 - pac::NVIC::unmask(interrupt::USBCTRL_IRQ); 280 - 281 - // FIXME testing 282 - core.NVIC.set_priority(interrupt::SW5_IRQ, 0b11000000); 283 - pac::NVIC::unmask(interrupt::SW5_IRQ); 284 - } 285 - */ 286 242 287 243 unsafe { 288 244 core.NVIC.set_priority(interrupt::USBCTRL_IRQ, 0b11000000);
+4 -1
eepy/src/syscall.rs
··· 168 168 169 169 mod input { 170 170 use core::sync::atomic::Ordering; 171 - use eepy_sys::input::{Event, InputSyscall}; 171 + use eepy_sys::input::InputSyscall; 172 + use eepy_sys::input_common::Event; 172 173 use eepy_sys::SafeOption; 173 174 use crate::{EVENT_QUEUE, TOUCH_ENABLED}; 174 175 use super::StackFrame; ··· 239 240 } 240 241 241 242 mod flash { 243 + use core::arch::asm; 242 244 use core::sync::atomic::Ordering; 243 245 use eepy_sys::flash::FlashSyscall; 244 246 use eepy_sys::header::{SLOT_SIZE, XIP_BASE}; ··· 344 346 let xip = pac::Peripherals::steal().XIP_CTRL; 345 347 xip.flush().write(|w| w.flush().set_bit()); 346 348 xip.flush().read(); 349 + asm!("dsb", "isb"); 347 350 }; 348 351 } 349 352 }
+1 -1
eepy/src/usb.rs
··· 3 3 use core::fmt::{Debug, Formatter}; 4 4 use core::sync::atomic::Ordering; 5 5 use critical_section::Mutex; 6 - use defmt::{debug, trace, warn}; 6 + use defmt::{trace, warn}; 7 7 use portable_atomic::AtomicPtr; 8 8 use eepy_sys::usb::{SafePollResult, SafeUsbError, UsbEpAllocArgs, UsbReadArgs, UsbSyscall, UsbWriteArgs}; 9 9 use fw16_epd_bsp::hal::clocks::UsbClock;
+12
eepytool/Cargo.toml
··· 1 + [package] 2 + name = "eepytool" 3 + version = "0.1.0" 4 + edition = "2021" 5 + 6 + [dependencies] 7 + eepy-serial = { path = "../eepy-serial" } 8 + postcard.workspace = true 9 + clap.workspace = true 10 + serialport.workspace = true 11 + tar.workspace = true 12 + zstd.workspace = true
+103
eepytool/src/main.rs
··· 1 + use std::fs::File; 2 + use std::io::{Read, Write}; 3 + use std::path::PathBuf; 4 + use std::time::Duration; 5 + use clap::{Parser, Subcommand}; 6 + use serialport::SerialPort; 7 + use tar::Archive; 8 + use eepy_serial::{Event, Response, SerialCommand, SerialError}; 9 + 10 + #[derive(Parser, Debug)] 11 + #[command(version, about, long_about = None)] 12 + struct Args { 13 + #[arg(short = 'p', long)] 14 + serial_port: String, 15 + 16 + #[command(subcommand)] 17 + command: Subcommands, 18 + } 19 + 20 + #[derive(Subcommand, Debug)] 21 + enum Subcommands { 22 + EnterHostApp, 23 + ExitHostApp, 24 + 25 + Refresh { 26 + #[arg(long, action)] 27 + fast: bool, 28 + #[arg(short, long)] 29 + image: PathBuf, 30 + }, 31 + 32 + DisableTouch, 33 + EnableTouch, 34 + NextEvent, 35 + 36 + UploadProgram { 37 + package: PathBuf, 38 + }, 39 + } 40 + 41 + use Subcommands::*; 42 + 43 + fn write(serial: &mut Box<dyn SerialPort>, command: SerialCommand, data: &[u8]) -> Result<(), SerialError> { 44 + serial.write_all(&[command as u8]).unwrap(); 45 + serial.write_all(data).unwrap(); 46 + let mut response_buf = [0u8]; 47 + serial.read_exact(&mut response_buf).unwrap(); 48 + Response::try_from(response_buf[0]).unwrap().into() 49 + } 50 + 51 + fn next_event(serial: &mut Box<dyn SerialPort>) -> Result<Option<Event>, SerialError> { 52 + write(serial, SerialCommand::NextEvent, &[])?; 53 + let mut event_buf = [0u8; 32]; 54 + serial.read(&mut event_buf).unwrap(); 55 + Ok(postcard::from_bytes(&event_buf).unwrap()) 56 + } 57 + 58 + fn upload_program(serial: &mut Box<dyn SerialPort>, path: PathBuf) -> Result<(), SerialError> { 59 + write(serial, SerialCommand::GetProgramSlot, &[])?; 60 + let mut slot_n = [0u8]; 61 + serial.read_exact(&mut slot_n).unwrap(); 62 + let slot_n = slot_n[0]; 63 + 64 + let file = File::open(path).unwrap(); 65 + let zstd_reader = zstd::stream::read::Decoder::new(file).unwrap(); 66 + let mut tar = Archive::new(zstd_reader); 67 + for file in tar.entries().unwrap() { 68 + let mut file = file.unwrap(); 69 + if file.path().unwrap().to_str().unwrap().ends_with(&format!(".s{slot_n:02}.epb")) { 70 + println!("Uploading {}", file.path().unwrap().to_str().unwrap()); 71 + let mut buf = vec![0u8; file.size() as usize]; 72 + file.read_exact(&mut buf).unwrap(); 73 + write(serial, SerialCommand::UploadProgram, &buf)?; 74 + return Ok(()); 75 + } 76 + } 77 + 78 + panic!("App package did not contain binary for slot {slot_n}"); 79 + } 80 + 81 + fn main() { 82 + let args = Args::parse(); 83 + 84 + // Baud rate setting doesn't matter for pure USB serial so use 0 85 + let mut port = serialport::new(&args.serial_port, 0) 86 + .timeout(Duration::from_secs(60)) 87 + .open() 88 + .expect(&format!("Failed to open serial port {}", args.serial_port)); 89 + 90 + match args.command { 91 + EnterHostApp => write(&mut port, SerialCommand::EnterHostApp, &[]).unwrap(), 92 + ExitHostApp => write(&mut port, SerialCommand::ExitHostApp, &[]).unwrap(), 93 + Refresh { fast, image } => { 94 + let data = std::fs::read(image).unwrap(); 95 + let cmd = if fast { SerialCommand::RefreshFast } else { SerialCommand::RefreshNormal }; 96 + write(&mut port, cmd, &data).unwrap(); 97 + }, 98 + DisableTouch => write(&mut port, SerialCommand::DisableTouch, &[]).unwrap(), 99 + EnableTouch => write(&mut port, SerialCommand::EnableTouch, &[]).unwrap(), 100 + NextEvent => println!("{:?}", next_event(&mut port).unwrap()), 101 + UploadProgram { package } => upload_program(&mut port, package).unwrap(), 102 + }; 103 + }
+2 -2
elf2epb/Cargo.toml
··· 4 4 edition = "2021" 5 5 6 6 [dependencies] 7 - elf = "0.7" 8 - clap = { version = "4.5", features = ["derive"] } 7 + elf.workspace = true 8 + clap.workspace = true 9 9 crc32fast.workspace = true
+1
elf2epb/src/main.rs
··· 43 43 for _ in 0..diff { 44 44 bin.push(0); 45 45 } 46 + *current_addr = phdr.p_paddr as u32; 46 47 } 47 48 48 49 let segment_data = elf.segment_data(&phdr).expect("Failed to parse ELF file");