common: Store distance travelled in Logger instance.
[gps-watch.git] / src / application / main.rs
1 /*
2  * Copyright (c) 2019 Tilman Sauerbeck (tilman at code-monkey de)
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23
24 #![no_std]
25 #![no_main]
26 #[link(name="libcommon.rlib")]
27
28 extern crate common;
29
30 mod uart0;
31 mod button;
32
33 use common::buffer::Buffer;
34 use common::ringbuf::Ringbuf;
35 use common::clock;
36 use common::systick;
37 use common::port;
38 use common::gpio;
39 use common::nvic;
40 use common::i2c;
41 use common::spi;
42 use common::uart;
43 use common::usb_serial;
44 use common::display;
45 use common::gps;
46 use common::screen;
47 use common::time::Time;
48 use common::mx25l::Mx25l;
49 use common::shell::Shell;
50 use common::logger::Logger;
51
52 extern {
53     fn enable_interrupts();
54
55     static mut cdc_tx_buf: Buffer;
56 }
57
58 struct Timer {
59     state: u32,
60     delay_ms: u32,
61     last_update_ticks: u32,
62 }
63
64 impl Timer {
65     pub fn new(delay_ms: u32) -> Timer {
66         Timer {
67             state: 0,
68             delay_ms: delay_ms,
69             last_update_ticks: systick::now(),
70         }
71     }
72
73     pub fn update<F>(&mut self, func: F)
74         where F: FnOnce(u32) -> u32
75     {
76         if systick::has_timeout_ms(self.last_update_ticks, self.delay_ms) {
77             self.state = func(self.state);
78
79             self.last_update_ticks = systick::now();
80         }
81     }
82 }
83
84 fn reset_requested() -> bool {
85     let pta1 = gpio::get(gpio::GPIOA) & (1 << 1);
86     let pte25 = gpio::get(gpio::GPIOE) & (1 << 25);
87
88     (pta1 | pte25) == 0
89 }
90
91 #[inline(never)]
92 #[panic_handler]
93 fn panic(_info: &core::panic::PanicInfo) -> ! {
94     loop {
95         if reset_requested() {
96             nvic::system_reset();
97         }
98     }
99 }
100
101 fn configure_push_buttons() {
102     // Configure lower right push button.
103     gpio::set_direction(gpio::GPIOA, 1 << 1, gpio::Direction::Input);
104     port::set_af(port::PORTA, 1, 1);
105     port::set_pull(port::PORTA, 1, port::Pull::Up);
106
107     // Configure upper right push button.
108     gpio::set_direction(gpio::GPIOA, 1 << 12, gpio::Direction::Input);
109     port::set_af(port::PORTA, 12, 1);
110     port::set_pull(port::PORTA, 12, port::Pull::Up);
111
112     // Configure lower left push button.
113     gpio::set_direction(gpio::GPIOE, 1 << 24, gpio::Direction::Input);
114     port::set_af(port::PORTE, 24, 1);
115     port::set_pull(port::PORTE, 24, port::Pull::Up);
116
117     // Configure upper left push button.
118     gpio::set_direction(gpio::GPIOE, 1 << 25, gpio::Direction::Input);
119     port::set_af(port::PORTE, 25, 1);
120     port::set_pull(port::PORTE, 25, port::Pull::Up);
121
122     // Configure middle left push button.
123     gpio::set_direction(gpio::GPIOE, 1 << 31, gpio::Direction::Input);
124     port::set_af(port::PORTE, 31, 1);
125     port::set_pull(port::PORTE, 31, port::Pull::Up);
126 }
127
128 fn uart0_try_read() -> Option<u8> {
129     extern {
130         static mut uart0_rx_buf: Ringbuf;
131     }
132
133     unsafe {
134         if uart0_rx_buf.is_empty() {
135             None
136         } else {
137             Some(uart0_rx_buf.read())
138         }
139     }
140 }
141
142 #[no_mangle]
143 pub unsafe extern "C" fn _start() -> ! {
144     clock::configure();
145     clock::enable_osc0();
146     systick::init();
147     port::init();
148
149     // Configure pins for I2C0.
150     port::set_af(port::PORTC, 8, 2);
151     port::set_af(port::PORTC, 9, 2);
152
153     i2c::configure(i2c::I2C0);
154
155     nvic::disable_irq(8); // I2C0
156
157     // Configure pin for the display's reset line.
158     gpio::set_direction(gpio::GPIOB, 1 << 16, gpio::Direction::Output);
159     port::set_af(port::PORTB, 16, 1);
160
161     // Configure pin for the MX25L's chip select line.
162     gpio::set_direction(gpio::GPIOD, 1 << 0, gpio::Direction::Output);
163     port::set_af(port::PORTD, 0, 1);
164     gpio::set(gpio::GPIOD, 1 << 0);
165
166     // Configure pins for SPI0.
167     port::set_af(port::PORTD, 1, 2);
168     port::set_af(port::PORTD, 2, 5);
169     port::set_af(port::PORTD, 3, 5);
170
171     spi::configure(spi::SPI0);
172
173     nvic::disable_irq(10); // SPI0
174
175     // Configure pins for UART0.
176     port::set_af(port::PORTE, 20, 4);
177     port::set_af(port::PORTE, 21, 4);
178
179     // Configure pin for the GPS's reset line.
180     gpio::set_direction(gpio::GPIOB, 1 << 1, gpio::Direction::Output);
181     port::set_af(port::PORTB, 1, 1);
182
183     configure_push_buttons();
184
185     enable_interrupts();
186
187     usb_serial::init(0xf055, 0x635d);
188
189     cdc_tx_buf.write(b"\n");
190     cdc_tx_buf.flush();
191
192     let mut display = display::Display::new(gpio::GPIOB, 1 << 16, 0x3c);
193
194     display.init();
195     display.clear();
196
197     let mut screen = screen::Screen::new();
198
199     // Hold GPS in reset while configuring its UART.
200     gpio::clear(gpio::GPIOB, 1);
201     systick::delay_ms(50);
202     uart::configure(uart::UART0);
203     systick::delay_ms(50);
204     gpio::set(gpio::GPIOB, 1);
205
206     nvic::enable_irq(12); // UART0
207
208     let mut shell = Shell::new(&mut cdc_tx_buf);
209
210     let mut mx25l = Mx25l::new(gpio::GPIOD, 1 << 0);
211
212     let mut logger = Logger::new(&mut mx25l);
213     logger.init();
214
215     let mut pta12 = button::Button::new(gpio::GPIOA, 1 << 12);
216     let mut is_recording = false;
217
218     let mut gps = gps::Gps::new();
219
220     let mut gps_has_fix = false;
221     let mut gps_has_fix_ticks = 0;
222
223     let mut heart_icon_timer = Timer::new(1000);
224     let mut gps_icon_timer = Timer::new(500);
225
226     let mut prev_tap = gps::TimeAndPos::new();
227
228     loop {
229         let mut tap = gps::TimeAndPos::new();
230         let mut show_time = false;
231         let mut show_distance = false;
232         let old_gps_has_fix = gps_has_fix;
233
234         while gps.update(&mut tap, uart0_try_read) {
235             if is_recording {
236                 logger.log(&prev_tap, &tap);
237
238                 show_distance = true;
239             }
240
241             prev_tap = tap;
242
243             show_time = true;
244
245             gps_has_fix = true;
246             gps_has_fix_ticks = systick::now();
247         }
248
249         // Did GPS fix information expire?
250         if gps_has_fix && systick::has_timeout_ms(gps_has_fix_ticks, 1500) {
251             gps_has_fix = false;
252         }
253
254         if gps_has_fix && !old_gps_has_fix {
255             display.show_icon(display::Icon::SatelliteBody);
256             display.show_icon(display::Icon::SatelliteWave1);
257             display.show_icon(display::Icon::SatelliteWave2);
258         } else if !gps_has_fix {
259             gps_icon_timer.update(|state| {
260                 if state == 1 {
261                     display.show_icon(display::Icon::SatelliteWave1);
262                     2
263                 } else if state == 2 {
264                     display.show_icon(display::Icon::SatelliteWave2);
265                     3
266                 } else if state == 3 {
267                     display.hide_icon(display::Icon::SatelliteBody);
268                     display.hide_icon(display::Icon::SatelliteWave1);
269                     display.hide_icon(display::Icon::SatelliteWave2);
270                     0
271                 } else {
272                     display.show_icon(display::Icon::SatelliteBody);
273                     1
274                 }
275             });
276         }
277
278         if show_distance {
279             let mut distance_m_s = [b' '; 8];
280
281             common::fmt::fmt_u32(&mut distance_m_s,
282                                  logger.total_distance_cm / 100);
283
284             screen.clear();
285             screen.draw_text(&distance_m_s);
286
287             display.draw(&screen);
288         } else if show_time {
289             if let Some(tm) = Time::from_unix_time(prev_tap.unix_time) {
290                 let mut time_s = [b' '; 8];
291                 tm.fmt_time(&mut time_s);
292
293                 screen.clear();
294                 screen.draw_text(&time_s);
295
296                 display.draw(&screen);
297             }
298         }
299
300         heart_icon_timer.update(|state| {
301             if state == 1 {
302                 display.hide_icon(display::Icon::Heart);
303                 0
304             } else {
305                 display.show_icon(display::Icon::Heart);
306                 1
307             }
308         });
309
310         shell.update(&mut logger);
311
312         if pta12.has_been_held_for_ms(1500) {
313             is_recording = !is_recording;
314
315             if is_recording {
316                 logger.start_recording(&prev_tap);
317             } else {
318                 logger.stop_recording(&prev_tap);
319             }
320         }
321
322         if reset_requested() {
323             nvic::system_reset();
324         }
325     }
326 }