Basic src migration from private repo
This commit is contained in:
parent
c2760375fe
commit
9df39ab4ae
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "st7701s"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Corey Vixie <corey@vixie.enterprises>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
num = "0.2.0"
|
||||||
|
spidev = "0.4.0"
|
||||||
|
enum_primitive = "0.1.1"
|
0
rustfmt.toml
Normal file
0
rustfmt.toml
Normal file
861
src/instructions.rs
Normal file
861
src/instructions.rs
Normal file
@ -0,0 +1,861 @@
|
|||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use crate::panel::Mode;
|
||||||
|
|
||||||
|
/// This is a 3-wire SPI implementation. Reads and writes share the SDA pin and
|
||||||
|
/// are performed half-duplex
|
||||||
|
///
|
||||||
|
/// Unless otherwise noted, any command pairs that set and unset a "mode" (eg.
|
||||||
|
/// DISPON/DISPOFF) will have no effect if the display is already in the mode
|
||||||
|
/// being requested. Therefore, these commands should be safe to use in a
|
||||||
|
/// "write-only, read-never" workflow.
|
||||||
|
///
|
||||||
|
/// NOTE: Some commands take many separate parameter words, most of which have at
|
||||||
|
/// least 8 bits of variability. Because of this, they aren't enumerated and
|
||||||
|
/// the vector is passed directly through.
|
||||||
|
|
||||||
|
/// The write mode of the interface means the micro controller writes
|
||||||
|
/// commands and data to the LCD driver. 3-lines serial data packet contains
|
||||||
|
/// a control bit D/CX and a transmission byte. In 4-lines serial interface,
|
||||||
|
/// data packet contains just transmission byte and control bit D/CX is
|
||||||
|
/// transferred by the D/CX pin. If D/CX is “low”, the transmission byte is
|
||||||
|
/// interpreted as a command byte. If D/CX is “high”, the transmission byte
|
||||||
|
/// is command register as parameter.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Command {
|
||||||
|
pub address: u8,
|
||||||
|
pub parameters: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Command {
|
||||||
|
fn new(address: u8) -> Command {
|
||||||
|
Command {
|
||||||
|
address: address,
|
||||||
|
parameters: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arg(mut self, arg: u8) -> Command {
|
||||||
|
self.parameters.push(arg);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn args(mut self, args: &[u8]) -> Command {
|
||||||
|
self.parameters.extend_from_slice(args);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize_address(&self) -> [u8; 2] {
|
||||||
|
[(self.address as u8), 0x00]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize_parameter(parameter: u8) -> [u8; 2] {
|
||||||
|
[parameter, 0x01]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum GammaCurve {
|
||||||
|
/// Gamma Curve 1 (G=2.2)
|
||||||
|
One = 0x01,
|
||||||
|
/// Reserved
|
||||||
|
Two = 0x02,
|
||||||
|
/// Reserved
|
||||||
|
Three = 0x04,
|
||||||
|
/// Reserved
|
||||||
|
Four = 0x08,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum TearingEffect {
|
||||||
|
/// V-blanking only
|
||||||
|
VBlank = 0x00,
|
||||||
|
/// V-blanking and H-blanking
|
||||||
|
VHBlank = 0x01,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum DataEnable {
|
||||||
|
DE = 0x00,
|
||||||
|
HV = 0x80,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum VsyncActive {
|
||||||
|
Low = 0x00,
|
||||||
|
High = 0x08,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum HsyncActive {
|
||||||
|
Low = 0x00,
|
||||||
|
High = 0x04,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum DataPolarity {
|
||||||
|
Rising = 0x00,
|
||||||
|
Falling = 0x02,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum EnablePolarity {
|
||||||
|
Low = 0x00,
|
||||||
|
High = 0x01,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum PWMPolarity {
|
||||||
|
Low = 0x00,
|
||||||
|
High = 0x20,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum LEDPolarity {
|
||||||
|
Low = 0x00,
|
||||||
|
High = 0x10,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum PixelPinout {
|
||||||
|
Normal = 0x00,
|
||||||
|
Condensed = 0x08,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum EndPixelFormat {
|
||||||
|
SelfMSB = 0x00,
|
||||||
|
GreenMSB = 0x01,
|
||||||
|
SelfLSB = 0x02,
|
||||||
|
Zero = 0x04,
|
||||||
|
One = 0x05,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum ScanDirection {
|
||||||
|
Normal = 0x00,
|
||||||
|
Reverse = 0x10,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum ColorOrder {
|
||||||
|
/// RGB mode
|
||||||
|
Rgb = 0x00,
|
||||||
|
/// BGR mode
|
||||||
|
Bgr = 0x08,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum BitsPerPixel {
|
||||||
|
/// 16 bits per pixel (RGB565)
|
||||||
|
Rgb565 = 0x50,
|
||||||
|
/// 18 bits per pixel (RGB666)
|
||||||
|
Rgb666 = 0x60,
|
||||||
|
/// 24 bits per pixel (RGB888)
|
||||||
|
Rgb888 = 0x70,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum BrightnessControl {
|
||||||
|
/// Ignore display brightness value and soft-set it to 0x00
|
||||||
|
Off = 0x00,
|
||||||
|
/// Use display brightness value normally
|
||||||
|
On = 0x20,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum DisplayDimming {
|
||||||
|
/// Ignore display brightness value and soft-set it to 0x00
|
||||||
|
Off = 0x00,
|
||||||
|
/// Use display brightness value normally
|
||||||
|
On = 0x08,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Backlight {
|
||||||
|
/// Disable backlight circuit. Control lines must be low.
|
||||||
|
Off = 0x00,
|
||||||
|
/// Enable backlight circuit. Normal behavior.
|
||||||
|
On = 0x04,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Enhancement {
|
||||||
|
/// Disable color enhancement
|
||||||
|
Off = 0x00,
|
||||||
|
/// Enable color enhancement
|
||||||
|
On = 0x80,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum EnhancementMode {
|
||||||
|
Low = 0x00,
|
||||||
|
Medium = 0x10,
|
||||||
|
High = 0x30,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum AdaptiveBrightness {
|
||||||
|
/// Off
|
||||||
|
Off = 0x00,
|
||||||
|
/// User Interface Mode
|
||||||
|
UserInterface = 0x01,
|
||||||
|
/// Still Picture Mode
|
||||||
|
StillPicture = 0x02,
|
||||||
|
/// Moving Image Mode
|
||||||
|
MovingImage = 0x03,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Inversion {
|
||||||
|
OneDot = 0x00,
|
||||||
|
TwoDot = 0x01,
|
||||||
|
Column = 0x07,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum GammaOPBias {
|
||||||
|
Off = 0x00,
|
||||||
|
Min = 0x40,
|
||||||
|
Middle = 0x80,
|
||||||
|
Max = 0xC0,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum SourceOPInput {
|
||||||
|
Off = 0x00,
|
||||||
|
Min = 0x04,
|
||||||
|
Middle = 0x08,
|
||||||
|
Max = 0x0C,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum SourceOPOutput {
|
||||||
|
Off = 0x00,
|
||||||
|
Min = 0x01,
|
||||||
|
Middle = 0x02,
|
||||||
|
Max = 0x03,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum VoltageAVDD {
|
||||||
|
Pos6_2 = 0x00,
|
||||||
|
Pos6_4 = 0x10,
|
||||||
|
Pos6_6 = 0x20,
|
||||||
|
Pos6_8 = 0x30,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum VoltageAVCL {
|
||||||
|
Neg4_4 = 0x00,
|
||||||
|
Neg4_6 = 0x01,
|
||||||
|
Neg4_8 = 0x02,
|
||||||
|
Neg5_0 = 0x03,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum SunlightReadable {
|
||||||
|
/// DEFAULT: Sunlight readable mode off
|
||||||
|
Off = 0x00,
|
||||||
|
/// Enable sunlight readable mode
|
||||||
|
On = 0x10,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum Command2Selection {
|
||||||
|
Disabled = 0x00,
|
||||||
|
BK0 = 0x10,
|
||||||
|
BK1 = 0x11,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum CommandsGeneral {
|
||||||
|
NOP = 0x00, // No-op
|
||||||
|
SWRESET = 0x01, // Software Reset
|
||||||
|
RDDID = 0x04, // Read Display ID
|
||||||
|
RDNUMED = 0x05, // Read Number of Errors on DSI
|
||||||
|
RDRED = 0x06, // Read the first pixel of Red Color
|
||||||
|
RDGREEN = 0x07, // Read the first pixel of Green Color
|
||||||
|
RDBLUE = 0x08, // Read the first pixel of Blue Color
|
||||||
|
RDDPM = 0x0A, // Read Display Power Mode
|
||||||
|
RDDMADCTL = 0x0B, // Read Display MADCTL
|
||||||
|
RDDCOLMOD = 0x0C, // Read Display Pixel Format
|
||||||
|
RDDIM = 0x0D, // Read Display Image Mode
|
||||||
|
RDDSM = 0x0E, // Read Display Signal Mode
|
||||||
|
RDDSDR = 0x0F, // Read Display Self-Diagnostic Result
|
||||||
|
SLPIN = 0x10, // Sleep in
|
||||||
|
SLPOUT = 0x11, // Sleep Out
|
||||||
|
PTLON = 0x12, // Partial Display Mode On
|
||||||
|
NORON = 0x13, // Normal Display Mode On
|
||||||
|
INVOFF = 0x20, // Display Inversion Off
|
||||||
|
INVON = 0x21, // Display Inversion On
|
||||||
|
ALLPOFF = 0x22, // All Pixel Off
|
||||||
|
ALLPON = 0x23, // All Pixel ON
|
||||||
|
GAMSET = 0x26, // Gamma Set
|
||||||
|
DISPOFF = 0x28, // Display Off
|
||||||
|
DISPON = 0x29, // Display On
|
||||||
|
TEOFF = 0x34, // Tearing Effect Line OFF
|
||||||
|
TEON = 0x35, // Tearing Effect Line ON
|
||||||
|
MADCTL = 0x36, // Display data access control
|
||||||
|
IDMOFF = 0x38, // Idle Mode Off
|
||||||
|
IDMON = 0x39, // Idle Mode On
|
||||||
|
COLMOD = 0x3A, // Interface Pixel Format
|
||||||
|
GSL = 0x45, // Get Scan Line
|
||||||
|
WRDISBV = 0x51, // Write Display Brightness
|
||||||
|
RDDISBV = 0x52, // Read Display Brightness Value
|
||||||
|
WRCTRLD = 0x53, // Write CTRL Display
|
||||||
|
RDCTRLD = 0x54, // Read CTRL Value Display
|
||||||
|
WRCACE = 0x55, // Write Content Adaptive Brightness Control and Color Enhancement
|
||||||
|
RDCABC = 0x56, // Read Content Adaptive Brightness Control
|
||||||
|
WRCABCMB = 0x5E, // Write CABC Minimum Brightness
|
||||||
|
RDCABCMB = 0x5F, // Read CABC Minimum Brightness
|
||||||
|
RDABCSDR = 0x68, // Read Automatic Brightness Control Self-Diagnostic Result
|
||||||
|
RDBWLB = 0x70, // Read Black/White Low Bits
|
||||||
|
RDBkx = 0x71, // Read Bkx
|
||||||
|
RDBky = 0x72, // Read Bky
|
||||||
|
RDWx = 0x73, // Read Wx
|
||||||
|
RDWy = 0x74, // Read Wy
|
||||||
|
RDRGLB = 0x75, // Read Red/Green Low Bits
|
||||||
|
RDRx = 0x76, // Read Rx
|
||||||
|
RDRy = 0x77, // Read Ry
|
||||||
|
RDGx = 0x78, // Read Gx
|
||||||
|
RDGy = 0x79, // Read Gy
|
||||||
|
RDBALB = 0x7A, // Read Blue/A Color Low Bits
|
||||||
|
RDBx = 0x7B, // Read Bx
|
||||||
|
CND2BKxSEL = 0xFF, // Set Command2 mode for BK Register
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum BK0Command2 {
|
||||||
|
PVGAMCTRL = 0xB0, // Positive Voltage Gamma Control
|
||||||
|
NVGAMCTRL = 0xB1, // Negative Voltage Gamma Control
|
||||||
|
DGMEN = 0xB8, // Digital Gamma Enable
|
||||||
|
DGMLUTR = 0xB9, // Digital Gamma Look-up Table for Red
|
||||||
|
DGMLUTB = 0xBA, // Digital Gamma Look-up Table for Blue
|
||||||
|
PWMCLKSEL = 0xBC, // PWM CLK select
|
||||||
|
LNESET = 0xC0, // Display Line Setting
|
||||||
|
PORCTRL = 0xC1, // Porch Control
|
||||||
|
INVSET = 0xC2, // Inversion selection & Frame Rate Control
|
||||||
|
RGBCTRL = 0xC3, // RGB control
|
||||||
|
PARCTRL = 0xC5, // Partial Mode Control
|
||||||
|
SDIR = 0xC7, // X-direction Control
|
||||||
|
PDOSET = 0xC8, // Pseudo-Dot inversion diving setting
|
||||||
|
COLCTRL = 0xCD, // Color Control
|
||||||
|
SRECTRL = 0xE0, // Sunlight Readable Enhancement
|
||||||
|
NRCTRL = 0xE1, // Noise Reduce Control
|
||||||
|
SECTRL = 0xE2, // Sharpness Control
|
||||||
|
CCCTRL = 0xE3, // Color Calibration Control
|
||||||
|
SKCTRL = 0xE4, // Skin Tone Preservation CONTROL
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum BK1Command2 {
|
||||||
|
VRHS = 0xB0, // Vop Amplitude setting
|
||||||
|
VCOMS = 0xB1, // VCOM amplitude setting
|
||||||
|
VGHSS = 0xB2, // VGH Voltage setting
|
||||||
|
TESTCMD = 0xB3, // TEST Command Setting
|
||||||
|
VGLS = 0xB5, // VGL Voltage setting
|
||||||
|
PWCTRL1 = 0xB7, // Power Control 1
|
||||||
|
PWCTRL2 = 0xB8, // Power Control 2
|
||||||
|
PCLKS1 = 0xBA, // Power pumping clk selection 1
|
||||||
|
PCLKS3 = 0xBC, // Power pumping clk selection 3
|
||||||
|
SPD1 = 0xC1, // Source pre_drive timing set1
|
||||||
|
SPD2 = 0xC2, // Source pre_drive timing set2
|
||||||
|
MIPISET1 = 0xD0, // MIPI Setting 1
|
||||||
|
MIPISET2 = 0xD1, // MIPI Setting 2
|
||||||
|
MIPISET3 = 0xD2, // MIPI Setting 3
|
||||||
|
MIPISET4 = 0xD3, // MIPI Setting 4
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandsGeneral {
|
||||||
|
/// # NO OPERATION
|
||||||
|
///
|
||||||
|
/// This command is "empty". It has no effect on the display, but it can be
|
||||||
|
/// used to terminate parameter write commands.
|
||||||
|
pub fn no_operation() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::NOP as u8))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # SOFTWARE RESET
|
||||||
|
///
|
||||||
|
/// The display module performs a software reset. Registers are written with
|
||||||
|
/// the default "reset" values.
|
||||||
|
///
|
||||||
|
/// - Frame buffer contents are unaffected by this command
|
||||||
|
/// - After a SWRESET command, sleep at least 5ms before the next command
|
||||||
|
/// - If the display is sleeping when a SWRESET is sent, the sleep
|
||||||
|
/// duration should be at least 120ms before sending the next command.
|
||||||
|
/// - SWRESET cannot be sent during SLPOUT
|
||||||
|
/// - (MIPI ONLY) Send a shutdown packet before SWRESET
|
||||||
|
pub fn software_reset() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::SWRESET as u8))
|
||||||
|
}
|
||||||
|
/// # SLEEP IN
|
||||||
|
///
|
||||||
|
/// This command causes the display module to enter a minimum power state.
|
||||||
|
/// The buck converter, display oscilator, and panel scanning are all shut
|
||||||
|
/// down.
|
||||||
|
///
|
||||||
|
/// The control interface, display data, and registers remain active.
|
||||||
|
///
|
||||||
|
/// The driver may send PCLK, HS, and CS information after SLPIN, and this
|
||||||
|
/// data will be valid for the next two frames if Normal Mode is active.
|
||||||
|
///
|
||||||
|
/// Dimming will not work when changing from sleep out to sleep in.
|
||||||
|
///
|
||||||
|
/// Normally, sleep state can be read with RDDST, but MISO must be connected.
|
||||||
|
pub fn sleep_mode_on() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::SLPIN as u8))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # SLEEP OUT
|
||||||
|
///
|
||||||
|
/// This command turns off the minimum power state set by SLPIN.
|
||||||
|
///
|
||||||
|
/// The driver may send PCLK, HS, and CS information before SLPOUT, and this
|
||||||
|
/// data will be valid for the two frames before the command if Normal Mode
|
||||||
|
/// is active.
|
||||||
|
pub fn sleep_mode_off() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::SLPOUT as u8))
|
||||||
|
}
|
||||||
|
/// # PARTIAL MODE ON
|
||||||
|
///
|
||||||
|
/// This command turns on Partial Mode. See PARTIAL AREA (30h) command.
|
||||||
|
pub fn partial_mode_on() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::PTLON as u8))
|
||||||
|
}
|
||||||
|
/// # NORMAL MODE ON (DEFAULT)
|
||||||
|
///
|
||||||
|
/// This command turns on Normal Mode and turns off Partial Mode.
|
||||||
|
pub fn normal_mode_on() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::NORON as u8))
|
||||||
|
}
|
||||||
|
/// # DISPLAY INVERSION OFF (DEFAULT)
|
||||||
|
///
|
||||||
|
/// This command restores normal pixel values.
|
||||||
|
pub fn invert_display_off() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::INVOFF as u8))
|
||||||
|
}
|
||||||
|
/// # DISPLAY INVERSION ON
|
||||||
|
///
|
||||||
|
/// This command inverts the display (white becomes black, red becomes blue).
|
||||||
|
pub fn invert_display_on() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::INVON as u8))
|
||||||
|
}
|
||||||
|
/// # ALL PIXELS OFF (BLACK)
|
||||||
|
///
|
||||||
|
/// This command sets all pixel values to black.
|
||||||
|
///
|
||||||
|
/// ALLPOFF may be used in Sleep Mode, Normal Mode, or Partial Mode.
|
||||||
|
pub fn all_pixels_off() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::ALLPOFF as u8))
|
||||||
|
}
|
||||||
|
/// # ALL PIXELS ON (WHITE)
|
||||||
|
///
|
||||||
|
/// This command sets all pixel values to white.
|
||||||
|
///
|
||||||
|
/// ALLPOFF may be used in Sleep Mode, Normal Mode, or Partial Mode.
|
||||||
|
pub fn all_pixels_on() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::ALLPON as u8))
|
||||||
|
}
|
||||||
|
/// # GAMMA CURVE SELECT
|
||||||
|
///
|
||||||
|
/// This command selects a predefined gamma curve from one of four values.
|
||||||
|
///
|
||||||
|
/// WARNING: It's not clear from the Sitronix documentation what any values
|
||||||
|
/// are aside from 01.
|
||||||
|
///
|
||||||
|
///| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|
||||||
|
///| -- | -- | -- | -- | -- | GC[3:0] |
|
||||||
|
pub fn gamma_curve_select(GC: GammaCurve) -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::GAMSET as u8).arg(GC as u8))
|
||||||
|
}
|
||||||
|
/// # DISPLAY OFF (DEFAULT?)
|
||||||
|
///
|
||||||
|
/// This command is used to enter Display Off Mode. In this mode, display
|
||||||
|
/// data is disabled and all pixels are blanked.
|
||||||
|
///
|
||||||
|
/// NOTE: It's possible that this is the default value.
|
||||||
|
pub fn display_off() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::DISPOFF as u8))
|
||||||
|
}
|
||||||
|
/// # DISPLAY ON
|
||||||
|
///
|
||||||
|
/// WARNING: I have no idea how this behaves. The Sitronix docs monkey copied
|
||||||
|
/// and pasted the description for DISPOFF. At a guess, it should turn the
|
||||||
|
/// display back on.
|
||||||
|
pub fn display_on() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::DISPON as u8))
|
||||||
|
}
|
||||||
|
/// # TEARING EFFECT LINE OFF
|
||||||
|
///
|
||||||
|
/// This command is used to turn off the display module's Tearing Effect
|
||||||
|
/// output signal (vsync?) on the TE signal line (active low).
|
||||||
|
pub fn tearing_effect_off() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::TEOFF as u8))
|
||||||
|
}
|
||||||
|
/// # TEARING EFFECT LINE ON
|
||||||
|
///
|
||||||
|
/// This command is used to turn on the display module's Tearing Effect
|
||||||
|
/// output signal line.
|
||||||
|
///
|
||||||
|
///| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|
||||||
|
///| -- | -- | -- | -- | -- | -- | -- | TE |
|
||||||
|
pub fn tearing_effect_on(TE: TearingEffect) -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::TEON as u8).arg(TE as u8))
|
||||||
|
}
|
||||||
|
/// # DISPLAY DATA ACCESS CONTROL
|
||||||
|
/// * [ML] - Scan direction
|
||||||
|
/// * []
|
||||||
|
///| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|
||||||
|
///| -- | -- | -- | ML | CO | -- | -- | -- |
|
||||||
|
pub fn display_data_control(
|
||||||
|
ML: ScanDirection,
|
||||||
|
CO: ColorOrder,
|
||||||
|
) -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::MADCTL as u8).arg(ML as u8 | CO as u8))
|
||||||
|
}
|
||||||
|
/// # IDLE MODE OFF
|
||||||
|
///
|
||||||
|
/// Turns off Idle Mode. Display is capable of its full 16.7 million color
|
||||||
|
/// palette
|
||||||
|
pub fn idle_mode_off() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::IDMOFF as u8))
|
||||||
|
}
|
||||||
|
/// # IDLE MODE ON
|
||||||
|
///
|
||||||
|
/// Turns on Idle Mode. In idle mode the color palette is significantly
|
||||||
|
/// reduced. The MSB of each color will be rounded up or down, creating a
|
||||||
|
/// palette limited to 8 colors.
|
||||||
|
pub fn idle_mode_on() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::IDMON as u8))
|
||||||
|
}
|
||||||
|
/// # SET INTERFACE PIXEL FORMAT
|
||||||
|
///
|
||||||
|
/// Defines the format for RGB pixel data.
|
||||||
|
///
|
||||||
|
///| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|
||||||
|
///| -- | BPP[2:0] | -- | -- | -- | -- |
|
||||||
|
pub fn set_color_mode(BPP: BitsPerPixel) -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::COLMOD as u8).arg(BPP as u8))
|
||||||
|
}
|
||||||
|
/// # WRDISBV
|
||||||
|
///
|
||||||
|
/// Change the display brightness to an 8-bit value.
|
||||||
|
///
|
||||||
|
/// 0x00: Lowest brightness
|
||||||
|
/// 0xFF: Hightest brightness
|
||||||
|
///
|
||||||
|
///| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|
||||||
|
///| Display Brightness Value [7:0] |
|
||||||
|
pub fn set_display_brightness(DBV: u8) -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::WRDISBV as u8).arg(DBV as u8))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # WRITE CTRL DISPLAY
|
||||||
|
///
|
||||||
|
/// This command changes more general behavior of the brightness controls.
|
||||||
|
///
|
||||||
|
/// [BCTRL] Brightness control on or off
|
||||||
|
/// [DD] Display dimming (only affects manual brightness settings)
|
||||||
|
/// [BL] Backlight control on or off
|
||||||
|
///
|
||||||
|
///| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|
||||||
|
///| -- | -- | BCTRL | -- | DD | BL | -- | -- |
|
||||||
|
pub fn configure_brightness(
|
||||||
|
BCTRL: BrightnessControl,
|
||||||
|
DD: DisplayDimming,
|
||||||
|
BL: Backlight,
|
||||||
|
) -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::WRCTRLD as u8).arg(BCTRL as u8 | DD as u8 | BL as u8))
|
||||||
|
}
|
||||||
|
/// # WRITE CONTENT ADAPTIVE BRIGHTNESS CONTROL AND COLOR ENHANCEMENT
|
||||||
|
///
|
||||||
|
/// Set parameters for content-based adaptive brightness control, set
|
||||||
|
/// different color enhancement modes.
|
||||||
|
///
|
||||||
|
/// [CE] Color enhancement on or off:
|
||||||
|
/// [CEMD] Color enhancement mode
|
||||||
|
/// [CABC] Adaptive brightness control
|
||||||
|
///
|
||||||
|
///| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|
||||||
|
///| CE | -- | CEMD[1:0] | -- | -- | CABC[1:0] |
|
||||||
|
pub fn configure_color_enhancement(
|
||||||
|
CE: Enhancement,
|
||||||
|
CEMD: EnhancementMode,
|
||||||
|
CABC: AdaptiveBrightness,
|
||||||
|
) -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::WRCACE as u8).arg(CE as u8 | CEMD as u8 | CABC as u8))
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// WRITE CABC MINIMUM BRIGHTNESS
|
||||||
|
///
|
||||||
|
/// Sets the minimum brightness value to be used for CABC (see WRCACE).
|
||||||
|
///
|
||||||
|
/// [MBV] Minimum Brightness Value
|
||||||
|
///
|
||||||
|
///| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|
||||||
|
///| Minimum Brightness Value [7:0] |
|
||||||
|
pub fn set_minimum_brightness(MBV: u8) -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::WRCABCMB as u8).arg(MBV as u8))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_display_pixel_format() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::RDDCOLMOD as u8))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_self_diagnostics() -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::RDDSDR as u8))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # SET COMMAND2 MODE
|
||||||
|
/// This is one of the most confusing attributes of the Sitronix chips.
|
||||||
|
/// BK0, BK1, and BK3 (maybe) all have "Command2" instructions that share a
|
||||||
|
/// common address space. To avoid collisions and to ensure you're sending
|
||||||
|
/// the command you think you're sending, we use a double-entry bookkeeping
|
||||||
|
/// approach, where set_command_2 will send the chip the updated Command2
|
||||||
|
/// setting AND record it back to the local flag, which is required for
|
||||||
|
/// static type checking in all Command2 instructions locally.
|
||||||
|
///
|
||||||
|
/// eg. for a BK1 Command2 instruction, "current" must be set to
|
||||||
|
/// Command2Selection::BK1.
|
||||||
|
pub fn set_command_2(set: Command2Selection) -> Result<Command, &'static str> {
|
||||||
|
Ok(Command::new(Self::CND2BKxSEL as u8).args(&[0x77, 0x01, 0x00, 0x00, set as u8]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BK0Command2 {
|
||||||
|
pub fn validate<F>(CMD2: &Command2Selection, build_command: F) -> Result<Command, &'static str>
|
||||||
|
where
|
||||||
|
F: Fn() -> Command,
|
||||||
|
{
|
||||||
|
match CMD2 {
|
||||||
|
Command2Selection::BK0 => Ok(build_command()),
|
||||||
|
_ => Err("Cannot run command '{}': BK0 Command 2 mode not set"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # POSITIVE GAMMA CONTROL
|
||||||
|
/// See note above about parameters
|
||||||
|
pub fn positive_gamma_control(
|
||||||
|
CMD2: &Command2Selection,
|
||||||
|
parameters: &[u8],
|
||||||
|
) -> Result<Command, &'static str> {
|
||||||
|
Self::validate(CMD2, || {
|
||||||
|
Command::new(Self::PVGAMCTRL as u8).args(parameters)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # POSITIVE GAMMA CONTROL
|
||||||
|
/// See note above about parameters
|
||||||
|
pub fn negative_gamma_control(
|
||||||
|
CMD2: &Command2Selection,
|
||||||
|
parameters: &[u8],
|
||||||
|
) -> Result<Command, &'static str> {
|
||||||
|
Self::validate(CMD2, || {
|
||||||
|
Command::new(Self::NVGAMCTRL as u8).args(parameters)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # DISPLAY LINE SETTING
|
||||||
|
pub fn display_line_setting(
|
||||||
|
CMD2: &Command2Selection,
|
||||||
|
LDE_EN: u8,
|
||||||
|
Line: u8,
|
||||||
|
Line_delta: u8,
|
||||||
|
) -> Result<Command, &'static str> {
|
||||||
|
Self::validate(CMD2, || {
|
||||||
|
Command::new(Self::LNESET as u8).args(&[LDE_EN | Line, Line_delta])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # PORCH CONTROL
|
||||||
|
pub fn porch_control(CMD2: &Command2Selection, mode: &Mode) -> Result<Command, &'static str> {
|
||||||
|
let front_porch: u8 = (mode.vtotal - mode.vsync_end).try_into().unwrap();
|
||||||
|
let back_porch: u8 = (mode.vsync_start - mode.vdisplay).try_into().unwrap();
|
||||||
|
Self::validate(CMD2, || {
|
||||||
|
Command::new(Self::PORCTRL as u8).args(&[front_porch, back_porch])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # INVERSION SELECT
|
||||||
|
/// * [LINV] - the type of inversion
|
||||||
|
/// * [RTNI] - minimum number of pclk in each line
|
||||||
|
pub fn inversion_select(
|
||||||
|
CMD2: &Command2Selection,
|
||||||
|
NLINV: Inversion,
|
||||||
|
RTNI: u8,
|
||||||
|
) -> Result<Command, &'static str> {
|
||||||
|
Self::validate(CMD2, || {
|
||||||
|
Command::new(Self::INVSET as u8).args(&[0x30 | NLINV as u8, RTNI])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # RGB CONTROLDE/HV:RGB Mode selection
|
||||||
|
/// * [DEHV]
|
||||||
|
/// 0: RGB DE mode
|
||||||
|
/// 1: RGB HV mode
|
||||||
|
/// * [VSP]: Sets the signal polarity of the VSYNC pin.
|
||||||
|
/// 0: Low active
|
||||||
|
/// 1: High active
|
||||||
|
/// * [HSP]: Sets the signal polarity of the HSYNC pin.
|
||||||
|
/// 0: Low active
|
||||||
|
/// 1: High active
|
||||||
|
/// * [DP]: Sets the signal polarity of the DOTCLK pin.
|
||||||
|
/// 0: The data is input on the positive edge of DOTCLK
|
||||||
|
/// 1: The data is input on the negative edge of DOTCLK
|
||||||
|
/// * [EP]: Sets the signal polarity of the ENABLE pin.
|
||||||
|
/// 0: The data DB23-0 is written when ENABLE = “1". Disable data write operation when ENABLE = “0”.
|
||||||
|
/// 1: The data DB23-0 is written when ENABLE = “0”. Disable data write operation when ENABLE = “1”.
|
||||||
|
///| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|
||||||
|
///| DEHV | -- | -- | -- | VSP | HSP | DP | EP |
|
||||||
|
///| HBP |
|
||||||
|
///| VBP |
|
||||||
|
pub fn rgb_control(
|
||||||
|
CMD2: &Command2Selection,
|
||||||
|
DEHV: DataEnable,
|
||||||
|
VSP: VsyncActive,
|
||||||
|
HSP: HsyncActive,
|
||||||
|
DP: DataPolarity,
|
||||||
|
EP: EnablePolarity,
|
||||||
|
mode: &Mode,
|
||||||
|
) -> Result<Command, &'static str> {
|
||||||
|
let HBP: u8 = (mode.htotal - mode.hsync_end).try_into().unwrap();
|
||||||
|
let VBP: u8 = (mode.vsync_start - mode.vdisplay).try_into().unwrap();
|
||||||
|
|
||||||
|
Self::validate(CMD2, || {
|
||||||
|
Command::new(Self::RGBCTRL as u8).args(&[
|
||||||
|
DEHV as u8 | VSP as u8 | HSP as u8 | DP as u8 | EP as u8,
|
||||||
|
HBP,
|
||||||
|
VBP,
|
||||||
|
])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # COLOR CONTROL
|
||||||
|
/// * [PWM]: LEDPWM polarity control.
|
||||||
|
/// 0: polarity normal.
|
||||||
|
/// 1: polarity reverse.
|
||||||
|
/// * [LED]: LED_ON polarity control.
|
||||||
|
/// 0: polarity normal.
|
||||||
|
/// 1: polarity reverse.
|
||||||
|
/// * [MDT]: RGB pixel format argument.(for 262K).See Table 17.
|
||||||
|
/// 0: pixel format argument normal.
|
||||||
|
/// 1: pixel collect to DB[17:0].
|
||||||
|
/// * [EPF][2:0]: end of pixel format (for 65k & 262k mode)
|
||||||
|
/// 0: copy self MSB
|
||||||
|
/// 1: copy G MSB
|
||||||
|
/// 2: copy self LSB
|
||||||
|
/// 4: FIX 0
|
||||||
|
/// 5: FIX 1
|
||||||
|
///| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|
||||||
|
///| -- | -- | PWM | LED | MDT | EPF |
|
||||||
|
pub fn color_control(
|
||||||
|
CMD2: &Command2Selection,
|
||||||
|
PWM: PWMPolarity,
|
||||||
|
LED: LEDPolarity,
|
||||||
|
MDT: PixelPinout,
|
||||||
|
EPF: EndPixelFormat,
|
||||||
|
) -> Result<Command, &'static str> {
|
||||||
|
Self::validate(CMD2, || {
|
||||||
|
Command::new(Self::COLCTRL as u8).arg(PWM as u8 | LED as u8 | MDT as u8 | EPF as u8)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # CONFIGURE SUNLIGHT READABLE ENHANCEMENT MODE
|
||||||
|
///
|
||||||
|
/// Sets the minimum brightness value to be used for CABC (see WRCACE).
|
||||||
|
///
|
||||||
|
/// [MBV] Minimum Brightness Value
|
||||||
|
///
|
||||||
|
///| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|
||||||
|
///| -- | -- | -- | -- | SRE | SRE_alpha[3:0] |
|
||||||
|
pub fn configure_sunlight_ehancement(
|
||||||
|
CMD2: &Command2Selection,
|
||||||
|
SRE: SunlightReadable,
|
||||||
|
mut SRE_alpha: u8,
|
||||||
|
) -> Result<Command, &'static str> {
|
||||||
|
if SRE_alpha > 0x0F {
|
||||||
|
SRE_alpha = 0x0F;
|
||||||
|
}
|
||||||
|
Self::validate(CMD2, || {
|
||||||
|
Command::new(Self::SECTRL as u8).arg(SRE as u8 | SRE_alpha)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BK1Command2 {
|
||||||
|
pub fn validate<F>(CMD2: &Command2Selection, build_command: F) -> Result<Command, &'static str>
|
||||||
|
where
|
||||||
|
F: Fn() -> Command,
|
||||||
|
{
|
||||||
|
match CMD2 {
|
||||||
|
Command2Selection::BK1 => Ok(build_command()),
|
||||||
|
_ => Err("Cannot run command '{}': BK0 Command 2 mode not set"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_vop_amplitude(CMD2: &Command2Selection, VRHA: u8) -> Result<Command, &'static str> {
|
||||||
|
Self::validate(CMD2, || Command::new(BK1Command2::VRHS as u8).arg(VRHA))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_vcom_amplitude(CMD2: &Command2Selection, VCOM: u8) -> Result<Command, &'static str> {
|
||||||
|
Self::validate(CMD2, || Command::new(BK1Command2::VCOMS as u8).arg(VCOM))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_vgh_voltage(CMD2: &Command2Selection, VGH: u8) -> Result<Command, &'static str> {
|
||||||
|
Self::validate(CMD2, || Command::new(BK1Command2::VGHSS as u8).arg(VGH))
|
||||||
|
}
|
||||||
|
pub fn test_command_setting(CMD2: &Command2Selection) -> Result<Command, &'static str> {
|
||||||
|
Self::validate(CMD2, || Command::new(BK1Command2::TESTCMD as u8).arg(0x80))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_vgl_voltage(CMD2: &Command2Selection, VGLS: u8) -> Result<Command, &'static str> {
|
||||||
|
Self::validate(CMD2, || {
|
||||||
|
Command::new(BK1Command2::VGLS as u8).arg(0x40 | VGLS)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn power_control_one(
|
||||||
|
CMD2: &Command2Selection,
|
||||||
|
AP: GammaOPBias,
|
||||||
|
APIS: SourceOPInput,
|
||||||
|
APOS: SourceOPOutput,
|
||||||
|
) -> Result<Command, &'static str> {
|
||||||
|
Self::validate(CMD2, || {
|
||||||
|
Command::new(BK1Command2::PWCTRL1 as u8).arg(AP as u8 | APIS as u8 | APOS as u8)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn power_control_two(
|
||||||
|
CMD2: &Command2Selection,
|
||||||
|
AVDD: VoltageAVDD,
|
||||||
|
AVCL: VoltageAVCL,
|
||||||
|
) -> Result<Command, &'static str> {
|
||||||
|
Self::validate(CMD2, || {
|
||||||
|
Command::new(BK1Command2::PWCTRL2 as u8).arg(AVDD as u8 | AVCL as u8)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # SET SOURCE PRE DRIVE TIMING CONTROL
|
||||||
|
/// T2D [3:0]: source pre_drive timing setting.(GND to VDD)
|
||||||
|
/// Adjust Range : 0 ~ 3 uS 1 step is 0.2uS
|
||||||
|
///| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|
||||||
|
///| -- | 1 | 1 | 1 | T2D |
|
||||||
|
pub fn set_pre_drive_timing_one(
|
||||||
|
CMD2: &Command2Selection,
|
||||||
|
T2D: u8,
|
||||||
|
) -> Result<Command, &'static str> {
|
||||||
|
Self::validate(CMD2, || {
|
||||||
|
Command::new(BK1Command2::SPD1 as u8).arg(0x70 | T2D)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # SET SOURCE PRE DRIVE TIMING CONTROL
|
||||||
|
/// Same parameters as SPD1
|
||||||
|
pub fn set_pre_drive_timing_two(
|
||||||
|
CMD2: &Command2Selection,
|
||||||
|
T2D: u8,
|
||||||
|
) -> Result<Command, &'static str> {
|
||||||
|
Self::validate(CMD2, || {
|
||||||
|
Command::new(BK1Command2::SPD2 as u8).arg(0x70 | T2D)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
184
src/main.rs
Normal file
184
src/main.rs
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
extern crate enum_primitive;
|
||||||
|
extern crate num;
|
||||||
|
extern crate spidev;
|
||||||
|
use std::{thread, time};
|
||||||
|
|
||||||
|
mod instructions;
|
||||||
|
mod panel;
|
||||||
|
mod spi;
|
||||||
|
|
||||||
|
use instructions::{BK0Command2, BK1Command2, Command, Command2Selection, CommandsGeneral};
|
||||||
|
use panel::CVTRB;
|
||||||
|
use spi::ST7701S;
|
||||||
|
|
||||||
|
/// ST7701S supports two kinds of RGB interface, DE mode (mode 1) and HV mode
|
||||||
|
/// (mode 2), and 16bit/18bit and 24 bit data format. When DE mode is selected
|
||||||
|
/// and the VSYNC, HSYNC, DOTCLK, DE, D23:0 pins can be used; when HV mode is
|
||||||
|
/// selected and the VSYNC, HSYNC, DOTCLK, D23:0 pins can be used. When using
|
||||||
|
/// RGB interface, only serial interface can be selected.
|
||||||
|
fn main() {
|
||||||
|
println!("Initializing SPI driver for ST7701S panel");
|
||||||
|
|
||||||
|
let mut CMD2: Command2Selection = Command2Selection::Disabled;
|
||||||
|
let mut display = ST7701S::new(String::from("/dev/spidev1.0"));
|
||||||
|
let mode = CVTRB;
|
||||||
|
|
||||||
|
// SOFTWARE RESET
|
||||||
|
// 5ms delay
|
||||||
|
display.write_command(CommandsGeneral::software_reset());
|
||||||
|
thread::sleep(time::Duration::from_millis(5));
|
||||||
|
|
||||||
|
// EXIT SLEEP MODE
|
||||||
|
// Variable delay (200ms is "safe")
|
||||||
|
display.write_command(CommandsGeneral::sleep_mode_off());
|
||||||
|
thread::sleep(time::Duration::from_millis(200));
|
||||||
|
|
||||||
|
// ENTER BK0 COMMAND2 MODE
|
||||||
|
display.write_command(CommandsGeneral::set_command_2(Command2Selection::BK0));
|
||||||
|
CMD2 = Command2Selection::BK0;
|
||||||
|
|
||||||
|
display.write_command(BK0Command2::positive_gamma_control(
|
||||||
|
&CMD2,
|
||||||
|
&[
|
||||||
|
0x00, 0x0E, 0x15, 0x0F, 0x11, 0x08, 0x08, 0x08, 0x08, 0x23, 0x04, 0x13, 0x12, 0x2B,
|
||||||
|
0x34, 0x1F,
|
||||||
|
],
|
||||||
|
));
|
||||||
|
display.write_command(BK0Command2::negative_gamma_control(
|
||||||
|
&CMD2,
|
||||||
|
&[
|
||||||
|
0x00, 0x0E, 0x95, 0x0F, 0x13, 0x07, 0x09, 0x08, 0x08, 0x22, 0x04, 0x10, 0x0E, 0x2C,
|
||||||
|
0x34, 0x1F,
|
||||||
|
],
|
||||||
|
));
|
||||||
|
display.write_command(BK0Command2::display_line_setting(&CMD2, 0x80, 0x69, 0x02));
|
||||||
|
display.write_command(BK0Command2::porch_control(&CMD2, &mode));
|
||||||
|
display.write_command(BK0Command2::inversion_select(
|
||||||
|
&CMD2,
|
||||||
|
instructions::Inversion::Column,
|
||||||
|
0xFF,
|
||||||
|
));
|
||||||
|
display.write_command(BK0Command2::rgb_control(
|
||||||
|
&CMD2,
|
||||||
|
instructions::DataEnable::DE,
|
||||||
|
instructions::VsyncActive::Low,
|
||||||
|
instructions::HsyncActive::Low,
|
||||||
|
instructions::DataPolarity::Rising,
|
||||||
|
instructions::EnablePolarity::Low,
|
||||||
|
&mode,
|
||||||
|
));
|
||||||
|
|
||||||
|
// ENTER BK1 COMMAND2 MODE
|
||||||
|
display.write_command(CommandsGeneral::set_command_2(Command2Selection::BK1));
|
||||||
|
CMD2 = Command2Selection::BK1;
|
||||||
|
|
||||||
|
display.write_command(BK1Command2::set_vop_amplitude(&CMD2, 0x45));
|
||||||
|
display.write_command(BK1Command2::set_vcom_amplitude(&CMD2, 0x13));
|
||||||
|
display.write_command(BK1Command2::set_vgh_voltage(&CMD2, 0x07));
|
||||||
|
display.write_command(BK1Command2::test_command_setting(&CMD2));
|
||||||
|
display.write_command(BK1Command2::set_vgl_voltage(&CMD2, 0x07));
|
||||||
|
display.write_command(BK1Command2::power_control_one(
|
||||||
|
&CMD2,
|
||||||
|
instructions::GammaOPBias::Middle,
|
||||||
|
instructions::SourceOPInput::Min,
|
||||||
|
instructions::SourceOPOutput::Off,
|
||||||
|
));
|
||||||
|
|
||||||
|
display.write_command(BK1Command2::power_control_two(
|
||||||
|
&CMD2,
|
||||||
|
instructions::VoltageAVDD::Pos6_6,
|
||||||
|
instructions::VoltageAVCL::Neg4_4,
|
||||||
|
));
|
||||||
|
display.write_command(BK1Command2::set_pre_drive_timing_one(&CMD2, 0x03));
|
||||||
|
display.write_command(BK1Command2::set_pre_drive_timing_two(&CMD2, 0x03));
|
||||||
|
|
||||||
|
// UNKNOWABLE CARGO-CULTED MYSTERY MEAT
|
||||||
|
//
|
||||||
|
// I copied this command sequence from the Linux MIPI driver for the ST7701,
|
||||||
|
// written in C. The author of that driver _also_ had no idea what this
|
||||||
|
// command sequence does or means, and claims to have himself copied it from
|
||||||
|
// a sample provided by a Sitronix engineer. Since this is a Linux SPI
|
||||||
|
// driver for the ST7701S written in Rust, there's ample opportunity for
|
||||||
|
// something to not line up.
|
||||||
|
//
|
||||||
|
// May whatever gods you pray to have mercy on our souls.
|
||||||
|
display.write_command(Ok(Command {
|
||||||
|
address: 0xE0,
|
||||||
|
parameters: vec![0x00, 0x00, 0x02],
|
||||||
|
}));
|
||||||
|
display.write_command(Ok(Command {
|
||||||
|
address: 0xE1,
|
||||||
|
parameters: vec![
|
||||||
|
0x0B, 0x00, 0x0D, 0x00, 0x0C, 0x00, 0x0E, 0x00, 0x00, 0x44, 0x44,
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
display.write_command(Ok(Command {
|
||||||
|
address: 0xE2,
|
||||||
|
parameters: vec![
|
||||||
|
0x33, 0x33, 0x44, 0x44, 0x64, 0x00, 0x66, 0x00, 0x65, 0x00, 0x67, 0x00, 0x00,
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
display.write_command(Ok(Command {
|
||||||
|
address: 0xE3,
|
||||||
|
parameters: vec![0x00, 0x00, 0x33, 0x33],
|
||||||
|
}));
|
||||||
|
display.write_command(Ok(Command {
|
||||||
|
address: 0xE4,
|
||||||
|
parameters: vec![0x44, 0x44],
|
||||||
|
}));
|
||||||
|
display.write_command(Ok(Command {
|
||||||
|
address: 0xE5,
|
||||||
|
parameters: vec![
|
||||||
|
0x0C, 0x78, 0x3C, 0xA0, 0x0E, 0x78, 0x3C, 0xA0, 0x10, 0x78, 0x3C, 0xA0, 0x12, 0x78,
|
||||||
|
0x3C, 0xA0,
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
display.write_command(Ok(Command {
|
||||||
|
address: 0xE6,
|
||||||
|
parameters: vec![0x00, 0x00, 0x33, 0x33],
|
||||||
|
}));
|
||||||
|
display.write_command(Ok(Command {
|
||||||
|
address: 0xE7,
|
||||||
|
parameters: vec![0x44, 0x44],
|
||||||
|
}));
|
||||||
|
display.write_command(Ok(Command {
|
||||||
|
address: 0xE8,
|
||||||
|
parameters: vec![
|
||||||
|
0x0D, 0x78, 0x3C, 0xA0, 0x0F, 0x78, 0x3C, 0xA0, 0x11, 0x78, 0x3C, 0xA0, 0x13, 0x78,
|
||||||
|
0x3C, 0xA0,
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
display.write_command(Ok(Command {
|
||||||
|
address: 0xEB,
|
||||||
|
parameters: vec![0x02, 0x02, 0x39, 0x39, 0xEE, 0x44, 0x00],
|
||||||
|
}));
|
||||||
|
display.write_command(Ok(Command {
|
||||||
|
address: 0xEC,
|
||||||
|
parameters: vec![0x00, 0x00],
|
||||||
|
}));
|
||||||
|
display.write_command(Ok(Command {
|
||||||
|
address: 0xED,
|
||||||
|
parameters: vec![
|
||||||
|
0xFF, 0xF1, 0x04, 0x56, 0x72, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0x27, 0x65, 0x40,
|
||||||
|
0x1F, 0xFF,
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// BK1 COMMAND2 DISABLE
|
||||||
|
display.write_command(CommandsGeneral::set_command_2(Command2Selection::Disabled));
|
||||||
|
CMD2 = Command2Selection::Disabled;
|
||||||
|
|
||||||
|
// display.write_command(CommandsGeneral::set_color_mode(
|
||||||
|
// instructions::BitsPerPixel::Rgb666,
|
||||||
|
// ));
|
||||||
|
// display.write_command(CommandsGeneral::display_data_control(
|
||||||
|
// instructions::ScanDirection::Normal,
|
||||||
|
// instructions::ColorOrder::Rgb,
|
||||||
|
// ));
|
||||||
|
display.write_command(CommandsGeneral::tearing_effect_on(
|
||||||
|
instructions::TearingEffect::VHBlank,
|
||||||
|
));
|
||||||
|
|
||||||
|
display.write_command(CommandsGeneral::display_on());
|
||||||
|
thread::sleep(time::Duration::from_millis(200));
|
||||||
|
}
|
84
src/panel.rs
Normal file
84
src/panel.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
pub struct Mode {
|
||||||
|
pub clock: u32,
|
||||||
|
|
||||||
|
pub hdisplay: u32,
|
||||||
|
pub hsync_start: u32,
|
||||||
|
pub hsync_end: u32,
|
||||||
|
pub htotal: u32,
|
||||||
|
|
||||||
|
pub vdisplay: u32,
|
||||||
|
pub vsync_start: u32,
|
||||||
|
pub vsync_end: u32,
|
||||||
|
pub vtotal: u32,
|
||||||
|
|
||||||
|
pub width_mm: u32,
|
||||||
|
pub height_mm: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const DefaultMode: Mode = Mode {
|
||||||
|
clock: 27500,
|
||||||
|
|
||||||
|
hdisplay: 480,
|
||||||
|
hsync_start: 480 + 38,
|
||||||
|
hsync_end: 480 + 38 + 12,
|
||||||
|
htotal: 480 + 38 + 12 + 12,
|
||||||
|
|
||||||
|
vdisplay: 854,
|
||||||
|
vsync_start: 854 + 18,
|
||||||
|
vsync_end: 854 + 18 + 8,
|
||||||
|
vtotal: 854 + 18 + 8 + 4,
|
||||||
|
|
||||||
|
width_mm: 69,
|
||||||
|
height_mm: 139,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const TDOMode: Mode = Mode {
|
||||||
|
clock: 16000,
|
||||||
|
|
||||||
|
hdisplay: 480,
|
||||||
|
hsync_start: 480 + 24,
|
||||||
|
hsync_end: 480 + 24 + 6,
|
||||||
|
htotal: 480 + 24 + 6 + 18,
|
||||||
|
|
||||||
|
vdisplay: 480,
|
||||||
|
vsync_start: 480 + 16,
|
||||||
|
vsync_end: 480 + 16 + 4,
|
||||||
|
vtotal: 480 + 16 + 4 + 10,
|
||||||
|
|
||||||
|
width_mm: 69,
|
||||||
|
height_mm: 139,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const CVTMode: Mode = Mode {
|
||||||
|
clock: 17000,
|
||||||
|
|
||||||
|
hdisplay: 480,
|
||||||
|
hsync_start: 480 + 8,
|
||||||
|
hsync_end: 480 + 8 + 48,
|
||||||
|
htotal: 480 + 8 + 48 + 56,
|
||||||
|
|
||||||
|
vdisplay: 480,
|
||||||
|
vsync_start: 480 + 1,
|
||||||
|
vsync_end: 480 + 1 + 3,
|
||||||
|
vtotal: 480 + 1 + 3 + 13,
|
||||||
|
|
||||||
|
width_mm: 69,
|
||||||
|
height_mm: 139,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const CVTRB: Mode = Mode {
|
||||||
|
clock: 23500,
|
||||||
|
|
||||||
|
hdisplay: 640,
|
||||||
|
hsync_start: 480 + 8,
|
||||||
|
hsync_end: 480 + 8 + 32,
|
||||||
|
htotal: 480 + 8 + 32 + 40,
|
||||||
|
|
||||||
|
vdisplay: 480,
|
||||||
|
vsync_start: 480 + 14,
|
||||||
|
vsync_end: 480 + 14 + 3,
|
||||||
|
vtotal: 480 + 14 + 3 + 4,
|
||||||
|
|
||||||
|
width_mm: 69,
|
||||||
|
height_mm: 139,
|
||||||
|
};
|
63
src/spi.rs
Normal file
63
src/spi.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
extern crate spidev;
|
||||||
|
|
||||||
|
use crate::instructions::Command;
|
||||||
|
use spidev::{SpiModeFlags, Spidev, SpidevOptions};
|
||||||
|
use std::io;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
|
||||||
|
pub struct ST7701S {
|
||||||
|
spi: Spidev,
|
||||||
|
options: SpidevOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ST7701S {
|
||||||
|
pub fn create_spi(device: String, options: &SpidevOptions) -> io::Result<Spidev> {
|
||||||
|
let mut spi = Spidev::open(device)?;
|
||||||
|
spi.configure(&options)?;
|
||||||
|
Ok(spi)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(device: String) -> ST7701S {
|
||||||
|
let options = SpidevOptions::new()
|
||||||
|
.bits_per_word(9)
|
||||||
|
.lsb_first(false)
|
||||||
|
.max_speed_hz(20_000)
|
||||||
|
.mode(SpiModeFlags::SPI_MODE_0)
|
||||||
|
.build();
|
||||||
|
let spi = ST7701S::create_spi(device, &options).unwrap();
|
||||||
|
|
||||||
|
ST7701S { options, spi }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_command(&mut self, command: Result<Command, &'static str>) -> Result<(), ()> {
|
||||||
|
match command {
|
||||||
|
Ok(c) => {
|
||||||
|
let address = &c.serialize_address();
|
||||||
|
self.spi.write(&c.serialize_address());
|
||||||
|
println!("address {}", c.address);
|
||||||
|
|
||||||
|
for parameter in c.parameters {
|
||||||
|
self.spi.write(&Command::serialize_parameter(parameter));
|
||||||
|
println!("parameter {}", parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => println!("{}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_command(&mut self, command: Result<Command, &'static str>) -> Result<(), ()> {
|
||||||
|
match command {
|
||||||
|
Ok(c) => {
|
||||||
|
let mut rx_buf = [0_u8; 10];
|
||||||
|
self.spi.write(&c.serialize_address());
|
||||||
|
self.spi.read(&mut rx_buf);
|
||||||
|
println!("{:?}", rx_buf);
|
||||||
|
}
|
||||||
|
Err(e) => println!("{}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user