#![no_std]
pub mod instruction;
use core::mem::transmute;
use crate::instruction::Instruction;
use num_traits::ToPrimitive;
use num_derive::ToPrimitive;
use embedded_hal::digital::v2::OutputPin;
use embedded_hal::blocking::spi;
use embedded_hal::blocking::delay::DelayMs;
pub struct ST7735 <SPI, DC, RST>
where
SPI: spi::Write<u8>,
DC: OutputPin,
RST: OutputPin,
{
#[cfg(feature = "noblock_spi")]
_spi: SPI,
#[cfg(not(feature = "noblock_spi"))]
spi: SPI,
#[cfg(feature = "noblock_spi")]
_dc: DC,
#[cfg(not(feature = "noblock_spi"))]
dc: DC,
rst: RST,
rgb: bool,
inverted: bool,
dx: u16,
dy: u16,
}
#[derive(ToPrimitive)]
pub enum Orientation {
Portrait = 0x00,
Landscape = 0x60,
PortraitSwapped = 0xC0,
LandscapeSwapped = 0xA0,
}
impl<SPI, DC, RST> ST7735<SPI, DC, RST>
where
SPI: spi::Write<u8>,
DC: OutputPin,
RST: OutputPin,
{
pub fn new(
spi: SPI,
dc: DC,
rst: RST,
rgb: bool,
inverted: bool,
) -> Self
{
let display = ST7735 {
#[cfg(feature = "noblock_spi")]
_spi: spi,
#[cfg(not(feature = "noblock_spi"))]
spi,
#[cfg(feature = "noblock_spi")]
_dc: dc,
#[cfg(not(feature = "noblock_spi"))]
dc,
rst,
rgb,
inverted,
dx: 0,
dy: 0
};
display
}
pub fn init<DELAY>(&mut self, delay: &mut DELAY) -> Result<(), ()>
where DELAY: DelayMs<u8>
{
#[cfg(feature = "noblock_spi")]
mynewt::spi::spi_noblock_init()
.expect("spi init fail");
self.hard_reset()?;
self.write_command(Instruction::SWRESET, None)?;
delay.delay_ms(200);
self.write_command(Instruction::SLPOUT, None)?;
delay.delay_ms(200);
self.write_command(Instruction::FRMCTR1, Some(&[0x01, 0x2C, 0x2D]))?;
self.write_command(Instruction::FRMCTR2, Some(&[0x01, 0x2C, 0x2D]))?;
self.write_command(Instruction::FRMCTR3,
Some(&[0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D]))?;
self.write_command(Instruction::INVCTR, Some(&[0x07]))?;
self.write_command(Instruction::PWCTR1, Some(&[0xA2, 0x02, 0x84]))?;
self.write_command(Instruction::PWCTR2, Some(&[0xC5]))?;
self.write_command(Instruction::PWCTR3, Some(&[0x0A, 0x00]))?;
self.write_command(Instruction::PWCTR4, Some(&[0x8A, 0x2A]))?;
self.write_command(Instruction::PWCTR5, Some(&[0x8A, 0xEE]))?;
self.write_command(Instruction::VMCTR1, Some(&[0x0E]))?;
if self.inverted {
self.write_command(Instruction::INVON, None)?;
} else {
self.write_command(Instruction::INVOFF, None)?;
}
if self.rgb {
self.write_command(Instruction::MADCTL, Some(&[0x00]))?;
} else {
self.write_command(Instruction::MADCTL, Some(&[0x08]))?;
}
self.write_command(Instruction::COLMOD, Some(&[0x05]))?;
self.write_command(Instruction::DISPON, None)?;
delay.delay_ms(200);
#[cfg(feature = "noblock_spi")]
mynewt::spi::spi_noblock_write_flush()
.expect("spi flush fail");
Ok(())
}
pub fn hard_reset(&mut self) -> Result<(), ()>
{
self.rst.set_high().map_err(|_| ())?;
self.rst.set_low().map_err(|_| ())?;
self.rst.set_high().map_err(|_| ())
}
#[cfg(feature = "noblock_spi")]
fn write_command(&mut self, command: Instruction, params: Option<&[u8]>) -> Result<(), ()> {
mynewt::spi::spi_noblock_write_command(
command.to_u8().unwrap()
).expect("spi cmd fail");
if params.is_some() {
mynewt::spi::spi_noblock_write_data(
params.unwrap()
).expect("spi data fail");
}
Ok(())
}
#[cfg(not(feature = "noblock_spi"))]
fn write_command(&mut self, command: Instruction, params: Option<&[u8]>) -> Result<(), ()> {
self.dc.set_low().map_err(|_| ())?;
self.spi.write(&[command.to_u8().unwrap()]).map_err(|_| ())?;
if params.is_some() {
self.write_data(params.unwrap())?;
}
Ok(())
}
#[cfg(feature = "noblock_spi")]
fn write_data(&mut self, data: &[u8]) -> Result<(), ()> {
mynewt::spi::spi_noblock_write_data(
data
).expect("spi data fail");
Ok(())
}
#[cfg(not(feature = "noblock_spi"))]
fn write_data(&mut self, data: &[u8]) -> Result<(), ()> {
self.dc.set_high().map_err(|_| ())?;
self.spi.write(data).map_err(|_| ())
}
fn write_word(&mut self, value: u16) -> Result<(), ()> {
let bytes: [u8; 2] = unsafe { transmute(value.to_be()) };
self.write_data(&bytes)
}
pub fn set_orientation(&mut self, orientation: &Orientation) -> Result<(), ()> {
if self.rgb {
self.write_command(
Instruction::MADCTL, Some(&[orientation.to_u8().unwrap()]
))?;
} else {
self.write_command(
Instruction::MADCTL, Some(&[orientation.to_u8().unwrap() | 0x08 ]
))?;
}
Ok(())
}
pub fn set_offset(&mut self, dx: u16, dy: u16) {
self.dx = dx;
self.dy = dy;
}
fn set_address_window(&mut self, sx: u16, sy: u16, ex: u16, ey: u16) -> Result<(), ()> {
self.write_command(Instruction::CASET, None)?;
self.write_word(sx + self.dx)?;
self.write_word(ex + self.dx)?;
self.write_command(Instruction::RASET, None)?;
self.write_word(sy + self.dy)?;
self.write_word(ey + self.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, None)?;
self.write_word(color) ? ;
#[cfg(feature = "noblock_spi")]
mynewt::spi::spi_noblock_write_flush()
.expect("spi flush fail");
Ok(())
}
pub fn write_pixels<P: IntoIterator<Item = u16>>(&mut self, colors: P) -> Result <(), ()> {
self.write_command(Instruction::RAMWR, None)?;
for color in colors {
self.write_word(color)?;
}
#[cfg(feature = "noblock_spi")]
mynewt::spi::spi_noblock_write_flush()
.expect("spi flush fail");
Ok(())
}
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) ? ;
#[cfg(feature = "noblock_spi")]
mynewt::spi::spi_noblock_write_flush()
.expect("spi flush fail");
Ok(())
}
}
#[cfg(feature = "graphics")]
extern crate embedded_graphics;
#[cfg(feature = "graphics")]
use self::embedded_graphics::{drawable::{Pixel, Dimensions}, pixelcolor::Rgb565, Drawing, SizedDrawing};
#[cfg(feature = "graphics")]
impl<SPI, DC, RST> Drawing<Rgb565> for ST7735<SPI, DC, RST>
where
SPI: spi::Write<u8>,
DC: OutputPin,
RST: OutputPin,
{
fn draw<T>(&mut self, item_pixels: T)
where
T: IntoIterator<Item = Pixel<Rgb565>>,
{
for Pixel(coord, color) in item_pixels {
self.set_pixel(coord.0 as u16, coord.1 as u16, color.0).expect("pixel write failed");
}
}
}
#[cfg(feature = "graphics")]
impl<SPI, DC, RST> SizedDrawing<Rgb565> for ST7735<SPI, DC, RST>
where
SPI: spi::Write<u8>,
DC: OutputPin,
RST: OutputPin,
{
fn draw_sized<T>(&mut self, item_pixels: T)
where
T: IntoIterator<Item = Pixel<Rgb565>> + Dimensions,
{
let top_left = item_pixels.top_left();
let bottom_right = item_pixels.bottom_right();
self.set_pixels(top_left.0 as u16, top_left.1 as u16,
bottom_right.0 as u16, bottom_right.1 as u16,
item_pixels.into_iter().map(|Pixel(_coord, color)| color.0)).expect("pixels write failed")
}
}