#![no_std]
pub mod command;
use crate::command::*;
use self::embedded_graphics_core::{
draw_target::DrawTarget,
pixelcolor::{
raw::{RawData, RawU16},
Rgb565,
},
prelude::*,
primitives::Rectangle,
};
use embedded_graphics_core;
use embedded_hal::delay::DelayNs;
use embedded_hal::digital::OutputPin;
use embedded_hal::spi;
pub struct ST7735S<SPI, DC, RST, const W: u16, const H: u16, const DX: u16, const DY: u16>
where
SPI: spi::SpiDevice,
DC: OutputPin,
RST: OutputPin,
{
spi: SPI,
dc: DC,
rst: RST,
}
#[derive(Clone, Copy)]
pub enum Orientation {
Portrait = 0x00,
Landscape = 0x60,
PortraitSwapped = 0xC0,
LandscapeSwapped = 0xA0,
}
impl<SPI, DC, RST, const W: u16, const H: u16, const DX: u16, const DY: u16>
ST7735S<SPI, DC, RST, W, H, DX, DY>
where
SPI: spi::SpiDevice,
DC: OutputPin,
RST: OutputPin,
{
pub fn new(spi: SPI, dc: DC, rst: RST) -> Self {
let display = ST7735S { spi, dc, rst };
display
}
pub fn init<DELAY>(
&mut self,
delay: &mut DELAY,
orientation: Orientation,
rgb: bool,
inverted: bool,
) -> Result<(), ()>
where
DELAY: DelayNs,
{
self.hard_reset(delay)?;
self.write_command(Instruction::SLPOUT, &[])?;
delay.delay_ms(120);
self.write_command(Instruction::FRMCTR1, &[0x05, 0x3C, 0x3C])?;
self.write_command(Instruction::FRMCTR2, &[0x05, 0x3C, 0x3C])?;
self.write_command(Instruction::FRMCTR3, &[0x05, 0x3C, 0x3C, 0x05, 0x3C, 0x3C])?;
self.write_command(Instruction::INVCTR, &[0x03])?;
self.write_command(Instruction::PWCTR1, &[0x28, 0x08, 0x04])?;
self.write_command(Instruction::PWCTR2, &[0xC0])?;
self.write_command(Instruction::PWCTR3, &[0x0D, 0x00])?;
self.write_command(Instruction::PWCTR4, &[0x8D, 0x2A])?;
self.write_command(Instruction::PWCTR5, &[0x8D, 0xEE])?;
self.write_command(Instruction::VMCTR1, &[0x1A])?;
if rgb {
self.write_command(Instruction::MADCTL, &[0xC0])?; } else {
self.write_command(Instruction::MADCTL, &[0xC8])?;
}
self.write_command(
Instruction::GMCTRP1,
&[
0x04, 0x22, 0x07, 0x0A, 0x2E, 0x30, 0x25, 0x2A, 0x28, 0x26, 0x2E, 0x3A, 0x00, 0x01,
0x03, 0x13,
],
)?;
self.write_command(
Instruction::GMCTRN1,
&[
0x04, 0x16, 0x06, 0x0D, 0x2D, 0x26, 0x23, 0x27, 0x27, 0x25, 0x2D, 0x3B, 0x00, 0x01,
0x04, 0x13,
],
)?;
self.write_command(Instruction::COLMOD, &[0x05])?;
if inverted {
self.write_command(Instruction::INVON, &[])?;
} else {
self.write_command(Instruction::INVOFF, &[])?;
}
self.set_orientation(&orientation, rgb)?;
delay.delay_ms(200);
Ok(())
}
pub fn on(&mut self) -> Result<(), ()> {
self.write_command(Instruction::DISPON, &[])
}
pub fn off(&mut self) -> Result<(), ()> {
self.write_command(Instruction::DISPOFF, &[])
}
pub fn hard_reset<DELAY>(&mut self, delay: &mut DELAY) -> Result<(), ()>
where
DELAY: DelayNs,
{
self.rst.set_low().map_err(|_| ())?;
delay.delay_ms(100);
self.rst.set_high().map_err(|_| ())?;
delay.delay_ms(100);
Ok(())
}
fn write_command(&mut self, command: Instruction, params: &[u8]) -> Result<(), ()> {
self.dc.set_low().map_err(|_| ())?;
self.spi.write(&[command as u8]).map_err(|_| ())?;
if !params.is_empty() {
self.start_data()?;
self.write_data(params)?;
}
Ok(())
}
fn start_data(&mut self) -> Result<(), ()> {
self.dc.set_high().map_err(|_| ())
}
fn write_data(&mut self, data: &[u8]) -> Result<(), ()> {
self.spi.write(data).map_err(|_| ())
}
fn write_word(&mut self, value: u16) -> Result<(), ()> {
self.write_data(&value.to_be_bytes())
}
fn write_words_buffered(&mut self, words: impl IntoIterator<Item = u16>) -> Result<(), ()> {
let mut buffer = [0; 32];
let mut index = 0;
for word in words {
let as_bytes = word.to_be_bytes();
buffer[index] = as_bytes[0];
buffer[index + 1] = as_bytes[1];
index += 2;
if index >= buffer.len() {
self.write_data(&buffer)?;
index = 0;
}
}
self.write_data(&buffer[0..index])
}
pub fn set_orientation(&mut self, orientation: &Orientation, rgb: bool) -> Result<(), ()> {
if rgb {
self.write_command(Instruction::MADCTL, &[*orientation as u8])?;
} else {
self.write_command(Instruction::MADCTL, &[*orientation as u8 | 0x08])?;
}
Ok(())
}
pub fn set_address_window(&mut self, sx: u16, sy: u16, ex: u16, ey: u16) -> Result<(), ()> {
self.write_command(Instruction::CASET, &[])?;
self.start_data()?;
self.write_word(sx + DX)?;
self.write_word(ex + DX)?;
self.write_command(Instruction::RASET, &[])?;
self.start_data()?;
self.write_word(sy + DY)?;
self.write_word(ey + DY)
}
pub fn set_pixel(&mut self, x: u16, y: u16, color: u16) -> Result<(), ()> {
self.set_address_window(x, y, x, y)?;
self.write_command(Instruction::RAMWR, &[])?;
self.start_data()?;
self.write_word(color)
}
pub fn write_pixels<P: IntoIterator<Item = u16>>(&mut self, colors: P) -> Result<(), ()> {
self.write_command(Instruction::RAMWR, &[])?;
self.start_data()?;
for color in colors {
self.write_word(color)?;
}
Ok(())
}
pub fn write_pixels_buffered<P: IntoIterator<Item = u16>>(
&mut self,
colors: P,
) -> Result<(), ()> {
self.write_command(Instruction::RAMWR, &[])?;
self.start_data()?;
self.write_words_buffered(colors)
}
pub fn set_pixels<P: IntoIterator<Item = u16>>(
&mut self,
sx: u16,
sy: u16,
ex: u16,
ey: u16,
colors: P,
) -> Result<(), ()> {
self.set_address_window(sx, sy, ex, ey)?;
self.write_pixels(colors)
}
pub fn set_pixels_buffered<P: IntoIterator<Item = u16>>(
&mut self,
sx: u16,
sy: u16,
ex: u16,
ey: u16,
colors: P,
) -> Result<(), ()> {
self.set_address_window(sx, sy, ex, ey)?;
self.write_pixels_buffered(colors)
}
pub fn clear_screen(&mut self, color: Rgb565) -> Result<(), ()> {
let wxh: usize = (W * H) as usize;
self.set_pixels_buffered(
0,
0,
W - 1,
H - 1,
core::iter::repeat(RawU16::from(color).into_inner()).take(wxh),
)
}
}
impl<SPI, DC, RST, const W: u16, const H: u16, const DX: u16, const DY: u16> DrawTarget
for ST7735S<SPI, DC, RST, W, H, DX, DY>
where
SPI: spi::SpiDevice,
DC: OutputPin,
RST: OutputPin,
{
type Error = ();
type Color = Rgb565;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
for Pixel(coord, color) in pixels.into_iter() {
if coord.x >= 0 && coord.y >= 0 && coord.x < W as i32 && coord.y < H as i32 {
self.set_pixel(
coord.x as u16,
coord.y as u16,
RawU16::from(color).into_inner(),
)?;
}
}
Ok(())
}
fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Self::Color>,
{
let drawable_area = area.intersection(&Rectangle::new(Point::zero(), self.size()));
if drawable_area.size != Size::zero() {
self.set_pixels_buffered(
drawable_area.top_left.x as u16,
drawable_area.top_left.y as u16,
(drawable_area.top_left.x + (drawable_area.size.width - 1) as i32) as u16,
(drawable_area.top_left.y + (drawable_area.size.height - 1) as i32) as u16,
area.points()
.zip(colors)
.filter(|(pos, _color)| drawable_area.contains(*pos))
.map(|(_pos, color)| RawU16::from(color).into_inner()),
)?;
}
Ok(())
}
}
impl<SPI, DC, RST, const W: u16, const H: u16, const DX: u16, const DY: u16> OriginDimensions
for ST7735S<SPI, DC, RST, W, H, DX, DY>
where
SPI: spi::SpiDevice,
DC: OutputPin,
RST: OutputPin,
{
fn size(&self) -> Size {
Size::new(W as u32, H as u32)
}
}