alpha
Login
or
Join now
arthomnix.dev
/
eepy
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
firmware for my Touchscreen E-Paper Input Module for Framework Laptop 16
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
Pipelines
fw16-epd-main: testing GUI interface
author
arthomnix
date
1 year ago
(Jan 10, 2025, 10:12 PM UTC)
commit
5f959c01
5f959c01a24a93853858be5280a4f5399d9dfd02
parent
c51bfbb6
c51bfbb65e6ca82f68ae6a0f5a123dd24afad261
+231
-103
7 changed files
Expand all
Collapse all
Unified
Split
Cargo.toml
fw16-epd-main
Cargo.toml
src
gui.rs
main.rs
fw16-epd-program-interface
Cargo.toml
src
eg.rs
lib.rs
+2
-1
Cargo.toml
Reviewed
···
24
24
usb-device = "0.3"
25
25
usbd-serial = "0.2"
26
26
crc32fast = { version = "1.4", default-features = false }
27
27
-
embedded-graphics = { version = "0.8.1", features = ["defmt"] }
27
27
+
embedded-graphics = { version = "0.8.1", features = ["defmt"] }
28
28
+
heapless = "0.8"
+3
-2
fw16-epd-main/Cargo.toml
Reviewed
···
1
1
[package]
2
2
name = "fw16-epd-main"
3
3
-
version = "0.1.0"
3
3
+
version = "0.1.0-alpha"
4
4
edition = "2021"
5
5
6
6
[dependencies]
···
21
21
usb-device.workspace = true
22
22
usbd-serial.workspace = true
23
23
crc32fast.workspace = true
24
24
-
embedded-graphics.workspace = true
24
24
+
embedded-graphics.workspace = true
25
25
+
heapless.workspace = true
+38
fw16-epd-main/src/gui.rs
Reviewed
···
1
1
+
use core::fmt::Write;
2
2
+
use defmt::debug;
3
3
+
use embedded_graphics::mono_font::ascii::FONT_10X20;
4
4
+
use embedded_graphics::mono_font::MonoTextStyle;
5
5
+
use embedded_graphics::pixelcolor::BinaryColor;
6
6
+
use embedded_graphics::prelude::*;
7
7
+
use embedded_graphics::text::Text;
8
8
+
use heapless::String;
9
9
+
use fw16_epd_program_interface::eg::EpdDrawTarget;
10
10
+
use fw16_epd_program_interface::{TouchEvent, TouchEventType};
11
11
+
use crate::{next_touch_event, set_touch_enabled};
12
12
+
13
13
+
pub(crate) fn gui_main(mut draw_target: EpdDrawTarget) -> ! {
14
14
+
draw_target.refresh(false);
15
15
+
16
16
+
unsafe { set_touch_enabled(true) };
17
17
+
18
18
+
loop {
19
19
+
let mut touch_event: Option<TouchEvent> = None;
20
20
+
while let Some(ev) = next_touch_event().into() {
21
21
+
debug!("{}", ev);
22
22
+
touch_event = Some(ev);
23
23
+
}
24
24
+
25
25
+
if let Some(ev) = touch_event {
26
26
+
draw_target.clear(BinaryColor::Off).unwrap();
27
27
+
28
28
+
let mut s = String::<32>::new();
29
29
+
write!(s, "{ev}").unwrap();
30
30
+
31
31
+
Text::new(s.as_str(), Point::new(10, 40), MonoTextStyle::new(&FONT_10X20, BinaryColor::On))
32
32
+
.draw(&mut draw_target)
33
33
+
.unwrap();
34
34
+
debug!("triggering refresh");
35
35
+
draw_target.refresh(true);
36
36
+
}
37
37
+
}
38
38
+
}
+109
-86
fw16-epd-main/src/main.rs
Reviewed
···
2
2
#![no_std]
3
3
4
4
mod programs;
5
5
+
mod gui;
5
6
6
7
#[allow(unused_imports)]
7
8
use panic_probe as _;
···
10
11
11
12
use core::cell::RefCell;
12
13
use critical_section::Mutex;
13
13
-
use defmt::{debug, error, info, trace};
14
14
-
use embedded_graphics::mono_font::ascii::FONT_10X20;
15
15
-
use embedded_graphics::mono_font::MonoTextStyle;
16
16
-
use embedded_graphics::pixelcolor::BinaryColor;
17
17
-
use embedded_graphics::prelude::*;
18
18
-
use embedded_graphics::primitives::{Line, PrimitiveStyle};
19
19
-
use embedded_graphics::text::Text;
14
14
+
use defmt::{debug, info, trace, warn};
20
15
use embedded_hal::digital::{OutputPin, PinState};
21
16
use embedded_hal::i2c::I2c;
22
22
-
use embedded_hal_bus::i2c::RefCellDevice;
23
17
use mcp9808::MCP9808;
24
18
use mcp9808::reg_res::ResolutionVal;
25
19
use mcp9808::reg_temp_generic::ReadableTempRegister;
···
38
32
use fw16_epd_bsp::pac::I2C0;
39
33
use fw16_epd_bsp::pac::interrupt;
40
34
use fw16_epd_program_interface::eg::EpdDrawTarget;
41
41
-
use fw16_epd_program_interface::ProgramFunctionTable;
35
35
+
use fw16_epd_program_interface::{SafeOption, TouchEvent, TouchEventType};
42
36
use tp370pgh01::rp2040::{Rp2040PervasiveSpiDelays, IoPin};
43
37
use tp370pgh01::{Tp370pgh01, IMAGE_BYTES};
44
44
-
use crate::programs::Programs;
38
38
+
//use crate::programs::Programs;
45
39
46
40
static CORE1_STACK: Stack<8192> = Stack::new();
47
41
···
59
53
static mut GLOBAL_USB_BUS: Option<UsbBusAllocator<hal::usb::UsbBus>> = None;
60
54
static mut GLOBAL_USB_SERIAL: Option<SerialPort<hal::usb::UsbBus>> = None;
61
55
56
56
+
static TOUCH_EVENT_BUFFER: Mutex<RefCell<TouchEventBuffer>> = Mutex::new(RefCell::new(TouchEventBuffer::new()));
57
57
+
62
58
extern "C" fn write_image(image: &[u8; IMAGE_BYTES]) {
63
59
critical_section::with(|cs| IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(image));
64
60
}
65
61
66
66
-
extern "C" fn refresh() {
62
62
+
extern "C" fn refresh(fast_refresh: bool) {
67
63
DO_REFRESH.store(true, Ordering::Relaxed);
68
68
-
FAST_REFRESH.store(false, Ordering::Relaxed);
64
64
+
FAST_REFRESH.store(fast_refresh, Ordering::Relaxed);
69
65
cortex_m::asm::sev();
66
66
+
// Wait until the refresh has been initiated
67
67
+
while DO_REFRESH.load(Ordering::Relaxed) {}
70
68
}
71
69
72
72
-
extern "C" fn refresh_fast() {
73
73
-
DO_REFRESH.store(true, Ordering::Relaxed);
74
74
-
FAST_REFRESH.store(true, Ordering::Relaxed);
75
75
-
cortex_m::asm::sev();
70
70
+
extern "C" fn next_touch_event() -> SafeOption<TouchEvent> {
71
71
+
critical_section::with(|cs| TOUCH_EVENT_BUFFER.borrow_ref_mut(cs).pop()).into()
72
72
+
}
73
73
+
74
74
+
unsafe extern "C" fn set_touch_enabled(enable: bool) {
75
75
+
if enable {
76
76
+
pac::NVIC::unpend(interrupt::IO_IRQ_BANK0);
77
77
+
pac::NVIC::unmask(interrupt::IO_IRQ_BANK0);
78
78
+
} else {
79
79
+
pac::NVIC::mask(interrupt::IO_IRQ_BANK0);
80
80
+
critical_section::with(|cs| TOUCH_EVENT_BUFFER.borrow_ref_mut(cs).clear());
81
81
+
}
82
82
+
}
83
83
+
84
84
+
struct TouchEventBuffer<const SIZE: usize = 32> {
85
85
+
inner: [TouchEvent; SIZE],
86
86
+
read_index: usize,
87
87
+
write_index: usize,
88
88
+
}
89
89
+
90
90
+
impl<const SIZE: usize> TouchEventBuffer<SIZE> {
91
91
+
const fn new() -> Self {
92
92
+
Self {
93
93
+
inner: [TouchEvent::new(); SIZE],
94
94
+
read_index: 0,
95
95
+
write_index: 0,
96
96
+
}
97
97
+
}
98
98
+
99
99
+
fn wrapping_inc_mut(n: &mut usize) {
100
100
+
if *n == SIZE - 1 {
101
101
+
*n = 0;
102
102
+
} else {
103
103
+
*n += 1;
104
104
+
}
105
105
+
}
106
106
+
107
107
+
fn wrapping_dec(n: usize) -> usize {
108
108
+
if n == 0 {
109
109
+
SIZE - 1
110
110
+
} else {
111
111
+
n - 1
112
112
+
}
113
113
+
}
114
114
+
115
115
+
fn pop(&mut self) -> Option<TouchEvent> {
116
116
+
if self.read_index == self.write_index {
117
117
+
None
118
118
+
} else {
119
119
+
let res = self.inner[self.read_index];
120
120
+
Self::wrapping_inc_mut(&mut self.read_index);
121
121
+
Some(res)
122
122
+
}
123
123
+
}
124
124
+
125
125
+
fn push(&mut self, item: TouchEvent) -> bool {
126
126
+
if self.write_index == Self::wrapping_dec(self.read_index) {
127
127
+
false
128
128
+
} else {
129
129
+
self.inner[self.write_index] = item;
130
130
+
Self::wrapping_inc_mut(&mut self.write_index);
131
131
+
true
132
132
+
}
133
133
+
}
134
134
+
135
135
+
fn clear(&mut self) {
136
136
+
self.read_index = 0;
137
137
+
self.write_index = 0;
138
138
+
}
76
139
}
77
140
78
141
#[entry]
···
95
158
let mut id = [0u8; 8];
96
159
unsafe { rp2040_flash::flash::flash_unique_id(&mut id, true) };
97
160
let mut id = u64::from_be_bytes(id);
98
98
-
info!("Framework 16 EPD firmware version {}, serial no. {:x}", env!("CARGO_PKG_VERSION"), id);
99
161
let mut serial_no = [0u8; 16];
100
162
for c in serial_no.iter_mut().rev() {
101
163
let nibble = (id & 0x0f) as u8;
···
109
171
// Safety: this function never returns, so we should be fine right?
110
172
let serial_no: &'static str = unsafe { &*&raw const *core::str::from_utf8(&serial_no).unwrap() };
111
173
unsafe { cortex_m::interrupt::enable() };
174
174
+
175
175
+
info!("Framework 16 EPD firmware version {}, serial no. {}", env!("CARGO_PKG_VERSION"), serial_no);
112
176
113
177
let mut sio = Sio::new(pac.SIO);
114
178
let pins = Pins::new(
···
139
203
let i2c_sda: I2CSda = pins.i2c_sda.reconfigure();
140
204
let i2c_scl: I2CScl = pins.i2c_scl.reconfigure();
141
205
let int: EpdTouchInt = pins.epd_touch_int.reconfigure();
206
206
+
// Actually unmasking this interrupt is done by calling set_touch_enabled(true)
207
207
+
int.set_interrupt_enabled(EdgeLow, true);
142
208
143
209
let mut timer = Timer::new(pac.TIMER, &mut pac.RESETS, &clocks);
144
210
let mut alarm = timer.alarm_0().unwrap();
···
185
251
core1.spawn(CORE1_STACK.take().unwrap(), move || {
186
252
info!("core1 init");
187
253
188
188
-
int.set_interrupt_enabled(EdgeLow, true);
189
189
-
190
254
let i2c = hal::i2c::I2C::i2c0(pac.I2C0, i2c_sda, i2c_scl, 400.kHz(), &mut pac.RESETS, clocks.system_clock.get_freq());
191
255
192
256
critical_section::with(|cs| {
···
194
258
GLOBAL_I2C.borrow_ref_mut(cs).replace(i2c);
195
259
});
196
260
197
197
-
unsafe {
198
198
-
pac::NVIC::unmask(interrupt::IO_IRQ_BANK0);
199
199
-
pac::NVIC::unmask(interrupt::USBCTRL_IRQ);
200
200
-
}
261
261
+
unsafe { pac::NVIC::unmask(interrupt::USBCTRL_IRQ) };
201
262
202
263
let mut epd = Tp370pgh01::new(cs, IoPin::new(sda), sck, dc, busy, rst, timer, Rp2040PervasiveSpiDelays);
203
264
epd.hard_reset().unwrap();
···
223
284
}
224
285
}).unwrap();
225
286
287
287
+
unsafe { set_touch_enabled(false) };
226
288
227
227
-
let mut draw_target = EpdDrawTarget::new(ProgramFunctionTable {
228
228
-
write_image,
229
229
-
refresh,
230
230
-
refresh_fast,
231
231
-
});
232
289
233
233
-
Text::new("Hello, World!", Point::new(20, 20), MonoTextStyle::new(&FONT_10X20, BinaryColor::On))
234
234
-
.draw(&mut draw_target)
235
235
-
.unwrap();
236
236
-
draw_target.refresh();
290
290
+
let draw_target = EpdDrawTarget::new(write_image, refresh);
237
291
238
238
-
let programs = Programs::new();
239
239
-
for _program in programs {
240
240
-
cortex_m::asm::delay(0);
241
241
-
}
242
242
-
243
243
-
loop {
244
244
-
cortex_m::asm::wfi();
245
245
-
}
292
292
+
gui::gui_main(draw_target);
246
293
}
247
294
248
295
#[interrupt]
···
319
366
320
367
#[interrupt]
321
368
fn IO_IRQ_BANK0() {
322
322
-
static mut FIRST_EVENT: bool = true;
323
369
static mut TOUCH_INT_PIN: Option<EpdTouchInt> = None;
324
324
-
static mut PREV_POS: Option<Point> = None;
325
325
-
static mut DRAW_TARGET: EpdDrawTarget = EpdDrawTarget::new(ProgramFunctionTable {
326
326
-
write_image,
327
327
-
refresh,
328
328
-
refresh_fast,
329
329
-
});
330
370
331
371
trace!("IO_IRQ_BANK0");
332
372
···
336
376
337
377
let mut i2c = critical_section::with(|cs| GLOBAL_I2C.borrow(cs).take());
338
378
339
339
-
if let Some(i2c) = &mut i2c {
340
340
-
let rc = RefCell::new(i2c);
341
341
-
let mut i2c = RefCellDevice::new(&rc);
379
379
+
if let Some(pin) = TOUCH_INT_PIN {
380
380
+
if let Some(i2c) = &mut i2c {
381
381
+
let mut buf = [0u8; 9];
382
382
+
i2c.write_read(0x38u8, &[0x00], &mut buf).unwrap();
383
383
+
let x = (((buf[3] & 0x0f) as u16) << 8) | buf[4] as u16;
384
384
+
let y = (((buf[5] & 0x0f) as u16) << 8) | buf[6] as u16;
342
385
343
343
-
344
344
-
if *FIRST_EVENT == true {
345
345
-
*FIRST_EVENT = false;
346
346
-
} else if let Some(int) = TOUCH_INT_PIN {
347
347
-
if int.interrupt_status(EdgeLow) {
348
348
-
let mut buf = [0u8; 9];
349
349
-
i2c.write_read(0x38u8, &[0x00], &mut buf).unwrap();
350
350
-
let x = (((buf[3] & 0x0f) as i32) << 8) | buf[4] as i32;
351
351
-
let y = (((buf[5] & 0x0f) as i32) << 8) | buf[6] as i32;
352
352
-
debug!("touch event at ({}, {})", x, y);
353
353
-
let pos = Point::new(x, y);
386
386
+
let state = match buf[3] >> 6 {
387
387
+
0 => TouchEventType::Down,
388
388
+
1 => TouchEventType::Up,
389
389
+
2 => TouchEventType::Move,
390
390
+
_ => panic!("received invalid touch event type {}", buf[3] >> 6),
391
391
+
};
354
392
355
355
-
let state = buf[3] >> 6;
393
393
+
let event = TouchEvent {
394
394
+
ev_type: state,
395
395
+
x,
396
396
+
y,
397
397
+
};
356
398
357
357
-
if state == 1 && y > 400 {
358
358
-
DRAW_TARGET.clear(BinaryColor::Off).unwrap();
359
359
-
DRAW_TARGET.refresh();
360
360
-
} else if state == 1 && y < 20 {
361
361
-
hal::rom_data::reset_to_usb_boot(0, 0);
362
362
-
} else {
363
363
-
if state == 1 || state == 2 {
364
364
-
if let Some(prev) = *PREV_POS {
365
365
-
Line::new(prev, pos)
366
366
-
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 5))
367
367
-
.draw(DRAW_TARGET)
368
368
-
.unwrap();
369
369
-
DRAW_TARGET.refresh_fast();
370
370
-
}
371
371
-
}
399
399
+
debug!("touch event: {}", event);
372
400
373
373
-
if state == 0 || state == 2 {
374
374
-
*PREV_POS = Some(Point::new(x, y));
375
375
-
}
376
376
-
}
377
377
-
int.clear_interrupt(EdgeLow);
401
401
+
if !critical_section::with(|cs| TOUCH_EVENT_BUFFER.borrow_ref_mut(cs).push(event)) {
402
402
+
warn!("touch event buffer full");
378
403
}
379
379
-
} else {
380
380
-
error!("int pin is None");
381
404
}
382
382
-
} else {
383
383
-
error!("i2c is None");
405
405
+
406
406
+
pin.clear_interrupt(EdgeLow);
384
407
}
385
408
386
409
critical_section::with(|cs| GLOBAL_I2C.borrow(cs).replace(i2c));
+2
-1
fw16-epd-program-interface/Cargo.toml
Reviewed
···
5
5
6
6
[dependencies]
7
7
tp370pgh01 = { path = "../tp370pgh01" }
8
8
-
embedded-graphics = { workspace = true, optional = true }
8
8
+
embedded-graphics = { workspace = true, optional = true }
9
9
+
defmt.workspace = true
+11
-11
fw16-epd-program-interface/src/eg.rs
Reviewed
···
8
8
use crate::ProgramFunctionTable;
9
9
10
10
pub struct EpdDrawTarget {
11
11
-
functions: ProgramFunctionTable,
11
11
+
write_image: extern "C" fn(&[u8; IMAGE_BYTES]),
12
12
+
refresh: extern "C" fn(bool),
12
13
buf: [u8; IMAGE_BYTES],
13
14
}
14
15
···
55
56
}
56
57
57
58
impl EpdDrawTarget {
58
58
-
pub const fn new(functions: ProgramFunctionTable) -> Self {
59
59
-
Self { functions, buf: [0; IMAGE_BYTES] }
59
59
+
pub const fn new(write_image: extern "C" fn(&[u8; IMAGE_BYTES]), refresh: extern "C" fn(bool)) -> Self {
60
60
+
Self {
61
61
+
write_image,
62
62
+
refresh,
63
63
+
buf: [0; IMAGE_BYTES]
64
64
+
}
60
65
}
61
66
62
62
-
pub fn refresh(&self) {
63
63
-
(self.functions.write_image)(&self.buf);
64
64
-
(self.functions.refresh)();
65
65
-
}
66
66
-
67
67
-
pub fn refresh_fast(&self) {
68
68
-
(self.functions.write_image)(&self.buf);
69
69
-
(self.functions.refresh_fast)();
67
67
+
pub fn refresh(&self, fast_refresh: bool) {
68
68
+
(self.write_image)(&self.buf);
69
69
+
(self.refresh)(fast_refresh);
70
70
}
71
71
}
+66
-2
fw16-epd-program-interface/src/lib.rs
Reviewed
···
3
3
#[cfg(feature = "embedded-graphics")]
4
4
pub mod eg;
5
5
6
6
+
use core::fmt::{Display, Formatter};
6
7
pub use tp370pgh01::IMAGE_BYTES;
7
8
9
9
+
/// Option type with stable ABI.
10
10
+
#[repr(C)]
11
11
+
pub enum SafeOption<T> {
12
12
+
None,
13
13
+
Some(T),
14
14
+
}
15
15
+
16
16
+
impl<T> From<Option<T>> for SafeOption<T> {
17
17
+
fn from(value: Option<T>) -> Self {
18
18
+
match value {
19
19
+
None => SafeOption::None,
20
20
+
Some(v) => SafeOption::Some(v),
21
21
+
}
22
22
+
}
23
23
+
}
24
24
+
25
25
+
impl<T> From<SafeOption<T>> for Option<T> {
26
26
+
fn from(value: SafeOption<T>) -> Self {
27
27
+
match value {
28
28
+
SafeOption::None => None,
29
29
+
SafeOption::Some(v) => Some(v),
30
30
+
}
31
31
+
}
32
32
+
}
33
33
+
8
34
#[repr(C)]
9
35
#[derive(Copy, Clone)]
10
36
pub struct ProgramFunctionTable {
11
37
pub write_image: extern "C" fn(&[u8; IMAGE_BYTES]),
12
12
-
pub refresh: extern "C" fn(),
13
13
-
pub refresh_fast: extern "C" fn(),
38
38
+
pub refresh: extern "C" fn(bool),
39
39
+
pub next_touch_event: extern "C" fn() -> SafeOption<TouchEvent>,
40
40
+
pub set_touch_enabled: unsafe extern "C" fn(bool),
41
41
+
}
42
42
+
43
43
+
#[repr(u8)]
44
44
+
#[derive(Copy, Clone, Debug, defmt::Format)]
45
45
+
pub enum TouchEventType {
46
46
+
Down,
47
47
+
Up,
48
48
+
Move,
49
49
+
}
50
50
+
51
51
+
#[repr(C)]
52
52
+
#[derive(Copy, Clone, Debug, defmt::Format)]
53
53
+
pub struct TouchEvent {
54
54
+
pub ev_type: TouchEventType,
55
55
+
pub x: u16,
56
56
+
pub y: u16,
57
57
+
}
58
58
+
59
59
+
impl TouchEvent {
60
60
+
pub const fn new() -> Self {
61
61
+
Self {
62
62
+
ev_type: TouchEventType::Down,
63
63
+
x: u16::MAX,
64
64
+
y: u16::MAX,
65
65
+
}
66
66
+
}
67
67
+
}
68
68
+
69
69
+
impl Display for TouchEvent {
70
70
+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
71
71
+
let ty = match self.ev_type {
72
72
+
TouchEventType::Down => "Down",
73
73
+
TouchEventType::Up => "Up",
74
74
+
TouchEventType::Move => "Move",
75
75
+
};
76
76
+
write!(f, "{ty} @ ({}, {})", self.x, self.y)
77
77
+
}
14
78
}