1#![cfg_attr(not(test), no_std)]
2mod regs;
10
11pub use regs::*;
12
13use embedded_display_controller::dsi::{DsiHostCtrlIo, DsiReadCommand, DsiWriteCommand};
14use embedded_hal::delay::DelayNs;
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub enum Error {
18 DsiRead,
19 DsiWrite,
20 ProbeMismatch(u8),
21 InvalidDimensions,
22}
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub enum Mode {
26 Portrait,
28 Landscape,
30}
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub enum ColorFormat {
34 Rgb565,
36 Rgb888,
38}
39
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45pub struct Nt35510Config {
46 pub mode: Mode,
47 pub color_format: ColorFormat,
48 pub cols: u16,
50 pub rows: u16,
52}
53
54impl Default for Nt35510Config {
55 fn default() -> Self {
56 Self {
57 mode: Mode::Portrait,
58 color_format: ColorFormat::Rgb565,
59 cols: 480,
60 rows: 800,
61 }
62 }
63}
64
65pub struct Nt35510 {
66 initialized: bool,
67}
68
69impl Default for Nt35510 {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl Nt35510 {
76 pub const fn new() -> Self {
77 Self { initialized: false }
78 }
79
80 pub fn probe<D: DelayNs>(
86 &mut self,
87 dsi_host: &mut impl DsiHostCtrlIo,
88 _delay: &mut D,
89 ) -> Result<(), Error> {
90 match self.read_id(dsi_host, NT35510_CMD_RDID2) {
91 Ok(id) if id == NT35510_ID2_EXPECTED => return Ok(()),
92 Ok(id) => return Err(Error::ProbeMismatch(id)),
93 Err(_) => {}
94 }
95
96 match self.read_id(dsi_host, NT35510_CMD_RDID1) {
97 Ok(id) if id == NT35510_ID1_EXPECTED => Ok(()),
98 Ok(id) => Err(Error::ProbeMismatch(id)),
99 Err(_) => Err(Error::DsiRead),
100 }
101 }
102
103 pub fn id_matches(&mut self, dsi_host: &mut impl DsiHostCtrlIo) -> Result<bool, Error> {
106 if let Ok(id) = self.read_id(dsi_host, NT35510_CMD_RDID2) {
107 return Ok(id == NT35510_ID2_EXPECTED);
108 }
109
110 match self.read_id(dsi_host, NT35510_CMD_RDID1) {
111 Ok(id) => Ok(id == NT35510_ID1_EXPECTED),
112 Err(_) => Err(Error::DsiRead),
113 }
114 }
115
116 pub fn init<D: DelayNs>(
118 &mut self,
119 dsi_host: &mut impl DsiHostCtrlIo,
120 delay: &mut D,
121 ) -> Result<(), Error> {
122 let config = Nt35510Config {
123 color_format: ColorFormat::Rgb888,
124 ..Nt35510Config::default()
125 };
126 self.init_with_config(dsi_host, delay, config)
127 }
128
129 pub fn init_rgb565<D: DelayNs>(
131 &mut self,
132 dsi_host: &mut impl DsiHostCtrlIo,
133 delay: &mut D,
134 ) -> Result<(), Error> {
135 self.init_with_config(dsi_host, delay, Nt35510Config::default())
136 }
137
138 pub fn init_with_config<D: DelayNs>(
140 &mut self,
141 dsi_host: &mut impl DsiHostCtrlIo,
142 delay: &mut D,
143 config: Nt35510Config,
144 ) -> Result<(), Error> {
145 if self.initialized {
146 return Ok(());
147 }
148
149 if config.cols == 0 || config.rows == 0 {
150 return Err(Error::InvalidDimensions);
151 }
152
153 self.write_reg(
154 dsi_host,
155 NT35510_CMD_SETEXTC,
156 &[0x55, 0xAA, 0x52, 0x08, 0x01],
157 )?;
158 self.write_reg(dsi_host, NT35510_CMD_B0, &[0x03, 0x03, 0x03])?;
159 self.write_reg(dsi_host, NT35510_CMD_B6, &[0x46, 0x46, 0x46])?;
160 self.write_reg(dsi_host, NT35510_CMD_B1, &[0x03, 0x03, 0x03])?;
161 self.write_reg(dsi_host, NT35510_CMD_B7, &[0x36, 0x36, 0x36])?;
162 self.write_reg(dsi_host, NT35510_CMD_B2, &[0x00, 0x00, 0x02])?;
163 self.write_reg(dsi_host, NT35510_CMD_B8, &[0x26, 0x26, 0x26])?;
164 self.write_reg(dsi_host, NT35510_CMD_BF, &[0x01])?;
165 self.write_reg(dsi_host, NT35510_CMD_B3, &[0x09, 0x09, 0x09])?;
166 self.write_reg(dsi_host, NT35510_CMD_B9, &[0x36, 0x36, 0x36])?;
167 self.write_reg(dsi_host, NT35510_CMD_B5, &[0x08, 0x08, 0x08])?;
168 self.write_reg(dsi_host, NT35510_CMD_BA, &[0x26, 0x26, 0x26])?;
169 self.write_reg(dsi_host, NT35510_CMD_BC, &[0x00, 0x80, 0x00])?;
170 self.write_reg(dsi_host, NT35510_CMD_BD, &[0x00, 0x80, 0x00])?;
171 self.write_reg(dsi_host, NT35510_CMD_BE, &[0x00, 0x50])?;
172
173 self.write_reg(
174 dsi_host,
175 NT35510_CMD_SETEXTC,
176 &[0x55, 0xAA, 0x52, 0x08, 0x00],
177 )?;
178 self.write_reg(dsi_host, NT35510_CMD_B1, &[0xFC, 0x00])?;
179 self.write_reg(dsi_host, NT35510_CMD_B6, &[0x03, 0x03])?;
180 self.write_reg(dsi_host, NT35510_CMD_B5, &[0x50, 0x50])?;
181 self.write_reg(dsi_host, NT35510_CMD_B7, &[0x00, 0x00])?;
182 self.write_reg(dsi_host, NT35510_CMD_B8, &[0x01, 0x02, 0x02, 0x02])?;
183 self.write_reg(dsi_host, NT35510_CMD_BC, &[0x00, 0x00, 0x00])?;
184 self.write_reg(dsi_host, NT35510_CMD_CC, &[0x03, 0x00, 0x00])?;
185 self.write_reg(dsi_host, NT35510_CMD_BA, &[0x01, 0x01])?;
186
187 let colmod = match config.color_format {
188 ColorFormat::Rgb565 => NT35510_COLMOD_RGB565,
189 ColorFormat::Rgb888 => NT35510_COLMOD_RGB888,
190 };
191 let madctl = match config.mode {
192 Mode::Portrait => NT35510_MADCTL_PORTRAIT,
193 Mode::Landscape => NT35510_MADCTL_LANDSCAPE,
194 };
195
196 let last_col = (config.cols - 1).to_be_bytes();
197 let last_row = (config.rows - 1).to_be_bytes();
198 let caset = [0x00, 0x00, last_col[0], last_col[1]];
199 let raset = [0x00, 0x00, last_row[0], last_row[1]];
200
201 delay.delay_us(200_000);
202 self.write_cmd(dsi_host, NT35510_CMD_SLPOUT, 0x00)?;
203 delay.delay_us(120_000);
204 self.write_cmd(dsi_host, NT35510_CMD_COLMOD, colmod)?;
205 self.write_cmd(dsi_host, NT35510_CMD_MADCTL, madctl)?;
206 self.write_reg(dsi_host, NT35510_CMD_CASET, &caset)?;
207 self.write_reg(dsi_host, NT35510_CMD_RASET, &raset)?;
208 self.write_cmd(dsi_host, NT35510_CMD_WRDISBV, 0x7F)?;
209 self.write_cmd(dsi_host, NT35510_CMD_WRCTRLD, 0x2C)?;
210 self.write_cmd(dsi_host, NT35510_CMD_WRCABC, 0x00)?;
211 delay.delay_us(10_000);
212 self.write_cmd(dsi_host, NT35510_CMD_DISPON, 0x00)?;
213 delay.delay_us(10_000);
214 self.write_cmd(dsi_host, NT35510_CMD_RAMWR, 0x00)?;
215
216 self.initialized = true;
217 Ok(())
218 }
219
220 fn read_id(&self, dsi_host: &mut impl DsiHostCtrlIo, cmd: u8) -> Result<u8, Error> {
221 let mut id = [0u8; 1];
222 dsi_host
223 .read(DsiReadCommand::DcsShort { arg: cmd }, &mut id)
224 .map_err(|_| Error::DsiRead)?;
225 Ok(id[0])
226 }
227
228 fn write_cmd(
229 &self,
230 dsi_host: &mut impl DsiHostCtrlIo,
231 cmd: u8,
232 param: u8,
233 ) -> Result<(), Error> {
234 dsi_host
235 .write(DsiWriteCommand::DcsShortP1 {
236 arg: cmd,
237 data: param,
238 })
239 .map_err(|_| Error::DsiWrite)
240 }
241
242 fn write_reg(
243 &self,
244 dsi_host: &mut impl DsiHostCtrlIo,
245 reg: u8,
246 data: &[u8],
247 ) -> Result<(), Error> {
248 if data.is_empty() {
249 self.write_cmd(dsi_host, reg, 0)
250 } else if data.len() == 1 {
251 self.write_cmd(dsi_host, reg, data[0])
252 } else {
253 dsi_host
254 .write(DsiWriteCommand::DcsLongWrite { arg: reg, data })
255 .map_err(|_| Error::DsiWrite)
256 }
257 }
258}