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: implement sleep

+180 -43
-1
Cargo.toml
··· 14 14 cortex-m = "0.7" 15 15 cortex-m-rt = "0.7" 16 16 embedded-hal = "1.0" 17 - embedded-hal-bus = "0.2" 18 17 defmt = "0.3" 19 18 defmt-rtt = "0.4" 20 19 panic-probe = { version = "0.3", features = ["print-defmt"] }
+18 -3
fw16-epd-bsp/src/lib.rs
··· 21 21 } 22 22 }, 23 23 24 - Gpio3 { name: epd_touch_rst }, 24 + Gpio3 { 25 + name: epd_touch_rst, 26 + aliases: { 27 + FunctionSioOutput, PullNone: EpdTouchReset 28 + } 29 + }, 25 30 26 31 Gpio4 { 27 32 name: i2c_sda, ··· 37 42 } 38 43 } 39 44 40 - Gpio6 { name: epd_pwr_sw }, 45 + Gpio6 { 46 + name: epd_pwr_sw, 47 + aliases: { 48 + FunctionSioOutput, PullNone: EpdPowerSwitch 49 + } 50 + }, 41 51 42 52 Gpio8 { 43 53 name: epd_busy, ··· 83 93 } 84 94 } 85 95 86 - Gpio25 { name: laptop_sleep }, 96 + Gpio25 { 97 + name: laptop_sleep, 98 + aliases: { 99 + FunctionSioInput, PullUp: LaptopSleep 100 + } 101 + }, 87 102 } 88 103 89 104 pub const XOSC_CRYSTAL_FREQ: u32 = 12_000_000;
+1 -2
fw16-epd-main/Cargo.toml
··· 12 12 cortex-m-rt.workspace = true 13 13 rp2040-flash.workspace = true 14 14 embedded-hal.workspace = true 15 - embedded-hal-bus.workspace = true 16 15 defmt.workspace = true 17 16 defmt-rtt.workspace = true 18 17 panic-probe.workspace = true ··· 23 22 usbd-serial.workspace = true 24 23 crc32fast.workspace = true 25 24 embedded-graphics.workspace = true 26 - heapless.workspace = true 25 + heapless.workspace = true
+3
fw16-epd-main/src/gui.rs
··· 1 1 use core::fmt::Write; 2 + use defmt::debug; 2 3 use embedded_graphics::mono_font::ascii::FONT_10X20; 3 4 use embedded_graphics::mono_font::MonoTextStyle; 4 5 use embedded_graphics::pixelcolor::BinaryColor; ··· 12 13 use crate::{next_touch_event, set_touch_enabled}; 13 14 14 15 pub(crate) fn gui_main(mut draw_target: EpdDrawTarget) -> ! { 16 + debug!("gui_main"); 17 + 15 18 draw_target.refresh(false, true); 16 19 17 20 unsafe { set_touch_enabled(true) };
+158 -37
fw16-epd-main/src/main.rs
··· 12 12 use core::cell::RefCell; 13 13 use critical_section::Mutex; 14 14 use defmt::{debug, info, trace, warn}; 15 - use embedded_hal::digital::{OutputPin, PinState}; 15 + use embedded_hal::digital::{InputPin, OutputPin, PinState}; 16 16 use embedded_hal::i2c::I2c; 17 17 use mcp9808::MCP9808; 18 + use mcp9808::reg_conf::{Configuration, ShutdownMode}; 18 19 use mcp9808::reg_res::ResolutionVal; 19 20 use mcp9808::reg_temp_generic::ReadableTempRegister; 20 21 use portable_atomic::{AtomicBool, AtomicU8}; ··· 22 23 use usb_device::bus::UsbBusAllocator; 23 24 use usb_device::prelude::*; 24 25 use usbd_serial::SerialPort; 25 - use fw16_epd_bsp::{entry, hal, pac, EpdBusy, EpdCs, EpdDc, EpdReset, EpdSck, EpdSdaWrite, EpdTouchInt, I2CScl, I2CSda, Pins}; 26 + use fw16_epd_bsp::{entry, hal, pac, EpdBusy, EpdCs, EpdDc, EpdPowerSwitch, EpdReset, EpdSck, EpdSdaWrite, EpdTouchInt, EpdTouchReset, I2CScl, I2CSda, LaptopSleep, Pins}; 26 27 use fw16_epd_bsp::hal::{Sio, Timer, I2C}; 27 28 use fw16_epd_bsp::hal::clocks::ClockSource; 28 29 use fw16_epd_bsp::hal::fugit::{RateExtU32, ExtU32}; 29 - use fw16_epd_bsp::hal::gpio::Interrupt::EdgeLow; 30 + use fw16_epd_bsp::hal::gpio::Interrupt::{EdgeHigh, EdgeLow}; 30 31 use fw16_epd_bsp::hal::multicore::{Multicore, Stack}; 31 32 use fw16_epd_bsp::hal::timer::{Alarm, Alarm0}; 32 - use fw16_epd_bsp::pac::I2C0; 33 + use fw16_epd_bsp::pac::{CorePeripherals, I2C0}; 33 34 use fw16_epd_bsp::pac::interrupt; 34 35 use fw16_epd_gui::draw_target::EpdDrawTarget; 35 36 use fw16_epd_program_interface::{SafeOption, TouchEvent, TouchEventType}; ··· 41 42 42 43 static GLOBAL_TOUCH_INT_PIN: Mutex<RefCell<Option<EpdTouchInt>>> = Mutex::new(RefCell::new(None)); 43 44 static GLOBAL_I2C: Mutex<RefCell<Option<I2C<I2C0, (I2CSda, I2CScl)>>>> = Mutex::new(RefCell::new(None)); 45 + static GLOBAL_SLEEP_PIN: Mutex<RefCell<Option<LaptopSleep>>> = Mutex::new(RefCell::new(None)); 46 + static GLOBAL_EPD_POWER_PIN: Mutex<RefCell<Option<EpdPowerSwitch>>> = Mutex::new(RefCell::new(None)); 47 + static GLOBAL_TOUCH_RESET_PIN: Mutex<RefCell<Option<EpdTouchReset>>> = Mutex::new(RefCell::new(None)); 44 48 45 49 static GLOBAL_ALARM0: Mutex<RefCell<Option<Alarm0>>> = Mutex::new(RefCell::new(None)); 46 50 47 51 static IMAGE_BUFFER: Mutex<RefCell<[u8; IMAGE_BYTES]>> = Mutex::new(RefCell::new([0; IMAGE_BYTES])); 48 52 static DO_REFRESH: AtomicBool = AtomicBool::new(false); 49 53 static FAST_REFRESH: AtomicBool = AtomicBool::new(false); 54 + static REFRESHING: AtomicBool = AtomicBool::new(false); 55 + static EPD_NEEDS_HARD_RESET: AtomicBool = AtomicBool::new(true); 50 56 static TEMP: AtomicU8 = AtomicU8::new(20); 51 57 52 58 static mut GLOBAL_USB_DEVICE: Option<UsbDevice<hal::usb::UsbBus>> = None; ··· 54 60 static mut GLOBAL_USB_SERIAL: Option<SerialPort<hal::usb::UsbBus>> = None; 55 61 56 62 static TOUCH_EVENT_BUFFER: Mutex<RefCell<TouchEventBuffer>> = Mutex::new(RefCell::new(TouchEventBuffer::new())); 63 + static TOUCH_ENABLED: AtomicBool = AtomicBool::new(false); 57 64 58 65 extern "C" fn write_image(image: &[u8; IMAGE_BYTES]) { 59 66 critical_section::with(|cs| IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(image)); ··· 74 81 } 75 82 76 83 unsafe extern "C" fn set_touch_enabled(enable: bool) { 77 - if enable { 78 - pac::NVIC::unpend(interrupt::IO_IRQ_BANK0); 79 - pac::NVIC::unmask(interrupt::IO_IRQ_BANK0); 80 - } else { 81 - pac::NVIC::mask(interrupt::IO_IRQ_BANK0); 84 + TOUCH_ENABLED.store(enable, Ordering::Relaxed); 85 + if !enable { 82 86 critical_section::with(|cs| TOUCH_EVENT_BUFFER.borrow_ref_mut(cs).clear()); 83 87 } 84 88 } ··· 143 147 #[entry] 144 148 fn main() -> ! { 145 149 let mut pac = pac::Peripherals::take().unwrap(); 150 + let mut core = pac::CorePeripherals::take().unwrap(); 146 151 let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); 147 152 148 153 let clocks = hal::clocks::init_clocks_and_plls( ··· 154 159 &mut pac.RESETS, 155 160 &mut watchdog, 156 161 ).unwrap(); 162 + 163 + core.SCB.set_sleepdeep(); 157 164 158 165 // Read flash unique ID 159 166 cortex_m::interrupt::disable(); ··· 185 192 ); 186 193 187 194 188 - let _power = pins.epd_pwr_sw.into_push_pull_output_in_state(PinState::Low); 195 + let mut epd_power: EpdPowerSwitch = pins.epd_pwr_sw.reconfigure(); 196 + epd_power.set_low().unwrap(); 197 + critical_section::with(|cs| GLOBAL_EPD_POWER_PIN.borrow_ref_mut(cs).replace(epd_power)); 198 + 199 + let mut sleep = pins.laptop_sleep.into_pull_up_input(); 200 + sleep.set_interrupt_enabled(EdgeLow, true); 201 + sleep.set_interrupt_enabled(EdgeHigh, true); 202 + sleep.clear_interrupt(EdgeLow); 203 + sleep.clear_interrupt(EdgeHigh); 204 + critical_section::with(|cs| GLOBAL_SLEEP_PIN.borrow_ref_mut(cs).replace(sleep)); 189 205 190 - let mut touch_reset = pins.epd_touch_rst.into_push_pull_output_in_state(PinState::High); 206 + let mut touch_reset: EpdTouchReset = pins.epd_touch_rst.reconfigure(); 207 + touch_reset.set_high().unwrap(); 191 208 cortex_m::asm::delay(1000000); 192 209 touch_reset.set_low().unwrap(); 193 210 cortex_m::asm::delay(1000000); 194 211 touch_reset.set_high().unwrap(); 195 212 cortex_m::asm::delay(10000000); 213 + critical_section::with(|cs| GLOBAL_TOUCH_RESET_PIN.borrow_ref_mut(cs).replace(touch_reset)); 196 214 197 215 let cs: EpdCs = pins.spi3_epd_cs.reconfigure(); 198 216 let dc: EpdDc = pins.epd_dc.reconfigure(); ··· 204 222 let i2c_sda: I2CSda = pins.i2c_sda.reconfigure(); 205 223 let i2c_scl: I2CScl = pins.i2c_scl.reconfigure(); 206 224 let int: EpdTouchInt = pins.epd_touch_int.reconfigure(); 207 - // Actually unmasking this interrupt is done by calling set_touch_enabled(true) 208 225 int.set_interrupt_enabled(EdgeLow, true); 209 226 210 227 let mut timer = Timer::new(pac.TIMER, &mut pac.RESETS, &clocks); 211 228 let mut alarm = timer.alarm_0().unwrap(); 212 229 alarm.enable_interrupt(); 213 230 critical_section::with(|cs| GLOBAL_ALARM0.borrow_ref_mut(cs).replace(alarm)); 214 - unsafe { pac::NVIC::unmask(interrupt::TIMER_IRQ_0); } 231 + unsafe { 232 + core.NVIC.set_priority(interrupt::TIMER_IRQ_0, 0b10000000); 233 + pac::NVIC::unmask(interrupt::TIMER_IRQ_0); 234 + } 215 235 pac::NVIC::pend(interrupt::TIMER_IRQ_0); 216 236 217 237 let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new( ··· 262 282 unsafe { pac::NVIC::unmask(interrupt::USBCTRL_IRQ) }; 263 283 264 284 let mut epd = Tp370pgh01::new(cs, IoPin::new(sda), sck, dc, busy, rst, timer, Rp2040PervasiveSpiDelays); 265 - epd.hard_reset().unwrap(); 266 285 267 286 let mut prev_image = [0u8; IMAGE_BYTES]; 268 287 let mut image = [0u8; IMAGE_BYTES]; ··· 270 289 loop { 271 290 cortex_m::asm::wfe(); 272 291 292 + if EPD_NEEDS_HARD_RESET.swap(false, Ordering::Relaxed) { 293 + epd.hard_reset().unwrap(); 294 + } 295 + 296 + REFRESHING.store(true, Ordering::Relaxed); 297 + 273 298 if DO_REFRESH.swap(false, Ordering::Relaxed) { 274 299 if FAST_REFRESH.load(Ordering::Relaxed) { 275 300 prev_image.copy_from_slice(&image); ··· 282 307 epd.refresh(&image, TEMP.load(Ordering::Relaxed)).unwrap(); 283 308 } 284 309 } 310 + 311 + REFRESHING.store(false, Ordering::Relaxed); 285 312 } 286 313 }).unwrap(); 287 314 288 - unsafe { set_touch_enabled(false) }; 289 - 315 + unsafe { 316 + core.NVIC.set_priority(interrupt::IO_IRQ_BANK0, 0); 317 + core.NVIC.set_priority(interrupt::SW0_IRQ, 0b01000000); 318 + pac::NVIC::unmask(interrupt::IO_IRQ_BANK0); 319 + pac::NVIC::unmask(interrupt::SW0_IRQ); 320 + }; 290 321 291 322 let draw_target = EpdDrawTarget::new(write_image, refresh); 292 - 293 323 gui::gui_main(draw_target); 294 324 } 295 325 ··· 368 398 #[interrupt] 369 399 fn IO_IRQ_BANK0() { 370 400 static mut TOUCH_INT_PIN: Option<EpdTouchInt> = None; 401 + static mut SLEEP_PIN: Option<LaptopSleep> = None; 402 + static mut EPD_POWER_PIN: Option<EpdPowerSwitch> = None; 403 + static mut TOUCH_RESET_PIN: Option<EpdTouchReset> = None; 371 404 372 405 trace!("IO_IRQ_BANK0"); 373 406 ··· 375 408 critical_section::with(|cs| *TOUCH_INT_PIN = GLOBAL_TOUCH_INT_PIN.borrow(cs).take()); 376 409 } 377 410 411 + if SLEEP_PIN.is_none() { 412 + critical_section::with(|cs| *SLEEP_PIN = GLOBAL_SLEEP_PIN.borrow(cs).take()); 413 + } 414 + 415 + if EPD_POWER_PIN.is_none() { 416 + critical_section::with(|cs| *EPD_POWER_PIN = GLOBAL_EPD_POWER_PIN.borrow(cs).take()); 417 + } 418 + 419 + if TOUCH_RESET_PIN.is_none() { 420 + critical_section::with(|cs| *TOUCH_RESET_PIN = GLOBAL_TOUCH_RESET_PIN.borrow(cs).take()); 421 + } 422 + 378 423 let mut i2c = critical_section::with(|cs| GLOBAL_I2C.borrow(cs).take()); 379 424 380 425 if let Some(pin) = TOUCH_INT_PIN { 381 - if let Some(i2c) = &mut i2c { 382 - let mut buf = [0u8; 9]; 383 - i2c.write_read(0x38u8, &[0x00], &mut buf).unwrap(); 384 - let x = (((buf[3] & 0x0f) as u16) << 8) | buf[4] as u16; 385 - let y = (((buf[5] & 0x0f) as u16) << 8) | buf[6] as u16; 426 + if pin.interrupt_status(EdgeLow) { 427 + 428 + if TOUCH_ENABLED.load(Ordering::Relaxed) { 429 + if let Some(i2c) = &mut i2c { 430 + let mut buf = [0u8; 9]; 431 + i2c.write_read(0x38u8, &[0x00], &mut buf).unwrap(); 432 + let x = (((buf[3] & 0x0f) as u16) << 8) | buf[4] as u16; 433 + let y = (((buf[5] & 0x0f) as u16) << 8) | buf[6] as u16; 434 + 435 + let state = match buf[3] >> 6 { 436 + 0 => TouchEventType::Down, 437 + 1 => TouchEventType::Up, 438 + 2 => TouchEventType::Move, 439 + _ => panic!("received invalid touch event type {}", buf[3] >> 6), 440 + }; 441 + 442 + let event = TouchEvent { 443 + ev_type: state, 444 + x, 445 + y, 446 + }; 447 + 448 + debug!("touch event: {}", event); 449 + 450 + if !critical_section::with(|cs| TOUCH_EVENT_BUFFER.borrow_ref_mut(cs).push(event)) { 451 + warn!("touch event buffer full"); 452 + } 453 + } 454 + } 455 + 456 + } 457 + pin.clear_interrupt(EdgeLow); 458 + } 386 459 387 - let state = match buf[3] >> 6 { 388 - 0 => TouchEventType::Down, 389 - 1 => TouchEventType::Up, 390 - 2 => TouchEventType::Move, 391 - _ => panic!("received invalid touch event type {}", buf[3] >> 6), 392 - }; 460 + if let Some(pin) = SLEEP_PIN { 461 + if pin.interrupt_status(EdgeLow) { 462 + debug!("sleeping"); 463 + pin.clear_interrupt(EdgeLow); 464 + 465 + if let Some(touch_int) = TOUCH_INT_PIN { 466 + touch_int.set_interrupt_enabled(EdgeLow, false); 467 + } 393 468 394 - let event = TouchEvent { 395 - ev_type: state, 396 - x, 397 - y, 398 - }; 469 + // Wait until refresh has finished 470 + while REFRESHING.load(Ordering::Relaxed) {} 399 471 400 - debug!("touch event: {}", event); 472 + // Power down temp sensor 473 + if let Some(i2c) = &mut i2c { 474 + let mut mcp9808 = MCP9808::new(i2c); 475 + let mut config = mcp9808.read_configuration().unwrap(); 476 + config.set_shutdown_mode(ShutdownMode::Shutdown); 477 + mcp9808.write_register(config).unwrap(); 478 + } 401 479 402 - if !critical_section::with(|cs| TOUCH_EVENT_BUFFER.borrow_ref_mut(cs).push(event)) { 403 - warn!("touch event buffer full"); 480 + // Power down display 481 + if let Some(power_pin) = EPD_POWER_PIN { 482 + power_pin.set_high().unwrap(); 404 483 } 484 + 485 + pac::NVIC::pend(interrupt::SW0_IRQ); 405 486 } 406 487 407 - pin.clear_interrupt(EdgeLow); 488 + if pin.interrupt_status(EdgeHigh) { 489 + pin.clear_interrupt(EdgeHigh); 490 + 491 + // Power up temp sensor 492 + if let Some(i2c) = &mut i2c { 493 + let mut mcp9808 = MCP9808::new(i2c); 494 + let mut config = mcp9808.read_configuration().unwrap(); 495 + config.set_shutdown_mode(ShutdownMode::Continuous); 496 + mcp9808.write_register(config).unwrap(); 497 + } 498 + 499 + // Power up EPD 500 + if let Some(power_pin) = EPD_POWER_PIN { 501 + power_pin.set_low().unwrap(); 502 + } 503 + 504 + EPD_NEEDS_HARD_RESET.store(true, Ordering::Relaxed); 505 + 506 + if let Some(reset) = TOUCH_RESET_PIN { 507 + cortex_m::asm::delay(1000000); 508 + reset.set_high().unwrap(); 509 + cortex_m::asm::delay(1000000); 510 + reset.set_low().unwrap(); 511 + cortex_m::asm::delay(1000000); 512 + reset.set_high().unwrap(); 513 + cortex_m::asm::delay(10000000); 514 + } 515 + 516 + if let Some(touch_int) = TOUCH_INT_PIN { 517 + touch_int.clear_interrupt(EdgeLow); 518 + touch_int.set_interrupt_enabled(EdgeLow, true); 519 + } 520 + 521 + debug!("woken up"); 522 + } 408 523 } 409 524 410 525 critical_section::with(|cs| GLOBAL_I2C.borrow(cs).replace(i2c)); 526 + } 527 + 528 + #[interrupt] 529 + fn SW0_IRQ() { 530 + trace!("SW0_IRQ"); 531 + cortex_m::asm::wfi(); 411 532 }