common: Add the display module.
[gps-watch.git] / src / common / display.rs
diff --git a/src/common/display.rs b/src/common/display.rs
new file mode 100644 (file)
index 0000000..4f09ef5
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2019 Tilman Sauerbeck (tilman at code-monkey de)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+use core::ptr;
+use screen;
+use gpio;
+use i2c;
+
+pub struct Display {
+    reset_gpio: u32,
+    reset_gpio_pin: u32,
+    i2c_slave_address: u8,
+}
+
+fn delay(u: u32) {
+    let mut r = u;
+
+    unsafe {
+        loop {
+            let x = ptr::read_volatile(&r);
+            ptr::write_volatile(&mut r, x - 1);
+
+            if x == 0 {
+                break
+            }
+
+            let mut a = 0;
+
+            loop {
+                let x = ptr::read_volatile(&a);
+                ptr::write_volatile(&mut a, x + 1);
+
+                if x == 5000 {
+                    break
+                }
+            }
+        }
+    }
+}
+
+fn delay2(u: u32) {
+    let mut r = u;
+
+    unsafe {
+        loop {
+            let x = ptr::read_volatile(&r);
+            ptr::write_volatile(&mut r, x - 1);
+
+            if x == 0 {
+                break
+            }
+        }
+    }
+}
+
+impl Display {
+    pub fn new(reset_gpio: u32, reset_gpio_pin: u32, i2c_slave_address: u8) -> Display {
+        Display {
+            reset_gpio: reset_gpio,
+            reset_gpio_pin: reset_gpio_pin,
+            i2c_slave_address: i2c_slave_address,
+        }
+    }
+
+    pub fn init(&mut self) {
+        let init_sequence : [u8; 18] = [
+            0x3a,
+            0x88,
+            0x88,
+            0x88,
+            0x88,
+            0x60,
+            0xe3,
+            0x2f,
+            0x30,
+            0x96,
+            0xa4,
+            0xb1,
+            0x53,
+            0xb2,
+            0x78,
+            0x01,
+            0x60,
+            0x3d,
+        ];
+
+        gpio::clear(self.reset_gpio, self.reset_gpio_pin);
+        delay(100);
+        gpio::set(self.reset_gpio, self.reset_gpio_pin);
+        delay(100);
+
+        for &b in init_sequence.iter() {
+            self.write_command(b);
+        }
+    }
+
+    pub fn clear(&mut self) {
+        self.seek(0, 0);
+
+        for _ in 0..(32 * 128) {
+            self.write_data(0x00);
+        }
+    }
+
+    pub fn draw(&mut self, screen: &screen::Screen) {
+        let mut mask : u8 = 0x80;
+
+        for col in 0..screen::WIDTH_PX {
+            mask = mask.rotate_left(1);
+
+            // Rotate by 90 degrees.
+            //
+            // Also skip special columns 0..8 which we don't consider
+            // to be part of the screen (they hold the icon area).
+            self.seek(screen::WIDTH_PX - 1 - col, 9);
+
+            // We'll draw eight pixels at once.
+            for row in 0..(screen::HEIGHT_PX / 8) {
+                let mut combined = 0;
+
+                for sub in 0..8 {
+                    let index = ((row as usize * 8) + sub) * screen::WIDTH_BYTES
+                              + (col as usize / 8);
+                    let pixel = screen.pixel(index);
+
+                    combined <<= 1;
+
+                    if (pixel & mask) != 0 {
+                        combined |= 1;
+                    }
+                }
+
+                self.write_data(combined);
+            }
+        }
+    }
+
+    fn write_command(&self, c: u8) {
+        i2c::tx16(i2c::I2C0, self.i2c_slave_address, 0x80 | ((c as u16) << 8));
+        delay2(50);
+    }
+
+    fn write_data(&self, d: u8) {
+        i2c::tx16(i2c::I2C0, self.i2c_slave_address, 0xc0 | ((d as u16) << 8));
+        delay2(50);
+    }
+
+    fn seek(&self, row: usize, col: usize) {
+        self.write_command(0x00 | ((row >> 0) & 0x0f) as u8);
+        self.write_command(0x10 | ((row >> 4) & 0x07) as u8);
+        self.write_command(0xc0 | ((col >> 0) & 0x1f) as u8);
+    }
+}