Skip to main content

st7305_lcd/cmd/
base.rs

1use crate::{
2    command::{WriteCommand, no_param_command},
3    instr::Instr,
4};
5
6no_param_command!(
7    /// No operation (§8.1.1).
8    Nop => NOP
9);
10no_param_command!(
11    /// Software reset (§8.1.2): registers return to their defaults, GRAM is
12    /// kept. Wait 5 ms before the next command.
13    SoftReset => SWRESET
14);
15no_param_command!(
16    /// Enters sleep mode (§8.1.6). Prefer the `sleep_in` method of the
17    /// drivers, which also waits the required 5 ms.
18    SleepIn => SLPIN
19);
20no_param_command!(
21    /// Leaves sleep mode (§8.1.7). Prefer the `sleep_out` method of the
22    /// drivers, which also waits the required pause.
23    SleepOut => SLPOUT
24);
25no_param_command!(
26    /// Enters partial mode (§8.1.8): the display window becomes 240 duty.
27    PartialOn => PTLON
28);
29no_param_command!(
30    /// Leaves partial mode (§8.1.9): the display window becomes 320 duty.
31    PartialOff => PTLOFF
32);
33no_param_command!(
34    /// Display inversion off (§8.1.10): bit 1 in GRAM is black.
35    DisplayInversionOff => INVOFF
36);
37no_param_command!(
38    /// Display inversion on (§8.1.11): bit 1 in GRAM is white.
39    DisplayInversionOn => INVON
40);
41no_param_command!(
42    /// Turns the display output off (§8.1.12); GRAM is kept.
43    DisplayOff => DISPOFF
44);
45no_param_command!(
46    /// Turns the display output on (§8.1.13).
47    DisplayOn => DISPON
48);
49no_param_command!(
50    /// Turns the tearing effect output off (§8.1.18).
51    TearingEffectLineOff => TEOFF
52);
53no_param_command!(
54    /// High power mode (§8.1.22), the reset default. The frame rate is set by
55    /// [`FrameRateControl`](crate::cmd::panel::FrameRateControl) and
56    /// [`OscSetting`](crate::cmd::power::OscSetting).
57    HighPowerModeOn => HPM
58);
59no_param_command!(
60    /// Low power mode (§8.1.23), 0.25 to 8 Hz set by
61    /// [`FrameRateControl`](crate::cmd::panel::FrameRateControl).
62    LowPowerModeOn => LPM
63);
64
65/// `CASET` (§8.1.14): column window for the following `RAMWR`, inclusive.
66///
67/// One column is 3 bytes of GRAM. The field is 6 bits (0..=63). The usable
68/// range depends on the panel wiring: datasheet v0.2 gives 0x13..=0x28 for a
69/// 264-source layout, the Waveshare 4.2" panel uses 0x12..=0x2A.
70#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub struct ColumnAddressSet {
72    xs: u8,
73    xe: u8,
74}
75
76impl ColumnAddressSet {
77    /// Register default `0x00, 0x00`: column 0 only.
78    pub const DEFAULT: Self = Self { xs: 0, xe: 0 };
79
80    /// Columns `xs..=xe`.
81    ///
82    /// # Panics
83    ///
84    /// If `xs > xe` or `xe > 63` (at compile time in a `const`).
85    pub const fn new(xs: u8, xe: u8) -> Self {
86        assert!(xs <= xe, "X start must be less or equal than X end");
87        assert!(xe < 64, "X end out of range");
88        Self { xs, xe }
89    }
90}
91
92impl Default for ColumnAddressSet {
93    fn default() -> Self {
94        Self::DEFAULT
95    }
96}
97
98impl WriteCommand for ColumnAddressSet {
99    const OPCODE: Instr = Instr::CASET;
100    type Params = [u8; 2];
101    fn params(&self) -> Self::Params {
102        [self.xs, self.xe]
103    }
104}
105
106/// `RASET` (§8.1.15): row (page) window for the following `RAMWR`,
107/// inclusive.
108///
109/// One row is one GRAM page line (2 gate lines). The field is 8 bits. The
110/// usable range depends on the panel wiring: datasheet v0.2 gives
111/// 0x00..=0x9F for a 320-gate layout, the Waveshare 4.2" panel uses
112/// 0x00..=0xC7.
113#[derive(Clone, Copy, Debug, PartialEq, Eq)]
114pub struct RowAddressSet {
115    ys: u8,
116    ye: u8,
117}
118
119impl RowAddressSet {
120    /// Register default `0x00, 0x00`: row 0 only.
121    pub const DEFAULT: Self = Self { ys: 0, ye: 0 };
122
123    /// Rows `ys..=ye`.
124    ///
125    /// # Panics
126    ///
127    /// If `ys > ye` (at compile time in a `const`).
128    pub const fn new(ys: u8, ye: u8) -> Self {
129        assert!(ys <= ye, "Y start must be less or equal than Y end");
130        Self { ys, ye }
131    }
132}
133
134impl Default for RowAddressSet {
135    fn default() -> Self {
136        Self::DEFAULT
137    }
138}
139
140impl WriteCommand for RowAddressSet {
141    const OPCODE: Instr = Instr::RASET;
142    type Params = [u8; 2];
143    fn params(&self) -> Self::Params {
144        [self.ys, self.ye]
145    }
146}
147
148/// `TEON` (§8.1.19): turns the tearing effect output on. During sleep the TE
149/// pin is held low.
150#[derive(Clone, Copy, Debug, PartialEq, Eq)]
151pub struct TearingEffectLineOn {
152    /// D0
153    tem: TearingEffectMode,
154}
155
156impl TearingEffectLineOn {
157    /// Register default: V-blanking only.
158    pub const DEFAULT: Self = Self {
159        tem: TearingEffectMode::VBlanking,
160    };
161
162    /// TE output in the mode `tem`.
163    pub const fn new(tem: TearingEffectMode) -> Self {
164        Self { tem }
165    }
166
167    const fn bytes(&self) -> [u8; 1] {
168        [self.tem as u8]
169    }
170}
171
172impl Default for TearingEffectLineOn {
173    fn default() -> Self {
174        Self::DEFAULT
175    }
176}
177
178impl WriteCommand for TearingEffectLineOn {
179    const OPCODE: Instr = Instr::TEON;
180    type Params = [u8; 1];
181    fn params(&self) -> Self::Params {
182        self.bytes()
183    }
184}
185
186/// `TEM`: what the TE line reports.
187#[derive(Clone, Copy, Debug, PartialEq, Eq)]
188pub enum TearingEffectMode {
189    /// V-blanking only
190    VBlanking = 0,
191    /// V-blanking and H-blanking
192    Both = 1,
193}
194
195const _: () = assert!(matches!(TearingEffectLineOn::DEFAULT.bytes(), [0x00]));
196
197/// `MADCTL` (§8.1.20): scan direction of the frame memory for reads and
198/// writes.
199///
200/// The data order bit `DO` (D3) is not a separate setting: the datasheet
201/// allows it only equal to `MX`, so it follows
202/// [`with_column_order`](Self::with_column_order).
203#[derive(Clone, Copy, Debug, PartialEq, Eq)]
204pub struct MemoryDataAccessControl {
205    /// D7
206    my: PageAddressOrder,
207    /// D6
208    mx: ColumnAddressOrder,
209    /// D5
210    mv: PageOrColumnOrder,
211    // NOTE: D3(DO) is derived from MX: DS allows DO=0 only with MX=0 and DO=1 only with MX=1
212    /// D2
213    gs: GateScanOrder,
214}
215
216impl MemoryDataAccessControl {
217    /// Register default `0x00`: everything top to bottom, left to right,
218    /// column direction.
219    pub const DEFAULT: Self = Self {
220        my: PageAddressOrder::TopToBottom,
221        mx: ColumnAddressOrder::LeftToRight,
222        mv: PageOrColumnOrder::ColumnDirectionMode,
223        gs: GateScanOrder::TopToBottom,
224    };
225
226    /// `MY` (D7): page address order.
227    #[must_use]
228    pub const fn with_page_order(mut self, v: PageAddressOrder) -> Self {
229        self.my = v;
230        self
231    }
232
233    /// `MX` (D6): column address order; also sets the data order `DO` (D3).
234    #[must_use]
235    pub const fn with_column_order(mut self, v: ColumnAddressOrder) -> Self {
236        self.mx = v;
237        self
238    }
239
240    /// `MV` (D5): whether the address advances along columns or pages.
241    #[must_use]
242    pub const fn with_page_or_column_order(mut self, v: PageOrColumnOrder) -> Self {
243        self.mv = v;
244        self
245    }
246
247    /// `GS` (D2): gate scan (refresh) order.
248    #[must_use]
249    pub const fn with_gate_scan_order(mut self, v: GateScanOrder) -> Self {
250        self.gs = v;
251        self
252    }
253}
254
255impl Default for MemoryDataAccessControl {
256    fn default() -> Self {
257        Self::DEFAULT
258    }
259}
260
261impl WriteCommand for MemoryDataAccessControl {
262    const OPCODE: Instr = Instr::MADCTL;
263    type Params = [u8; 1];
264    fn params(&self) -> Self::Params {
265        let byte = (self.my as u8) << 7
266            | (self.mx as u8) << 6
267            | (self.mv as u8) << 5
268            | (self.mx as u8) << 3
269            | (self.gs as u8) << 2;
270        [byte]
271    }
272}
273
274/// `MY`: page address order.
275#[derive(Clone, Copy, Debug, PartialEq, Eq)]
276pub enum PageAddressOrder {
277    /// Top to bottom
278    TopToBottom = 0,
279    /// Bottom to top
280    BottomToTop = 1,
281}
282
283/// `MX`: column address order.
284#[derive(Clone, Copy, Debug, PartialEq, Eq)]
285pub enum ColumnAddressOrder {
286    /// Left to right
287    LeftToRight = 0,
288    /// Right to left
289    RightToLeft = 1,
290}
291
292/// `MV`: direction in which the address advances.
293#[derive(Clone, Copy, Debug, PartialEq, Eq)]
294pub enum PageOrColumnOrder {
295    /// Along the columns, then to the next page
296    ColumnDirectionMode = 0,
297    /// Along the pages, then to the next column
298    PageDirectionMode = 1,
299}
300
301/// `GS`: order in which the gates refresh the panel.
302#[derive(Clone, Copy, Debug, PartialEq, Eq)]
303pub enum GateScanOrder {
304    /// Top to bottom
305    TopToBottom = 0,
306    /// Bottom to top
307    BottomToTop = 1,
308}
309
310/// `VSCSAD` (§8.1.21): the frame memory line shown as the first display
311/// line (vertical scrolling).
312#[derive(Clone, Copy, Debug, PartialEq, Eq)]
313pub struct VerticalScrollStartAddressOfRam {
314    vsp: u16, // 9-bit vertical scroll shift value (first line)
315}
316
317impl VerticalScrollStartAddressOfRam {
318    /// Register default `0x00, 0x00`: line 0.
319    pub const DEFAULT: Self = Self { vsp: 0 };
320
321    /// Start line `vsp` (9 bits).
322    ///
323    /// # Panics
324    ///
325    /// If `vsp > 511` (at compile time in a `const`).
326    pub const fn new(vsp: u16) -> Self {
327        assert!(vsp >> 9 == 0, "VSP out of range");
328        Self { vsp }
329    }
330}
331
332impl Default for VerticalScrollStartAddressOfRam {
333    fn default() -> Self {
334        Self::DEFAULT
335    }
336}
337
338impl WriteCommand for VerticalScrollStartAddressOfRam {
339    const OPCODE: Instr = Instr::VSCSAD;
340    type Params = [u8; 2];
341    fn params(&self) -> Self::Params {
342        [(self.vsp >> 8) as u8, self.vsp as u8]
343    }
344}
345
346/// `DTFORM` (§8.1.24): format of the pixel data written to GRAM.
347#[derive(Clone, Copy, Debug, PartialEq, Eq)]
348pub struct DataFormatSelect {
349    xde: DataUpDownSwitch,
350    bps: BytesPerPixelSelect,
351}
352
353impl DataFormatSelect {
354    /// Register default `0x00`.
355    pub const DEFAULT: Self = Self {
356        xde: DataUpDownSwitch::On,
357        bps: BytesPerPixelSelect::FourWriteOps,
358    };
359
360    /// `XDE` (D4): data up/down switch.
361    #[must_use]
362    pub const fn with_data_up_down_switch(mut self, p: DataUpDownSwitch) -> Self {
363        self.xde = p;
364        self
365    }
366
367    /// `BPS` (D0): number of write operations per 24 bits of data.
368    #[must_use]
369    pub const fn with_bytes_per_pixel(mut self, p: BytesPerPixelSelect) -> Self {
370        self.bps = p;
371        self
372    }
373
374    const fn bytes(&self) -> [u8; 1] {
375        [(self.xde as u8) << 4 | self.bps as u8]
376    }
377}
378
379impl Default for DataFormatSelect {
380    fn default() -> Self {
381        Self::DEFAULT
382    }
383}
384
385impl WriteCommand for DataFormatSelect {
386    const OPCODE: Instr = Instr::DTFORM;
387    type Params = [u8; 1];
388    fn params(&self) -> Self::Params {
389        self.bytes()
390    }
391}
392
393/// `XDE`: data up/down switch.
394#[derive(Clone, Copy, Debug, PartialEq, Eq)]
395pub enum DataUpDownSwitch {
396    /// Switch on (to be used with `MY` = 1)
397    On = 0,
398    /// Switch off
399    Off = 1,
400}
401
402/// `BPS`: how 24 bits of pixel data are written.
403#[derive(Clone, Copy, Debug, PartialEq, Eq)]
404pub enum BytesPerPixelSelect {
405    /// 4 write operations per 24 bits
406    FourWriteOps = 0,
407    /// 3 write operations per 24 bits (used by the Waveshare preset)
408    ThreeWriteOps = 1,
409}
410
411const _: () = assert!(matches!(DataFormatSelect::DEFAULT.bytes(), [0x00]));
412
413/// `TESCAN` (§8.1.27): turns the TE output on when the panel reaches line
414/// `N` (V-blanking only). `N` = 0 is the same as `TEON` with
415/// [`TearingEffectMode::VBlanking`].
416#[derive(Clone, Copy, Debug, PartialEq, Eq)]
417pub struct SetTearScanline {
418    n: u16, // 9-bit turn on TE pin on this line
419}
420
421impl SetTearScanline {
422    /// Register default `0x00, 0x00`: line 0.
423    pub const DEFAULT: Self = Self { n: 0 };
424
425    /// TE on line `n` (9 bits).
426    ///
427    /// # Panics
428    ///
429    /// If `n > 511` (at compile time in a `const`).
430    pub const fn new(n: u16) -> Self {
431        assert!(n >> 9 == 0, "N out of range");
432        Self { n }
433    }
434}
435
436impl Default for SetTearScanline {
437    fn default() -> Self {
438        Self::DEFAULT
439    }
440}
441
442impl WriteCommand for SetTearScanline {
443    const OPCODE: Instr = Instr::TESCAN;
444    type Params = [u8; 2];
445    fn params(&self) -> Self::Params {
446        [(self.n >> 8) as u8, self.n as u8]
447    }
448}