A fork of pulp-os for the xteink4 adding custom apps
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add rotation logic, cargo fmt

hansmrtn 0f283858 4894fefb

+80 -44
+4 -6
src/bin/main.rs
··· 12 12 use log::info; 13 13 14 14 use embedded_graphics::{ 15 - mono_font::{ascii::FONT_10X20, MonoTextStyle}, 15 + mono_font::{MonoTextStyle, ascii::FONT_10X20}, 16 16 pixelcolor::BinaryColor, 17 17 prelude::*, 18 18 primitives::{Circle, Line, PrimitiveStyle, Rectangle}, ··· 22 22 use pulp_os::board::Board; 23 23 use pulp_os::drivers::input::InputDriver; 24 24 25 - 26 25 extern crate alloc; 27 26 28 27 esp_bootloader_esp_idf::esp_app_desc!(); 29 28 30 29 /// The rectangle that will flash on button events. 31 30 /// X must be 8-pixel aligned for efficient partial refresh. 32 - const FLASH_RECT_X: u16 = 24; // Aligned to 8 pixels 31 + const FLASH_RECT_X: u16 = 24; // Aligned to 8 pixels 33 32 const FLASH_RECT_Y: u16 = 70; 34 33 const FLASH_RECT_W: u16 = 120; // Multiple of 8 35 34 const FLASH_RECT_H: u16 = 60; ··· 53 52 54 53 // Initialize display 55 54 board.display.epd.init(&mut delay); 56 - 55 + 57 56 let sz = board.display.epd.size(); 58 57 let w = sz.width as i32; 59 58 let h = sz.height as i32; ··· 120 119 loop { 121 120 if let Some(btn) = input.poll() { 122 121 info!("[BTN] {:?} pressed", btn); 123 - 122 + 124 123 // Toggle rectangle 125 124 rect_is_black = !rect_is_black; 126 125 let color = if rect_is_black { ··· 146 145 FLASH_RECT_H, 147 146 &mut delay, 148 147 ); 149 - 150 148 } 151 149 152 150 delay.delay_millis(10);
+2 -3
src/board/button.rs
··· 45 45 pub const DEFAULT_TOLERANCE: u16 = 150; 46 46 47 47 pub const ROW1_THRESHOLDS: &[(u16, u16, Button)] = &[ 48 - (3, 50, Button::Right), // Near ground 48 + (3, 50, Button::Right), // Near ground 49 49 (1113, DEFAULT_TOLERANCE, Button::Left), 50 50 (1984, DEFAULT_TOLERANCE, Button::Back), 51 51 (2556, DEFAULT_TOLERANCE, Button::Confirm), 52 52 ]; 53 53 54 54 pub const ROW2_THRESHOLDS: &[(u16, u16, Button)] = &[ 55 - (3, 50, Button::VolDown), // Near ground 55 + (3, 50, Button::VolDown), // Near ground 56 56 (1659, DEFAULT_TOLERANCE, Button::VolUp), 57 57 ]; 58 58 ··· 66 66 } 67 67 None 68 68 } 69 -
+67 -25
src/board/display.rs
··· 3 3 //! Based on GxEPD2_426_GDEQ0426T82.cpp by Jean-Marc Zingg 4 4 //! <https://github.com/ZinggJM/GxEPD2> 5 5 use embedded_graphics_core::{ 6 + Pixel, 6 7 draw_target::DrawTarget, 7 8 geometry::{OriginDimensions, Size}, 8 9 pixelcolor::BinaryColor, 9 - Pixel, 10 10 }; 11 11 use embedded_hal::digital::{InputPin, OutputPin}; 12 12 use embedded_hal::spi::SpiDevice; ··· 39 39 pub const MASTER_ACTIVATION: u8 = 0x20; 40 40 pub const DISPLAY_UPDATE_CONTROL_1: u8 = 0x21; 41 41 pub const DISPLAY_UPDATE_CONTROL_2: u8 = 0x22; 42 - pub const WRITE_RAM_BW: u8 = 0x24; // Current/New buffer 43 - pub const WRITE_RAM_RED: u8 = 0x26; // Previous buffer (for differential) 42 + pub const WRITE_RAM_BW: u8 = 0x24; // Current/New buffer 43 + pub const WRITE_RAM_RED: u8 = 0x26; // Previous buffer (for differential) 44 44 pub const BORDER_WAVEFORM: u8 = 0x3C; 45 45 pub const SET_RAM_X_RANGE: u8 = 0x44; 46 46 pub const SET_RAM_Y_RANGE: u8 = 0x45; ··· 48 48 pub const SET_RAM_Y_COUNTER: u8 = 0x4F; 49 49 } 50 50 51 + pub enum Rotation { 52 + Deg0, 53 + Deg90, 54 + Deg180, 55 + Deg270, 56 + } 57 + 51 58 /// Display driver for SSD1677-based e-paper (GDEQ0426T82) 52 59 pub struct DisplayDriver<SPI, DC, RST, BUSY> { 53 60 spi: SPI, ··· 59 66 init_done: bool, 60 67 initial_refresh: bool, 61 68 initial_write: bool, 69 + rotation: Rotation, 62 70 } 63 71 64 72 impl<SPI, DC, RST, BUSY, E> DisplayDriver<SPI, DC, RST, BUSY> ··· 80 88 init_done: false, 81 89 initial_refresh: true, 82 90 initial_write: true, 91 + rotation: Rotation::Deg0, 83 92 } 84 93 } 85 94 ··· 97 106 pub fn init(&mut self, delay: &mut Delay) { 98 107 self.reset(delay); 99 108 self.init_display(delay); 109 + self.set_rotation(Rotation::Deg270); 100 110 } 101 111 102 112 /// Clear the entire screen to white ··· 117 127 118 128 /// Set a pixel in the framebuffer (0,0 is top-left) 119 129 pub fn set_pixel(&mut self, x: u16, y: u16, black: bool) { 120 - if x >= WIDTH || y >= HEIGHT { 121 - return; 122 - } 123 - let idx = (x as usize / 8) + (y as usize * (WIDTH as usize / 8)); 124 - let bit = 7 - (x % 8); 130 + // Transform logical (x, y) to physical (px, py) 131 + let (px, py) = match self.rotation { 132 + Rotation::Deg0 => { 133 + if x >= WIDTH || y >= HEIGHT { 134 + return; 135 + } 136 + (x, y) 137 + } 138 + Rotation::Deg90 => { 139 + // Logical size: 480×800 (HEIGHT × WIDTH) 140 + if x >= HEIGHT || y >= WIDTH { 141 + return; 142 + } 143 + (WIDTH - 1 - y, x) 144 + } 145 + Rotation::Deg180 => { 146 + if x >= WIDTH || y >= HEIGHT { 147 + return; 148 + } 149 + (WIDTH - 1 - x, HEIGHT - 1 - y) 150 + } 151 + Rotation::Deg270 => { 152 + // Logical size: 480×800 (HEIGHT × WIDTH) 153 + if x >= HEIGHT || y >= WIDTH { 154 + return; 155 + } 156 + (y, HEIGHT - 1 - x) 157 + } 158 + }; 159 + 160 + let idx = (px as usize / 8) + (py as usize * (WIDTH as usize / 8)); 161 + let bit = 7 - (px % 8); // Use px, not x! 125 162 if black { 126 163 self.framebuffer[idx] &= !(1 << bit); 127 164 } else { ··· 146 183 // Write to both buffers for full refresh 147 184 self.set_partial_ram_area(0, 0, WIDTH, HEIGHT); 148 185 self.write_full_buffer(cmd::WRITE_RAM_RED); // Previous 149 - 186 + 150 187 delay.delay_millis(1); // Yield between large transfers 151 - 188 + 152 189 self.set_partial_ram_area(0, 0, WIDTH, HEIGHT); 153 - self.write_full_buffer(cmd::WRITE_RAM_BW); // Current 190 + self.write_full_buffer(cmd::WRITE_RAM_BW); // Current 154 191 155 192 self.update_full(delay); 156 193 self.initial_refresh = false; ··· 244 281 // Driver output control 245 282 self.send_command(cmd::DRIVER_OUTPUT_CONTROL); 246 283 self.send_data(&[ 247 - ((HEIGHT - 1) & 0xFF) as u8, // A[7:0] 248 - ((HEIGHT - 1) >> 8) as u8, // A[9:8] 249 - 0x02, // SM = interlaced 284 + ((HEIGHT - 1) & 0xFF) as u8, // A[7:0] 285 + ((HEIGHT - 1) >> 8) as u8, // A[9:8] 286 + 0x02, // SM = interlaced 250 287 ]); 251 288 252 289 // Border waveform ··· 330 367 // Write in normal row order - gate reversal is handled by RAM address setup 331 368 // (Y-decrease mode in _setPartialRamArea), NOT by reversing rows here 332 369 let bytes_per_row = (WIDTH / 8) as usize; 333 - 370 + 334 371 // Use a temporary buffer to avoid borrow checker issues 335 372 let mut row_buf = [0u8; 256]; 336 - 373 + 337 374 for row in 0..HEIGHT as usize { 338 375 let start = row * bytes_per_row; 339 376 // Write row in chunks to avoid issues 340 377 for chunk_start in (0..bytes_per_row).step_by(256) { 341 378 let chunk_end = (chunk_start + 256).min(bytes_per_row); 342 379 let chunk_len = chunk_end - chunk_start; 343 - 380 + 344 381 // Copy to temp buffer 345 - row_buf[..chunk_len].copy_from_slice( 346 - &self.framebuffer[start + chunk_start..start + chunk_end] 347 - ); 382 + row_buf[..chunk_len] 383 + .copy_from_slice(&self.framebuffer[start + chunk_start..start + chunk_end]); 348 384 self.send_data(&row_buf[..chunk_len]); 349 385 } 350 386 } ··· 367 403 for row in 0..h as usize { 368 404 let src_row = y as usize + row; 369 405 let src_start = src_row * bytes_per_row + x_byte; 370 - 406 + 371 407 // Copy to temp buffer 372 - row_buf[..window_bytes].copy_from_slice( 373 - &self.framebuffer[src_start..src_start + window_bytes] 374 - ); 408 + row_buf[..window_bytes] 409 + .copy_from_slice(&self.framebuffer[src_start..src_start + window_bytes]); 375 410 self.send_data(&row_buf[..window_bytes]); 376 411 } 377 412 } ··· 427 462 let _ = self.dc.set_high(); 428 463 let _ = self.spi.write(data); 429 464 } 465 + 466 + fn set_rotation(&mut self, rotation: Rotation) { 467 + self.rotation = rotation; 468 + } 430 469 } 431 470 432 471 // ========== embedded-graphics integration ========== ··· 439 478 BUSY: InputPin, 440 479 { 441 480 fn size(&self) -> Size { 442 - Size::new(WIDTH as u32, HEIGHT as u32) 481 + match &self.rotation { 482 + Rotation::Deg0 | Rotation::Deg180 => Size::new(WIDTH as u32, HEIGHT as u32), 483 + Rotation::Deg90 | Rotation::Deg270 => Size::new(HEIGHT as u32, WIDTH as u32), 484 + } 443 485 } 444 486 } 445 487
+5 -6
src/board/mod.rs
··· 8 8 pub mod display; 9 9 pub mod pins; 10 10 11 - pub use button::{decode_ladder, Button, ROW1_THRESHOLDS, ROW2_THRESHOLDS}; 12 - pub use display::{DisplayDriver, HEIGHT, WIDTH, FRAMEBUFFER_SIZE, SPI_FREQ_MHZ}; 11 + pub use button::{Button, ROW1_THRESHOLDS, ROW2_THRESHOLDS, decode_ladder}; 12 + pub use display::{DisplayDriver, FRAMEBUFFER_SIZE, HEIGHT, SPI_FREQ_MHZ, WIDTH}; 13 13 14 14 use embedded_hal_bus::spi::ExclusiveDevice; 15 15 use esp_hal::{ 16 + Blocking, 16 17 analog::adc::{Adc, AdcCalCurve, AdcConfig, AdcPin, Attenuation}, 17 18 delay::Delay, 18 19 gpio::{Input, InputConfig, Level, Output, OutputConfig, Pull}, 19 - peripherals::{Peripherals, ADC1, GPIO1, GPIO2}, 20 + peripherals::{ADC1, GPIO1, GPIO2, Peripherals}, 20 21 spi, 21 22 time::Rate, 22 - Blocking, 23 23 }; 24 24 25 25 // Type Aliases ··· 91 91 let busy = Input::new(p.GPIO6, InputConfig::default().with_pull(Pull::None)); 92 92 93 93 // SPI bus 94 - let spi_cfg = 95 - spi::master::Config::default().with_frequency(Rate::from_mhz(SPI_FREQ_MHZ)); 94 + let spi_cfg = spi::master::Config::default().with_frequency(Rate::from_mhz(SPI_FREQ_MHZ)); 96 95 let spi_bus = spi::master::Spi::new(p.SPI2, spi_cfg) 97 96 .unwrap() 98 97 .with_sck(p.GPIO8)
+1 -1
src/drivers/input.rs
··· 12 12 13 13 use esp_hal::time::{Duration, Instant}; 14 14 15 - use crate::board::button::{decode_ladder, Button, ROW1_THRESHOLDS, ROW2_THRESHOLDS}; 16 15 use crate::board::InputHw; 16 + use crate::board::button::{Button, ROW1_THRESHOLDS, ROW2_THRESHOLDS, decode_ladder}; 17 17 18 18 /// Debounce time - ignore state changes shorter than this. 19 19 const DEBOUNCE_MS: u64 = 30;
+1 -3
src/drivers/mod.rs
··· 1 - 2 - 3 - pub mod input; 1 + pub mod input; 4 2 // pub mod display_driver;