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.

[WIP] program overhaul

+760 -196
.cargo/config.toml fw16-epd-example-app/.cargo/config.toml
+3
.idea/epd_firmware.iml
··· 11 11 <sourceFolder url="file://$MODULE_DIR$/fw16-epd-main/src" isTestSource="false" /> 12 12 <sourceFolder url="file://$MODULE_DIR$/fw16-epd-program-interface/src" isTestSource="false" /> 13 13 <sourceFolder url="file://$MODULE_DIR$/fw16-epd-gui/src" isTestSource="false" /> 14 + <sourceFolder url="file://$MODULE_DIR$/fw16-epd-example-app/src" isTestSource="false" /> 15 + <sourceFolder url="file://$MODULE_DIR$/elf2epb/src" isTestSource="false" /> 16 + <sourceFolder url="file://$MODULE_DIR$/fw16-epd-serial-common/src" isTestSource="false" /> 14 17 <excludeFolder url="file://$MODULE_DIR$/fw16_epd_bsp/target" /> 15 18 <excludeFolder url="file://$MODULE_DIR$/target" /> 16 19 <excludeFolder url="file://$MODULE_DIR$/fw16-epd-bsp/target" />
+7 -5
Cargo.toml
··· 1 1 [workspace] 2 2 resolver = "2" 3 3 4 - members = [ 5 - "fw16-epd-bsp", "fw16-epd-gui", 6 - "fw16-epd-main", "fw16-epd-program-interface", 4 + members = [ "elf2epb", 5 + "fw16-epd-bsp", "fw16-epd-example-app", "fw16-epd-gui", 6 + "fw16-epd-main", "fw16-epd-program-interface", "fw16-epd-serial-common", 7 7 "pervasive-spi", 8 8 "tp370pgh01", 9 9 ] ··· 23 23 usb-device = "0.3" 24 24 usbd-serial = "0.2" 25 25 crc32fast = { version = "1.4", default-features = false } 26 - embedded-graphics = { version = "0.8.1", features = ["defmt"] } 26 + embedded-graphics = "0.8.1" 27 27 heapless = "0.8" 28 - once_cell = { version = "1.20", default-features = false, features = ["critical-section", "portable-atomic"] } 28 + once_cell = { version = "1.20", default-features = false, features = ["critical-section", "portable-atomic"] } 29 + postcard = "1.1" 30 + serde = { version = "1.0", default-features = false, features = ["derive"] }
+9
elf2epb/Cargo.toml
··· 1 + [package] 2 + name = "elf2epb" 3 + version = "0.1.0" 4 + edition = "2021" 5 + 6 + [dependencies] 7 + elf = "0.7" 8 + clap = { version = "4.5", features = ["derive"] } 9 + crc32fast.workspace = true
+69
elf2epb/src/main.rs
··· 1 + use std::path::PathBuf; 2 + use clap::Parser; 3 + use elf::abi::PT_LOAD; 4 + use elf::ElfBytes; 5 + use elf::endian::LittleEndian; 6 + 7 + #[derive(Parser, Debug)] 8 + #[command(version, about, long_about = None)] 9 + struct Args { 10 + #[arg(short)] 11 + in_file: PathBuf, 12 + 13 + #[arg(short)] 14 + out_file: PathBuf, 15 + } 16 + 17 + fn main() { 18 + let args = Args::parse(); 19 + 20 + let elf_data = std::fs::read(args.in_file).expect("Failed to open ELF file"); 21 + 22 + let elf = ElfBytes::<LittleEndian>::minimal_parse(&elf_data) 23 + .expect("Failed to parse ELF file"); 24 + 25 + // Copy all the LOAD segments, like objcopy -O binary 26 + let mut bin: Vec<u8> = Vec::new(); 27 + 28 + let mut data_paddr = 0u32; 29 + let mut data_vaddr = 0u32; 30 + let mut data_len = 0u32; 31 + 32 + for phdr in elf.segments().expect("Failed to parse ELF file") { 33 + if phdr.p_type == PT_LOAD { 34 + bin.extend_from_slice(elf.segment_data(&phdr).expect("Failed to parse ELF file")); 35 + } 36 + 37 + // Assume that there is at most one segment that needs to be copied from flash to RAM 38 + // (i.e. the segment containing .data) 39 + if phdr.p_paddr != phdr.p_vaddr { 40 + data_paddr = phdr.p_paddr as u32; 41 + data_vaddr = phdr.p_vaddr as u32; 42 + data_len = phdr.p_memsz as u32; 43 + } 44 + } 45 + 46 + let mut bss_paddr = 0u32; 47 + let mut bss_len = 0u32; 48 + if let Some(bss) = elf.section_header_by_name(".bss") 49 + .expect("Failed to parse ELF file") 50 + { 51 + bss_paddr = bss.sh_addr as u32; 52 + bss_len = bss.sh_size as u32; 53 + } 54 + 55 + let len = bin.len() as u32; 56 + 57 + // Add missing EPB header entries 58 + bin[8..12].copy_from_slice(&len.to_le_bytes()); // len 59 + bin[12..16].copy_from_slice(&data_len.to_le_bytes()); // data_len 60 + bin[16..20].copy_from_slice(&data_paddr.to_le_bytes()); // data_lma 61 + bin[20..24].copy_from_slice(&data_vaddr.to_le_bytes()); // data_vma 62 + bin[24..28].copy_from_slice(&bss_len.to_le_bytes()); // bss_len 63 + bin[28..32].copy_from_slice(&bss_paddr.to_le_bytes()); // bss_vma 64 + 65 + let crc = crc32fast::hash(&bin[8..]); 66 + bin[4..8].copy_from_slice(&crc.to_le_bytes()); // crc 67 + 68 + std::fs::write(args.out_file, &bin).expect("Failed to write output"); 69 + }
+1
fw16-epd-example-app/.gitignore
··· 1 + out/
+11
fw16-epd-example-app/Cargo.toml
··· 1 + [package] 2 + name = "fw16-epd-example-app" 3 + version = "0.1.0" 4 + edition = "2021" 5 + 6 + [dependencies] 7 + fw16-epd-program-interface = { path = "../fw16-epd-program-interface" } 8 + fw16-epd-gui = { path = "../fw16-epd-gui" } 9 + embedded-graphics.workspace = true 10 + heapless.workspace = true 11 + panic-halt = "1.0"
+22
fw16-epd-example-app/build.rs
··· 1 + use std::env; 2 + use std::path::Path; 3 + 4 + fn main() { 5 + if env::var("TARGET").unwrap() != "thumbv6m-none-eabi" { 6 + return; 7 + } 8 + 9 + let linker_script = std::fs::read_to_string("program.x").unwrap(); 10 + let slot = env::var("EPD_PROG_SLOT").unwrap(); 11 + let out_script = format!("SLOT_N = {slot};\n\n{linker_script}"); 12 + 13 + let out_dir = env::var_os("OUT_DIR").unwrap(); 14 + let out_dir = Path::new(&out_dir); 15 + let out_file = out_dir.join("program.x"); 16 + std::fs::write(out_file, &out_script).unwrap(); 17 + 18 + println!("cargo::rerun-if-changed={}", out_dir.join("program.x").to_str().unwrap()); 19 + println!("cargo::rustc-link-search={}", out_dir.to_str().unwrap()); 20 + println!("cargo::rustc-link-arg=-Tprogram.x"); 21 + println!("cargo::rustc-link-arg=--nmagic"); 22 + }
+13
fw16-epd-example-app/build.sh
··· 1 + #!/bin/sh 2 + 3 + rm -r out 4 + mkdir out 5 + mkdir out/bins 6 + 7 + for slot in `seq -f "%02g" 31` 8 + do 9 + EPD_PROG_SLOT=$slot cargo build --release 10 + elf2epb -i ../target/thumbv6m-none-eabi/release/fw16-epd-example-app -o "out/bins/fw16-epd-example-app.s$slot.epb" 11 + done 12 + 13 + (cd out/bins && tar --zstd -cf ../fw16-epd-example-app.epa *)
+50
fw16-epd-example-app/program.x
··· 1 + EXTERN(entry); 2 + ENTRY(entry); 3 + 4 + MEMORY { 5 + RAM : ORIGIN = 0x20020000, LENGTH = 136K 6 + SLOT : ORIGIN = 0x10000000 + 512K * SLOT_N, LENGTH = 512K 7 + } 8 + 9 + SECTIONS { 10 + .header ORIGIN(SLOT) : { 11 + KEEP(*(.header)); 12 + } > SLOT 13 + 14 + .text : ALIGN(4) { 15 + . = ALIGN(4); 16 + *(.text .text.*); 17 + . = ALIGN(4); 18 + } > SLOT 19 + 20 + .rodata : ALIGN(4) { 21 + . = ALIGN(4); 22 + *(.rodata .rodata.*); 23 + . = ALIGN(4); 24 + } > SLOT 25 + 26 + .data : ALIGN(4) { 27 + . = ALIGN(4); 28 + KEEP(*(.data .data.*)); 29 + . = ALIGN(4); 30 + } > RAM AT>SLOT 31 + 32 + .bss (NOLOAD) : ALIGN(4) { 33 + . = ALIGN(4); 34 + *(.bss .bss.*); 35 + *(COMMON); 36 + . = ALIGN(4); 37 + } > RAM 38 + 39 + .uninit (NOLOAD) : ALIGN(4) { 40 + . = ALIGN(4); 41 + *(.uninit .uninit.*); 42 + . = ALIGN(4); 43 + } > RAM 44 + 45 + /DISCARD/ : { 46 + *(.ARM.exidx); 47 + *(.ARM.exidx.*); 48 + *(.ARM.extab.*); 49 + } 50 + }
+71
fw16-epd-example-app/src/main.rs
··· 1 + #![no_std] 2 + #![no_main] 3 + 4 + extern crate panic_halt; 5 + 6 + use core::fmt::Write; 7 + use embedded_graphics::draw_target::DrawTarget; 8 + use embedded_graphics::Drawable; 9 + use embedded_graphics::pixelcolor::BinaryColor; 10 + use embedded_graphics::prelude::Point; 11 + use embedded_graphics::text::Text; 12 + use heapless::String; 13 + use fw16_epd_gui::draw_target::EpdDrawTarget; 14 + use fw16_epd_gui::element::button::Button; 15 + use fw16_epd_gui::element::{Gui, DEFAULT_TEXT_STYLE}; 16 + use fw16_epd_program_interface::{ProgramFunctionTable, RefreshBlockMode, SafeOption}; 17 + use fw16_epd_program_interface::header::ProgramSlotHeader; 18 + 19 + extern "C" { 20 + static _end: *const u8; 21 + } 22 + 23 + #[link_section = ".header"] 24 + #[used] 25 + static HEADER: ProgramSlotHeader = ProgramSlotHeader::partial( 26 + "Example TESTING FOO BAR BAZ", 27 + env!("CARGO_PKG_VERSION"), 28 + entry, 29 + ); 30 + 31 + #[used] 32 + static mut FOO: u32 = 10; 33 + 34 + #[no_mangle] 35 + pub extern "C" fn entry(pft: &ProgramFunctionTable) { 36 + 37 + unsafe { (pft.set_touch_enabled)(true) }; 38 + 39 + let mut draw_target = EpdDrawTarget::new(pft.write_image, pft.refresh); 40 + 41 + let mut button = Button::with_default_style_auto_sized(Point::new(10, 40), "Click me", true); 42 + button.draw_init(&mut draw_target); 43 + draw_target.refresh(false, RefreshBlockMode::BlockAcknowledge); 44 + 45 + let mut counter = 0; 46 + 47 + loop { 48 + while let SafeOption::Some(ev) = (pft.next_event)() { 49 + let mut needs_refresh = false; 50 + 51 + let response = button.tick(&mut draw_target, ev); 52 + if response.clicked { 53 + draw_target.clear(BinaryColor::Off).unwrap(); 54 + button.draw_init(&mut draw_target); 55 + 56 + counter += 1; 57 + let mut s = String::<16>::new(); 58 + write!(s, "{counter}").unwrap(); 59 + Text::new(&s, Point::new(10, 80), DEFAULT_TEXT_STYLE) 60 + .draw(&mut draw_target) 61 + .unwrap(); 62 + needs_refresh = true; 63 + } 64 + needs_refresh |= response.needs_refresh; 65 + 66 + if needs_refresh { 67 + draw_target.refresh(true, RefreshBlockMode::NonBlocking); 68 + } 69 + } 70 + } 71 + }
+4 -1
fw16-epd-gui/Cargo.toml
··· 7 7 fw16-epd-program-interface = { path = "../fw16-epd-program-interface", features = ["embedded-graphics"] } 8 8 tp370pgh01 = { path = "../tp370pgh01" } 9 9 embedded-graphics.workspace = true 10 - defmt.workspace = true 10 + defmt = { workspace = true, optional = true } 11 + 12 + [features] 13 + defmt-derive = ["defmt", "tp370pgh01/defmt", "embedded-graphics/defmt"]
+4 -3
fw16-epd-gui/src/element/button.rs
··· 1 - use core::fmt::Binary; 2 1 use embedded_graphics::prelude::*; 3 2 use embedded_graphics::mono_font::MonoTextStyle; 4 3 use embedded_graphics::pixelcolor::BinaryColor; ··· 14 13 .baseline(Baseline::Middle) 15 14 .build(); 16 15 17 - #[derive(Copy, Clone, Debug, Eq, PartialEq, Default, defmt::Format)] 16 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 17 + #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] 18 18 pub struct ButtonOutput { 19 19 pub clicked: bool, 20 20 pub needs_refresh: bool, 21 21 } 22 22 23 - #[derive(Debug, defmt::Format)] 23 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 24 + #[derive(Debug)] 24 25 pub struct Button<'a> { 25 26 pub rect: RoundedRectangle, 26 27 pub label: &'a str,
+1 -1
fw16-epd-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 fw16_epd_program_interface::{Event, TouchEvent}; 9 + use fw16_epd_program_interface::Event; 10 10 use tp370pgh01::{DIM_X, DIM_Y}; 11 11 use crate::draw_target::EpdDrawTarget; 12 12
+3
fw16-epd-main/.cargo/config.toml
··· 1 + [build] 2 + target = "thumbv6m-none-eabi" 3 + 1 4 [target.thumbv6m-none-eabi] 2 5 rustflags = [ 3 6 "-C", "link-arg=--nmagic",
+5 -4
fw16-epd-main/Cargo.toml
··· 5 5 6 6 [dependencies] 7 7 fw16-epd-bsp = { path = "../fw16-epd-bsp" } 8 - fw16-epd-program-interface = { path = "../fw16-epd-program-interface" } 9 - fw16-epd-gui = { path = "../fw16-epd-gui" } 10 - tp370pgh01 = { path = "../tp370pgh01", features = ["rp2040"] } 8 + fw16-epd-program-interface = { path = "../fw16-epd-program-interface", features = ["defmt"] } 9 + fw16-epd-gui = { path = "../fw16-epd-gui", features = ["defmt-derive"] } 10 + fw16-epd-serial-common = { path = "../fw16-epd-serial-common" } 11 + tp370pgh01 = { path = "../tp370pgh01", features = ["rp2040", "defmt"] } 11 12 cortex-m.workspace = true 12 13 cortex-m-rt.workspace = true 13 14 rp2040-flash.workspace = true ··· 21 22 usb-device.workspace = true 22 23 usbd-serial.workspace = true 23 24 crc32fast.workspace = true 24 - embedded-graphics.workspace = true 25 + embedded-graphics = { workspace = true, features = ["defmt"] } 25 26 heapless.workspace = true 26 27 once_cell.workspace = true
+1 -1
fw16-epd-main/src/gui.rs
··· 5 5 use embedded_graphics::primitives::{Circle, Line, PrimitiveStyle, Rectangle}; 6 6 use fw16_epd_gui::draw_target::EpdDrawTarget; 7 7 use fw16_epd_gui::element::button::Button; 8 - use fw16_epd_gui::element::{Gui, DEFAULT_PRIMITIVE_STYLE}; 8 + use fw16_epd_gui::element::Gui; 9 9 use fw16_epd_gui::element::slider::Slider; 10 10 use fw16_epd_program_interface::{RefreshBlockMode, SafeOption, Event, TouchEventType}; 11 11 use crate::{next_event, set_touch_enabled};
+43 -90
fw16-epd-main/src/main.rs
··· 3 3 4 4 mod programs; 5 5 mod gui; 6 + mod serial; 7 + mod ringbuffer; 6 8 7 9 #[allow(unused_imports)] 8 10 use panic_probe as _; ··· 13 15 use critical_section::Mutex; 14 16 use defmt::{debug, info, trace, warn}; 15 17 use embedded_hal::delay::DelayNs; 16 - use embedded_hal::digital::{InputPin, OutputPin, PinState}; 18 + use embedded_hal::digital::OutputPin; 17 19 use embedded_hal::i2c::I2c; 18 20 use mcp9808::MCP9808; 19 21 use mcp9808::reg_conf::{Configuration, ShutdownMode}; 20 22 use mcp9808::reg_res::{Resolution, ResolutionVal}; 21 23 use mcp9808::reg_temp_generic::ReadableTempRegister; 22 24 use once_cell::sync::OnceCell; 23 - use portable_atomic::{AtomicBool, AtomicU128, AtomicU8}; 25 + use portable_atomic::{AtomicBool, AtomicU8}; 24 26 use portable_atomic::Ordering; 25 27 use usb_device::bus::UsbBusAllocator; 26 28 use usb_device::prelude::*; ··· 38 40 use fw16_epd_program_interface::{Event, RefreshBlockMode, SafeOption, TouchEvent, TouchEventType}; 39 41 use tp370pgh01::rp2040::{Rp2040PervasiveSpiDelays, IoPin}; 40 42 use tp370pgh01::{Tp370pgh01, IMAGE_BYTES}; 43 + use crate::ringbuffer::RingBuffer; 41 44 //use crate::programs::Programs; 42 45 43 46 static CORE1_STACK: Stack<8192> = Stack::new(); ··· 63 66 64 67 static SERIAL_NUMBER: OnceCell<[u8; 16]> = OnceCell::new(); 65 68 66 - static EVENT_QUEUE: Mutex<RefCell<EventQueue>> = Mutex::new(RefCell::new(EventQueue::new())); 69 + static EVENT_QUEUE: Mutex<RefCell<RingBuffer<Event>>> = Mutex::new(RefCell::new(RingBuffer::new())); 67 70 static TOUCH_ENABLED: AtomicBool = AtomicBool::new(false); 68 71 72 + static FLASHING: AtomicBool = AtomicBool::new(false); 73 + static FLASHING_ACK: AtomicBool = AtomicBool::new(false); 74 + 69 75 extern "C" fn write_image(image: &[u8; IMAGE_BYTES]) { 70 76 critical_section::with(|cs| IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(image)); 71 77 } ··· 99 105 SERIAL_NUMBER.get().unwrap() 100 106 } 101 107 102 - struct EventQueue<const SIZE: usize = 32> { 103 - inner: [Event; SIZE], 104 - read_index: usize, 105 - write_index: usize, 106 - } 108 + /// Function in RAM to be executed by core1 whilst flashing programs (core1 cannot be executing code 109 + /// from flash whilst writing/erasing flash) 110 + /// 111 + /// To exit this function from core0, set [FLASHING] to false and SEV 112 + #[link_section = ".data.ram_func"] 113 + fn core1_flash_wait() { 114 + cortex_m::interrupt::disable(); 115 + FLASHING_ACK.store(true, Ordering::Relaxed); 107 116 108 - impl<const SIZE: usize> EventQueue<SIZE> { 109 - const fn new() -> Self { 110 - Self { 111 - inner: [Event::Null; SIZE], 112 - read_index: 0, 113 - write_index: 0, 114 - } 117 + while FLASHING.load(Ordering::Relaxed) { 118 + cortex_m::asm::wfe(); 115 119 } 116 120 117 - fn wrapping_inc_mut(n: &mut usize) { 118 - if *n == SIZE - 1 { 119 - *n = 0; 120 - } else { 121 - *n += 1; 122 - } 123 - } 124 - 125 - fn wrapping_dec(n: usize) -> usize { 126 - if n == 0 { 127 - SIZE - 1 128 - } else { 129 - n - 1 130 - } 131 - } 132 - 133 - fn pop(&mut self) -> Option<Event> { 134 - if self.read_index == self.write_index { 135 - None 136 - } else { 137 - let res = self.inner[self.read_index]; 138 - Self::wrapping_inc_mut(&mut self.read_index); 139 - Some(res) 140 - } 141 - } 142 - 143 - fn push(&mut self, item: Event) -> bool { 144 - if self.write_index == Self::wrapping_dec(self.read_index) { 145 - false 146 - } else { 147 - self.inner[self.write_index] = item; 148 - Self::wrapping_inc_mut(&mut self.write_index); 149 - true 150 - } 151 - } 152 - 153 - fn clear(&mut self) { 154 - self.read_index = 0; 155 - self.write_index = 0; 156 - } 121 + FLASHING_ACK.store(false, Ordering::Relaxed); 122 + unsafe { cortex_m::interrupt::enable() }; 157 123 } 158 124 159 125 #[entry] ··· 294 260 GLOBAL_USB_DEVICE = Some(usb_device); 295 261 } 296 262 263 + unsafe { pac::NVIC::unmask(interrupt::USBCTRL_IRQ) }; 264 + 297 265 let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio.fifo); 298 266 let core1 = &mut mc.cores()[1]; 299 267 core1.spawn(CORE1_STACK.take().unwrap(), move || { 300 268 info!("core1 init"); 301 - 302 - unsafe { pac::NVIC::unmask(interrupt::USBCTRL_IRQ) }; 303 269 304 270 let mut epd = Tp370pgh01::new(cs, IoPin::new(sda), sck, dc, busy, rst, timer, Rp2040PervasiveSpiDelays); 305 271 ··· 308 274 309 275 loop { 310 276 cortex_m::asm::wfe(); 277 + 278 + if FLASHING.load(Ordering::Relaxed) { 279 + core1_flash_wait(); 280 + continue; 281 + } 311 282 312 283 if EPD_NEEDS_HARD_RESET.swap(false, Ordering::Relaxed) { 313 284 epd.hard_reset().unwrap(); ··· 340 311 pac::NVIC::unmask(interrupt::SW0_IRQ); 341 312 }; 342 313 314 + /*let program = Programs::new().next(); 315 + if let Some(program) = program { 316 + unsafe { 317 + program.load(); 318 + let pft = ProgramFunctionTable { 319 + write_image, 320 + refresh, 321 + next_event, 322 + set_touch_enabled, 323 + serial_number, 324 + }; 325 + program.entry()(&pft); 326 + } 327 + }*/ 328 + 343 329 let draw_target = EpdDrawTarget::new(write_image, refresh); 344 330 gui::gui_main(draw_target); 345 331 } ··· 381 367 } 382 368 383 369 critical_section::with(|cs| GLOBAL_I2C.borrow(cs).replace(i2c)); 384 - } 385 - 386 - #[interrupt] 387 - fn USBCTRL_IRQ() { 388 - static mut INDEX: usize = 0; 389 - 390 - trace!("USBCTRL_IRQ"); 391 - 392 - // Safety: These are only accessed within this interrupt handler, or in main() before the 393 - // interrupt is enabled. 394 - #[allow(static_mut_refs)] 395 - let usb_dev = unsafe { GLOBAL_USB_DEVICE.as_mut().unwrap() }; 396 - #[allow(static_mut_refs)] 397 - let serial = unsafe { GLOBAL_USB_SERIAL.as_mut().unwrap() }; 398 - 399 - if usb_dev.poll(&mut [serial]) { 400 - critical_section::with(|cs| { 401 - let mut buf = IMAGE_BUFFER.borrow_ref_mut(cs); 402 - match serial.read(&mut buf.as_mut()[*INDEX..]) { 403 - Err(UsbError::WouldBlock) => {}, 404 - Err(e) => panic!("{e:?}"), 405 - Ok(count) => { 406 - *INDEX += count; 407 - if *INDEX >= (240 * 416) / 8 { 408 - info!("Finished rx frame over USB"); 409 - *INDEX = 0; 410 - FAST_REFRESH.store(false, Ordering::Relaxed); 411 - DO_REFRESH.store(true, Ordering::Relaxed); 412 - } 413 - } 414 - } 415 - }) 416 - } 417 370 } 418 371 419 372 #[interrupt]
+4 -79
fw16-epd-main/src/programs.rs
··· 1 1 use core::marker::PhantomData; 2 - use core::str::Utf8Error; 3 - use defmt::debug; 4 - use fw16_epd_program_interface::ProgramFunctionTable; 5 - 6 - #[link_section = ".prog1"] 7 - #[used] 8 - static PROG1: [u32; 11] = [ 9 - 0, // block_erase_cycles 10 - 1419452448, // crc 11 - 44, // len 12 - 4, // name_len 13 - 32, // name_offset 14 - 5, // version_len 15 - 36, // version_offset 16 - 0, // entry_offset 17 - 0x74736554, 18 - 0x2E302E30, 19 - 0x00000032, 20 - ]; 21 - 22 - const XIP_BASE: *const u8 = 0x10000000 as *const u8; 23 - 24 - const SLOT_SIZE: usize = 0x80000; 2 + use fw16_epd_program_interface::header::*; 25 3 26 4 pub(crate) const unsafe fn slot<'a>(id: u8) -> &'a ProgramSlotHeader { 27 5 if id < 1 || id > 31 { ··· 31 9 &*XIP_BASE.add(SLOT_SIZE * id as usize).cast::<ProgramSlotHeader>() 32 10 } 33 11 34 - #[repr(C)] 35 - pub(crate) struct ProgramSlotHeader { 36 - pub(crate) block_erase_cycles: usize, 37 - pub(crate) crc: u32, 38 - pub(crate) len: usize, 39 - pub(crate) name_len: usize, 40 - pub(crate) name_offset: usize, 41 - pub(crate) version_len: usize, 42 - pub(crate) version_offset: usize, 43 - pub(crate) entry_offset: usize, 44 - } 45 - 46 - impl ProgramSlotHeader { 47 - fn ptr(&self) -> *const u8 { 48 - (&raw const *self).cast() 49 - } 50 - 51 - pub(crate) fn is_valid_program(&self) -> bool { 52 - self.len != 0 53 - && self.check_crc() 54 - && self.name_offset.saturating_add(self.name_len) <= self.len 55 - && self.version_offset.saturating_add(self.version_len) <= self.len 56 - && self.entry_offset < self.len 57 - && self.name().is_ok() 58 - && self.version_string().is_ok() 59 - } 60 - 61 - pub(crate) fn check_crc(&self) -> bool { 62 - if self.len >= SLOT_SIZE || self.len < size_of::<ProgramSlotHeader>() { 63 - return false; 64 - } 65 - 66 - let ptr = unsafe { self.ptr().add(8) }; 67 - let slice = unsafe { core::slice::from_raw_parts(ptr, self.len - 8) }; 68 - let crc = crc32fast::hash(slice); 69 - crc == self.crc 70 - } 71 - 72 - pub(crate) fn name(&self) -> Result<&str, Utf8Error> { 73 - let ptr = unsafe { self.ptr().add(self.name_offset) }; 74 - let slice = unsafe { core::slice::from_raw_parts(ptr, self.name_len) }; 75 - core::str::from_utf8(slice) 76 - } 77 - 78 - pub(crate) fn version_string(&self) -> Result<&str, Utf8Error> { 79 - let ptr = unsafe { self.ptr().add(self.version_offset) }; 80 - let slice = unsafe { core::slice::from_raw_parts(ptr, self.version_len) }; 81 - core::str::from_utf8(slice) 82 - } 83 - 84 - pub(crate) unsafe fn entry(&self) -> unsafe extern "C" fn(&ProgramFunctionTable) -> () { 85 - let ptr = self.ptr().add(self.entry_offset); 86 - core::mem::transmute(ptr) 87 - } 88 - } 89 12 90 13 pub(crate) struct Programs<'a> { 91 14 id: u8, ··· 102 25 type Item = &'a ProgramSlotHeader; 103 26 104 27 fn next(&mut self) -> Option<Self::Item> { 28 + return None; 29 + /* 105 30 loop { 106 31 self.id += 1; 107 32 ··· 118 43 debug!("No program found in slot {}", self.id); 119 44 } 120 45 121 - } 46 + }*/ 122 47 } 123 48 124 49 fn size_hint(&self) -> (usize, Option<usize>) {
+60
fw16-epd-main/src/ringbuffer.rs
··· 1 + use core::mem::MaybeUninit; 2 + 3 + pub(crate) struct RingBuffer<T, const SIZE: usize = 32> { 4 + inner: [MaybeUninit<T>; SIZE], 5 + read_index: usize, 6 + write_index: usize, 7 + } 8 + 9 + impl<T: Copy, const SIZE: usize> RingBuffer<T, SIZE> { 10 + pub(crate) const fn new() -> Self { 11 + Self { 12 + inner: [MaybeUninit::uninit(); SIZE], 13 + read_index: 0, 14 + write_index: 0, 15 + } 16 + } 17 + 18 + fn wrapping_inc_mut(n: &mut usize) { 19 + if *n == SIZE - 1 { 20 + *n = 0; 21 + } else { 22 + *n += 1; 23 + } 24 + } 25 + 26 + fn wrapping_dec(n: usize) -> usize { 27 + if n == 0 { 28 + SIZE - 1 29 + } else { 30 + n - 1 31 + } 32 + } 33 + 34 + pub(crate) fn pop(&mut self) -> Option<T> { 35 + if self.read_index == self.write_index { 36 + None 37 + } else { 38 + let res = self.inner[self.read_index]; 39 + Self::wrapping_inc_mut(&mut self.read_index); 40 + // Safety: The read_index == write_index check above ensures we cannot pop an 41 + // uninitialised element 42 + unsafe { Some(res.assume_init()) } 43 + } 44 + } 45 + 46 + pub(crate) fn push(&mut self, item: T) -> bool { 47 + if self.write_index == Self::wrapping_dec(self.read_index) { 48 + false 49 + } else { 50 + self.inner[self.write_index] = MaybeUninit::new(item); 51 + Self::wrapping_inc_mut(&mut self.write_index); 52 + true 53 + } 54 + } 55 + 56 + pub(crate) fn clear(&mut self) { 57 + self.read_index = 0; 58 + self.write_index = 0; 59 + } 60 + }
+183
fw16-epd-main/src/serial.rs
··· 1 + use core::sync::atomic::Ordering; 2 + use crate::{interrupt, FLASHING, FLASHING_ACK}; 3 + use defmt::{debug, trace}; 4 + use usbd_serial::SerialPort; 5 + use fw16_epd_bsp::hal::usb::UsbBus; 6 + use fw16_epd_bsp::pac; 7 + use fw16_epd_program_interface::header::ProgramSlotHeader; 8 + use fw16_epd_program_interface::RefreshBlockMode; 9 + use fw16_epd_serial_common::{Response, SerialCommand}; 10 + use tp370pgh01::IMAGE_BYTES; 11 + use crate::{refresh, write_image, GLOBAL_USB_DEVICE, GLOBAL_USB_SERIAL}; 12 + use crate::programs::Programs; 13 + 14 + #[derive(Copy, Clone, Debug, defmt::Format)] 15 + enum SerialState { 16 + ReadyForCommand, 17 + 18 + ReceivingImage { 19 + fast_refresh: bool, 20 + index: usize, 21 + }, 22 + 23 + FlashingProgram { 24 + index: usize, 25 + page: usize, 26 + num_pages: Option<usize>, 27 + remainder: Option<usize>, 28 + }, 29 + } 30 + 31 + fn write_all(serial: &mut SerialPort<UsbBus>, mut buf: &[u8]) { 32 + while !buf.is_empty() { 33 + let _ = serial.write(buf).map(|len| buf = &buf[len..]); 34 + } 35 + } 36 + 37 + /// Safety: 38 + /// 39 + /// This function takes care of the main safety requirements of flashing, but the 40 + /// caller must ensure that the `slot` and `page` parameters are valid and do 41 + /// not produce an address outside the flash's range. Additionally, do not write 42 + /// to slot 0 as this contains the firmware. 43 + unsafe fn write_flash(buf: &[u8], slot: u8, page: usize) { 44 + debug!("Begin write slot {} page {}", slot, page); 45 + 46 + // Make sure core1 is running code from RAM with interrupts disabled 47 + FLASHING.store(true, Ordering::Relaxed); 48 + cortex_m::asm::sev(); 49 + // Wait until core1 has acknowledged that it is now in RAM code 50 + while !FLASHING_ACK.load(Ordering::Relaxed) {} 51 + // Disable interrupts on this core 52 + cortex_m::interrupt::disable(); 53 + 54 + unsafe { 55 + rp2040_flash::flash::flash_range_erase_and_program( 56 + (slot as u32) * 512 * 1024 + (page as u32) * 4096, 57 + buf, 58 + true 59 + ); 60 + } 61 + 62 + // Enable interrupts 63 + unsafe { cortex_m::interrupt::enable() } 64 + // Wake up core1 65 + FLASHING.store(false, Ordering::Relaxed); 66 + cortex_m::asm::sev(); 67 + 68 + debug!("End write slot {} page {}", slot, page); 69 + } 70 + 71 + #[interrupt] 72 + fn USBCTRL_IRQ() { 73 + static mut STATE: SerialState = SerialState::ReadyForCommand; 74 + 75 + // Receive buffer. Size equal to IMAGE_BYTES so it can store an entire frame; also used for 76 + // receiving flash applications. 77 + static mut BUF: [u8; IMAGE_BYTES] = [0; IMAGE_BYTES]; 78 + 79 + trace!("USBCTRL_IRQ"); 80 + 81 + // Safety: These are only accessed within this interrupt handler, or in main() before the 82 + // interrupt is enabled. 83 + #[allow(static_mut_refs)] 84 + let usb_dev = unsafe { GLOBAL_USB_DEVICE.as_mut().unwrap() }; 85 + #[allow(static_mut_refs)] 86 + let serial = unsafe { GLOBAL_USB_SERIAL.as_mut().unwrap() }; 87 + 88 + if usb_dev.poll(&mut [serial]) { 89 + match STATE { 90 + SerialState::ReadyForCommand => { 91 + let mut cmd_buf = [0u8]; 92 + if let Ok(count) = serial.read(&mut cmd_buf) { 93 + if count == 0 { 94 + return; 95 + } 96 + 97 + match SerialCommand::try_from(cmd_buf[0]) { 98 + Ok(SerialCommand::RefreshNormal) => *STATE = SerialState::ReceivingImage { fast_refresh: false, index: 0 }, 99 + Ok(SerialCommand::RefreshFast) => *STATE = SerialState::ReceivingImage { fast_refresh: true, index: 0 }, 100 + Ok(SerialCommand::UploadProgram) => *STATE = SerialState::FlashingProgram { index: 0, page: 0, num_pages: None, remainder: None }, 101 + Ok(_) => write_all(serial, &[Response::UnknownCommand as u8]), 102 + Err(_) => write_all(serial, &[Response::UnknownCommand as u8]), 103 + } 104 + } 105 + } 106 + 107 + SerialState::ReceivingImage { fast_refresh, index } => { 108 + if let Ok(count) = serial.read(&mut BUF[*index..]) { 109 + *index += count; 110 + if *index == IMAGE_BYTES { 111 + write_image(BUF); 112 + refresh(*fast_refresh, RefreshBlockMode::NonBlocking); 113 + write_all(serial, &[Response::Ack as u8]); 114 + *STATE = SerialState::ReadyForCommand; 115 + } 116 + } 117 + } 118 + 119 + SerialState::FlashingProgram { index, page, num_pages, remainder } => { 120 + debug!("Flashing program - page {}", *page); 121 + // Write page 0 last - this is the header, so we only want to write it once everything 122 + // else is written successfully 123 + // Keep page 0 in the first 4096 bytes of BUF for the end 124 + debug!("{} {} {} {}", index, page, num_pages, remainder); 125 + if *page == 0 { 126 + if let Ok(count) = serial.read(&mut BUF[*index..4096]) { 127 + *index += count; 128 + 129 + if num_pages.is_none() && *index >= 12 { 130 + let mut b = [0u8; 4]; 131 + b.copy_from_slice(&BUF[8..12]); 132 + let num_bytes = usize::from_le_bytes(b); 133 + debug!("Program is {} bytes ({} pages) long", num_bytes, num_bytes.div_ceil(4096)); 134 + *num_pages = Some(num_bytes.div_ceil(4096)); 135 + *remainder = Some(num_bytes % 4096); 136 + } 137 + 138 + if *index == 4096 { 139 + *index = 0; 140 + *page += 1; 141 + } 142 + } 143 + } else { 144 + if let Ok(count) = serial.read(&mut BUF[(4096 + *index)..8192]) { 145 + *index += count; 146 + 147 + let num_pages = num_pages.unwrap(); 148 + let remainder = remainder.unwrap(); 149 + if *index == 4096 || (*page == num_pages - 1 && *index == remainder) { 150 + *index = 0; 151 + 152 + // Actually write the flash page 153 + // TODO: get next slot instead of always using slot 1 154 + // TODO: wear levelling 155 + unsafe { write_flash(&BUF[4096..8192], 1, *page) }; 156 + 157 + *page += 1; 158 + // If this is the last page, also flash the first page which we didn't 159 + // do at the start 160 + if *page == num_pages { 161 + unsafe { write_flash(&BUF[0..4096], 1, 0) }; 162 + 163 + // Invalidate the XIP cache, in case something from the flash area 164 + // we just wrote is in there 165 + unsafe { 166 + // FIXME: steal 167 + let xip = pac::Peripherals::steal().XIP_CTRL; 168 + xip.flush().write(|w| w.flush().set_bit()); 169 + xip.flush().read(); 170 + }; 171 + 172 + let program = unsafe { &*(0x10080000 as *const ProgramSlotHeader) }; 173 + debug!("{} {}", program.name().unwrap(), program.version().unwrap()); 174 + 175 + *STATE = SerialState::ReadyForCommand; 176 + } 177 + } 178 + } 179 + } 180 + } 181 + } 182 + } 183 + }
+4 -2
fw16-epd-program-interface/Cargo.toml
··· 5 5 6 6 [dependencies] 7 7 tp370pgh01 = { path = "../tp370pgh01" } 8 - defmt.workspace = true 9 - embedded-graphics = { workspace = true, optional = true } 8 + defmt = { workspace = true, optional = true } 9 + embedded-graphics = { workspace = true, optional = true } 10 + serde.workspace = true 11 + crc32fast.workspace = true
+74
fw16-epd-program-interface/src/header.rs
··· 1 + use core::str::Utf8Error; 2 + use crate::ProgramFunctionTable; 3 + 4 + pub const XIP_BASE: *const u8 = 0x10000000 as *const u8; 5 + pub const PROGRAM_RAM_AREA_BASE: *mut u8 = 0x20020000 as *mut u8; 6 + 7 + pub const SLOT_SIZE: usize = 0x80000; 8 + 9 + #[repr(C)] 10 + pub struct ProgramSlotHeader { 11 + pub block_erase_cycles: usize, 12 + pub crc: u32, 13 + pub len: usize, 14 + 15 + pub data_len: usize, 16 + pub data_lma: *const u8, 17 + pub data_vma: *const u8, 18 + 19 + pub bss_len: usize, 20 + pub bss_vma: *const u8, 21 + 22 + pub name_len: usize, 23 + pub name_ptr: *const u8, 24 + 25 + pub version_len: usize, 26 + pub version_ptr: *const u8, 27 + 28 + pub entry: unsafe extern "C" fn(&ProgramFunctionTable), 29 + } 30 + 31 + unsafe impl Sync for ProgramSlotHeader {} 32 + 33 + impl ProgramSlotHeader { 34 + pub const fn partial(name: &'static str, version: &'static str, entry: unsafe extern "C" fn(&ProgramFunctionTable)) -> Self { 35 + Self { 36 + block_erase_cycles: 0, 37 + crc: 0, 38 + len: 0, 39 + data_len: 0, 40 + data_lma: core::ptr::null(), 41 + data_vma: core::ptr::null(), 42 + bss_len: 0, 43 + bss_vma: core::ptr::null(), 44 + name_len: name.len(), 45 + name_ptr: name.as_ptr(), 46 + version_len: version.len(), 47 + version_ptr: version.as_ptr(), 48 + entry, 49 + } 50 + } 51 + 52 + pub fn check_crc(&self) -> bool { 53 + if self.len >= SLOT_SIZE || self.len < size_of::<ProgramSlotHeader>() { 54 + return false; 55 + } 56 + 57 + let ptr = unsafe { (&raw const *self).cast::<u8>().add(8) }; 58 + let slice = unsafe { core::slice::from_raw_parts(ptr, self.len - 8) }; 59 + let crc = crc32fast::hash(slice); 60 + crc == self.crc 61 + } 62 + 63 + pub fn name(&self) -> Result<&str, Utf8Error> { 64 + unsafe { 65 + core::str::from_utf8(core::slice::from_raw_parts(self.name_ptr, self.name_len)) 66 + } 67 + } 68 + 69 + pub fn version(&self) -> Result<&str, Utf8Error> { 70 + unsafe { 71 + core::str::from_utf8(core::slice::from_raw_parts(self.version_ptr, self.version_len)) 72 + } 73 + } 74 + }
+9 -3
fw16-epd-program-interface/src/lib.rs
··· 1 1 #![no_std] 2 2 3 + pub mod header; 4 + 3 5 use core::fmt::{Display, Formatter}; 4 6 pub use tp370pgh01::IMAGE_BYTES; 5 7 6 8 /// Option type with stable ABI. 7 9 #[repr(C)] 10 + #[derive(Debug, serde::Serialize, serde::Deserialize)] 8 11 pub enum SafeOption<T> { 9 12 None, 10 13 Some(T), ··· 52 55 } 53 56 54 57 #[repr(C)] 55 - #[derive(Copy, Clone, Debug, Eq, PartialEq, defmt::Format)] 58 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 59 + #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 56 60 pub enum Event { 57 61 Null, 58 62 Touch(TouchEvent), ··· 60 64 } 61 65 62 66 #[repr(u8)] 63 - #[derive(Copy, Clone, Debug, Eq, PartialEq, defmt::Format)] 67 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 68 + #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 64 69 pub enum TouchEventType { 65 70 Down, 66 71 Up, ··· 68 73 } 69 74 70 75 #[repr(C)] 71 - #[derive(Copy, Clone, Debug, Eq, PartialEq, defmt::Format)] 76 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 77 + #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 72 78 pub struct TouchEvent { 73 79 pub ev_type: TouchEventType, 74 80 pub x: u16,
+6
fw16-epd-serial-common/Cargo.toml
··· 1 + [package] 2 + name = "fw16-epd-serial-common" 3 + version = "0.1.0" 4 + edition = "2021" 5 + 6 + [dependencies]
+77
fw16-epd-serial-common/src/lib.rs
··· 1 + #![no_std] 2 + 3 + #[repr(u8)] 4 + pub enum SerialCommand { 5 + /// Refresh the screen. Must be followed by exactly 12480 bytes of image data. 6 + RefreshNormal = 0, 7 + /// Fast refresh the screen. Must be followed by exactly 12480 bytes of image data. 8 + RefreshFast = 1, 9 + 10 + /// Enter Host App mode. In this mode, events will not be processed by the builtin UI and can 11 + /// instead be retrieved by host programs using the [SerialCommand::NextEvent] command. This 12 + /// mode should be used by all host programs writing images to the display. 13 + EnterHostApp = 2, 14 + /// Exit Host App mode (see [SerialCommand::EnterHostApp]). 15 + ExitHostApp = 3, 16 + 17 + /// Get the next event. Only works in Host App mode. 18 + NextEvent = 4, 19 + 20 + /// Disable touch. Touch events will no longer be added to the event queue. Only works in Host 21 + /// App mode. 22 + DisableTouch = 5, 23 + /// Enable touch. 24 + EnableTouch = 6, 25 + 26 + /// Get the program slot that will be used to store the next uploaded program. This command is 27 + /// only available when not in Host App mode. 28 + GetProgramSlot = 7, 29 + /// Upload a program. The program will be stored in the slot returned by the previous 30 + /// GetProgramSlot call. The host program should 31 + UploadProgram = 8, 32 + } 33 + 34 + impl TryFrom<u8> for SerialCommand { 35 + type Error = (); 36 + 37 + fn try_from(value: u8) -> Result<Self, Self::Error> { 38 + match value { 39 + x if x == SerialCommand::RefreshNormal as u8 => Ok(SerialCommand::RefreshNormal), 40 + x if x == SerialCommand::RefreshFast as u8 => Ok(SerialCommand::RefreshFast), 41 + x if x == SerialCommand::EnterHostApp as u8 => Ok(SerialCommand::EnterHostApp), 42 + x if x == SerialCommand::ExitHostApp as u8 => Ok(SerialCommand::ExitHostApp), 43 + x if x == SerialCommand::NextEvent as u8 => Ok(SerialCommand::NextEvent), 44 + x if x == SerialCommand::DisableTouch as u8 => Ok(SerialCommand::DisableTouch), 45 + x if x == SerialCommand::EnableTouch as u8 => Ok(SerialCommand::EnableTouch), 46 + x if x == SerialCommand::GetProgramSlot as u8 => Ok(SerialCommand::GetProgramSlot), 47 + x if x == SerialCommand::UploadProgram as u8 => Ok(SerialCommand::UploadProgram), 48 + 49 + _ => Err(()), 50 + } 51 + } 52 + } 53 + 54 + #[repr(u8)] 55 + pub enum Response { 56 + UnknownCommand = 0x00, 57 + IncorrectMode = 0x01, 58 + ProgramSlotsFull = 0x02, 59 + NoProgramSlot = 0x03, 60 + Ack = 0xff, 61 + } 62 + 63 + impl TryFrom<u8> for Response { 64 + type Error = (); 65 + 66 + fn try_from(value: u8) -> Result<Self, Self::Error> { 67 + match value { 68 + x if x == Response::UnknownCommand as u8 => Ok(Response::UnknownCommand), 69 + x if x == Response::IncorrectMode as u8 => Ok(Response::IncorrectMode), 70 + x if x == Response::ProgramSlotsFull as u8 => Ok(Response::ProgramSlotsFull), 71 + x if x == Response::NoProgramSlot as u8 => Ok(Response::NoProgramSlot), 72 + x if x == Response::Ack as u8 => Ok(Response::Ack), 73 + 74 + _ => Err(()), 75 + } 76 + } 77 + }
+2 -1
memory.x
··· 3 3 FLASH : ORIGIN = 0x10000100, LENGTH = 256K - 0x100 4 4 RESERVED: ORIGIN = 0x10040000, LENGTH = 256K 5 5 PROGRAM_SLOT_1: ORIGIN = 0x10080000, LENGTH = 512K 6 - RAM : ORIGIN = 0x20000000, LENGTH = 256K 6 + RAM : ORIGIN = 0x20000000, LENGTH = 128K 7 + PROGRAM_RAM : ORIGIN = 0x20020000, LENGTH = 136K 7 8 } 8 9 9 10 EXTERN(BOOT2_FIRMWARE)
-1
pervasive-spi/Cargo.toml
··· 6 6 7 7 [dependencies] 8 8 embedded-hal.workspace = true 9 - defmt.workspace = true 10 9 rp2040-hal = { workspace = true, optional = true } 11 10 12 11 [features]
-3
pervasive-spi/src/lib.rs
··· 5 5 #[cfg(feature = "rp2040")] 6 6 pub mod rp2040; 7 7 8 - use defmt::trace; 9 8 use embedded_hal::digital::{InputPin, OutputPin, PinState}; 10 9 11 10 /// A trait for pin types that allows running a closure with the pin configured as an input. ··· 157 156 self.sck.set_low()?; 158 157 self.delay.delay_read_after_sck_low(); 159 158 } 160 - 161 - trace!("read byte {}", byte); 162 159 163 160 self.cs.set_high()?; 164 161 self.delay.delay_read_after_byte();
+1 -1
tp370pgh01/Cargo.toml
··· 6 6 [dependencies] 7 7 pervasive-spi = { path = "../pervasive-spi" } 8 8 embedded-hal.workspace = true 9 - defmt.workspace = true 9 + defmt = { workspace = true, optional = true } 10 10 cortex-m = { workspace = true, optional = true } 11 11 12 12 [features]
+23 -1
tp370pgh01/src/lib.rs
··· 5 5 #[cfg(feature = "rp2040")] 6 6 pub mod rp2040; 7 7 8 + #[cfg(feature = "defmt")] 9 + use defmt::{debug, error, trace}; 10 + 8 11 use core::error::Error; 9 12 use core::fmt::{Debug, Display, Formatter}; 10 - use defmt::{debug, error, trace}; 11 13 use embedded_hal::delay::DelayNs; 12 14 use embedded_hal::digital::{InputPin, OutputPin}; 13 15 use pervasive_spi::{PervasiveSpi, PervasiveSpiDelays, WithInput, WithOutput}; ··· 115 117 116 118 /// Hard-reset the display using the reset pin, then perform a soft reset. 117 119 pub fn hard_reset(&mut self) -> Result<(), Tp370pgh01Error<Error>> { 120 + #[cfg(feature = "defmt")] 118 121 debug!("tp370pgh01: hard resetting display"); 119 122 self.spi.set_cs_low()?; 120 123 self.reset.set_high()?; ··· 125 128 self.delay.delay_ms(5); 126 129 self.spi.set_cs_high()?; 127 130 self.soft_reset()?; 131 + #[cfg(feature = "defmt")] 128 132 debug!("tp370pgh01: hard reset display"); 129 133 130 134 Ok(()) ··· 132 136 133 137 /// Perform a soft reset. 134 138 pub fn soft_reset(&mut self) -> Result<(), Tp370pgh01Error<Error>> { 139 + #[cfg(feature = "defmt")] 135 140 debug!("tp370pgh01: soft resetting display"); 136 141 self.spi.write_register(&[0x00, 0x0e])?; 137 142 self.delay.delay_ms(5); 143 + #[cfg(feature = "defmt")] 138 144 debug!("tp370pgh01: soft reset display"); 139 145 Ok(()) 140 146 } ··· 144 150 return Ok(psr); 145 151 } 146 152 153 + #[cfg(feature = "defmt")] 147 154 debug!("tp370pgh01: reading PSR"); 148 155 self.spi.write_register(&[0xa2])?; 149 156 let mut buf = [0u8; 2]; ··· 151 158 self.spi.read(&mut buf)?; 152 159 153 160 let bank0 = buf[1] == 0xa5; 161 + #[cfg(feature = "defmt")] 154 162 debug!( 155 163 "tp370pgh01: PSR is in bank {}", 156 164 if bank0 { '0' } else { '1' } ··· 167 175 let mut buf = [0]; 168 176 self.spi.read(&mut buf)?; 169 177 if buf[0] != 0xa5 { 178 + #[cfg(feature = "defmt")] 170 179 error!("tp370pgh01: failed to find PSR"); 171 180 return Err(Tp370pgh01Error::ReadPsrInvalid); 172 181 } ··· 177 186 } 178 187 179 188 self.spi.read(&mut buf)?; 189 + #[cfg(feature = "defmt")] 180 190 debug!("tp370pgh01: found PSR: {} {}", buf[0], buf[1]); 181 191 182 192 self.psr.replace(buf); ··· 185 195 } 186 196 187 197 fn busy_wait(&mut self) -> Result<(), Tp370pgh01Error<Error>> { 198 + #[cfg(feature = "defmt")] 188 199 trace!("tp370pgh01: busy waiting"); 189 200 while self.busy.is_low()? {} 190 201 Ok(()) 191 202 } 192 203 193 204 fn trigger_refresh(&mut self) -> Result<(), Tp370pgh01Error<Error>> { 205 + #[cfg(feature = "defmt")] 194 206 debug!("tp370pgh01: turning on DC/DC"); 195 207 // turn on DC/DC 196 208 self.spi.write_register(&[0x04])?; 197 209 self.busy_wait()?; 198 210 // refresh 211 + #[cfg(feature = "defmt")] 199 212 debug!("tp370pgh01: refreshing"); 200 213 self.spi.write_register(&[0x12])?; 201 214 self.busy_wait()?; 202 215 // turn off DC/DC 216 + #[cfg(feature = "defmt")] 203 217 debug!("tp370pgh01: turning off DC/DC"); 204 218 self.spi.write_register(&[0x02])?; 205 219 self.busy_wait()?; ··· 220 234 image: &[u8; IMAGE_BYTES], 221 235 temperature: u8, 222 236 ) -> Result<(), Tp370pgh01Error<Error>> { 237 + #[cfg(feature = "defmt")] 223 238 debug!("tp370pgh01: begin normal refresh"); 224 239 225 240 let psr = self.get_psr()?; ··· 233 248 self.spi.write_data(&psr)?; 234 249 235 250 // write image data 251 + #[cfg(feature = "defmt")] 236 252 debug!("tp370pgh01: writing image"); 237 253 self.spi.write_register(&[0x10])?; 238 254 self.spi.write_data_reversed(image)?; ··· 241 257 for _ in 0..IMAGE_BYTES { 242 258 self.spi.write_data(&[0x00])?; 243 259 } 260 + #[cfg(feature = "defmt")] 244 261 debug!("tp370pgh01: finished writing image"); 245 262 246 263 self.trigger_refresh()?; 247 264 265 + #[cfg(feature = "defmt")] 248 266 debug!("tp370pgh01: end normal refresh"); 249 267 Ok(()) 250 268 } ··· 264 282 prev_image: &[u8; IMAGE_BYTES], 265 283 temperature: u8, 266 284 ) -> Result<(), Tp370pgh01Error<Error>> { 285 + #[cfg(feature = "defmt")] 267 286 debug!("tp370pgh01: begin fast refresh"); 268 287 269 288 let psr = self.get_psr()?; ··· 280 299 // set "border setting" 281 300 self.spi.write_register(&[0x50, 0x27])?; 282 301 // send previous image 302 + #[cfg(feature = "defmt")] 283 303 debug!("tp370pgh01: writing image"); 284 304 self.spi.write_register(&[0x10])?; 285 305 self.spi.write_data_reversed(prev_image)?; 286 306 // send new image 287 307 self.spi.write_register(&[0x13])?; 288 308 self.spi.write_data_reversed(image)?; 309 + #[cfg(feature = "defmt")] 289 310 debug!("tp370pgh01: finished writing image"); 290 311 // set "border setting" 291 312 self.spi.write_register(&[0x50, 0x07])?; 292 313 293 314 self.trigger_refresh()?; 294 315 316 + #[cfg(feature = "defmt")] 295 317 debug!("tp370pgh01: end fast refresh"); 296 318 Ok(()) 297 319 }