.idea
elf2epb
fw16-epd-example-app
fw16-epd-gui
fw16-epd-main
fw16-epd-program-interface
fw16-epd-serial-common
pervasive-spi
tp370pgh01
···
11
11
<sourceFolder url="file://$MODULE_DIR$/fw16-epd-main/src" isTestSource="false" />
12
12
<sourceFolder url="file://$MODULE_DIR$/fw16-epd-program-interface/src" isTestSource="false" />
13
13
<sourceFolder url="file://$MODULE_DIR$/fw16-epd-gui/src" isTestSource="false" />
14
14
+
<sourceFolder url="file://$MODULE_DIR$/fw16-epd-example-app/src" isTestSource="false" />
15
15
+
<sourceFolder url="file://$MODULE_DIR$/elf2epb/src" isTestSource="false" />
16
16
+
<sourceFolder url="file://$MODULE_DIR$/fw16-epd-serial-common/src" isTestSource="false" />
14
17
<excludeFolder url="file://$MODULE_DIR$/fw16_epd_bsp/target" />
15
18
<excludeFolder url="file://$MODULE_DIR$/target" />
16
19
<excludeFolder url="file://$MODULE_DIR$/fw16-epd-bsp/target" />
···
1
1
[workspace]
2
2
resolver = "2"
3
3
4
4
-
members = [
5
5
-
"fw16-epd-bsp", "fw16-epd-gui",
6
6
-
"fw16-epd-main", "fw16-epd-program-interface",
4
4
+
members = [ "elf2epb",
5
5
+
"fw16-epd-bsp", "fw16-epd-example-app", "fw16-epd-gui",
6
6
+
"fw16-epd-main", "fw16-epd-program-interface", "fw16-epd-serial-common",
7
7
"pervasive-spi",
8
8
"tp370pgh01",
9
9
]
···
23
23
usb-device = "0.3"
24
24
usbd-serial = "0.2"
25
25
crc32fast = { version = "1.4", default-features = false }
26
26
-
embedded-graphics = { version = "0.8.1", features = ["defmt"] }
26
26
+
embedded-graphics = "0.8.1"
27
27
heapless = "0.8"
28
28
-
once_cell = { version = "1.20", default-features = false, features = ["critical-section", "portable-atomic"] }
28
28
+
once_cell = { version = "1.20", default-features = false, features = ["critical-section", "portable-atomic"] }
29
29
+
postcard = "1.1"
30
30
+
serde = { version = "1.0", default-features = false, features = ["derive"] }
···
1
1
+
[package]
2
2
+
name = "elf2epb"
3
3
+
version = "0.1.0"
4
4
+
edition = "2021"
5
5
+
6
6
+
[dependencies]
7
7
+
elf = "0.7"
8
8
+
clap = { version = "4.5", features = ["derive"] }
9
9
+
crc32fast.workspace = true
···
1
1
+
use std::path::PathBuf;
2
2
+
use clap::Parser;
3
3
+
use elf::abi::PT_LOAD;
4
4
+
use elf::ElfBytes;
5
5
+
use elf::endian::LittleEndian;
6
6
+
7
7
+
#[derive(Parser, Debug)]
8
8
+
#[command(version, about, long_about = None)]
9
9
+
struct Args {
10
10
+
#[arg(short)]
11
11
+
in_file: PathBuf,
12
12
+
13
13
+
#[arg(short)]
14
14
+
out_file: PathBuf,
15
15
+
}
16
16
+
17
17
+
fn main() {
18
18
+
let args = Args::parse();
19
19
+
20
20
+
let elf_data = std::fs::read(args.in_file).expect("Failed to open ELF file");
21
21
+
22
22
+
let elf = ElfBytes::<LittleEndian>::minimal_parse(&elf_data)
23
23
+
.expect("Failed to parse ELF file");
24
24
+
25
25
+
// Copy all the LOAD segments, like objcopy -O binary
26
26
+
let mut bin: Vec<u8> = Vec::new();
27
27
+
28
28
+
let mut data_paddr = 0u32;
29
29
+
let mut data_vaddr = 0u32;
30
30
+
let mut data_len = 0u32;
31
31
+
32
32
+
for phdr in elf.segments().expect("Failed to parse ELF file") {
33
33
+
if phdr.p_type == PT_LOAD {
34
34
+
bin.extend_from_slice(elf.segment_data(&phdr).expect("Failed to parse ELF file"));
35
35
+
}
36
36
+
37
37
+
// Assume that there is at most one segment that needs to be copied from flash to RAM
38
38
+
// (i.e. the segment containing .data)
39
39
+
if phdr.p_paddr != phdr.p_vaddr {
40
40
+
data_paddr = phdr.p_paddr as u32;
41
41
+
data_vaddr = phdr.p_vaddr as u32;
42
42
+
data_len = phdr.p_memsz as u32;
43
43
+
}
44
44
+
}
45
45
+
46
46
+
let mut bss_paddr = 0u32;
47
47
+
let mut bss_len = 0u32;
48
48
+
if let Some(bss) = elf.section_header_by_name(".bss")
49
49
+
.expect("Failed to parse ELF file")
50
50
+
{
51
51
+
bss_paddr = bss.sh_addr as u32;
52
52
+
bss_len = bss.sh_size as u32;
53
53
+
}
54
54
+
55
55
+
let len = bin.len() as u32;
56
56
+
57
57
+
// Add missing EPB header entries
58
58
+
bin[8..12].copy_from_slice(&len.to_le_bytes()); // len
59
59
+
bin[12..16].copy_from_slice(&data_len.to_le_bytes()); // data_len
60
60
+
bin[16..20].copy_from_slice(&data_paddr.to_le_bytes()); // data_lma
61
61
+
bin[20..24].copy_from_slice(&data_vaddr.to_le_bytes()); // data_vma
62
62
+
bin[24..28].copy_from_slice(&bss_len.to_le_bytes()); // bss_len
63
63
+
bin[28..32].copy_from_slice(&bss_paddr.to_le_bytes()); // bss_vma
64
64
+
65
65
+
let crc = crc32fast::hash(&bin[8..]);
66
66
+
bin[4..8].copy_from_slice(&crc.to_le_bytes()); // crc
67
67
+
68
68
+
std::fs::write(args.out_file, &bin).expect("Failed to write output");
69
69
+
}
···
1
1
+
[package]
2
2
+
name = "fw16-epd-example-app"
3
3
+
version = "0.1.0"
4
4
+
edition = "2021"
5
5
+
6
6
+
[dependencies]
7
7
+
fw16-epd-program-interface = { path = "../fw16-epd-program-interface" }
8
8
+
fw16-epd-gui = { path = "../fw16-epd-gui" }
9
9
+
embedded-graphics.workspace = true
10
10
+
heapless.workspace = true
11
11
+
panic-halt = "1.0"
···
1
1
+
use std::env;
2
2
+
use std::path::Path;
3
3
+
4
4
+
fn main() {
5
5
+
if env::var("TARGET").unwrap() != "thumbv6m-none-eabi" {
6
6
+
return;
7
7
+
}
8
8
+
9
9
+
let linker_script = std::fs::read_to_string("program.x").unwrap();
10
10
+
let slot = env::var("EPD_PROG_SLOT").unwrap();
11
11
+
let out_script = format!("SLOT_N = {slot};\n\n{linker_script}");
12
12
+
13
13
+
let out_dir = env::var_os("OUT_DIR").unwrap();
14
14
+
let out_dir = Path::new(&out_dir);
15
15
+
let out_file = out_dir.join("program.x");
16
16
+
std::fs::write(out_file, &out_script).unwrap();
17
17
+
18
18
+
println!("cargo::rerun-if-changed={}", out_dir.join("program.x").to_str().unwrap());
19
19
+
println!("cargo::rustc-link-search={}", out_dir.to_str().unwrap());
20
20
+
println!("cargo::rustc-link-arg=-Tprogram.x");
21
21
+
println!("cargo::rustc-link-arg=--nmagic");
22
22
+
}
···
1
1
+
#!/bin/sh
2
2
+
3
3
+
rm -r out
4
4
+
mkdir out
5
5
+
mkdir out/bins
6
6
+
7
7
+
for slot in `seq -f "%02g" 31`
8
8
+
do
9
9
+
EPD_PROG_SLOT=$slot cargo build --release
10
10
+
elf2epb -i ../target/thumbv6m-none-eabi/release/fw16-epd-example-app -o "out/bins/fw16-epd-example-app.s$slot.epb"
11
11
+
done
12
12
+
13
13
+
(cd out/bins && tar --zstd -cf ../fw16-epd-example-app.epa *)
···
1
1
+
EXTERN(entry);
2
2
+
ENTRY(entry);
3
3
+
4
4
+
MEMORY {
5
5
+
RAM : ORIGIN = 0x20020000, LENGTH = 136K
6
6
+
SLOT : ORIGIN = 0x10000000 + 512K * SLOT_N, LENGTH = 512K
7
7
+
}
8
8
+
9
9
+
SECTIONS {
10
10
+
.header ORIGIN(SLOT) : {
11
11
+
KEEP(*(.header));
12
12
+
} > SLOT
13
13
+
14
14
+
.text : ALIGN(4) {
15
15
+
. = ALIGN(4);
16
16
+
*(.text .text.*);
17
17
+
. = ALIGN(4);
18
18
+
} > SLOT
19
19
+
20
20
+
.rodata : ALIGN(4) {
21
21
+
. = ALIGN(4);
22
22
+
*(.rodata .rodata.*);
23
23
+
. = ALIGN(4);
24
24
+
} > SLOT
25
25
+
26
26
+
.data : ALIGN(4) {
27
27
+
. = ALIGN(4);
28
28
+
KEEP(*(.data .data.*));
29
29
+
. = ALIGN(4);
30
30
+
} > RAM AT>SLOT
31
31
+
32
32
+
.bss (NOLOAD) : ALIGN(4) {
33
33
+
. = ALIGN(4);
34
34
+
*(.bss .bss.*);
35
35
+
*(COMMON);
36
36
+
. = ALIGN(4);
37
37
+
} > RAM
38
38
+
39
39
+
.uninit (NOLOAD) : ALIGN(4) {
40
40
+
. = ALIGN(4);
41
41
+
*(.uninit .uninit.*);
42
42
+
. = ALIGN(4);
43
43
+
} > RAM
44
44
+
45
45
+
/DISCARD/ : {
46
46
+
*(.ARM.exidx);
47
47
+
*(.ARM.exidx.*);
48
48
+
*(.ARM.extab.*);
49
49
+
}
50
50
+
}
···
1
1
+
#![no_std]
2
2
+
#![no_main]
3
3
+
4
4
+
extern crate panic_halt;
5
5
+
6
6
+
use core::fmt::Write;
7
7
+
use embedded_graphics::draw_target::DrawTarget;
8
8
+
use embedded_graphics::Drawable;
9
9
+
use embedded_graphics::pixelcolor::BinaryColor;
10
10
+
use embedded_graphics::prelude::Point;
11
11
+
use embedded_graphics::text::Text;
12
12
+
use heapless::String;
13
13
+
use fw16_epd_gui::draw_target::EpdDrawTarget;
14
14
+
use fw16_epd_gui::element::button::Button;
15
15
+
use fw16_epd_gui::element::{Gui, DEFAULT_TEXT_STYLE};
16
16
+
use fw16_epd_program_interface::{ProgramFunctionTable, RefreshBlockMode, SafeOption};
17
17
+
use fw16_epd_program_interface::header::ProgramSlotHeader;
18
18
+
19
19
+
extern "C" {
20
20
+
static _end: *const u8;
21
21
+
}
22
22
+
23
23
+
#[link_section = ".header"]
24
24
+
#[used]
25
25
+
static HEADER: ProgramSlotHeader = ProgramSlotHeader::partial(
26
26
+
"Example TESTING FOO BAR BAZ",
27
27
+
env!("CARGO_PKG_VERSION"),
28
28
+
entry,
29
29
+
);
30
30
+
31
31
+
#[used]
32
32
+
static mut FOO: u32 = 10;
33
33
+
34
34
+
#[no_mangle]
35
35
+
pub extern "C" fn entry(pft: &ProgramFunctionTable) {
36
36
+
37
37
+
unsafe { (pft.set_touch_enabled)(true) };
38
38
+
39
39
+
let mut draw_target = EpdDrawTarget::new(pft.write_image, pft.refresh);
40
40
+
41
41
+
let mut button = Button::with_default_style_auto_sized(Point::new(10, 40), "Click me", true);
42
42
+
button.draw_init(&mut draw_target);
43
43
+
draw_target.refresh(false, RefreshBlockMode::BlockAcknowledge);
44
44
+
45
45
+
let mut counter = 0;
46
46
+
47
47
+
loop {
48
48
+
while let SafeOption::Some(ev) = (pft.next_event)() {
49
49
+
let mut needs_refresh = false;
50
50
+
51
51
+
let response = button.tick(&mut draw_target, ev);
52
52
+
if response.clicked {
53
53
+
draw_target.clear(BinaryColor::Off).unwrap();
54
54
+
button.draw_init(&mut draw_target);
55
55
+
56
56
+
counter += 1;
57
57
+
let mut s = String::<16>::new();
58
58
+
write!(s, "{counter}").unwrap();
59
59
+
Text::new(&s, Point::new(10, 80), DEFAULT_TEXT_STYLE)
60
60
+
.draw(&mut draw_target)
61
61
+
.unwrap();
62
62
+
needs_refresh = true;
63
63
+
}
64
64
+
needs_refresh |= response.needs_refresh;
65
65
+
66
66
+
if needs_refresh {
67
67
+
draw_target.refresh(true, RefreshBlockMode::NonBlocking);
68
68
+
}
69
69
+
}
70
70
+
}
71
71
+
}
···
7
7
fw16-epd-program-interface = { path = "../fw16-epd-program-interface", features = ["embedded-graphics"] }
8
8
tp370pgh01 = { path = "../tp370pgh01" }
9
9
embedded-graphics.workspace = true
10
10
-
defmt.workspace = true
10
10
+
defmt = { workspace = true, optional = true }
11
11
+
12
12
+
[features]
13
13
+
defmt-derive = ["defmt", "tp370pgh01/defmt", "embedded-graphics/defmt"]
···
1
1
-
use core::fmt::Binary;
2
1
use embedded_graphics::prelude::*;
3
2
use embedded_graphics::mono_font::MonoTextStyle;
4
3
use embedded_graphics::pixelcolor::BinaryColor;
···
14
13
.baseline(Baseline::Middle)
15
14
.build();
16
15
17
17
-
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, defmt::Format)]
16
16
+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17
17
+
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
18
18
pub struct ButtonOutput {
19
19
pub clicked: bool,
20
20
pub needs_refresh: bool,
21
21
}
22
22
23
23
-
#[derive(Debug, defmt::Format)]
23
23
+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
24
24
+
#[derive(Debug)]
24
25
pub struct Button<'a> {
25
26
pub rect: RoundedRectangle,
26
27
pub label: &'a str,
···
6
6
use embedded_graphics::pixelcolor::BinaryColor;
7
7
use embedded_graphics::prelude::*;
8
8
use embedded_graphics::primitives::{PrimitiveStyle, PrimitiveStyleBuilder, Rectangle};
9
9
-
use fw16_epd_program_interface::{Event, TouchEvent};
9
9
+
use fw16_epd_program_interface::Event;
10
10
use tp370pgh01::{DIM_X, DIM_Y};
11
11
use crate::draw_target::EpdDrawTarget;
12
12
···
1
1
+
[build]
2
2
+
target = "thumbv6m-none-eabi"
3
3
+
1
4
[target.thumbv6m-none-eabi]
2
5
rustflags = [
3
6
"-C", "link-arg=--nmagic",
···
5
5
6
6
[dependencies]
7
7
fw16-epd-bsp = { path = "../fw16-epd-bsp" }
8
8
-
fw16-epd-program-interface = { path = "../fw16-epd-program-interface" }
9
9
-
fw16-epd-gui = { path = "../fw16-epd-gui" }
10
10
-
tp370pgh01 = { path = "../tp370pgh01", features = ["rp2040"] }
8
8
+
fw16-epd-program-interface = { path = "../fw16-epd-program-interface", features = ["defmt"] }
9
9
+
fw16-epd-gui = { path = "../fw16-epd-gui", features = ["defmt-derive"] }
10
10
+
fw16-epd-serial-common = { path = "../fw16-epd-serial-common" }
11
11
+
tp370pgh01 = { path = "../tp370pgh01", features = ["rp2040", "defmt"] }
11
12
cortex-m.workspace = true
12
13
cortex-m-rt.workspace = true
13
14
rp2040-flash.workspace = true
···
21
22
usb-device.workspace = true
22
23
usbd-serial.workspace = true
23
24
crc32fast.workspace = true
24
24
-
embedded-graphics.workspace = true
25
25
+
embedded-graphics = { workspace = true, features = ["defmt"] }
25
26
heapless.workspace = true
26
27
once_cell.workspace = true
···
5
5
use embedded_graphics::primitives::{Circle, Line, PrimitiveStyle, Rectangle};
6
6
use fw16_epd_gui::draw_target::EpdDrawTarget;
7
7
use fw16_epd_gui::element::button::Button;
8
8
-
use fw16_epd_gui::element::{Gui, DEFAULT_PRIMITIVE_STYLE};
8
8
+
use fw16_epd_gui::element::Gui;
9
9
use fw16_epd_gui::element::slider::Slider;
10
10
use fw16_epd_program_interface::{RefreshBlockMode, SafeOption, Event, TouchEventType};
11
11
use crate::{next_event, set_touch_enabled};
···
3
3
4
4
mod programs;
5
5
mod gui;
6
6
+
mod serial;
7
7
+
mod ringbuffer;
6
8
7
9
#[allow(unused_imports)]
8
10
use panic_probe as _;
···
13
15
use critical_section::Mutex;
14
16
use defmt::{debug, info, trace, warn};
15
17
use embedded_hal::delay::DelayNs;
16
16
-
use embedded_hal::digital::{InputPin, OutputPin, PinState};
18
18
+
use embedded_hal::digital::OutputPin;
17
19
use embedded_hal::i2c::I2c;
18
20
use mcp9808::MCP9808;
19
21
use mcp9808::reg_conf::{Configuration, ShutdownMode};
20
22
use mcp9808::reg_res::{Resolution, ResolutionVal};
21
23
use mcp9808::reg_temp_generic::ReadableTempRegister;
22
24
use once_cell::sync::OnceCell;
23
23
-
use portable_atomic::{AtomicBool, AtomicU128, AtomicU8};
25
25
+
use portable_atomic::{AtomicBool, AtomicU8};
24
26
use portable_atomic::Ordering;
25
27
use usb_device::bus::UsbBusAllocator;
26
28
use usb_device::prelude::*;
···
38
40
use fw16_epd_program_interface::{Event, RefreshBlockMode, SafeOption, TouchEvent, TouchEventType};
39
41
use tp370pgh01::rp2040::{Rp2040PervasiveSpiDelays, IoPin};
40
42
use tp370pgh01::{Tp370pgh01, IMAGE_BYTES};
43
43
+
use crate::ringbuffer::RingBuffer;
41
44
//use crate::programs::Programs;
42
45
43
46
static CORE1_STACK: Stack<8192> = Stack::new();
···
63
66
64
67
static SERIAL_NUMBER: OnceCell<[u8; 16]> = OnceCell::new();
65
68
66
66
-
static EVENT_QUEUE: Mutex<RefCell<EventQueue>> = Mutex::new(RefCell::new(EventQueue::new()));
69
69
+
static EVENT_QUEUE: Mutex<RefCell<RingBuffer<Event>>> = Mutex::new(RefCell::new(RingBuffer::new()));
67
70
static TOUCH_ENABLED: AtomicBool = AtomicBool::new(false);
68
71
72
72
+
static FLASHING: AtomicBool = AtomicBool::new(false);
73
73
+
static FLASHING_ACK: AtomicBool = AtomicBool::new(false);
74
74
+
69
75
extern "C" fn write_image(image: &[u8; IMAGE_BYTES]) {
70
76
critical_section::with(|cs| IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(image));
71
77
}
···
99
105
SERIAL_NUMBER.get().unwrap()
100
106
}
101
107
102
102
-
struct EventQueue<const SIZE: usize = 32> {
103
103
-
inner: [Event; SIZE],
104
104
-
read_index: usize,
105
105
-
write_index: usize,
106
106
-
}
108
108
+
/// Function in RAM to be executed by core1 whilst flashing programs (core1 cannot be executing code
109
109
+
/// from flash whilst writing/erasing flash)
110
110
+
///
111
111
+
/// To exit this function from core0, set [FLASHING] to false and SEV
112
112
+
#[link_section = ".data.ram_func"]
113
113
+
fn core1_flash_wait() {
114
114
+
cortex_m::interrupt::disable();
115
115
+
FLASHING_ACK.store(true, Ordering::Relaxed);
107
116
108
108
-
impl<const SIZE: usize> EventQueue<SIZE> {
109
109
-
const fn new() -> Self {
110
110
-
Self {
111
111
-
inner: [Event::Null; SIZE],
112
112
-
read_index: 0,
113
113
-
write_index: 0,
114
114
-
}
117
117
+
while FLASHING.load(Ordering::Relaxed) {
118
118
+
cortex_m::asm::wfe();
115
119
}
116
120
117
117
-
fn wrapping_inc_mut(n: &mut usize) {
118
118
-
if *n == SIZE - 1 {
119
119
-
*n = 0;
120
120
-
} else {
121
121
-
*n += 1;
122
122
-
}
123
123
-
}
124
124
-
125
125
-
fn wrapping_dec(n: usize) -> usize {
126
126
-
if n == 0 {
127
127
-
SIZE - 1
128
128
-
} else {
129
129
-
n - 1
130
130
-
}
131
131
-
}
132
132
-
133
133
-
fn pop(&mut self) -> Option<Event> {
134
134
-
if self.read_index == self.write_index {
135
135
-
None
136
136
-
} else {
137
137
-
let res = self.inner[self.read_index];
138
138
-
Self::wrapping_inc_mut(&mut self.read_index);
139
139
-
Some(res)
140
140
-
}
141
141
-
}
142
142
-
143
143
-
fn push(&mut self, item: Event) -> bool {
144
144
-
if self.write_index == Self::wrapping_dec(self.read_index) {
145
145
-
false
146
146
-
} else {
147
147
-
self.inner[self.write_index] = item;
148
148
-
Self::wrapping_inc_mut(&mut self.write_index);
149
149
-
true
150
150
-
}
151
151
-
}
152
152
-
153
153
-
fn clear(&mut self) {
154
154
-
self.read_index = 0;
155
155
-
self.write_index = 0;
156
156
-
}
121
121
+
FLASHING_ACK.store(false, Ordering::Relaxed);
122
122
+
unsafe { cortex_m::interrupt::enable() };
157
123
}
158
124
159
125
#[entry]
···
294
260
GLOBAL_USB_DEVICE = Some(usb_device);
295
261
}
296
262
263
263
+
unsafe { pac::NVIC::unmask(interrupt::USBCTRL_IRQ) };
264
264
+
297
265
let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio.fifo);
298
266
let core1 = &mut mc.cores()[1];
299
267
core1.spawn(CORE1_STACK.take().unwrap(), move || {
300
268
info!("core1 init");
301
301
-
302
302
-
unsafe { pac::NVIC::unmask(interrupt::USBCTRL_IRQ) };
303
269
304
270
let mut epd = Tp370pgh01::new(cs, IoPin::new(sda), sck, dc, busy, rst, timer, Rp2040PervasiveSpiDelays);
305
271
···
308
274
309
275
loop {
310
276
cortex_m::asm::wfe();
277
277
+
278
278
+
if FLASHING.load(Ordering::Relaxed) {
279
279
+
core1_flash_wait();
280
280
+
continue;
281
281
+
}
311
282
312
283
if EPD_NEEDS_HARD_RESET.swap(false, Ordering::Relaxed) {
313
284
epd.hard_reset().unwrap();
···
340
311
pac::NVIC::unmask(interrupt::SW0_IRQ);
341
312
};
342
313
314
314
+
/*let program = Programs::new().next();
315
315
+
if let Some(program) = program {
316
316
+
unsafe {
317
317
+
program.load();
318
318
+
let pft = ProgramFunctionTable {
319
319
+
write_image,
320
320
+
refresh,
321
321
+
next_event,
322
322
+
set_touch_enabled,
323
323
+
serial_number,
324
324
+
};
325
325
+
program.entry()(&pft);
326
326
+
}
327
327
+
}*/
328
328
+
343
329
let draw_target = EpdDrawTarget::new(write_image, refresh);
344
330
gui::gui_main(draw_target);
345
331
}
···
381
367
}
382
368
383
369
critical_section::with(|cs| GLOBAL_I2C.borrow(cs).replace(i2c));
384
384
-
}
385
385
-
386
386
-
#[interrupt]
387
387
-
fn USBCTRL_IRQ() {
388
388
-
static mut INDEX: usize = 0;
389
389
-
390
390
-
trace!("USBCTRL_IRQ");
391
391
-
392
392
-
// Safety: These are only accessed within this interrupt handler, or in main() before the
393
393
-
// interrupt is enabled.
394
394
-
#[allow(static_mut_refs)]
395
395
-
let usb_dev = unsafe { GLOBAL_USB_DEVICE.as_mut().unwrap() };
396
396
-
#[allow(static_mut_refs)]
397
397
-
let serial = unsafe { GLOBAL_USB_SERIAL.as_mut().unwrap() };
398
398
-
399
399
-
if usb_dev.poll(&mut [serial]) {
400
400
-
critical_section::with(|cs| {
401
401
-
let mut buf = IMAGE_BUFFER.borrow_ref_mut(cs);
402
402
-
match serial.read(&mut buf.as_mut()[*INDEX..]) {
403
403
-
Err(UsbError::WouldBlock) => {},
404
404
-
Err(e) => panic!("{e:?}"),
405
405
-
Ok(count) => {
406
406
-
*INDEX += count;
407
407
-
if *INDEX >= (240 * 416) / 8 {
408
408
-
info!("Finished rx frame over USB");
409
409
-
*INDEX = 0;
410
410
-
FAST_REFRESH.store(false, Ordering::Relaxed);
411
411
-
DO_REFRESH.store(true, Ordering::Relaxed);
412
412
-
}
413
413
-
}
414
414
-
}
415
415
-
})
416
416
-
}
417
370
}
418
371
419
372
#[interrupt]
···
1
1
use core::marker::PhantomData;
2
2
-
use core::str::Utf8Error;
3
3
-
use defmt::debug;
4
4
-
use fw16_epd_program_interface::ProgramFunctionTable;
5
5
-
6
6
-
#[link_section = ".prog1"]
7
7
-
#[used]
8
8
-
static PROG1: [u32; 11] = [
9
9
-
0, // block_erase_cycles
10
10
-
1419452448, // crc
11
11
-
44, // len
12
12
-
4, // name_len
13
13
-
32, // name_offset
14
14
-
5, // version_len
15
15
-
36, // version_offset
16
16
-
0, // entry_offset
17
17
-
0x74736554,
18
18
-
0x2E302E30,
19
19
-
0x00000032,
20
20
-
];
21
21
-
22
22
-
const XIP_BASE: *const u8 = 0x10000000 as *const u8;
23
23
-
24
24
-
const SLOT_SIZE: usize = 0x80000;
2
2
+
use fw16_epd_program_interface::header::*;
25
3
26
4
pub(crate) const unsafe fn slot<'a>(id: u8) -> &'a ProgramSlotHeader {
27
5
if id < 1 || id > 31 {
···
31
9
&*XIP_BASE.add(SLOT_SIZE * id as usize).cast::<ProgramSlotHeader>()
32
10
}
33
11
34
34
-
#[repr(C)]
35
35
-
pub(crate) struct ProgramSlotHeader {
36
36
-
pub(crate) block_erase_cycles: usize,
37
37
-
pub(crate) crc: u32,
38
38
-
pub(crate) len: usize,
39
39
-
pub(crate) name_len: usize,
40
40
-
pub(crate) name_offset: usize,
41
41
-
pub(crate) version_len: usize,
42
42
-
pub(crate) version_offset: usize,
43
43
-
pub(crate) entry_offset: usize,
44
44
-
}
45
45
-
46
46
-
impl ProgramSlotHeader {
47
47
-
fn ptr(&self) -> *const u8 {
48
48
-
(&raw const *self).cast()
49
49
-
}
50
50
-
51
51
-
pub(crate) fn is_valid_program(&self) -> bool {
52
52
-
self.len != 0
53
53
-
&& self.check_crc()
54
54
-
&& self.name_offset.saturating_add(self.name_len) <= self.len
55
55
-
&& self.version_offset.saturating_add(self.version_len) <= self.len
56
56
-
&& self.entry_offset < self.len
57
57
-
&& self.name().is_ok()
58
58
-
&& self.version_string().is_ok()
59
59
-
}
60
60
-
61
61
-
pub(crate) fn check_crc(&self) -> bool {
62
62
-
if self.len >= SLOT_SIZE || self.len < size_of::<ProgramSlotHeader>() {
63
63
-
return false;
64
64
-
}
65
65
-
66
66
-
let ptr = unsafe { self.ptr().add(8) };
67
67
-
let slice = unsafe { core::slice::from_raw_parts(ptr, self.len - 8) };
68
68
-
let crc = crc32fast::hash(slice);
69
69
-
crc == self.crc
70
70
-
}
71
71
-
72
72
-
pub(crate) fn name(&self) -> Result<&str, Utf8Error> {
73
73
-
let ptr = unsafe { self.ptr().add(self.name_offset) };
74
74
-
let slice = unsafe { core::slice::from_raw_parts(ptr, self.name_len) };
75
75
-
core::str::from_utf8(slice)
76
76
-
}
77
77
-
78
78
-
pub(crate) fn version_string(&self) -> Result<&str, Utf8Error> {
79
79
-
let ptr = unsafe { self.ptr().add(self.version_offset) };
80
80
-
let slice = unsafe { core::slice::from_raw_parts(ptr, self.version_len) };
81
81
-
core::str::from_utf8(slice)
82
82
-
}
83
83
-
84
84
-
pub(crate) unsafe fn entry(&self) -> unsafe extern "C" fn(&ProgramFunctionTable) -> () {
85
85
-
let ptr = self.ptr().add(self.entry_offset);
86
86
-
core::mem::transmute(ptr)
87
87
-
}
88
88
-
}
89
12
90
13
pub(crate) struct Programs<'a> {
91
14
id: u8,
···
102
25
type Item = &'a ProgramSlotHeader;
103
26
104
27
fn next(&mut self) -> Option<Self::Item> {
28
28
+
return None;
29
29
+
/*
105
30
loop {
106
31
self.id += 1;
107
32
···
118
43
debug!("No program found in slot {}", self.id);
119
44
}
120
45
121
121
-
}
46
46
+
}*/
122
47
}
123
48
124
49
fn size_hint(&self) -> (usize, Option<usize>) {
···
1
1
+
use core::mem::MaybeUninit;
2
2
+
3
3
+
pub(crate) struct RingBuffer<T, const SIZE: usize = 32> {
4
4
+
inner: [MaybeUninit<T>; SIZE],
5
5
+
read_index: usize,
6
6
+
write_index: usize,
7
7
+
}
8
8
+
9
9
+
impl<T: Copy, const SIZE: usize> RingBuffer<T, SIZE> {
10
10
+
pub(crate) const fn new() -> Self {
11
11
+
Self {
12
12
+
inner: [MaybeUninit::uninit(); SIZE],
13
13
+
read_index: 0,
14
14
+
write_index: 0,
15
15
+
}
16
16
+
}
17
17
+
18
18
+
fn wrapping_inc_mut(n: &mut usize) {
19
19
+
if *n == SIZE - 1 {
20
20
+
*n = 0;
21
21
+
} else {
22
22
+
*n += 1;
23
23
+
}
24
24
+
}
25
25
+
26
26
+
fn wrapping_dec(n: usize) -> usize {
27
27
+
if n == 0 {
28
28
+
SIZE - 1
29
29
+
} else {
30
30
+
n - 1
31
31
+
}
32
32
+
}
33
33
+
34
34
+
pub(crate) fn pop(&mut self) -> Option<T> {
35
35
+
if self.read_index == self.write_index {
36
36
+
None
37
37
+
} else {
38
38
+
let res = self.inner[self.read_index];
39
39
+
Self::wrapping_inc_mut(&mut self.read_index);
40
40
+
// Safety: The read_index == write_index check above ensures we cannot pop an
41
41
+
// uninitialised element
42
42
+
unsafe { Some(res.assume_init()) }
43
43
+
}
44
44
+
}
45
45
+
46
46
+
pub(crate) fn push(&mut self, item: T) -> bool {
47
47
+
if self.write_index == Self::wrapping_dec(self.read_index) {
48
48
+
false
49
49
+
} else {
50
50
+
self.inner[self.write_index] = MaybeUninit::new(item);
51
51
+
Self::wrapping_inc_mut(&mut self.write_index);
52
52
+
true
53
53
+
}
54
54
+
}
55
55
+
56
56
+
pub(crate) fn clear(&mut self) {
57
57
+
self.read_index = 0;
58
58
+
self.write_index = 0;
59
59
+
}
60
60
+
}
···
1
1
+
use core::sync::atomic::Ordering;
2
2
+
use crate::{interrupt, FLASHING, FLASHING_ACK};
3
3
+
use defmt::{debug, trace};
4
4
+
use usbd_serial::SerialPort;
5
5
+
use fw16_epd_bsp::hal::usb::UsbBus;
6
6
+
use fw16_epd_bsp::pac;
7
7
+
use fw16_epd_program_interface::header::ProgramSlotHeader;
8
8
+
use fw16_epd_program_interface::RefreshBlockMode;
9
9
+
use fw16_epd_serial_common::{Response, SerialCommand};
10
10
+
use tp370pgh01::IMAGE_BYTES;
11
11
+
use crate::{refresh, write_image, GLOBAL_USB_DEVICE, GLOBAL_USB_SERIAL};
12
12
+
use crate::programs::Programs;
13
13
+
14
14
+
#[derive(Copy, Clone, Debug, defmt::Format)]
15
15
+
enum SerialState {
16
16
+
ReadyForCommand,
17
17
+
18
18
+
ReceivingImage {
19
19
+
fast_refresh: bool,
20
20
+
index: usize,
21
21
+
},
22
22
+
23
23
+
FlashingProgram {
24
24
+
index: usize,
25
25
+
page: usize,
26
26
+
num_pages: Option<usize>,
27
27
+
remainder: Option<usize>,
28
28
+
},
29
29
+
}
30
30
+
31
31
+
fn write_all(serial: &mut SerialPort<UsbBus>, mut buf: &[u8]) {
32
32
+
while !buf.is_empty() {
33
33
+
let _ = serial.write(buf).map(|len| buf = &buf[len..]);
34
34
+
}
35
35
+
}
36
36
+
37
37
+
/// Safety:
38
38
+
///
39
39
+
/// This function takes care of the main safety requirements of flashing, but the
40
40
+
/// caller must ensure that the `slot` and `page` parameters are valid and do
41
41
+
/// not produce an address outside the flash's range. Additionally, do not write
42
42
+
/// to slot 0 as this contains the firmware.
43
43
+
unsafe fn write_flash(buf: &[u8], slot: u8, page: usize) {
44
44
+
debug!("Begin write slot {} page {}", slot, page);
45
45
+
46
46
+
// Make sure core1 is running code from RAM with interrupts disabled
47
47
+
FLASHING.store(true, Ordering::Relaxed);
48
48
+
cortex_m::asm::sev();
49
49
+
// Wait until core1 has acknowledged that it is now in RAM code
50
50
+
while !FLASHING_ACK.load(Ordering::Relaxed) {}
51
51
+
// Disable interrupts on this core
52
52
+
cortex_m::interrupt::disable();
53
53
+
54
54
+
unsafe {
55
55
+
rp2040_flash::flash::flash_range_erase_and_program(
56
56
+
(slot as u32) * 512 * 1024 + (page as u32) * 4096,
57
57
+
buf,
58
58
+
true
59
59
+
);
60
60
+
}
61
61
+
62
62
+
// Enable interrupts
63
63
+
unsafe { cortex_m::interrupt::enable() }
64
64
+
// Wake up core1
65
65
+
FLASHING.store(false, Ordering::Relaxed);
66
66
+
cortex_m::asm::sev();
67
67
+
68
68
+
debug!("End write slot {} page {}", slot, page);
69
69
+
}
70
70
+
71
71
+
#[interrupt]
72
72
+
fn USBCTRL_IRQ() {
73
73
+
static mut STATE: SerialState = SerialState::ReadyForCommand;
74
74
+
75
75
+
// Receive buffer. Size equal to IMAGE_BYTES so it can store an entire frame; also used for
76
76
+
// receiving flash applications.
77
77
+
static mut BUF: [u8; IMAGE_BYTES] = [0; IMAGE_BYTES];
78
78
+
79
79
+
trace!("USBCTRL_IRQ");
80
80
+
81
81
+
// Safety: These are only accessed within this interrupt handler, or in main() before the
82
82
+
// interrupt is enabled.
83
83
+
#[allow(static_mut_refs)]
84
84
+
let usb_dev = unsafe { GLOBAL_USB_DEVICE.as_mut().unwrap() };
85
85
+
#[allow(static_mut_refs)]
86
86
+
let serial = unsafe { GLOBAL_USB_SERIAL.as_mut().unwrap() };
87
87
+
88
88
+
if usb_dev.poll(&mut [serial]) {
89
89
+
match STATE {
90
90
+
SerialState::ReadyForCommand => {
91
91
+
let mut cmd_buf = [0u8];
92
92
+
if let Ok(count) = serial.read(&mut cmd_buf) {
93
93
+
if count == 0 {
94
94
+
return;
95
95
+
}
96
96
+
97
97
+
match SerialCommand::try_from(cmd_buf[0]) {
98
98
+
Ok(SerialCommand::RefreshNormal) => *STATE = SerialState::ReceivingImage { fast_refresh: false, index: 0 },
99
99
+
Ok(SerialCommand::RefreshFast) => *STATE = SerialState::ReceivingImage { fast_refresh: true, index: 0 },
100
100
+
Ok(SerialCommand::UploadProgram) => *STATE = SerialState::FlashingProgram { index: 0, page: 0, num_pages: None, remainder: None },
101
101
+
Ok(_) => write_all(serial, &[Response::UnknownCommand as u8]),
102
102
+
Err(_) => write_all(serial, &[Response::UnknownCommand as u8]),
103
103
+
}
104
104
+
}
105
105
+
}
106
106
+
107
107
+
SerialState::ReceivingImage { fast_refresh, index } => {
108
108
+
if let Ok(count) = serial.read(&mut BUF[*index..]) {
109
109
+
*index += count;
110
110
+
if *index == IMAGE_BYTES {
111
111
+
write_image(BUF);
112
112
+
refresh(*fast_refresh, RefreshBlockMode::NonBlocking);
113
113
+
write_all(serial, &[Response::Ack as u8]);
114
114
+
*STATE = SerialState::ReadyForCommand;
115
115
+
}
116
116
+
}
117
117
+
}
118
118
+
119
119
+
SerialState::FlashingProgram { index, page, num_pages, remainder } => {
120
120
+
debug!("Flashing program - page {}", *page);
121
121
+
// Write page 0 last - this is the header, so we only want to write it once everything
122
122
+
// else is written successfully
123
123
+
// Keep page 0 in the first 4096 bytes of BUF for the end
124
124
+
debug!("{} {} {} {}", index, page, num_pages, remainder);
125
125
+
if *page == 0 {
126
126
+
if let Ok(count) = serial.read(&mut BUF[*index..4096]) {
127
127
+
*index += count;
128
128
+
129
129
+
if num_pages.is_none() && *index >= 12 {
130
130
+
let mut b = [0u8; 4];
131
131
+
b.copy_from_slice(&BUF[8..12]);
132
132
+
let num_bytes = usize::from_le_bytes(b);
133
133
+
debug!("Program is {} bytes ({} pages) long", num_bytes, num_bytes.div_ceil(4096));
134
134
+
*num_pages = Some(num_bytes.div_ceil(4096));
135
135
+
*remainder = Some(num_bytes % 4096);
136
136
+
}
137
137
+
138
138
+
if *index == 4096 {
139
139
+
*index = 0;
140
140
+
*page += 1;
141
141
+
}
142
142
+
}
143
143
+
} else {
144
144
+
if let Ok(count) = serial.read(&mut BUF[(4096 + *index)..8192]) {
145
145
+
*index += count;
146
146
+
147
147
+
let num_pages = num_pages.unwrap();
148
148
+
let remainder = remainder.unwrap();
149
149
+
if *index == 4096 || (*page == num_pages - 1 && *index == remainder) {
150
150
+
*index = 0;
151
151
+
152
152
+
// Actually write the flash page
153
153
+
// TODO: get next slot instead of always using slot 1
154
154
+
// TODO: wear levelling
155
155
+
unsafe { write_flash(&BUF[4096..8192], 1, *page) };
156
156
+
157
157
+
*page += 1;
158
158
+
// If this is the last page, also flash the first page which we didn't
159
159
+
// do at the start
160
160
+
if *page == num_pages {
161
161
+
unsafe { write_flash(&BUF[0..4096], 1, 0) };
162
162
+
163
163
+
// Invalidate the XIP cache, in case something from the flash area
164
164
+
// we just wrote is in there
165
165
+
unsafe {
166
166
+
// FIXME: steal
167
167
+
let xip = pac::Peripherals::steal().XIP_CTRL;
168
168
+
xip.flush().write(|w| w.flush().set_bit());
169
169
+
xip.flush().read();
170
170
+
};
171
171
+
172
172
+
let program = unsafe { &*(0x10080000 as *const ProgramSlotHeader) };
173
173
+
debug!("{} {}", program.name().unwrap(), program.version().unwrap());
174
174
+
175
175
+
*STATE = SerialState::ReadyForCommand;
176
176
+
}
177
177
+
}
178
178
+
}
179
179
+
}
180
180
+
}
181
181
+
}
182
182
+
}
183
183
+
}
···
5
5
6
6
[dependencies]
7
7
tp370pgh01 = { path = "../tp370pgh01" }
8
8
-
defmt.workspace = true
9
9
-
embedded-graphics = { workspace = true, optional = true }
8
8
+
defmt = { workspace = true, optional = true }
9
9
+
embedded-graphics = { workspace = true, optional = true }
10
10
+
serde.workspace = true
11
11
+
crc32fast.workspace = true
···
1
1
#![no_std]
2
2
3
3
+
pub mod header;
4
4
+
3
5
use core::fmt::{Display, Formatter};
4
6
pub use tp370pgh01::IMAGE_BYTES;
5
7
6
8
/// Option type with stable ABI.
7
9
#[repr(C)]
10
10
+
#[derive(Debug, serde::Serialize, serde::Deserialize)]
8
11
pub enum SafeOption<T> {
9
12
None,
10
13
Some(T),
···
52
55
}
53
56
54
57
#[repr(C)]
55
55
-
#[derive(Copy, Clone, Debug, Eq, PartialEq, defmt::Format)]
58
58
+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
59
59
+
#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
56
60
pub enum Event {
57
61
Null,
58
62
Touch(TouchEvent),
···
60
64
}
61
65
62
66
#[repr(u8)]
63
63
-
#[derive(Copy, Clone, Debug, Eq, PartialEq, defmt::Format)]
67
67
+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
68
68
+
#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
64
69
pub enum TouchEventType {
65
70
Down,
66
71
Up,
···
68
73
}
69
74
70
75
#[repr(C)]
71
71
-
#[derive(Copy, Clone, Debug, Eq, PartialEq, defmt::Format)]
76
76
+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
77
77
+
#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
72
78
pub struct TouchEvent {
73
79
pub ev_type: TouchEventType,
74
80
pub x: u16,
···
1
1
+
[package]
2
2
+
name = "fw16-epd-serial-common"
3
3
+
version = "0.1.0"
4
4
+
edition = "2021"
5
5
+
6
6
+
[dependencies]
···
1
1
+
#![no_std]
2
2
+
3
3
+
#[repr(u8)]
4
4
+
pub enum SerialCommand {
5
5
+
/// Refresh the screen. Must be followed by exactly 12480 bytes of image data.
6
6
+
RefreshNormal = 0,
7
7
+
/// Fast refresh the screen. Must be followed by exactly 12480 bytes of image data.
8
8
+
RefreshFast = 1,
9
9
+
10
10
+
/// Enter Host App mode. In this mode, events will not be processed by the builtin UI and can
11
11
+
/// instead be retrieved by host programs using the [SerialCommand::NextEvent] command. This
12
12
+
/// mode should be used by all host programs writing images to the display.
13
13
+
EnterHostApp = 2,
14
14
+
/// Exit Host App mode (see [SerialCommand::EnterHostApp]).
15
15
+
ExitHostApp = 3,
16
16
+
17
17
+
/// Get the next event. Only works in Host App mode.
18
18
+
NextEvent = 4,
19
19
+
20
20
+
/// Disable touch. Touch events will no longer be added to the event queue. Only works in Host
21
21
+
/// App mode.
22
22
+
DisableTouch = 5,
23
23
+
/// Enable touch.
24
24
+
EnableTouch = 6,
25
25
+
26
26
+
/// Get the program slot that will be used to store the next uploaded program. This command is
27
27
+
/// only available when not in Host App mode.
28
28
+
GetProgramSlot = 7,
29
29
+
/// Upload a program. The program will be stored in the slot returned by the previous
30
30
+
/// GetProgramSlot call. The host program should
31
31
+
UploadProgram = 8,
32
32
+
}
33
33
+
34
34
+
impl TryFrom<u8> for SerialCommand {
35
35
+
type Error = ();
36
36
+
37
37
+
fn try_from(value: u8) -> Result<Self, Self::Error> {
38
38
+
match value {
39
39
+
x if x == SerialCommand::RefreshNormal as u8 => Ok(SerialCommand::RefreshNormal),
40
40
+
x if x == SerialCommand::RefreshFast as u8 => Ok(SerialCommand::RefreshFast),
41
41
+
x if x == SerialCommand::EnterHostApp as u8 => Ok(SerialCommand::EnterHostApp),
42
42
+
x if x == SerialCommand::ExitHostApp as u8 => Ok(SerialCommand::ExitHostApp),
43
43
+
x if x == SerialCommand::NextEvent as u8 => Ok(SerialCommand::NextEvent),
44
44
+
x if x == SerialCommand::DisableTouch as u8 => Ok(SerialCommand::DisableTouch),
45
45
+
x if x == SerialCommand::EnableTouch as u8 => Ok(SerialCommand::EnableTouch),
46
46
+
x if x == SerialCommand::GetProgramSlot as u8 => Ok(SerialCommand::GetProgramSlot),
47
47
+
x if x == SerialCommand::UploadProgram as u8 => Ok(SerialCommand::UploadProgram),
48
48
+
49
49
+
_ => Err(()),
50
50
+
}
51
51
+
}
52
52
+
}
53
53
+
54
54
+
#[repr(u8)]
55
55
+
pub enum Response {
56
56
+
UnknownCommand = 0x00,
57
57
+
IncorrectMode = 0x01,
58
58
+
ProgramSlotsFull = 0x02,
59
59
+
NoProgramSlot = 0x03,
60
60
+
Ack = 0xff,
61
61
+
}
62
62
+
63
63
+
impl TryFrom<u8> for Response {
64
64
+
type Error = ();
65
65
+
66
66
+
fn try_from(value: u8) -> Result<Self, Self::Error> {
67
67
+
match value {
68
68
+
x if x == Response::UnknownCommand as u8 => Ok(Response::UnknownCommand),
69
69
+
x if x == Response::IncorrectMode as u8 => Ok(Response::IncorrectMode),
70
70
+
x if x == Response::ProgramSlotsFull as u8 => Ok(Response::ProgramSlotsFull),
71
71
+
x if x == Response::NoProgramSlot as u8 => Ok(Response::NoProgramSlot),
72
72
+
x if x == Response::Ack as u8 => Ok(Response::Ack),
73
73
+
74
74
+
_ => Err(()),
75
75
+
}
76
76
+
}
77
77
+
}
···
3
3
FLASH : ORIGIN = 0x10000100, LENGTH = 256K - 0x100
4
4
RESERVED: ORIGIN = 0x10040000, LENGTH = 256K
5
5
PROGRAM_SLOT_1: ORIGIN = 0x10080000, LENGTH = 512K
6
6
-
RAM : ORIGIN = 0x20000000, LENGTH = 256K
6
6
+
RAM : ORIGIN = 0x20000000, LENGTH = 128K
7
7
+
PROGRAM_RAM : ORIGIN = 0x20020000, LENGTH = 136K
7
8
}
8
9
9
10
EXTERN(BOOT2_FIRMWARE)
···
6
6
7
7
[dependencies]
8
8
embedded-hal.workspace = true
9
9
-
defmt.workspace = true
10
9
rp2040-hal = { workspace = true, optional = true }
11
10
12
11
[features]
···
5
5
#[cfg(feature = "rp2040")]
6
6
pub mod rp2040;
7
7
8
8
-
use defmt::trace;
9
8
use embedded_hal::digital::{InputPin, OutputPin, PinState};
10
9
11
10
/// A trait for pin types that allows running a closure with the pin configured as an input.
···
157
156
self.sck.set_low()?;
158
157
self.delay.delay_read_after_sck_low();
159
158
}
160
160
-
161
161
-
trace!("read byte {}", byte);
162
159
163
160
self.cs.set_high()?;
164
161
self.delay.delay_read_after_byte();
···
6
6
[dependencies]
7
7
pervasive-spi = { path = "../pervasive-spi" }
8
8
embedded-hal.workspace = true
9
9
-
defmt.workspace = true
9
9
+
defmt = { workspace = true, optional = true }
10
10
cortex-m = { workspace = true, optional = true }
11
11
12
12
[features]
···
5
5
#[cfg(feature = "rp2040")]
6
6
pub mod rp2040;
7
7
8
8
+
#[cfg(feature = "defmt")]
9
9
+
use defmt::{debug, error, trace};
10
10
+
8
11
use core::error::Error;
9
12
use core::fmt::{Debug, Display, Formatter};
10
10
-
use defmt::{debug, error, trace};
11
13
use embedded_hal::delay::DelayNs;
12
14
use embedded_hal::digital::{InputPin, OutputPin};
13
15
use pervasive_spi::{PervasiveSpi, PervasiveSpiDelays, WithInput, WithOutput};
···
115
117
116
118
/// Hard-reset the display using the reset pin, then perform a soft reset.
117
119
pub fn hard_reset(&mut self) -> Result<(), Tp370pgh01Error<Error>> {
120
120
+
#[cfg(feature = "defmt")]
118
121
debug!("tp370pgh01: hard resetting display");
119
122
self.spi.set_cs_low()?;
120
123
self.reset.set_high()?;
···
125
128
self.delay.delay_ms(5);
126
129
self.spi.set_cs_high()?;
127
130
self.soft_reset()?;
131
131
+
#[cfg(feature = "defmt")]
128
132
debug!("tp370pgh01: hard reset display");
129
133
130
134
Ok(())
···
132
136
133
137
/// Perform a soft reset.
134
138
pub fn soft_reset(&mut self) -> Result<(), Tp370pgh01Error<Error>> {
139
139
+
#[cfg(feature = "defmt")]
135
140
debug!("tp370pgh01: soft resetting display");
136
141
self.spi.write_register(&[0x00, 0x0e])?;
137
142
self.delay.delay_ms(5);
143
143
+
#[cfg(feature = "defmt")]
138
144
debug!("tp370pgh01: soft reset display");
139
145
Ok(())
140
146
}
···
144
150
return Ok(psr);
145
151
}
146
152
153
153
+
#[cfg(feature = "defmt")]
147
154
debug!("tp370pgh01: reading PSR");
148
155
self.spi.write_register(&[0xa2])?;
149
156
let mut buf = [0u8; 2];
···
151
158
self.spi.read(&mut buf)?;
152
159
153
160
let bank0 = buf[1] == 0xa5;
161
161
+
#[cfg(feature = "defmt")]
154
162
debug!(
155
163
"tp370pgh01: PSR is in bank {}",
156
164
if bank0 { '0' } else { '1' }
···
167
175
let mut buf = [0];
168
176
self.spi.read(&mut buf)?;
169
177
if buf[0] != 0xa5 {
178
178
+
#[cfg(feature = "defmt")]
170
179
error!("tp370pgh01: failed to find PSR");
171
180
return Err(Tp370pgh01Error::ReadPsrInvalid);
172
181
}
···
177
186
}
178
187
179
188
self.spi.read(&mut buf)?;
189
189
+
#[cfg(feature = "defmt")]
180
190
debug!("tp370pgh01: found PSR: {} {}", buf[0], buf[1]);
181
191
182
192
self.psr.replace(buf);
···
185
195
}
186
196
187
197
fn busy_wait(&mut self) -> Result<(), Tp370pgh01Error<Error>> {
198
198
+
#[cfg(feature = "defmt")]
188
199
trace!("tp370pgh01: busy waiting");
189
200
while self.busy.is_low()? {}
190
201
Ok(())
191
202
}
192
203
193
204
fn trigger_refresh(&mut self) -> Result<(), Tp370pgh01Error<Error>> {
205
205
+
#[cfg(feature = "defmt")]
194
206
debug!("tp370pgh01: turning on DC/DC");
195
207
// turn on DC/DC
196
208
self.spi.write_register(&[0x04])?;
197
209
self.busy_wait()?;
198
210
// refresh
211
211
+
#[cfg(feature = "defmt")]
199
212
debug!("tp370pgh01: refreshing");
200
213
self.spi.write_register(&[0x12])?;
201
214
self.busy_wait()?;
202
215
// turn off DC/DC
216
216
+
#[cfg(feature = "defmt")]
203
217
debug!("tp370pgh01: turning off DC/DC");
204
218
self.spi.write_register(&[0x02])?;
205
219
self.busy_wait()?;
···
220
234
image: &[u8; IMAGE_BYTES],
221
235
temperature: u8,
222
236
) -> Result<(), Tp370pgh01Error<Error>> {
237
237
+
#[cfg(feature = "defmt")]
223
238
debug!("tp370pgh01: begin normal refresh");
224
239
225
240
let psr = self.get_psr()?;
···
233
248
self.spi.write_data(&psr)?;
234
249
235
250
// write image data
251
251
+
#[cfg(feature = "defmt")]
236
252
debug!("tp370pgh01: writing image");
237
253
self.spi.write_register(&[0x10])?;
238
254
self.spi.write_data_reversed(image)?;
···
241
257
for _ in 0..IMAGE_BYTES {
242
258
self.spi.write_data(&[0x00])?;
243
259
}
260
260
+
#[cfg(feature = "defmt")]
244
261
debug!("tp370pgh01: finished writing image");
245
262
246
263
self.trigger_refresh()?;
247
264
265
265
+
#[cfg(feature = "defmt")]
248
266
debug!("tp370pgh01: end normal refresh");
249
267
Ok(())
250
268
}
···
264
282
prev_image: &[u8; IMAGE_BYTES],
265
283
temperature: u8,
266
284
) -> Result<(), Tp370pgh01Error<Error>> {
285
285
+
#[cfg(feature = "defmt")]
267
286
debug!("tp370pgh01: begin fast refresh");
268
287
269
288
let psr = self.get_psr()?;
···
280
299
// set "border setting"
281
300
self.spi.write_register(&[0x50, 0x27])?;
282
301
// send previous image
302
302
+
#[cfg(feature = "defmt")]
283
303
debug!("tp370pgh01: writing image");
284
304
self.spi.write_register(&[0x10])?;
285
305
self.spi.write_data_reversed(prev_image)?;
286
306
// send new image
287
307
self.spi.write_register(&[0x13])?;
288
308
self.spi.write_data_reversed(image)?;
309
309
+
#[cfg(feature = "defmt")]
289
310
debug!("tp370pgh01: finished writing image");
290
311
// set "border setting"
291
312
self.spi.write_register(&[0x50, 0x07])?;
292
313
293
314
self.trigger_refresh()?;
294
315
316
316
+
#[cfg(feature = "defmt")]
295
317
debug!("tp370pgh01: end fast refresh");
296
318
Ok(())
297
319
}