ri-esp-display 0.1.1

no_std display drivers for ESP32: HD44780+PCF8574 I2C LCD and ILI9341 bit-bang SPI TFT
Documentation
use embedded_hal::i2c::I2c;

const LCD_EN: u8 = 0b0100_0000;
const LCD_RS: u8 = 0b0001_0000;
const LCD_BL: u8 = 0b1000_0000;

pub struct Lcd20x4<I2C> {
    i2c: I2C,
    addr: u8,
    backlight: bool,
}

impl<I2C, E> Lcd20x4<I2C>
where
    I2C: I2c<Error = E>,
{
    pub fn new(i2c: I2C, addr: u8) -> Self {
        Self {
            i2c,
            addr,
            backlight: true,
        }
    }
    pub fn release(self) -> I2C {
        self.i2c
    }
    fn write_byte(&mut self, data: u8) -> Result<(), E> {
        self.i2c.write(self.addr, &[data])
    }
    fn send_nibble(&mut self, nibble: u8, rs: bool) -> Result<(), E> {
        let rs_bit = if rs { LCD_RS } else { 0 };
        let bl_bit = if self.backlight { LCD_BL } else { 0 };
        let data = (nibble & 0x0f) | rs_bit | bl_bit;
        self.write_byte(data | LCD_EN)?;
        for _ in 0..240 {
            core::hint::spin_loop();
        }
        self.write_byte(data & !LCD_EN)
    }
    fn send_byte(&mut self, byte: u8, rs: bool) -> Result<(), E> {
        self.send_nibble(byte >> 4, rs)?;
        self.send_nibble(byte & 0x0f, rs)
    }
    fn command(&mut self, cmd: u8) -> Result<(), E> {
        self.send_byte(cmd, false)
    }
    fn write_char(&mut self, c: char) -> Result<(), E> {
        self.send_byte(c as u8, true)
    }
    pub fn init(&mut self) -> Result<(), E> {
        self.send_nibble(0x03, false)?;
        for _ in 0..10000 {
            core::hint::spin_loop();
        }
        self.send_nibble(0x03, false)?;
        for _ in 0..10000 {
            core::hint::spin_loop();
        }
        self.send_nibble(0x03, false)?;
        for _ in 0..10000 {
            core::hint::spin_loop();
        }
        self.send_nibble(0x02, false)?;
        for _ in 0..10000 {
            core::hint::spin_loop();
        }
        self.command(0x28)?;
        self.command(0x08)?;
        self.clear()?;
        self.command(0x06)?;
        self.command(0x0c)
    }
    pub fn clear(&mut self) -> Result<(), E> {
        self.command(0x01)
    }
    pub fn set_cursor(&mut self, row: u8, col: u8) -> Result<(), E> {
        let base = match row {
            0 => 0x00,
            1 => 0x40,
            2 => 0x14,
            3 => 0x54,
            _ => 0x00,
        };
        self.command(0x80 | (base + col))
    }
    pub fn write_str(&mut self, s: &str) -> Result<(), E> {
        for c in s.chars() {
            self.write_char(c)?;
        }
        Ok(())
    }
    pub fn write_at(&mut self, row: u8, col: u8, s: &str) -> Result<(), E> {
        self.set_cursor(row, col)?;
        self.write_str(s)
    }
    pub fn set_backlight(&mut self, on: bool) {
        self.backlight = on;
    }
}