1
0
mirror of https://github.com/Dejvino/lilybook.git synced 2024-11-14 12:23:28 +00:00
lilybook/components/epaper/EPDspi.c

790 lines
23 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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;
}
}