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: basic drawing and USB

+336 -3
+6 -1
Cargo.toml
··· 16 16 embedded-hal-bus = "0.2" 17 17 defmt = "0.3" 18 18 defmt-rtt = "0.4" 19 - panic-probe = { version = "0.3", features = ["print-defmt"] } 19 + panic-probe = { version = "0.3", features = ["print-defmt"] } 20 + critical-section = "1.2" 21 + portable-atomic = { version = "1.10", features = ["critical-section"] } 22 + mcp9808 = "0.4" 23 + usb-device = "0.3" 24 + usbd-serial = "0.2"
+7
fw16-epd-main/.cargo/config.toml
··· 1 + [target.thumbv6m-none-eabi] 2 + rustflags = [ 3 + "-C", "link-arg=--nmagic", 4 + "-C", "link-arg=-Tlink.x", 5 + "-C", "link-arg=-Tdefmt.x", 6 + ] 7 + runner = "probe-rs run --chip RP2040"
+7 -1
fw16-epd-main/Cargo.toml
··· 5 5 6 6 [dependencies] 7 7 fw16-epd-bsp = { path = "../fw16-epd-bsp" } 8 + tp370pgh01 = { path = "../tp370pgh01", features = ["rp2040"] } 8 9 cortex-m.workspace = true 9 10 cortex-m-rt.workspace = true 10 11 embedded-hal.workspace = true 11 12 embedded-hal-bus.workspace = true 12 13 defmt.workspace = true 13 14 defmt-rtt.workspace = true 14 - panic-probe.workspace = true 15 + panic-probe.workspace = true 16 + critical-section.workspace = true 17 + portable-atomic.workspace = true 18 + mcp9808.workspace = true 19 + usb-device.workspace = true 20 + usbd-serial.workspace = true
+301 -1
fw16-epd-main/src/main.rs
··· 6 6 #[allow(unused_imports)] 7 7 use defmt_rtt as _; 8 8 9 - use fw16_epd_bsp::{entry, hal, pac}; 9 + use core::cell::{RefCell, RefMut}; 10 + use critical_section::Mutex; 11 + use defmt::{debug, error, info, trace}; 12 + use embedded_hal::digital::PinState; 13 + use embedded_hal::i2c::I2c; 14 + use embedded_hal_bus::i2c::RefCellDevice; 15 + use mcp9808::MCP9808; 16 + use mcp9808::reg_res::ResolutionVal; 17 + use mcp9808::reg_temp_generic::ReadableTempRegister; 18 + use portable_atomic::{AtomicBool, AtomicU8}; 19 + use portable_atomic::Ordering; 20 + use usb_device::bus::UsbBusAllocator; 21 + use usb_device::prelude::*; 22 + use usbd_serial::SerialPort; 23 + use fw16_epd_bsp::{entry, hal, pac, EpdBusy, EpdCs, EpdDc, EpdReset, EpdSck, EpdSdaRead, EpdTouchInt, I2CScl, I2CSda, Pins}; 24 + use fw16_epd_bsp::hal::{Sio, Timer, I2C}; 25 + use fw16_epd_bsp::hal::clocks::ClockSource; 26 + use fw16_epd_bsp::hal::fugit::RateExtU32; 27 + use fw16_epd_bsp::hal::gpio::Interrupt::EdgeLow; 28 + use fw16_epd_bsp::hal::multicore::{Multicore, Stack}; 29 + use fw16_epd_bsp::pac::I2C0; 30 + use fw16_epd_bsp::pac::interrupt; 31 + use tp370pgh01::rp2040::Rp2040PervasiveSpiDelays; 32 + use tp370pgh01::Tp370pgh01; 33 + 34 + static CORE1_STACK: Stack<8192> = Stack::new(); 35 + 36 + static GLOBAL_TOUCH_INT_PIN: Mutex<RefCell<Option<EpdTouchInt>>> = Mutex::new(RefCell::new(None)); 37 + static GLOBAL_I2C: Mutex<RefCell<Option<I2C<I2C0, (I2CSda, I2CScl)>>>> = Mutex::new(RefCell::new(None)); 38 + 39 + static IMAGE_BUFFER: Mutex<RefCell<[u8; tp370pgh01::IMAGE_BYTES]>> = Mutex::new(RefCell::new([0; tp370pgh01::IMAGE_BYTES])); 40 + static DO_REFRESH: AtomicBool = AtomicBool::new(false); 41 + static FAST_REFRESH: AtomicBool = AtomicBool::new(false); 42 + static TEMP: AtomicU8 = AtomicU8::new(20); 43 + 44 + static mut GLOBAL_USB_DEVICE: Option<UsbDevice<hal::usb::UsbBus>> = None; 45 + static mut GLOBAL_USB_BUS: Option<UsbBusAllocator<hal::usb::UsbBus>> = None; 46 + static mut GLOBAL_USB_SERIAL: Option<SerialPort<hal::usb::UsbBus>> = None; 10 47 11 48 #[entry] 12 49 fn main() -> ! { 13 50 let mut pac = pac::Peripherals::take().unwrap(); 51 + let core = pac::CorePeripherals::take().unwrap(); 14 52 let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); 15 53 16 54 let clocks = hal::clocks::init_clocks_and_plls( ··· 23 61 &mut watchdog, 24 62 ).unwrap(); 25 63 64 + let mut sio = Sio::new(pac.SIO); 65 + let pins = Pins::new( 66 + pac.IO_BANK0, 67 + pac.PADS_BANK0, 68 + sio.gpio_bank0, 69 + &mut pac.RESETS, 70 + ); 71 + 72 + 73 + let _power = pins.epd_pwr_sw.into_push_pull_output_in_state(PinState::Low); 74 + 75 + let cs: EpdCs = pins.spi3_epd_cs.reconfigure(); 76 + let dc: EpdDc = pins.epd_dc.reconfigure(); 77 + let busy: EpdBusy = pins.epd_busy.reconfigure(); 78 + let rst: EpdReset = pins.epd_rst.reconfigure(); 79 + let sda: EpdSdaRead = pins.spi3_epd_sda.reconfigure(); 80 + let sck: EpdSck = pins.spi3_epd_sck.reconfigure(); 81 + 82 + let i2c_sda: I2CSda = pins.i2c_sda.reconfigure(); 83 + let i2c_scl: I2CScl = pins.i2c_scl.reconfigure(); 84 + let int: EpdTouchInt = pins.epd_touch_int.reconfigure(); 85 + 86 + let timer = Timer::new(pac.TIMER, &mut pac.RESETS, &clocks); 87 + 88 + let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new( 89 + pac.USBCTRL_REGS, 90 + pac.USBCTRL_DPRAM, 91 + clocks.usb_clock, 92 + true, 93 + &mut pac.RESETS, 94 + )); 95 + 96 + unsafe { 97 + GLOBAL_USB_BUS = Some(usb_bus); 98 + } 99 + 100 + let bus_ref = unsafe { GLOBAL_USB_BUS.as_ref().unwrap() }; 101 + 102 + let serial = SerialPort::new(bus_ref); 103 + let usb_device = UsbDeviceBuilder::new(bus_ref, UsbVidPid(0x2e8a, 0x000a)) 104 + .strings(&[StringDescriptors::default() 105 + .manufacturer("arthomnix") 106 + .product("Touchscreen EPD Input Module for Framework 16") 107 + ]) 108 + .unwrap() 109 + .device_class(usbd_serial::USB_CLASS_CDC) 110 + .build(); 111 + 112 + unsafe { 113 + GLOBAL_USB_SERIAL = Some(serial); 114 + GLOBAL_USB_DEVICE = Some(usb_device); 115 + } 116 + 117 + let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio.fifo); 118 + let core1 = &mut mc.cores()[1]; 119 + core1.spawn(CORE1_STACK.take().unwrap(), move || { 120 + int.set_interrupt_enabled(EdgeLow, true); 121 + 122 + let i2c = hal::i2c::I2C::i2c0(pac.I2C0, i2c_sda, i2c_scl, 400.kHz(), &mut pac.RESETS, clocks.system_clock.get_freq()); 123 + 124 + critical_section::with(|cs| { 125 + GLOBAL_TOUCH_INT_PIN.borrow_ref_mut(cs).replace(int); 126 + GLOBAL_I2C.borrow_ref_mut(cs).replace(i2c); 127 + }); 128 + 129 + unsafe { 130 + pac::NVIC::unmask(interrupt::IO_IRQ_BANK0); 131 + pac::NVIC::unmask(interrupt::USBCTRL_IRQ); 132 + } 133 + 134 + let mut epd = Tp370pgh01::new(cs, Some(sda), sck, dc, busy, rst, timer, Rp2040PervasiveSpiDelays); 135 + epd.hard_reset().unwrap(); 136 + 137 + let mut prev_image = [0u8; tp370pgh01::IMAGE_BYTES]; 138 + let mut image = [0u8; tp370pgh01::IMAGE_BYTES]; 139 + 140 + loop { 141 + cortex_m::asm::wfe(); 142 + 143 + if DO_REFRESH.swap(false, Ordering::Relaxed) { 144 + if FAST_REFRESH.load(Ordering::Relaxed) { 145 + prev_image.copy_from_slice(&image); 146 + critical_section::with(|cs| image.copy_from_slice(IMAGE_BUFFER.borrow_ref(cs).as_ref())); 147 + epd.soft_reset().unwrap(); 148 + epd.refresh_fast(&image, &prev_image, TEMP.load(Ordering::Relaxed)).unwrap(); 149 + } else { 150 + critical_section::with(|cs| image.copy_from_slice(IMAGE_BUFFER.borrow_ref(cs).as_ref())); 151 + epd.soft_reset().unwrap(); 152 + epd.refresh(&image, TEMP.load(Ordering::Relaxed)).unwrap(); 153 + } 154 + } 155 + } 156 + }).unwrap(); 157 + 26 158 loop { 27 159 cortex_m::asm::wfi(); 160 + } 161 + } 162 + 163 + fn plot(data: &mut RefMut<[u8; 12480]>, x: u16, y: u16) { 164 + let bit_index = (y as usize) * 240 + (x as usize); 165 + let i = bit_index / 8; 166 + let j = 1 << (bit_index % 8); 167 + 168 + let val = &mut data[i]; 169 + *val |= j; 170 + } 171 + 172 + fn plot_line_low(data: &mut RefMut<[u8; 12480]>, x0: i16, y0: i16, x1: i16, y1: i16) { 173 + let dx = x1 - x0; 174 + let mut dy = y1 - y0; 175 + let mut yi = 1; 176 + if dy < 0 { 177 + yi = -1; 178 + dy = -dy; 179 + } 180 + let mut d = (2 * dy) - dx; 181 + let mut y = y0; 182 + 183 + for x in x0..=x1 { 184 + plot(data, x as u16, y as u16); 185 + if d > 0 { 186 + y += yi; 187 + d += 2 * (dy - dx); 188 + } else { 189 + d += 2 * dy; 190 + } 191 + } 192 + } 193 + 194 + fn plot_line_high(data: &mut RefMut<[u8; 12480]>, x0: i16, y0: i16, x1: i16, y1: i16) { 195 + let mut dx = x1 - x0; 196 + let dy = y1 - y0; 197 + let mut xi = 1; 198 + if dx < 0 { 199 + xi = -1; 200 + dx = -dx; 201 + } 202 + let mut d = (2 * dx) - dy; 203 + let mut x = x0; 204 + 205 + for y in y0..=y1 { 206 + plot(data, x as u16, y as u16); 207 + if d > 0 { 208 + x += xi; 209 + d += 2 * (dx - dy); 210 + } else { 211 + d += 2 * dx; 212 + } 213 + } 214 + } 215 + 216 + fn plot_line(data: &mut RefMut<[u8; 12480]>, x0: u16, y0: u16, x1: u16, y1: u16) { 217 + if y1.abs_diff(y0) < x1.abs_diff(x0) { 218 + if x0 > x1 { 219 + plot_line_low(data, x1 as i16, y1 as i16, x0 as i16, y0 as i16); 220 + } else { 221 + plot_line_low(data, x0 as i16, y0 as i16, x1 as i16, y1 as i16); 222 + } 223 + } else { 224 + if y0 > y1 { 225 + plot_line_high(data, x1 as i16, y1 as i16, x0 as i16, y0 as i16); 226 + } else { 227 + plot_line_high(data, x0 as i16, y0 as i16, x1 as i16, y1 as i16); 228 + } 229 + } 230 + } 231 + 232 + #[interrupt] 233 + fn USBCTRL_IRQ() { 234 + static mut INDEX: usize = 0; 235 + 236 + trace!("USBCTRL_IRQ"); 237 + 238 + let usb_dev = unsafe { GLOBAL_USB_DEVICE.as_mut().unwrap() }; 239 + let serial = unsafe { GLOBAL_USB_SERIAL.as_mut().unwrap() }; 240 + 241 + if usb_dev.poll(&mut [serial]) { 242 + critical_section::with(|cs| { 243 + let mut buf = IMAGE_BUFFER.borrow_ref_mut(cs); 244 + match serial.read(&mut buf.as_mut()[*INDEX..]) { 245 + Err(UsbError::WouldBlock) => {}, 246 + Err(e) => panic!("{e:?}"), 247 + Ok(count) => { 248 + *INDEX += count; 249 + if *INDEX >= (240 * 416) / 8 { 250 + info!("Finished rx frame over USB"); 251 + *INDEX = 0; 252 + FAST_REFRESH.store(false, Ordering::Relaxed); 253 + DO_REFRESH.store(true, Ordering::Relaxed); 254 + } 255 + } 256 + } 257 + }) 258 + } 259 + } 260 + 261 + #[interrupt] 262 + fn IO_IRQ_BANK0() { 263 + static mut TOUCH_INT_PIN: Option<EpdTouchInt> = None; 264 + static mut I2C: Option<I2C<I2C0, (I2CSda, I2CScl)>> = None; 265 + static mut PREV_POS: (u16, u16) = (0, 0); 266 + 267 + trace!("IO_IRQ_BANK0"); 268 + 269 + if TOUCH_INT_PIN.is_none() { 270 + critical_section::with(|cs| *TOUCH_INT_PIN = GLOBAL_TOUCH_INT_PIN.borrow(cs).take()); 271 + } 272 + 273 + if I2C.is_none() { 274 + critical_section::with(|cs| *I2C = GLOBAL_I2C.borrow(cs).take()); 275 + } 276 + 277 + if let Some(i2c) = I2C { 278 + let rc = RefCell::new(i2c); 279 + let mut i2c = RefCellDevice::new(&rc); 280 + let mut mcp9808 = MCP9808::new(RefCellDevice::new(&rc)); 281 + 282 + let temp = match mcp9808.read_temperature().unwrap().get_celsius(ResolutionVal::Deg_0_0625C) { 283 + ..=0.0 => 0u8, 284 + 60.0.. => 60u8, 285 + t => t as u8, 286 + }; 287 + TEMP.store(temp, Ordering::Relaxed); 288 + 289 + if let Some(int) = TOUCH_INT_PIN { 290 + if int.interrupt_status(EdgeLow) { 291 + let mut buf = [0u8; 9]; 292 + i2c.write_read(0x38u8, &[0x00], &mut buf).unwrap(); 293 + let x = (((buf[3] & 0x0f) as u16) << 8) | buf[4] as u16; 294 + let y = (((buf[5] & 0x0f) as u16) << 8) | buf[6] as u16; 295 + 296 + let state = buf[3] >> 6; 297 + 298 + if state == 1 && y > 400 { 299 + critical_section::with(|cs| { 300 + IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(&[0; (240 * 416) / 8]); 301 + }); 302 + FAST_REFRESH.store(false, Ordering::Relaxed); 303 + DO_REFRESH.store(true, Ordering::Relaxed); 304 + } else if state == 1 && y < 20 { 305 + hal::rom_data::reset_to_usb_boot(0, 0); 306 + } else { 307 + if state == 1 || state == 2 { 308 + let (x0, y0) = *PREV_POS; 309 + let (x1, y1) = (x, y); 310 + critical_section::with(|cs| { 311 + plot_line(&mut IMAGE_BUFFER.borrow_ref_mut(cs), x0, y0, x1, y1); 312 + }); 313 + } 314 + FAST_REFRESH.store(true, Ordering::Relaxed); 315 + DO_REFRESH.store(true, Ordering::Relaxed); 316 + 317 + if state == 0 || state == 2 { 318 + *PREV_POS = (x, y); 319 + } 320 + } 321 + int.clear_interrupt(EdgeLow); 322 + } 323 + } else { 324 + error!("int pin is None"); 325 + } 326 + } else { 327 + error!("i2c is None"); 28 328 } 29 329 }
+15
memory.x
··· 1 + MEMORY { 2 + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 3 + FLASH : ORIGIN = 0x10000100, LENGTH = 16384K - 0x100 4 + RAM : ORIGIN = 0x20000000, LENGTH = 256K 5 + } 6 + 7 + EXTERN(BOOT2_FIRMWARE) 8 + 9 + SECTIONS { 10 + /* ### Boot loader */ 11 + .boot2 ORIGIN(BOOT2) : 12 + { 13 + KEEP(*(.boot2)); 14 + } > BOOT2 15 + } INSERT BEFORE .text;