application: Start and stop recordings by pressing the play button.
[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
32 use common::buffer::Buffer;
33 use common::ringbuf::Ringbuf;
34 use common::clock;
35 use common::systick;
36 use common::port;
37 use common::gpio;
38 use common::nvic;
39 use common::i2c;
40 use common::spi;
41 use common::uart;
42 use common::usb_serial;
43 use common::display;
44 use common::gps;
45 use common::screen;
46 use common::time::Time;
47 use common::mx25l::Mx25l;
48 use common::shell::Shell;
49 use common::logger::Logger;
50
51 extern {
52     fn enable_interrupts();
53
54     static mut cdc_tx_buf: Buffer;
55 }
56
57 struct Timer {
58     state: u32,
59     delay_ms: u32,
60     last_update_ticks: u32,
61 }
62
63 impl Timer {
64     pub fn new(delay_ms: u32) -> Timer {
65         Timer {
66             state: 0,
67             delay_ms: delay_ms,
68             last_update_ticks: systick::now(),
69         }
70     }
71
72     pub fn update<F>(&mut self, func: F)
73         where F: FnOnce(u32) -> u32
74     {
75         if systick::has_timeout_ms(self.last_update_ticks, self.delay_ms) {
76             self.state = func(self.state);
77
78             self.last_update_ticks = systick::now();
79         }
80     }
81 }
82
83 fn reset_requested() -> bool {
84     let pta1 = gpio::get(gpio::GPIOA) & (1 << 1);
85     let pte25 = gpio::get(gpio::GPIOE) & (1 << 25);
86
87     (pta1 | pte25) == 0
88 }
89
90 #[inline(never)]
91 #[panic_handler]
92 fn panic(_info: &core::panic::PanicInfo) -> ! {
93     loop {
94         if reset_requested() {
95             nvic::system_reset();
96         }
97     }
98 }
99
100 fn configure_push_buttons() {
101     // Configure lower right push button.
102     gpio::set_direction(gpio::GPIOA, 1 << 1, gpio::Direction::Input);
103     port::set_af(port::PORTA, 1, 1);
104     port::set_pull(port::PORTA, 1, port::Pull::Up);
105
106     // Configure upper right push button.
107     gpio::set_direction(gpio::GPIOA, 1 << 12, gpio::Direction::Input);
108     port::set_af(port::PORTA, 12, 1);
109     port::set_pull(port::PORTA, 12, port::Pull::Up);
110
111     // Configure lower left push button.
112     gpio::set_direction(gpio::GPIOE, 1 << 24, gpio::Direction::Input);
113     port::set_af(port::PORTE, 24, 1);
114     port::set_pull(port::PORTE, 24, port::Pull::Up);
115
116     // Configure upper left push button.
117     gpio::set_direction(gpio::GPIOE, 1 << 25, gpio::Direction::Input);
118     port::set_af(port::PORTE, 25, 1);
119     port::set_pull(port::PORTE, 25, port::Pull::Up);
120
121     // Configure middle left push button.
122     gpio::set_direction(gpio::GPIOE, 1 << 31, gpio::Direction::Input);
123     port::set_af(port::PORTE, 31, 1);
124     port::set_pull(port::PORTE, 31, port::Pull::Up);
125 }
126
127 fn poll_pta12() -> bool {
128     (gpio::get(gpio::GPIOA) & (1 << 12)) == 0
129 }
130
131 fn uart0_try_read() -> Option<u8> {
132     extern {
133         static mut uart0_rx_buf: Ringbuf;
134     }
135
136     unsafe {
137         if uart0_rx_buf.is_empty() {
138             None
139         } else {
140             Some(uart0_rx_buf.read())
141         }
142     }
143 }
144
145 #[no_mangle]
146 pub unsafe extern "C" fn _start() -> ! {
147     clock::configure();
148     clock::enable_osc0();
149     systick::init();
150     port::init();
151
152     // Configure pins for I2C0.
153     port::set_af(port::PORTC, 8, 2);
154     port::set_af(port::PORTC, 9, 2);
155
156     i2c::configure(i2c::I2C0);
157
158     nvic::disable_irq(8); // I2C0
159
160     // Configure pin for the display's reset line.
161     gpio::set_direction(gpio::GPIOB, 1 << 16, gpio::Direction::Output);
162     port::set_af(port::PORTB, 16, 1);
163
164     // Configure pin for the MX25L's chip select line.
165     gpio::set_direction(gpio::GPIOD, 1 << 0, gpio::Direction::Output);
166     port::set_af(port::PORTD, 0, 1);
167     gpio::set(gpio::GPIOD, 1 << 0);
168
169     // Configure pins for SPI0.
170     port::set_af(port::PORTD, 1, 2);
171     port::set_af(port::PORTD, 2, 5);
172     port::set_af(port::PORTD, 3, 5);
173
174     spi::configure(spi::SPI0);
175
176     nvic::disable_irq(10); // SPI0
177
178     // Configure pins for UART0.
179     port::set_af(port::PORTE, 20, 4);
180     port::set_af(port::PORTE, 21, 4);
181
182     // Configure pin for the GPS's reset line.
183     gpio::set_direction(gpio::GPIOB, 1 << 1, gpio::Direction::Output);
184     port::set_af(port::PORTB, 1, 1);
185
186     configure_push_buttons();
187
188     enable_interrupts();
189
190     usb_serial::init(0xf055, 0x635d);
191
192     cdc_tx_buf.write(b"\n");
193     cdc_tx_buf.flush();
194
195     let mut display = display::Display::new(gpio::GPIOB, 1 << 16, 0x3c);
196
197     display.init();
198     display.clear();
199
200     let mut screen = screen::Screen::new();
201
202     // Hold GPS in reset while configuring its UART.
203     gpio::clear(gpio::GPIOB, 1);
204     systick::delay_ms(50);
205     uart::configure(uart::UART0);
206     systick::delay_ms(50);
207     gpio::set(gpio::GPIOB, 1);
208
209     nvic::enable_irq(12); // UART0
210
211     let mut shell = Shell::new(&mut cdc_tx_buf);
212
213     let mut mx25l = Mx25l::new(gpio::GPIOD, 1 << 0);
214
215     let mut logger = Logger::new(&mut mx25l);
216     logger.init();
217
218     let mut pta1_start_press_ticks = 0;
219     let mut pta12_was_pressed = poll_pta12();
220     let mut is_recording = false;
221
222     let mut gps = gps::Gps::new();
223
224     let mut gps_has_fix = false;
225     let mut gps_has_fix_ticks = 0;
226
227     let mut heart_icon_timer = Timer::new(1000);
228     let mut gps_icon_timer = Timer::new(500);
229
230     let mut prev_tap = gps::TimeAndPos::new();
231
232     loop {
233         let mut tap = gps::TimeAndPos::new();
234         let mut show_time = false;
235         let old_gps_has_fix = gps_has_fix;
236
237         while gps.update(&mut tap, uart0_try_read) {
238             if is_recording {
239                 logger.log(&prev_tap, &tap);
240             }
241
242             prev_tap = tap;
243
244             show_time = true;
245
246             gps_has_fix = true;
247             gps_has_fix_ticks = systick::now();
248         }
249
250         // Did GPS fix information expire?
251         if gps_has_fix && systick::has_timeout_ms(gps_has_fix_ticks, 1500) {
252             gps_has_fix = false;
253         }
254
255         if gps_has_fix && !old_gps_has_fix {
256             display.show_icon(display::Icon::SatelliteBody);
257             display.show_icon(display::Icon::SatelliteWave1);
258             display.show_icon(display::Icon::SatelliteWave2);
259         } else if !gps_has_fix {
260             gps_icon_timer.update(|state| {
261                 if state == 1 {
262                     display.show_icon(display::Icon::SatelliteWave1);
263                     2
264                 } else if state == 2 {
265                     display.show_icon(display::Icon::SatelliteWave2);
266                     3
267                 } else if state == 3 {
268                     display.hide_icon(display::Icon::SatelliteBody);
269                     display.hide_icon(display::Icon::SatelliteWave1);
270                     display.hide_icon(display::Icon::SatelliteWave2);
271                     0
272                 } else {
273                     display.show_icon(display::Icon::SatelliteBody);
274                     1
275                 }
276             });
277         }
278
279         if show_time {
280             if let Some(tm) = Time::from_unix_time(prev_tap.unix_time) {
281                 let mut time_s = [b' '; 8];
282                 tm.fmt_time(&mut time_s);
283
284                 screen.clear();
285                 screen.draw_text(&time_s);
286
287                 display.draw(&screen);
288             }
289         }
290
291         heart_icon_timer.update(|state| {
292             if state == 1 {
293                 display.hide_icon(display::Icon::Heart);
294                 0
295             } else {
296                 display.show_icon(display::Icon::Heart);
297                 1
298             }
299         });
300
301         shell.update(&mut logger);
302
303         let pta12_is_pressed = poll_pta12();
304
305         if !pta12_was_pressed && pta12_is_pressed {
306             pta1_start_press_ticks = systick::now();
307         } else if pta12_was_pressed && !pta12_is_pressed {
308             if systick::has_timeout_ms(pta1_start_press_ticks, 1500) {
309                 is_recording = !is_recording;
310
311                 if is_recording {
312                     logger.start_recording(&prev_tap);
313                 } else {
314                     logger.stop_recording(&prev_tap);
315                 }
316             }
317         }
318
319         pta12_was_pressed = pta12_is_pressed;
320
321         if reset_requested() {
322             nvic::system_reset();
323         }
324     }
325 }