1use core::cell::Cell;
8use kernel::hil;
9use kernel::utilities::cells::{MapCell, OptionalCell, TakeCell};
10use kernel::utilities::leasable_buffer::SubSliceMut;
11use kernel::ErrorCode;
12
13pub const BUFFER_SIZE: usize = 1032;
14
15const WIDTH: usize = 128;
16const HEIGHT: usize = 64;
17
18#[derive(Copy, Clone, PartialEq)]
19#[repr(usize)]
20pub enum Command {
21 SetChargePump { enable: bool },
24
25 SetContrast { contrast: u8 },
29 EntireDisplayOn { ignore_ram: bool },
31 SetDisplayInvert { inverse: bool },
33 SetDisplayOnOff { on: bool },
35
36 ContinuousHorizontalScroll {
39 left: bool,
40 page_start: u8,
41 interval: u8,
42 page_end: u8,
43 },
44 ContinuousVerticalHorizontalScroll {
47 left: bool,
48 page_start: u8,
49 interval: u8,
50 page_end: u8,
51 vertical_offset: u8,
52 },
53 DeactivateScroll = 0x2e,
55 ActivateScroll = 0x2f,
57 SetVerticalScrollArea { rows_fixed: u8, rows_scroll: u8 },
61
62 SetLowerColumnStartAddress { address: u8 },
69 SetHigherColumnStartAddress { address: u8 },
75 SetMemoryAddressingMode { mode: u8 },
77 SetColumnAddress { column_start: u8, column_end: u8 },
79 SetPageAddress { page_start: u8, page_end: u8 },
81 SetPageStartAddress { address: u8 },
84
85 SetDisplayStartLine { line: u8 },
89 SetSegmentRemap { reverse: bool },
91 SetMultiplexRatio { ratio: u8 },
93 SetComScanDirection { decrement: bool },
95 SetDisplayOffset { vertical_shift: u8 } = 0xd3,
97 SetComPins { alternative: bool, enable_com: bool },
99
100 SetDisplayClockDivide {
103 divide_ratio: u8,
104 oscillator_frequency: u8,
105 },
106 SetPrechargePeriod { phase1: u8, phase2: u8 },
108 SetVcomDeselect { level: u8 },
110}
111
112impl Command {
113 pub fn encode(self, buffer: &mut SubSliceMut<'static, u8>) {
114 let take = match self {
115 Self::SetChargePump { enable } => {
116 buffer[0] = 0x8D;
117 buffer[1] = 0x10 | ((enable as u8) << 2);
118 2
119 }
120 Self::SetContrast { contrast } => {
121 buffer[0] = 0x81;
122 buffer[1] = contrast;
123 2
124 }
125 Self::EntireDisplayOn { ignore_ram } => {
126 buffer[0] = 0xa4 | (ignore_ram as u8);
127 1
128 }
129 Self::SetDisplayInvert { inverse } => {
130 buffer[0] = 0xa6 | (inverse as u8);
131 1
132 }
133 Self::SetDisplayOnOff { on } => {
134 buffer[0] = 0xae | (on as u8);
135 1
136 }
137 Self::ContinuousHorizontalScroll {
138 left,
139 page_start,
140 interval,
141 page_end,
142 } => {
143 buffer[0] = 0x26 | (left as u8);
144 buffer[1] = 0;
145 buffer[2] = page_start;
146 buffer[3] = interval;
147 buffer[4] = page_end;
148 buffer[5] = 0;
149 buffer[6] = 0xff;
150 7
151 }
152 Self::ContinuousVerticalHorizontalScroll {
153 left,
154 page_start,
155 interval,
156 page_end,
157 vertical_offset,
158 } => {
159 buffer[0] = 0x29 | (left as u8);
160 buffer[1] = 0;
161 buffer[2] = page_start;
162 buffer[3] = interval;
163 buffer[4] = page_end;
164 buffer[5] = vertical_offset;
165 6
166 }
167 Self::DeactivateScroll => {
168 buffer[0] = 0x2e;
169 1
170 }
171 Self::ActivateScroll => {
172 buffer[0] = 0x2f;
173 1
174 }
175 Self::SetVerticalScrollArea {
176 rows_fixed,
177 rows_scroll,
178 } => {
179 buffer[0] = 0xa3;
180 buffer[1] = rows_fixed;
181 buffer[2] = rows_scroll;
182 3
183 }
184 Self::SetLowerColumnStartAddress { address } => {
185 buffer[0] = 0x00 | (address & 0xF);
186 1
187 }
188 Self::SetHigherColumnStartAddress { address } => {
189 buffer[0] = 0x10 | ((address >> 4) & 0xF);
190 1
191 }
192 Self::SetMemoryAddressingMode { mode } => {
193 buffer[0] = 0x20;
194 buffer[1] = mode;
195 2
196 }
197 Self::SetColumnAddress {
198 column_start,
199 column_end,
200 } => {
201 buffer[0] = 0x21;
202 buffer[1] = column_start;
203 buffer[2] = column_end;
204 3
205 }
206 Self::SetPageAddress {
207 page_start,
208 page_end,
209 } => {
210 buffer[0] = 0x22;
211 buffer[1] = page_start;
212 buffer[2] = page_end;
213 3
214 }
215 Self::SetPageStartAddress { address } => {
216 buffer[0] = 0xb0 | (address & 0x7);
217 1
218 }
219 Self::SetDisplayStartLine { line } => {
220 buffer[0] = 0x40 | (line & 0x3F);
221 1
222 }
223 Self::SetSegmentRemap { reverse } => {
224 buffer[0] = 0xa0 | (reverse as u8);
225 1
226 }
227 Self::SetMultiplexRatio { ratio } => {
228 buffer[0] = 0xa8;
229 buffer[1] = ratio;
230 2
231 }
232 Self::SetComScanDirection { decrement } => {
233 buffer[0] = 0xc0 | ((decrement as u8) << 3);
234 1
235 }
236 Self::SetDisplayOffset { vertical_shift } => {
237 buffer[0] = 0xd3;
238 buffer[1] = vertical_shift;
239 2
240 }
241 Self::SetComPins {
242 alternative,
243 enable_com,
244 } => {
245 buffer[0] = 0xda;
246 buffer[1] = ((alternative as u8) << 4) | ((enable_com as u8) << 5) | 0x2;
247 2
248 }
249 Self::SetDisplayClockDivide {
250 divide_ratio,
251 oscillator_frequency,
252 } => {
253 buffer[0] = 0xd5;
254 buffer[1] = ((oscillator_frequency & 0xF) << 4) | (divide_ratio & 0xf);
255 2
256 }
257 Self::SetPrechargePeriod { phase1, phase2 } => {
258 buffer[0] = 0xd9;
259 buffer[1] = ((phase2 & 0xF) << 4) | (phase1 & 0xf);
260 2
261 }
262 Self::SetVcomDeselect { level } => {
263 buffer[0] = 0xdb;
264 buffer[1] = (level & 0xF) << 4;
265 2
266 }
267 };
268
269 buffer.slice(take..);
272 }
273}
274
275#[derive(Clone, Copy, PartialEq)]
277enum State {
278 Idle,
279 Init,
280 SimpleCommand,
281 Write,
282}
283
284pub struct Ssd1306<'a, I: hil::i2c::I2CDevice> {
285 i2c: &'a I,
286 state: Cell<State>,
287 client: OptionalCell<&'a dyn hil::screen::ScreenClient>,
288 setup_client: OptionalCell<&'a dyn hil::screen::ScreenSetupClient>,
289 buffer: TakeCell<'static, [u8]>,
290 write_buffer: MapCell<SubSliceMut<'static, u8>>,
291 enable_charge_pump: bool,
292}
293
294impl<'a, I: hil::i2c::I2CDevice> Ssd1306<'a, I> {
295 pub fn new(i2c: &'a I, buffer: &'static mut [u8], enable_charge_pump: bool) -> Ssd1306<'a, I> {
296 Ssd1306 {
297 i2c,
298 state: Cell::new(State::Idle),
299 client: OptionalCell::empty(),
300 setup_client: OptionalCell::empty(),
301 buffer: TakeCell::new(buffer),
302 write_buffer: MapCell::empty(),
303 enable_charge_pump,
304 }
305 }
306
307 pub fn init_screen(&self) {
308 let commands = [
309 Command::SetDisplayOnOff { on: false },
310 Command::SetDisplayClockDivide {
311 divide_ratio: 0,
312 oscillator_frequency: 0x8,
313 },
314 Command::SetMultiplexRatio {
315 ratio: HEIGHT as u8 - 1,
316 },
317 Command::SetDisplayOffset { vertical_shift: 0 },
318 Command::SetDisplayStartLine { line: 0 },
319 Command::SetChargePump {
320 enable: self.enable_charge_pump,
321 },
322 Command::SetMemoryAddressingMode { mode: 0 }, Command::SetSegmentRemap { reverse: true },
324 Command::SetComScanDirection { decrement: true },
325 Command::SetComPins {
326 alternative: true,
327 enable_com: false,
328 },
329 Command::SetContrast { contrast: 0xcf },
330 Command::SetPrechargePeriod {
331 phase1: 0x1,
332 phase2: 0xf,
333 },
334 Command::SetVcomDeselect { level: 2 },
335 Command::EntireDisplayOn { ignore_ram: false },
336 Command::SetDisplayInvert { inverse: false },
337 Command::DeactivateScroll,
338 Command::SetDisplayOnOff { on: true },
339 ];
340
341 match self.send_sequence(&commands) {
342 Ok(()) => {
343 self.state.set(State::Init);
344 }
345 Err(_e) => {}
346 }
347 }
348
349 fn send_sequence(&self, sequence: &[Command]) -> Result<(), ErrorCode> {
350 if self.state.get() == State::Idle {
351 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
352 let mut buf_slice = SubSliceMut::new(buffer);
353
354 buf_slice[0] = 0; buf_slice.slice(1..);
359
360 for cmd in sequence.iter() {
361 cmd.encode(&mut buf_slice);
362 }
363
364 let remaining_len = buf_slice.len();
367 buf_slice.reset();
368 let tx_len = buf_slice.len() - remaining_len;
369
370 self.i2c.enable();
371 match self.i2c.write(buf_slice.take(), tx_len) {
372 Ok(()) => Ok(()),
373 Err((_e, buf)) => {
374 self.buffer.replace(buf);
375 self.i2c.disable();
376 Err(ErrorCode::INVAL)
377 }
378 }
379 })
380 } else {
381 Err(ErrorCode::BUSY)
382 }
383 }
384}
385
386impl<'a, I: hil::i2c::I2CDevice> hil::screen::ScreenSetup<'a> for Ssd1306<'a, I> {
387 fn set_client(&self, client: &'a dyn hil::screen::ScreenSetupClient) {
388 self.setup_client.set(client);
389 }
390
391 fn set_resolution(&self, _resolution: (usize, usize)) -> Result<(), ErrorCode> {
392 Err(ErrorCode::NOSUPPORT)
393 }
394
395 fn set_pixel_format(&self, _depth: hil::screen::ScreenPixelFormat) -> Result<(), ErrorCode> {
396 Err(ErrorCode::NOSUPPORT)
397 }
398
399 fn set_rotation(&self, _rotation: hil::screen::ScreenRotation) -> Result<(), ErrorCode> {
400 Err(ErrorCode::NOSUPPORT)
401 }
402
403 fn get_num_supported_resolutions(&self) -> usize {
404 1
405 }
406
407 fn get_supported_resolution(&self, index: usize) -> Option<(usize, usize)> {
408 match index {
409 0 => Some((WIDTH, HEIGHT)),
410 _ => None,
411 }
412 }
413
414 fn get_num_supported_pixel_formats(&self) -> usize {
415 1
416 }
417
418 fn get_supported_pixel_format(&self, index: usize) -> Option<hil::screen::ScreenPixelFormat> {
419 match index {
420 0 => Some(hil::screen::ScreenPixelFormat::Mono),
421 _ => None,
422 }
423 }
424}
425
426impl<'a, I: hil::i2c::I2CDevice> hil::screen::Screen<'a> for Ssd1306<'a, I> {
427 fn set_client(&self, client: &'a dyn hil::screen::ScreenClient) {
428 self.client.set(client);
429 }
430
431 fn get_resolution(&self) -> (usize, usize) {
432 (WIDTH, HEIGHT)
433 }
434
435 fn get_pixel_format(&self) -> hil::screen::ScreenPixelFormat {
436 hil::screen::ScreenPixelFormat::Mono
437 }
438
439 fn get_rotation(&self) -> hil::screen::ScreenRotation {
440 hil::screen::ScreenRotation::Normal
441 }
442
443 fn set_write_frame(
444 &self,
445 x: usize,
446 y: usize,
447 width: usize,
448 height: usize,
449 ) -> Result<(), ErrorCode> {
450 let commands = [
451 Command::SetPageAddress {
452 page_start: (y / 8) as u8,
453 page_end: ((y / 8) + (height / 8) - 1) as u8,
454 },
455 Command::SetColumnAddress {
456 column_start: x as u8,
457 column_end: (x + width - 1) as u8,
458 },
459 ];
460 match self.send_sequence(&commands) {
461 Ok(()) => {
462 self.state.set(State::SimpleCommand);
463 Ok(())
464 }
465 Err(e) => Err(e),
466 }
467 }
468
469 fn write(&self, data: SubSliceMut<'static, u8>, _continue: bool) -> Result<(), ErrorCode> {
470 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
471 let mut buf_slice = SubSliceMut::new(buffer);
472
473 buf_slice[0] = 0x40; buf_slice.slice(1..);
478
479 let copy_len = core::cmp::min(buf_slice.len(), data.len());
481
482 for i in 0..copy_len {
483 buf_slice[i] = data[i];
484 }
485
486 let tx_len = copy_len + 1;
487
488 self.i2c.enable();
489 match self.i2c.write(buf_slice.take(), tx_len) {
490 Ok(()) => {
491 self.state.set(State::Write);
492 self.write_buffer.replace(data);
493 Ok(())
494 }
495 Err((_e, buf)) => {
496 self.buffer.replace(buf);
497 Err(ErrorCode::INVAL)
498 }
499 }
500 })
501 }
502
503 fn set_brightness(&self, brightness: u16) -> Result<(), ErrorCode> {
504 let commands = [Command::SetContrast {
505 contrast: (brightness >> 8) as u8,
506 }];
507 match self.send_sequence(&commands) {
508 Ok(()) => {
509 self.state.set(State::SimpleCommand);
510 Ok(())
511 }
512 Err(e) => Err(e),
513 }
514 }
515
516 fn set_power(&self, enabled: bool) -> Result<(), ErrorCode> {
517 let commands = [Command::SetDisplayOnOff { on: enabled }];
518 match self.send_sequence(&commands) {
519 Ok(()) => {
520 self.state.set(State::SimpleCommand);
521 Ok(())
522 }
523 Err(e) => Err(e),
524 }
525 }
526
527 fn set_invert(&self, enabled: bool) -> Result<(), ErrorCode> {
528 let commands = [Command::SetDisplayInvert { inverse: enabled }];
529 match self.send_sequence(&commands) {
530 Ok(()) => {
531 self.state.set(State::SimpleCommand);
532 Ok(())
533 }
534 Err(e) => Err(e),
535 }
536 }
537}
538
539impl<I: hil::i2c::I2CDevice> hil::i2c::I2CClient for Ssd1306<'_, I> {
540 fn command_complete(&self, buffer: &'static mut [u8], _status: Result<(), hil::i2c::Error>) {
541 self.buffer.replace(buffer);
542 self.i2c.disable();
543
544 match self.state.get() {
545 State::Init => {
546 self.state.set(State::Idle);
547 self.client.map(|client| client.screen_is_ready());
548 }
549
550 State::SimpleCommand => {
551 self.state.set(State::Idle);
552 self.client.map(|client| client.command_complete(Ok(())));
553 }
554
555 State::Write => {
556 self.state.set(State::Idle);
557 self.write_buffer.take().map(|buf| {
558 self.client.map(|client| client.write_complete(buf, Ok(())));
559 });
560 }
561 _ => {}
562 }
563 }
564}