362 lines
10 KiB
C++
362 lines
10 KiB
C++
#include "uDisplay.h"
|
|
#include "uDisplay_config.h"
|
|
#include "uDisplay_spi.h"
|
|
|
|
|
|
// ===== EPD Command Definitions =====
|
|
static constexpr uint8_t DRIVER_OUTPUT_CONTROL = 0x01;
|
|
static constexpr uint8_t BOOSTER_SOFT_START_CONTROL = 0x0C;
|
|
static constexpr uint8_t GATE_SCAN_START_POSITION = 0x0F;
|
|
static constexpr uint8_t DEEP_SLEEP_MODE = 0x10;
|
|
static constexpr uint8_t DATA_ENTRY_MODE_SETTING = 0x11;
|
|
static constexpr uint8_t SW_RESET = 0x12;
|
|
static constexpr uint8_t TEMPERATURE_SENSOR_CONTROL = 0x1A;
|
|
static constexpr uint8_t MASTER_ACTIVATION = 0x20;
|
|
static constexpr uint8_t DISPLAY_UPDATE_CONTROL_1 = 0x21;
|
|
static constexpr uint8_t DISPLAY_UPDATE_CONTROL_2 = 0x22;
|
|
static constexpr uint8_t WRITE_RAM = 0x24;
|
|
static constexpr uint8_t WRITE_VCOM_REGISTER = 0x2C;
|
|
static constexpr uint8_t WRITE_LUT_REGISTER = 0x32;
|
|
static constexpr uint8_t SET_DUMMY_LINE_PERIOD = 0x3A;
|
|
static constexpr uint8_t SET_GATE_TIME = 0x3B;
|
|
static constexpr uint8_t BORDER_WAVEFORM_CONTROL = 0x3C;
|
|
static constexpr uint8_t SET_RAM_X_ADDRESS_START_END_POSITION = 0x44;
|
|
static constexpr uint8_t SET_RAM_Y_ADDRESS_START_END_POSITION = 0x45;
|
|
static constexpr uint8_t SET_RAM_X_ADDRESS_COUNTER = 0x4E;
|
|
static constexpr uint8_t SET_RAM_Y_ADDRESS_COUNTER = 0x4F;
|
|
static constexpr uint8_t TERMINATE_FRAME_READ_WRITE = 0xFF;
|
|
|
|
// ===== EPD Initialization and Control =====
|
|
|
|
void uDisplay::Init_EPD(int8_t p) {
|
|
if (p == DISPLAY_INIT_PARTIAL) {
|
|
if (lutpsize) {
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("DSP: init partial epaper mode"));
|
|
SetLut(lut_partial);
|
|
Updateframe_EPD();
|
|
delay_sync(lutptime * 10);
|
|
}
|
|
return;
|
|
} else if (p == DISPLAY_INIT_FULL) {
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("DSP: init full epaper mode"));
|
|
if (lutfsize) {
|
|
SetLut(lut_full);
|
|
Updateframe_EPD();
|
|
}
|
|
if (ep_mode == 2) {
|
|
ClearFrame_42();
|
|
DisplayFrame_42();
|
|
}
|
|
delay_sync(lutftime * 10);
|
|
return;
|
|
}
|
|
|
|
if (ep_mode == 2) Init_EPD(DISPLAY_INIT_FULL);
|
|
}
|
|
|
|
void uDisplay::SetLut(const unsigned char* lut) {
|
|
spi_command_EPD(lut_cmd[0]);
|
|
for (int i = 0; i < lutfsize; i++) {
|
|
spi_data8_EPD(lut[i]);
|
|
}
|
|
}
|
|
|
|
void uDisplay::SetLuts(void) {
|
|
uint8_t index, count;
|
|
for (index = 0; index < MAX_LUTS; index++) {
|
|
spi_command_EPD(lut_cmd[index]);
|
|
for (count = 0; count < lut_cnt[index]; count++) {
|
|
spi_data8_EPD(lut_array[index][count]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ===== EPD Frame Memory Management =====
|
|
|
|
void uDisplay::ClearFrameMemory(unsigned char color) {
|
|
SetMemoryArea(0, 0, gxs - 1, gys - 1);
|
|
SetMemoryPointer(0, 0);
|
|
spi_command_EPD(WRITE_RAM);
|
|
for (int i = 0; i < gxs / 8 * gys; i++) {
|
|
spi_data8_EPD(color);
|
|
}
|
|
}
|
|
|
|
void uDisplay::ClearFrame_42(void) {
|
|
spi_command_EPD(saw_1);
|
|
for (uint16_t j = 0; j < gys; j++) {
|
|
for (uint16_t i = 0; i < gxs; i++) {
|
|
spi_data8_EPD(0xFF);
|
|
}
|
|
}
|
|
|
|
spi_command_EPD(saw_2);
|
|
for (uint16_t j = 0; j < gys; j++) {
|
|
for (uint16_t i = 0; i < gxs; i++) {
|
|
spi_data8_EPD(0xFF);
|
|
}
|
|
}
|
|
|
|
spi_command_EPD(saw_3);
|
|
delay_sync(100);
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("DSP: EPD Clearframe"));
|
|
}
|
|
|
|
void uDisplay::DisplayFrame_42(void) {
|
|
spi_command_EPD(saw_1);
|
|
for(int i = 0; i < gxs / 8 * gys; i++) {
|
|
spi_data8_EPD(0xFF);
|
|
}
|
|
delay(2);
|
|
|
|
spi_command_EPD(saw_2);
|
|
for(int i = 0; i < gxs / 8 * gys; i++) {
|
|
spi_data8_EPD(framebuffer[i]^0xff);
|
|
}
|
|
delay(2);
|
|
|
|
SetLuts();
|
|
|
|
spi_command_EPD(saw_3);
|
|
delay_sync(100);
|
|
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("DSP: EPD Displayframe"));
|
|
}
|
|
|
|
void uDisplay::DisplayFrame_29(void) {
|
|
spi_command_EPD(DISPLAY_UPDATE_CONTROL_2);
|
|
spi_data8_EPD(0xC4);
|
|
spi_command_EPD(MASTER_ACTIVATION);
|
|
spi_data8_EPD(TERMINATE_FRAME_READ_WRITE);
|
|
}
|
|
|
|
// ===== EPD Memory Addressing =====
|
|
|
|
void uDisplay::SetMemoryArea(int x_start, int y_start, int x_end, int y_end) {
|
|
int x_start1 = (x_start >> 3) & 0xFF;
|
|
int x_end1 = (x_end >> 3) & 0xFF;
|
|
int y_start1 = y_start & 0xFF;
|
|
int y_start2 = (y_start >> 8) & 0xFF;
|
|
int y_end1 = y_end & 0xFF;
|
|
int y_end2 = (y_end >> 8) & 0xFF;
|
|
|
|
if (ep_mode == 3) {
|
|
spi_command_EPD(SET_RAM_X_ADDRESS_START_END_POSITION);
|
|
spi_data8_EPD(x_start1);
|
|
spi_data8_EPD(x_end1);
|
|
spi_command_EPD(SET_RAM_Y_ADDRESS_START_END_POSITION);
|
|
spi_data8_EPD(y_end1);
|
|
spi_data8_EPD(y_end2);
|
|
spi_data8_EPD(y_start1);
|
|
spi_data8_EPD(y_start2);
|
|
} else {
|
|
spi_command_EPD(SET_RAM_X_ADDRESS_START_END_POSITION);
|
|
spi_data8_EPD(x_start1);
|
|
spi_data8_EPD(x_end1);
|
|
spi_command_EPD(SET_RAM_Y_ADDRESS_START_END_POSITION);
|
|
spi_data8_EPD(y_start1);
|
|
spi_data8_EPD(y_start2);
|
|
spi_data8_EPD(y_end1);
|
|
spi_data8_EPD(y_end2);
|
|
}
|
|
}
|
|
|
|
void uDisplay::SetMemoryPointer(int x, int y) {
|
|
int x1;
|
|
int y1;
|
|
int y2;
|
|
|
|
if (ep_mode == 3) {
|
|
x1 = (x >> 3) & 0xFF;
|
|
y--;
|
|
y1 = y & 0xFF;
|
|
y2 = (y >> 8) & 0xFF;
|
|
} else {
|
|
x1 = (x >> 3) & 0xFF;
|
|
y1 = y & 0xFF;
|
|
y2 = (y >> 8) & 0xFF;
|
|
}
|
|
|
|
spi_command_EPD(SET_RAM_X_ADDRESS_COUNTER);
|
|
spi_data8_EPD(x1);
|
|
spi_command_EPD(SET_RAM_Y_ADDRESS_COUNTER);
|
|
spi_data8_EPD(y1);
|
|
spi_data8_EPD(y2);
|
|
}
|
|
|
|
// ===== EPD Frame Updates =====
|
|
|
|
void uDisplay::Updateframe_EPD(void) {
|
|
if (ep_mode == 1 || ep_mode == 3) {
|
|
switch (ep_update_mode) {
|
|
case DISPLAY_INIT_PARTIAL:
|
|
if (epc_part_cnt) {
|
|
send_spi_cmds(epcoffs_part, epc_part_cnt);
|
|
}
|
|
break;
|
|
case DISPLAY_INIT_FULL:
|
|
if (epc_full_cnt) {
|
|
send_spi_cmds(epcoffs_full, epc_full_cnt);
|
|
}
|
|
break;
|
|
default:
|
|
SetFrameMemory(framebuffer, 0, 0, gxs, gys);
|
|
DisplayFrame_29();
|
|
}
|
|
} else {
|
|
DisplayFrame_42();
|
|
}
|
|
}
|
|
|
|
void uDisplay::SetFrameMemory(const unsigned char* image_buffer) {
|
|
SetMemoryArea(0, 0, gxs - 1, gys - 1);
|
|
SetMemoryPointer(0, 0);
|
|
spi_command_EPD(WRITE_RAM);
|
|
for (int i = 0; i < gxs / 8 * gys; i++) {
|
|
spi_data8_EPD(image_buffer[i] ^ 0xff);
|
|
}
|
|
}
|
|
|
|
void uDisplay::SetFrameMemory(const unsigned char* image_buffer,
|
|
uint16_t x, uint16_t y, uint16_t image_width, uint16_t image_height) {
|
|
|
|
uint16_t x_end;
|
|
uint16_t y_end;
|
|
|
|
if (image_buffer == NULL || x < 0 || image_width < 0 || y < 0 || image_height < 0) {
|
|
return;
|
|
}
|
|
|
|
x &= 0xFFF8;
|
|
image_width &= 0xFFF8;
|
|
if (x + image_width >= gxs) {
|
|
x_end = gxs - 1;
|
|
} else {
|
|
x_end = x + image_width - 1;
|
|
}
|
|
if (y + image_height >= gys) {
|
|
y_end = gys - 1;
|
|
} else {
|
|
y_end = y + image_height - 1;
|
|
}
|
|
|
|
if (!x && !y && image_width == gxs && image_height == gys) {
|
|
SetFrameMemory(image_buffer);
|
|
return;
|
|
}
|
|
|
|
SetMemoryArea(x, y, x_end, y_end);
|
|
SetMemoryPointer(x, y);
|
|
spi_command_EPD(WRITE_RAM);
|
|
for (uint16_t j = 0; j < y_end - y + 1; j++) {
|
|
for (uint16_t i = 0; i < (x_end - x + 1) / 8; i++) {
|
|
spi_data8_EPD(image_buffer[i + j * (image_width / 8)] ^ 0xff);
|
|
}
|
|
}
|
|
}
|
|
|
|
void uDisplay::Send_EP_Data() {
|
|
uint16_t image_width = gxs & 0xFFF8;
|
|
uint16_t x = 0;
|
|
uint16_t y = 0;
|
|
uint16_t x_end = gxs - 1;
|
|
uint16_t y_end = gys - 1;
|
|
|
|
for (uint16_t j = 0; j < y_end - y + 1; j++) {
|
|
for (uint16_t i = 0; i < (x_end - x + 1) / 8; i++) {
|
|
spi_data8_EPD(framebuffer[i + j * (image_width / 8)] ^ 0xff);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ===== EPD Drawing Primitives =====
|
|
#define IF_INVERT_COLOR 1
|
|
#define renderer_swap(a, b) { int16_t t = a; a = b; b = t; }
|
|
|
|
void uDisplay::DrawAbsolutePixel(int x, int y, int16_t color) {
|
|
int16_t w = width(), h = height();
|
|
if (cur_rot == 1 || cur_rot == 3) {
|
|
renderer_swap(w, h);
|
|
}
|
|
|
|
if (x < 0 || x >= w || y < 0 || y >= h) {
|
|
return;
|
|
}
|
|
|
|
if (IF_INVERT_COLOR) {
|
|
if (color) {
|
|
framebuffer[(x + y * w) / 8] |= 0x80 >> (x % 8);
|
|
} else {
|
|
framebuffer[(x + y * w) / 8] &= ~(0x80 >> (x % 8));
|
|
}
|
|
} else {
|
|
if (color) {
|
|
framebuffer[(x + y * w) / 8] &= ~(0x80 >> (x % 8));
|
|
} else {
|
|
framebuffer[(x + y * w) / 8] |= 0x80 >> (x % 8);
|
|
}
|
|
}
|
|
}
|
|
|
|
void uDisplay::drawPixel_EPD(int16_t x, int16_t y, uint16_t color) {
|
|
if (!framebuffer) return;
|
|
if ((x < 0) || (x >= width()) || (y < 0) || (y >= height()))
|
|
return;
|
|
|
|
switch (cur_rot) {
|
|
case 1:
|
|
renderer_swap(x, y);
|
|
x = gxs - x - 1;
|
|
break;
|
|
case 2:
|
|
x = gxs - x - 1;
|
|
y = gys - y - 1;
|
|
break;
|
|
case 3:
|
|
renderer_swap(x, y);
|
|
y = gys - y - 1;
|
|
break;
|
|
}
|
|
|
|
DrawAbsolutePixel(x, y, color);
|
|
}
|
|
|
|
void uDisplay::fillRect_EPD(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
|
|
for (uint32_t yp = y; yp < y + h; yp++) {
|
|
for (uint32_t xp = x; xp < x + w; xp++) {
|
|
drawPixel_EPD(xp, yp, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
void uDisplay::drawFastVLine_EPD(int16_t x, int16_t y, int16_t h, uint16_t color) {
|
|
while (h--) {
|
|
drawPixel_EPD(x, y, color);
|
|
y++;
|
|
}
|
|
}
|
|
|
|
void uDisplay::drawFastHLine_EPD(int16_t x, int16_t y, int16_t w, uint16_t color) {
|
|
while (w--) {
|
|
drawPixel_EPD(x, y, color);
|
|
x++;
|
|
}
|
|
}
|
|
|
|
// ===== EPD SPI Helpers =====
|
|
|
|
void uDisplay::spi_command_EPD(uint8_t val) {
|
|
SPI_BEGIN_TRANSACTION
|
|
SPI_CS_LOW
|
|
ulcd_command(val);
|
|
SPI_CS_HIGH
|
|
SPI_END_TRANSACTION
|
|
}
|
|
|
|
void uDisplay::spi_data8_EPD(uint8_t val) {
|
|
SPI_BEGIN_TRANSACTION
|
|
SPI_CS_LOW
|
|
ulcd_data8(val);
|
|
SPI_CS_HIGH
|
|
SPI_END_TRANSACTION
|
|
} |