04f6c354d776b36d20ed4df8b3ee597c21277e73
[gps-watch.git] / src / common / display.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 use core::ptr;
25 use screen;
26 use gpio;
27 use i2c;
28
29 pub struct Display {
30     reset_gpio: u32,
31     reset_gpio_pin: u32,
32     i2c_slave_address: u8,
33 }
34
35 pub enum Icon {
36     SatelliteBody,
37     SatelliteWave1,
38     SatelliteWave2,
39     Heart,
40     Alarm,
41 }
42
43 fn delay(u: u32) {
44     let mut r = u;
45
46     unsafe {
47         loop {
48             let x = ptr::read_volatile(&r);
49             ptr::write_volatile(&mut r, x - 1);
50
51             if x == 0 {
52                 break
53             }
54
55             let mut a = 0;
56
57             loop {
58                 let x = ptr::read_volatile(&a);
59                 ptr::write_volatile(&mut a, x + 1);
60
61                 if x == 5000 {
62                     break
63                 }
64             }
65         }
66     }
67 }
68
69 fn delay2(u: u32) {
70     let mut r = u;
71
72     unsafe {
73         loop {
74             let x = ptr::read_volatile(&r);
75             ptr::write_volatile(&mut r, x - 1);
76
77             if x == 0 {
78                 break
79             }
80         }
81     }
82 }
83
84 const ICON_COLUMN: usize = 8;
85
86 impl Display {
87     pub fn new(reset_gpio: u32, reset_gpio_pin: u32, i2c_slave_address: u8) -> Display {
88         Display {
89             reset_gpio: reset_gpio,
90             reset_gpio_pin: reset_gpio_pin,
91             i2c_slave_address: i2c_slave_address,
92         }
93     }
94
95     pub fn init(&mut self) {
96         let init_sequence : [u8; 18] = [
97             0x3a,
98             0x88,
99             0x88,
100             0x88,
101             0x88,
102             0x60,
103             0xe3,
104             0x2f,
105             0x30,
106             0x96,
107             0xa4,
108             0xb1,
109             0x53,
110             0xb2,
111             0x78,
112             0x01,
113             0x60,
114             0x3d,
115         ];
116
117         gpio::clear(self.reset_gpio, self.reset_gpio_pin);
118         delay(100);
119         gpio::set(self.reset_gpio, self.reset_gpio_pin);
120         delay(100);
121
122         for &b in init_sequence.iter() {
123             self.write_command(b);
124         }
125     }
126
127     fn icon_row(&self, icon: Icon) -> usize {
128         match icon {
129             Icon::SatelliteBody => 31,
130             Icon::SatelliteWave1 => 29,
131             Icon::SatelliteWave2 => 27,
132             Icon::Heart => 45,
133             Icon::Alarm => 61,
134         }
135     }
136
137     pub fn show_icon(&mut self, icon: Icon) {
138         let row = self.icon_row(icon);
139
140         self.seek(row, ICON_COLUMN);
141         self.write_data(0x80);
142     }
143
144     pub fn hide_icon(&mut self, icon: Icon) {
145         let row = self.icon_row(icon);
146
147         self.seek(row, ICON_COLUMN);
148         self.write_data(0x00);
149     }
150
151     pub fn clear(&mut self) {
152         self.seek(0, 0);
153
154         for _ in 0..(32 * 128) {
155             self.write_data(0x00);
156         }
157     }
158
159     pub fn draw(&mut self, screen: &screen::Screen) {
160         let mut mask : u8 = 0x80;
161
162         for col in 0..screen::WIDTH_PX {
163             mask = mask.rotate_left(1);
164
165             // Rotate by 90 degrees.
166             //
167             // Also skip special columns 0..8 which we don't consider
168             // to be part of the screen (they hold the icon area).
169             self.seek(screen::WIDTH_PX - 1 - col, 9);
170
171             // We'll draw eight pixels at once.
172             for row in 0..(screen::HEIGHT_PX / 8) {
173                 let mut combined = 0;
174
175                 for sub in 0..8 {
176                     let index = ((row as usize * 8) + sub) * screen::WIDTH_BYTES
177                               + (col as usize / 8);
178                     let pixel = screen.pixel(index);
179
180                     combined <<= 1;
181
182                     if (pixel & mask) != 0 {
183                         combined |= 1;
184                     }
185                 }
186
187                 self.write_data(combined);
188             }
189         }
190     }
191
192     fn write_command(&self, c: u8) {
193         i2c::tx16(i2c::I2C0, self.i2c_slave_address, 0x80 | ((c as u16) << 8));
194         delay2(50);
195     }
196
197     fn write_data(&self, d: u8) {
198         i2c::tx16(i2c::I2C0, self.i2c_slave_address, 0xc0 | ((d as u16) << 8));
199         delay2(50);
200     }
201
202     fn seek(&self, row: usize, col: usize) {
203         self.write_command(0x00 | ((row >> 0) & 0x0f) as u8);
204         self.write_command(0x10 | ((row >> 4) & 0x07) as u8);
205         self.write_command(0xc0 | ((col >> 0) & 0x1f) as u8);
206     }
207 }