use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiBus}; use rp_pico::hal::{ gpio::{FunctionSio, Pin, PinId, PullDown, SioOutput}, spi::{Enabled, SpiDevice, ValidSpiPinout}, Spi, Timer, }; use crate::canvas::Canvas; pub struct Step { param_cnt: usize, command: u8, params: [u8; 4], delay: Option<u32>, } impl Step { pub fn send_command<D, Pinout, P>( &self, spi: &mut Spi<Enabled, D, Pinout, 8>, data_command: &mut Pin<P, FunctionSio<SioOutput>, PullDown>, ) where D: SpiDevice, Pinout: ValidSpiPinout<D>, P: PinId, { let _ = data_command.set_low(); let _ = spi.write(&[self.command]); if self.param_cnt > 0 { let _ = data_command.set_high(); let _ = spi.write(&self.params[0..self.param_cnt]); } } } const NOP: u8 = 0x00; const SWRESET: Step = Step { param_cnt: 0, command: 0x01, params: [0, 0, 0, 0], delay: Some(150), }; const SLPOUT: Step = Step { param_cnt: 0, command: 0x11, params: [0, 0, 0, 0], delay: Some(10), }; const COLMOD: u8 = 0x3a; const MADCTL: Step = Step { param_cnt: 1, command: 0x36, params: [0x00, 0, 0, 0], delay: None, }; const CASET: u8 = 0x2a; const RASET: u8 = 0x2b; const INVON: Step = Step { param_cnt: 0, command: 0x21, params: [0, 0, 0, 0], delay: Some(10), }; const NORON: Step = Step { param_cnt: 0, command: 0x13, params: [0, 0, 0, 0], delay: Some(10), }; const DISPOFF: Step = Step { param_cnt: 0, command: 0x28, params: [0, 0, 0, 0], delay: Some(10), }; const DISPON: Step = Step { param_cnt: 0, command: 0x29, params: [0, 0, 0, 0], delay: Some(10), }; const RAMWR: u8 = 0x2c; // Adafruit setup instructions // SWRESET (0x01), 150ms delay // SLPOUT (0x11), 10ms delay // COLMOD (0x3a) 0x55 (65K RGB, 16bit/pixel), 10ms delay // MADCTL (0x36) 0x00, // memory data access control, RGB // CASET 0x00, 0, 0, 170, // column address set, 4 parameters // 0x00, 0x00 indicates xstart is 0 // 0x00, 170 indicates xend is 170 // RASET 0x00, 0, 320 >> 8, 320 & 0xFF, // row address set, 4 parameters // 0x00, 0x00 indicates ystart is 0 // 3230 >> 8, 320 & 0xff indicates that 320 is the last y address // INVON, 10ms delay // invert the display // NORON, 10ms delay // normal display mode // DISPON, 10ms delay // turn the display on pub const SETUP_PROGRAM: [Step; 8] = [ SWRESET, SLPOUT, Step { param_cnt: 1, command: COLMOD, params: [0x66, 0, 0, 0], delay: Some(10), }, MADCTL, Step { param_cnt: 4, command: CASET, params: [0, 35, 0, 204], delay: None, }, /* Step { param_cnt: 4, command: RASET, params: [0, 0, (320 >> 8) as u8, (320 & 0xff) as u8], delay: None, }, */ INVON, NORON, DISPON, ]; pub struct ST7789Display< BoardSelectId: PinId, DataCommandId: PinId, D: SpiDevice, Pinout: ValidSpiPinout<D>, > { inner: ST7789DisplayEnabled<BoardSelectId, DataCommandId, D, Pinout>, } impl<BoardSelectId: PinId, DataCommandId: PinId, D: SpiDevice, Pinout: ValidSpiPinout<D>> ST7789Display<BoardSelectId, DataCommandId, D, Pinout> { pub fn new( board_select: Pin<BoardSelectId, FunctionSio<SioOutput>, PullDown>, data_command: Pin<DataCommandId, FunctionSio<SioOutput>, PullDown>, spi: Spi<Enabled, D, Pinout, 8>, ) -> Self { Self { inner: ST7789DisplayEnabled { board_select, data_command, spi, }, } } pub fn acquire( &mut self, ) -> &mut ST7789DisplayEnabled<BoardSelectId, DataCommandId, D, Pinout> { self.inner.board_select.set_low(); &mut self.inner } } pub struct ST7789DisplayEnabled< BoardSelectId: PinId, DataCommandId: PinId, D: SpiDevice, Pinout: ValidSpiPinout<D>, > { board_select: Pin<BoardSelectId, FunctionSio<SioOutput>, PullDown>, data_command: Pin<DataCommandId, FunctionSio<SioOutput>, PullDown>, spi: Spi<Enabled, D, Pinout, 8>, } impl<BoardSelectId: PinId, DataCommandId: PinId, D: SpiDevice, Pinout: ValidSpiPinout<D>> ST7789DisplayEnabled<BoardSelectId, DataCommandId, D, Pinout> { pub fn send_command(&mut self, step: &Step, timer: &mut Timer) { step.send_command(&mut self.spi, &mut self.data_command); if let Some(delay) = step.delay { timer.delay_ms(delay); } } pub fn blit_frame(&mut self, frame: &impl Canvas) { // let _ = DISPOFF.send_command(&mut self.spi, &mut self.data_command); let _ = self.data_command.set_low(); let _ = self.spi.write(&[RAMWR]); let _ = self.data_command.set_high(); let _ = self.spi.write(frame.buf()); // let _ = DISPON.send_command(&mut self.spi, &mut self.data_command); } pub fn blit_dirty(&mut self, frame: &impl Canvas) { if let Some((start_x, start_y, end_x, end_y)) = frame.dirty() { let end_y = end_y + 60; Step { param_cnt: 4, command: 0x30, params: [ (start_y >> 8 & 0xff) as u8, (start_y & 0xff) as u8, (end_y >> 8 & 0xff) as u8, (end_y & 0xff) as u8, ], delay: None, } .send_command(&mut self.spi, &mut self.data_command); Step { param_cnt: 0, command: 0x12, params: [0, 0, 0, 0], delay: None, } .send_command(&mut self.spi, &mut self.data_command); let _ = self.data_command.set_low(); let _ = self.spi.write(&[RAMWR]); let _ = self.data_command.set_high(); for row in frame.partial(0, start_y, frame.width(), end_y) { let _ = self.spi.write(row); } Step { param_cnt: 0, command: 0x13, params: [0, 0, 0, 0], delay: None, } .send_command(&mut self.spi, &mut self.data_command); } } } impl<BoardSelectId: PinId, DataCommandId: PinId, D: SpiDevice, Pinout: ValidSpiPinout<D>> Drop for ST7789DisplayEnabled<BoardSelectId, DataCommandId, D, Pinout> { fn drop(&mut self) { self.board_select.set_high(); } }