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-gui: fast visual feedback for buttons

+305 -141
+5 -4
fw16-epd-gui/src/draw_target.rs
··· 4 4 use embedded_graphics::Pixel; 5 5 use embedded_graphics::pixelcolor::BinaryColor; 6 6 use embedded_graphics::primitives::Rectangle; 7 + use fw16_epd_program_interface::RefreshBlockMode; 7 8 use tp370pgh01::{DIM_X, DIM_Y, IMAGE_BYTES}; 8 9 9 10 pub struct EpdDrawTarget { 10 11 write_image: extern "C" fn(&[u8; IMAGE_BYTES]), 11 - refresh: extern "C" fn(bool, bool), 12 + refresh: extern "C" fn(bool, RefreshBlockMode), 12 13 buf: [u8; IMAGE_BYTES], 13 14 } 14 15 ··· 55 56 } 56 57 57 58 impl EpdDrawTarget { 58 - pub const fn new(write_image: extern "C" fn(&[u8; IMAGE_BYTES]), refresh: extern "C" fn(bool, bool)) -> Self { 59 + pub const fn new(write_image: extern "C" fn(&[u8; IMAGE_BYTES]), refresh: extern "C" fn(bool, RefreshBlockMode)) -> Self { 59 60 Self { 60 61 write_image, 61 62 refresh, ··· 63 64 } 64 65 } 65 66 66 - pub fn refresh(&self, fast_refresh: bool, block_ack: bool) { 67 + pub fn refresh(&self, fast_refresh: bool, block_mode: RefreshBlockMode) { 67 68 (self.write_image)(&self.buf); 68 - (self.refresh)(fast_refresh, block_ack); 69 + (self.refresh)(fast_refresh, block_mode); 69 70 } 70 71 }
+93 -42
fw16-epd-gui/src/element/button.rs
··· 5 5 use embedded_graphics::primitives::{CornerRadii, PrimitiveStyle, Rectangle, RoundedRectangle}; 6 6 use embedded_graphics::text::{Alignment, Baseline, Text, TextStyle, TextStyleBuilder}; 7 7 use embedded_graphics::text::renderer::TextRenderer; 8 - use fw16_epd_program_interface::{TouchEvent, TouchEventType}; 8 + use fw16_epd_program_interface::{Event, TouchEventType}; 9 9 use crate::draw_target::EpdDrawTarget; 10 - use crate::element::{GuiElement, DEFAULT_PRIMITIVE_STYLE, DEFAULT_TEXT_STYLE}; 10 + use crate::element::{Gui, DEFAULT_PRIMITIVE_STYLE, DEFAULT_TEXT_STYLE}; 11 11 12 12 const CENTRE_STYLE: TextStyle = TextStyleBuilder::new() 13 13 .alignment(Alignment::Center) 14 14 .baseline(Baseline::Middle) 15 15 .build(); 16 16 17 - #[derive(Copy, Clone, Debug, Eq, PartialEq, defmt::Format)] 18 - enum ClickState { 19 - /// The button has not been clicked 20 - None, 21 - /// The button has been pressed, but not released 22 - Pressed, 23 - /// The button has been fully clicked and released 24 - Clicked, 17 + #[derive(Copy, Clone, Debug, Eq, PartialEq, Default, defmt::Format)] 18 + pub struct ButtonOutput { 19 + pub clicked: bool, 20 + pub needs_refresh: bool, 25 21 } 26 22 27 23 #[derive(Debug, defmt::Format)] ··· 30 26 label: &'a str, 31 27 rect_style: PrimitiveStyle<BinaryColor>, 32 28 char_style: MonoTextStyle<'a, BinaryColor>, 33 - click_state: ClickState, 29 + touch_feedback: bool, 30 + touch_feedback_immediate_release: bool, 31 + 32 + began_click: bool, 33 + inverted: bool, 34 + should_uninvert: bool, 34 35 } 35 36 36 37 impl<'a> Button<'a> { 37 - pub fn new(rect: RoundedRectangle, label: &'a str, rect_style: PrimitiveStyle<BinaryColor>, char_style: MonoTextStyle<'a, BinaryColor>) -> Self { 38 + pub fn new(rect: RoundedRectangle, label: &'a str, rect_style: PrimitiveStyle<BinaryColor>, char_style: MonoTextStyle<'a, BinaryColor>, touch_feedback: bool, touch_feedback_immediate_release: bool) -> Self { 38 39 Self { 39 40 rect, 40 41 label, 41 42 rect_style, 42 43 char_style, 43 - click_state: ClickState::None, 44 + touch_feedback, 45 + touch_feedback_immediate_release, 46 + began_click: false, 47 + inverted: false, 48 + should_uninvert: false, 44 49 } 45 50 } 46 51 47 - pub fn auto_sized(top_left: Point, corner_radii: CornerRadii, label: &'a str, rect_style: PrimitiveStyle<BinaryColor>, char_style: MonoTextStyle<'a, BinaryColor>) -> Self { 52 + pub fn auto_sized(top_left: Point, corner_radii: CornerRadii, label: &'a str, rect_style: PrimitiveStyle<BinaryColor>, char_style: MonoTextStyle<'a, BinaryColor>, touch_feedback: bool, touch_feedback_immediate_release: bool) -> Self { 48 53 let size = Size::new((char_style.font.character_size.width + char_style.font.character_spacing) * (label.len() as u32 + 1), char_style.line_height()); 49 54 Self { 50 55 rect: RoundedRectangle::new(Rectangle::new(top_left, size), corner_radii), 51 56 label, 52 57 rect_style, 53 58 char_style, 54 - click_state: ClickState::None, 59 + touch_feedback, 60 + touch_feedback_immediate_release, 61 + began_click: false, 62 + inverted: false, 63 + should_uninvert: false, 55 64 } 56 65 } 57 66 58 - pub fn with_default_style(rect: Rectangle, label: &'a str) -> Self { 67 + pub fn with_default_style(rect: Rectangle, label: &'a str, touch_feedback_immediate_release: bool) -> Self { 59 68 Self { 60 69 rect: RoundedRectangle::new(rect, CornerRadii::new(Size::new(3, 3))), 61 70 label, 62 71 rect_style: DEFAULT_PRIMITIVE_STYLE, 63 72 char_style: DEFAULT_TEXT_STYLE, 64 - click_state: ClickState::None, 73 + touch_feedback: true, 74 + touch_feedback_immediate_release, 75 + began_click: false, 76 + inverted: false, 77 + should_uninvert: false, 65 78 } 66 79 } 67 80 68 - pub fn with_default_style_auto_sized(top_left: Point, label: &'a str) -> Self { 81 + pub fn with_default_style_auto_sized(top_left: Point, label: &'a str, touch_feedback_immediate_release: bool) -> Self { 69 82 Self::auto_sized( 70 83 top_left, 71 84 CornerRadii::new(Size::new(3, 3)), 72 85 label, 73 86 DEFAULT_PRIMITIVE_STYLE, 74 - DEFAULT_TEXT_STYLE 87 + DEFAULT_TEXT_STYLE, 88 + true, 89 + touch_feedback_immediate_release, 75 90 ) 76 91 } 77 92 78 - pub fn clicked(&mut self, clear: bool) -> bool { 79 - if self.click_state == ClickState::Clicked { 80 - if clear { 81 - self.click_state = ClickState::None; 82 - } 83 - 84 - true 85 - } else { 86 - false 87 - } 93 + pub fn rect(&self) -> Rectangle { 94 + self.rect.bounding_box() 88 95 } 89 96 90 - pub fn rect(&self) -> Rectangle { 91 - self.rect.bounding_box() 97 + fn invert(&mut self) { 98 + self.rect_style.fill_color = self.rect_style.fill_color.map(|c| c.invert()); 99 + self.char_style.text_color = self.char_style.text_color.map(|c| c.invert()); 100 + self.inverted = !self.inverted; 92 101 } 93 102 } 94 103 95 - impl<'a> GuiElement for Button<'a> { 96 - fn draw_element(&self, target: &mut EpdDrawTarget) { 104 + impl<'a> Gui for Button<'a> { 105 + type Output = ButtonOutput; 106 + 107 + fn draw_init(&self, target: &mut EpdDrawTarget) { 97 108 self.rect 98 109 .into_styled(self.rect_style) 99 110 .draw(target) ··· 109 120 .unwrap(); 110 121 } 111 122 112 - fn handle_touch(&mut self, ev: TouchEvent) { 113 - if self.rect.contains(ev.eg_point()) { 114 - match (self.click_state, ev.ev_type) { 115 - (ClickState::None, TouchEventType::Down) => self.click_state = ClickState::Pressed, 116 - (ClickState::Pressed, TouchEventType::Up) => self.click_state = ClickState::Clicked, 117 - _ => {}, 123 + fn tick(&mut self, target: &mut EpdDrawTarget, ev: Event) -> Self::Output { 124 + let mut ret = ButtonOutput::default(); 125 + 126 + if let Event::Touch(ev) = ev { 127 + if self.rect.contains(ev.eg_point()) { 128 + match (self.began_click, ev.ev_type) { 129 + (false, TouchEventType::Down) => { 130 + self.began_click = true; 131 + if self.touch_feedback { 132 + self.invert(); 133 + self.draw_init(target); 134 + ret.needs_refresh = true; 135 + } 136 + }, 137 + (true, TouchEventType::Up) => { 138 + self.began_click = false; 139 + if self.inverted { 140 + if self.touch_feedback_immediate_release { 141 + self.invert(); 142 + self.draw_init(target); 143 + ret.needs_refresh = true; 144 + } else { 145 + self.should_uninvert = true; 146 + } 147 + } 148 + ret.clicked = true; 149 + }, 150 + _ => {}, 151 + } 152 + } else { 153 + self.began_click = false; 154 + if self.inverted { 155 + if self.touch_feedback_immediate_release { 156 + self.invert(); 157 + self.draw_init(target); 158 + ret.needs_refresh = true; 159 + } else { 160 + self.should_uninvert = true; 161 + } 162 + } 118 163 } 119 - } else if self.click_state == ClickState::Pressed { 120 - // user dragged their finger out of the button bounding box 121 - self.click_state = ClickState::None; 122 164 } 165 + 166 + if ev == Event::RefreshFinished && self.should_uninvert { 167 + self.should_uninvert = false; 168 + self.invert(); 169 + self.draw_init(target); 170 + ret.needs_refresh = true; 171 + } 172 + 173 + ret 123 174 } 124 175 }
+11 -7
fw16-epd-gui/src/element/mod.rs
··· 5 5 use embedded_graphics::pixelcolor::BinaryColor; 6 6 use embedded_graphics::prelude::*; 7 7 use embedded_graphics::primitives::{PrimitiveStyle, PrimitiveStyleBuilder}; 8 - use fw16_epd_program_interface::TouchEvent; 8 + use fw16_epd_program_interface::{Event, TouchEvent}; 9 9 use crate::draw_target::EpdDrawTarget; 10 10 11 11 pub const DEFAULT_PRIMITIVE_STYLE: PrimitiveStyle<BinaryColor> = PrimitiveStyleBuilder::new() ··· 15 15 .build(); 16 16 pub const DEFAULT_TEXT_STYLE: MonoTextStyle<BinaryColor> = MonoTextStyle::new(&FONT_10X20, BinaryColor::On); 17 17 18 - pub trait GuiElement { 19 - fn draw_element(&self, target: &mut EpdDrawTarget); 18 + pub trait Gui { 19 + type Output; 20 + 21 + fn draw_init(&self, target: &mut EpdDrawTarget); 20 22 21 - fn handle_touch(&mut self, ev: TouchEvent); 23 + fn tick(&mut self, target: &mut EpdDrawTarget, ev: Event) -> Self::Output; 22 24 } 23 25 24 - impl<T: Drawable<Color = BinaryColor>> GuiElement for T { 25 - fn draw_element(&self, target: &mut EpdDrawTarget) { 26 + impl<T: Drawable<Color = BinaryColor>> Gui for T { 27 + type Output = (); 28 + 29 + fn draw_init(&self, target: &mut EpdDrawTarget) { 26 30 self.draw(target).unwrap(); 27 31 } 28 32 29 - fn handle_touch(&mut self, _ev: TouchEvent) { 33 + fn tick(&mut self, _target: &mut EpdDrawTarget, _ev: Event) { 30 34 // no-op 31 35 } 32 36 }
+156 -69
fw16-epd-main/src/gui.rs
··· 2 2 use embedded_graphics::geometry::AnchorPoint; 3 3 use embedded_graphics::pixelcolor::BinaryColor; 4 4 use embedded_graphics::prelude::*; 5 - use embedded_graphics::primitives::Line; 5 + use embedded_graphics::primitives::{Line, 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::{GuiElement, DEFAULT_PRIMITIVE_STYLE}; 9 - use fw16_epd_program_interface::{SafeOption, TouchEvent, TouchEventType}; 10 - use crate::{next_touch_event, set_touch_enabled}; 8 + use fw16_epd_gui::element::{Gui, DEFAULT_PRIMITIVE_STYLE}; 9 + use fw16_epd_program_interface::{RefreshBlockMode, SafeOption, Event, TouchEventType}; 10 + use crate::{next_event, set_touch_enabled}; 11 + 12 + enum Page { 13 + MainPage, 14 + ScratchpadPage, 15 + } 11 16 12 17 struct MainPage { 13 18 scratchpad_button: Button<'static>, 19 + test_buttons: [Button<'static>; 16], 14 20 } 15 21 16 22 impl MainPage { 17 23 fn new() -> Self { 24 + let test_buttons = [ 25 + Button::with_default_style(Rectangle::new(Point::new(10, 50), Size::new(40, 40)), "0", false), 26 + Button::with_default_style(Rectangle::new(Point::new(60, 50), Size::new(40, 40)), "1", false), 27 + Button::with_default_style(Rectangle::new(Point::new(110, 50), Size::new(40, 40)), "2", false), 28 + Button::with_default_style(Rectangle::new(Point::new(160, 50), Size::new(40, 40)), "3", false), 29 + Button::with_default_style(Rectangle::new(Point::new(10, 100), Size::new(40, 40)), "4", false), 30 + Button::with_default_style(Rectangle::new(Point::new(60, 100), Size::new(40, 40)), "5", false), 31 + Button::with_default_style(Rectangle::new(Point::new(110, 100), Size::new(40, 40)), "6", false), 32 + Button::with_default_style(Rectangle::new(Point::new(160, 100), Size::new(40, 40)), "7", false), 33 + Button::with_default_style(Rectangle::new(Point::new(10, 150), Size::new(40, 40)), "8", false), 34 + Button::with_default_style(Rectangle::new(Point::new(60, 150), Size::new(40, 40)), "9", false), 35 + Button::with_default_style(Rectangle::new(Point::new(110, 150), Size::new(40, 40)), "A", false), 36 + Button::with_default_style(Rectangle::new(Point::new(160, 150), Size::new(40, 40)), "B", false), 37 + Button::with_default_style(Rectangle::new(Point::new(10, 200), Size::new(40, 40)), "C", false), 38 + Button::with_default_style(Rectangle::new(Point::new(60, 200), Size::new(40, 40)), "D", false), 39 + Button::with_default_style(Rectangle::new(Point::new(110, 200), Size::new(40, 40)), "E", false), 40 + Button::with_default_style(Rectangle::new(Point::new(160, 200), Size::new(40, 40)), "F", false), 41 + ]; 42 + 43 + 18 44 Self { 19 - scratchpad_button: Button::with_default_style_auto_sized(Point::new(10, 10), "Scratchpad"), 45 + scratchpad_button: Button::with_default_style_auto_sized(Point::new(10, 10), "Scratchpad", true), 46 + test_buttons, 20 47 } 21 48 } 22 49 } 23 50 24 - impl GuiElement for MainPage { 25 - fn draw_element(&self, target: &mut EpdDrawTarget) { 26 - self.scratchpad_button.draw_element(target); 51 + impl Gui for MainPage { 52 + type Output = Option<Page>; 53 + 54 + fn draw_init(&self, draw_target: &mut EpdDrawTarget) { 55 + self.scratchpad_button.draw_init(draw_target); 56 + for button in &self.test_buttons { 57 + button.draw_init(draw_target); 58 + } 27 59 } 28 60 29 - fn handle_touch(&mut self, ev: TouchEvent) { 30 - self.scratchpad_button.handle_touch(ev); 61 + fn tick(&mut self, draw_target: &mut EpdDrawTarget, ev: Event) -> Self::Output { 62 + let mut needs_refresh = false; 63 + 64 + let s = self.scratchpad_button.tick(draw_target, ev); 65 + if s.clicked { 66 + return Some(Page::ScratchpadPage); 67 + } else if s.needs_refresh { 68 + draw_target.refresh(true, RefreshBlockMode::BlockAcknowledge); 69 + } 70 + 71 + 72 + for button in &mut self.test_buttons { 73 + needs_refresh |= button.tick(draw_target, ev).needs_refresh; 74 + } 75 + 76 + if needs_refresh { 77 + draw_target.refresh(true, RefreshBlockMode::NonBlocking); 78 + } 79 + 80 + None 31 81 } 32 82 } 33 83 ··· 39 89 40 90 impl ScratchpadPage { 41 91 fn new() -> Self { 42 - let exit_button = Button::with_default_style_auto_sized(Point::new(10, 416 - 10 - 20), "Exit"); 92 + let exit_button = Button::with_default_style_auto_sized(Point::new(10, 416 - 10 - 20), "Exit", true); 43 93 let next_pos = exit_button 44 94 .rect() 45 95 .translate(Point::new(10, 0)) 46 96 .anchor_point(AnchorPoint::TopRight); 47 - let clear_button = Button::with_default_style_auto_sized(next_pos, "Clear"); 97 + let clear_button = Button::with_default_style_auto_sized(next_pos, "Clear", true); 48 98 49 99 Self { 50 100 exit_button, ··· 54 104 } 55 105 } 56 106 57 - impl GuiElement for ScratchpadPage { 58 - fn draw_element(&self, target: &mut EpdDrawTarget) { 59 - self.exit_button.draw_element(target); 60 - self.clear_button.draw_element(target); 107 + impl Gui for ScratchpadPage { 108 + type Output = Option<Page>; 109 + 110 + fn draw_init(&self, draw_target: &mut EpdDrawTarget) { 111 + self.exit_button.draw_init(draw_target); 112 + self.clear_button.draw_init(draw_target); 61 113 } 62 114 63 - fn handle_touch(&mut self, ev: TouchEvent) { 64 - self.exit_button.handle_touch(ev); 65 - self.clear_button.handle_touch(ev); 66 - if matches!(ev.ev_type, TouchEventType::Down | TouchEventType::Move) { 67 - self.prev_pos = Some(ev.eg_point()); 115 + fn tick(&mut self, draw_target: &mut EpdDrawTarget, ev: Event) -> Self::Output { 116 + let mut refresh: Option<RefreshBlockMode> = None; 117 + 118 + let e = self.exit_button.tick(draw_target, ev); 119 + if e.clicked { 120 + return Some(Page::MainPage); 121 + } 122 + if e.needs_refresh { 123 + refresh = Some(RefreshBlockMode::BlockAcknowledge); 124 + } 125 + 126 + let c = self.clear_button.tick(draw_target, ev); 127 + if c.clicked { 128 + draw_target.clear(BinaryColor::Off).unwrap(); 129 + self.draw_init(draw_target); 130 + draw_target.refresh(false, RefreshBlockMode::BlockAcknowledge); 131 + return None; 132 + } 133 + if c.needs_refresh { 134 + refresh = Some(RefreshBlockMode::BlockAcknowledge); 135 + } 136 + 137 + if let Event::Touch(ev) = ev { 138 + if matches!(ev.ev_type, TouchEventType::Move | TouchEventType::Up) { 139 + if let Some(prev) = self.prev_pos { 140 + Line::new(prev, ev.eg_point()) 141 + .into_styled(DEFAULT_PRIMITIVE_STYLE) 142 + .draw(draw_target) 143 + .unwrap(); 144 + 145 + self.exit_button.draw_init(draw_target); 146 + self.clear_button.draw_init(draw_target); 147 + 148 + if refresh.is_none() { 149 + refresh = Some(RefreshBlockMode::NonBlocking); 150 + } 151 + } 152 + } 153 + 154 + if matches!(ev.ev_type, TouchEventType::Down | TouchEventType::Move) { 155 + self.prev_pos = Some(ev.eg_point()); 156 + } 68 157 } 158 + 159 + if let Some(mode) = refresh { 160 + draw_target.refresh(true, mode); 161 + } 162 + 163 + None 69 164 } 70 165 } 71 166 72 - enum Gui { 73 - MainPage(MainPage), 74 - ScratchpadPage(ScratchpadPage), 167 + struct MainGui { 168 + current_page: Page, 169 + main_page: MainPage, 170 + scratchpad_page: ScratchpadPage, 75 171 } 76 172 77 - impl Gui { 78 - fn tick(&mut self, target: &mut EpdDrawTarget, ev: TouchEvent) { 79 - match self { 80 - Gui::MainPage(page) => { 81 - page.handle_touch(ev); 173 + impl MainGui { 174 + fn new() -> Self { 175 + Self { 176 + current_page: Page::MainPage, 177 + main_page: MainPage::new(), 178 + scratchpad_page: ScratchpadPage::new(), 179 + } 180 + } 82 181 83 - if page.scratchpad_button.clicked(true) { 84 - let scratchpad = ScratchpadPage::new(); 85 - target.clear(BinaryColor::Off).unwrap(); 86 - scratchpad.draw_element(target); 87 - target.refresh(false, true); 88 - *self = Gui::ScratchpadPage(scratchpad); 89 - return; 90 - } 91 - } 182 + fn get_current_page(&self) -> &dyn Gui<Output = Option<Page>> { 183 + match self.current_page { 184 + Page::MainPage => &self.main_page, 185 + Page::ScratchpadPage => &self.scratchpad_page, 186 + } 187 + } 92 188 93 - Gui::ScratchpadPage(page) => { 94 - if matches!(ev.ev_type, TouchEventType::Move | TouchEventType::Up) { 95 - if let Some(prev) = page.prev_pos { 96 - Line::new(prev, ev.eg_point()) 97 - .into_styled(DEFAULT_PRIMITIVE_STYLE) 98 - .draw(target) 99 - .unwrap(); 100 - page.draw_element(target); 101 - target.refresh(true, false); 102 - } 103 - } 189 + fn get_current_page_mut(&mut self) -> &mut dyn Gui<Output = Option<Page>> { 190 + match self.current_page { 191 + Page::MainPage => &mut self.main_page, 192 + Page::ScratchpadPage => &mut self.scratchpad_page, 193 + } 194 + } 195 + } 104 196 105 - page.handle_touch(ev); 197 + impl Gui for MainGui { 198 + type Output = (); 106 199 107 - if page.exit_button.clicked(true) { 108 - let main = MainPage::new(); 109 - target.clear(BinaryColor::Off).unwrap(); 110 - main.draw_element(target); 111 - target.refresh(false, true); 112 - *self = Gui::MainPage(main); 113 - return; 114 - } 200 + fn draw_init(&self, draw_target: &mut EpdDrawTarget) { 201 + self.get_current_page().draw_init(draw_target); 202 + } 115 203 116 - if page.clear_button.clicked(true) { 117 - target.clear(BinaryColor::Off).unwrap(); 118 - page.draw_element(target); 119 - target.refresh(false, false); 120 - } 121 - } 204 + fn tick(&mut self, draw_target: &mut EpdDrawTarget, ev: Event) -> Self::Output { 205 + if let Some(page) = self.get_current_page_mut().tick(draw_target, ev) { 206 + self.current_page = page; 207 + draw_target.clear(BinaryColor::Off).unwrap(); 208 + self.draw_init(draw_target); 209 + draw_target.refresh(false, RefreshBlockMode::BlockAcknowledge); 122 210 } 123 211 } 124 212 } ··· 127 215 debug!("gui_main"); 128 216 129 217 unsafe { set_touch_enabled(true) }; 130 - let main = MainPage::new(); 131 - main.draw_element(&mut draw_target); 132 - draw_target.refresh(false, true); 133 - let mut gui = Gui::MainPage(main); 218 + let mut gui = MainGui::new(); 219 + gui.draw_init(&mut draw_target); 220 + draw_target.refresh(false, RefreshBlockMode::BlockAcknowledge); 134 221 135 222 loop { 136 - while let SafeOption::Some(ev) = next_touch_event() { 223 + while let SafeOption::Some(ev) = next_event() { 137 224 gui.tick(&mut draw_target, ev); 138 225 } 139 226 }
+21 -15
fw16-epd-main/src/main.rs
··· 9 9 #[allow(unused_imports)] 10 10 use defmt_rtt as _; 11 11 12 - use core::cell::{RefCell, UnsafeCell}; 12 + use core::cell::RefCell; 13 13 use critical_section::Mutex; 14 14 use defmt::{debug, info, trace, warn}; 15 15 use embedded_hal::digital::{InputPin, OutputPin, PinState}; ··· 34 34 use fw16_epd_bsp::pac::I2C0; 35 35 use fw16_epd_bsp::pac::interrupt; 36 36 use fw16_epd_gui::draw_target::EpdDrawTarget; 37 - use fw16_epd_program_interface::{SafeOption, TouchEvent, TouchEventType}; 37 + use fw16_epd_program_interface::{Event, RefreshBlockMode, SafeOption, TouchEvent, TouchEventType}; 38 38 use tp370pgh01::rp2040::{Rp2040PervasiveSpiDelays, IoPin}; 39 39 use tp370pgh01::{Tp370pgh01, IMAGE_BYTES}; 40 40 //use crate::programs::Programs; ··· 62 62 63 63 static SERIAL_NUMBER: OnceCell<[u8; 16]> = OnceCell::new(); 64 64 65 - static TOUCH_EVENT_BUFFER: Mutex<RefCell<TouchEventBuffer>> = Mutex::new(RefCell::new(TouchEventBuffer::new())); 65 + static EVENT_QUEUE: Mutex<RefCell<EventQueue>> = Mutex::new(RefCell::new(EventQueue::new())); 66 66 static TOUCH_ENABLED: AtomicBool = AtomicBool::new(false); 67 67 68 68 extern "C" fn write_image(image: &[u8; IMAGE_BYTES]) { 69 69 critical_section::with(|cs| IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(image)); 70 70 } 71 71 72 - extern "C" fn refresh(fast_refresh: bool, block_ack: bool) { 72 + extern "C" fn refresh(fast_refresh: bool, block_mode: RefreshBlockMode) { 73 73 DO_REFRESH.store(true, Ordering::Relaxed); 74 74 FAST_REFRESH.store(fast_refresh, Ordering::Relaxed); 75 75 cortex_m::asm::sev(); 76 76 77 - if block_ack { 77 + if matches!(block_mode, RefreshBlockMode::BlockAcknowledge | RefreshBlockMode::BlockFinish) { 78 78 while DO_REFRESH.load(Ordering::Relaxed) {} 79 + } 80 + 81 + if matches!(block_mode, RefreshBlockMode::BlockFinish) { 82 + while REFRESHING.load(Ordering::Relaxed) {} 79 83 } 80 84 } 81 85 82 - extern "C" fn next_touch_event() -> SafeOption<TouchEvent> { 83 - critical_section::with(|cs| TOUCH_EVENT_BUFFER.borrow_ref_mut(cs).pop()).into() 86 + extern "C" fn next_event() -> SafeOption<Event> { 87 + critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).pop()).into() 84 88 } 85 89 86 90 unsafe extern "C" fn set_touch_enabled(enable: bool) { 87 91 TOUCH_ENABLED.store(enable, Ordering::Relaxed); 88 92 if !enable { 89 - critical_section::with(|cs| TOUCH_EVENT_BUFFER.borrow_ref_mut(cs).clear()); 93 + critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).clear()); 90 94 } 91 95 } 92 96 ··· 94 98 SERIAL_NUMBER.get().unwrap() 95 99 } 96 100 97 - struct TouchEventBuffer<const SIZE: usize = 32> { 98 - inner: [TouchEvent; SIZE], 101 + struct EventQueue<const SIZE: usize = 32> { 102 + inner: [Event; SIZE], 99 103 read_index: usize, 100 104 write_index: usize, 101 105 } 102 106 103 - impl<const SIZE: usize> TouchEventBuffer<SIZE> { 107 + impl<const SIZE: usize> EventQueue<SIZE> { 104 108 const fn new() -> Self { 105 109 Self { 106 - inner: [TouchEvent::new(); SIZE], 110 + inner: [Event::Null; SIZE], 107 111 read_index: 0, 108 112 write_index: 0, 109 113 } ··· 125 129 } 126 130 } 127 131 128 - fn pop(&mut self) -> Option<TouchEvent> { 132 + fn pop(&mut self) -> Option<Event> { 129 133 if self.read_index == self.write_index { 130 134 None 131 135 } else { ··· 135 139 } 136 140 } 137 141 138 - fn push(&mut self, item: TouchEvent) -> bool { 142 + fn push(&mut self, item: Event) -> bool { 139 143 if self.write_index == Self::wrapping_dec(self.read_index) { 140 144 false 141 145 } else { ··· 314 318 } 315 319 } 316 320 321 + critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).push(Event::RefreshFinished)); 317 322 REFRESHING.store(false, Ordering::Relaxed); 318 323 } 319 324 }).unwrap(); ··· 453 458 454 459 debug!("touch event: {}", event); 455 460 456 - if !critical_section::with(|cs| TOUCH_EVENT_BUFFER.borrow_ref_mut(cs).push(event)) { 461 + if !critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).push(Event::Touch(event))) { 457 462 warn!("touch event buffer full"); 458 463 } 459 464 } ··· 484 489 } 485 490 486 491 // Power down display 492 + // (pin is connected to P-ch MOSFET so high = off) 487 493 if let Some(power_pin) = EPD_POWER_PIN { 488 494 power_pin.set_high().unwrap(); 489 495 }
+19 -4
fw16-epd-program-interface/src/lib.rs
··· 29 29 } 30 30 31 31 #[repr(C)] 32 + pub enum RefreshBlockMode { 33 + NonBlocking, 34 + BlockAcknowledge, 35 + BlockFinish, 36 + } 37 + 38 + #[repr(C)] 32 39 #[derive(Copy, Clone)] 33 40 pub struct ProgramFunctionTable { 34 41 pub write_image: extern "C" fn(&[u8; IMAGE_BYTES]), 35 - pub refresh: extern "C" fn(bool, bool), 36 - pub next_touch_event: extern "C" fn() -> SafeOption<TouchEvent>, 42 + pub refresh: extern "C" fn(bool, RefreshBlockMode), 43 + pub next_event: extern "C" fn() -> SafeOption<Event>, 37 44 pub set_touch_enabled: unsafe extern "C" fn(bool), 38 45 pub serial_number: extern "C" fn() -> &'static [u8; 16] 39 46 } ··· 44 51 } 45 52 } 46 53 54 + #[repr(C)] 55 + #[derive(Copy, Clone, Debug, Eq, PartialEq, defmt::Format)] 56 + pub enum Event { 57 + Null, 58 + Touch(TouchEvent), 59 + RefreshFinished, 60 + } 61 + 47 62 #[repr(u8)] 48 - #[derive(Copy, Clone, Debug, defmt::Format)] 63 + #[derive(Copy, Clone, Debug, Eq, PartialEq, defmt::Format)] 49 64 pub enum TouchEventType { 50 65 Down, 51 66 Up, ··· 53 68 } 54 69 55 70 #[repr(C)] 56 - #[derive(Copy, Clone, Debug, defmt::Format)] 71 + #[derive(Copy, Clone, Debug, Eq, PartialEq, defmt::Format)] 57 72 pub struct TouchEvent { 58 73 pub ev_type: TouchEventType, 59 74 pub x: u16,