/** * Based on GDEM042F52 driver from Good Display * https://www.good-display.com/product/564.html */ #include "EPD_driver.h" #include "nrf_log.h" // Driver command list. #define CMD_PSR 0x00 // Panel Setting Register #define CMD_PWR 0x01 // Power Setting Register #define CMD_POF 0x02 // Power OFF Command #define CMD_PFS 0x03 // Power OFF Sequence Setting #define CMD_PON 0x04 // Power ON Command #define CMD_BTST 0x06 // Booster Soft Start Command #define CMD_DSLP 0x07 // Deep sleep Command #define CMD_DTM 0x10 // Display Start Transmission Register #define CMD_DSP 0x11 // Data Stop Command #define CMD_DRF 0x12 // Display Refresh Command #define CMD_AUTO 0x17 // Auto Sequence #define CMD_PLL 0x30 // PLL control Register #define CMD_TSC 0x40 // Temperature Sensor Command #define CMD_TSE 0x41 // Temperature Sensor Calibration Register #define CMD_TSW 0x42 // Temperature Sensor Write Register #define CMD_TSR 0x43 // Temperature Sensor Read Register #define CMD_CDI 0x50 // VCOM and DATA interval setting Register #define CMD_LPD 0x51 // Lower Power Detection Register #define CMD_TRES 0x61 // Resolution setting #define CMD_GSST 0x65 // Gate/Source Start Setting Register #define CMD_REV 0x70 // REVISION Register #define CMD_AMV 0x80 // Auto Measure VCOM Register #define CMD_VV 0x81 // VCOM Value Register #define CMD_VDCS 0x82 // VCOM_DC Setting Register #define CMD_PTL 0x83 // Partial Window Register #define CMD_PGM 0x90 // Program Mode #define CMD_APG 0x91 // Active Program #define CMD_RMTP 0x92 // Read MTP Data #define CMD_PGM_CFG 0xA2 // MTP Program Config Register #define CMD_CCSET 0xE0 // Cascade Setting #define CMD_PWS 0xE3 // Power Saving Register #define CMD_LVSEL 0xE4 // LVD Voltage Select Register static void JD79668_WaitBusy(uint16_t timeout) { EPD_WaitBusy(LOW, timeout); } static void JD79668_PowerOn(void) { EPD_WriteCmd(CMD_PON); JD79668_WaitBusy(200); } static void JD79668_PowerOff(void) { EPD_WriteCmd(CMD_POF); JD79668_WaitBusy(200); } int8_t JD79668_Read_Temp(void) { EPD_WriteCmd(CMD_TSC); JD79668_WaitBusy(100); return (int8_t)EPD_ReadByte(); } void JD79668_Force_Temp(int8_t value) { EPD_Write(CMD_CCSET, 0x02); EPD_Write(0xE6, value); } static void _setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { EPD_Write(CMD_PTL, x / 256, x % 256, (x + w - 1) / 256, (x + w - 1) % 256, y / 256, y % 256, (y + h - 1) / 256, (y + h - 1) % 256, 0x01); } // Full update: 20s void JD79668_Init() { epd_model_t *EPD = epd_get(); EPD_Reset(HIGH, 50); EPD_Write(0x4D, 0x78); EPD_Write(CMD_PSR, 0x0F, 0x29); EPD_Write(CMD_BTST, 0x0D, 0x12, 0x24, 0x25, 0x12, 0x29, 0x10); EPD_Write(CMD_PLL, 0x08); EPD_Write(CMD_CDI, 0x37); EPD_Write(CMD_TRES, EPD->width / 256, EPD->width % 256, EPD->height / 256, EPD->height % 256); EPD_Write(0xAE, 0xCF); EPD_Write(0xB0, 0x13); EPD_Write(0xBD, 0x07); EPD_Write(0xBE, 0xFE); EPD_Write(0xE9, 0x01); } // Fast update: 12s void JD79668_Init_Fast() { epd_model_t *EPD = epd_get(); EPD_Reset(HIGH, 50); EPD_Write(0x4D, 0x78); EPD_Write(CMD_PSR, 0x0F, 0x29); EPD_Write(CMD_PWR, 0x07, 0x00); EPD_Write(CMD_PFS, 0x10, 0x54, 0x44); EPD_Write(CMD_BTST, 0x0F, 0x0A, 0x2F, 0x25, 0x22, 0x2E, 0x21); EPD_Write(CMD_CDI, 0x37); EPD_Write(CMD_TRES, EPD->width / 256, EPD->width % 256, EPD->height / 256, EPD->height % 256); EPD_Write(CMD_PWS, 0x22); EPD_Write(0xB6, 0x6F); EPD_Write(0xB4, 0xD0); EPD_Write(0xE9, 0x01); EPD_Write(CMD_PLL, 0x08); JD79668_PowerOn(); EPD_Write(CMD_CCSET, 0x02); EPD_Write(0xE6, 0x5A); EPD_Write(0xA5, 0x00); JD79668_WaitBusy(200); JD79668_PowerOff(); } static void JD79668_Refresh(void) { NRF_LOG_DEBUG("[EPD]: refresh begin\n"); epd_model_t *EPD = epd_get(); JD79668_PowerOn(); _setPartialRamArea(0, 0, EPD->width, EPD->height); EPD_Write(CMD_DRF, 0x00); JD79668_WaitBusy(30000); JD79668_PowerOff(); NRF_LOG_DEBUG("[EPD]: refresh end\n"); } void JD79668_Clear(bool refresh) { epd_model_t *EPD = epd_get(); uint32_t ram_bytes = ((EPD->width + 3) / 4) * EPD->height; EPD_FillRAM(CMD_DTM, 0x55, ram_bytes); if (refresh) JD79668_Refresh(); } void JD79668_Write_Image(uint8_t *black, uint8_t *color, uint16_t x, uint16_t y, uint16_t w, uint16_t h) { epd_model_t *EPD = epd_get(); 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_WriteCmd(CMD_DTM); for (uint16_t i = 0; i < h * 2; i++) // 2 bits per pixel { for (uint16_t j = 0; j < w / 8; j++) EPD_WriteByte(black ? black[j + i * wb] : 0x55); } } void JD79668_Sleep(void) { EPD_Write(CMD_POF, 0x00); // power off JD79668_WaitBusy(200); delay(100); EPD_Write(CMD_DSLP, 0xA5); // deep sleep } static epd_driver_t epd_drv_JD79668 = { .init = JD79668_Init, .clear = JD79668_Clear, .write_image = JD79668_Write_Image, .refresh = JD79668_Refresh, .sleep = JD79668_Sleep, .read_temp = JD79668_Read_Temp, .force_temp = JD79668_Force_Temp, .cmd_write_ram1 = CMD_DTM, .cmd_write_ram2 = CMD_DTM, }; // JD79668 400x300 Black/White/Red/Yellow const epd_model_t epd_jd79668_420 = { .id = EPD_JD79668_420_BWRY, .color = BWRY, .drv = &epd_drv_JD79668, .width = 400, .height = 300, };