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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
use crate::{
Error, InitStep, PowerMode,
cmd::base::{
ColumnAddressSet, DisplayInversionOff, DisplayInversionOn, DisplayOff, DisplayOn,
HighPowerModeOn, LowPowerModeOn, RowAddressSet, SleepIn, SleepOut, TearingEffectLineOff,
TearingEffectLineOn, TearingEffectMode,
},
command::WriteCommand,
instr::Instr,
};
use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiDevice};
/// Blocking driver of the ST7305 on the SPI device `SPI`, the data/command pin `DC` and the reset
/// pin `RST`. `DC` and `RST` must share one pin error type.
///
/// Create it with [`Self::new`] and initialize the panel with [`Self::init_with`].
#[derive(Debug)]
pub struct St7305<SPI, DC, RST> {
spi: SPI,
dc: DC,
rst: RST,
}
impl<SPI: SpiDevice, DC: OutputPin<Error = P>, RST: OutputPin<Error = P>, P> St7305<SPI, DC, RST> {
/// Creates the driver. Does no I/O: the pins and the bus must already be configured.
pub fn new(spi: SPI, dc: DC, rst: RST) -> Self {
Self { spi, dc, rst }
}
/// Returns the SPI device and the pins. The LCD panel is left in its
/// current state.
pub fn release(self) -> (SPI, DC, RST) {
(self.spi, self.dc, self.rst)
}
/// Hardware reset: `RST` low for 2 ms, then high and a 120 ms wait for the panel.
///
/// Does not use the SPI device, so the only possible error is [`Error::Pin`].
pub fn reset(&mut self, delay: &mut impl DelayNs) -> Result<(), Error<SPI::Error, P>> {
self.rst.set_low().map_err(Error::Pin)?;
delay.delay_ms(2); // >= 1 ms
self.rst.set_high().map_err(Error::Pin)?;
delay.delay_ms(120); // ready after < 120
Ok(())
}
/// Turns the display output on (`DISPON`). Does not leave sleep mode.
pub fn display_on(&mut self) -> Result<(), Error<SPI::Error, P>> {
self.write_command(&DisplayOn)
}
/// Turns the display output off (`DISPOFF`). GRAM content is kept.
pub fn display_off(&mut self) -> Result<(), Error<SPI::Error, P>> {
self.write_command(&DisplayOff)
}
/// Switches display inversion (`INVON` / `INVOFF`).
/// With inversion on, bit 1 is white; with inversion off, bit 1 is black.
pub fn set_inversion(&mut self, on: bool) -> Result<(), Error<SPI::Error, P>> {
if on {
self.write_command(&DisplayInversionOn)
} else {
self.write_command(&DisplayInversionOff)
}
}
/// Selects the power mode (`HPM` / `LPM`). The frame rate of each mode is
/// set with `FrameRateControl`.
pub fn set_power_mode(&mut self, mode: PowerMode) -> Result<(), Error<SPI::Error, P>> {
match mode {
PowerMode::High => self.write_command(&HighPowerModeOn),
PowerMode::Low => self.write_command(&LowPowerModeOn),
}
}
/// Configures the tearing effect output line (TE): `Some(mode)` sends
/// `TEON` with that mode, `None` sends `TEOFF`. The driver does not own
/// the TE pin, wait for it yourself.
pub fn set_tearing_effect(
&mut self,
mode: Option<TearingEffectMode>,
) -> Result<(), Error<SPI::Error, P>> {
match mode {
Some(mode) => self.write_command(&TearingEffectLineOn::new(mode)),
None => self.write_command(&TearingEffectLineOff),
}
}
/// Sets the GRAM window for the next `RAMWR` (`CASET` + `RASET`), in
/// controller units: a column is 3 bytes of GRAM, a row is one GRAM
/// page line. The following frame data must contain exactly `columns *
/// 3 * rows` bytes, and the window stays set until the next call. The
/// coordinate ranges are validated by the command constructors (they
/// panic).
pub fn set_address_window(
&mut self,
columns: ColumnAddressSet,
rows: RowAddressSet,
) -> Result<(), Error<SPI::Error, P>> {
self.write_command(&columns)?;
self.write_command(&rows)
}
/// Enters sleep mode (`SLPIN`) and waits 5 ms, as the datasheet requires
/// before the next command. The display on/off state and GRAM are kept, so
/// no `display_off` is needed. Leave it with [`Self::sleep_out`].
pub fn sleep_in(&mut self, delay: &mut impl DelayNs) -> Result<(), Error<SPI::Error, P>> {
self.write_command(&SleepIn)?;
delay.delay_ms(5);
Ok(())
}
/// Leaves sleep mode (`SLPOUT`) and waits 100 ms (datasheet: >= 100 ms
/// before the next `SLPIN`). Does not change the display on/off state:
/// if the display was on before [`Self::sleep_in`], it is on again. After
/// `init_with` use [`Self::display_on`] if needed.
pub fn sleep_out(&mut self, delay: &mut impl DelayNs) -> Result<(), Error<SPI::Error, P>> {
self.write_command(&SleepOut)?;
delay.delay_ms(100);
Ok(())
}
fn write_cmd(&mut self, cmd: u8, params: &[u8]) -> Result<(), Error<SPI::Error, P>> {
self.dc.set_low().map_err(Error::Pin)?;
self.spi.write(&[cmd]).map_err(Error::Spi)?;
if !params.is_empty() {
self.dc.set_high().map_err(Error::Pin)?;
self.spi.write(params).map_err(Error::Spi)?;
}
Ok(())
}
/// Sends a typed command (see [`cmd`](crate::cmd)): the opcode with `DC`
/// low, then the parameter bytes, if any, with `DC` high, each in its own
/// SPI transaction.
///
/// The `DC` level after an error is not guaranteed; the next command sets
/// it again.
pub fn write_command<T: WriteCommand>(
&mut self,
command: &T,
) -> Result<(), Error<SPI::Error, P>> {
self.write_cmd(T::OPCODE as u8, command.params().as_ref())
}
/// Writes `data` into the GRAM window `columns` x `rows`:
/// [`Self::set_address_window`], then [`Self::write_ram`]. `data` must
/// contain exactly `columns * 3 * rows` bytes (see
/// [`FrameBuffer::copy_window`](crate::framebuffer::FrameBuffer::copy_window)).
///
/// The window stays set afterwards. Before the next full frame restore the
/// whole GRAM window of the panel with [`Self::set_address_window`], e.g.
/// from [`Window::full`](crate::framebuffer::Window::full).
pub fn write_window(
&mut self,
columns: ColumnAddressSet,
rows: RowAddressSet,
data: &[u8],
) -> Result<(), Error<SPI::Error, P>> {
self.set_address_window(columns, rows)?;
self.write_ram(data)
}
/// Hardware [`reset`](Self::reset), then runs the initialization table
/// `seq` (see [`presets`](crate::presets)) step by step. Stops at the
/// first error.
pub fn init_with(
&mut self,
seq: &[InitStep<'_>],
delay: &mut impl DelayNs,
) -> Result<(), Error<SPI::Error, P>> {
self.reset(delay)?;
for step in seq {
match step {
InitStep::Cmd(opcode, params) => self.write_cmd(*opcode, params)?,
InitStep::DelayMs(ms) => delay.delay_ms(*ms),
}
}
Ok(())
}
/// Sends the `RAMWR` command without data. Send the pixel bytes with
/// [`Self::write_ram_data`] right after it.
///
/// The split form lets a frame go out in several pieces, e.g. from a
/// buffer smaller than the frame. [`Self::write_ram`] does both steps.
pub fn begin_write_ram(&mut self) -> Result<(), Error<SPI::Error, P>> {
self.write_cmd(Instr::RAMWR as u8, &[])
}
/// Sends pixel bytes with `DC` high. Call it only after a successful
/// [`Self::begin_write_ram`] (or another `write_ram_data`); the order is
/// not checked. Empty `data` sends nothing.
pub fn write_ram_data(&mut self, data: &[u8]) -> Result<(), Error<SPI::Error, P>> {
if !data.is_empty() {
self.dc.set_high().map_err(Error::Pin)?;
self.spi.write(data).map_err(Error::Spi)?;
}
Ok(())
}
/// Writes a frame into the GRAM window set by
/// [`Self::set_address_window`] (or by the initialization table): `RAMWR`,
/// then all of `data` in one SPI transaction.
///
/// The `DC` level after an error is not guaranteed; the next command sets
/// it again.
pub fn write_ram(&mut self, data: &[u8]) -> Result<(), Error<SPI::Error, P>> {
self.begin_write_ram()?;
self.write_ram_data(data)
}
}