Skip to main content

st7305_lcd/cmd/
panel.rs

1use crate::{command::WriteCommand, instr::Instr};
2
3/// `GATESET` (§8.2.1): number of gate lines of the panel, in steps of 4.
4///
5/// Must be sent before sleep out; do not change it while the display is on.
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub struct GateLineSetting {
8    gl: u8,
9}
10
11impl GateLineSetting {
12    /// `lines` gate lines. The datasheet table ends at 320, the Waveshare
13    /// 4.2" panel uses 400.
14    ///
15    /// # Panics
16    ///
17    /// If `lines` is 0, not a multiple of 4 or above 508 (at compile time in
18    /// a `const`).
19    pub const fn new(lines: u16) -> Self {
20        assert!(lines != 0, "lines must be not zero");
21        assert!(lines.is_multiple_of(4), "lines must be %4");
22        assert!(lines / 4 < 128, "lines out of range");
23        Self {
24            gl: (lines / 4) as u8,
25        }
26    }
27}
28
29impl WriteCommand for GateLineSetting {
30    const OPCODE: Instr = Instr::GATESET;
31    type Params = [u8; 1];
32    fn params(&self) -> Self::Params {
33        [self.gl]
34    }
35}
36
37/// `FSTCOM` (§8.2.2): first output gate number minus one, mapped to GRAM
38/// page 0. Changing it step by step scrolls vertically in 4-line steps.
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub struct FirstGateSetting {
41    fst: u16,
42}
43
44impl FirstGateSetting {
45    /// Register default `0x00, 0x00`: gate 1 shows GRAM page 0.
46    pub const DEFAULT: Self = Self { fst: 0 };
47
48    /// `FST[8:0]` = `fst`; e.g. 7 makes gate 8 show GRAM page 0.
49    ///
50    /// # Panics
51    ///
52    /// If `fst > 511` (at compile time in a `const`).
53    pub const fn new(fst: u16) -> Self {
54        assert!(fst >> 9 == 0, "FST out of range");
55        Self { fst }
56    }
57}
58
59impl Default for FirstGateSetting {
60    fn default() -> Self {
61        Self::DEFAULT
62    }
63}
64
65impl WriteCommand for FirstGateSetting {
66    const OPCODE: Instr = Instr::FSTCOM;
67    type Params = [u8; 2];
68    fn params(&self) -> Self::Params {
69        [(self.fst >> 8) as u8, self.fst as u8]
70    }
71}
72
73/// `FRCTRL` (§8.2.3): frame rates of the high and the low power mode.
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
75pub struct FrameRateControl {
76    hfra: HpmFrameRate,
77    lfra: LpmFrameRate,
78}
79
80impl FrameRateControl {
81    /// Register default `0x12`: full rate in HPM, 1 Hz in LPM.
82    pub const DEFAULT: Self = Self {
83        hfra: HpmFrameRate::Full,
84        lfra: LpmFrameRate::OneHz,
85    };
86
87    /// `HFRA` (D4): frame rate in high power mode.
88    #[must_use]
89    pub const fn with_hpm_frame_rate(mut self, p: HpmFrameRate) -> Self {
90        self.hfra = p;
91        self
92    }
93
94    /// `LFRA[2:0]`: frame rate in low power mode.
95    #[must_use]
96    pub const fn with_lpm_frame_rate(mut self, p: LpmFrameRate) -> Self {
97        self.lfra = p;
98        self
99    }
100
101    const fn bytes(&self) -> [u8; 1] {
102        [(self.hfra as u8) << 4 | (self.lfra as u8)]
103    }
104}
105
106impl Default for FrameRateControl {
107    fn default() -> Self {
108        Self::DEFAULT
109    }
110}
111
112impl WriteCommand for FrameRateControl {
113    const OPCODE: Instr = Instr::FRCTRL;
114    type Params = [u8; 1];
115    fn params(&self) -> Self::Params {
116        self.bytes()
117    }
118}
119
120/// `HFRA`: HPM frame rate, relative to the maximum selected by
121/// [`OscSwitch`](crate::cmd::power::OscSwitch).
122#[derive(Clone, Copy, Debug, PartialEq, Eq)]
123pub enum HpmFrameRate {
124    /// 16 Hz at OSCSW = 111; 25.5 Hz at OSCSW = 000
125    Half = 0,
126    /// 32 Hz at OSCSW = 111; 51 Hz at OSCSW = 000
127    Full = 1,
128}
129
130/// `LFRA[2:0]`: LPM frame rate.
131#[derive(Clone, Copy, Debug, PartialEq, Eq)]
132pub enum LpmFrameRate {
133    /// 0.25 Hz
134    QuarterHz = 0,
135    /// 0.5 Hz
136    HalfHz = 1,
137    /// 1 Hz
138    OneHz = 2,
139    /// 2 Hz
140    TwoHz = 3,
141    /// 4 Hz
142    FourHz = 4,
143    /// 8 Hz
144    EightHz = 5,
145}
146
147const _: () = assert!(matches!(FrameRateControl::DEFAULT.bytes(), [0x12]));
148
149/// `GTUPEQH` (§8.2.4): gate EQ waveform of the update period in HPM, as the
150/// voltage of each of its 16 PWM steps.
151#[derive(Clone, Copy, Debug, PartialEq, Eq)]
152pub struct UpdatePeriodGateEqControlInHpm {
153    geqen: GateEqEnable,
154    gpchpu: [GateEqVoltage; 16],
155}
156
157impl UpdatePeriodGateEqControlInHpm {
158    /// Register default (gate EQ on, the datasheet waveform).
159    pub const DEFAULT: Self = Self {
160        geqen: GateEqEnable::Enable,
161        gpchpu: [
162            GateEqVoltage::Floating,
163            GateEqVoltage::NaVdd,
164            GateEqVoltage::Gnd,
165            GateEqVoltage::AVdd,
166            GateEqVoltage::Vgh,
167            GateEqVoltage::Vgh,
168            GateEqVoltage::Vgh,
169            GateEqVoltage::Vgh,
170            GateEqVoltage::Vgh,
171            GateEqVoltage::Vgh,
172            GateEqVoltage::Vgh,
173            GateEqVoltage::Vgh,
174            GateEqVoltage::Vgh,
175            GateEqVoltage::AVdd,
176            GateEqVoltage::Gnd,
177            GateEqVoltage::NaVdd,
178        ],
179    };
180
181    /// `GEQEN`: gate EQ on or off, for both HPM and LPM.
182    #[must_use]
183    pub const fn with_gate_eq_enable(mut self, v: GateEqEnable) -> Self {
184        self.geqen = v;
185        self
186    }
187
188    /// `GPCHPU0..=15`: voltage of each PWM step.
189    #[must_use]
190    pub const fn with_gate_waveform(mut self, v: [GateEqVoltage; 16]) -> Self {
191        self.gpchpu = v;
192        self
193    }
194
195    const fn bytes(&self) -> [u8; 10] {
196        [
197            0xE5 & !(1 << 6) | (self.geqen as u8) << 6,
198            0xF6,
199            (self.gpchpu[0] as u8) << 4 | (self.gpchpu[1] as u8),
200            (self.gpchpu[2] as u8) << 4 | (self.gpchpu[3] as u8),
201            (self.gpchpu[4] as u8) << 4 | (self.gpchpu[5] as u8),
202            (self.gpchpu[6] as u8) << 4 | (self.gpchpu[7] as u8),
203            (self.gpchpu[8] as u8) << 4 | (self.gpchpu[9] as u8),
204            (self.gpchpu[10] as u8) << 4 | (self.gpchpu[11] as u8),
205            (self.gpchpu[12] as u8) << 4 | (self.gpchpu[13] as u8),
206            (self.gpchpu[14] as u8) << 4 | (self.gpchpu[15] as u8),
207        ]
208    }
209}
210
211impl Default for UpdatePeriodGateEqControlInHpm {
212    fn default() -> Self {
213        Self::DEFAULT
214    }
215}
216
217impl WriteCommand for UpdatePeriodGateEqControlInHpm {
218    const OPCODE: Instr = Instr::GTUPEQH;
219    type Params = [u8; 10];
220    fn params(&self) -> Self::Params {
221        self.bytes()
222    }
223}
224
225/// `GEQEN`: gate EQ on or off.
226#[derive(Clone, Copy, Debug, PartialEq, Eq)]
227pub enum GateEqEnable {
228    /// Off
229    Disable = 0,
230    /// On
231    Enable = 1,
232}
233
234/// Gate voltage of one PWM step of the gate EQ waveform.
235#[derive(Clone, Copy, Debug, PartialEq, Eq)]
236pub enum GateEqVoltage {
237    /// Floating
238    Floating = 0b000,
239    /// VGL
240    Vgl = 0b001,
241    /// GND
242    Gnd = 0b100,
243    /// NAVDD
244    NaVdd = 0b101,
245    /// AVDD
246    AVdd = 0b110,
247    /// VGH
248    Vgh = 0b111,
249}
250
251const _: () = assert!(
252    matches!(
253        UpdatePeriodGateEqControlInHpm::DEFAULT.bytes(),
254        [0xE5, 0xF6, 0x05, 0x46, 0x77, 0x77, 0x77, 0x77, 0x76, 0x45]
255    ),
256    "GTUPEQH DEFAULT must match datasheet's default"
257);
258
259/// `GTUPEQL` (§8.2.5): gate EQ waveform of the update period in LPM, as the
260/// voltage of each of its 16 PWM steps. Gate EQ itself is switched by
261/// [`UpdatePeriodGateEqControlInHpm::with_gate_eq_enable`].
262#[derive(Clone, Copy, Debug, PartialEq, Eq)]
263pub struct UpdatePeriodGateEqControlInLpm {
264    gpclpu: [GateEqVoltage; 16],
265}
266
267impl UpdatePeriodGateEqControlInLpm {
268    /// Register default (the datasheet waveform).
269    pub const DEFAULT: Self = Self {
270        gpclpu: [
271            GateEqVoltage::Floating,
272            GateEqVoltage::NaVdd,
273            GateEqVoltage::Gnd,
274            GateEqVoltage::AVdd,
275            GateEqVoltage::Vgh,
276            GateEqVoltage::Vgh,
277            GateEqVoltage::Vgh,
278            GateEqVoltage::Vgh,
279            GateEqVoltage::Vgh,
280            GateEqVoltage::Vgh,
281            GateEqVoltage::Vgh,
282            GateEqVoltage::Vgh,
283            GateEqVoltage::Vgh,
284            GateEqVoltage::AVdd,
285            GateEqVoltage::Gnd,
286            GateEqVoltage::NaVdd,
287        ],
288    };
289
290    /// `GPCLPU0..=15`: voltage of each PWM step.
291    #[must_use]
292    pub const fn with_gate_waveform(mut self, v: [GateEqVoltage; 16]) -> Self {
293        self.gpclpu = v;
294        self
295    }
296
297    const fn bytes(&self) -> [u8; 8] {
298        [
299            (self.gpclpu[0] as u8) << 4 | (self.gpclpu[1] as u8),
300            (self.gpclpu[2] as u8) << 4 | (self.gpclpu[3] as u8),
301            (self.gpclpu[4] as u8) << 4 | (self.gpclpu[5] as u8),
302            (self.gpclpu[6] as u8) << 4 | (self.gpclpu[7] as u8),
303            (self.gpclpu[8] as u8) << 4 | (self.gpclpu[9] as u8),
304            (self.gpclpu[10] as u8) << 4 | (self.gpclpu[11] as u8),
305            (self.gpclpu[12] as u8) << 4 | (self.gpclpu[13] as u8),
306            (self.gpclpu[14] as u8) << 4 | (self.gpclpu[15] as u8),
307        ]
308    }
309}
310
311impl Default for UpdatePeriodGateEqControlInLpm {
312    fn default() -> Self {
313        Self::DEFAULT
314    }
315}
316
317impl WriteCommand for UpdatePeriodGateEqControlInLpm {
318    const OPCODE: Instr = Instr::GTUPEQL;
319    type Params = [u8; 8];
320    fn params(&self) -> Self::Params {
321        self.bytes()
322    }
323}
324
325const _: () = assert!(
326    matches!(
327        UpdatePeriodGateEqControlInLpm::DEFAULT.bytes(),
328        [0x05, 0x46, 0x77, 0x77, 0x77, 0x77, 0x76, 0x45]
329    ),
330    "GTUPEQL DEFAULT must match datasheet's default"
331);
332
333/// `SOUEQ` (§8.2.6): source EQ on or off.
334#[derive(Clone, Copy, Debug, PartialEq, Eq)]
335pub struct SourceEqEnable {
336    soueq: SourceEq,
337}
338
339impl SourceEqEnable {
340    /// Register default `0x13`: source EQ on.
341    pub const DEFAULT: Self = Self {
342        soueq: SourceEq::Enable,
343    };
344
345    /// `SOUEQ` (D4).
346    #[must_use]
347    pub const fn with_source_eq(mut self, v: SourceEq) -> Self {
348        self.soueq = v;
349        self
350    }
351
352    const fn bytes(&self) -> [u8; 1] {
353        [0x13 & !(1 << 4) | (self.soueq as u8) << 4]
354    }
355}
356
357impl Default for SourceEqEnable {
358    fn default() -> Self {
359        Self::DEFAULT
360    }
361}
362
363impl WriteCommand for SourceEqEnable {
364    const OPCODE: Instr = Instr::SOUEQ;
365    type Params = [u8; 1];
366    fn params(&self) -> Self::Params {
367        self.bytes()
368    }
369}
370
371/// `SOUEQ`: source EQ on or off.
372#[derive(Clone, Copy, Debug, PartialEq, Eq)]
373pub enum SourceEq {
374    /// Off
375    Disable = 0,
376    /// On
377    Enable = 1,
378}
379
380const _: () = assert!(matches!(SourceEqEnable::DEFAULT.bytes(), [0x13]));
381
382/// `PNLSET` (§8.2.7): inversion, gate scan mode and panel layout.
383#[derive(Clone, Copy, Debug, PartialEq, Eq)]
384pub struct PanelSetting {
385    dotinv: ColumnDotInversion,
386    dpscn: GateScanMode,
387    lay: PanelLayoutSelect,
388}
389
390impl PanelSetting {
391    /// Register default `0x0A`: column inversion, frame interval, non
392    /// interlaced.
393    pub const DEFAULT: Self = Self {
394        dotinv: ColumnDotInversion::ColumnInversion,
395        dpscn: GateScanMode::FrameInterval,
396        lay: PanelLayoutSelect::NonInterlace,
397    };
398
399    /// `DOTINV[1:0]` (D6..D5).
400    #[must_use]
401    pub const fn with_column_dot_inversion(mut self, v: ColumnDotInversion) -> Self {
402        self.dotinv = v;
403        self
404    }
405
406    /// `DPSCN[1:0]` (D3..D2).
407    #[must_use]
408    pub const fn with_gate_scan_mode(mut self, v: GateScanMode) -> Self {
409        self.dpscn = v;
410        self
411    }
412
413    /// `LAY[1:0]` (D1..D0).
414    #[must_use]
415    pub const fn with_panel_layout(mut self, v: PanelLayoutSelect) -> Self {
416        self.lay = v;
417        self
418    }
419
420    const fn bytes(&self) -> [u8; 1] {
421        [(self.dotinv as u8) << 5 | (self.dpscn as u8) << 2 | self.lay as u8]
422    }
423}
424
425impl Default for PanelSetting {
426    fn default() -> Self {
427        Self::DEFAULT
428    }
429}
430
431impl WriteCommand for PanelSetting {
432    const OPCODE: Instr = Instr::PNLSET;
433    type Params = [u8; 1];
434    fn params(&self) -> Self::Params {
435        self.bytes()
436    }
437}
438
439/// `DOTINV[1:0]`: polarity inversion scheme.
440#[derive(Clone, Copy, Debug, PartialEq, Eq)]
441pub enum ColumnDotInversion {
442    /// Column inversion
443    ColumnInversion = 0,
444    /// 1-dot inversion
445    OneDotInversion = 1,
446    /// 2-dot inversion
447    TwoDotInversion = 2,
448}
449
450/// `DPSCN[1:0]`: display gate scan mode.
451#[derive(Clone, Copy, Debug, PartialEq, Eq)]
452pub enum GateScanMode {
453    /// Two-line interval
454    TwoLineInterval = 0,
455    /// One-line interval
456    OneLineInterval = 1,
457    /// Frame interval
458    FrameInterval = 2,
459}
460
461/// `LAY[1:0]`: panel layout.
462#[derive(Clone, Copy, Debug, PartialEq, Eq)]
463pub enum PanelLayoutSelect {
464    /// Two-line interlace
465    TwoLineInterlace = 0,
466    /// One-line interlace
467    OneLineInterlace = 1,
468    /// Non-interlaced
469    NonInterlace = 2,
470}
471
472const _: () = assert!(matches!(PanelSetting::DEFAULT.bytes(), [0x0A]));
473
474/// `GAMAMS` (§8.2.8): monochrome or 4 grey levels.
475#[derive(Clone, Copy, Debug, PartialEq, Eq)]
476pub struct GammaModeSetting {
477    gama: GammaMode,
478}
479
480impl GammaModeSetting {
481    /// Register default `0x20`: monochrome.
482    pub const DEFAULT: Self = Self {
483        gama: GammaMode::Mono,
484    };
485
486    /// Gamma mode `gama`.
487    pub const fn new(gama: GammaMode) -> Self {
488        Self { gama }
489    }
490
491    const fn bytes(&self) -> [u8; 1] {
492        [(self.gama as u8) << 5]
493    }
494}
495
496impl Default for GammaModeSetting {
497    fn default() -> Self {
498        Self::DEFAULT
499    }
500}
501
502impl WriteCommand for GammaModeSetting {
503    const OPCODE: Instr = Instr::GAMAMS;
504    type Params = [u8; 1];
505    fn params(&self) -> Self::Params {
506        self.bytes()
507    }
508}
509
510/// `GAMA`: gamma mode.
511#[derive(Clone, Copy, Debug, PartialEq, Eq)]
512pub enum GammaMode {
513    /// 4 grey scales
514    FourGS = 0,
515    /// Monochrome
516    Mono = 1,
517}
518
519const _: () = assert!(matches!(GammaModeSetting::DEFAULT.bytes(), [0x20]));
520
521/// `CLRAM` (§8.2.9): clears GRAM to 0.
522#[derive(Clone, Copy, Debug, PartialEq, Eq)]
523pub struct EnableClearRam {
524    clr: ClearRam,
525}
526
527impl EnableClearRam {
528    /// `CLR` (D7) = `clr`. The datasheet gives no register default, so there is
529    /// no `Default`: sending `ClearRam::Enable` clears the GRAM.
530    pub const fn new(clr: ClearRam) -> Self {
531        Self { clr }
532    }
533
534    const fn bytes(&self) -> [u8; 1] {
535        [0x4F & !(1 << 7) | (self.clr as u8) << 7]
536    }
537}
538
539impl WriteCommand for EnableClearRam {
540    const OPCODE: Instr = Instr::CLRAM;
541    type Params = [u8; 1];
542    fn params(&self) -> Self::Params {
543        self.bytes()
544    }
545}
546
547/// `CLR`: note the inverted encoding, 0 enables the clear.
548#[derive(Clone, Copy, Debug, PartialEq, Eq)]
549pub enum ClearRam {
550    /// Clear GRAM (`CLR` = 0)
551    Enable = 0,
552    /// Do not clear (`CLR` = 1)
553    Disable = 1,
554}
555
556const _: () = assert!(matches!(
557    EnableClearRam::new(ClearRam::Enable).bytes(),
558    [0x4F]
559));
560const _: () = assert!(matches!(
561    EnableClearRam::new(ClearRam::Disable).bytes(),
562    [0xCF]
563));
564
565/// `GTCTRL` (`0x62`): gate timing control. Not described in datasheet v0.2;
566/// the Waveshare preset sends `0x32, 0x03, 0x1F`.
567#[derive(Clone, Copy, Debug, PartialEq, Eq)]
568pub struct GateTimingControl {
569    p1: u8,
570    p2: u8,
571    p3: u8,
572}
573
574impl GateTimingControl {
575    /// The three raw parameter bytes.
576    pub const fn new(p1: u8, p2: u8, p3: u8) -> Self {
577        Self { p1, p2, p3 }
578    }
579}
580
581impl WriteCommand for GateTimingControl {
582    const OPCODE: Instr = Instr::GTCTRL;
583    type Params = [u8; 3];
584    fn params(&self) -> Self::Params {
585        [self.p1, self.p2, self.p3]
586    }
587}