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}