aboutsummaryrefslogtreecommitdiffstats
path: root/sw/deps/hd44780-driver/src
diff options
context:
space:
mode:
Diffstat (limited to 'sw/deps/hd44780-driver/src')
-rw-r--r--sw/deps/hd44780-driver/src/bus/eightbit.rs171
-rw-r--r--sw/deps/hd44780-driver/src/bus/fourbit.rs144
-rw-r--r--sw/deps/hd44780-driver/src/bus/i2c.rs71
-rw-r--r--sw/deps/hd44780-driver/src/bus/i2c_mcp23008.rs102
-rw-r--r--sw/deps/hd44780-driver/src/bus/mod.rs25
-rw-r--r--sw/deps/hd44780-driver/src/display_mode.rs95
-rw-r--r--sw/deps/hd44780-driver/src/entry_mode.rs97
-rw-r--r--sw/deps/hd44780-driver/src/error.rs3
-rw-r--r--sw/deps/hd44780-driver/src/lib.rs552
9 files changed, 1260 insertions, 0 deletions
diff --git a/sw/deps/hd44780-driver/src/bus/eightbit.rs b/sw/deps/hd44780-driver/src/bus/eightbit.rs
new file mode 100644
index 0000000..99aff96
--- /dev/null
+++ b/sw/deps/hd44780-driver/src/bus/eightbit.rs
@@ -0,0 +1,171 @@
+use embedded_hal::blocking::delay::{DelayMs, DelayUs};
+use embedded_hal::digital::v2::OutputPin;
+
+use crate::{
+ bus::DataBus,
+ error::{Error, Result},
+};
+
+pub struct EightBitBus<
+ RS: OutputPin,
+ EN: OutputPin,
+ D0: OutputPin,
+ D1: OutputPin,
+ D2: OutputPin,
+ D3: OutputPin,
+ D4: OutputPin,
+ D5: OutputPin,
+ D6: OutputPin,
+ D7: OutputPin,
+> {
+ rs: RS,
+ en: EN,
+ d0: D0,
+ d1: D1,
+ d2: D2,
+ d3: D3,
+ d4: D4,
+ d5: D5,
+ d6: D6,
+ d7: D7,
+}
+
+impl<
+ RS: OutputPin,
+ EN: OutputPin,
+ D0: OutputPin,
+ D1: OutputPin,
+ D2: OutputPin,
+ D3: OutputPin,
+ D4: OutputPin,
+ D5: OutputPin,
+ D6: OutputPin,
+ D7: OutputPin,
+ > EightBitBus<RS, EN, D0, D1, D2, D3, D4, D5, D6, D7>
+{
+ pub fn from_pins(
+ rs: RS,
+ en: EN,
+ d0: D0,
+ d1: D1,
+ d2: D2,
+ d3: D3,
+ d4: D4,
+ d5: D5,
+ d6: D6,
+ d7: D7,
+ ) -> EightBitBus<RS, EN, D0, D1, D2, D3, D4, D5, D6, D7> {
+ EightBitBus {
+ rs,
+ en,
+ d0,
+ d1,
+ d2,
+ d3,
+ d4,
+ d5,
+ d6,
+ d7,
+ }
+ }
+
+ fn set_bus_bits(&mut self, data: u8) -> Result<()> {
+ let db0: bool = (0b0000_0001 & data) != 0;
+ let db1: bool = (0b0000_0010 & data) != 0;
+ let db2: bool = (0b0000_0100 & data) != 0;
+ let db3: bool = (0b0000_1000 & data) != 0;
+ let db4: bool = (0b0001_0000 & data) != 0;
+ let db5: bool = (0b0010_0000 & data) != 0;
+ let db6: bool = (0b0100_0000 & data) != 0;
+ let db7: bool = (0b1000_0000 & data) != 0;
+
+ if db0 {
+ self.d0.set_high().map_err(|_| Error)?;
+ } else {
+ self.d0.set_low().map_err(|_| Error)?;
+ }
+
+ if db1 {
+ self.d1.set_high().map_err(|_| Error)?;
+ } else {
+ self.d1.set_low().map_err(|_| Error)?;
+ }
+
+ if db2 {
+ self.d2.set_high().map_err(|_| Error)?;
+ } else {
+ self.d2.set_low().map_err(|_| Error)?;
+ }
+
+ if db3 {
+ self.d3.set_high().map_err(|_| Error)?;
+ } else {
+ self.d3.set_low().map_err(|_| Error)?;
+ }
+
+ if db4 {
+ self.d4.set_high().map_err(|_| Error)?;
+ } else {
+ self.d4.set_low().map_err(|_| Error)?;
+ }
+
+ if db5 {
+ self.d5.set_high().map_err(|_| Error)?;
+ } else {
+ self.d5.set_low().map_err(|_| Error)?;
+ }
+
+ if db6 {
+ self.d6.set_high().map_err(|_| Error)?;
+ } else {
+ self.d6.set_low().map_err(|_| Error)?;
+ }
+
+ if db7 {
+ self.d7.set_high().map_err(|_| Error)?;
+ } else {
+ self.d7.set_low().map_err(|_| Error)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl<
+ RS: OutputPin,
+ EN: OutputPin,
+ D0: OutputPin,
+ D1: OutputPin,
+ D2: OutputPin,
+ D3: OutputPin,
+ D4: OutputPin,
+ D5: OutputPin,
+ D6: OutputPin,
+ D7: OutputPin,
+ > DataBus for EightBitBus<RS, EN, D0, D1, D2, D3, D4, D5, D6, D7>
+{
+ fn write<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ byte: u8,
+ data: bool,
+ delay: &mut D,
+ ) -> Result<()> {
+ if data {
+ self.rs.set_high().map_err(|_| Error)?;
+ } else {
+ self.rs.set_low().map_err(|_| Error)?;
+ }
+
+ self.set_bus_bits(byte)?;
+
+ self.en.set_high().map_err(|_| Error)?;
+ delay.delay_ms(2u8);
+ self.en.set_low().map_err(|_| Error)?;
+
+ if data {
+ self.rs.set_low().map_err(|_| Error)?;
+ }
+
+ Ok(())
+ }
+}
diff --git a/sw/deps/hd44780-driver/src/bus/fourbit.rs b/sw/deps/hd44780-driver/src/bus/fourbit.rs
new file mode 100644
index 0000000..27519df
--- /dev/null
+++ b/sw/deps/hd44780-driver/src/bus/fourbit.rs
@@ -0,0 +1,144 @@
+use embedded_hal::blocking::delay::{DelayMs, DelayUs};
+use embedded_hal::digital::v2::OutputPin;
+
+use crate::bus::DataBus;
+use crate::error::{Error, Result};
+
+pub struct FourBitBus<
+ RS: OutputPin,
+ EN: OutputPin,
+ D4: OutputPin,
+ D5: OutputPin,
+ D6: OutputPin,
+ D7: OutputPin,
+> {
+ rs: RS,
+ en: EN,
+ d4: D4,
+ d5: D5,
+ d6: D6,
+ d7: D7,
+}
+
+impl<RS: OutputPin, EN: OutputPin, D4: OutputPin, D5: OutputPin, D6: OutputPin, D7: OutputPin>
+ FourBitBus<RS, EN, D4, D5, D6, D7>
+{
+ pub fn from_pins(
+ rs: RS,
+ en: EN,
+ d4: D4,
+ d5: D5,
+ d6: D6,
+ d7: D7,
+ ) -> FourBitBus<RS, EN, D4, D5, D6, D7> {
+ FourBitBus {
+ rs,
+ en,
+ d4,
+ d5,
+ d6,
+ d7,
+ }
+ }
+
+ fn write_lower_nibble(&mut self, data: u8) -> Result<()> {
+ let db0: bool = (0b0000_0001 & data) != 0;
+ let db1: bool = (0b0000_0010 & data) != 0;
+ let db2: bool = (0b0000_0100 & data) != 0;
+ let db3: bool = (0b0000_1000 & data) != 0;
+
+ if db0 {
+ self.d4.set_high().map_err(|_| Error)?;
+ } else {
+ self.d4.set_low().map_err(|_| Error)?;
+ }
+
+ if db1 {
+ self.d5.set_high().map_err(|_| Error)?;
+ } else {
+ self.d5.set_low().map_err(|_| Error)?;
+ }
+
+ if db2 {
+ self.d6.set_high().map_err(|_| Error)?;
+ } else {
+ self.d6.set_low().map_err(|_| Error)?;
+ }
+
+ if db3 {
+ self.d7.set_high().map_err(|_| Error)?;
+ } else {
+ self.d7.set_low().map_err(|_| Error)?;
+ }
+
+ Ok(())
+ }
+
+ fn write_upper_nibble(&mut self, data: u8) -> Result<()> {
+ let db4: bool = (0b0001_0000 & data) != 0;
+ let db5: bool = (0b0010_0000 & data) != 0;
+ let db6: bool = (0b0100_0000 & data) != 0;
+ let db7: bool = (0b1000_0000 & data) != 0;
+
+ if db4 {
+ self.d4.set_high().map_err(|_| Error)?;
+ } else {
+ self.d4.set_low().map_err(|_| Error)?;
+ }
+
+ if db5 {
+ self.d5.set_high().map_err(|_| Error)?;
+ } else {
+ self.d5.set_low().map_err(|_| Error)?;
+ }
+
+ if db6 {
+ self.d6.set_high().map_err(|_| Error)?;
+ } else {
+ self.d6.set_low().map_err(|_| Error)?;
+ }
+
+ if db7 {
+ self.d7.set_high().map_err(|_| Error)?;
+ } else {
+ self.d7.set_low().map_err(|_| Error)?;
+ }
+ Ok(())
+ }
+}
+
+impl<RS: OutputPin, EN: OutputPin, D4: OutputPin, D5: OutputPin, D6: OutputPin, D7: OutputPin>
+ DataBus for FourBitBus<RS, EN, D4, D5, D6, D7>
+{
+ fn write<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ byte: u8,
+ data: bool,
+ delay: &mut D,
+ ) -> Result<()> {
+ if data {
+ self.rs.set_high().map_err(|_| Error)?;
+ } else {
+ self.rs.set_low().map_err(|_| Error)?;
+ }
+
+ self.write_upper_nibble(byte)?;
+
+ // Pulse the enable pin to recieve the upper nibble
+ self.en.set_high().map_err(|_| Error)?;
+ delay.delay_ms(2u8);
+ self.en.set_low().map_err(|_| Error)?;
+
+ self.write_lower_nibble(byte)?;
+
+ // Pulse the enable pin to recieve the lower nibble
+ self.en.set_high().map_err(|_| Error)?;
+ delay.delay_ms(2u8);
+ self.en.set_low().map_err(|_| Error)?;
+
+ if data {
+ self.rs.set_low().map_err(|_| Error)?;
+ }
+ Ok(())
+ }
+}
diff --git a/sw/deps/hd44780-driver/src/bus/i2c.rs b/sw/deps/hd44780-driver/src/bus/i2c.rs
new file mode 100644
index 0000000..160e205
--- /dev/null
+++ b/sw/deps/hd44780-driver/src/bus/i2c.rs
@@ -0,0 +1,71 @@
+use embedded_hal::blocking::delay::{DelayMs, DelayUs};
+use embedded_hal::blocking::i2c::Write;
+
+use crate::{bus::DataBus, error::Result};
+
+/// This module supports I2C backpacks with a PCF8574 IC.
+/// Connections as follows:
+///
+/// <table>
+/// <tr><th>PCF8574 pin</th><th>name</th><th>LCD pin</th></tr>
+/// <tr><td>P0</td><td>RS</td><td>4</td></tr>
+/// <tr><td>P1</td><td>RW</td><td>5</td></tr>
+/// <tr><td>P2</td><td>E</td><td>6</td></tr>
+/// <tr><td>P3</td><td>Backlight</td><td></td></tr>
+/// <tr><td>P4</td><td>DB4</td><td>11</td></tr>
+/// <tr><td>P5</td><td>DB5</td><td>12</td></tr>
+/// <tr><td>P6</td><td>DB6</td><td>13</td></tr>
+/// <tr><td>P7</td><td>DB7</td><td>14</td></tr>
+/// </table>
+
+pub struct I2CBus<I2C: Write> {
+ i2c_bus: I2C,
+ address: u8,
+}
+
+const BACKLIGHT: u8 = 0b0000_1000;
+const ENABLE: u8 = 0b0000_0100;
+// const READ_WRITE: u8 = 0b0000_0010; // Not used as no reading of the `HD44780` is done
+const REGISTER_SELECT: u8 = 0b0000_0001;
+
+impl<I2C: Write> I2CBus<I2C> {
+ pub fn new(i2c_bus: I2C, address: u8) -> I2CBus<I2C> {
+ I2CBus { i2c_bus, address }
+ }
+
+ /// Write a nibble to the lcd
+ /// The nibble should be in the upper part of the byte
+ fn write_nibble<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ nibble: u8,
+ data: bool,
+ delay: &mut D,
+ ) {
+ let rs = match data {
+ false => 0u8,
+ true => REGISTER_SELECT,
+ };
+ let byte = nibble | rs | BACKLIGHT;
+
+ let _ = self.i2c_bus.write(self.address, &[byte, byte | ENABLE]);
+ delay.delay_ms(2u8);
+ let _ = self.i2c_bus.write(self.address, &[byte]);
+ }
+}
+
+impl<I2C: Write> DataBus for I2CBus<I2C> {
+ fn write<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ byte: u8,
+ data: bool,
+ delay: &mut D,
+ ) -> Result<()> {
+ let upper_nibble = byte & 0xF0;
+ self.write_nibble(upper_nibble, data, delay);
+
+ let lower_nibble = (byte & 0x0F) << 4;
+ self.write_nibble(lower_nibble, data, delay);
+
+ Ok(())
+ }
+}
diff --git a/sw/deps/hd44780-driver/src/bus/i2c_mcp23008.rs b/sw/deps/hd44780-driver/src/bus/i2c_mcp23008.rs
new file mode 100644
index 0000000..1ccb479
--- /dev/null
+++ b/sw/deps/hd44780-driver/src/bus/i2c_mcp23008.rs
@@ -0,0 +1,102 @@
+use embedded_hal::blocking::delay::{DelayMs, DelayUs};
+use embedded_hal::blocking::i2c::Write;
+
+use crate::{bus::DataBus, error::Result};
+
+/// This module supports I2C backpacks with a MCP23008 IC, like
+/// the one from adafruit.
+/// Connections as follows:
+///
+/// <table>
+/// <tr><th>MCP23008 pin</th><th>name</th><th>LCD pin</th></tr>
+/// <tr><td>0</td><td>N/C</td><td></td></tr>
+/// <tr><td>1</td><td>RS</td><td>4</td></tr>
+/// <tr><td>2</td><td>E</td><td>6</td></tr>
+/// <tr><td>3</td><td>DB4</td><td>11</td></tr>
+/// <tr><td>4</td><td>DB5</td><td>12</td></tr>
+/// <tr><td>5</td><td>DB6</td><td>13</td></tr>
+/// <tr><td>6</td><td>DB7</td><td>14</td></tr>
+/// <tr><td>7</td><td>Backlight</td><td></td></tr>
+/// </table>
+
+pub struct I2CMCP23008Bus<I2C: Write> {
+ i2c_bus: I2C,
+ address: u8,
+}
+
+const REG_IODIR : u8 = 0x00;
+const REG_GPIO : u8 = 0x09;
+
+impl<I2C: Write> I2CMCP23008Bus<I2C> {
+
+ /// Create a new instance of the MCP23008 I2C driver. The address of those
+ /// devices is 0b010_0xxx where x is configured by bootstrap pins.
+ pub fn new(i2c_bus: I2C, address: u8) -> Result<I2CMCP23008Bus<I2C>> {
+ let mut mcp23008 = I2CMCP23008Bus { i2c_bus, address };
+ // Set to reset values according to datasheet
+ mcp23008.write_reg(REG_IODIR, 0b1111_1111)?;
+ for reg in 0x01usize..0x0A {
+ mcp23008.write_reg(reg as u8, 0)?;
+ }
+ // Configure pins 1..=7 as outputs, see pin mapping above
+ mcp23008.write_reg(REG_IODIR, 0b0000_0001)?;
+ Ok(mcp23008)
+ }
+
+ fn write_reg(&mut self, reg: u8, value: u8) -> Result<()> {
+ let data = [reg, value];
+ self.i2c_bus.write(self.address, &data)
+ .map_err(|_| crate::error::Error)
+ }
+
+ fn set_pins(&mut self, pins: u8) -> Result<()> {
+ self.write_reg(REG_GPIO, pins)
+ }
+
+}
+
+impl<I2C: Write> DataBus for I2CMCP23008Bus<I2C> {
+ fn write<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ byte: u8,
+ data: bool,
+ delay: &mut D,
+ ) -> Result<()> {
+ let rs = if data { 0b10 } else { 0b00 };
+ let en = 0b0000_0100;
+ let backlight = 0b1000_0000; // always enable backlight
+
+ let upper_nibble = (byte & 0xF0) >> 4;
+ let lower_nibble = byte & 0x0F;
+
+ // upper nibble: [d7 d6 d5 d4]
+ // Pulse EN
+ // lower nibble: [d3 d2 d1 d0]
+ // Pulse EN
+
+ let pins = rs | backlight | (upper_nibble << 3);
+ self.set_pins(pins)?;
+
+ delay.delay_ms(1);
+
+ let pins = rs | en | backlight | (upper_nibble << 3);
+ self.set_pins(pins)?;
+
+ delay.delay_ms(1);
+
+ let pins = rs | backlight | (lower_nibble << 3);
+ self.set_pins(pins)?;
+
+ delay.delay_ms(1);
+
+ let pins = rs | en | backlight | (lower_nibble << 3);
+ self.set_pins(pins)?;
+
+ delay.delay_ms(1);
+
+ let pins = backlight | (lower_nibble << 3);
+ self.set_pins(pins)?;
+
+ Ok(())
+ }
+}
diff --git a/sw/deps/hd44780-driver/src/bus/mod.rs b/sw/deps/hd44780-driver/src/bus/mod.rs
new file mode 100644
index 0000000..b141f7e
--- /dev/null
+++ b/sw/deps/hd44780-driver/src/bus/mod.rs
@@ -0,0 +1,25 @@
+use embedded_hal::blocking::delay::{DelayMs, DelayUs};
+
+mod eightbit;
+mod fourbit;
+mod i2c;
+mod i2c_mcp23008;
+
+pub use self::eightbit::EightBitBus;
+pub use self::fourbit::FourBitBus;
+pub use self::i2c::I2CBus;
+pub use self::i2c_mcp23008::I2CMCP23008Bus;
+
+use crate::error::Result;
+
+pub trait DataBus {
+ fn write<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ byte: u8,
+ data: bool,
+ delay: &mut D,
+ ) -> Result<()>;
+
+ // TODO
+ // fn read(...)
+}
diff --git a/sw/deps/hd44780-driver/src/display_mode.rs b/sw/deps/hd44780-driver/src/display_mode.rs
new file mode 100644
index 0000000..8039f7b
--- /dev/null
+++ b/sw/deps/hd44780-driver/src/display_mode.rs
@@ -0,0 +1,95 @@
+use crate::{Cursor, CursorBlink, Display};
+
+pub struct DisplayMode {
+ pub cursor_visibility: Cursor,
+ pub cursor_blink: CursorBlink,
+ pub display: Display,
+}
+
+impl Default for DisplayMode {
+ fn default() -> DisplayMode {
+ DisplayMode {
+ cursor_visibility: Cursor::Visible,
+ cursor_blink: CursorBlink::On,
+ display: Display::On,
+ }
+ }
+}
+
+impl DisplayMode {
+ pub fn as_byte(&self) -> u8 {
+ let cursor_blink_bits = match self.cursor_blink {
+ CursorBlink::On => 0b0000_0001,
+ CursorBlink::Off => 0,
+ };
+
+ let cursor_visible_bits = match self.cursor_visibility {
+ Cursor::Visible => 0b0000_0010,
+ Cursor::Invisible => 0,
+ };
+
+ let display_bits = match self.display {
+ Display::On => 0b0000_0100,
+ Display::Off => 0,
+ };
+
+ 0b0000_1000 | cursor_visible_bits | cursor_blink_bits | display_bits
+ }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use super::*;
+
+ #[test]
+ fn cursor_visible() {
+ let dm = DisplayMode {
+ cursor_visibility: Cursor::Visible,
+ ..Default::default()
+ };
+
+ assert!(dm.as_byte() & 0b0000_0010 != 0);
+
+ let dm = DisplayMode {
+ cursor_visibility: Cursor::Invisible,
+ ..Default::default()
+ };
+
+ assert!(dm.as_byte() & 0b0000_0010 == 0);
+ }
+
+ #[test]
+ fn cursor_blink() {
+ let dm = DisplayMode {
+ cursor_blink: CursorBlink::On,
+ ..Default::default()
+ };
+
+ assert!(dm.as_byte() & 0b0000_0001 != 0);
+
+ let dm = DisplayMode {
+ cursor_blink: CursorBlink::Off,
+ ..Default::default()
+ };
+
+ assert!(dm.as_byte() & 0b0000_0001 == 0);
+ }
+
+ #[test]
+ fn display_visible() {
+ let dm = DisplayMode {
+ display: Display::On,
+ ..Default::default()
+ };
+
+ assert!(dm.as_byte() & 0b0000_0100 != 0);
+
+ let dm = DisplayMode {
+ display: Display::Off,
+ ..Default::default()
+ };
+
+ assert!(dm.as_byte() & 0b0000_0100 == 0);
+ }
+}
diff --git a/sw/deps/hd44780-driver/src/entry_mode.rs b/sw/deps/hd44780-driver/src/entry_mode.rs
new file mode 100644
index 0000000..577c5d0
--- /dev/null
+++ b/sw/deps/hd44780-driver/src/entry_mode.rs
@@ -0,0 +1,97 @@
+/// Determines if the cursor should be incremented or decremented on write
+#[derive(Debug, PartialEq, Eq)]
+pub enum CursorMode {
+ Increment,
+ Decrement,
+}
+
+impl Default for CursorMode {
+ fn default() -> CursorMode {
+ CursorMode::Increment
+ }
+}
+
+/// Determines if the screen should be shifted on write
+#[derive(Debug, PartialEq, Eq)]
+pub enum ShiftMode {
+ Enabled,
+ Disabled,
+}
+
+impl From<bool> for ShiftMode {
+ fn from(b: bool) -> ShiftMode {
+ if b {
+ ShiftMode::Enabled
+ } else {
+ ShiftMode::Disabled
+ }
+ }
+}
+
+impl Default for ShiftMode {
+ fn default() -> ShiftMode {
+ ShiftMode::Disabled
+ }
+}
+
+#[derive(Default)]
+pub struct EntryMode {
+ pub cursor_mode: CursorMode,
+ pub shift_mode: ShiftMode,
+}
+
+impl EntryMode {
+ pub fn as_byte(&self) -> u8 {
+ let cursor_bits = match self.cursor_mode {
+ CursorMode::Increment => 0b0000_0010,
+ CursorMode::Decrement => 0,
+ };
+
+ let shift_bits = match self.shift_mode {
+ ShiftMode::Enabled => 0b0000_0001,
+ ShiftMode::Disabled => 0,
+ };
+
+ 0b0000_0100 | cursor_bits | shift_bits
+ }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use super::*;
+
+ #[test]
+ fn cursor_mode() {
+ let em = EntryMode {
+ cursor_mode: CursorMode::Increment,
+ shift_mode: Default::default(),
+ };
+
+ assert!(em.as_byte() & 0b0000_0010 != 0);
+
+ let em = EntryMode {
+ cursor_mode: CursorMode::Decrement,
+ shift_mode: Default::default(),
+ };
+
+ assert!(em.as_byte() & 0b0000_0010 == 0);
+ }
+
+ #[test]
+ fn shift_mode() {
+ let em = EntryMode {
+ cursor_mode: Default::default(),
+ shift_mode: ShiftMode::Enabled,
+ };
+
+ assert!(em.as_byte() & 0b0000_0001 != 0);
+
+ let em = EntryMode {
+ cursor_mode: Default::default(),
+ shift_mode: ShiftMode::Disabled,
+ };
+
+ assert!(em.as_byte() & 0b0000_0001 == 0);
+ }
+}
diff --git a/sw/deps/hd44780-driver/src/error.rs b/sw/deps/hd44780-driver/src/error.rs
new file mode 100644
index 0000000..2deaf85
--- /dev/null
+++ b/sw/deps/hd44780-driver/src/error.rs
@@ -0,0 +1,3 @@
+#[derive(Debug)]
+pub struct Error;
+pub type Result<T> = core::result::Result<T, Error>;
diff --git a/sw/deps/hd44780-driver/src/lib.rs b/sw/deps/hd44780-driver/src/lib.rs
new file mode 100644
index 0000000..5211346
--- /dev/null
+++ b/sw/deps/hd44780-driver/src/lib.rs
@@ -0,0 +1,552 @@
+#![no_std]
+
+//use core::fmt::Result;
+//use core::fmt::Write;
+
+use embedded_hal::blocking::delay::{DelayMs, DelayUs};
+use embedded_hal::blocking::i2c;
+use embedded_hal::digital::v2::OutputPin;
+
+pub mod bus;
+use bus::{DataBus, EightBitBus, FourBitBus, I2CBus, I2CMCP23008Bus};
+
+pub mod error;
+use error::Result;
+
+pub mod entry_mode;
+
+use entry_mode::{CursorMode, EntryMode};
+
+pub mod display_mode;
+
+pub use display_mode::DisplayMode;
+
+pub struct HD44780<B: DataBus> {
+ bus: B,
+ entry_mode: EntryMode,
+ display_mode: DisplayMode,
+}
+
+/// Used in the direction argument for shifting the cursor and the display
+pub enum Direction {
+ Left,
+ Right,
+}
+
+/// Used in set_display_mode to make the parameters more clear
+pub enum Display {
+ On,
+ Off,
+}
+
+pub enum Cursor {
+ Visible,
+ Invisible,
+}
+
+pub enum CursorBlink {
+ On,
+ Off,
+}
+
+impl<
+ RS: OutputPin,
+ EN: OutputPin,
+ D0: OutputPin,
+ D1: OutputPin,
+ D2: OutputPin,
+ D3: OutputPin,
+ D4: OutputPin,
+ D5: OutputPin,
+ D6: OutputPin,
+ D7: OutputPin,
+ > HD44780<EightBitBus<RS, EN, D0, D1, D2, D3, D4, D5, D6, D7>>
+{
+ /// Create an instance of a `HD44780` from 8 data pins, a register select
+ /// pin, an enable pin and a struct implementing the delay trait.
+ /// - The delay instance is used to sleep between commands to
+ /// ensure the `HD44780` has enough time to process commands.
+ /// - The eight db0..db7 pins are used to send and recieve with
+ /// the `HD44780`.
+ /// - The register select pin is used to tell the `HD44780`
+ /// if incoming data is a command or data.
+ /// - The enable pin is used to tell the `HD44780` that there
+ /// is data on the 8 data pins and that it should read them in.
+ ///
+ pub fn new_8bit<D: DelayUs<u16> + DelayMs<u8>>(
+ rs: RS,
+ en: EN,
+ d0: D0,
+ d1: D1,
+ d2: D2,
+ d3: D3,
+ d4: D4,
+ d5: D5,
+ d6: D6,
+ d7: D7,
+ delay: &mut D,
+ ) -> Result<HD44780<EightBitBus<RS, EN, D0, D1, D2, D3, D4, D5, D6, D7>>> {
+ let mut hd = HD44780 {
+ bus: EightBitBus::from_pins(rs, en, d0, d1, d2, d3, d4, d5, d6, d7),
+ entry_mode: EntryMode::default(),
+ display_mode: DisplayMode::default(),
+ };
+
+ hd.init_8bit(delay)?;
+
+ return Ok(hd);
+ }
+}
+
+impl<RS: OutputPin, EN: OutputPin, D4: OutputPin, D5: OutputPin, D6: OutputPin, D7: OutputPin>
+ HD44780<FourBitBus<RS, EN, D4, D5, D6, D7>>
+{
+ /// Create an instance of a `HD44780` from 4 data pins, a register select
+ /// pin, an enable pin and a struct implementing the delay trait.
+ /// - The delay instance is used to sleep between commands to
+ /// ensure the `HD44780` has enough time to process commands.
+ /// - The four db0..db3 pins are used to send and recieve with
+ /// the `HD44780`.
+ /// - The register select pin is used to tell the `HD44780`
+ /// if incoming data is a command or data.
+ /// - The enable pin is used to tell the `HD44780` that there
+ /// is data on the 4 data pins and that it should read them in.
+ ///
+ /// This mode operates differently than 8 bit mode by using 4 less
+ /// pins for data, which is nice on devices with less I/O although
+ /// the I/O takes a 'bit' longer
+ ///
+ /// Instead of commands being sent byte by byte each command is
+ /// broken up into it's upper and lower nibbles (4 bits) before
+ /// being sent over the data bus
+ ///
+ pub fn new_4bit<D: DelayUs<u16> + DelayMs<u8>>(
+ rs: RS,
+ en: EN,
+ d4: D4,
+ d5: D5,
+ d6: D6,
+ d7: D7,
+ delay: &mut D,
+ ) -> Result<HD44780<FourBitBus<RS, EN, D4, D5, D6, D7>>> {
+ let mut hd = HD44780 {
+ bus: FourBitBus::from_pins(rs, en, d4, d5, d6, d7),
+ entry_mode: EntryMode::default(),
+ display_mode: DisplayMode::default(),
+ };
+
+ hd.init_4bit(delay)?;
+
+ return Ok(hd);
+ }
+}
+
+impl<I2C: i2c::Write> HD44780<I2CBus<I2C>> {
+ /// Create an instance of a `HD44780` from an i2c write peripheral,
+ /// I2C address and a struct implementing the delay trait.
+ /// The `HD44780` is driven through a PCF8574 I2C port expander.
+ /// - The delay instance is used to sleep between commands to
+ /// ensure the `HD44780` has enough time to process commands.
+ /// - The i2c peripheral is used to send data to the `HD44780` and to set
+ /// its register select and enable pins.
+ ///
+ /// This mode operates on an I2C bus, using a PCF8574 I2C to port expander
+ /// The IC connections are described in `I2CBus`
+ ///
+ pub fn new_i2c<D: DelayUs<u16> + DelayMs<u8>>(
+ i2c_bus: I2C,
+ address: u8,
+ delay: &mut D,
+ ) -> Result<HD44780<I2CBus<I2C>>> {
+ let mut hd = HD44780 {
+ bus: I2CBus::new(i2c_bus, address),
+ entry_mode: EntryMode::default(),
+ display_mode: DisplayMode::default(),
+ };
+
+ hd.init_4bit(delay)?;
+
+ return Ok(hd);
+ }
+}
+
+impl<I2C: i2c::Write> HD44780<I2CMCP23008Bus<I2C>> {
+ /// Create an instance of a `HD44780` from an i2c write peripheral,
+ /// I2C address and a struct implementing the delay trait.
+ /// The `HD44780` is driven through a MCP23008 I2C port expander.
+ /// - The delay instance is used to sleep between commands to
+ /// ensure the `HD44780` has enough time to process commands.
+ /// - The i2c peripheral is used to send data to the `HD44780` and to set
+ /// its register select and enable pins.
+ ///
+ /// This mode operates on an I2C bus, using an I2C to parallel port expander based on MCP23008.
+ /// The IC connections are described in `I2CMCP23008Bus`
+ ///
+ pub fn new_i2c_mcp23008<D: DelayUs<u16> + DelayMs<u8>>(
+ i2c_bus: I2C,
+ address: u8,
+ delay: &mut D,
+ ) -> Result<HD44780<I2CMCP23008Bus<I2C>>> {
+ let mut hd = HD44780 {
+ bus: I2CMCP23008Bus::new(i2c_bus, address)?,
+ entry_mode: EntryMode::default(),
+ display_mode: DisplayMode::default(),
+ };
+
+ hd.init_4bit(delay)?;
+
+ return Ok(hd);
+ }
+}
+
+impl<B> HD44780<B>
+where
+ B: DataBus,
+{
+ /// Unshifts the display and sets the cursor position to 0
+ ///
+ /// ```rust,ignore
+ /// lcd.reset();
+ /// ```
+ pub fn reset<D: DelayUs<u16> + DelayMs<u8>>(&mut self, delay: &mut D) -> Result<()> {
+ self.write_command(0b0000_0010, delay)?;
+
+ Ok(())
+ }
+
+ /// Set if the display should be on, if the cursor should be
+ /// visible, and if the cursor should blink
+ ///
+ /// Note: This is equivilent to calling all of the other relavent
+ /// methods however this operation does it all in one go to the `HD44780`
+ pub fn set_display_mode<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ display_mode: DisplayMode,
+ delay: &mut D,
+ ) -> Result<()> {
+ self.display_mode = display_mode;
+
+ let cmd_byte = self.display_mode.as_byte();
+
+ self.write_command(cmd_byte, delay)?;
+
+ Ok(())
+ }
+
+ /// Clear the entire display
+ ///
+ /// ```rust,ignore
+ /// lcd.clear();
+ /// ```
+ pub fn clear<D: DelayUs<u16> + DelayMs<u8>>(&mut self, delay: &mut D) -> Result<()> {
+ self.write_command(0b0000_0001, delay)?;
+
+ Ok(())
+ }
+
+ /// If enabled, automatically scroll the display when a new
+ /// character is written to the display
+ ///
+ /// ```rust,ignore
+ /// lcd.set_autoscroll(true);
+ /// ```
+ pub fn set_autoscroll<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ enabled: bool,
+ delay: &mut D,
+ ) -> Result<()> {
+ self.entry_mode.shift_mode = enabled.into();
+
+ let cmd = self.entry_mode.as_byte();
+
+ self.write_command(cmd, delay)?;
+
+ Ok(())
+ }
+
+ /// Set if the cursor should be visible
+ pub fn set_cursor_visibility<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ visibility: Cursor,
+ delay: &mut D,
+ ) -> Result<()> {
+ self.display_mode.cursor_visibility = visibility;
+
+ let cmd = self.display_mode.as_byte();
+
+ self.write_command(cmd, delay)?;
+
+ Ok(())
+ }
+
+ /// Set if the characters on the display should be visible
+ pub fn set_display<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ display: Display,
+ delay: &mut D,
+ ) -> Result<()> {
+ self.display_mode.display = display;
+
+ let cmd = self.display_mode.as_byte();
+
+ self.write_command(cmd, delay)?;
+
+ Ok(())
+ }
+
+ /// Set if the cursor should blink
+ pub fn set_cursor_blink<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ blink: CursorBlink,
+ delay: &mut D,
+ ) -> Result<()> {
+ self.display_mode.cursor_blink = blink;
+
+ let cmd = self.display_mode.as_byte();
+
+ self.write_command(cmd, delay)?;
+
+ Ok(())
+ }
+
+ /// Set which way the cursor will move when a new character is written
+ ///
+ /// ```rust,ignore
+ /// // Move right (Default) when a new character is written
+ /// lcd.set_cursor_mode(CursorMode::Right)
+ ///
+ /// // Move left when a new character is written
+ /// lcd.set_cursor_mode(CursorMode::Left)
+ /// ```
+ pub fn set_cursor_mode<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ mode: CursorMode,
+ delay: &mut D,
+ ) -> Result<()> {
+ self.entry_mode.cursor_mode = mode;
+
+ let cmd = self.entry_mode.as_byte();
+
+ self.write_command(cmd, delay)?;
+
+ Ok(())
+ }
+
+ /// Set the cursor position
+ ///
+ /// ```rust,ignore
+ /// // Move to line 2
+ /// lcd.set_cursor_pos(40)
+ /// ```
+ pub fn set_cursor_pos<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ position: u8,
+ delay: &mut D,
+ ) -> Result<()> {
+ let lower_7_bits = 0b0111_1111 & position;
+
+ self.write_command(0b1000_0000 | lower_7_bits, delay)?;
+
+ Ok(())
+ }
+
+ /// Shift just the cursor to the left or the right
+ ///
+ /// ```rust,ignore
+ /// lcd.shift_cursor(Direction::Left);
+ /// lcd.shift_cursor(Direction::Right);
+ /// ```
+ pub fn shift_cursor<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ dir: Direction,
+ delay: &mut D,
+ ) -> Result<()> {
+ let bits = match dir {
+ Direction::Left => 0b0000_0000,
+ Direction::Right => 0b0000_0100,
+ };
+
+ self.write_command(0b0001_0000 | bits | bits, delay)?;
+
+ Ok(())
+ }
+
+ /// Shift the entire display to the left or the right
+ ///
+ /// ```rust,ignore
+ /// lcd.shift_display(Direction::Left);
+ /// lcd.shift_display(Direction::Right);
+ /// ```
+ pub fn shift_display<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ dir: Direction,
+ delay: &mut D,
+ ) -> Result<()> {
+ let bits = match dir {
+ Direction::Left => 0b0000_0000,
+ Direction::Right => 0b0000_0100,
+ };
+
+ self.write_command(0b0001_1000 | bits, delay)?;
+
+ Ok(())
+ }
+
+ /// Write a single character to the `HD44780`
+ ///
+ /// ```rust,ignore
+ /// lcd.write_char('A');
+ /// ```
+ pub fn write_char<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ data: char,
+ delay: &mut D,
+ ) -> Result<()> {
+ self.bus.write(data as u8, true, delay)?;
+
+ // Wait for the command to be processed
+ delay.delay_us(100);
+
+ Ok(())
+ }
+
+ fn write_command<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ cmd: u8,
+ delay: &mut D,
+ ) -> Result<()> {
+ self.bus.write(cmd, false, delay)?;
+
+ // Wait for the command to be processed
+ delay.delay_us(100);
+ Ok(())
+ }
+
+ fn init_4bit<D: DelayUs<u16> + DelayMs<u8>>(&mut self, delay: &mut D) -> Result<()> {
+ // Wait for the LCD to wakeup if it was off
+ delay.delay_ms(15u8);
+
+ // Initialize Lcd in 4-bit mode
+ self.bus.write(0x33, false, delay)?;
+
+ // Wait for the command to be processed
+ delay.delay_ms(5u8);
+
+ // Sets 4-bit operation and enables 5x7 mode for chars
+ self.bus.write(0x32, false, delay)?;
+
+ // Wait for the command to be processed
+ delay.delay_us(100);
+
+ self.bus.write(0x28, false, delay)?;
+
+ // Wait for the command to be processed
+ delay.delay_us(100);
+
+ // Clear Display
+ self.bus.write(0x0E, false, delay)?;
+
+ // Wait for the command to be processed
+ delay.delay_us(100);
+
+ // Move the cursor to beginning of first line
+ self.bus.write(0x01, false, delay)?;
+
+ // Wait for the command to be processed
+ delay.delay_us(100);
+
+ // Set entry mode
+ self.bus.write(self.entry_mode.as_byte(), false, delay)?;
+
+ // Wait for the command to be processed
+ delay.delay_us(100);
+
+ self.bus.write(0x80, false, delay)?;
+
+ // Wait for the command to be processed
+ delay.delay_us(100);
+
+ Ok(())
+ }
+
+ // Follow the 8-bit setup procedure as specified in the HD44780 datasheet
+ fn init_8bit<D: DelayUs<u16> + DelayMs<u8>>(&mut self, delay: &mut D) -> Result<()> {
+ // Wait for the LCD to wakeup if it was off
+ delay.delay_ms(15u8);
+
+ // Initialize Lcd in 8-bit mode
+ self.bus.write(0b0011_0000, false, delay)?;
+
+ // Wait for the command to be processed
+ delay.delay_ms(5u8);
+
+ // Sets 8-bit operation and enables 5x7 mode for chars
+ self.bus.write(0b0011_1000, false, delay)?;
+
+ // Wait for the command to be processed
+ delay.delay_us(100);
+
+ self.bus.write(0b0000_1110, false, delay)?;
+
+ // Wait for the command to be processed
+ delay.delay_us(100);
+
+ // Clear Display
+ self.bus.write(0b0000_0001, false, delay)?;
+
+ // Wait for the command to be processed
+ delay.delay_us(100);
+
+ // Move the cursor to beginning of first line
+ self.bus.write(0b000_0111, false, delay)?;
+
+ // Wait for the command to be processed
+ delay.delay_us(100);
+
+ // Set entry mode
+ self.bus.write(self.entry_mode.as_byte(), false, delay)?;
+
+ // Wait for the command to be processed
+ delay.delay_us(100);
+
+ Ok(())
+ }
+
+ pub fn write_str<D: DelayUs<u16> + DelayMs<u8>>(
+ &mut self,
+ string: &str,
+ delay: &mut D,
+ ) -> Result<()> {
+ for c in string.chars() {
+ self.write_char(c, delay)?;
+ }
+ Ok(())
+ }
+
+ // Send a byte to the HD44780 by setting the data on the bus and
+ // also pulsing the enable pin
+ /*fn send_byte(&mut self, data: u8) {
+ // Pulse the enable pin
+ self.set_bus_bits(data);
+ self.pulse_enable();
+ }*/
+
+ // Pulse the enable pin telling the HD44780 that we something for it
+ /*fn pulse_enable(&mut self) {
+ self.en.set_high();
+ self.delay.delay_ms(15u8);
+ self.en.set_low();
+ }*/
+}
+
+//impl<B> Write for HD44780<B>
+//where
+// B: DataBus,
+//{
+// fn write_str(&mut self, string: &str) -> Result {
+// for c in string.chars() {
+// self.write_char(c, delay);
+// }
+// Ok(())
+// }
+//}