|
|
@@ -1,54 +1,37 @@
|
|
1
|
1
|
#![no_main]
|
|
2
|
2
|
#![no_std]
|
|
3
|
3
|
|
|
|
4
|
+mod display;
|
|
|
5
|
+mod information;
|
|
4
|
6
|
mod pins;
|
|
|
7
|
+
|
|
|
8
|
+// TODO: Enable warnings again.
|
|
|
9
|
+#[allow(unused)]
|
|
5
|
10
|
mod assets {
|
|
6
|
11
|
include!(concat!(env!("OUT_DIR"), "/assets.rs"));
|
|
7
|
12
|
}
|
|
8
|
13
|
|
|
9
|
|
-use cortex_m_semihosting::{debug, hprintln};
|
|
10
|
14
|
use embedded_epd as epd;
|
|
11
|
|
-use embedded_epd::{gui, Display};
|
|
12
|
|
-use embedded_hal::blocking::delay::DelayMs;
|
|
13
|
|
-use embedded_hal::digital::v2::OutputPin;
|
|
14
|
|
-use embedded_hal::timer::CountDown;
|
|
15
|
|
-use epd_waveshare::{epd4in2::*, prelude::*};
|
|
16
|
|
-use mkl25z4_hal as hal;
|
|
17
|
15
|
use mkl25z4_hal::clocks::ClockConfiguration;
|
|
18
|
|
-use mkl25z4_hal::time::NonCopyableMonoTimer;
|
|
19
|
|
-use mkl25z4_hal::time::U32Ext;
|
|
20
|
|
-use mkl25z4_hal::timer::Timer;
|
|
21
|
|
-use nb::block;
|
|
|
16
|
+use mkl25z4_hal::time::{CopyableMonoTimer, NonCopyableMonoTimer, U32Ext};
|
|
22
|
17
|
use panic_semihosting as _;
|
|
23
|
18
|
|
|
24
|
|
-struct EPDTimer<Timer> {
|
|
25
|
|
- timer: Timer,
|
|
26
|
|
-}
|
|
27
|
|
-
|
|
28
|
|
-impl<Timer> CountDown for EPDTimer<Timer>
|
|
29
|
|
-where
|
|
30
|
|
- Timer: CountDown<Time = hal::time::Hertz>,
|
|
31
|
|
-{
|
|
32
|
|
- type Time = epd::Hertz;
|
|
33
|
|
-
|
|
34
|
|
- fn start<T>(&mut self, timeout: T)
|
|
35
|
|
- where
|
|
36
|
|
- T: Into<epd::Hertz>,
|
|
37
|
|
- {
|
|
38
|
|
- self.timer.start(hal::time::Hertz(timeout.into().0))
|
|
39
|
|
- }
|
|
40
|
|
-
|
|
41
|
|
- fn wait(&mut self) -> nb::Result<(), void::Void> {
|
|
42
|
|
- self.timer.wait()
|
|
43
|
|
- }
|
|
44
|
|
-}
|
|
|
19
|
+use display::{Display, DisplayState};
|
|
|
20
|
+use information::Information;
|
|
|
21
|
+use pins::DisplayBmeSpi;
|
|
45
|
22
|
|
|
46
|
23
|
#[rtfm::app(device = mkl25z4_hal::mkl25z4, peripherals = true)]
|
|
47
|
24
|
const APP: () = {
|
|
48
|
|
- struct Resources {}
|
|
|
25
|
+ struct Resources {
|
|
|
26
|
+ info: Information,
|
|
|
27
|
+ display_state: DisplayState,
|
|
|
28
|
+ display: Display,
|
|
|
29
|
+ display_bme_spi: Option<DisplayBmeSpi>,
|
|
|
30
|
+ time: CopyableMonoTimer,
|
|
|
31
|
+ }
|
|
49
|
32
|
|
|
50
|
|
- #[init]
|
|
51
|
|
- fn init(ctx: init::Context) {
|
|
|
33
|
+ #[init(spawn = [fetch_update])]
|
|
|
34
|
+ fn init(ctx: init::Context) -> init::LateResources {
|
|
52
|
35
|
let mut sim = ctx.device.SIM;
|
|
53
|
36
|
mkl25z4_hal::watchdog::disable(&mut sim);
|
|
54
|
37
|
let clocks = ClockConfiguration::new()
|
|
|
@@ -69,29 +52,70 @@ const APP: () = {
|
|
69
|
52
|
ctx.device.SPI1,
|
|
70
|
53
|
);
|
|
71
|
54
|
|
|
72
|
|
- // Enable power.
|
|
73
|
|
- pins.display_pwr.set_low().ok();
|
|
74
|
|
- pins.display_rst.set_high().unwrap();
|
|
75
|
|
- pins.display_dc.set_high().unwrap();
|
|
76
|
|
- pins.display_cs.set_high().unwrap();
|
|
77
|
|
-
|
|
78
|
|
- let mut timer = NonCopyableMonoTimer::new(ctx.device.PIT, clocks, &mut sim);
|
|
79
|
|
-
|
|
80
|
|
- let mut epd = EPD4in2::new(
|
|
81
|
|
- &mut pins.display_bme_spi,
|
|
82
|
|
- pins.display_cs,
|
|
83
|
|
- pins.display_busy,
|
|
84
|
|
- pins.display_dc,
|
|
85
|
|
- pins.display_rst,
|
|
86
|
|
- &mut timer,
|
|
87
|
|
- )
|
|
88
|
|
- .unwrap();
|
|
89
|
|
-
|
|
90
|
|
- epd.set_background_color(Color::Black);
|
|
91
|
|
- let checkerboard = Checkerboard::new(400, 300, true);
|
|
92
|
|
- epd.update_frame_stream(&mut pins.display_bme_spi, checkerboard)
|
|
93
|
|
- .unwrap();
|
|
94
|
|
- epd.display_frame(&mut pins.display_bme_spi).unwrap();
|
|
|
55
|
+ let time =
|
|
|
56
|
+ CopyableMonoTimer::new(NonCopyableMonoTimer::new(ctx.device.PIT, clocks, &mut sim));
|
|
|
57
|
+
|
|
|
58
|
+ let display = Display::init(pins.display, &mut pins.display_bme_spi, time);
|
|
|
59
|
+
|
|
|
60
|
+ // Fetch the first update.
|
|
|
61
|
+ ctx.spawn.fetch_update().ok();
|
|
|
62
|
+
|
|
|
63
|
+ init::LateResources {
|
|
|
64
|
+ info: Information {
|
|
|
65
|
+ time: 0,
|
|
|
66
|
+ valid: false,
|
|
|
67
|
+ },
|
|
|
68
|
+ display_state: DisplayState{
|
|
|
69
|
+ // TODO
|
|
|
70
|
+ },
|
|
|
71
|
+ display,
|
|
|
72
|
+ display_bme_spi: Some(pins.display_bme_spi),
|
|
|
73
|
+ time,
|
|
|
74
|
+ }
|
|
|
75
|
+ }
|
|
|
76
|
+
|
|
|
77
|
+ #[task(binds = LPTMR0, priority = 3, spawn = [fetch_update])]
|
|
|
78
|
+ fn lptmr0_interrupt(ctx: lptmr0_interrupt::Context) {
|
|
|
79
|
+ // Restart the timer.
|
|
|
80
|
+ // TODO
|
|
|
81
|
+
|
|
|
82
|
+ // Trigger an update of the displayed values.
|
|
|
83
|
+ ctx.spawn.fetch_update().ok();
|
|
|
84
|
+ }
|
|
|
85
|
+
|
|
|
86
|
+ #[task(binds = LLWU, priority = 3)]
|
|
|
87
|
+ fn llwu_interrupt(_: llwu_interrupt::Context) {
|
|
|
88
|
+ // TODO
|
|
|
89
|
+ }
|
|
|
90
|
+
|
|
|
91
|
+ #[task(priority = 1, spawn = [update_display], resources = [info])]
|
|
|
92
|
+ fn fetch_update(ctx: fetch_update::Context) {
|
|
|
93
|
+ // Fetch local updates.
|
|
|
94
|
+ // TODO
|
|
|
95
|
+
|
|
|
96
|
+ // Send local measurements to the base station. This needs to be done before fetching data
|
|
|
97
|
+ // from the base station as the base station might return the data to us.
|
|
|
98
|
+ // TODO
|
|
|
99
|
+
|
|
|
100
|
+ // Fetch updates via radio.
|
|
|
101
|
+ // TODO
|
|
|
102
|
+
|
|
|
103
|
+ // If the update failed, simply increment the time and place a hint that
|
|
|
104
|
+ // the information might be invalid.
|
|
|
105
|
+ // TODO
|
|
|
106
|
+
|
|
|
107
|
+ // Update the display to show the new values.
|
|
|
108
|
+ ctx.spawn.update_display().ok();
|
|
|
109
|
+ }
|
|
|
110
|
+
|
|
|
111
|
+ #[task(priority = 2, resources = [info, display_state, display, display_bme_spi, &time])]
|
|
|
112
|
+ fn update_display(ctx: update_display::Context) {
|
|
|
113
|
+ ctx.resources.display.update(
|
|
|
114
|
+ &ctx.resources.display_state,
|
|
|
115
|
+ &ctx.resources.info,
|
|
|
116
|
+ ctx.resources.display_bme_spi.as_mut().unwrap(),
|
|
|
117
|
+ ctx.resources.time.clone(),
|
|
|
118
|
+ );
|
|
95
|
119
|
}
|
|
96
|
120
|
|
|
97
|
121
|
#[idle]
|
|
|
@@ -105,36 +129,9 @@ const APP: () = {
|
|
105
|
129
|
cortex_m::asm::wfi();
|
|
106
|
130
|
}
|
|
107
|
131
|
}
|
|
108
|
|
-};
|
|
109
|
|
-
|
|
110
|
|
-struct Checkerboard {
|
|
111
|
|
- width: usize,
|
|
112
|
|
- height: usize,
|
|
113
|
|
- index: usize,
|
|
114
|
|
- buffer: [u8; 1],
|
|
115
|
|
-}
|
|
116
|
|
-
|
|
117
|
|
-impl Checkerboard {
|
|
118
|
|
- fn new(width: usize, height: usize, start_black: bool) -> Self {
|
|
119
|
|
- Self {
|
|
120
|
|
- width: width / 8,
|
|
121
|
|
- height,
|
|
122
|
|
- index: 0,
|
|
123
|
|
- buffer: [if start_black { 0x55 } else { 0xaa }],
|
|
124
|
|
- }
|
|
125
|
|
- }
|
|
126
|
|
-}
|
|
127
|
132
|
|
|
128
|
|
-impl DisplayStream for Checkerboard {
|
|
129
|
|
- fn next<'a>(&'a mut self) -> Option<&'a [u8]> {
|
|
130
|
|
- if self.index != self.width * self.height {
|
|
131
|
|
- self.index += 1;
|
|
132
|
|
- if (self.index - 1) % self.width == 0 {
|
|
133
|
|
- self.buffer[0] = !self.buffer[0];
|
|
134
|
|
- }
|
|
135
|
|
- Some(&self.buffer)
|
|
136
|
|
- } else {
|
|
137
|
|
- None
|
|
138
|
|
- }
|
|
|
133
|
+ extern "C" {
|
|
|
134
|
+ fn I2C0();
|
|
|
135
|
+ fn I2C1();
|
|
139
|
136
|
}
|
|
140
|
|
-}
|
|
|
137
|
+};
|