diff options
Diffstat (limited to 'sw/deps/hd44780-driver/src')
-rw-r--r-- | sw/deps/hd44780-driver/src/bus/eightbit.rs | 171 | ||||
-rw-r--r-- | sw/deps/hd44780-driver/src/bus/fourbit.rs | 144 | ||||
-rw-r--r-- | sw/deps/hd44780-driver/src/bus/i2c.rs | 71 | ||||
-rw-r--r-- | sw/deps/hd44780-driver/src/bus/i2c_mcp23008.rs | 102 | ||||
-rw-r--r-- | sw/deps/hd44780-driver/src/bus/mod.rs | 25 | ||||
-rw-r--r-- | sw/deps/hd44780-driver/src/display_mode.rs | 95 | ||||
-rw-r--r-- | sw/deps/hd44780-driver/src/entry_mode.rs | 97 | ||||
-rw-r--r-- | sw/deps/hd44780-driver/src/error.rs | 3 | ||||
-rw-r--r-- | sw/deps/hd44780-driver/src/lib.rs | 552 |
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(()) +// } +//} |