Skip to main content

edrv_ssd1327/
lib.rs

1//! Driver for SSD1327.
2//!
3//! # Examples
4//!
5//! ```
6//! use embedded_graphics::framebuffer::Framebuffer;
7//!
8//! let dc = Output::new(r.dc, Level::Low, Default::default());
9//! let cs = Output::new(r.cs, Level::High, Default::default());
10//!
11//! let mut config = hal::spi::Config::default();
12//! config.frequency = Hertz::mhz(20);
13//! let spi = hal::spi::Spi::new_txonly(r.spi1, r.sclk, r.mosi, r.dma_ch0, config);
14//! let spi_bus = embassy_sync::mutex::Mutex::<NoopRawMutex, _>::new(spi);
15//! let spi_dev = embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice::new(&spi_bus, cs);
16//! let mut ssd1327 = edrv_ssd1327::SSD1327::new(spi_dev, dc);
17//! ssd1327.init().await.unwrap();
18//! let mut fb = Framebuffer::<Gray4, _, LittleEndian, 128, 128, { 128 * 128 / 2 }>::new();
19//!
20//! // Draw something on the framebuffer
21//!
22//! ssd1327.write_framebuffer(fb.data()).await.unwrap();
23//! ```
24
25#![no_std]
26
27use embedded_hal::digital::OutputPin;
28
29pub mod blocking;
30
31pub mod cmds {
32    // Fundamental Commands
33    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    // Graphic Acceleration Commands
56    pub const HORIZONTAL_SCROLL_SETUP: u8 = 0x26;
57    pub const HORIZONTAL_SCROLL_SETUP_ALT: u8 = 0x27; // Alternative command for Horizontal Scroll Setup
58    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
76/// SSD1327 driver
77///
78/// Framebuffer format:
79///
80/// ```
81/// let mut fb = Framebuffer::<Gray4, _, LittleEndian, 128, 128, { embedded_graphics::framebuffer::buffer_size::<Gray4>(128, 128) }>::new();
82/// // or
83/// let mut fb = Framebuffer::<Gray4, _, LittleEndian, 128, 128, { 128 * 128 / 2 }>::new();
84/// ```
85pub 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        // 0x3F
98        self.write_command(&[cmds::SET_COLUMN_ADDRESS, 0x00, WIDTH / 8 * 4 - 1])
99            .await?;
100        // 0x7F
101        self.write_command(&[cmds::SET_ROW_ADDRESS, 0x00, HEIGHT - 1]).await?;
102        self.write_command(&[cmds::SET_CONTRAST_CURRENT, 0x80]).await?;
103
104        // address remap
105        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?; // gray scale tune
112
113        // gamma setting
114        // 0xb8: SET_GRAY_SCALE_TABLE, [u8; 15]
115        // 0xb9: SET_DEFAULT_LINEAR_GRAY_SCALE_TABLE
116        //self.send_cmd_data(0xb8, &[1,2,30,40,5,6,7,8,9,10,11,12,13,14,0b11111])?;
117        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?; // display mode normal
128        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_command(&[cmds::SET_COLUMN_ADDRESS, 0x00, 0x3F])
137        //    .await?;
138        // self.write_command(&[cmds::SET_ROW_ADDRESS, 0x00, 0x7F])
139        //    .await?;
140        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}