mini_oled/command.rs
1//! # Commands
2//!
3//! This module defines the commands that can be sent to the SH1106 display controller.
4//! It includes the `Command` enum and the `CommandBuffer` struct for batching commands.
5//!
6//! ## Example
7//!
8//! Sending a manual command sequence (pseudo-code as `CommunicationInterface` is needed).
9//!
10//! ```rust
11//! use mini_oled::command::{Command, CommandBuffer};
12//!
13//! let commands: CommandBuffer<2> = [
14//! Command::TurnDisplayOn,
15//! Command::Contrast(0xFF),
16//! ].into();
17//!
18//! // Write commands using the interface...
19//! // interface.write_command(&commands).unwrap();
20//! ```
21
22use crate::error::MiniOledError;
23
24/// A buffer for storing commands to be sent to the display.
25///
26/// This struct holds an array of `Command`s.
27#[derive(Debug, Clone, Copy)]
28pub struct CommandBuffer<const N: usize> {
29 buffer: [Command; N],
30}
31
32impl From<Command> for CommandBuffer<1> {
33 fn from(value: Command) -> Self {
34 CommandBuffer { buffer: [value] }
35 }
36}
37
38impl<const N: usize> From<[Command; N]> for CommandBuffer<N> {
39 fn from(value: [Command; N]) -> Self {
40 CommandBuffer { buffer: value }
41 }
42}
43
44impl<const N: usize> CommandBuffer<N> {
45 /// Serializes the command buffer into a byte slice.
46 ///
47 /// # Arguments
48 ///
49 /// * `buffer` - A mutable byte slice to write the serialized commands into.
50 ///
51 /// # Returns
52 ///
53 /// A slice containing the written bytes on success, or `MiniOledError` if the buffer is too small.
54 pub fn to_bytes<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a [u8], MiniOledError> {
55 let mut output_length = 1usize;
56 for command in &self.buffer {
57 let (command_bytes, bytes_length) = command.to_bytes();
58 if output_length + bytes_length > buffer.len() {
59 return Err(MiniOledError::CommandBufferSizeError);
60 }
61 buffer[output_length..output_length + bytes_length]
62 .copy_from_slice(&command_bytes[0..bytes_length]);
63 output_length += bytes_length;
64 }
65 Ok(&buffer[..output_length])
66 }
67}
68
69/// Enum representing commands that can be sent to the SH1106 controller.
70#[derive(Debug, Clone, Copy)]
71pub enum Command {
72 /// Set contrast. Higher number is higher contrast.
73 /// Default is `0x7F`.
74 Contrast(u8),
75 /// Forces the entire display to be on regardless of the contents of the display RAM.
76 /// It does not overwrite the RAM. Often used for testing pixels or creating a flash effect.
77 /// Sending `DisableTestSceen` resumes displaying the RAM content.
78 EnableTestScreen,
79 /// Disables test screen mode.
80 DisableTestScreen,
81 /// Inverts the display data.
82 /// Normally, a 1 in memory means a lit pixel. (`PositiveImageMode`)
83 /// When inverted, 0 means lit and 1 means off. (`NegativeImageMode`)
84 /// Default is `PositiveImageMode`.
85 PositiveImageMode,
86 /// Enable negative image mode.
87 NegativeImageMode,
88 /// Turns the display on.
89 TurnDisplayOn,
90 /// Puts the display into sleep mode.
91 /// In sleep mode (0xAE), the internal circuit is active but the driving circuit is off,
92 /// reducing power consumption drastically (< 20µA). RAM content is preserved.
93 TurnDisplayOff,
94 /// Set column address lower 4 bits.
95 ColumnAddressLow(u8),
96 /// Set column address higher 4 bits.
97 ColumnAddressHigh(u8),
98 /// Set page address.
99 PageAddress(Page),
100 /// Set display start line from 0-63.
101 StartLine(u8),
102 /// Reverse columns from 127-0, mirrors the display horizontally (X-axis).
103 /// Default is `DisableSegmentRemap`.
104 EnableSegmentRemap,
105 /// Disable segment remap (normal column order).
106 DisableSegmentRemap,
107 /// Set multipex ratio from 15-63 (MUX-1).
108 Multiplex(u8),
109 /// Scan from COM[n-1] to COM0 (where N is mux ratio).
110 /// Used together with `EnableSegmentRemap` to rotate the display 180 degrees.
111 /// Default is `DisableReverseComDir`.
112 EnableReverseComDir,
113 /// Disable reverse COM direction (normal scan).
114 DisableReverseComDir,
115 /// Set vertical display offset.
116 DisplayOffset(u8),
117 /// Setup COM hardware configuration.
118 /// Value indicates sequential (`SequentialComPinConfig`) or alternative (`AlternativeComPinConfig`)
119 /// pin configuration.
120 /// Default is `AlternativeComPinConfig`.
121 AlternativeComPinConfig,
122 /// Sequential COM pin configuration.
123 SequentialComPinConfig,
124 /// Set up display clock.
125 /// First value is oscillator frequency, increasing with higher value.
126 /// Second value is divide ratio - 1.
127 DisplayClockDiv(u8, u8),
128 /// Set up phase 1 and 2 of precharge period. Each value is from 0-63.
129 PreChargePeriod(u8, u8),
130 /// Set Vcomh Deselect level.
131 VcomhDeselect(VcomhLevel),
132 /// No Operation.
133 Noop,
134 /// Enable charge pump.
135 /// Display must be off when performing this command.
136 /// Default is `EnableChargePump`.
137 EnableChargePump,
138 /// Disable charge pump.
139 DisableChargePump,
140}
141
142impl Command {
143 pub fn to_bytes(&self) -> ([u8; 2], usize) {
144 match self {
145 Command::Contrast(val) => ([0x81, *val], self.get_byte_size()),
146 Command::EnableTestScreen => ([0xA5, 0], self.get_byte_size()),
147 Command::DisableTestScreen => ([0xA4, 0], self.get_byte_size()),
148 Command::PositiveImageMode => ([0xA6, 0], self.get_byte_size()),
149 Command::NegativeImageMode => ([0xA7, 0], self.get_byte_size()),
150 Command::TurnDisplayOn => ([0xAF, 0], self.get_byte_size()),
151 Command::TurnDisplayOff => ([0xAE, 0], self.get_byte_size()),
152 Command::ColumnAddressLow(addr) => ([0xF & addr, 0], self.get_byte_size()),
153 Command::ColumnAddressHigh(addr) => ([0x10 | (0xF & addr), 0], self.get_byte_size()),
154 Command::PageAddress(page) => ([0xB0 | (*page as u8), 0], self.get_byte_size()),
155 Command::StartLine(line) => ([0x40 | (0x3F & line), 0], self.get_byte_size()),
156 Command::EnableSegmentRemap => ([0xA1, 0], self.get_byte_size()),
157 Command::DisableSegmentRemap => ([0xA0, 0], self.get_byte_size()),
158 Command::Multiplex(ratio) => ([0xA8, *ratio], self.get_byte_size()),
159 Command::EnableReverseComDir => ([0xC8, 0], self.get_byte_size()),
160 Command::DisableReverseComDir => ([0xC0, 0], self.get_byte_size()),
161 Command::DisplayOffset(offset) => ([0xD3, *offset], self.get_byte_size()),
162 Command::AlternativeComPinConfig => ([0xDA, 0x12], self.get_byte_size()),
163 Command::SequentialComPinConfig => ([0xDA, 0x02], self.get_byte_size()),
164 Command::DisplayClockDiv(fosc, div) => (
165 [0xD5, ((0xF & fosc) << 4) | (0xF & div)],
166 self.get_byte_size(),
167 ),
168 Command::PreChargePeriod(phase1, phase2) => (
169 [0xD9, ((0xF & phase2) << 4) | (0xF & phase1)],
170 self.get_byte_size(),
171 ),
172 Command::VcomhDeselect(level) => ([0xDB, (*level as u8) << 4], self.get_byte_size()),
173 Command::Noop => ([0xE3, 0], self.get_byte_size()),
174 Command::EnableChargePump => ([0xAD, 0x8B], self.get_byte_size()),
175 Command::DisableChargePump => ([0xAD, 0x8A], self.get_byte_size()),
176 }
177 }
178
179 /// Returns the size in bytes of the command when serialized.
180 pub const fn get_byte_size(&self) -> usize {
181 match self {
182 Command::Contrast(_) => 2,
183 Command::EnableTestScreen => 1,
184 Command::DisableTestScreen => 1,
185 Command::PositiveImageMode => 1,
186 Command::NegativeImageMode => 1,
187 Command::TurnDisplayOn => 1,
188 Command::TurnDisplayOff => 1,
189 Command::ColumnAddressLow(_) => 1,
190 Command::ColumnAddressHigh(_) => 1,
191 Command::PageAddress(_) => 1,
192 Command::StartLine(_) => 1,
193 Command::EnableSegmentRemap => 1,
194 Command::DisableSegmentRemap => 1,
195 Command::Multiplex(_) => 2,
196 Command::EnableReverseComDir => 1,
197 Command::DisableReverseComDir => 1,
198 Command::DisplayOffset(_) => 2,
199 Command::AlternativeComPinConfig => 2,
200 Command::SequentialComPinConfig => 2,
201 Command::DisplayClockDiv(_, _) => 2,
202 Command::PreChargePeriod(_, _) => 2,
203 Command::VcomhDeselect(_) => 2,
204 Command::Noop => 1,
205 Command::EnableChargePump => 2,
206 Command::DisableChargePump => 2,
207 }
208 }
209}
210
211/// Display page address (0-7).
212///
213/// The display memory is divided into 8 pages, each 8 pixels high.
214///
215/// # Example
216///
217/// ```rust
218/// use mini_oled::command::Page;
219///
220/// let page = Page::Page0;
221/// assert_eq!(page as u8, 0);
222/// ```
223#[repr(u8)]
224#[derive(Debug, Clone, Copy)]
225pub enum Page {
226 /// Page 0
227 Page0 = 0,
228 /// Page 1
229 Page1 = 1,
230 /// Page 2
231 Page2 = 2,
232 /// Page 3
233 Page3 = 3,
234 /// Page 4
235 Page4 = 4,
236 /// Page 5
237 Page5 = 5,
238 /// Page 6
239 Page6 = 6,
240 /// Page 7
241 Page7 = 7,
242}
243
244impl Page {
245 /// Returns an iterator over a range of pages.
246 pub fn range(start: Page, end: Page) -> impl Iterator<Item = Page> {
247 (start as u8..=end as u8).map(Page::from)
248 }
249
250 /// Returns an iterator over all 8 pages (0-7).
251 pub fn all() -> impl Iterator<Item = Page> {
252 (0..8).map(Page::from)
253 }
254}
255
256impl From<u8> for Page {
257 fn from(val: u8) -> Page {
258 // Faster way the casting u8 to Page
259 // ```rust
260 // 0x00 => Page::Page0,
261 // 0x08 => Page::Page1,
262 // 0x09 => Page::Page2,
263 // 0x0A => Page::Page3,
264 // ```
265 let new_val = val & 0b111;
266 unsafe { core::mem::transmute(new_val) }
267 }
268}
269
270/// Frame interval configuration for the display clock.
271///
272/// This determines how often the display refreshes.
273///
274/// # Example
275///
276/// ```rust
277/// use mini_oled::command::NFrames;
278///
279/// let frames = NFrames::F5;
280/// ```
281#[repr(u8)]
282#[derive(Debug, Clone, Copy)]
283
284pub enum NFrames {
285 /// 2 Frames
286 F2 = 0b111,
287 /// 3 Frames
288 F3 = 0b100,
289 /// 4 Frames
290 F4 = 0b101,
291 /// 5 Frames
292 F5 = 0b000,
293 /// 25 Frames
294 F25 = 0b110,
295 /// 64 Frames
296 F64 = 0b001,
297 /// 128 Frames
298 F128 = 0b010,
299 /// 256 Frames
300 F256 = 0b011,
301}
302
303/// Vcomh Deselect level.
304///
305/// This adjusts the Vcomh regulator output.
306///
307/// # Example
308///
309/// ```rust
310/// use mini_oled::command::VcomhLevel;
311///
312/// let level = VcomhLevel::V077;
313/// ```
314#[repr(u8)]
315#[derive(Debug, Clone, Copy)]
316
317pub enum VcomhLevel {
318 /// 0.65 * Vcc
319 V065 = 0b001,
320 /// 0.77 * Vcc
321 V077 = 0b010,
322 /// 0.83 * Vcc
323 V083 = 0b011,
324 /// Auto
325 Auto = 0b100,
326}