1#![no_std]
26
27use embedded_hal::digital::OutputPin;
28
29pub mod blocking;
30
31pub mod cmds {
32 pub const SET_COLUMN_ADDRESS: u8 = 0x15;
34 pub const SET_ROW_ADDRESS: u8 = 0x75;
35 pub const SET_CONTRAST_CURRENT: u8 = 0x81;
36 pub const SET_REMAP: u8 = 0xA0;
37 pub const SET_DISPLAY_START_LINE: u8 = 0xA1;
38 pub const SET_DISPLAY_OFFSET: u8 = 0xA2;
39 pub const SET_DISPLAY_MODE: u8 = 0xA4;
40 pub const SET_MULTIPLEX_RATIO: u8 = 0xA8;
41 pub const FUNCTION_SELECTION_A: u8 = 0xAB;
42 pub const SET_DISPLAY_ON: u8 = 0xAF;
43 pub const SET_DISPLAY_OFF: u8 = 0xAE;
44 pub const SET_PHASE_LENGTH: u8 = 0xB1;
45 pub const SET_FRONT_CLOCK_DIVIDER: u8 = 0xB3;
46 pub const SET_GPIO: u8 = 0xB5;
47 pub const SET_SECOND_PRECHARGE_PERIOD: u8 = 0xB6;
48 pub const SET_GRAY_SCALE_TABLE: u8 = 0xB8;
49 pub const SELECT_DEFAULT_LINEAR_GRAY_SCALE_TABLE: u8 = 0xB9;
50 pub const SET_PRECHARGE_VOLTAGE: u8 = 0xBC;
51 pub const SET_VCOMH_VOLTAGE: u8 = 0xBE;
52 pub const FUNCTION_SELECTION_B: u8 = 0xD5;
53 pub const SET_COMMAND_LOCK: u8 = 0xFD;
54
55 pub const HORIZONTAL_SCROLL_SETUP: u8 = 0x26;
57 pub const HORIZONTAL_SCROLL_SETUP_ALT: u8 = 0x27; pub const DEACTIVATE_SCROLL: u8 = 0x2E;
59 pub const ACTIVATE_SCROLL: u8 = 0x2F;
60}
61
62pub(crate) const WIDTH: u8 = 128;
63pub(crate) const HEIGHT: u8 = 128;
64
65#[derive(Debug)]
66pub enum Error<BusError> {
67 Bus(BusError),
68}
69
70impl<E> From<E> for Error<E> {
71 fn from(e: E) -> Self {
72 Error::Bus(e)
73 }
74}
75
76pub struct SSD1327<SPI: embedded_hal_async::spi::SpiDevice, DC: OutputPin> {
86 spi: SPI,
87 dc: DC,
88}
89
90impl<SPI: embedded_hal_async::spi::SpiDevice, DC: OutputPin> SSD1327<SPI, DC> {
91 pub fn new(spi: SPI, dc: DC) -> Self {
92 Self { spi, dc }
93 }
94
95 pub async fn init(&mut self) -> Result<(), Error<SPI::Error>> {
96 self.write_command(&[cmds::SET_DISPLAY_OFF]).await?;
97 self.write_command(&[cmds::SET_COLUMN_ADDRESS, 0x00, WIDTH / 8 * 4 - 1])
99 .await?;
100 self.write_command(&[cmds::SET_ROW_ADDRESS, 0x00, HEIGHT - 1]).await?;
102 self.write_command(&[cmds::SET_CONTRAST_CURRENT, 0x80]).await?;
103
104 self.write_command(&[cmds::SET_REMAP, 0x51]).await?;
106
107 self.write_command(&[cmds::SET_DISPLAY_START_LINE, 0x00]).await?;
108 self.write_command(&[cmds::SET_DISPLAY_OFFSET, 0x00]).await?;
109
110 self.write_command(&[cmds::SET_MULTIPLEX_RATIO, 0x7F]).await?;
111 self.write_command(&[cmds::SET_PHASE_LENGTH, 0x11]).await?; self.write_command(&[cmds::SELECT_DEFAULT_LINEAR_GRAY_SCALE_TABLE])
118 .await?;
119
120 self.write_command(&[cmds::SET_FRONT_CLOCK_DIVIDER, 0x00]).await?;
121 self.write_command(&[cmds::FUNCTION_SELECTION_A, 0x01]).await?;
122 self.write_command(&[cmds::SET_SECOND_PRECHARGE_PERIOD, 0x08]).await?;
123 self.write_command(&[cmds::SET_VCOMH_VOLTAGE, 0x0f]).await?;
124 self.write_command(&[cmds::SET_PRECHARGE_VOLTAGE, 0x08]).await?;
125 self.write_command(&[cmds::FUNCTION_SELECTION_B, 0x62]).await?;
126 self.write_command(&[cmds::SET_COMMAND_LOCK, 0x12]).await?;
127 self.write_command(&[cmds::SET_DISPLAY_MODE]).await?; self.write_command(&[cmds::SET_DISPLAY_ON]).await?;
129
130 self.write_command(&[cmds::DEACTIVATE_SCROLL]).await?;
131
132 Ok(())
133 }
134
135 pub async fn write_framebuffer(&mut self, fb: &[u8]) -> Result<(), Error<SPI::Error>> {
136 self.write_data(fb).await?;
141 Ok(())
142 }
143
144 async fn write_command(&mut self, cmd: &[u8]) -> Result<(), Error<SPI::Error>> {
145 let _ = self.dc.set_low().unwrap();
146 self.spi.write(cmd).await?;
147 Ok(())
148 }
149
150 async fn write_data(&mut self, data: &[u8]) -> Result<(), Error<SPI::Error>> {
151 let _ = self.dc.set_high().unwrap();
152 self.spi.write(data).await?;
153 let _ = self.dc.set_low();
154 Ok(())
155 }
156}