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.

whoops: cargo fmt

hansmrtn 9a61f3f4 9082a61e

+104 -101
+94 -88
src/bin/main.rs
··· 6 6 use esp_hal::delay::Delay; 7 7 use esp_hal::interrupt::Priority; 8 8 use esp_hal::time::Duration; 9 - use esp_hal::timer::timg::TimerGroup; 10 9 use esp_hal::timer::PeriodicTimer; 10 + use esp_hal::timer::timg::TimerGroup; 11 11 use log::info; 12 12 13 13 use core::cell::RefCell; ··· 18 18 use pulp_os::board::Board; 19 19 use pulp_os::board::button::Button as HwButton; 20 20 use pulp_os::drivers::input::{Event, InputDriver}; 21 + use pulp_os::kernel::wake::{WakeReason, signal_timer, try_wake}; 21 22 use pulp_os::kernel::{AdaptivePoller, Job, Scheduler}; 22 - use pulp_os::kernel::wake::{signal_timer, try_wake, WakeReason}; 23 23 use pulp_os::ui::{Button, Label, Region, Widget}; 24 24 25 25 extern crate alloc; ··· 40 40 signal_timer(); 41 41 } 42 42 43 - // test ui 43 + // test ui 44 44 const TITLE: Region = Region::new(16, 16, 200, 32); 45 45 const ITEM0: Region = Region::new(16, 80, 200, 48); 46 46 const ITEM1: Region = Region::new(16, 144, 200, 48); ··· 62 62 critical_section::with(|cs| { 63 63 timer0.set_interrupt_handler(timer0_handler); 64 64 timer0.start(Duration::from_millis(10)).unwrap(); 65 + timer0.listen(); 65 66 TIMER0.borrow_ref_mut(cs).replace(timer0); 66 67 }); 67 68 ··· 98 99 99 100 info!("kernel ready."); 100 101 101 - let mut tick_count: u32 = 0; 102 - 103 102 loop { 103 + // 1. drain pending jobs 104 104 while let Some(job) = scheduler.pop() { 105 105 match job { 106 106 Job::RenderPage => { ··· 122 122 } 123 123 } 124 124 125 - let timer_wake = match try_wake() { 126 - Some(WakeReason::Timer) | Some(WakeReason::Multiple) => true, 125 + // 2. Check wake events (non-blocking). 126 + // When nothing is pending, idle briefly so we don't spin at full speed. 127 + // TODO: replace with proper WFI once esp-hal wake sources are configured. 128 + let should_poll = match try_wake() { 129 + Some(WakeReason::Timer) | Some(WakeReason::Multiple) => poller.tick(), 130 + 127 131 Some(WakeReason::Button) => { 128 132 poller.on_activity(); 129 133 true 130 134 } 135 + 131 136 Some(WakeReason::Display) => { 132 - info!("Display ready"); 137 + info!("display ready."); 133 138 false 134 139 } 135 - None => false, 140 + 141 + None => { 142 + delay.delay_millis(1); 143 + continue; 144 + } 136 145 }; 137 146 138 - tick_count += 1; 139 - let should_poll = timer_wake || (tick_count >= 10); 147 + if !should_poll { 148 + continue; 149 + } 140 150 141 - if should_poll { 142 - tick_count = 0; 151 + // 3. poll input and handle events 152 + let Some(event) = input.poll() else { 153 + poller.on_idle(); 154 + continue; 155 + }; 143 156 144 - if poller.tick() { 145 - if let Some(event) = input.poll() { 146 - poller.on_activity(); 157 + poller.on_activity(); 147 158 148 - // Handle input and only act on Press for navigation 149 - // LongPress/Repeat only for special actions 150 - match event { 151 - Event::Press(button) => { 152 - info!("[BTN] Press: {}", button.name()); 159 + match event { 160 + Event::Press(button) => { 161 + info!("[BTN] Press: {}", button.name()); 153 162 154 - match button { 155 - HwButton::Right | HwButton::VolUp => { 156 - let old = selected; 157 - selected = (selected + 1) % 2; 158 - if old != selected { 159 - update_selection( 160 - selected, 161 - &mut item0, 162 - &mut item1, 163 - &mut board.display.epd, 164 - &mut delay, 165 - ); 166 - } 167 - scheduler.push_or_drop(Job::PrefetchNext); 168 - } 169 - HwButton::Left | HwButton::VolDown => { 170 - let old = selected; 171 - selected = if selected == 0 { 1 } else { 0 }; 172 - if old != selected { 173 - update_selection( 174 - selected, 175 - &mut item0, 176 - &mut item1, 177 - &mut board.display.epd, 178 - &mut delay, 179 - ); 180 - } 181 - scheduler.push_or_drop(Job::PrefetchPrev); 182 - } 183 - HwButton::Confirm => { 184 - let msg = if selected == 0 { 185 - "Selected: Item 0" 186 - } else { 187 - "Selected: Item 1" 188 - }; 189 - status.set_text(msg); 190 - status.draw(&mut board.display.epd).unwrap(); 191 - let r = status.refresh_bounds(); 192 - board.display.epd.refresh_partial(r.x, r.y, r.w, r.h, &mut delay); 193 - scheduler.push_or_drop(Job::RenderPage); 194 - } 195 - HwButton::Power => { 196 - // TODO: do smth but just log for now 197 - info!("Power pressed"); 198 - } 199 - _ => {} 200 - } 163 + match button { 164 + HwButton::Right | HwButton::VolUp => { 165 + let old = selected; 166 + selected = (selected + 1) % 2; 167 + if old != selected { 168 + update_selection( 169 + selected, 170 + &mut item0, 171 + &mut item1, 172 + &mut board.display.epd, 173 + &mut delay, 174 + ); 201 175 } 202 - Event::Release(button) => { 203 - info!("[BTN] Release: {}", button.name()); 176 + scheduler.push_or_drop(Job::PrefetchNext); 177 + } 178 + HwButton::Left | HwButton::VolDown => { 179 + let old = selected; 180 + selected = if selected == 0 { 1 } else { 0 }; 181 + if old != selected { 182 + update_selection( 183 + selected, 184 + &mut item0, 185 + &mut item1, 186 + &mut board.display.epd, 187 + &mut delay, 188 + ); 204 189 } 205 - Event::LongPress(button) => { 206 - info!("[BTN] LongPress: {}", button.name()); 207 - // TODO: could use for special actions like shutdown 208 - if button == HwButton::Power { 209 - status.set_text("Shutting down..."); 210 - status.draw(&mut board.display.epd).unwrap(); 211 - let r = status.refresh_bounds(); 212 - board.display.epd.refresh_partial(r.x, r.y, r.w, r.h, &mut delay); 213 - } 214 - } 215 - Event::Repeat(button) => { 216 - // TODO: figure it out 217 - info!("[BTN] Repeat: {}", button.name()); 218 - } 190 + scheduler.push_or_drop(Job::PrefetchPrev); 191 + } 192 + HwButton::Confirm => { 193 + let msg = if selected == 0 { 194 + "Selected: Item 0" 195 + } else { 196 + "Selected: Item 1" 197 + }; 198 + status.set_text(msg); 199 + status.draw(&mut board.display.epd).unwrap(); 200 + let r = status.refresh_bounds(); 201 + board 202 + .display 203 + .epd 204 + .refresh_partial(r.x, r.y, r.w, r.h, &mut delay); 205 + scheduler.push_or_drop(Job::RenderPage); 206 + } 207 + HwButton::Power => { 208 + // TODO: sleep/shutdown 209 + info!("Power pressed"); 219 210 } 220 - } else { 221 - poller.on_idle(); 211 + _ => {} 222 212 } 223 213 } 214 + Event::Release(button) => { 215 + info!("[BTN] Release: {}", button.name()); 216 + } 217 + Event::LongPress(button) => { 218 + info!("[BTN] LongPress: {}", button.name()); 219 + if button == HwButton::Power { 220 + status.set_text("Shutting down..."); 221 + status.draw(&mut board.display.epd).unwrap(); 222 + let r = status.refresh_bounds(); 223 + board 224 + .display 225 + .epd 226 + .refresh_partial(r.x, r.y, r.w, r.h, &mut delay); 227 + } 228 + } 229 + Event::Repeat(button) => { 230 + info!("[BTN] Repeat: {}", button.name()); 231 + } 224 232 } 225 - 226 - delay.delay_millis(1); 227 233 } 228 234 } 229 235
+1 -1
src/drivers/input.rs
··· 3 3 // The X4 has three physical input sources that all funnel into a 4 4 // single "one button at a time" deal: 5 5 // - Row 1 ADC (GPIO1): Right, Left, Confirm, Back via resistance ladder 6 - // - Row 2 ADC (GPIO2): Volume Up/Down via resistance ladder 6 + // - Row 2 ADC (GPIO2): Volume Up/Down via resistance ladder 7 7 // - Power button (GPIO3): Digital input, active low 8 8 // NOTE: Because each resistance ladder can only report one press at a time, 9 9 // we collapse everything into `Option<Button>` per poll cycle.
+2 -2
src/kernel/mod.rs
··· 9 9 pub mod scheduler; 10 10 pub mod wake; 11 11 12 + pub use poll::{AdaptivePoller, BASE_TICK_MS, PollRate}; 12 13 pub use scheduler::{Job, Priority, PushError, Scheduler}; 13 - pub use poll::{PollRate, AdaptivePoller, BASE_TICK_MS}; 14 - pub use wake::{WakeReason, sleep_until_wake, signal_button, signal_display, signal_timer}; 14 + pub use wake::{WakeReason, signal_button, signal_display, signal_timer, sleep_until_wake};
+3 -4
src/kernel/poll.rs
··· 6 6 7 7 use core::fmt; 8 8 9 - /// Base timer tick interval (ms) 9 + // Base timer tick interval (ms) 10 10 pub const BASE_TICK_MS: u32 = 10; 11 11 12 12 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] ··· 43 43 } 44 44 } 45 45 46 - // Thresholds for rate transitions 46 + // Thresholds for rate transitions 47 47 mod thresholds { 48 48 pub const FAST_TO_NORMAL: u32 = 20; // 20 × 10ms = 200ms 49 49 pub const NORMAL_TO_SLOW: u32 = 20; // 20 × 50ms = 1000ms ··· 54 54 rate: PollRate, 55 55 // since last poll 56 56 tick_count: u32, 57 - // consecutive idle polls 57 + // consecutive idle polls 58 58 idle_count: u32, 59 59 } 60 60 ··· 134 134 Self::new() 135 135 } 136 136 } 137 -
+4 -5
src/kernel/scheduler.rs
··· 150 150 151 151 // The job scheduler 152 152 pub struct Scheduler { 153 - high: JobQueue<4>, 154 - normal: JobQueue<8>, 155 - low: JobQueue<16>, 153 + high: JobQueue<4>, 154 + normal: JobQueue<8>, 155 + low: JobQueue<16>, 156 156 } 157 157 158 158 impl Scheduler { ··· 227 227 } 228 228 } 229 229 230 - // the next job to execute 230 + // the next job to execute 231 231 pub fn pop(&mut self) -> Option<Job> { 232 232 self.high 233 233 .pop() ··· 278 278 Self::new() 279 279 } 280 280 } 281 -
-1
src/kernel/wake.rs
··· 79 79 WAKE_TIMER.store(true, Ordering::Release); 80 80 } 81 81 82 - 83 82 #[inline] 84 83 pub fn wait_for_interrupt() { 85 84 #[cfg(target_arch = "riscv32")]