1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#![no_std]
//! Generic SPI interface for display drivers
mod command;
mod error;
mod mode;

use command::{Booster, Command};
use error::Error;
use mode::{BasicMode, BufferedGraphicsMode};

use display_interface::{DataFormat::U8, DisplayError, WriteOnlyDataCommand};
use embedded_hal::{blocking::delay::DelayMs, digital::v2::OutputPin};

/// IST7920 LCD display driver.
#[derive(Copy, Clone, Debug)]
pub struct Ist7920<DI, MODE> {
    interface: DI,
    mode: MODE,
}

impl<DI> Ist7920<DI, BasicMode>
where
    DI: WriteOnlyDataCommand,
{
    /// Create a basic IST7920 interface
    pub fn new(interface: DI) -> Self {
        Self {
            interface,
            mode: BasicMode,
        }
    }
}

impl<DI, MODE> Ist7920<DI, MODE>
where
    DI: WriteOnlyDataCommand,
{
    /// Convert the display into another interface mode.
    fn into_mode<MODE2>(self, mode: MODE2) -> Ist7920<DI, MODE2> {
        Ist7920 {
            interface: self.interface,
            mode,
        }
    }

    /// Convert the display into a buffered graphics mode, supporting
    /// [embedded-graphics](https://crates.io/crates/embedded-graphics).
    ///
    /// See [BufferedGraphicsMode] for more information.
    pub fn into_buffered_graphics_mode(self) -> Ist7920<DI, BufferedGraphicsMode> {
        self.into_mode(BufferedGraphicsMode::new())
    }

    /// Initialise the display in one of the available addressing modes.
    /// TODO: Add address setup
    pub fn init<DELAY>(&mut self, delay: &mut DELAY) -> Result<(), DisplayError>
    where
        DELAY: DelayMs<u8>,
    {
        Command::SWReset.send(&mut self.interface)?;
        delay.delay_ms(50);
        Command::DisplayOn(false).send(&mut self.interface)?;
        Command::Duty(128).send(&mut self.interface)?;
        Command::Bias(16).send(&mut self.interface)?;
        Command::VoltageClock(0x3f).send(&mut self.interface)?;
        Command::PowerControl(0x20).send(&mut self.interface)?;
        delay.delay_ms(100);
        Command::PowerControl(0x2c).send(&mut self.interface)?;
        delay.delay_ms(100);
        Command::Booster(Booster::VddX3).send(&mut self.interface)?;
        delay.delay_ms(100);
        Command::PowerControl(0x2f).send(&mut self.interface)?;
        delay.delay_ms(200);
        Command::DisplayControl(false, true, false, false).send(&mut self.interface)?;

        Command::AYWindow(0x0, 0xf).send(&mut self.interface)?;
        Command::AXWindow(0x0, 0x7f).send(&mut self.interface)?;

        Command::StartLine(64).send(&mut self.interface)?;
        Command::AYAddress(0).send(&mut self.interface)?;
        Command::AXAddress(0).send(&mut self.interface)?;

        Command::Contrast(110).send(&mut self.interface)?;

        Command::DisplayOn(true).send(&mut self.interface)?;

        Ok(())
    }

    /// Send a raw buffer to the display.
    pub fn draw(&mut self, buffer: &[u8]) -> Result<(), DisplayError> {
        self.interface.send_data(U8(&buffer))
    }

    /// Turn the display on or off. The display can be drawn to and retains all
    /// of its memory even while off.
    pub fn set_display_on(&mut self, on: bool) -> Result<(), DisplayError> {
        Command::DisplayOn(on).send(&mut self.interface)
    }

    /// Set the position in the framebuffer of the display limiting where any sent data should be
    /// drawn. This method can be used for changing the affected area on the screen as well
    /// as (re-)setting the start point of the next `draw` call.
    pub fn set_draw_area(&mut self, start: (u8, u8), end: (u8, u8)) -> Result<(), DisplayError> {
        Command::AYWindow(start.0 / 8, end.0 / 8).send(&mut self.interface)?;

        Command::AXWindow(start.0, end.0).send(&mut self.interface)?;

        Command::AXAddress(start.1).send(&mut self.interface)?;
        Command::AYAddress(start.0 / 8).send(&mut self.interface)?;

        Ok(())
    }

    fn flush_buffer_chunks(interface: &mut DI, buffer: &[u8]) -> Result<(), DisplayError> {
        interface.send_data(U8(&buffer))
    }

    /// Reset the display.
    pub fn reset<RST, DELAY, PinE>(
        &mut self,
        rst: &mut RST,
        delay: &mut DELAY,
    ) -> Result<(), Error<(), PinE>>
    where
        RST: OutputPin<Error = PinE>,
        DELAY: DelayMs<u8>,
    {
        inner_reset(rst, delay)
    }
}

fn inner_reset<RST, DELAY, PinE>(rst: &mut RST, delay: &mut DELAY) -> Result<(), Error<(), PinE>>
where
    RST: OutputPin<Error = PinE>,
    DELAY: DelayMs<u8>,
{
    rst.set_high().map_err(Error::Pin)?;
    delay.delay_ms(1);
    rst.set_low().map_err(Error::Pin)?;
    delay.delay_ms(10);
    rst.set_high().map_err(Error::Pin)?;
    delay.delay_ms(20);

    Ok(())
}