Skip to main content

display_driver_gc9a01/
lib.rs

1#![no_std]
2
3use embedded_hal::digital::OutputPin;
4use embedded_hal_async::delay::DelayNs;
5
6use display_driver::bus::DisplayBus;
7use display_driver::panel::initseq::{sequenced_init, InitStep};
8use display_driver::panel::reset::{LCDResetHandler, LCDResetOption};
9use display_driver::panel::{Orientation, Panel, PanelSetBrightness};
10
11use display_driver::{ColorFormat, DisplayError};
12
13use display_driver_mipidcs as mipidcs;
14use display_driver_mipidcs::{dcs_types::AddressMode, GenericMipidcs};
15
16pub mod consts;
17pub mod spec;
18
19use consts::*;
20pub use spec::Gc9a01Spec;
21
22/// Driver for the GC9A01 display controller.
23pub struct Gc9a01<Spec, RST, B>
24where
25    Spec: Gc9a01Spec,
26    RST: OutputPin,
27    B: DisplayBus,
28{
29    /// Inner generic driver for standard functionality.
30    inner: GenericMipidcs<B, Spec, RST>,
31}
32
33impl<Spec, RST, B> Gc9a01<Spec, RST, B>
34where
35    Spec: Gc9a01Spec,
36    RST: OutputPin,
37    B: DisplayBus,
38{
39    /// Creates a new driver instance.
40    pub fn new(reset_pin: LCDResetOption<RST>) -> Self {
41        Self {
42            inner: GenericMipidcs::new(reset_pin),
43        }
44    }
45
46    delegate::delegate! {
47        to self.inner {
48            pub async fn set_invert_mode(
49                &mut self,
50                bus: &mut B,
51                invert: bool,
52            ) -> Result<(), B::Error>;
53
54            pub async fn set_address_mode(
55                &mut self,
56                bus: &mut B,
57                address_mode: AddressMode,
58                orientation_if_changed: Option<Orientation>,
59            ) -> Result<(), B::Error>;
60
61            pub async fn set_bgr_order(&mut self, bus: &mut B, bgr: bool) -> Result<(), B::Error>;
62        }
63    }
64
65    /// Initialization sequence for GC9A01.
66    const INIT_STEPS: &'static [InitStep<'static>] = &[
67        InitStep::CommandWithParams(INTER_REGISTER_ENABLE_2, &[]), // 0xEF - Use Command 2
68        InitStep::CommandWithParams(0xEB, &[0x14]),
69        InitStep::CommandWithParams(INTER_REGISTER_ENABLE_1, &[]), // 0xFE - Use Command 1
70        InitStep::CommandWithParams(INTER_REGISTER_ENABLE_2, &[]), // 0xEF - Use Command 2, enable inter register
71        // There are so many magic here!!!
72        InitStep::CommandWithParams(0xEB, &[0x14]),
73        InitStep::CommandWithParams(0x84, &[0x40]),
74        InitStep::CommandWithParams(0x85, &[0xFF]),
75        InitStep::CommandWithParams(0x86, &[0xFF]),
76        InitStep::CommandWithParams(0x87, &[0xFF]),
77        InitStep::CommandWithParams(0x88, &[0x0A]),
78        InitStep::CommandWithParams(0x89, &[0x21]),
79        InitStep::CommandWithParams(0x8A, &[0x00]),
80        InitStep::CommandWithParams(0x8B, &[0x80]),
81        InitStep::CommandWithParams(0x8C, &[0x01]),
82        InitStep::CommandWithParams(0x8D, &[0x01]),
83        InitStep::CommandWithParams(0x8E, &[0xFF]),
84        InitStep::CommandWithParams(0x8F, &[0xFF]),
85        // Display Function Control
86        InitStep::CommandWithParams(DISPLAY_FUNCTION_CONTROL, &[0x00, 0x20]), // Scan direction S1-S360 G1-32
87        InitStep::select_cmd(
88            Spec::INVERTED,
89            mipidcs::ENTER_INVERT_MODE,
90            mipidcs::EXIT_INVERT_MODE,
91        ),
92        InitStep::CommandWithParams(
93            mipidcs::SET_ADDRESS_MODE,
94            &[if Spec::BGR {
95                AddressMode::BGR.bits()
96            } else {
97                0u8
98            }],
99        ),
100        // Pixel Format Set
101        InitStep::CommandWithParams(mipidcs::SET_PIXEL_FORMAT, &[0x05]), // 16bit MCU
102        InitStep::CommandWithParams(0x90, &[0x08, 0x08, 0x08, 0x08]),
103        InitStep::CommandWithParams(0xBD, &[0x06]),
104        InitStep::CommandWithParams(0xBC, &[0x00]),
105        InitStep::CommandWithParams(0xFF, &[0x60, 0x01, 0x04]),
106        // Power Control
107        InitStep::CommandWithParams(POWER_CONTROL_2, &[0x13]), // Vreg1a voltage
108        InitStep::CommandWithParams(POWER_CONTROL_3, &[0x13]), // Vreg1b voltage
109        InitStep::CommandWithParams(POWER_CONTROL_4, &[0x22]), // Vreg2a voltage
110        InitStep::CommandWithParams(0xBE, &[0x11]),
111        InitStep::CommandWithParams(0xE1, &[0x10, 0x0E]),
112        InitStep::CommandWithParams(0xDF, &[0x21, 0x0C, 0x02]),
113        // Gamma
114        InitStep::CommandWithParams(SET_GAMMA_1, &[0x45, 0x09, 0x08, 0x08, 0x26, 0x2A]),
115        InitStep::CommandWithParams(SET_GAMMA_2, &[0x43, 0x70, 0x72, 0x36, 0x37, 0x6F]),
116        InitStep::CommandWithParams(SET_GAMMA_3, &[0x45, 0x09, 0x08, 0x08, 0x26, 0x2A]),
117        InitStep::CommandWithParams(SET_GAMMA_4, &[0x43, 0x70, 0x72, 0x36, 0x37, 0x6F]),
118        InitStep::CommandWithParams(0xED, &[0x1B, 0x0B]),
119        InitStep::CommandWithParams(0xAE, &[0x77]),
120        InitStep::CommandWithParams(0xCD, &[0x63]),
121        // TODO: Adafruit says remove this line may solve some problems
122        // InitStep::CommandWithParams(0x70, &[0x07, 0x07, 0x04, 0x0E, 0x0F, 0x09, 0x07, 0x08, 0x03]),
123
124        // Frame Rate
125        InitStep::CommandWithParams(FRAME_RATE_CONTROL, &[0x34]),
126        InitStep::CommandWithParams(
127            0x62,
128            &[
129                0x18, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x18, 0x0F, 0x71, 0xEF, 0x70, 0x70,
130            ],
131        ),
132        InitStep::CommandWithParams(
133            0x63,
134            &[
135                0x18, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x18, 0x13, 0x71, 0xF3, 0x70, 0x70,
136            ],
137        ),
138        InitStep::CommandWithParams(0x64, &[0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07]),
139        InitStep::CommandWithParams(
140            0x66,
141            &[0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00],
142        ),
143        InitStep::CommandWithParams(
144            0x67,
145            &[0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98],
146        ),
147        InitStep::CommandWithParams(0x74, &[0x10, 0x85, 0x80, 0x00, 0x00, 0x4E, 0x00]),
148        InitStep::CommandWithParams(0x98, &[0x3E, 0x07]),
149        InitStep::SingleCommand(mipidcs::SET_TEAR_OFF),
150        InitStep::SingleCommand(mipidcs::ENTER_INVERT_MODE), // Display Inversion ON
151        InitStep::SingleCommand(mipidcs::EXIT_SLEEP_MODE),   // Sleep Out
152        // InitStep::SingleCommand(mipidcs::EXIT_IDLE_MODE), // Idle Mode OFF
153        InitStep::DelayMs(120),
154        InitStep::SingleCommand(mipidcs::SET_DISPLAY_ON),
155        InitStep::DelayMs(20),
156    ];
157}
158
159impl<Spec, RST, B> Panel<B> for Gc9a01<Spec, RST, B>
160where
161    Spec: Gc9a01Spec,
162    RST: OutputPin,
163    B: DisplayBus,
164{
165    const CMD_LEN: usize = 1;
166    const PIXEL_WRITE_CMD: [u8; 4] = [mipidcs::WRITE_MEMORY_START, 0, 0, 0];
167
168    async fn init<D: DelayNs>(&mut self, bus: &mut B, mut delay: D) -> Result<(), B::Error> {
169        // Hardware Reset
170        let mut reseter = LCDResetHandler::new(
171            &mut self.inner.reset_pin,
172            bus,
173            &mut delay,
174            10,
175            120,
176            Some(&[mipidcs::SOFT_RESET]),
177        );
178        reseter.reset().await?;
179
180        // Initialize address mode cache
181        self.inner.address_mode.set(AddressMode::BGR, Spec::BGR);
182
183        // Execute Initialization Sequence
184        // `copied()` only copies the items during iteration; it does not copy the entire sequence
185        sequenced_init(Self::INIT_STEPS.iter().copied(), &mut delay, bus).await
186    }
187
188    delegate::delegate! {
189        to self.inner {
190            fn width(&self) -> u16;
191
192            fn height(&self) -> u16;
193
194            fn size(&self) -> (u16, u16);
195
196            async fn set_window(
197                &mut self,
198                bus: &mut B,
199                x0: u16,
200                y0: u16,
201                x1: u16,
202                y1: u16,
203            ) -> Result<(), DisplayError<B::Error>>;
204
205            async fn set_color_format(
206                &mut self,
207                bus: &mut B,
208                color_format: ColorFormat,
209            ) -> Result<(), DisplayError<B::Error>>;
210
211            async fn set_orientation(
212                &mut self,
213                bus: &mut B,
214                orientation: Orientation,
215            ) -> Result<(), DisplayError<B::Error>>;
216        }
217    }
218}
219
220impl<Spec, RST, B> PanelSetBrightness<B> for Gc9a01<Spec, RST, B>
221where
222    Spec: Gc9a01Spec,
223    RST: OutputPin,
224    B: DisplayBus,
225{
226    async fn set_brightness(
227        &mut self,
228        bus: &mut B,
229        brightness: u8,
230    ) -> Result<(), DisplayError<B::Error>> {
231        bus.write_cmd_with_params(&[WRITE_DISPLAY_BRIGHTNESS], &[brightness])
232            .await
233            .map_err(DisplayError::BusError)
234    }
235}