From e6974cffb2476cc97f691ae850831833f5c140a3 Mon Sep 17 00:00:00 2001
From: kirbylife <kirbylife@protonmail.com>
Date: Tue, 4 Apr 2023 14:29:28 -0600
Subject: [PATCH] Add the STN 16x2 screen display driver

---
 Cargo.toml    |   1 +
 src/screen.rs | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 110 insertions(+)
 create mode 100644 src/screen.rs

diff --git a/Cargo.toml b/Cargo.toml
index db604a6..96518d8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,6 +10,7 @@ sha1_smol = "1.0.0"
 binascii = { version = "0.1", default-features = false, features = ["decode"] }
 ds323x = "0.5.0"
 ufmt = "0.2.0"
+embedded-hal = "0.2.7"
 
 [profile.release]
 lto = true
diff --git a/src/screen.rs b/src/screen.rs
new file mode 100644
index 0000000..6062191
--- /dev/null
+++ b/src/screen.rs
@@ -0,0 +1,109 @@
+use arduino_hal::delay_ms;
+use arduino_hal::delay_us;
+use embedded_hal::digital::v2::OutputPin;
+
+const INIT_CMD: u8 = 0x03; // 0b00000011
+const MODE_4_BITS_CMD: u8 = 0x02; // 0b00000010
+const DISPLAY_MODE_CMD: u8 = 0x28; // 0b00101000
+const ENABLE_SCREEN_CMD: u8 = 0x0C; // 0b00001100
+const CLEAN_SCREEN_CMD: u8 = 0x01; // 0b00000001
+
+pub struct StnScreen<'a, RS, EN, D4, D5, D6, D7> {
+    rs: &'a mut RS,
+    en: &'a mut EN,
+    d4: &'a mut D4,
+    d5: &'a mut D5,
+    d6: &'a mut D6,
+    d7: &'a mut D7,
+}
+
+impl<
+        'a,
+        RS: OutputPin,
+        EN: OutputPin,
+        D4: OutputPin,
+        D5: OutputPin,
+        D6: OutputPin,
+        D7: OutputPin,
+    > StnScreen<'a, RS, EN, D4, D5, D6, D7>
+{
+    pub fn new(
+        rs: &'a mut RS,
+        en: &'a mut EN,
+        d4: &'a mut D4,
+        d5: &'a mut D5,
+        d6: &'a mut D6,
+        d7: &'a mut D7,
+    ) -> Self {
+        let mut display = StnScreen {
+            rs,
+            en,
+            d4,
+            d5,
+            d6,
+            d7,
+        };
+
+        // Initializing of LCM on page 16
+        // https://z3d9b7u8.stackpathcdn.com/pdf-down/L/C/D/LCD-1602A_CA.pdf
+        delay_ms(16);
+        display.write_nibble(INIT_CMD, false);
+        delay_ms(5);
+        display.write_nibble(INIT_CMD, false);
+        delay_us(100);
+        display.write_nibble(INIT_CMD, false);
+        delay_us(100);
+        display.write_nibble(MODE_4_BITS_CMD, false);
+        delay_us(100);
+        display.send_cmd(DISPLAY_MODE_CMD, false);
+        display.send_cmd(ENABLE_SCREEN_CMD, false);
+
+        display
+    }
+
+    fn pulse_enable(&mut self) {
+        self.en.set_low().ok();
+        delay_us(1);
+        self.en.set_high().ok();
+        delay_us(1);
+        self.en.set_low().ok();
+        delay_us(100);
+    }
+
+    fn send_cmd(&mut self, cmd: u8, is_data: bool) {
+        self.write_nibble((cmd >> 4) & 0x0F, is_data);
+        self.write_nibble(cmd & 0x0F, is_data);
+        delay_ms(10);
+    }
+
+    fn write_nibble(&mut self, nibble: u8, is_data: bool) {
+        self.rs.set_state(is_data.into()).ok();
+        self.d4.set_state(last_bit(nibble >> 0).into()).ok();
+        self.d5.set_state(last_bit(nibble >> 1).into()).ok();
+        self.d6.set_state(last_bit(nibble >> 2).into()).ok();
+        self.d7.set_state(last_bit(nibble >> 3).into()).ok();
+        self.pulse_enable();
+    }
+
+    fn clear(&mut self) {
+        display.send_cmd(CLEAN_SCREEN_CMD, false);
+    }
+
+    pub fn write_char(&mut self, c: char) {
+        let c = c as u8;
+        self.write_nibble((c >> 4) & 0x0F, true);
+        self.write_nibble(c & 0x0F, true);
+        delay_us(100);
+    }
+
+    pub fn write_str<S: AsRef<str>>(&mut self, s: S) {
+        let s = s.as_ref();
+        for c in s.chars() {
+            self.write_char(c);
+        }
+    }
+}
+
+fn last_bit(n: u8) -> bool {
+    (n & 1) != 0
+}