capsules_extra/
sh1106.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2024.
4
5//! SH1106 OLED Screen Driver
6//!
7//! This display is similar to the SSD1306, but has two key differences:
8//! - The commands are different. In particular, the SH1106 does not support the
9//!   `SetColumnAddress` and `SetPageAddress` commands which are useful for
10//!   setting frames on the screen.
11//! - The driver does not automatically wrap to the next page. This driver
12//!   manually sets up each page (row).
13
14use core::cell::Cell;
15
16use crate::ssd1306::Command;
17use kernel::hil;
18use kernel::utilities::cells::{MapCell, OptionalCell, TakeCell};
19use kernel::utilities::leasable_buffer::SubSliceMut;
20use kernel::ErrorCode;
21
22// Only need to be able to write one page (row) at a time.
23pub const BUFFER_SIZE: usize = 132;
24
25const WIDTH: usize = 128;
26const HEIGHT: usize = 64;
27
28// #[derive(Copy, Clone, PartialEq)]
29#[derive(Clone, Copy, PartialEq)]
30enum State {
31    Idle,
32    Init,
33    SimpleCommand,
34    WriteSetPage(u8),
35    WritePage(u8),
36}
37
38pub struct Sh1106<'a, I: hil::i2c::I2CDevice> {
39    i2c: &'a I,
40    state: Cell<State>,
41    client: OptionalCell<&'a dyn hil::screen::ScreenClient>,
42    setup_client: OptionalCell<&'a dyn hil::screen::ScreenSetupClient>,
43    buffer: TakeCell<'static, [u8]>,
44    write_buffer: MapCell<SubSliceMut<'static, u8>>,
45    enable_charge_pump: bool,
46
47    active_frame_x: Cell<u8>,
48    active_frame_y: Cell<u8>,
49    active_frame_width: Cell<u8>,
50    active_frame_height: Cell<u8>,
51}
52
53impl<'a, I: hil::i2c::I2CDevice> Sh1106<'a, I> {
54    pub fn new(i2c: &'a I, buffer: &'static mut [u8], enable_charge_pump: bool) -> Self {
55        Self {
56            i2c,
57            state: Cell::new(State::Idle),
58            client: OptionalCell::empty(),
59            setup_client: OptionalCell::empty(),
60            buffer: TakeCell::new(buffer),
61            write_buffer: MapCell::empty(),
62            enable_charge_pump,
63            active_frame_x: Cell::new(0),
64            active_frame_y: Cell::new(0),
65            active_frame_width: Cell::new(0),
66            active_frame_height: Cell::new(0),
67        }
68    }
69
70    pub fn init_screen(&self) {
71        let commands = [
72            Command::SetDisplayOnOff { on: false },
73            Command::SetDisplayClockDivide {
74                divide_ratio: 0,
75                oscillator_frequency: 0x8,
76            },
77            Command::SetMultiplexRatio {
78                ratio: HEIGHT as u8 - 1,
79            },
80            Command::SetDisplayOffset { vertical_shift: 0 },
81            Command::SetDisplayStartLine { line: 0 },
82            Command::SetChargePump {
83                enable: self.enable_charge_pump,
84            },
85            Command::SetMemoryAddressingMode { mode: 0 }, //horizontal
86            Command::SetSegmentRemap { reverse: true },
87            Command::SetComScanDirection { decrement: true },
88            Command::SetComPins {
89                alternative: true,
90                enable_com: false,
91            },
92            Command::SetContrast { contrast: 0xcf },
93            Command::SetPrechargePeriod {
94                phase1: 0x1,
95                phase2: 0xf,
96            },
97            Command::SetVcomDeselect { level: 2 },
98            Command::EntireDisplayOn { ignore_ram: false },
99            Command::SetDisplayInvert { inverse: false },
100            Command::DeactivateScroll,
101            Command::SetDisplayOnOff { on: true },
102        ];
103
104        match self.send_sequence(&commands) {
105            Ok(()) => {
106                self.state.set(State::Init);
107            }
108            Err(_e) => {}
109        }
110    }
111
112    fn send_sequence(&self, sequence: &[Command]) -> Result<(), ErrorCode> {
113        self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
114            let mut buf_slice = SubSliceMut::new(buffer);
115
116            // Specify this is a series of command bytes.
117            buf_slice[0] = 0; // Co = 0, D/C̅ = 0
118
119            // Move the window of the subslice after the command byte header.
120            buf_slice.slice(1..);
121
122            for cmd in sequence.iter() {
123                cmd.encode(&mut buf_slice);
124            }
125
126            // We need the amount of data that has been sliced away
127            // at the start of the subslice.
128            let remaining_len = buf_slice.len();
129            buf_slice.reset();
130            let tx_len = buf_slice.len() - remaining_len;
131
132            self.i2c.enable();
133            match self.i2c.write(buf_slice.take(), tx_len) {
134                Ok(()) => Ok(()),
135                Err((_e, buf)) => {
136                    self.buffer.replace(buf);
137                    self.i2c.disable();
138                    Err(ErrorCode::INVAL)
139                }
140            }
141        })
142    }
143
144    fn write_continue(&self) -> Result<(), ErrorCode> {
145        match self.state.get() {
146            State::WriteSetPage(page_index) => {
147                self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
148                    self.write_buffer.map_or(Err(ErrorCode::NOMEM), |data| {
149                        // Calculate which part of the data buffer we need to
150                        // write.
151                        let start_page_index = self.active_frame_y.get() / 8;
152                        let buffer_start_index = ((page_index - start_page_index) as usize)
153                            * self.active_frame_width.get() as usize;
154                        let page_len = self.active_frame_width.get() as usize;
155
156                        let mut buf_slice = SubSliceMut::new(buffer);
157
158                        // Specify this is data.
159                        buf_slice[0] = 0x40; // Co = 0, D/C̅ = 1
160
161                        // Move the window of the subslice after the command
162                        // byte header.
163                        buf_slice.slice(1..);
164
165                        // Copy the correct page data to the buffer.
166                        for i in 0..page_len {
167                            buf_slice[i] = data[buffer_start_index + i];
168                        }
169
170                        // Length includes the header byte.
171                        let tx_len = page_len + 1;
172
173                        self.i2c.enable();
174                        match self.i2c.write(buf_slice.take(), tx_len) {
175                            Ok(()) => {
176                                self.state.set(State::WritePage(page_index));
177                                Ok(())
178                            }
179                            Err((_e, buf)) => {
180                                self.buffer.replace(buf);
181                                Err(ErrorCode::INVAL)
182                            }
183                        }
184                    })
185                })
186            }
187
188            State::WritePage(page_index) => {
189                // Finished writing a page of data. Check if there is more to
190                // do.
191                let next_page = page_index + 1;
192                let last_page = (self.active_frame_y.get() + self.active_frame_height.get()) / 8;
193
194                if next_page >= last_page {
195                    // Done, can issue callback.
196                    self.state.set(State::Idle);
197                    self.write_buffer.take().map(|buf| {
198                        self.client.map(|client| client.write_complete(buf, Ok(())));
199                    });
200                    Ok(())
201                } else {
202                    // Continue writing by setting up the next page.
203                    self.set_page(next_page)
204                }
205            }
206
207            _ => Err(ErrorCode::FAIL),
208        }
209    }
210
211    fn set_page(&self, page_index: u8) -> Result<(), ErrorCode> {
212        let column_start = self.active_frame_x.get() + 2;
213        let commands = [
214            Command::SetPageStartAddress {
215                address: page_index,
216            },
217            Command::SetLowerColumnStartAddress {
218                address: column_start,
219            },
220            Command::SetHigherColumnStartAddress {
221                address: column_start,
222            },
223        ];
224        match self.send_sequence(&commands) {
225            Ok(()) => {
226                self.state.set(State::WriteSetPage(page_index));
227                Ok(())
228            }
229            Err(e) => Err(e),
230        }
231    }
232}
233
234impl<'a, I: hil::i2c::I2CDevice> hil::screen::ScreenSetup<'a> for Sh1106<'a, I> {
235    fn set_client(&self, client: &'a dyn hil::screen::ScreenSetupClient) {
236        self.setup_client.set(client);
237    }
238
239    fn set_resolution(&self, _resolution: (usize, usize)) -> Result<(), ErrorCode> {
240        Err(ErrorCode::NOSUPPORT)
241    }
242
243    fn set_pixel_format(&self, _depth: hil::screen::ScreenPixelFormat) -> Result<(), ErrorCode> {
244        Err(ErrorCode::NOSUPPORT)
245    }
246
247    fn set_rotation(&self, _rotation: hil::screen::ScreenRotation) -> Result<(), ErrorCode> {
248        Err(ErrorCode::NOSUPPORT)
249    }
250
251    fn get_num_supported_resolutions(&self) -> usize {
252        1
253    }
254
255    fn get_supported_resolution(&self, index: usize) -> Option<(usize, usize)> {
256        match index {
257            0 => Some((WIDTH, HEIGHT)),
258            _ => None,
259        }
260    }
261
262    fn get_num_supported_pixel_formats(&self) -> usize {
263        1
264    }
265
266    fn get_supported_pixel_format(&self, index: usize) -> Option<hil::screen::ScreenPixelFormat> {
267        match index {
268            0 => Some(hil::screen::ScreenPixelFormat::Mono),
269            _ => None,
270        }
271    }
272}
273
274impl<'a, I: hil::i2c::I2CDevice> hil::screen::Screen<'a> for Sh1106<'a, I> {
275    fn set_client(&self, client: &'a dyn hil::screen::ScreenClient) {
276        self.client.set(client);
277    }
278
279    fn get_resolution(&self) -> (usize, usize) {
280        (WIDTH, HEIGHT)
281    }
282
283    fn get_pixel_format(&self) -> hil::screen::ScreenPixelFormat {
284        hil::screen::ScreenPixelFormat::Mono
285    }
286
287    fn get_rotation(&self) -> hil::screen::ScreenRotation {
288        hil::screen::ScreenRotation::Normal
289    }
290
291    fn set_write_frame(
292        &self,
293        x: usize,
294        y: usize,
295        width: usize,
296        height: usize,
297    ) -> Result<(), ErrorCode> {
298        // Save the current frame settings.
299        self.active_frame_x.set(x as u8);
300        self.active_frame_y.set(y as u8);
301        self.active_frame_width.set(width as u8);
302        self.active_frame_height.set(height as u8);
303
304        // The driver RAM is 132 bytes wide, the screen is 128 bytes wide, so we
305        // offset by two.
306        let column_start: u8 = (x as u8) + 2;
307        let commands = [
308            Command::SetPageStartAddress {
309                address: (y / 8) as u8,
310            },
311            Command::SetLowerColumnStartAddress {
312                address: column_start,
313            },
314            Command::SetHigherColumnStartAddress {
315                address: column_start,
316            },
317        ];
318        match self.send_sequence(&commands) {
319            Ok(()) => {
320                self.state.set(State::SimpleCommand);
321                Ok(())
322            }
323            Err(e) => Err(e),
324        }
325    }
326
327    fn write(&self, data: SubSliceMut<'static, u8>, _continue: bool) -> Result<(), ErrorCode> {
328        self.write_buffer.replace(data);
329
330        // Start by setting the page as active in the screen.
331        self.set_page(self.active_frame_y.get() / 8)
332    }
333
334    fn set_brightness(&self, brightness: u16) -> Result<(), ErrorCode> {
335        let commands = [Command::SetContrast {
336            contrast: (brightness >> 8) as u8,
337        }];
338        match self.send_sequence(&commands) {
339            Ok(()) => {
340                self.state.set(State::SimpleCommand);
341                Ok(())
342            }
343            Err(e) => Err(e),
344        }
345    }
346
347    fn set_power(&self, enabled: bool) -> Result<(), ErrorCode> {
348        let commands = [Command::SetDisplayOnOff { on: enabled }];
349        match self.send_sequence(&commands) {
350            Ok(()) => {
351                self.state.set(State::SimpleCommand);
352                Ok(())
353            }
354            Err(e) => Err(e),
355        }
356    }
357
358    fn set_invert(&self, enabled: bool) -> Result<(), ErrorCode> {
359        let commands = [Command::SetDisplayInvert { inverse: enabled }];
360        match self.send_sequence(&commands) {
361            Ok(()) => {
362                self.state.set(State::SimpleCommand);
363                Ok(())
364            }
365            Err(e) => Err(e),
366        }
367    }
368}
369
370impl<I: hil::i2c::I2CDevice> hil::i2c::I2CClient for Sh1106<'_, I> {
371    fn command_complete(&self, buffer: &'static mut [u8], _status: Result<(), hil::i2c::Error>) {
372        self.buffer.replace(buffer);
373        self.i2c.disable();
374
375        match self.state.get() {
376            State::Init => {
377                self.state.set(State::Idle);
378                self.client.map(|client| client.screen_is_ready());
379            }
380
381            State::SimpleCommand => {
382                self.state.set(State::Idle);
383                self.client.map(|client| client.command_complete(Ok(())));
384            }
385
386            State::WritePage(_) | State::WriteSetPage(_) => {
387                let _ = self.write_continue();
388            }
389            _ => {}
390        }
391    }
392}