mirror of
https://github.com/Dejvino/lilybook.git
synced 2024-11-14 12:23:28 +00:00
790 lines
23 KiB
C
790 lines
23 KiB
C
/*
|
||
* Author: LoBo (loboris@gmail.com, loboris.github)
|
||
*
|
||
* Module supporting SPI ePaper displays
|
||
*
|
||
* HIGH SPEED LOW LEVEL DISPLAY FUNCTIONS
|
||
* USING DIRECT or DMA SPI TRANSFER MODEs
|
||
*
|
||
*/
|
||
|
||
|
||
#include "spidriver/spi_master_lobo.h"
|
||
#include <errno.h>
|
||
#include <stdio.h>
|
||
#include <time.h>
|
||
#include <string.h>
|
||
#include "esp_system.h"
|
||
#include "freertos/FreeRTOS.h"
|
||
#include "freertos/task.h"
|
||
#include "esp_heap_alloc_caps.h"
|
||
#include "soc/spi_reg.h"
|
||
#include "EPDspi.h"
|
||
|
||
#define EPD_DEBUG 1
|
||
|
||
#define EPD_BORDER_WHITE 0x61
|
||
#define EPD_BORDER_BLACK 0x51
|
||
#define EPD_BORDER EPD_BORDER_BLACK
|
||
|
||
#define EPD2X9 1
|
||
|
||
#define xDot 128
|
||
#define yDot 296
|
||
#define DELAYTIME 1500
|
||
|
||
// ===== Global ======
|
||
spi_lobo_device_handle_t disp_spi;
|
||
uint8_t *gs_disp_buffer;
|
||
uint8_t *disp_buffer;
|
||
uint8_t *gs_drawBuff;
|
||
uint8_t *drawBuff;
|
||
int _width;
|
||
int _height;
|
||
uint16_t gs_used_shades;
|
||
uint8_t _gs;
|
||
uint8_t *LUT_part;
|
||
uint8_t LUTDefault_fastest[31];
|
||
uint8_t LUTDefault_part[31];
|
||
uint8_t LUT_gs[31];
|
||
uint8_t LUTDefault_full[31];
|
||
uint8_t lvl_buf[16];
|
||
uint8_t lvl_buf_jpg[16];
|
||
// =====
|
||
|
||
static uint8_t GDOControl[] = {0x01, (yDot-1)%256, (yDot-1)/256, 0x00};
|
||
static uint8_t softstart[4] = {0x0c, 0xd7, 0xd6, 0x9d};
|
||
static uint8_t VCOMVol[2] = {0x2c, 0xa8}; // VCOM 7c
|
||
static uint8_t DummyLine[2] = {0x3a, 0x1a}; // 4 dummy line per gate
|
||
static uint8_t Gatetime[2] = {0x3b, 0x08}; // 2us per line
|
||
static uint8_t RamDataEntryMode[2] = {0x11, 0x01}; // Ram data entry mode
|
||
static uint8_t Border[2] = {0x3c, EPD_BORDER}; // Border control ( 0x61: white border; 0x51: black border
|
||
|
||
/*
|
||
There are totally 20 phases for programmable Source waveform of different phase length.
|
||
The phase period defined as TP [n] * T FRAME , where TP [n] range from 0 to 15.
|
||
TP [n] = 0 indicates phase skipped
|
||
Source Voltage Level: VS [n-XY] is constant in each phase
|
||
VS [n-XY] indicates the voltage in phase n for transition from GS X to GS Y
|
||
00 – VSS
|
||
01 – VSH
|
||
10 – VSL
|
||
11 – NA
|
||
VS [n-XY] and TP[n] are stored in waveform lookup table register [LUT].
|
||
|
||
VS coding: VS[0-11] VS[0-10] VS[0-01] VS[0-00]
|
||
|
||
*/
|
||
// --- VS ---- ---- TP ----
|
||
//uint8_t LUTDefault_full[31] = {0x32, 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};
|
||
uint8_t LUTDefault_full[31] = {0x32, 0x11,0x11,0x10,0x02,0x02,0x22,0x22,0x22,0x22,0x22,0x51,0x51,0x55,0x88,0x08,0x08,0x88,0x88,0x00,0x00, 0x34,0x23,0x12,0x21,0x24,0x28,0x22,0x21,0xA1,0x01};
|
||
uint8_t LUTDefault_part[31] = {0x32, 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};
|
||
uint8_t LUT_gs[31] = {0x32, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
|
||
uint8_t LUTFastest[31] = {0x32, 0x99,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
|
||
|
||
uint8_t lvl_buf[16] = {32,70,110,150,185,210,220,225,230,235,240,243,248,251,253,255};
|
||
uint8_t lvl_buf_jpg[16] = {4,8,12,16,22,30,40,60,80,110,140,180,220,240,250,255};
|
||
|
||
uint8_t *LUT_part = LUTDefault_part;
|
||
spi_lobo_device_handle_t disp_spi = NULL;
|
||
uint8_t *gs_disp_buffer = NULL;
|
||
uint8_t *disp_buffer = NULL;
|
||
uint8_t *drawBuff = NULL;
|
||
uint8_t *gs_drawBuff = NULL;
|
||
int _width = EPD_DISPLAY_WIDTH;
|
||
int _height = EPD_DISPLAY_HEIGHT;
|
||
uint8_t _gs = 0;
|
||
|
||
uint16_t gs_used_shades = 0;
|
||
|
||
//-----------------------------------------------------------
|
||
static void IRAM_ATTR _dma_send(uint8_t *data, uint32_t size)
|
||
{
|
||
//Fill DMA descriptors
|
||
spi_lobo_dmaworkaround_transfer_active(disp_spi->host->dma_chan); //mark channel as active
|
||
spi_lobo_setup_dma_desc_links(disp_spi->host->dmadesc_tx, size, data, false);
|
||
disp_spi->host->hw->user.usr_mosi_highpart=0;
|
||
disp_spi->host->hw->dma_out_link.addr=(int)(&disp_spi->host->dmadesc_tx[0]) & 0xFFFFF;
|
||
disp_spi->host->hw->dma_out_link.start=1;
|
||
disp_spi->host->hw->user.usr_mosi_highpart=0;
|
||
|
||
disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = (size * 8) - 1;
|
||
|
||
// Start transfer
|
||
disp_spi->host->hw->cmd.usr = 1;
|
||
// Wait for SPI bus ready
|
||
while (disp_spi->host->hw->cmd.usr);
|
||
|
||
//Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset.
|
||
if (disp_spi->host->dma_chan) spi_lobo_dmaworkaround_idle(disp_spi->host->dma_chan);
|
||
|
||
// Reset DMA
|
||
disp_spi->host->hw->dma_conf.val |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST;
|
||
disp_spi->host->hw->dma_out_link.start=0;
|
||
disp_spi->host->hw->dma_in_link.start=0;
|
||
disp_spi->host->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST);
|
||
disp_spi->host->hw->dma_conf.out_data_burst_en=1;
|
||
}
|
||
|
||
//--------------------------------------------------------------------------
|
||
static void IRAM_ATTR _direct_send(uint8_t *data, uint32_t len, uint8_t rep)
|
||
{
|
||
uint32_t cidx = 0; // buffer index
|
||
uint32_t wd = 0;
|
||
int idx = 0;
|
||
int bits = 0;
|
||
int wbits = 0;
|
||
|
||
taskDISABLE_INTERRUPTS();
|
||
|
||
while (len) {
|
||
|
||
wd |= (uint32_t)data[idx] << wbits;
|
||
wbits += 8;
|
||
if (wbits == 32) {
|
||
bits += wbits;
|
||
wbits = 0;
|
||
disp_spi->host->hw->data_buf[idx++] = wd;
|
||
wd = 0;
|
||
}
|
||
len--; // Decrement data counter
|
||
if (rep == 0) cidx++; // if not repeating data, increment buffer index
|
||
}
|
||
if (bits) {
|
||
while (disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready
|
||
// Load send buffer
|
||
disp_spi->host->hw->user.usr_mosi_highpart = 0;
|
||
disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = bits-1;
|
||
disp_spi->host->hw->user.usr_mosi = 1;
|
||
disp_spi->host->hw->miso_dlen.usr_miso_dbitlen = 0;
|
||
disp_spi->host->hw->user.usr_miso = 0;
|
||
disp_spi->host->hw->cmd.usr = 1; // Start transfer
|
||
}
|
||
// Wait for SPI bus ready
|
||
while (disp_spi->host->hw->cmd.usr);
|
||
taskENABLE_INTERRUPTS();
|
||
}
|
||
|
||
// ================================================================
|
||
// === Main function to send data to display ======================
|
||
// If rep==true: repeat sending data to display 'len' times
|
||
// If rep==false: send 'len' data bytes from buffer to display
|
||
// ** Device must already be selected and address window set **
|
||
// ================================================================
|
||
//---------------------------------------------------------------------------
|
||
static void IRAM_ATTR SPI_send_data(uint8_t *data, uint32_t len, uint8_t rep)
|
||
{
|
||
if (len == 0) return;
|
||
|
||
if ((len*8) <= 512) _direct_send(data, len, rep);
|
||
else if (rep == 0) _dma_send(data, len);
|
||
else {
|
||
// ==== Repeat data, more than 512 bits total ====
|
||
uint8_t *transbuf = pvPortMallocCaps(len, MALLOC_CAP_DMA);
|
||
if (transbuf == NULL) return;
|
||
|
||
memset(transbuf, data[0], len);
|
||
_dma_send(transbuf, len);
|
||
free(transbuf);
|
||
}
|
||
}
|
||
|
||
// Send one byte to display
|
||
//-------------------------------------
|
||
void IRAM_ATTR SPI_Write(uint8_t value)
|
||
{
|
||
disp_spi->host->hw->data_buf[0] = (uint32_t)value;
|
||
// Load send buffer
|
||
disp_spi->host->hw->user.usr_mosi_highpart = 0;
|
||
disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7;
|
||
disp_spi->host->hw->user.usr_mosi = 1;
|
||
disp_spi->host->hw->miso_dlen.usr_miso_dbitlen = 0;
|
||
disp_spi->host->hw->user.usr_miso = 0;
|
||
// Start transfer
|
||
disp_spi->host->hw->cmd.usr = 1;
|
||
// Wait for SPI bus ready
|
||
while (disp_spi->host->hw->cmd.usr);
|
||
}
|
||
|
||
// Check display busy line and wait while busy
|
||
//-----------------------
|
||
static uint8_t ReadBusy()
|
||
{
|
||
for (int i=0; i<400; i++){
|
||
if (isEPD_BUSY == EPD_BUSY_LEVEL) return 1;
|
||
vTaskDelay(10 / portTICK_RATE_MS);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
//-----------------------
|
||
static uint8_t WaitBusy()
|
||
{
|
||
if (isEPD_BUSY != EPD_BUSY_LEVEL) return 1;
|
||
vTaskDelay(10 / portTICK_RATE_MS);
|
||
if (isEPD_BUSY != EPD_BUSY_LEVEL) return 1;
|
||
return 0;
|
||
}
|
||
|
||
// Write one command without parameters
|
||
//---------------------------------------
|
||
static void EPD_WriteCMD(uint8_t command)
|
||
{
|
||
spi_lobo_device_select(disp_spi, 0);
|
||
EPD_DC_0; // command write
|
||
SPI_Write(command);
|
||
}
|
||
|
||
// Write command with one paramet
|
||
//---------------------------------------
|
||
static void EPD_WriteCMD_p1(uint8_t command,uint8_t para)
|
||
{
|
||
spi_lobo_device_select(disp_spi, 0);
|
||
//ReadBusy();
|
||
EPD_DC_0; // command write
|
||
SPI_Write(command);
|
||
EPD_DC_1; // data write
|
||
SPI_Write(para);
|
||
spi_lobo_device_deselect(disp_spi);
|
||
}
|
||
|
||
//----------------
|
||
void EPD_PowerOn()
|
||
{
|
||
EPD_WriteCMD_p1(0x22,0xc0);
|
||
EPD_WriteCMD(0x20);
|
||
//EPD_WriteCMD(0xff);
|
||
spi_lobo_device_deselect(disp_spi);
|
||
#if EPD_DEBUG
|
||
if (!WaitBusy()) printf("[EPD] NOT BUSY\r\n");
|
||
if (!ReadBusy()) printf("[EPD] NOT READY\r\n");
|
||
#else
|
||
WaitBusy();
|
||
ReadBusy();
|
||
#endif
|
||
}
|
||
|
||
//-----------------
|
||
void EPD_PowerOff()
|
||
{
|
||
EPD_WriteCMD_p1(0x22,0x03);
|
||
EPD_WriteCMD(0x20);
|
||
//EPD_WriteCMD(0xff);
|
||
spi_lobo_device_deselect(disp_spi);
|
||
#if EPD_DEBUG
|
||
if (!WaitBusy()) printf("[EPD] NOT BUSY\r\n");
|
||
if (!ReadBusy()) printf("[EPD] NOT READY\r\n");
|
||
#else
|
||
WaitBusy();
|
||
ReadBusy();
|
||
#endif
|
||
#ifdef POWER_Pin
|
||
gpio_set_level(DC_Pin, 0);
|
||
gpio_set_level(MOSI_Pin, 0);
|
||
gpio_set_level(SCK_Pin, 0);
|
||
gpio_set_level(RST_Pin, 0);
|
||
gpio_set_level(CS_Pin, 0);
|
||
gpio_set_level(POWER_Pin, 0);
|
||
#endif
|
||
}
|
||
|
||
// Send command with multiple parameters
|
||
//----------------------------------------------------
|
||
static void EPD_Write(uint8_t *value, uint8_t datalen)
|
||
{
|
||
uint8_t i = 0;
|
||
uint8_t *ptemp;
|
||
|
||
ptemp = value;
|
||
spi_lobo_device_select(disp_spi, 0);
|
||
//ReadBusy();
|
||
EPD_DC_0; // When DC is 0, write command
|
||
SPI_Write(*ptemp); //The first byte is written with the command value
|
||
ptemp++;
|
||
EPD_DC_1; // When DC is 1, write data
|
||
for(i= 0;i<datalen-1;i++){ // sub the data
|
||
SPI_Write(*ptemp);
|
||
ptemp++;
|
||
}
|
||
spi_lobo_device_deselect(disp_spi);
|
||
}
|
||
|
||
// Send data buffer to display
|
||
//----------------------------------------------------------------------------
|
||
static void EPD_WriteDispRam(uint8_t XSize, uint16_t YSize, uint8_t *Dispbuff)
|
||
{
|
||
if (XSize%8 != 0) XSize = XSize+(8-XSize%8);
|
||
XSize = XSize/8;
|
||
|
||
spi_lobo_device_select(disp_spi, 0);
|
||
//ReadBusy();
|
||
EPD_DC_0; //command write
|
||
SPI_Write(0x24);
|
||
EPD_DC_1; //data write
|
||
SPI_send_data(Dispbuff, XSize*YSize, 0);
|
||
spi_lobo_device_deselect(disp_spi);
|
||
}
|
||
|
||
// Fill the display with value
|
||
//-------------------------------------------------------------------------------
|
||
static void EPD_WriteDispRamMono(uint8_t XSize, uint16_t YSize, uint8_t dispdata)
|
||
{
|
||
if (XSize%8 != 0) XSize = XSize+(8-XSize%8);
|
||
XSize = XSize/8;
|
||
|
||
spi_lobo_device_select(disp_spi, 0);
|
||
//ReadBusy();
|
||
EPD_DC_0; // command write
|
||
SPI_Write(0x24);
|
||
EPD_DC_1; // data write
|
||
SPI_send_data(&dispdata, XSize*YSize, 1);
|
||
spi_lobo_device_deselect(disp_spi);
|
||
}
|
||
|
||
/*
|
||
=== Set RAM X - Address Start / End Position (44h) ===
|
||
Specify the start/end positions of the window address in the X direction by 8 times address unit.
|
||
Data is written to the RAM within the area determined by the addresses specified by XSA [4:0] and XEA [4:0].
|
||
These addresses must be set before the RAM write. It allows on XEA [4:0] ≤ XSA [4:0].
|
||
The settings follow the condition on 00h ≤ XSA [4:0], XEA [4:0] ≤ 1Dh.
|
||
The windows is followed by the control setting of Data Entry Setting (R11h)
|
||
|
||
=== Set RAM Y - Address Start / End Position (45h) ===
|
||
Specify the start/end positions of the window address in the Y direction by an address unit.
|
||
Data is written to the RAM within the area determined by the addresses specified by YSA [8:0] and YEA [8:0].
|
||
These addresses must be set before the RAM write.
|
||
It allows YEA [8:0] ≤ YSA [8:0].
|
||
The settings follow the condition on 00h ≤ YSA [8:0], YEA [8:0] ≤ 13Fh.
|
||
The windows is followed by the control setting of Data Entry Setting (R11h)
|
||
*/
|
||
//--------------------------------------------------------------------------------------
|
||
static void EPD_SetRamArea(uint8_t Xstart, uint8_t Xend, uint16_t Ystart, uint16_t Yend)
|
||
{
|
||
uint8_t RamAreaX[3]; // X start and end
|
||
uint8_t RamAreaY[5]; // Y start and end
|
||
RamAreaX[0] = 0x44; // command
|
||
RamAreaX[1] = Xstart;
|
||
RamAreaX[2] = Xend;
|
||
RamAreaY[0] = 0x45; // command
|
||
RamAreaY[1] = Ystart & 0xFF;
|
||
RamAreaY[2] = Ystart >> 8;
|
||
RamAreaY[3] = Yend & 0xFF;
|
||
RamAreaY[4] = Yend >> 8;
|
||
EPD_Write(RamAreaX, sizeof(RamAreaX));
|
||
EPD_Write(RamAreaY, sizeof(RamAreaY));
|
||
}
|
||
|
||
//Set RAM X and Y address counter
|
||
/*
|
||
=== Set RAM Address Counter (4Eh-4Fh) ===
|
||
adrX[4:0]: Make initial settings for the RAM X address in the address counter (AC).
|
||
adrY[8:0]: Make initial settings for the RAM Y address in the address counter (AC).
|
||
After RAM data is written, the address counter is automatically updated according to the settings with AM, ID
|
||
bits and setting for a new RAM address is not required in the address counter.
|
||
Therefore, data is written consecutively without setting an address.
|
||
The address counter is not automatically updated when data is read out from the RAM.
|
||
RAM address setting cannot be made during the standby mode.
|
||
The address setting should be made within the area designated with window addresses which is controlled
|
||
by the Data Entry Setting (R11h) {AM, ID[1:0]} ; RAM Address XStart / XEnd Position (R44h) and RAM Address Ystart /Yend Position (R45h).
|
||
Otherwise undesirable image will be displayed on the Panel.
|
||
*/
|
||
//----------------------------------------------------------
|
||
static void EPD_SetRamPointer(uint8_t addrX, uint16_t addrY)
|
||
{
|
||
uint8_t RamPointerX[2]; // default (0,0)
|
||
uint8_t RamPointerY[3];
|
||
//Set RAM X address counter
|
||
RamPointerX[0] = 0x4e;
|
||
RamPointerX[1] = addrX;
|
||
//Set RAM Y address counter
|
||
RamPointerY[0] = 0x4f;
|
||
RamPointerY[1] = addrY & 0xFF;
|
||
RamPointerY[2] = addrY >> 8;
|
||
|
||
EPD_Write(RamPointerX, sizeof(RamPointerX));
|
||
EPD_Write(RamPointerY, sizeof(RamPointerY));
|
||
}
|
||
|
||
|
||
//Set RAM X and Y address Start / End position
|
||
//Set RAM X and Y address counter
|
||
//----------------------------------------------------------------------------------------------
|
||
static void part_display(uint8_t RAM_XST, uint8_t RAM_XEND ,uint16_t RAM_YST, uint16_t RAM_YEND)
|
||
{
|
||
EPD_SetRamArea(RAM_XST, RAM_XEND, RAM_YST, RAM_YEND);
|
||
EPD_SetRamPointer (RAM_XST, RAM_YST);
|
||
}
|
||
|
||
//Initialize the display
|
||
//--------------------
|
||
static void EPD_Init()
|
||
{
|
||
#ifdef POWER_Pin
|
||
gpio_set_level(POWER_Pin, 1);
|
||
vTaskDelay(100 / portTICK_RATE_MS);
|
||
#else
|
||
vTaskDelay(10 / portTICK_RATE_MS);
|
||
#endif
|
||
// reset
|
||
EPD_RST_0;
|
||
vTaskDelay(10 / portTICK_RATE_MS);
|
||
#if EPD_DEBUG
|
||
uint32_t t1 = clock();
|
||
#endif
|
||
EPD_RST_1;
|
||
for (int n=0; n<50; n++) {
|
||
vTaskDelay(10 / portTICK_RATE_MS);
|
||
if (isEPD_BUSY == EPD_BUSY_LEVEL) break;
|
||
}
|
||
|
||
SPI_Write(0x12); // software reset
|
||
vTaskDelay(10 / portTICK_RATE_MS);
|
||
ReadBusy();
|
||
|
||
// set registers
|
||
EPD_Write(GDOControl, sizeof(GDOControl)); // Panel configuration, Gate selection
|
||
EPD_Write(softstart, sizeof(softstart)); // X decrease, Y decrease
|
||
EPD_Write(VCOMVol, sizeof(VCOMVol)); // VCOM setting
|
||
EPD_Write(DummyLine, sizeof(DummyLine)); // dummy line per gate
|
||
EPD_Write(Gatetime, sizeof(Gatetime)); // Gate time setting
|
||
EPD_Write(Border, sizeof(Border));
|
||
EPD_Write(RamDataEntryMode, sizeof(RamDataEntryMode)); // X increase, Y decrease
|
||
|
||
EPD_SetRamArea(0x00, (xDot-1)/8, yDot-1, 0);
|
||
EPD_SetRamPointer(0x00, yDot-1);
|
||
#if EPD_DEBUG
|
||
t1 = clock() - t1;
|
||
printf("[EPD] Init: %u ms\r\n", t1);
|
||
#endif
|
||
}
|
||
|
||
//------------------------------
|
||
static void EPD_UpdateFull(void)
|
||
{
|
||
/*
|
||
+ Enable Clock Signal,
|
||
+ Then Enable CP
|
||
- Then Load Temperature value
|
||
- Then Load LUT
|
||
- Then INITIAL DISPLAY
|
||
+ Then PATTERN DISPLAY
|
||
+ Then Disable CP
|
||
+ Then Disable OSC
|
||
*/
|
||
EPD_WriteCMD_p1(0x22,0xC7);
|
||
EPD_WriteCMD(0x20);
|
||
//EPD_WriteCMD(0xff);
|
||
spi_lobo_device_deselect(disp_spi);
|
||
|
||
#if EPD_DEBUG
|
||
if (!WaitBusy()) printf("[EPD] NOT BUSY\r\n");
|
||
if (!ReadBusy()) printf("[EPD] NOT READY\r\n");
|
||
#else
|
||
WaitBusy();
|
||
ReadBusy();
|
||
#endif
|
||
}
|
||
|
||
//-------------------------------
|
||
static void EPD_Update_Part(void)
|
||
{
|
||
/*
|
||
- Enable Clock Signal,
|
||
- Then Enable CP
|
||
- Then Load Temperature value
|
||
- Then Load LUT
|
||
- Then INITIAL DISPLAY
|
||
+ Then PATTERN DISPLAY
|
||
- Then Disable CP
|
||
- Then Disable OSC
|
||
*/
|
||
EPD_WriteCMD_p1(0x22,0x04);
|
||
EPD_WriteCMD(0x20);
|
||
//EPD_WriteCMD(0xff);
|
||
spi_lobo_device_deselect(disp_spi);
|
||
|
||
#if EPD_DEBUG
|
||
if (!WaitBusy()) printf("[EPD] NOT BUSY\r\n");
|
||
if (!ReadBusy()) printf("[EPD] NOT READY\r\n");
|
||
#else
|
||
WaitBusy();
|
||
ReadBusy();
|
||
#endif
|
||
}
|
||
|
||
/*******************************************************************************
|
||
Full screen initialization
|
||
********************************************************************************/
|
||
static void EPD_init_Full(void)
|
||
{
|
||
EPD_Init(); // Reset and set register
|
||
EPD_Write((uint8_t *)LUTDefault_full,sizeof(LUTDefault_full));
|
||
|
||
EPD_PowerOn();
|
||
}
|
||
|
||
/*******************************************************************************
|
||
Part screen initialization
|
||
********************************************************************************/
|
||
static void EPD_init_Part(void)
|
||
{
|
||
EPD_Init(); // display
|
||
EPD_Write((uint8_t *)LUT_part, 31);
|
||
EPD_PowerOn();
|
||
}
|
||
|
||
void EPD_wake()
|
||
{
|
||
EPD_Init();
|
||
EPD_PowerOn();
|
||
}
|
||
|
||
/********************************************************************************
|
||
parameter:
|
||
Label :
|
||
=1 Displays the contents of the DisBuffer
|
||
=0 Displays the contents of the first byte in DisBuffer,
|
||
********************************************************************************/
|
||
static void EPD_Dis_Full(uint8_t *DisBuffer,uint8_t type)
|
||
{
|
||
EPD_SetRamPointer(0x00, yDot-1); // set ram pointer
|
||
if (type == 0){
|
||
// Fill screen with white
|
||
EPD_WriteDispRamMono(xDot, yDot, 0xff);
|
||
}
|
||
else {
|
||
// Fill screen from buffer
|
||
EPD_WriteDispRam(xDot, yDot, (uint8_t *)DisBuffer);
|
||
}
|
||
EPD_UpdateFull();
|
||
|
||
}
|
||
|
||
/********************************************************************************
|
||
WARNING: X is smaller screen dimension (0~127) !
|
||
Y is larger screen dimension (0~295) !
|
||
parameter:
|
||
xStart : X direction Start coordinate
|
||
xEnd : X direction end coordinate
|
||
yStart : Y direction Start coordinate
|
||
yEnd : Y direction end coordinate
|
||
DisBuffer : Display content
|
||
type :
|
||
=1 Displays the contents of the DisBuffer
|
||
=0 Displays the contents of the first byte in DisBuffer,
|
||
********************************************************************************/
|
||
static void EPD_Dis_Part(uint8_t xStart, uint8_t xEnd, uint16_t yStart, uint16_t yEnd, uint8_t *DisBuffer, uint8_t type)
|
||
{
|
||
if (type == 0) {
|
||
// Repeated color
|
||
part_display(xStart/8, xEnd/8, yEnd, yStart);
|
||
EPD_WriteDispRamMono(xEnd-xStart+1, yEnd-yStart+1, DisBuffer[0]);
|
||
EPD_Update_Part();
|
||
part_display(xStart/8, xEnd/8, yEnd, yStart);
|
||
EPD_WriteDispRamMono(xEnd-xStart+1, yEnd-yStart+1, DisBuffer[0]);
|
||
}
|
||
else {
|
||
// From buffer
|
||
part_display(xStart/8, xEnd/8, yEnd, yStart);
|
||
EPD_WriteDispRam(xEnd-xStart+1, yEnd-yStart+1,DisBuffer);
|
||
EPD_Update_Part();
|
||
part_display(xStart/8, xEnd/8, yEnd, yStart);
|
||
EPD_WriteDispRam(xEnd-xStart+1, yEnd-yStart+1,DisBuffer);
|
||
}
|
||
}
|
||
|
||
//======================================================================================================================================
|
||
|
||
// Clear full screen
|
||
//=========================
|
||
void EPD_DisplayClearFull()
|
||
{
|
||
uint8_t m;
|
||
EPD_init_Full();
|
||
|
||
#if EPD_DEBUG
|
||
uint32_t t1 = clock();
|
||
#endif
|
||
m = 0x00;
|
||
EPD_Dis_Full(&m, 0); //all black
|
||
#if EPD_DEBUG
|
||
t1 = clock() - t1;
|
||
printf("[EPD] Clear black: %u ms\r\n", t1);
|
||
t1 = clock();
|
||
#endif
|
||
m = 0xff;
|
||
EPD_Dis_Full(&m, 0); //all white
|
||
#if EPD_DEBUG
|
||
t1 = clock() - t1;
|
||
printf("[EPD] Clear white: %u ms\r\n", t1);
|
||
#endif
|
||
}
|
||
|
||
// Partial clear screen
|
||
//=========================
|
||
void EPD_DisplayClearPart()
|
||
{
|
||
uint8_t m = 0xFF;
|
||
EPD_init_Part();
|
||
#if EPD_DEBUG
|
||
uint32_t t1 = clock();
|
||
EPD_Dis_Part(0, xDot-1, 0, yDot-1, &m, 0); //all white
|
||
m = 0x00;
|
||
EPD_Dis_Part(0, xDot-1, 0, yDot-1, &m, 0); //all black
|
||
m = 0xFF;
|
||
EPD_Dis_Part(0, xDot-1, 0, yDot-1, &m, 0); //all white
|
||
t1 = clock() - t1;
|
||
printf("[EPD] Part Clear: %u ms\r\n", t1);
|
||
#else
|
||
EPD_Dis_Part(0, xDot-1, 0, yDot-1, &m, 0); //all white
|
||
m = 0x00;
|
||
EPD_Dis_Part(0, xDot-1, 0, yDot-1, &m, 0); //all black
|
||
m = 0xFF;
|
||
EPD_Dis_Part(0, xDot-1, 0, yDot-1, &m, 0); //all white
|
||
#endif
|
||
}
|
||
|
||
//==================================
|
||
void EPD_DisplaySetFull(uint8_t val)
|
||
{
|
||
EPD_Write((uint8_t *)LUTDefault_full,sizeof(LUTDefault_full));
|
||
#if EPD_DEBUG
|
||
uint32_t t1 = clock();
|
||
EPD_Dis_Full(&val, 0);
|
||
t1 = clock() - t1;
|
||
printf("[EPD] Display Set Full: %u ms [%02x]\r\n", t1, val);
|
||
#else
|
||
EPD_Dis_Full(&val, 0);
|
||
#endif
|
||
}
|
||
|
||
//======================================================================================
|
||
void EPD_DisplaySetPart(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd, uint8_t val)
|
||
{
|
||
EPD_Write((uint8_t *)LUT_part, 31);
|
||
#if EPD_DEBUG
|
||
uint32_t t1 = clock();
|
||
EPD_Dis_Part(yStart,yEnd,xStart,xEnd, &val,0);
|
||
t1 = clock() - t1;
|
||
printf("[EPD] Display Set Part: %u ms [%02x]\r\n", t1, val);
|
||
#else
|
||
EPD_Dis_Part(yStart,yEnd,xStart,xEnd, &val,0);
|
||
#endif
|
||
}
|
||
|
||
//======================================
|
||
void EPD_DisplayFull(uint8_t *DisBuffer)
|
||
{
|
||
EPD_Write((uint8_t *)LUTDefault_full,sizeof(LUTDefault_full));
|
||
#if EPD_DEBUG
|
||
uint32_t t1 = clock();
|
||
EPD_Dis_Full((uint8_t *)DisBuffer,1);
|
||
t1 = clock() - t1;
|
||
printf("[EPD] Display Full: %u ms\r\n", t1);
|
||
#else
|
||
EPD_Dis_Full((uint8_t *)DisBuffer,1);
|
||
#endif
|
||
}
|
||
|
||
//==========================================================================================
|
||
void EPD_DisplayPart(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd, uint8_t *DisBuffer)
|
||
{
|
||
EPD_Write((uint8_t *)LUT_part, 31);
|
||
#if EPD_DEBUG
|
||
uint32_t t1 = clock();
|
||
EPD_Dis_Part(yStart,yEnd,xStart,xEnd,(uint8_t *)DisBuffer,1);
|
||
t1 = clock() - t1;
|
||
printf("[EPD] Display Part: %u ms [%02x:%02x]\r\n", t1, LUT_gs[1], LUT_gs[21]);
|
||
#else
|
||
EPD_Dis_Part(yStart,yEnd,xStart,xEnd,(uint8_t *)DisBuffer,1);
|
||
#endif
|
||
}
|
||
|
||
//============
|
||
void EPD_Cls()
|
||
{
|
||
EPD_DisplaySetPart(0, EPD_DISPLAY_WIDTH-1, 0, EPD_DISPLAY_HEIGHT-1, 0xFF);
|
||
memset(disp_buffer, 0xFF, _width * (_height/8));
|
||
memset(gs_disp_buffer, 0, _width * _height);
|
||
gs_used_shades = 0;
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------
|
||
void EPD_gsUpdate(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd, uint8_t gs)
|
||
{
|
||
uint8_t val, buf_val, new_val;
|
||
int count=0, changed=0;
|
||
int x;
|
||
uint8_t y;
|
||
for (x=xStart; x<=xEnd; x++) {
|
||
for (y=yStart; y<=yEnd; y++) {
|
||
val = gs_drawBuff[(y * (xEnd-xStart+1)) + x];
|
||
if (val > 15) val >>= 4;
|
||
if (val == gs) {
|
||
buf_val = drawBuff[(x * ((yEnd-yStart+1)>>3)) + (y>>3)];
|
||
new_val = buf_val;
|
||
if (gs > 0) new_val &= (0x80 >> (y % 8)) ^ 0xFF;
|
||
else new_val |= (0x80 >> (y % 8));
|
||
if (new_val != buf_val) {
|
||
drawBuff[(x * (_height>>3)) + (y>>3)] = new_val;
|
||
changed++;
|
||
}
|
||
count++;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (changed) {
|
||
#if EPD_DEBUG
|
||
printf("[EPD] GS Update %02x, count=%d changed=%d\r\n", gs, count, changed);
|
||
#endif
|
||
uint8_t *_lutPart = LUT_part;
|
||
memset(LUT_gs+1, 0, 30);
|
||
if (gs > 0) {
|
||
if (gs > 0) {
|
||
LUT_gs[1] = 0x18;
|
||
LUT_gs[21] = gs;
|
||
}
|
||
}
|
||
else {
|
||
LUT_gs[1] = 0x28;
|
||
LUT_gs[2] = 0x00;
|
||
LUT_gs[21] = 15;
|
||
}
|
||
LUT_part = LUT_gs;
|
||
EPD_DisplayPart(xStart, xEnd, yStart, yEnd, drawBuff);
|
||
|
||
LUT_part = _lutPart;
|
||
}
|
||
}
|
||
|
||
//-----------------------------------------------------------------------
|
||
void EPD_Update(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd)
|
||
{
|
||
if (_gs == 0) EPD_DisplayPart(xStart, xEnd, yStart, yEnd, drawBuff);
|
||
else {
|
||
for (int n=0; n<16; n++) {
|
||
if (gs_used_shades & (1<<n)) EPD_gsUpdate(xStart, xEnd, yStart, yEnd, n);
|
||
}
|
||
}
|
||
}
|
||
|
||
//-------------------
|
||
void EPD_UpdateScreen()
|
||
{
|
||
EPD_Update(0, EPD_DISPLAY_WIDTH-1, 0, EPD_DISPLAY_HEIGHT-1);
|
||
}
|
||
|
||
//------------------------
|
||
void EPD_wait(uint32_t ms)
|
||
{
|
||
if (ms < 100) ms = 100;
|
||
uint32_t n = 0;
|
||
while (n < ms) {
|
||
vTaskDelay(100 / portTICK_RATE_MS);
|
||
n += 100;
|
||
}
|
||
}
|
||
|
||
|