-
Notifications
You must be signed in to change notification settings - Fork 543
/
esp32_s3_box.rs
231 lines (199 loc) · 8.38 KB
/
esp32_s3_box.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT
use alloc::boxed::Box;
use alloc::rc::Rc;
use core::cell::RefCell;
use display_interface_spi::SPIInterfaceNoCS;
use embedded_graphics_core::geometry::OriginDimensions;
use embedded_hal::digital::v2::OutputPin;
use esp32s3_hal::{
clock::{ClockControl, CpuClock},
i2c::I2C,
peripherals::Peripherals,
prelude::*,
spi::{Spi, SpiMode},
systimer::SystemTimer,
timer::TimerGroup,
Delay, Rtc, IO,
};
use esp_alloc::EspHeap;
use esp_backtrace as _;
use mipidsi::{Display, Orientation};
use slint::platform::WindowEvent;
pub use xtensa_lx_rt::entry;
#[global_allocator]
static ALLOCATOR: EspHeap = EspHeap::empty();
pub fn init() {
const HEAP_SIZE: usize = 250 * 1024;
static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE];
unsafe { ALLOCATOR.init(&mut HEAP as *mut u8, core::mem::size_of_val(&HEAP)) }
slint::platform::set_platform(Box::new(EspBackend::default()))
.expect("backend already initialized");
}
#[derive(Default)]
struct EspBackend {
window: RefCell<Option<Rc<slint::platform::software_renderer::MinimalSoftwareWindow>>>,
}
impl slint::platform::Platform for EspBackend {
fn create_window_adapter(
&self,
) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
let window = slint::platform::software_renderer::MinimalSoftwareWindow::new(
slint::platform::software_renderer::RepaintBufferType::ReusedBuffer,
);
self.window.replace(Some(window.clone()));
Ok(window)
}
fn duration_since_start(&self) -> core::time::Duration {
core::time::Duration::from_millis(
SystemTimer::now() / (SystemTimer::TICKS_PER_SECOND / 1000),
)
}
fn run_event_loop(&self) -> Result<(), slint::PlatformError> {
let peripherals = Peripherals::take();
let mut system = peripherals.SYSTEM.split();
let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock240MHz).freeze();
let mut rtc = Rtc::new(peripherals.RTC_CNTL);
let timer_group0 =
TimerGroup::new(peripherals.TIMG0, &clocks, &mut system.peripheral_clock_control);
let mut wdt0 = timer_group0.wdt;
let timer_group1 =
TimerGroup::new(peripherals.TIMG1, &clocks, &mut system.peripheral_clock_control);
let mut wdt1 = timer_group1.wdt;
rtc.rwdt.disable();
wdt0.disable();
wdt1.disable();
let mut delay = Delay::new(&clocks);
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
let i2c = I2C::new(
peripherals.I2C0,
io.pins.gpio8,
io.pins.gpio18,
400u32.kHz(),
&mut system.peripheral_clock_control,
&clocks,
);
let mut touch = tt21100::TT21100::new(i2c, io.pins.gpio3.into_pull_up_input())
.expect("Initialize the touch device");
let sclk = io.pins.gpio7;
let mosi = io.pins.gpio6;
let spi = Spi::new_no_cs_no_miso(
peripherals.SPI2,
sclk,
mosi,
60u32.MHz(),
SpiMode::Mode0,
&mut system.peripheral_clock_control,
&clocks,
);
let dc = io.pins.gpio4.into_push_pull_output();
let rst = io.pins.gpio48.into_push_pull_output();
let di = SPIInterfaceNoCS::new(spi, dc);
let display = mipidsi::Builder::ili9342c_rgb565(di)
.with_orientation(Orientation::PortraitInverted(false))
.with_color_order(mipidsi::options::ColorOrder::Bgr)
.init(&mut delay, Some(rst))
.unwrap();
let mut backlight = io.pins.gpio45.into_push_pull_output();
backlight.set_high().unwrap();
let size = display.size();
let size = slint::PhysicalSize::new(size.width, size.height);
self.window.borrow().as_ref().unwrap().set_size(size);
let mut buffer_provider = DrawBuffer {
display,
buffer: &mut [slint::platform::software_renderer::Rgb565Pixel(0); 320],
};
let mut last_touch = None;
loop {
slint::platform::update_timers_and_animations();
if let Some(window) = self.window.borrow().clone() {
let mut event_count = 0;
// The hardware keeps a queue of events. We should ideally process all event from the queue before rendering
// or we will get outdated event in the next frames. But move events are constantly added to the queue
// so we would block the whole interface, so add an arbitrary threshold
while event_count < 15 && touch.data_available().unwrap() {
event_count += 1;
match touch.event() {
// Ignore error because we sometimes get an error at the beginning
Err(_) => (),
Ok(tt21100::Event::Button(..)) => (),
Ok(tt21100::Event::Touch { report: _, touches }) => {
let button = slint::platform::PointerEventButton::Left;
if let Some(event) = touches
.0
.map(|record| {
let position = slint::PhysicalPosition::new(
((319. - record.x as f32) * size.width as f32 / 319.) as _,
(record.y as f32 * size.height as f32 / 239.) as _,
)
.to_logical(window.scale_factor());
match last_touch.replace(position) {
Some(_) => WindowEvent::PointerMoved { position },
None => WindowEvent::PointerPressed { position, button },
}
})
.or_else(|| {
last_touch.take().map(|position| WindowEvent::PointerReleased {
position,
button,
})
})
{
let is_pointer_release_event =
matches!(event, WindowEvent::PointerReleased { .. });
window.dispatch_event(event);
// removes hover state on widgets
if is_pointer_release_event {
window.dispatch_event(WindowEvent::PointerExited);
}
}
}
}
}
window.draw_if_needed(|renderer| {
renderer.render_by_line(&mut buffer_provider);
});
if window.has_active_animations() {
continue;
}
}
// TODO
}
}
fn debug_log(&self, arguments: core::fmt::Arguments) {
esp_println::println!("{}", arguments);
}
}
struct DrawBuffer<'a, Display> {
display: Display,
buffer: &'a mut [slint::platform::software_renderer::Rgb565Pixel],
}
impl<
DI: display_interface::WriteOnlyDataCommand,
RST: OutputPin<Error = core::convert::Infallible>,
> slint::platform::software_renderer::LineBufferProvider
for &mut DrawBuffer<'_, Display<DI, mipidsi::models::ILI9342CRgb565, RST>>
{
type TargetPixel = slint::platform::software_renderer::Rgb565Pixel;
fn process_line(
&mut self,
line: usize,
range: core::ops::Range<usize>,
render_fn: impl FnOnce(&mut [slint::platform::software_renderer::Rgb565Pixel]),
) {
let buffer = &mut self.buffer[range.clone()];
render_fn(buffer);
// We send empty data just to get the device in the right window
self.display
.set_pixels(
range.start as u16,
line as _,
range.end as u16,
line as u16,
buffer
.iter()
.map(|x| embedded_graphics_core::pixelcolor::raw::RawU16::new(x.0).into()),
)
.unwrap();
}
}