1#![no_std]
2
3pub mod consts;
4pub mod dcs_types;
5pub mod display_bus;
6
7use core::marker::PhantomData;
8use display_driver::bus::DisplayBus;
9use display_driver::panel::{initseq::InitStep, reset::LCDResetOption, Orientation};
10use embedded_hal::digital::OutputPin;
11
12pub use crate::consts::*;
13pub use crate::dcs_types::*;
14
15pub struct GenericMipidcs<B, S, RST>
21where
22 B: DisplayBus,
23 S: PanelSpec,
24 RST: OutputPin,
25{
26 pub reset_pin: LCDResetOption<RST>,
27 pub address_mode: AddressMode,
29 pub orientation: Orientation,
30 _phantom: PhantomData<(B, S)>,
31}
32
33impl<B, S, RST> GenericMipidcs<B, S, RST>
34where
35 B: DisplayBus,
36 S: PanelSpec,
37 RST: OutputPin,
38{
39 pub fn new(reset_pin: LCDResetOption<RST>) -> Self {
41 Self {
42 reset_pin,
43 address_mode: AddressMode::empty(),
44 orientation: Orientation::Deg0,
45 _phantom: PhantomData,
46 }
47 }
48
49 pub fn get_offset(&self) -> (u16, u16) {
52 match (self.orientation, S::INVERT_TRANSPOSED_OFFSET) {
53 (Orientation::Deg0, _) => (S::PHYSICAL_X_OFFSET, S::PHYSICAL_Y_OFFSET),
54 (Orientation::Deg180, _) => {
55 (S::PHYSICAL_X_OFFSET_ROTATED, S::PHYSICAL_Y_OFFSET_ROTATED)
56 }
57 (Orientation::Deg90, false) | (Orientation::Deg270, true) => {
58 (S::PHYSICAL_Y_OFFSET, S::PHYSICAL_X_OFFSET)
59 }
60 (Orientation::Deg270, false) | (Orientation::Deg90, true) => {
61 (S::PHYSICAL_Y_OFFSET_ROTATED, S::PHYSICAL_X_OFFSET_ROTATED)
62 }
63 }
64 }
65
66 pub async fn soft_reset(&self, bus: &mut B) -> Result<(), B::Error> {
68 bus.write_cmd(&[SOFT_RESET]).await
69 }
70
71 pub async fn enter_sleep_mode(&self, bus: &mut B) -> Result<(), B::Error> {
73 bus.write_cmd(&[ENTER_SLEEP_MODE]).await
74 }
75
76 pub async fn exit_sleep_mode(&self, bus: &mut B) -> Result<(), B::Error> {
78 bus.write_cmd(&[EXIT_SLEEP_MODE]).await
79 }
80
81 pub async fn set_display_off(&self, bus: &mut B) -> Result<(), B::Error> {
83 bus.write_cmd(&[SET_DISPLAY_OFF]).await
84 }
85
86 pub async fn set_display_on(&self, bus: &mut B) -> Result<(), B::Error> {
88 bus.write_cmd(&[SET_DISPLAY_ON]).await
89 }
90
91 pub async fn set_column_address(
93 &self,
94 bus: &mut B,
95 start: u16,
96 end: u16,
97 ) -> Result<(), B::Error> {
98 let params = AddressRange::new_with_offset(start, end, S::PHYSICAL_X_OFFSET);
99 bus.write_cmd_with_params(&[SET_COLUMN_ADDRESS], params.as_bytes())
100 .await
101 }
102
103 pub async fn set_page_address(
105 &self,
106 bus: &mut B,
107 start: u16,
108 end: u16,
109 ) -> Result<(), B::Error> {
110 let params = AddressRange::new_with_offset(start, end, S::PHYSICAL_Y_OFFSET);
111 bus.write_cmd_with_params(&[SET_PAGE_ADDRESS], params.as_bytes())
112 .await
113 }
114
115 pub async fn set_address_window(
116 &self,
117 bus: &mut B,
118 x0: u16,
119 y0: u16,
120 x1: u16,
121 y1: u16,
122 ) -> Result<(), B::Error> {
123 let (x_offset, y_offset) = self.get_offset();
124
125 bus.write_cmd_with_params(
126 &[SET_COLUMN_ADDRESS],
127 AddressRange::new_with_offset(x0, x1, x_offset).as_bytes(),
128 )
129 .await?;
130
131 bus.write_cmd_with_params(
132 &[SET_PAGE_ADDRESS],
133 AddressRange::new_with_offset(y0, y1, y_offset).as_bytes(),
134 )
135 .await
136 }
137
138 pub async fn set_address_mode(
151 &mut self,
152 bus: &mut B,
153 mode: AddressMode,
154 orientation_if_changed: Option<Orientation>,
155 ) -> Result<(), B::Error> {
156 self.address_mode = mode;
157 if let Some(orientation) = orientation_if_changed {
158 self.orientation = orientation;
159 }
160 bus.write_cmd_with_params(&[SET_ADDRESS_MODE], &[mode.bits()])
161 .await
162 }
163
164 pub async fn set_bgr_order(&mut self, bus: &mut B, bgr: bool) -> Result<(), B::Error> {
166 self.address_mode.set(AddressMode::BGR, bgr);
167 bus.write_cmd_with_params(&[SET_ADDRESS_MODE], &[self.address_mode.bits()])
168 .await
169 }
170
171 pub async fn set_pixel_format(&self, bus: &mut B, mode: PixelFormat) -> Result<(), B::Error> {
173 bus.write_cmd_with_params(&[SET_PIXEL_FORMAT], &[mode.0])
174 .await
175 }
176
177 pub async fn set_invert_mode(&self, bus: &mut B, inverted: bool) -> Result<(), B::Error> {
181 match inverted {
182 true => bus.write_cmd(&[ENTER_INVERT_MODE]).await,
183 false => bus.write_cmd(&[EXIT_INVERT_MODE]).await,
184 }
185 }
186
187 const INIT_STEPS: [InitStep<'_>; 6] = [
188 InitStep::SingleCommand(EXIT_SLEEP_MODE),
189 InitStep::DelayMs(120),
190 InitStep::select_cmd(S::INVERTED, ENTER_INVERT_MODE, EXIT_INVERT_MODE),
191 InitStep::CommandWithParams(
192 SET_ADDRESS_MODE,
193 &[if S::BGR { AddressMode::BGR.bits() } else { 0u8 }],
194 ),
195 InitStep::SingleCommand(SET_DISPLAY_ON),
197 InitStep::DelayMs(20),
198 ];
199}
200
201pub trait PanelSpec {
203 const PHYSICAL_WIDTH: u16;
205 const PHYSICAL_HEIGHT: u16;
207 const PHYSICAL_X_OFFSET: u16 = 0;
209 const PHYSICAL_Y_OFFSET: u16 = 0;
211
212 const PHYSICAL_X_OFFSET_ROTATED: u16 = Self::PHYSICAL_X_OFFSET;
216
217 const PHYSICAL_Y_OFFSET_ROTATED: u16 = Self::PHYSICAL_Y_OFFSET;
221
222 const INVERT_TRANSPOSED_OFFSET: bool = false;
239
240 const INVERTED: bool = false;
242
243 const BGR: bool = false;
245}