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.

fw16-epd-main: testing GUI interface

+231 -103
+2 -1
Cargo.toml
··· 24 24 usb-device = "0.3" 25 25 usbd-serial = "0.2" 26 26 crc32fast = { version = "1.4", default-features = false } 27 - embedded-graphics = { version = "0.8.1", features = ["defmt"] } 27 + embedded-graphics = { version = "0.8.1", features = ["defmt"] } 28 + heapless = "0.8"
+3 -2
fw16-epd-main/Cargo.toml
··· 1 1 [package] 2 2 name = "fw16-epd-main" 3 - version = "0.1.0" 3 + version = "0.1.0-alpha" 4 4 edition = "2021" 5 5 6 6 [dependencies] ··· 21 21 usb-device.workspace = true 22 22 usbd-serial.workspace = true 23 23 crc32fast.workspace = true 24 - embedded-graphics.workspace = true 24 + embedded-graphics.workspace = true 25 + heapless.workspace = true
+38
fw16-epd-main/src/gui.rs
··· 1 + use core::fmt::Write; 2 + use defmt::debug; 3 + use embedded_graphics::mono_font::ascii::FONT_10X20; 4 + use embedded_graphics::mono_font::MonoTextStyle; 5 + use embedded_graphics::pixelcolor::BinaryColor; 6 + use embedded_graphics::prelude::*; 7 + use embedded_graphics::text::Text; 8 + use heapless::String; 9 + use fw16_epd_program_interface::eg::EpdDrawTarget; 10 + use fw16_epd_program_interface::{TouchEvent, TouchEventType}; 11 + use crate::{next_touch_event, set_touch_enabled}; 12 + 13 + pub(crate) fn gui_main(mut draw_target: EpdDrawTarget) -> ! { 14 + draw_target.refresh(false); 15 + 16 + unsafe { set_touch_enabled(true) }; 17 + 18 + loop { 19 + let mut touch_event: Option<TouchEvent> = None; 20 + while let Some(ev) = next_touch_event().into() { 21 + debug!("{}", ev); 22 + touch_event = Some(ev); 23 + } 24 + 25 + if let Some(ev) = touch_event { 26 + draw_target.clear(BinaryColor::Off).unwrap(); 27 + 28 + let mut s = String::<32>::new(); 29 + write!(s, "{ev}").unwrap(); 30 + 31 + Text::new(s.as_str(), Point::new(10, 40), MonoTextStyle::new(&FONT_10X20, BinaryColor::On)) 32 + .draw(&mut draw_target) 33 + .unwrap(); 34 + debug!("triggering refresh"); 35 + draw_target.refresh(true); 36 + } 37 + } 38 + }
+109 -86
fw16-epd-main/src/main.rs
··· 2 2 #![no_std] 3 3 4 4 mod programs; 5 + mod gui; 5 6 6 7 #[allow(unused_imports)] 7 8 use panic_probe as _; ··· 10 11 11 12 use core::cell::RefCell; 12 13 use critical_section::Mutex; 13 - use defmt::{debug, error, info, trace}; 14 - use embedded_graphics::mono_font::ascii::FONT_10X20; 15 - use embedded_graphics::mono_font::MonoTextStyle; 16 - use embedded_graphics::pixelcolor::BinaryColor; 17 - use embedded_graphics::prelude::*; 18 - use embedded_graphics::primitives::{Line, PrimitiveStyle}; 19 - use embedded_graphics::text::Text; 14 + use defmt::{debug, info, trace, warn}; 20 15 use embedded_hal::digital::{OutputPin, PinState}; 21 16 use embedded_hal::i2c::I2c; 22 - use embedded_hal_bus::i2c::RefCellDevice; 23 17 use mcp9808::MCP9808; 24 18 use mcp9808::reg_res::ResolutionVal; 25 19 use mcp9808::reg_temp_generic::ReadableTempRegister; ··· 38 32 use fw16_epd_bsp::pac::I2C0; 39 33 use fw16_epd_bsp::pac::interrupt; 40 34 use fw16_epd_program_interface::eg::EpdDrawTarget; 41 - use fw16_epd_program_interface::ProgramFunctionTable; 35 + use fw16_epd_program_interface::{SafeOption, TouchEvent, TouchEventType}; 42 36 use tp370pgh01::rp2040::{Rp2040PervasiveSpiDelays, IoPin}; 43 37 use tp370pgh01::{Tp370pgh01, IMAGE_BYTES}; 44 - use crate::programs::Programs; 38 + //use crate::programs::Programs; 45 39 46 40 static CORE1_STACK: Stack<8192> = Stack::new(); 47 41 ··· 59 53 static mut GLOBAL_USB_BUS: Option<UsbBusAllocator<hal::usb::UsbBus>> = None; 60 54 static mut GLOBAL_USB_SERIAL: Option<SerialPort<hal::usb::UsbBus>> = None; 61 55 56 + static TOUCH_EVENT_BUFFER: Mutex<RefCell<TouchEventBuffer>> = Mutex::new(RefCell::new(TouchEventBuffer::new())); 57 + 62 58 extern "C" fn write_image(image: &[u8; IMAGE_BYTES]) { 63 59 critical_section::with(|cs| IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(image)); 64 60 } 65 61 66 - extern "C" fn refresh() { 62 + extern "C" fn refresh(fast_refresh: bool) { 67 63 DO_REFRESH.store(true, Ordering::Relaxed); 68 - FAST_REFRESH.store(false, Ordering::Relaxed); 64 + FAST_REFRESH.store(fast_refresh, Ordering::Relaxed); 69 65 cortex_m::asm::sev(); 66 + // Wait until the refresh has been initiated 67 + while DO_REFRESH.load(Ordering::Relaxed) {} 70 68 } 71 69 72 - extern "C" fn refresh_fast() { 73 - DO_REFRESH.store(true, Ordering::Relaxed); 74 - FAST_REFRESH.store(true, Ordering::Relaxed); 75 - cortex_m::asm::sev(); 70 + extern "C" fn next_touch_event() -> SafeOption<TouchEvent> { 71 + critical_section::with(|cs| TOUCH_EVENT_BUFFER.borrow_ref_mut(cs).pop()).into() 72 + } 73 + 74 + unsafe extern "C" fn set_touch_enabled(enable: bool) { 75 + if enable { 76 + pac::NVIC::unpend(interrupt::IO_IRQ_BANK0); 77 + pac::NVIC::unmask(interrupt::IO_IRQ_BANK0); 78 + } else { 79 + pac::NVIC::mask(interrupt::IO_IRQ_BANK0); 80 + critical_section::with(|cs| TOUCH_EVENT_BUFFER.borrow_ref_mut(cs).clear()); 81 + } 82 + } 83 + 84 + struct TouchEventBuffer<const SIZE: usize = 32> { 85 + inner: [TouchEvent; SIZE], 86 + read_index: usize, 87 + write_index: usize, 88 + } 89 + 90 + impl<const SIZE: usize> TouchEventBuffer<SIZE> { 91 + const fn new() -> Self { 92 + Self { 93 + inner: [TouchEvent::new(); SIZE], 94 + read_index: 0, 95 + write_index: 0, 96 + } 97 + } 98 + 99 + fn wrapping_inc_mut(n: &mut usize) { 100 + if *n == SIZE - 1 { 101 + *n = 0; 102 + } else { 103 + *n += 1; 104 + } 105 + } 106 + 107 + fn wrapping_dec(n: usize) -> usize { 108 + if n == 0 { 109 + SIZE - 1 110 + } else { 111 + n - 1 112 + } 113 + } 114 + 115 + fn pop(&mut self) -> Option<TouchEvent> { 116 + if self.read_index == self.write_index { 117 + None 118 + } else { 119 + let res = self.inner[self.read_index]; 120 + Self::wrapping_inc_mut(&mut self.read_index); 121 + Some(res) 122 + } 123 + } 124 + 125 + fn push(&mut self, item: TouchEvent) -> bool { 126 + if self.write_index == Self::wrapping_dec(self.read_index) { 127 + false 128 + } else { 129 + self.inner[self.write_index] = item; 130 + Self::wrapping_inc_mut(&mut self.write_index); 131 + true 132 + } 133 + } 134 + 135 + fn clear(&mut self) { 136 + self.read_index = 0; 137 + self.write_index = 0; 138 + } 76 139 } 77 140 78 141 #[entry] ··· 95 158 let mut id = [0u8; 8]; 96 159 unsafe { rp2040_flash::flash::flash_unique_id(&mut id, true) }; 97 160 let mut id = u64::from_be_bytes(id); 98 - info!("Framework 16 EPD firmware version {}, serial no. {:x}", env!("CARGO_PKG_VERSION"), id); 99 161 let mut serial_no = [0u8; 16]; 100 162 for c in serial_no.iter_mut().rev() { 101 163 let nibble = (id & 0x0f) as u8; ··· 109 171 // Safety: this function never returns, so we should be fine right? 110 172 let serial_no: &'static str = unsafe { &*&raw const *core::str::from_utf8(&serial_no).unwrap() }; 111 173 unsafe { cortex_m::interrupt::enable() }; 174 + 175 + info!("Framework 16 EPD firmware version {}, serial no. {}", env!("CARGO_PKG_VERSION"), serial_no); 112 176 113 177 let mut sio = Sio::new(pac.SIO); 114 178 let pins = Pins::new( ··· 139 203 let i2c_sda: I2CSda = pins.i2c_sda.reconfigure(); 140 204 let i2c_scl: I2CScl = pins.i2c_scl.reconfigure(); 141 205 let int: EpdTouchInt = pins.epd_touch_int.reconfigure(); 206 + // Actually unmasking this interrupt is done by calling set_touch_enabled(true) 207 + int.set_interrupt_enabled(EdgeLow, true); 142 208 143 209 let mut timer = Timer::new(pac.TIMER, &mut pac.RESETS, &clocks); 144 210 let mut alarm = timer.alarm_0().unwrap(); ··· 185 251 core1.spawn(CORE1_STACK.take().unwrap(), move || { 186 252 info!("core1 init"); 187 253 188 - int.set_interrupt_enabled(EdgeLow, true); 189 - 190 254 let i2c = hal::i2c::I2C::i2c0(pac.I2C0, i2c_sda, i2c_scl, 400.kHz(), &mut pac.RESETS, clocks.system_clock.get_freq()); 191 255 192 256 critical_section::with(|cs| { ··· 194 258 GLOBAL_I2C.borrow_ref_mut(cs).replace(i2c); 195 259 }); 196 260 197 - unsafe { 198 - pac::NVIC::unmask(interrupt::IO_IRQ_BANK0); 199 - pac::NVIC::unmask(interrupt::USBCTRL_IRQ); 200 - } 261 + unsafe { pac::NVIC::unmask(interrupt::USBCTRL_IRQ) }; 201 262 202 263 let mut epd = Tp370pgh01::new(cs, IoPin::new(sda), sck, dc, busy, rst, timer, Rp2040PervasiveSpiDelays); 203 264 epd.hard_reset().unwrap(); ··· 223 284 } 224 285 }).unwrap(); 225 286 287 + unsafe { set_touch_enabled(false) }; 226 288 227 - let mut draw_target = EpdDrawTarget::new(ProgramFunctionTable { 228 - write_image, 229 - refresh, 230 - refresh_fast, 231 - }); 232 289 233 - Text::new("Hello, World!", Point::new(20, 20), MonoTextStyle::new(&FONT_10X20, BinaryColor::On)) 234 - .draw(&mut draw_target) 235 - .unwrap(); 236 - draw_target.refresh(); 290 + let draw_target = EpdDrawTarget::new(write_image, refresh); 237 291 238 - let programs = Programs::new(); 239 - for _program in programs { 240 - cortex_m::asm::delay(0); 241 - } 242 - 243 - loop { 244 - cortex_m::asm::wfi(); 245 - } 292 + gui::gui_main(draw_target); 246 293 } 247 294 248 295 #[interrupt] ··· 319 366 320 367 #[interrupt] 321 368 fn IO_IRQ_BANK0() { 322 - static mut FIRST_EVENT: bool = true; 323 369 static mut TOUCH_INT_PIN: Option<EpdTouchInt> = None; 324 - static mut PREV_POS: Option<Point> = None; 325 - static mut DRAW_TARGET: EpdDrawTarget = EpdDrawTarget::new(ProgramFunctionTable { 326 - write_image, 327 - refresh, 328 - refresh_fast, 329 - }); 330 370 331 371 trace!("IO_IRQ_BANK0"); 332 372 ··· 336 376 337 377 let mut i2c = critical_section::with(|cs| GLOBAL_I2C.borrow(cs).take()); 338 378 339 - if let Some(i2c) = &mut i2c { 340 - let rc = RefCell::new(i2c); 341 - let mut i2c = RefCellDevice::new(&rc); 379 + if let Some(pin) = TOUCH_INT_PIN { 380 + if let Some(i2c) = &mut i2c { 381 + let mut buf = [0u8; 9]; 382 + i2c.write_read(0x38u8, &[0x00], &mut buf).unwrap(); 383 + let x = (((buf[3] & 0x0f) as u16) << 8) | buf[4] as u16; 384 + let y = (((buf[5] & 0x0f) as u16) << 8) | buf[6] as u16; 342 385 343 - 344 - if *FIRST_EVENT == true { 345 - *FIRST_EVENT = false; 346 - } else if let Some(int) = TOUCH_INT_PIN { 347 - if int.interrupt_status(EdgeLow) { 348 - let mut buf = [0u8; 9]; 349 - i2c.write_read(0x38u8, &[0x00], &mut buf).unwrap(); 350 - let x = (((buf[3] & 0x0f) as i32) << 8) | buf[4] as i32; 351 - let y = (((buf[5] & 0x0f) as i32) << 8) | buf[6] as i32; 352 - debug!("touch event at ({}, {})", x, y); 353 - let pos = Point::new(x, y); 386 + let state = match buf[3] >> 6 { 387 + 0 => TouchEventType::Down, 388 + 1 => TouchEventType::Up, 389 + 2 => TouchEventType::Move, 390 + _ => panic!("received invalid touch event type {}", buf[3] >> 6), 391 + }; 354 392 355 - let state = buf[3] >> 6; 393 + let event = TouchEvent { 394 + ev_type: state, 395 + x, 396 + y, 397 + }; 356 398 357 - if state == 1 && y > 400 { 358 - DRAW_TARGET.clear(BinaryColor::Off).unwrap(); 359 - DRAW_TARGET.refresh(); 360 - } else if state == 1 && y < 20 { 361 - hal::rom_data::reset_to_usb_boot(0, 0); 362 - } else { 363 - if state == 1 || state == 2 { 364 - if let Some(prev) = *PREV_POS { 365 - Line::new(prev, pos) 366 - .into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 5)) 367 - .draw(DRAW_TARGET) 368 - .unwrap(); 369 - DRAW_TARGET.refresh_fast(); 370 - } 371 - } 399 + debug!("touch event: {}", event); 372 400 373 - if state == 0 || state == 2 { 374 - *PREV_POS = Some(Point::new(x, y)); 375 - } 376 - } 377 - int.clear_interrupt(EdgeLow); 401 + if !critical_section::with(|cs| TOUCH_EVENT_BUFFER.borrow_ref_mut(cs).push(event)) { 402 + warn!("touch event buffer full"); 378 403 } 379 - } else { 380 - error!("int pin is None"); 381 404 } 382 - } else { 383 - error!("i2c is None"); 405 + 406 + pin.clear_interrupt(EdgeLow); 384 407 } 385 408 386 409 critical_section::with(|cs| GLOBAL_I2C.borrow(cs).replace(i2c));
+2 -1
fw16-epd-program-interface/Cargo.toml
··· 5 5 6 6 [dependencies] 7 7 tp370pgh01 = { path = "../tp370pgh01" } 8 - embedded-graphics = { workspace = true, optional = true } 8 + embedded-graphics = { workspace = true, optional = true } 9 + defmt.workspace = true
+11 -11
fw16-epd-program-interface/src/eg.rs
··· 8 8 use crate::ProgramFunctionTable; 9 9 10 10 pub struct EpdDrawTarget { 11 - functions: ProgramFunctionTable, 11 + write_image: extern "C" fn(&[u8; IMAGE_BYTES]), 12 + refresh: extern "C" fn(bool), 12 13 buf: [u8; IMAGE_BYTES], 13 14 } 14 15 ··· 55 56 } 56 57 57 58 impl EpdDrawTarget { 58 - pub const fn new(functions: ProgramFunctionTable) -> Self { 59 - Self { functions, buf: [0; IMAGE_BYTES] } 59 + pub const fn new(write_image: extern "C" fn(&[u8; IMAGE_BYTES]), refresh: extern "C" fn(bool)) -> Self { 60 + Self { 61 + write_image, 62 + refresh, 63 + buf: [0; IMAGE_BYTES] 64 + } 60 65 } 61 66 62 - pub fn refresh(&self) { 63 - (self.functions.write_image)(&self.buf); 64 - (self.functions.refresh)(); 65 - } 66 - 67 - pub fn refresh_fast(&self) { 68 - (self.functions.write_image)(&self.buf); 69 - (self.functions.refresh_fast)(); 67 + pub fn refresh(&self, fast_refresh: bool) { 68 + (self.write_image)(&self.buf); 69 + (self.refresh)(fast_refresh); 70 70 } 71 71 }
+66 -2
fw16-epd-program-interface/src/lib.rs
··· 3 3 #[cfg(feature = "embedded-graphics")] 4 4 pub mod eg; 5 5 6 + use core::fmt::{Display, Formatter}; 6 7 pub use tp370pgh01::IMAGE_BYTES; 7 8 9 + /// Option type with stable ABI. 10 + #[repr(C)] 11 + pub enum SafeOption<T> { 12 + None, 13 + Some(T), 14 + } 15 + 16 + impl<T> From<Option<T>> for SafeOption<T> { 17 + fn from(value: Option<T>) -> Self { 18 + match value { 19 + None => SafeOption::None, 20 + Some(v) => SafeOption::Some(v), 21 + } 22 + } 23 + } 24 + 25 + impl<T> From<SafeOption<T>> for Option<T> { 26 + fn from(value: SafeOption<T>) -> Self { 27 + match value { 28 + SafeOption::None => None, 29 + SafeOption::Some(v) => Some(v), 30 + } 31 + } 32 + } 33 + 8 34 #[repr(C)] 9 35 #[derive(Copy, Clone)] 10 36 pub struct ProgramFunctionTable { 11 37 pub write_image: extern "C" fn(&[u8; IMAGE_BYTES]), 12 - pub refresh: extern "C" fn(), 13 - pub refresh_fast: extern "C" fn(), 38 + pub refresh: extern "C" fn(bool), 39 + pub next_touch_event: extern "C" fn() -> SafeOption<TouchEvent>, 40 + pub set_touch_enabled: unsafe extern "C" fn(bool), 41 + } 42 + 43 + #[repr(u8)] 44 + #[derive(Copy, Clone, Debug, defmt::Format)] 45 + pub enum TouchEventType { 46 + Down, 47 + Up, 48 + Move, 49 + } 50 + 51 + #[repr(C)] 52 + #[derive(Copy, Clone, Debug, defmt::Format)] 53 + pub struct TouchEvent { 54 + pub ev_type: TouchEventType, 55 + pub x: u16, 56 + pub y: u16, 57 + } 58 + 59 + impl TouchEvent { 60 + pub const fn new() -> Self { 61 + Self { 62 + ev_type: TouchEventType::Down, 63 + x: u16::MAX, 64 + y: u16::MAX, 65 + } 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 + } 14 78 }