ssd1315_driver/lib.rs
1//! # SSD1315
2//!
3//! SSD1315 driver.
4//!
5//! ## Usage
6//!
7//! Here is an example of how to use `ssd1315`:
8//!
9//! ```rust
10//! use ssd1315::*;
11//! use embedded_graphics::{
12//! pixelcolor::BinaryColor,
13//! prelude::*,
14//! primitives::{Circle, PrimitiveStyle},
15//! };
16//!
17//! let interface = interface::I2cDisplayInterface::new(i2c);
18//! // let interface = interface::SpiDisplayInterface::new(spi, dc);
19//!
20//! let mut display = Ssd1315::new(interface);
21//!
22//! Circle::new(Point::new(0, 0), 40)
23//! .into_styled(PrimitiveStyle::with_fill(BinaryColor::On))
24//! .draw(&mut display)
25//! .unwrap();
26//!
27//! display.init(Default::default()).unwrap();
28//! display.flush().unwrap();
29//! ```
30//!
31//! Congratulations! Now you can see a small circle displayed on your OLED screen!
32//!
33//! If you want to apply your own configuration for the SSD1315 (for example, to change the contrast),
34//! follow this example:
35//!
36//! ```rust
37//! use ssd1315::*;
38//! use embedded_graphics::{
39//! pixelcolor::BinaryColor,
40//! prelude::*,
41//! primitives::{Circle, PrimitiveStyle},
42//! };
43//!
44//! let interface = interface::I2cDisplayInterface::new(i2c);
45//! // let interface = interface::SpiDisplayInterface::new(spi, dc);
46//!
47//! let mut config = config::Ssd1315Config::new();
48//! config.contrast = 0xff;
49//!
50//! let mut display = Ssd1315::new(interface);
51//!
52//! Circle::new(Point::new(0, 0), 40)
53//! .into_styled(PrimitiveStyle::with_fill(BinaryColor::On))
54//! .draw(&mut display)
55//! .unwrap();
56//!
57//! display.init(config).unwrap();
58//! display.flush().unwrap();
59//! ```
60//!
61//! Alternatively, you can use a preset configuration provided by `ssd1315`:
62//!
63//! ```rust
64//! let config = config::Ssd1315Config::preset_config();
65//! ```
66//!
67//! Now you can see the change in contrast!
68//!
69//! You might also want to draw some raw image(s) manually to fit your specific requirements.
70//! That's no problem! You can draw it/them in an easy way:
71//!
72//! ```rust
73//! let mut display = Ssd1315::new(interface);
74//!
75//! let raw_image = [[0b1010_1010; 128]; 8];
76//! *display.buffer_mut() = raw_image;
77//!
78//! display.init(Default::default()).unwrap();
79//! display.flush().unwrap();
80//! ```
81
82#![no_std]
83
84pub mod command;
85pub mod config;
86mod draw_buffer;
87pub mod interface;
88
89use crate::command::{Command, Page};
90use config::Ssd1315Config;
91use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand};
92
93#[cfg(feature = "async")]
94use crate::command::CommandAsync;
95#[cfg(feature = "async")]
96use display_interface::AsyncWriteOnlyDataCommand;
97
98pub const WIDTH: usize = 128;
99pub const HEIGHT: usize = 64;
100
101/// A virtual SSD1315 device that holds interface data, a buffer
102/// that maps to the actual buffer in the SSD1315 and a configuration.
103
104#[maybe_async_cfg::maybe(sync(keep_self), async(feature = "async"))]
105pub struct Ssd1315<DI> {
106 interface: DI,
107 buffer: [[u8; WIDTH]; 8],
108}
109
110#[maybe_async_cfg::maybe(
111 sync(keep_self),
112 async(
113 feature = "async",
114 idents(Command, WriteOnlyDataCommand(async = "AsyncWriteOnlyDataCommand"),)
115 )
116)]
117impl<DI: WriteOnlyDataCommand> Ssd1315<DI> {
118 /// Creates a new instance of SSD1315.
119 ///
120 /// The `interface` can be either an I2C or an SPI interface.
121 pub fn new(interface: DI) -> Self {
122 Self {
123 interface,
124 buffer: [[0; WIDTH]; 8],
125 }
126 }
127
128 /// Initializes the SSD1315.
129 /// config - Configuration for the SSD1315.
130 pub async fn init(&mut self, config: Ssd1315Config) -> Result<(), DisplayError> {
131 let init_commands = [
132 // Display off
133 Command::DisplayOn(false),
134 // Set display clock divide ratio/oscillator frequency
135 Command::DisplayClockDiv(
136 (config.display_clock_divide_ratio_and_oscillator_freq >> 4) & 0x0F,
137 config.display_clock_divide_ratio_and_oscillator_freq & 0x0F,
138 ),
139 // Set multiplex ratio
140 Command::Multiplex(config.multiplex_ratio),
141 // Set display offset
142 Command::DisplayOffset(config.display_offset),
143 // Set display start line
144 Command::StartLine(config.start_line),
145 // Set segment remap
146 Command::SegmentRemap(config.segment_remap),
147 // Set COM output scan direction
148 Command::ReverseComDir(config.reverse_com),
149 // Set COM pins hardware configuration
150 Command::ComPinConfig(
151 (config.com_pins_hardware_config >> 4) & 0x01 != 0,
152 (config.com_pins_hardware_config >> 5) & 0x01 != 0,
153 ),
154 // Set contrast control
155 Command::Contrast(config.contrast),
156 // Set pre-charge period
157 Command::PreChargePeriod(
158 config.precharge_period & 0x0F,
159 (config.precharge_period >> 4) & 0x0F,
160 ),
161 // Set VCOMH deselect level
162 Command::VcomhDeselect(config.v_comh_select_level),
163 // Entire display ON (resume to RAM content display)
164 Command::AllOn(false),
165 // Set normal/inverse display
166 Command::Invert(config.inverse_display),
167 // Set charge pump
168 Command::ChargePump(config.charge_pump_level),
169 // Display on
170 Command::DisplayOn(true),
171 ];
172
173 for cmd in init_commands {
174 cmd.send(&mut self.interface).await?;
175 }
176 Ok(())
177 }
178
179 /// Flushes the SSD1315 buffer to display its contents on the OLED screen.
180 pub async fn flush(&mut self) -> Result<(), DisplayError> {
181 let pages = [
182 Page::Page0,
183 Page::Page1,
184 Page::Page2,
185 Page::Page3,
186 Page::Page4,
187 Page::Page5,
188 Page::Page6,
189 Page::Page7,
190 ];
191
192 for (page, data) in pages.iter().zip(self.buffer.iter()) {
193 Command::PageStart(*page).send(&mut self.interface).await?;
194 Command::LowerColStart(0).send(&mut self.interface).await?;
195 Command::UpperColStart(0).send(&mut self.interface).await?;
196
197 self.interface.send_data(DataFormat::U8(data)).await?;
198 }
199
200 Ok(())
201 }
202
203 /// Clear the internal buffer.
204 pub fn clear_buffer(&mut self) {
205 self.buffer = [[0; WIDTH]; 8];
206 }
207
208 /// Clear the full screen
209 pub async fn clear(&mut self) -> Result<(), DisplayError> {
210 self.clear_buffer();
211 self.flush().await
212 }
213
214 /// Release the contained interface.
215 pub fn release(self) -> DI {
216 self.interface
217 }
218}
219
220/// Getter methods for the interface and buffer.
221#[maybe_async_cfg::maybe(
222 sync(keep_self),
223 async(
224 feature = "async",
225 idents(WriteOnlyDataCommand(async = "AsyncWriteOnlyDataCommand"),)
226 )
227)]
228impl<DI: WriteOnlyDataCommand> Ssd1315<DI> {
229 /// Get the display interface.
230 pub fn interface(&mut self) -> &DI {
231 &self.interface
232 }
233
234 /// Get the display interface.
235 pub fn interface_mut(&mut self) -> &mut DI {
236 &mut self.interface
237 }
238
239 /// Get a immutable reference to the SSD1315 buffer.
240 pub fn buffer(&mut self) -> &[[u8; WIDTH]; 8] {
241 &self.buffer
242 }
243
244 /// Get a mutable reference to the SSD1315 buffer.
245 pub fn buffer_mut(&mut self) -> &mut [[u8; WIDTH]; 8] {
246 &mut self.buffer
247 }
248}
249
250/// Additional methods for controlling the display.
251#[maybe_async_cfg::maybe(
252 sync(keep_self),
253 async(
254 feature = "async",
255 idents(Command, WriteOnlyDataCommand(async = "AsyncWriteOnlyDataCommand"),)
256 )
257)]
258impl<DI: WriteOnlyDataCommand> Ssd1315<DI> {
259 /// Turn the display on or off. The display can be drawn to and retains all
260 /// of its memory even while off.
261 pub async fn set_display_on(&mut self, on: bool) -> Result<(), DisplayError> {
262 Command::DisplayOn(on).send(&mut self.interface).await
263 }
264
265 /// Set the screen pixel on/off inversion
266 pub async fn set_invert(&mut self, invert: bool) -> Result<(), DisplayError> {
267 Command::Invert(invert).send(&mut self.interface).await
268 }
269
270 /// Change the display brightness.
271 ///
272 /// `brightness` should be a value between 0x00 and 0xFF.
273 pub async fn set_brightness(&mut self, brightness: u8) -> Result<(), DisplayError> {
274 Command::PreChargePeriod(1, brightness)
275 .send(&mut self.interface)
276 .await?;
277 Command::Contrast(brightness)
278 .send(&mut self.interface)
279 .await
280 }
281
282 /// Set the column address in the framebuffer of the display where any sent data should be
283 /// drawn.
284 pub async fn set_column(&mut self, column: u8) -> Result<(), DisplayError> {
285 Command::LowerColStart(column)
286 .send(&mut self.interface)
287 .await?;
288 Command::UpperColStart(column)
289 .send(&mut self.interface)
290 .await
291 }
292
293 /// Set the page address (row 8px high) in the framebuffer of the display where any sent data
294 /// should be drawn.
295 ///
296 /// Note that the parameter is in pixels, but the page will be set to the start of the 8px
297 /// row which contains the passed-in row.
298 pub async fn set_row(&mut self, row: u8) -> Result<(), DisplayError> {
299 Command::PageStart(row.into())
300 .send(&mut self.interface)
301 .await
302 }
303
304 /// Send a raw buffer to the display.
305 pub async fn draw(&mut self, buffer: &[u8]) -> Result<(), DisplayError> {
306 self.interface.send_data(DataFormat::U8(buffer)).await
307 }
308}