#![no_std]
#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![deny(warnings)]
#![deny(missing_copy_implementations)]
#![deny(trivial_casts)]
#![deny(trivial_numeric_casts)]
#![deny(unsafe_code)]
#![deny(unstable_features)]
#![deny(unused_import_braces)]
#![deny(unused_qualifications)]
#![deny(rustdoc::broken_intra_doc_links)]
#![allow(async_fn_in_trait)]
mod brightness;
pub mod command;
mod error;
mod i2c_interface;
pub mod mode;
pub mod prelude;
pub mod rotation;
pub mod size;
#[doc(hidden)]
pub mod test_helpers;
use core::convert::Infallible;
pub use crate::i2c_interface::I2CDisplayInterface;
use crate::mode::BasicMode;
use brightness::Brightness;
#[cfg(feature = "async")]
use command::CommandAsync;
use command::{AddrMode, Command, VcomhLevel};
#[cfg(feature = "async")]
use display_interface::AsyncWriteOnlyDataCommand;
use display_interface::{DataFormat::U8, DisplayError, WriteOnlyDataCommand};
use embedded_hal::{delay::DelayNs, digital::OutputPin};
#[cfg(feature = "async")]
use embedded_hal_async::delay::DelayNs as DelayNsAsync;
use error::Error;
use mode::{BufferedGraphicsMode, TerminalMode};
#[cfg(feature = "async")]
use mode::{BufferedGraphicsModeAsync, TerminalModeAsync};
use rotation::DisplayRotation;
use size::DisplaySize;
#[cfg(feature = "async")]
use size::DisplaySizeAsync;
#[maybe_async_cfg::maybe(sync(keep_self), async(feature = "async"))]
#[derive(Copy, Clone, Debug)]
pub struct Ssd1306<DI, SIZE, MODE> {
interface: DI,
mode: MODE,
size: SIZE,
addr_mode: AddrMode,
rotation: DisplayRotation,
}
#[maybe_async_cfg::maybe(
sync(keep_self,),
async(feature = "async", idents(DisplaySize(async = "DisplaySizeAsync")))
)]
impl<DI, SIZE> Ssd1306<DI, SIZE, BasicMode>
where
SIZE: DisplaySize,
{
pub fn new(interface: DI, size: SIZE, rotation: DisplayRotation) -> Self {
Self {
interface,
size,
addr_mode: AddrMode::Page,
mode: BasicMode,
rotation,
}
}
}
#[maybe_async_cfg::maybe(
sync(keep_self,),
async(
feature = "async",
idents(
DisplaySize(async = "DisplaySizeAsync"),
BufferedGraphicsMode(async = "BufferedGraphicsModeAsync"),
TerminalMode(async = "TerminalModeAsync"),
)
)
)]
impl<DI, SIZE, MODE> Ssd1306<DI, SIZE, MODE>
where
SIZE: DisplaySize,
{
fn into_mode<MODE2>(self, mode: MODE2) -> Ssd1306<DI, SIZE, MODE2> {
Ssd1306 {
mode,
addr_mode: self.addr_mode,
interface: self.interface,
size: self.size,
rotation: self.rotation,
}
}
pub fn into_buffered_graphics_mode(self) -> Ssd1306<DI, SIZE, BufferedGraphicsMode<SIZE>> {
self.into_mode(BufferedGraphicsMode::new())
}
pub fn into_terminal_mode(self) -> Ssd1306<DI, SIZE, TerminalMode> {
self.into_mode(TerminalMode::new())
}
}
#[maybe_async_cfg::maybe(
sync(keep_self),
async(
feature = "async",
idents(
Command(async = "CommandAsync"),
DisplaySize(async = "DisplaySizeAsync"),
WriteOnlyDataCommand(async = "AsyncWriteOnlyDataCommand"),
)
)
)]
impl<DI, SIZE, MODE> Ssd1306<DI, SIZE, MODE>
where
DI: WriteOnlyDataCommand,
SIZE: DisplaySize,
{
pub async fn init_with_addr_mode(&mut self, mode: AddrMode) -> Result<(), DisplayError> {
let rotation = self.rotation;
Command::DisplayOn(false).send(&mut self.interface).await?;
Command::DisplayClockDiv(0x8, 0x0)
.send(&mut self.interface)
.await?;
Command::Multiplex(SIZE::HEIGHT - 1)
.send(&mut self.interface)
.await?;
Command::DisplayOffset(0).send(&mut self.interface).await?;
Command::StartLine(0).send(&mut self.interface).await?;
Command::ChargePump(true).send(&mut self.interface).await?;
Command::AddressMode(mode).send(&mut self.interface).await?;
self.size.configure(&mut self.interface).await?;
self.set_rotation(rotation).await?;
self.set_brightness(Brightness::default()).await?;
Command::VcomhDeselect(VcomhLevel::Auto)
.send(&mut self.interface)
.await?;
Command::AllOn(false).send(&mut self.interface).await?;
Command::Invert(false).send(&mut self.interface).await?;
Command::EnableScroll(false)
.send(&mut self.interface)
.await?;
Command::DisplayOn(true).send(&mut self.interface).await?;
self.addr_mode = mode;
Ok(())
}
pub async fn set_addr_mode(&mut self, mode: AddrMode) -> Result<(), DisplayError> {
Command::AddressMode(mode).send(&mut self.interface).await?;
self.addr_mode = mode;
Ok(())
}
pub async fn bounded_draw(
&mut self,
buffer: &[u8],
disp_width: usize,
upper_left: (u8, u8),
lower_right: (u8, u8),
) -> Result<(), DisplayError> {
Self::flush_buffer_chunks(
&mut self.interface,
buffer,
disp_width,
upper_left,
lower_right,
)
.await
}
pub async fn draw(&mut self, buffer: &[u8]) -> Result<(), DisplayError> {
self.interface.send_data(U8(buffer)).await
}
pub fn dimensions(&self) -> (u8, u8) {
match self.rotation {
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => (SIZE::WIDTH, SIZE::HEIGHT),
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => (SIZE::HEIGHT, SIZE::WIDTH),
}
}
pub fn rotation(&self) -> DisplayRotation {
self.rotation
}
pub async fn set_rotation(&mut self, rotation: DisplayRotation) -> Result<(), DisplayError> {
self.rotation = rotation;
match rotation {
DisplayRotation::Rotate0 => {
Command::SegmentRemap(true)
.send(&mut self.interface)
.await?;
Command::ReverseComDir(true)
.send(&mut self.interface)
.await?;
}
DisplayRotation::Rotate90 => {
Command::SegmentRemap(false)
.send(&mut self.interface)
.await?;
Command::ReverseComDir(true)
.send(&mut self.interface)
.await?;
}
DisplayRotation::Rotate180 => {
Command::SegmentRemap(false)
.send(&mut self.interface)
.await?;
Command::ReverseComDir(false)
.send(&mut self.interface)
.await?;
}
DisplayRotation::Rotate270 => {
Command::SegmentRemap(true)
.send(&mut self.interface)
.await?;
Command::ReverseComDir(false)
.send(&mut self.interface)
.await?;
}
};
Ok(())
}
pub async fn set_mirror(&mut self, mirror: bool) -> Result<(), DisplayError> {
if mirror {
match self.rotation {
DisplayRotation::Rotate0 => {
Command::SegmentRemap(false)
.send(&mut self.interface)
.await?;
Command::ReverseComDir(true)
.send(&mut self.interface)
.await?;
}
DisplayRotation::Rotate90 => {
Command::SegmentRemap(false)
.send(&mut self.interface)
.await?;
Command::ReverseComDir(false)
.send(&mut self.interface)
.await?;
}
DisplayRotation::Rotate180 => {
Command::SegmentRemap(true)
.send(&mut self.interface)
.await?;
Command::ReverseComDir(false)
.send(&mut self.interface)
.await?;
}
DisplayRotation::Rotate270 => {
Command::SegmentRemap(true)
.send(&mut self.interface)
.await?;
Command::ReverseComDir(true)
.send(&mut self.interface)
.await?;
}
};
} else {
self.set_rotation(self.rotation).await?;
}
Ok(())
}
pub async fn set_brightness(&mut self, brightness: Brightness) -> Result<(), DisplayError> {
Command::PreChargePeriod(1, brightness.precharge)
.send(&mut self.interface)
.await?;
Command::Contrast(brightness.contrast)
.send(&mut self.interface)
.await
}
pub async fn set_display_on(&mut self, on: bool) -> Result<(), DisplayError> {
Command::DisplayOn(on).send(&mut self.interface).await
}
pub async fn set_draw_area(
&mut self,
start: (u8, u8),
end: (u8, u8),
) -> Result<(), DisplayError> {
Command::ColumnAddress(start.0, end.0.saturating_sub(1))
.send(&mut self.interface)
.await?;
if self.addr_mode != AddrMode::Page {
Command::PageAddress(start.1.into(), (end.1.saturating_sub(1)).into())
.send(&mut self.interface)
.await?;
}
Ok(())
}
pub async fn set_column(&mut self, column: u8) -> Result<(), DisplayError> {
Command::ColStart(column).send(&mut self.interface).await
}
pub async fn set_row(&mut self, row: u8) -> Result<(), DisplayError> {
Command::PageStart(row.into())
.send(&mut self.interface)
.await
}
pub async fn set_invert(&mut self, invert: bool) -> Result<(), DisplayError> {
Command::Invert(invert).send(&mut self.interface).await
}
async fn flush_buffer_chunks(
interface: &mut DI,
buffer: &[u8],
disp_width: usize,
upper_left: (u8, u8),
lower_right: (u8, u8),
) -> Result<(), DisplayError> {
let num_pages = ((lower_right.1 - upper_left.1) / 8) as usize + 1;
let starting_page = (upper_left.1 / 8) as usize;
let page_lower = upper_left.0 as usize;
let page_upper = lower_right.0 as usize;
for c in buffer
.chunks(disp_width)
.skip(starting_page)
.take(num_pages)
.map(|s| &s[page_lower..page_upper])
{
interface.send_data(U8(c)).await?
}
Ok(())
}
pub fn release(self) -> DI {
self.interface
}
}
#[maybe_async_cfg::maybe(
sync(keep_self),
async(
feature = "async",
idents(
Command(async = "CommandAsync"),
DisplaySize(async = "DisplaySizeAsync"),
WriteOnlyDataCommand(async = "AsyncWriteOnlyDataCommand"),
DelayNs(async = "DelayNsAsync")
)
)
)]
impl<DI, SIZE, MODE> Ssd1306<DI, SIZE, MODE> {
pub async fn reset<RST, DELAY>(
&mut self,
rst: &mut RST,
delay: &mut DELAY,
) -> Result<(), Error<Infallible, RST::Error>>
where
RST: OutputPin,
DELAY: DelayNs,
{
async fn inner_reset<RST, DELAY>(rst: &mut RST, delay: &mut DELAY) -> Result<(), RST::Error>
where
RST: OutputPin,
DELAY: DelayNs,
{
rst.set_high()?;
delay.delay_ms(1).await;
rst.set_low()?;
delay.delay_ms(10).await;
rst.set_high()
}
inner_reset(rst, delay).await.map_err(Error::Pin)
}
}