ws_oled_driver/
display.rs

1use std::{thread::sleep, time::Duration};
2pub type Buffer = Vec<u8>;
3
4use anyhow::Result;
5use rppal::{
6    gpio::{Gpio, OutputPin},
7    spi::Spi,
8};
9
10const BUS_CLOCK_SPEED: u32 = 8_000_000;
11const RST_PIN: u8 = 25;
12const DC_PIN: u8 = 24;
13const CS_PIN: u8 = 8;
14const BL_PIN: u8 = 18;
15const WIDTH: u8 = 132;
16const HEIGHT: u8 = 64;
17
18/// Protocol used to access the device. Currently only supports SPI
19#[derive(Debug)]
20pub enum Protocol {
21    SPI,
22    I2C,
23}
24
25#[derive(Debug)]
26pub struct Display {
27    pub width: u8,
28    pub height: u8,
29    pub dc_pin: OutputPin,
30    pub cs_pin: OutputPin,
31    pub rst_pin: OutputPin,
32    pub bl_pin: OutputPin,
33    pub protocol: Protocol,
34    pub bus: Spi,
35    pub memory: Buffer,
36}
37
38impl Display {
39    /// Write a command byte to the device. The byte should be of the type `&[u8]`. The DC pin is
40    /// set low to indicate that the byte being written is a command byte.
41    pub fn write_command(&mut self, byte: &[u8]) -> Result<()> {
42        match self.protocol {
43            Protocol::SPI => {
44                self.dc_pin.set_low();
45                self.spi_write_byte(byte)
46            }
47            Protocol::I2C => self.i2c_write_byte(byte),
48        }
49    }
50
51    /// Write a data byte to the device. The byte should be of the type `&[u8]`. The DC pin is set
52    /// high to indicate that the byte being written is a data byte
53    pub fn write_data(&mut self, byte: &[u8]) -> Result<()> {
54        match self.protocol {
55            Protocol::SPI => {
56                self.dc_pin.set_high();
57                self.spi_write_byte(byte)
58            }
59            Protocol::I2C => self.i2c_write_byte(byte),
60        }
61    }
62
63    /// Raw SPI write byte function. DO NOT USE. use `write_command` or `write_data` instead.
64    pub fn spi_write_byte(&mut self, byte: &[u8]) -> Result<()> {
65        self.bus.write(byte)?;
66
67        Ok(())
68    }
69
70    /// TODO: Add Support for I2C
71    pub fn i2c_write_byte(&self, _byte: &[u8]) -> Result<()> {
72        /* TODO: IMPLEMENT I2C */
73        Ok(())
74    }
75
76    /// Reset the display.
77    pub fn reset(&mut self) -> Result<()> {
78        self.rst_pin.set_high();
79        sleep(Duration::from_millis(100));
80        self.rst_pin.set_low();
81        sleep(Duration::from_millis(100));
82        self.rst_pin.set_high();
83        sleep(Duration::from_millis(100));
84
85        Ok(())
86    }
87
88    /// The Graphics buffer is stored in the `Display { memory: Vec<u8> }` field. The
89    /// `ws_oled_driver::gfx` library functions work on the memory field. It writes the pixes onto the
90    /// memory field. The `render` function dumps the `memory` field onto the display.
91    pub fn render(&mut self) -> Result<()> {
92        for page in 0..8 {
93            self.write_command(&[0xB0 + page])?;
94            self.write_command(&[0x02])?;
95            self.write_command(&[0x10])?;
96            sleep(Duration::from_millis(10));
97            match self.protocol {
98                Protocol::I2C => todo!("implement i2c"),
99                Protocol::SPI => {
100                    self.dc_pin.set_high();
101                    for index in 0..=(self.width as usize) {
102                        let byte = self.memory[index + self.width as usize * page as usize];
103                        self.spi_write_byte(&[byte])?;
104                    }
105                }
106            }
107        }
108
109        Ok(())
110    }
111
112    /// Initializes the display device.
113    pub fn initialize(&mut self) -> Result<()> {
114        self.reset()?;
115        self.write_command(&[0xAE])?;
116        self.write_command(&[0x02])?;
117        self.write_command(&[0x10])?;
118        self.write_command(&[0x40])?;
119        self.write_command(&[0x81])?;
120        self.write_command(&[0xA0])?;
121        self.write_command(&[0xC0])?;
122        self.write_command(&[0xA6])?;
123        self.write_command(&[0xA8])?;
124        self.write_command(&[0x3F])?;
125        self.write_command(&[0xD3])?;
126        self.write_command(&[0x00])?;
127        self.write_command(&[0xd5])?;
128        self.write_command(&[0x80])?;
129        self.write_command(&[0xD9])?;
130        self.write_command(&[0xF1])?;
131        self.write_command(&[0xDA])?;
132        self.write_command(&[0x12])?;
133        self.write_command(&[0xDB])?;
134        self.write_command(&[0x40])?;
135        self.write_command(&[0x20])?;
136        self.write_command(&[0x02])?;
137        self.write_command(&[0xA4])?;
138        self.write_command(&[0xA6])?;
139        sleep(Duration::from_millis(100));
140        self.write_command(&[0xAF])?; // --turn on oled panel
141        Ok(())
142    }
143
144    /// Initializes SPI protocol for the device.
145    pub fn new() -> Result<Self> {
146        /* INITIALIZE GPIO */
147        let gpio = Gpio::new()?;
148        let rst_pin = gpio.get(RST_PIN)?.into_output();
149        let mut dc_pin = gpio.get(DC_PIN)?.into_output();
150        let mut cs_pin = gpio.get(CS_PIN)?.into_output();
151        let mut bl_pin = gpio.get(BL_PIN)?.into_output();
152
153        cs_pin.set_low();
154        bl_pin.set_high();
155        dc_pin.set_low();
156
157        let bus: Spi = Spi::new(
158            rppal::spi::Bus::Spi0,
159            rppal::spi::SlaveSelect::Ss0,
160            BUS_CLOCK_SPEED,
161            rppal::spi::Mode::Mode0,
162        )?;
163
164        Ok(Self {
165            width: WIDTH,
166            height: HEIGHT,
167            protocol: Protocol::SPI,
168            rst_pin,
169            dc_pin,
170            cs_pin,
171            bl_pin,
172            bus,
173            memory: vec![0; WIDTH as usize * HEIGHT as usize],
174        })
175    }
176}