From 3d3787603ee1f762df4721d83d9ddb0f054474e4 Mon Sep 17 00:00:00 2001 From: Christopher Anderson Date: Wed, 4 May 2016 11:21:57 -0700 Subject: [PATCH] EINK code, busy on refresh --- platform/stm32f7xx/include/platform/spi.h | 257 ++++++++++++ target/dartuinoP0/display/eink.c | 475 +++++++++++++++++----- 2 files changed, 635 insertions(+), 97 deletions(-) diff --git a/platform/stm32f7xx/include/platform/spi.h b/platform/stm32f7xx/include/platform/spi.h index aca1f6ad..d44d7fb3 100644 --- a/platform/stm32f7xx/include/platform/spi.h +++ b/platform/stm32f7xx/include/platform/spi.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include @@ -15,3 +16,259 @@ status_t spi_write(SPI_HandleTypeDef *handle, uint8_t *data, size_t len, uint32_ status_t spi_read(SPI_HandleTypeDef *handle, uint8_t *data, size_t len, uint32_t cs); status_t spi_transaction(SPI_HandleTypeDef *handle, uint8_t *wdata, uint8_t *rdata, size_t len, uint32_t cs); +// Structure definitions for the STM32F7 spi controller. The intention is to point stm32f7_spi_t +// at one of the spi base addresses defines such as SPI1 +// +// ex: volatile stm32f7_spi_t *spi2 = (stm32f7_spi_t *) SPI2; + +typedef struct { + uint16_t cpha:1; + uint16_t cpol:1; + uint16_t mstr:1; + uint16_t br:3; + uint16_t spe:1; + uint16_t lsb_first:1; + uint16_t ssi:1; + uint16_t ssm:1; + uint16_t rx_only:1; + uint16_t crcl:1; + uint16_t crc_next:1; + uint16_t crc_en:1; + uint16_t bidi_oe:1; + uint16_t bidi_mode:1; +} SPIx_CR1_t; + +typedef struct { + uint16_t rxdmaen:1; + uint16_t txdmaen:1; + uint16_t ssoe:1; + uint16_t nssp:1; + uint16_t frf:1; + uint16_t errie:1; + uint16_t rxneie:1; + uint16_t txeie:1; + uint16_t ds:4; + uint16_t frxth:1; + uint16_t ldma_rx:1; + uint16_t ldma_tx:1; + uint16_t __reserved0:1; +} SPIx_CR2_t; + +typedef struct { + uint16_t rxne:1; + uint16_t txe:1; + uint16_t chside:1; + uint16_t udr:1; + uint16_t crcerr:1; + uint16_t modf:1; + uint16_t ovr:1; + uint16_t bsy:1; + uint16_t fre:1; + uint16_t frlvl:2; + uint16_t ftlvl:2; + uint16_t __reserved0:3; +} SPIx_SR_t; + +typedef struct { + SPIx_CR1_t CR1; + uint16_t __reserved0; + SPIx_CR2_t CR2; + uint16_t __reserved1; + SPIx_SR_t SR; + uint16_t __reserved2; + uint16_t DR; +} stm32f7_spi_t; + +enum cpha { + cpha_first_transition = 0x0, + cpha_second_transition = 0x1, +}; + +enum cpol { + cpol_clk_idle_low = 0x0, + cpol_clk_idle_high = 0x1, +}; + +enum mstr { + mstr_spi_slave = 0x0, + mstr_spi_master = 0x1, +}; + +enum br { + fpclk_div_2 = 0b000, + fpclk_div_4 = 0b001, + fpclk_div_8 = 0b010, + fpclk_div_16 = 0b011, + fpclk_div_32 = 0b100, + fpclk_div_64 = 0b101, + fpclk_div_128 = 0b110, + fpclk_div_256 = 0b111, +}; + +enum spe { + spe_spi_disable = 0x0, + spe_spi_enabled = 0x1, +}; + +enum ssm { + ssm_disabled = 0x0, + ssm_enabled = 0x1, +}; + +enum rxonly { + rxonly_full_duplex = 0x0, + rxonly_output_disabled = 0x1, +}; + +enum crcl { + crcl_8bit = 0x0, + crcl_16bit = 0x1, +}; + +enum crcnext { + crcnext_tx_buf_next = 0x0, + crcnext_tx_crc_next = 0x1, +}; + +enum crcen { + hw_crc_disable = 0x0, + hw_crc_enable = 0x1, +}; + +enum bidioe { + bidi_output_disable = 0x0, + bidi_output_enable = 0x1, +}; + +enum bidimode { + bidi_mode_2 = 0x0, + bidi_mode_1 = 0x1, +}; + +enum rxdmaen { + rx_dma_disabled = 0x0, + rx_dma_enabled = 0x1, +}; + +enum txdmaen { + tx_dma_disabled = 0x0, + tx_dma_enabled = 0x1, +}; + +enum ssoe { + ss_output_disabled = 0x0, + ss_output_enabled = 0x1, +}; + +enum nssp { + nss_pulse_disable = 0x0, + nss_pulse_enable = 0x1, +}; + +enum frf { + motorola_mode = 0x0, + ti_mode = 0x1, +}; + +enum errie { + err_int_mask = 0x0, + err_int_enable = 0x1, +}; + +enum rxneie { + rxne_int_mask = 0x0, + rxne_int_enable = 0x1, +}; + +enum txeie { + txe_int_mask = 0x0, + txe_int_enable = 0x1, +}; + +enum ds { + data_size_4bit = 0b0011, + data_size_5bit = 0b0100, + data_size_6bit = 0b0101, + data_size_7bit = 0b0110, + data_size_8bit = 0b0111, + data_size_9bit = 0b1000, + data_size_10bit = 0b1001, + data_size_11bit = 0b1010, + data_size_12bit = 0b1011, + data_size_13bit = 0b1100, + data_size_14bit = 0b1101, + data_size_15bit = 0b1110, + data_size_16bit = 0b1111, +}; + +enum frxth { + rxne_event_fifo_1_2 = 0x0, + rxne_event_fifo_1_4 = 0x1, +}; + +enum ldma_rx { + data_rx_even = 0x0, + data_rx_odd = 0x1, +}; + +enum ldma_tx { + data_tx_even = 0x0, + data_tx_odd = 0x1, +}; + +enum sr_rxne { + sr_rx_buf_empty = 0x0, + sr_rx_buf_not_empty = 0x1, +}; + +enum sr_txne { + sr_tx_buf_empty = 0x0, + sr_tx_buf_not_empty = 0x1, +}; + +// CHSIDE unused in SPI mode +// UDR unused in SPI mode + +enum crcerr { + sr_crc_match = 0x0, + sr_crc_mismatch = 0x1, +}; + +enum modf { + sr_mode_fault = 0x0, + sr_no_mode_fault = 0x1, +}; + +enum ovr { + sr_no_overrun = 0x0, + sr_overrun = 0x1, +}; + +enum bsy { + sr_not_busy = 0x0, + sr_busy = 0x1, +}; + +enum fre { + sr_no_fre = 0x0, + sr_fre = 0x1, +}; + +enum frlvl { + sr_rx_fifo_empty = 0b00, + sr_rx_fifo_1_4 = 0b01, + sr_rx_fifo_1_2 = 0b10, + sr_rx_fifo_full = 0b11, +}; + +enum ftlvl { + sr_tx_fifo_empty = 0b00, + sr_tx_fifo_1_4 = 0b01, + sr_tx_fifo_1_2 = 0b10, + sr_tx_fifo_full = 0b11, +}; + +STATIC_ASSERT(__builtin_offsetof(stm32f7_spi_t, CR1) == 0x0); +STATIC_ASSERT(__builtin_offsetof(stm32f7_spi_t, CR2) == 0x4); +STATIC_ASSERT(__builtin_offsetof(stm32f7_spi_t, SR) == 0x8); +STATIC_ASSERT(__builtin_offsetof(stm32f7_spi_t, DR) == 0xC); diff --git a/target/dartuinoP0/display/eink.c b/target/dartuinoP0/display/eink.c index e2b11811..29715fa8 100644 --- a/target/dartuinoP0/display/eink.c +++ b/target/dartuinoP0/display/eink.c @@ -8,6 +8,7 @@ #include #include #include +#include // TODO The eink driver should not include stm headers. We likely need INIT to store // a spihandle and then spi functions use it some other way #include @@ -149,40 +150,6 @@ static uint8_t lut_kwg[512] = { SPI_HandleTypeDef SpiHandle; bool spi_inited = false; -enum { - PanelSetting = 0x00, - PowerSetting = 0x01, - PowerOff = 0x02, - PowerOffSequenceSetting = 0x03, - PowerOn = 0x04, - BoosterSoftStart = 0x06, - DeepSleep = 0x07, - DisplayRefresh = 0x12, - DataStartTransmission2 = 0x13, - DataStartTransmissionWindow = 0x14, - KwgVcomLutRegister = 0x20, - KwgLutRegister = 0x22, - FtLutRegister = 0x26, - PllControl = 0x30, - TemperatureSensor = 0x40, - TemperatureSensorEnable = 0x41, - TemperatureSensorWrite = 0x42, - TemperatureSensorRead = 0x43, - VcomAndDataIntervalSetting = 0x50, - LowPowerDetection = 0x51, - ResolutionSetting = 0x61, - GateGroupSetting = 0x62, - GateBlockSetting = 0x63, - GateSelectSetting = 0x64, - Revision = 0x70, - GetStatus = 0x71, - AutoMeasureVcom = 0x80, - VcomValue = 0x81, - VcomDcSetting = 0x82, - BorderDcVoltageSetting = 0x84, - LpdSelect = 0xE4, - }; - typedef struct { // Gate power selection // 0: External gate power from VGH/VGL pins @@ -238,7 +205,7 @@ typedef struct { uint8_t i2c_busyn:1; uint8_t i2c_err:1; uint8_t __rs0:2; -} eink_status_t; +} et011tt2_status_t; typedef struct { // DDX[1:0]: Data polarity @@ -286,29 +253,209 @@ typedef struct { uint8_t vres_low; } resolution_settings_t; +typedef struct { + // G1~4NUM[3:0]: Channel Number used for Gate Group 1~4. For example: + // 2: GGx[2:0] ON + // 15: GGx[15:0] ON + uint8_t g1num:4; + uint8_t __rs0:1; + // G1~4UD: Gate Group 1~4 Up/Down Selection + // 0: Down scan within Gate Group + // 1: Up scan within Gate Group (default) + uint8_t g1ud:1; + // G1~4BS: Gate Group 1~4 Block/Select Selection + // 0: Gate Select + // 1: Gate Block + uint8_t g1bs:1; + // G1~4EN: Gate Group 1~4 Enable + // 0: Disable + // 1: Enable + uint8_t g1en:1; + //---- new byte + uint8_t g2num:4; + uint8_t __rs1:1; + uint8_t g2ud:1; + uint8_t g2bs:1; + uint8_t g2en:1; + //---- new byte + uint8_t g3num:4; + uint8_t __rs2:1; + uint8_t g3ud:1; + uint8_t g3bs:1; + uint8_t g3en:1; + //---- new byte + uint8_t g4num:4; + uint8_t __rs3:1; + uint8_t g4ud:1; + uint8_t g4bs:1; + uint8_t g4en:1; + //---- new byte + // GSFB: Gate Select Forward/Backward + // 0: Gate select backward + // 1: Gate select forward + uint8_t gsfb:1; + // GBFB: Gate Block Forward/Backward + // 0: Gate block backward + // 1: Gate block forward + uint8_t gbfb:1; + uint8_t __rs4:2; + // XOPT: XON Option + // 0: No all gate on during vertical blanking in XON mode (default) + // 1: All gate on during vertical blanking in XON mode + uint8_t xopt:1; + uint8_t __rs5:3; +} gate_group_settings_t; + +typedef struct { + uint8_t vbds:7; + uint8_t __rs0:1; +} border_dc_v_settings_t; + +typedef struct { + // Low Power Voltage Selection + uint8_t lpd_sel:2; + uint8_t __rs0:6; +} lpdselect_t; + +typedef struct { + uint8_t pixel3:2; + uint8_t pixel2:2; + uint8_t pixel1:2; + uint8_t pixel0:2; +} data_tranmission_t; + +typedef struct { + // X[7:0]: X-axis Start Point. X-axis start point for update display window. + // NOTE: The X-axis start point needs to be a multiple of 4. + uint8_t x; + // Y[9:0]: Y-axis Start Point. Y-axis start point for update display window. + uint8_t y_high:2; + uint8_t __rs0:6; + uint8_t y_low; + // W[7:0]: X-axis Window Width. X-axis width for update display window. + // NOTE: The width needs to be a multiple of 4. + // NOTE: This needs to be set to W - 1. + uint8_t w; + uint8_t l_high:2; + uint8_t __rs1:6; + // L[9:0]: Y-axis Window Width. Y-axis width for update display window + // NOTE: This needs to be set to L - 1. + uint8_t l_low; +} data_transmission_window_t; + +typedef struct { + uint8_t mode:2; + // DN_EN: Do-nothing function enabled + // 0: Data follow VCOM function disable + // 1: Data output follows VCOM LUT if new pixel data equal to old pixel + // data inside Update Display Area + // NOTE: Do-nothing function is always active outside Update Display Area. + uint8_t dn_en:1; + // RGL_EN: REGAL function control + // 0: REGAL function disable + // 1: REGAL function enable + uint8_t rgl_en:1; + // PSCAN: Partial Scan control + // 0: Partial Scan disable + // 1: Partial Scan enable (Gate Scan within Display Window only) + uint8_t pscan:1; + uint8_t __rs0:3; + //---- new byte + // X[7:0]: X-axis Start Point. X-axis start point for update display window. + // NOTE: The X-axis start point needs to be a multiple of 4. + uint8_t x; + // Y[9:0]: Y-axis Start Point. Y-axis start point for update display window. + uint8_t y_high:2; + uint8_t __rs1:6; + //---- new byte + uint8_t y_low; + // W[7:0]: X-axis Window Width. X-axis width for update display window. + // NOTE: The width needs to be a multiple of 4. + // NOTE: This needs to be set to W - 1. + uint8_t w; + // L[9:0]: Y-axis Window Width. Y-axis width for update display window + // NOTE: This needs to be set to L - 1. + uint8_t l_high:2; + uint8_t __rs2:6; + uint8_t l_low; +} display_refresh_t; + +enum { + PanelSetting = 0x00, + PowerSetting = 0x01, + PowerOff = 0x02, + PowerOffSequenceSetting = 0x03, + PowerOn = 0x04, + BoosterSoftStart = 0x06, + DeepSleep = 0x07, + DisplayRefresh = 0x12, + DataStartTransmission2 = 0x13, + DataStartTransmissionWindow = 0x14, + KwgVcomLutRegister = 0x20, + KwgLutRegister = 0x22, + FtLutRegister = 0x26, + PllControl = 0x30, + TemperatureSensor = 0x40, + TemperatureSensorEnable = 0x41, + TemperatureSensorWrite = 0x42, + TemperatureSensorRead = 0x43, + VcomAndDataIntervalSetting = 0x50, + LowPowerDetection = 0x51, + ResolutionSetting = 0x61, + GateGroupSetting = 0x62, + GateBlockSetting = 0x63, + GateSelectSetting = 0x64, + Revision = 0x70, + GetStatus = 0x71, + AutoMeasureVcom = 0x80, + VcomValue = 0x81, + VcomDcSetting = 0x82, + BorderDcVoltageSetting = 0x84, + LpdSelect = 0xE4, + }; + enum booster_soft_start_min_off { - BOOSTER_0p27us = 0b000, - BOOSTER_0p34us = 0b001, - BOOSTER_0p40us = 0b010, - BOOSTER_0p50us = 0b011, - BOOSTER_0p80us = 0b100, - BOOSTER_1p54us = 0b101, - BOOSTER_3p34us = 0b110, - BOOSTER_6p58us = 0b111, + soft_start_min_off_0p27us = 0b000, + soft_start_min_off_0p34us = 0b001, + soft_start_min_off_0p40us = 0b010, + soft_start_min_off_0p50us = 0b011, + soft_start_min_off_0p80us = 0b100, + soft_start_min_off_1p54us = 0b101, + soft_start_min_off_3p34us = 0b110, + soft_start_min_off_6p58us = 0b111, }; -enum start_drive_strength { - SDS_1 = 0b000, - SDS_2 = 0b001, - SDS_3 = 0b010, - SDS_4 = 0b011, - SDS_5 = 0b100, - SDS_6 = 0b101, - SDS_7 = 0b110, - SDS_8 = 0b111, // (strongest) +enum drive_strength { + drive_strength_1 = 0b000, + drive_strength_2 = 0b001, + drive_strength_3 = 0b010, + drive_strength_4 = 0b011, + drive_strength_5 = 0b100, + drive_strength_6 = 0b101, + drive_strength_7 = 0b110, + drive_strength_8 = 0b111, // (strongest) }; +enum soft_start_period { + soft_start_period_10ms = 0b00, + soft_start_period_20ms = 0b01, + soft_start_period_30ms = 0b10, + soft_start_period_40ms = 0b11, +}; + +enum lpd_select_lpdsel { + LPDSEL_2p2v = 0b00, + LPDSEL_2p3v = 0b01, + LPDSEL_2p4v = 0b10, + LPDSEL_2p5v = 0b11, // (default) +}; + +#define PHYSICAL_WIDTH 240 +#define PHYSICAL_HEIGHT 240 +#define EINK_WHITE 0xFF +#define EINK_BLACK 0x00 + static bool poll_gpio(uint32_t gpio, bool desired, uint8_t timeout) { lk_time_t now = current_time(); @@ -323,6 +470,11 @@ static bool poll_gpio(uint32_t gpio, bool desired, uint8_t timeout) return (current == desired); } +/* The display pulls the BUSY line low while BUSY and releases it when done */ +static bool check_busy(void) { + return poll_gpio(GPIO_DISP_BUSY, 1, 50); +} + static inline void assert_reset(void) { gpio_set(GPIO_DISP_RST, 0); } @@ -344,21 +496,43 @@ void write_cmd(uint8_t cmd) { cmd_buf[0] = cmd; set_data_command_mode(); +#if 0 + gpio_set(GPIO_DISP_CS, 0); + HAL_SPI_Transmit(&SpiHandle, cmd_buf, sizeof(cmd_buf), HAL_MAX_DELAY); + gpio_set(GPIO_DISP_CS, 1); +#else spi_write(&SpiHandle, cmd_buf, sizeof(cmd_buf), GPIO_DISP_CS); - set_data_parameter_mode(); +#endif } void write_data(uint8_t *buf, size_t len) { + set_data_parameter_mode(); + //enter_critical_section; +#if 0 + gpio_set(GPIO_DISP_CS, 0); + HAL_SPI_Transmit(&SpiHandle, buf, len, HAL_MAX_DELAY); + gpio_set(GPIO_DISP_CS, 1); +#else spi_write(&SpiHandle, buf, len, GPIO_DISP_CS); +#endif + //exit_critical_section; } -void read_data(uint8_t *buf, size_t len) { - spi_read(&SpiHandle, buf, len, GPIO_DISP_CS); +status_t read_data(uint8_t *buf, size_t len) { + return spi_read(&SpiHandle, buf, len, GPIO_DISP_CS); } -void get_status(void) { - // NOT IMPLEMENTED, just wait and pray the busy-ness works out :D - thread_sleep(50); +status_t get_status(et011tt2_status_t *status) { + status_t err; + + if (status == NULL) { + return ERR_INVALID_ARGS; + } + + write_cmd(GetStatus); + err = read_data((uint8_t *) status, sizeof(et011tt2_status_t)); + + return err; } // SpiBus must be configured in half-duplex mode. Display supports up to 6600 @@ -379,13 +553,29 @@ void get_status(void) { // L: Driver is busy, data/VCOM is transforming. // H: Not busy. Host side can send command/data to driver. -#define PHYSICAL_WIDTH 240 -#define PHYSICAL_HEIGHT 240 static int cmd_eink(int argc, const cmd_args *argv) { status_t err = NO_ERROR; - TRACE_ENTRY; +/* + volatile stm32f7_spi_t *spi = (stm32f7_spi_t *)SPI2; + SPIx_CR1_t CR1 = { + .br = fpclk_div_256; + .cpha = cpha_first_transition; + .cpol = cpol_clk_idle_low; + .mstr = mstr_spi_master; + .rxonly = rxonly_full_duplex; + .crcen = hw_crc_disable; + .bidi_mode = bidi_mode_1; + .bidi_output = bidi_output_enable; + .lsb_first = 0; + .ssm = ssm_disabled; + .ssi = 0; + cr1 = spi->CR1; + uint16_t cr2 = spi->CR2; + uint16_t sr = spi->SR;*/ + + if (!spi_inited) { SpiHandle.Instance = SPI2; @@ -417,12 +607,14 @@ static int cmd_eink(int argc, const cmd_args *argv) }; booster_settings_t booster = { - .btpha_min_off = BOOSTER_3p34us, - .btpha_drive_strength = SDS_5, - .btphb_min_off = BOOSTER_3p34us, - .btphb_drive_strength = SDS_5, - .btphc_min_off = BOOSTER_3p34us, - .btphc_drive_strength = SDS_5, + .btpha_min_off = soft_start_min_off_3p34us, + .btpha_drive_strength = drive_strength_8, + .btpha_soft_start = soft_start_period_10ms, + .btphb_min_off = soft_start_min_off_3p34us, + .btphb_drive_strength = drive_strength_8, + .btphb_soft_start = soft_start_period_10ms, + .btphc_min_off = soft_start_min_off_3p34us, + .btphc_drive_strength = drive_strength_8, }; vcom_data_int_settings_t vdi = { @@ -436,50 +628,139 @@ static int cmd_eink(int argc, const cmd_args *argv) .vres_low = PHYSICAL_HEIGHT - 1, }; - // Hold Display in reset - assert_reset(); + border_dc_v_settings_t bdvs = { + .vbds = 0x4e, // -4.0V + }; - // Pull down chip select - gpio_set(GPIO_DISP_CS, 1); + gate_group_settings_t ggs = { + .g1num = 0x09, // GG1[9:0] ON + .g1ud = 0, // GG1[9] -> GG1[0] + .g2num = 0x09, // GG2[9:0] ON + .g2ud = 0, // GG2[9] -> GG2[0] + .g3num = 0x0B, // GG3[11:0] ON + .g3ud = 0, // GG3[11] -> GG3[0] + .g3bs = 1, // Gate block + .g4num = 0x0B, // GG4[11:0] ON + .g4ud = 0, // GG4[11] -> GG4[0] + .g4bs = 1, // Gate block + }; - // Set data_command to 'Command' - gpio_set(GPIO_DISP_DC, 0); + lpdselect_t lpds = { + .lpd_sel = LPDSEL_2p4v, + }; - if (!poll_gpio(GPIO_DISP_BUSY, 0, 50)) { - printf("err: Display should be BUSY while held in RST.\n"); - goto err; - } - - // Pull display out of reset - release_reset(); - - // datasheet says to wait 1ms coming out of reset - if (!poll_gpio(GPIO_DISP_BUSY, 1, 1)) { - printf("err: Display should not be BUSY after coming out of reset.\n"); - goto err; - } - - write_cmd(PowerSetting); - write_data((uint8_t *)&pwr, sizeof(pwr)); - - // TODO: Need to get half duplex working for status checks - get_status(); - - write_cmd(PowerOn); - if (!poll_gpio(GPIO_DISP_BUSY, 1, 1)) { - printf("err: Display should not be BUSY after power_on\n"); - goto err; + display_refresh_t dr = { + .dn_en = 1, + .pscan = 0, + .rgl_en = 0, + .x = 0, + .y_low = 0, + .w = 240, + .l_high = 0, + .l_low = 240, + }; + + data_transmission_window_t dtw = { + .x = 0, + .y_low = 0, + .w = 240, + .l_high = 0, + .l_low = 240, + }; + + // VDD (wired straight to 3v3) + spin(2000); // Delay 2 ms + assert_reset(); // RST_LOW + spin(30); // Delay 30 us + release_reset(); // RST_HIGH + + if (!check_busy()) { + printf("Device is still busy after initial reset!\n"); + return ERR_GENERIC; } + // Configure Boost write_cmd(BoosterSoftStart); write_data((uint8_t *)&booster, sizeof(booster)); + // Power on display + write_cmd(PowerOn); + + // Initialize -> Check_Busy + if (!check_busy()) { + printf("Device is still busy after Power On!\n"); + return ERR_GENERIC; + } + + /* Quick buffer to toss at it */ + uint8_t buf[128]; + for (size_t i = 0; i < sizeof(buf); i++) { + buf[i] = i % 255; + } + + // DTMW + write_cmd(DataStartTransmissionWindow); + write_data((uint8_t *) &dtw, sizeof(dtw)); + + // DTM2 + write_cmd(DataStartTransmission2); + write_data(buf, sizeof(buf)); + + // DRF + write_cmd(DisplayRefresh); + write_data((uint8_t *)&dr, sizeof(dr)); + + // Check_Busy + if (!check_busy()) { + printf("Device is still busy after Display Refresh!\n"); + return ERR_GENERIC; + } + + // POF + write_cmd(PowerOff); + + // Check_Busy + if (!check_busy()) { + printf("Device is still busy after Power Off!\n"); + return ERR_GENERIC; + } + + // DSLP + uint8_t sleepbuf = 0b10100101; + write_cmd(DeepSleep); + write_data(&sleepbuf, sizeof(sleepbuf)); + // + return 0; + + +/* + * Config writes that need to be verified against the tables and defaults to ensure + * I'm not missing important predefined bits. write_cmd(VcomAndDataIntervalSetting); write_data((uint8_t *)&vdi, sizeof(vdi)); write_cmd(ResolutionSetting); write_data((uint8_t *)&rs, sizeof(rs)); + write_cmd(GateGroupSetting); + write_data((uint8_t *)&ggs, sizeof(ggs)); + + write_cmd(BorderDcVoltageSetting); + write_data((uint8_t *)&bdvs, sizeof(bdvs)); + + write_cmd(LpdSelect); + write_data((uint8_t *)&lpds, sizeof(lpds)); + + write_cmd(FtLutRegister); + write_data(lut_ft, sizeof(lut_ft)); + + write_cmd(KwgVcomLutRegister); + write_data(lut_kwg_vcom, sizeof(lut_kwg_vcom)); + + write_cmd(KwgLutRegister); + write_data(lut_kwg, sizeof(lut_kwg)); + +*/ err: return err; }