#![no_std]
extern crate embedded_hal;
#[macro_use]
extern crate nb;
extern crate volatile_register;
use embedded_hal::blocking::delay::DelayMs;
use embedded_hal::digital::{OutputPin, InputPin};
use embedded_hal::spi::FullDuplex;
pub fn width_pixels_to_bytes(x: u16) -> u8 {
((x + 7) >> 3) as u8
}
pub enum Preset {
CFAP200200A0_154,
CFAP200200A1_154,
}
pub struct ScreenBuilder {
pub x_size: u16,
pub y_size: u16,
pub soft_start: [u8; 3],
pub vcom: u8,
pub dummy_line: u8,
pub gate_line: u8,
pub entry_mode: EntryMode,
pub lut_full: [u8; 30],
pub lut_part: [u8; 30],
}
impl ScreenBuilder {
pub fn preset(preset: Preset) -> ScreenBuilder {
match preset {
Preset::CFAP200200A0_154 => ScreenBuilder {
x_size: 200,
y_size: 200,
soft_start: SOFT_START_CFAP200200A0_154,
vcom: VCOM_CFAP200200A0_154,
dummy_line: DUMMY_LINE_CFAP200200A0_154,
gate_line: GATE_LINE_CFAP200200A0_154,
entry_mode: EntryMode::XIncrementYDecrement,
lut_full: LUT_FULL_CFAP200200A0_154,
lut_part: LUT_PART_CFAP200200A0_154,
},
Preset::CFAP200200A1_154 => ScreenBuilder {
x_size: 200,
y_size: 200,
soft_start: SOFT_START_CFAP200200A1_154,
vcom: VCOM_CFAP200200A1_154,
dummy_line: DUMMY_LINE_CFAP200200A1_154,
gate_line: GATE_LINE_CFAP200200A1_154,
entry_mode: EntryMode::XIncrementYDecrement,
lut_full: LUT_FULL_CFAP200200A1_154,
lut_part: LUT_PART_CFAP200200A1_154,
},
}
}
pub fn new_screen<SPI, DC, CS, BUSY, RST, ERR, DELAY>(
self,
serial: SPI,
dc: DC,
cs: CS,
busy: BUSY,
reset: RST,
delay: &mut DELAY,
) -> Result<Screen<SPI, DC, CS, BUSY, RST, ERR>, ERR>
where
SPI: FullDuplex<u8, Error = ERR>,
DC: OutputPin,
CS: OutputPin,
BUSY: InputPin,
RST: OutputPin,
DELAY: DelayMs<u16>,
{
Screen::new(
serial,
dc,
cs,
busy,
reset,
self,
delay,
)
}
}
#[derive(Clone, Copy, Debug)]
pub enum EntryMode {
XDecrementYDecrement = 0b00,
XIncrementYDecrement = 0b01,
XDecrementYIncrement = 0b10,
XIncrementYIncrement = 0b11,
}
#[derive(Clone, Copy, Debug)]
pub enum Command {
DriverOutputControl = 0x01,
BoosterSoftStartControl = 0x0c,
GateScanStartPosition = 0x0f,
DeepSleepMode = 0x10,
DataEntryModeSetting = 0x11,
SwReset = 0x12,
TemperatureSensorControl = 0x1a,
MasterActivation = 0x20,
DisplayUpdateControl1 = 0x21,
DisplayUpdateControl2 = 0x22,
WriteRam = 0x24,
WriteVcomRegister = 0x2c,
WriteLutRegister = 0x32,
SetDummyLinePeriod = 0x3a,
SetGateLineWidth = 0x3b,
BorderWaveformControl = 0x3c,
SetRamXAddressStartEndPosition = 0x44,
SetRamYAddressStartEndPosition = 0x45,
SetRamXAddressCounter = 0x4e,
SetRamYAddressCounter = 0x4f,
Nop = 0xff,
}
pub const SOFT_START_CFAP200200A0_154: [u8; 3] = [0xd7, 0xd6, 0x9d];
pub const SOFT_START_CFAP200200A1_154: [u8; 3] = [0xd7, 0xd6, 0x9d];
pub const VCOM_CFAP200200A0_154: u8 = 0xA8;
pub const VCOM_CFAP200200A1_154: u8 = 0x7F;
pub const DUMMY_LINE_CFAP200200A0_154: u8 = 0x1A;
pub const DUMMY_LINE_CFAP200200A1_154: u8 = 0x1A;
pub const GATE_LINE_CFAP200200A0_154: u8 = 0x08;
pub const GATE_LINE_CFAP200200A1_154: u8 = 0x08;
pub const LUT_FULL_CFAP200200A0_154: [u8; 30] = [
0x02,
0x02,
0x01,
0x11,
0x12,
0x12,
0x22,
0x22,
0x66,
0x69,
0x69,
0x59,
0x58,
0x99,
0x99,
0x88,
0x00,
0x00,
0x00,
0x00,
0xF8,
0xB4,
0x13,
0x51,
0x35,
0x51,
0x51,
0x19,
0x01,
0x00,
];
pub const LUT_FULL_CFAP200200A1_154: [u8; 30] = [
0x66,
0x66,
0x44,
0x66,
0xAA,
0x11,
0x80,
0x08,
0x11,
0x18,
0x81,
0x18,
0x11,
0x88,
0x11,
0x88,
0x11,
0x88,
0x00,
0x00,
0xFF,
0xFF,
0xFF,
0xFF,
0x5F,
0xAF,
0xFF,
0xFF,
0x2F,
0x00,
];
pub const LUT_PART_CFAP200200A0_154: [u8; 30] = [
0x10,
0x18,
0x18,
0x08,
0x18,
0x18,
0x08,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x13,
0x14,
0x44,
0x12,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
];
pub const LUT_PART_CFAP200200A1_154: [u8; 30] = [
0x10,
0x18,
0x18,
0x28,
0x18,
0x18,
0x18,
0x18,
0x08,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x13,
0x11,
0x22,
0x63,
0x11,
0x00,
0x00,
0x00,
0x00,
0x00,
];
#[derive(Debug)]
pub enum ScreenError<ERR> {
SizeError,
BoundsError,
LengthError,
SpiError(ERR)
}
impl<ERR> From<ERR> for ScreenError<ERR> {
fn from(err:ERR) -> Self {
ScreenError::SpiError(err)
}
}
impl<ERR> Clone for ScreenError<ERR> where ERR: Clone {
fn clone(&self) -> Self {
match self {
ScreenError::SizeError => ScreenError::SizeError,
ScreenError::BoundsError => ScreenError::BoundsError,
ScreenError::LengthError => ScreenError::LengthError,
ScreenError::SpiError(err) => ScreenError::SpiError(err.clone()),
}
}
}
impl<ERR> Copy for ScreenError<ERR> where ERR: Copy {}
pub struct Screen<SPI, DC, CS, BUSY, RST, ERR>
where
SPI: FullDuplex<u8, Error = ERR>,
DC: OutputPin,
CS: OutputPin,
BUSY: InputPin,
RST: OutputPin,
{
serial: SPI,
dc: DC,
cs: CS,
busy: BUSY,
reset: RST,
entry_mode: EntryMode,
x_size: u16,
y_size: u16,
lut_full: [u8; 30],
lut_part: [u8; 30],
}
impl<SPI, DC, CS, BUSY, RST, ERR> Screen<SPI, DC, CS, BUSY, RST, ERR>
where
SPI: FullDuplex<u8, Error = ERR>,
DC: OutputPin,
CS: OutputPin,
BUSY: InputPin,
RST: OutputPin,
{
fn new<DELAY>(
serial: SPI,
dc: DC,
cs: CS,
busy: BUSY,
reset: RST,
builder: ScreenBuilder,
delay: &mut DELAY,
) -> Result<Screen<SPI, DC, CS, BUSY, RST, ERR>, ERR>
where
DELAY: DelayMs<u16>,
{
let mut screen = Screen {
serial,
dc,
cs,
busy,
reset,
entry_mode: builder.entry_mode,
x_size: builder.x_size,
y_size: builder.y_size,
lut_full: builder.lut_full,
lut_part: builder.lut_part
};
screen.reset.set_low();
screen.cs.set_high();
screen.dc.set_high();
delay.delay_ms(10);
screen.reset.set_high();
delay.delay_ms(10);
screen.write_cmd_string(
Command::DriverOutputControl,
&[builder.x_size as u8, ((builder.x_size >> 8) & 0x01) as u8, 0x00]
)?;
screen.write_cmd_string(
Command::BoosterSoftStartControl,
&builder.soft_start
)?;
screen.write_cmd_string(
Command::WriteVcomRegister,
&[builder.vcom]
)?;
screen.write_cmd_string(
Command::SetDummyLinePeriod,
&[builder.dummy_line]
)?;
screen.write_cmd_string(
Command::SetGateLineWidth,
&[builder.gate_line]
)?;
screen.write_cmd_string(
Command::DataEntryModeSetting,
&[builder.entry_mode as u8]
)?;
Ok(screen)
}
pub fn show_full_screen_image(&mut self, image: &[u8]) -> Result<(), ScreenError<ERR>> {
let x_size = ((self.x_size + 7) >> 3) as u8;
let y_size = self.y_size;
if x_size as usize * y_size as usize != image.len() {
return Err(ScreenError::LengthError);
}
self.load_full_update_lut()?;
self.power_on()?;
self.set_display_area(
0, x_size - 1,
0, y_size - 1,
)?;
self.load_image(image)?;
self.update_full()?;
self.power_off()?;
Ok(())
}
pub fn load_partial_image(
&mut self,
x_start: u8, x_size: u8,
y_start: u16, y_size: u16,
image: &[u8],
) -> Result<(), ScreenError<ERR>> {
if x_size as usize * y_size as usize != image.len() {
return Err(ScreenError::LengthError);
}
self.set_display_area(
x_start, x_start + x_size - 1,
y_start, y_start + y_size - 1,
)?;
self.load_image(image)?;
Ok(())
}
pub fn load_full_update_lut(&mut self) -> Result<(), ERR> {
let lut_full_update = self.lut_full;
self.write_cmd_string(Command::WriteLutRegister, &lut_full_update)
}
pub fn load_partial_update_lut(&mut self) -> Result<(), ERR> {
let lut_partial_update = self.lut_part;
self.write_cmd_string(Command::WriteLutRegister, &lut_partial_update)
}
pub fn power_on(&mut self) -> Result<(), ERR> {
self.write_cmd_string(Command::DisplayUpdateControl2, &[0xc0])?;
self.write_cmd(Command::MasterActivation)?;
while self.busy.is_high() {}
Ok(())
}
pub fn power_off(&mut self) -> Result<(), ERR> {
self.write_cmd_string(Command::DisplayUpdateControl2, &[0xc3])?;
self.write_cmd(Command::MasterActivation)?;
while self.busy.is_high() {}
Ok(())
}
pub fn update_full(&mut self) -> Result<(), ERR> {
self.write_cmd_string(Command::DisplayUpdateControl2, &[0xc7])?;
self.write_cmd(Command::MasterActivation)?;
while self.busy.is_high() {}
self.write_cmd(Command::Nop)
}
pub fn update_partial(&mut self) -> Result<(), ERR> {
self.write_cmd_string(Command::DisplayUpdateControl2, &[0x04])?;
self.write_cmd(Command::MasterActivation)?;
while self.busy.is_high() {}
self.write_cmd(Command::Nop)
}
pub fn load_image(&mut self, image: &[u8]) -> Result<(), ERR> {
while self.busy.is_high() {}
self.cs.set_low();
self.dc.set_low();
block!(self.serial.send(Command::WriteRam as u8))?;
let _ = block!(self.serial.read())?;
self.dc.set_high();
for d in image {
block!(self.serial.send(*d))?;
let _ = block!(self.serial.read())?;
}
self.cs.set_high();
Ok(())
}
pub fn set_display_area(&mut self, x_start: u8, x_end: u8, y_start: u16, y_end: u16) -> Result<(), ScreenError<ERR>> {
let x_size = width_pixels_to_bytes(self.x_size);
if x_start > x_size || x_end > x_size || y_start > self.y_size || y_end > self.y_size {
return Err(ScreenError::SizeError);
}
if x_start >= x_end || y_start >= y_end {
return Err(ScreenError::BoundsError);
}
let (x_start, x_end, y_start, y_end) = match self.entry_mode {
EntryMode::XIncrementYIncrement => (x_start, x_end, y_start, y_end),
EntryMode::XDecrementYIncrement => (x_end, x_start, y_start, y_end),
EntryMode::XIncrementYDecrement => (x_start, x_end, y_end, y_start),
EntryMode::XDecrementYDecrement => (x_end, x_start, y_end, y_start),
};
self.write_cmd_string(
Command::SetRamXAddressStartEndPosition,
&[x_start, x_end]
)?;
self.write_cmd_string(
Command::SetRamYAddressStartEndPosition,
&[y_start as u8, (y_start >> 8) as u8, y_end as u8, (y_end >> 8) as u8]
)?;
self.write_cmd_string(
Command::SetRamXAddressCounter,
&[x_start]
)?;
self.write_cmd_string(
Command::SetRamYAddressCounter,
&[y_start as u8, (y_start >> 8) as u8]
)?;
Ok(())
}
pub fn write_cmd(&mut self, cmd: Command) -> Result<(), ERR> {
self.cs.set_low();
self.dc.set_low();
block!(self.serial.send(cmd as u8))?;
let _ = block!(self.serial.read())?;
self.cs.set_high();
Ok(())
}
pub fn write_data(&mut self, data: u8) -> Result<(), ERR> {
self.cs.set_low();
self.dc.set_high();
block!(self.serial.send(data))?;
let _ = block!(self.serial.read())?;
self.cs.set_high();
Ok(())
}
pub fn write_cmd_string(&mut self, cmd: Command, data: &[u8]) -> Result<(), ERR> {
self.cs.set_low();
self.dc.set_low();
block!(self.serial.send(cmd as u8))?;
let _ = block!(self.serial.read())?;
self.dc.set_high();
for d in data {
block!(self.serial.send(*d))?;
let _ = block!(self.serial.read())?;
}
self.cs.set_high();
Ok(())
}
}