application: Set up a 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
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 uart0_try_read() -> Option<u8> {
128     extern {
129         static mut uart0_rx_buf: Ringbuf;
130     }
131
132     unsafe {
133         if uart0_rx_buf.is_empty() {
134             None
135         } else {
136             Some(uart0_rx_buf.read())
137         }
138     }
139 }
140
141 #[no_mangle]
142 pub unsafe extern "C" fn _start() -> ! {
143     clock::configure();
144     clock::enable_osc0();
145     systick::init();
146     port::init();
147
148     // Configure pins for I2C0.
149     port::set_af(port::PORTC, 8, 2);
150     port::set_af(port::PORTC, 9, 2);
151
152     i2c::configure(i2c::I2C0);
153
154     nvic::disable_irq(8); // I2C0
155
156     // Configure pin for the display's reset line.
157     gpio::set_direction(gpio::GPIOB, 1 << 16, gpio::Direction::Output);
158     port::set_af(port::PORTB, 16, 1);
159
160     // Configure pin for the MX25L's chip select line.
161     gpio::set_direction(gpio::GPIOD, 1 << 0, gpio::Direction::Output);
162     port::set_af(port::PORTD, 0, 1);
163     gpio::set(gpio::GPIOD, 1 << 0);
164
165     // Configure pins for SPI0.
166     port::set_af(port::PORTD, 1, 2);
167     port::set_af(port::PORTD, 2, 5);
168     port::set_af(port::PORTD, 3, 5);
169
170     spi::configure(spi::SPI0);
171
172     nvic::disable_irq(10); // SPI0
173
174     // Configure pins for UART0.
175     port::set_af(port::PORTE, 20, 4);
176     port::set_af(port::PORTE, 21, 4);
177
178     // Configure pin for the GPS's reset line.
179     gpio::set_direction(gpio::GPIOB, 1 << 1, gpio::Direction::Output);
180     port::set_af(port::PORTB, 1, 1);
181
182     configure_push_buttons();
183
184     enable_interrupts();
185
186     usb_serial::init(0xf055, 0x635d);
187
188     cdc_tx_buf.write(b"\n");
189     cdc_tx_buf.flush();
190
191     let mut display = display::Display::new(gpio::GPIOB, 1 << 16, 0x3c);
192
193     display.init();
194     display.clear();
195
196     let mut screen = screen::Screen::new();
197
198     // Hold GPS in reset while configuring its UART.
199     gpio::clear(gpio::GPIOB, 1);
200     systick::delay_ms(50);
201     uart::configure(uart::UART0);
202     systick::delay_ms(50);
203     gpio::set(gpio::GPIOB, 1);
204
205     nvic::enable_irq(12); // UART0
206
207     let mut shell = Shell::new(&mut cdc_tx_buf);
208
209     let mut mx25l = Mx25l::new(gpio::GPIOD, 1 << 0);
210
211     let mut logger = Logger::new(&mut mx25l);
212     logger.init();
213
214     let mut gps = gps::Gps::new();
215
216     let mut gps_has_fix = false;
217     let mut gps_has_fix_ticks = 0;
218
219     let mut heart_icon_timer = Timer::new(1000);
220     let mut gps_icon_timer = Timer::new(500);
221
222     let mut prev_tap = gps::TimeAndPos::new();
223
224     loop {
225         let mut tap = gps::TimeAndPos::new();
226         let mut show_time = false;
227         let old_gps_has_fix = gps_has_fix;
228
229         while gps.update(&mut tap, uart0_try_read) {
230             prev_tap = tap;
231
232             show_time = true;
233
234             gps_has_fix = true;
235             gps_has_fix_ticks = systick::now();
236         }
237
238         // Did GPS fix information expire?
239         if gps_has_fix && systick::has_timeout_ms(gps_has_fix_ticks, 1500) {
240             gps_has_fix = false;
241         }
242
243         if gps_has_fix && !old_gps_has_fix {
244             display.show_icon(display::Icon::SatelliteBody);
245             display.show_icon(display::Icon::SatelliteWave1);
246             display.show_icon(display::Icon::SatelliteWave2);
247         } else if !gps_has_fix {
248             gps_icon_timer.update(|state| {
249                 if state == 1 {
250                     display.show_icon(display::Icon::SatelliteWave1);
251                     2
252                 } else if state == 2 {
253                     display.show_icon(display::Icon::SatelliteWave2);
254                     3
255                 } else if state == 3 {
256                     display.hide_icon(display::Icon::SatelliteBody);
257                     display.hide_icon(display::Icon::SatelliteWave1);
258                     display.hide_icon(display::Icon::SatelliteWave2);
259                     0
260                 } else {
261                     display.show_icon(display::Icon::SatelliteBody);
262                     1
263                 }
264             });
265         }
266
267         if show_time {
268             if let Some(tm) = Time::from_unix_time(prev_tap.unix_time) {
269                 let mut time_s = [b' '; 8];
270                 tm.fmt_time(&mut time_s);
271
272                 screen.clear();
273                 screen.draw_text(&time_s);
274
275                 display.draw(&screen);
276             }
277         }
278
279         heart_icon_timer.update(|state| {
280             if state == 1 {
281                 display.hide_icon(display::Icon::Heart);
282                 0
283             } else {
284                 display.show_icon(display::Icon::Heart);
285                 1
286             }
287         });
288
289         shell.update(&mut logger);
290
291         if reset_requested() {
292             nvic::system_reset();
293         }
294     }
295 }