mirror of
https://github.com/jam422470459/EPD-nRF52-hema213.git
synced 2025-12-06 08:32:54 +08:00
initial SSD1619 support
This commit is contained in:
@@ -296,12 +296,14 @@ void EPD_LED_TOGGLE(void)
|
||||
}
|
||||
|
||||
// EPD models
|
||||
extern epd_model_t epd_4in2;
|
||||
extern epd_model_t epd_4in2bv2;
|
||||
extern epd_model_t epd_uc8176_420_bw;
|
||||
extern epd_model_t epd_uc8176_420_bwr;
|
||||
extern epd_model_t epd_ssd1619_420_bwr;
|
||||
|
||||
static epd_model_t *epd_models[] = {
|
||||
&epd_4in2,
|
||||
&epd_4in2bv2,
|
||||
&epd_uc8176_420_bw,
|
||||
&epd_uc8176_420_bwr,
|
||||
&epd_ssd1619_420_bwr,
|
||||
};
|
||||
|
||||
static epd_model_t *epd_model_get(epd_model_id_t id)
|
||||
|
||||
@@ -47,9 +47,9 @@ typedef struct
|
||||
|
||||
typedef enum
|
||||
{
|
||||
EPD_4IN2 = 1,
|
||||
EPD_4IN2_V2 = 2,
|
||||
EPD_4IN2B_V2 = 3,
|
||||
EPD_UC8176_420_BW = 1,
|
||||
EPD_UC8176_420_BWR = 3,
|
||||
EPD_SSD1619_420_BWR = 2,
|
||||
} epd_model_id_t;
|
||||
|
||||
typedef struct
|
||||
|
||||
199
EPD/SSD1619.c
Normal file
199
EPD/SSD1619.c
Normal file
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* Based on GDEH042Z96 driver from Good Display
|
||||
* https://www.good-display.com/product/214.html
|
||||
*/
|
||||
#include "EPD_driver.h"
|
||||
#include "nrf_log.h"
|
||||
|
||||
// commands used by this driver
|
||||
#define CMD_DRIVER_CTRL 0x01 // Driver Output control
|
||||
#define CMD_BOOSTER_CTRL 0x0C // Booster Soft start Control
|
||||
#define CMD_DEEP_SLEEP 0x10 // Deep Sleep mode
|
||||
#define CMD_DATA_MODE 0x11 // Data Entry mode setting
|
||||
#define CMD_SW_RESET 0x12 // SW RESET
|
||||
#define CMD_TSENSOR_CTRL 0x18 // Temperature Sensor Control
|
||||
#define CMD_TSENSOR_WRITE 0x1A // Temperature Sensor Control (Write to temperature register)
|
||||
#define CMD_TSENSOR_READ 0x1B // Temperature Sensor Control (Read from temperature register)
|
||||
#define CMD_MASTER_ACTIVATE 0x20 // Master Activation
|
||||
#define CMD_DISP_CTRL1 0x21 // Display Update Control 1
|
||||
#define CMD_DISP_CTRL2 0x22 // Display Update Control 2
|
||||
#define CMD_WRITE_RAM1 0x24 // Write RAM (BW)
|
||||
#define CMD_WRITE_RAM2 0x26 // Write RAM (RED)
|
||||
#define CMD_VCOM_CTRL 0x2B // Write Register for VCOM Control
|
||||
#define CMD_BORDER_CTRL 0x3C // Border Waveform Control
|
||||
#define CMD_RAM_XPOS 0x44 // Set RAM X - address Start / End position
|
||||
#define CMD_RAM_YPOS 0x45 // Set Ram Y- address Start / End position
|
||||
#define CMD_RAM_XCOUNT 0x4E // Set RAM X address counter
|
||||
#define CMD_RAM_YCOUNT 0x4F // Set RAM Y address counter
|
||||
#define CMD_ANALOG_BLOCK_CTRL 0x74 // Set Analog Block Control
|
||||
#define CMD_DIGITAL_BLOCK_CTRL 0x7E // Set Digital Block Control
|
||||
|
||||
int8_t SSD1619_Read_Temp(void)
|
||||
{
|
||||
EPD_WriteCommand_SW(CMD_TSENSOR_READ);
|
||||
return (int8_t) EPD_ReadByte_SW();
|
||||
}
|
||||
|
||||
void SSD1619_Force_Temp(int8_t value)
|
||||
{
|
||||
EPD_WriteCommand(CMD_TSENSOR_WRITE);
|
||||
EPD_WriteByte(value);
|
||||
}
|
||||
|
||||
static void _setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
|
||||
{
|
||||
EPD_WriteCommand(CMD_DATA_MODE); // set ram entry mode
|
||||
EPD_WriteByte(0x03); // x increase, y increase
|
||||
EPD_WriteCommand(CMD_RAM_XPOS);
|
||||
EPD_WriteByte(x / 8);
|
||||
EPD_WriteByte((x + w - 1) / 8);
|
||||
EPD_WriteCommand(CMD_RAM_YPOS);
|
||||
EPD_WriteByte(y % 256);
|
||||
EPD_WriteByte(y / 256);
|
||||
EPD_WriteByte((y + h - 1) % 256);
|
||||
EPD_WriteByte((y + h - 1) / 256);
|
||||
EPD_WriteCommand(CMD_RAM_XCOUNT);
|
||||
EPD_WriteByte(x / 8);
|
||||
EPD_WriteCommand(CMD_RAM_YCOUNT);
|
||||
EPD_WriteByte(y % 256);
|
||||
EPD_WriteByte(y / 256);
|
||||
}
|
||||
|
||||
void SSD1619_Init(epd_res_t res, bool bwr)
|
||||
{
|
||||
EPD_BWR_MODE = bwr;
|
||||
EPD_Reset(HIGH, 10);
|
||||
|
||||
switch (res) {
|
||||
case EPD_RES_320x300:
|
||||
EPD_WIDTH = 320;
|
||||
EPD_HEIGHT = 300;
|
||||
break;
|
||||
case EPD_RES_320x240:
|
||||
EPD_WIDTH = 320;
|
||||
EPD_HEIGHT = 240;
|
||||
break;
|
||||
case EPD_RES_200x300:
|
||||
EPD_WIDTH = 200;
|
||||
EPD_HEIGHT = 300;
|
||||
break;
|
||||
case EPD_RES_400x300:
|
||||
default:
|
||||
EPD_WIDTH = 400;
|
||||
EPD_HEIGHT = 300;
|
||||
break;
|
||||
}
|
||||
|
||||
EPD_WriteCommand(CMD_SW_RESET);
|
||||
EPD_WaitBusy(HIGH, 200);
|
||||
|
||||
EPD_WriteCommand(CMD_ANALOG_BLOCK_CTRL);
|
||||
EPD_WriteByte(0x54);
|
||||
EPD_WriteCommand(CMD_DIGITAL_BLOCK_CTRL);
|
||||
EPD_WriteByte(0x3B);
|
||||
EPD_WriteCommand(CMD_VCOM_CTRL); // Reduce glitch under ACVCOM
|
||||
EPD_WriteByte(0x04);
|
||||
EPD_WriteByte(0x63);
|
||||
|
||||
EPD_WriteCommand(CMD_BOOSTER_CTRL);
|
||||
EPD_WriteByte(0x8B);
|
||||
EPD_WriteByte(0x9C);
|
||||
EPD_WriteByte(0x96);
|
||||
EPD_WriteByte(0x0F);
|
||||
|
||||
EPD_WriteCommand(CMD_DRIVER_CTRL);
|
||||
EPD_WriteByte((EPD_HEIGHT - 1) % 256);
|
||||
EPD_WriteByte((EPD_HEIGHT - 1) / 256);
|
||||
EPD_WriteByte(0x00);
|
||||
|
||||
_setPartialRamArea(0, 0, EPD_WIDTH, EPD_HEIGHT);
|
||||
|
||||
EPD_WriteCommand(CMD_BORDER_CTRL);
|
||||
EPD_WriteByte(0x01);
|
||||
|
||||
EPD_WriteCommand(CMD_TSENSOR_CTRL);
|
||||
EPD_WriteByte(0x80);
|
||||
EPD_WriteCommand(CMD_DISP_CTRL2);
|
||||
EPD_WriteByte(0xB1);
|
||||
EPD_WriteCommand(CMD_MASTER_ACTIVATE);
|
||||
EPD_WaitBusy(HIGH, 200);
|
||||
}
|
||||
|
||||
static void SSD1619_Refresh(void)
|
||||
{
|
||||
NRF_LOG_DEBUG("[EPD]: refresh begin\n");
|
||||
NRF_LOG_DEBUG("[EPD]: temperature: %d\n", SSD1619_Read_Temp());
|
||||
EPD_WriteCommand(CMD_DISP_CTRL2);
|
||||
EPD_WriteByte(0xC7);
|
||||
EPD_WriteCommand(CMD_MASTER_ACTIVATE);
|
||||
EPD_WaitBusy(HIGH, 30000);
|
||||
NRF_LOG_DEBUG("[EPD]: refresh end\n");
|
||||
|
||||
_setPartialRamArea(0, 0, EPD_WIDTH, EPD_HEIGHT);
|
||||
}
|
||||
|
||||
void SSD1619_Clear(void)
|
||||
{
|
||||
uint16_t Width = (EPD_WIDTH % 8 == 0)? (EPD_WIDTH / 8 ): (EPD_WIDTH / 8 + 1);
|
||||
uint16_t Height = EPD_HEIGHT;
|
||||
|
||||
EPD_WriteCommand(CMD_WRITE_RAM1);
|
||||
for (uint16_t j = 0; j < Height; j++) {
|
||||
for (uint16_t i = 0; i < Width; i++) {
|
||||
EPD_WriteByte(0xFF);
|
||||
}
|
||||
}
|
||||
EPD_WriteCommand(CMD_WRITE_RAM2);
|
||||
for (uint16_t j = 0; j < Height; j++) {
|
||||
for (uint16_t i = 0; i < Width; i++) {
|
||||
EPD_WriteByte(0x00);
|
||||
}
|
||||
}
|
||||
|
||||
SSD1619_Refresh();
|
||||
}
|
||||
|
||||
void SSD1619_Write_Image(uint8_t *black, uint8_t *color, uint16_t x, uint16_t y, uint16_t w, uint16_t h)
|
||||
{
|
||||
uint16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
|
||||
x -= x % 8; // byte boundary
|
||||
w = wb * 8; // byte boundary
|
||||
if (x + w > EPD_WIDTH || y + h > EPD_HEIGHT) return;
|
||||
_setPartialRamArea(x, y, w, h);
|
||||
EPD_WriteCommand(CMD_WRITE_RAM1);
|
||||
for (uint16_t i = 0; i < h; i++) {
|
||||
for (uint16_t j = 0; j < w / 8; j++) {
|
||||
EPD_WriteByte(black ? black[j + i * wb] : 0xFF);
|
||||
}
|
||||
}
|
||||
EPD_WriteCommand(CMD_WRITE_RAM2);
|
||||
for (uint16_t i = 0; i < h; i++) {
|
||||
for (uint16_t j = 0; j < w / 8; j++) {
|
||||
EPD_WriteByte(color ? ~color[j + i * wb] : 0x00);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSD1619_Sleep(void)
|
||||
{
|
||||
EPD_WriteCommand(CMD_DEEP_SLEEP);
|
||||
EPD_WriteByte(0x01);
|
||||
delay(100);
|
||||
}
|
||||
|
||||
static epd_driver_t epd_drv_ssd1619 = {
|
||||
.init = SSD1619_Init,
|
||||
.clear = SSD1619_Clear,
|
||||
.write_image = SSD1619_Write_Image,
|
||||
.refresh = SSD1619_Refresh,
|
||||
.sleep = SSD1619_Sleep,
|
||||
.read_temp = SSD1619_Read_Temp,
|
||||
.force_temp = SSD1619_Force_Temp,
|
||||
};
|
||||
|
||||
const epd_model_t epd_ssd1619_420_bwr = {
|
||||
.id = EPD_SSD1619_420_BWR,
|
||||
.drv = &epd_drv_ssd1619,
|
||||
.res = EPD_RES_400x300,
|
||||
.bwr = true,
|
||||
};
|
||||
16
EPD/UC8176.c
16
EPD/UC8176.c
@@ -78,10 +78,10 @@ int8_t UC8176_Read_Temp(void)
|
||||
// Force temperature (will trigger OTP LUT switch)
|
||||
void UC8176_Force_Temp(int8_t value)
|
||||
{
|
||||
EPD_WriteCommand_SW(CMD_CCSET);
|
||||
EPD_WriteByte_SW(0x02);
|
||||
EPD_WriteCommand_SW(CMD_TSSET);
|
||||
EPD_WriteByte_SW(value);
|
||||
EPD_WriteCommand(CMD_CCSET);
|
||||
EPD_WriteByte(0x02);
|
||||
EPD_WriteCommand(CMD_TSSET);
|
||||
EPD_WriteByte(value);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
@@ -238,15 +238,15 @@ static epd_driver_t epd_drv_uc8176 = {
|
||||
.force_temp = UC8176_Force_Temp,
|
||||
};
|
||||
|
||||
const epd_model_t epd_4in2 = {
|
||||
.id = EPD_4IN2,
|
||||
const epd_model_t epd_uc8176_420_bw = {
|
||||
.id = EPD_UC8176_420_BW,
|
||||
.drv = &epd_drv_uc8176,
|
||||
.res = EPD_RES_400x300,
|
||||
.bwr = false,
|
||||
};
|
||||
|
||||
const epd_model_t epd_4in2bv2 = {
|
||||
.id = EPD_4IN2B_V2,
|
||||
const epd_model_t epd_uc8176_420_bwr = {
|
||||
.id = EPD_UC8176_420_BWR,
|
||||
.drv = &epd_drv_uc8176,
|
||||
.res = EPD_RES_400x300,
|
||||
.bwr = true,
|
||||
|
||||
@@ -419,6 +419,11 @@
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\EPD\UC8176.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>SSD1619.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\EPD\SSD1619.c</FilePath>
|
||||
</File>
|
||||
</Files>
|
||||
</Group>
|
||||
<Group>
|
||||
@@ -1021,6 +1026,11 @@
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\EPD\UC8176.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>SSD1619.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\EPD\SSD1619.c</FilePath>
|
||||
</File>
|
||||
</Files>
|
||||
</Group>
|
||||
<Group>
|
||||
|
||||
@@ -419,6 +419,11 @@
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\EPD\UC8176.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>SSD1619.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\EPD\SSD1619.c</FilePath>
|
||||
</File>
|
||||
</Files>
|
||||
</Group>
|
||||
<Group>
|
||||
@@ -1146,6 +1151,11 @@
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\EPD\UC8176.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>SSD1619.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\EPD\SSD1619.c</FilePath>
|
||||
</File>
|
||||
</Files>
|
||||
</Group>
|
||||
<Group>
|
||||
|
||||
@@ -40,6 +40,7 @@ SRC_FILES += \
|
||||
$(PROJ_DIR)/EPD/EPD_driver.c \
|
||||
$(PROJ_DIR)/EPD/EPD_service.c \
|
||||
$(PROJ_DIR)/EPD/UC8176.c \
|
||||
$(PROJ_DIR)/EPD/SSD1619.c \
|
||||
$(PROJ_DIR)/GUI/Calendar.c \
|
||||
$(PROJ_DIR)/GUI/Lunar.c \
|
||||
$(PROJ_DIR)/GUI/fonts.c \
|
||||
|
||||
@@ -65,6 +65,7 @@ SRC_FILES += \
|
||||
$(PROJ_DIR)/EPD/EPD_driver.c \
|
||||
$(PROJ_DIR)/EPD/EPD_service.c \
|
||||
$(PROJ_DIR)/EPD/UC8176.c \
|
||||
$(PROJ_DIR)/EPD/SSD1619.c \
|
||||
$(PROJ_DIR)/GUI/Calendar.c \
|
||||
$(PROJ_DIR)/GUI/Lunar.c \
|
||||
$(PROJ_DIR)/GUI/fonts.c \
|
||||
|
||||
@@ -45,8 +45,9 @@
|
||||
<div>
|
||||
<label for="epddriver">驱动</label>
|
||||
<select id="epddriver" onchange="filterDitheringOptions()">
|
||||
<option value="01">EPD_4in2(黑白屏)</option>
|
||||
<option value="03">EPD_4in2b_V2(三色屏)</option>
|
||||
<option value="01">UC8176(黑白屏)</option>
|
||||
<option value="03">UC8176(三色屏)</option>
|
||||
<option value="02">SSD1619(三色屏)</option>
|
||||
</select>
|
||||
<label for="epdpins">引脚</label>
|
||||
<input id="epdpins" type="text" value="">
|
||||
@@ -87,7 +88,7 @@
|
||||
<option value="floydsteinberg">floydsteinberg</option>
|
||||
<option value="Atkinson">Atkinson</option>
|
||||
</optgroup>
|
||||
<optgroup id="dithering-bwr" data-driver="03" label="黑白红多色">
|
||||
<optgroup id="dithering-bwr" data-driver="02|03" label="黑白红多色">
|
||||
<option value="bwr_floydsteinberg">黑白红floydsteinberg</option>
|
||||
<option value="bwr_Atkinson">黑白红Atkinson</option>
|
||||
</optgroup>
|
||||
|
||||
@@ -122,7 +122,7 @@ function canvas2gray(canvas) {
|
||||
}
|
||||
|
||||
// white: 1, black/red: 0
|
||||
function canvas2bytes(canvas, type='bw') {
|
||||
function canvas2bytes(canvas, type='bw', invert = false) {
|
||||
const ctx = canvas.getContext("2d");
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
|
||||
@@ -132,11 +132,14 @@ function canvas2bytes(canvas, type='bw') {
|
||||
for (let y = 0; y < canvas.height; y++) {
|
||||
for (let x = 0; x < canvas.width; x++) {
|
||||
const i = (canvas.width * y + x) * 4;
|
||||
let data;
|
||||
if (type !== 'red') {
|
||||
buffer.push(imageData.data[i] === 0 && imageData.data[i+1] === 0 && imageData.data[i+2] === 0 ? 0 : 1);
|
||||
data = imageData.data[i] === 0 && imageData.data[i+1] === 0 && imageData.data[i+2] === 0 ? 0 : 1;
|
||||
} else {
|
||||
buffer.push(imageData.data[i] > 0 && imageData.data[i+1] === 0 && imageData.data[i+2] === 0 ? 0 : 1);
|
||||
data = imageData.data[i] > 0 && imageData.data[i+1] === 0 && imageData.data[i+2] === 0 ? 0 : 1;
|
||||
}
|
||||
if (invert) data = ~data;
|
||||
buffer.push(data);
|
||||
|
||||
if (buffer.length === 8) {
|
||||
arr.push(parseInt(buffer.join(''), 2));
|
||||
|
||||
@@ -147,8 +147,8 @@ function getImageData(canvas, driver, mode) {
|
||||
return canvas2gray(canvas);
|
||||
} else {
|
||||
let data = canvas2bytes(canvas, 'bw');
|
||||
if (driver === '03' && mode.startsWith('bwr')) {
|
||||
data.push(...canvas2bytes(canvas, 'red'));
|
||||
if (mode.startsWith('bwr')) {
|
||||
data.push(...canvas2bytes(canvas, 'red', driver === '02'));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
@@ -167,9 +167,9 @@ async function sendimg() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (imgArray.length == ramSize * 2) {
|
||||
await epdWrite(0x10, imgArray.slice(0, ramSize));
|
||||
await epdWrite(0x13, imgArray.slice(ramSize));
|
||||
if (imgArray.length === ramSize * 2) {
|
||||
await epdWrite(driver === "02" ? 0x24 : 0x10, imgArray.slice(0, ramSize));
|
||||
await epdWrite(driver === "02" ? 0x26 : 0x13, imgArray.slice(ramSize));
|
||||
} else {
|
||||
await epdWrite(driver === "03" ? 0x10 : 0x13, imgArray);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user