mirror of https://github.com/arendst/Tasmota.git
370 lines
11 KiB
C++
370 lines
11 KiB
C++
// slimmed down version of the Open Book IL0398 driver.
|
|
// https://github.com/joeycastillo/The-Open-Book/blob/master/src/Grayscale_IL0398.cpp
|
|
|
|
#include "Grayscale_IL0398.h"
|
|
|
|
#define BUSY_WAIT 500
|
|
|
|
#ifndef min
|
|
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
|
#endif
|
|
|
|
#ifndef _swap_int16_t
|
|
#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; }
|
|
#endif
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief constructor if using external SRAM chip and software SPI
|
|
@param width the width of the display in pixels
|
|
@param height the height of the display in pixels
|
|
@param SID the SID pin to use
|
|
@param SCLK the SCLK pin to use
|
|
@param DC the data/command pin to use
|
|
@param RST the reset pin to use
|
|
@param CS the chip select pin to use
|
|
@param SRCS the SRAM chip select pin to use
|
|
@param MISO the MISO pin to use
|
|
@param BUSY the busy pin to use
|
|
*/
|
|
/**************************************************************************/
|
|
Grayscale_IL0398::Grayscale_IL0398(int width, int height,
|
|
int8_t SID, int8_t SCLK, int8_t DC, int8_t RST,
|
|
int8_t CS, int8_t SRCS, int8_t MISO, int8_t BUSY) :
|
|
Adafruit_EPD(width, height, SID, SCLK, DC, RST, CS, SRCS, MISO, BUSY) {
|
|
|
|
buffer1_size = ((uint32_t)width * (uint32_t)height) / 8;
|
|
buffer2_size = buffer1_size;
|
|
|
|
if (SRCS >= 0) {
|
|
use_sram = true;
|
|
buffer1_addr = 0;
|
|
buffer2_addr = buffer1_size;
|
|
buffer1 = buffer2 = NULL;
|
|
} else {
|
|
buffer1 = (uint8_t *)malloc(buffer1_size);
|
|
buffer2 = (uint8_t *)malloc(buffer2_size);
|
|
}
|
|
}
|
|
|
|
// constructor for hardware SPI - we indicate DataCommand, ChipSelect, Reset
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief constructor if using on-chip RAM and hardware SPI
|
|
@param width the width of the display in pixels
|
|
@param height the height of the display in pixels
|
|
@param DC the data/command pin to use
|
|
@param RST the reset pin to use
|
|
@param CS the chip select pin to use
|
|
@param SRCS the SRAM chip select pin to use
|
|
@param BUSY the busy pin to use
|
|
*/
|
|
/**************************************************************************/
|
|
Grayscale_IL0398::Grayscale_IL0398(int width, int height,
|
|
int8_t DC, int8_t RST,
|
|
int8_t CS, int8_t SRCS, int8_t BUSY, SPIClass *spi) :
|
|
Adafruit_EPD(width, height, DC, RST, CS, SRCS, BUSY, spi) {
|
|
|
|
buffer1_size = ((uint32_t)width * (uint32_t)height) / 8;
|
|
buffer2_size = buffer1_size;
|
|
|
|
if (SRCS >= 0) {
|
|
use_sram = true;
|
|
buffer1_addr = 0;
|
|
buffer2_addr = 0;
|
|
buffer1 = buffer2 = NULL;
|
|
} else {
|
|
buffer1 = (uint8_t *)malloc(buffer1_size);
|
|
buffer2 = (uint8_t *)malloc(buffer1_size);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief wait for busy signal to end
|
|
*/
|
|
/**************************************************************************/
|
|
void Grayscale_IL0398::busy_wait(void)
|
|
{
|
|
if (_busy_pin > -1) {
|
|
do {
|
|
EPD_command(IL0398_GETSTATUS);
|
|
delay(10);
|
|
} while (!digitalRead(_busy_pin)); //wait for busy high
|
|
delay(200);
|
|
} else {
|
|
delay(BUSY_WAIT);
|
|
}
|
|
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief begin communication with and set up the display.
|
|
@param reset if true the reset pin will be toggled.
|
|
*/
|
|
/**************************************************************************/
|
|
void Grayscale_IL0398::begin(bool reset)
|
|
{
|
|
Adafruit_EPD::begin(reset);
|
|
setBlackBuffer(0, true);
|
|
powerDown();
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief signal the display to update
|
|
*/
|
|
/**************************************************************************/
|
|
void Grayscale_IL0398::update()
|
|
{
|
|
EPD_command(IL0398_DISPLAY_REFRESH);
|
|
delay(100);
|
|
|
|
busy_wait();
|
|
if (_busy_pin <= -1) {
|
|
delay(5000);
|
|
}
|
|
}
|
|
|
|
const unsigned char Grayscale_IL0398::LUT_VCOM_GRAYSCALE[] PROGMEM =
|
|
{
|
|
0x00, 0x0A, 0x00, 0x00, 0x00, 0x01,
|
|
0x60, 0x14, 0x14, 0x00, 0x00, 0x01,
|
|
0x00, 0x14, 0x00, 0x00, 0x00, 0x01,
|
|
0x00, 0x13, 0x0A, 0x01, 0x00, 0x01,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00,
|
|
0x00
|
|
};
|
|
|
|
const unsigned char Grayscale_IL0398::LUT_WW_GRAYSCALE[] PROGMEM =
|
|
{
|
|
0x40, 0x0A, 0x00, 0x00, 0x00, 0x01,
|
|
0x90, 0x14, 0x14, 0x00, 0x00, 0x01,
|
|
0x10, 0x14, 0x0A, 0x00, 0x00, 0x01,
|
|
0xA0, 0x13, 0x01, 0x00, 0x00, 0x01,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
};
|
|
|
|
const unsigned char Grayscale_IL0398::LUT_WB_GRAYSCALE[] PROGMEM =
|
|
{
|
|
0x40, 0x0A, 0x00, 0x00, 0x00, 0x01,
|
|
0x90, 0x14, 0x14, 0x00, 0x00, 0x01,
|
|
0x00, 0x14, 0x0A, 0x00, 0x00, 0x01,
|
|
0x99, 0x0B, 0x04, 0x04, 0x01, 0x01,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
};
|
|
|
|
const unsigned char Grayscale_IL0398::LUT_BW_GRAYSCALE[] PROGMEM =
|
|
{
|
|
0x40, 0x0A, 0x00, 0x00, 0x00, 0x01,
|
|
0x90, 0x14, 0x14, 0x00, 0x00, 0x01,
|
|
0x00, 0x14, 0x0A, 0x00, 0x00, 0x01,
|
|
0x99, 0x0C, 0x01, 0x03, 0x04, 0x01,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
};
|
|
|
|
const unsigned char Grayscale_IL0398::LUT_BB_GRAYSCALE[] PROGMEM =
|
|
{
|
|
0x80, 0x0A, 0x00, 0x00, 0x00, 0x01,
|
|
0x90, 0x14, 0x14, 0x00, 0x00, 0x01,
|
|
0x20, 0x14, 0x0A, 0x00, 0x00, 0x01,
|
|
0x50, 0x13, 0x01, 0x00, 0x00, 0x01,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
};
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief start up the display. Same as init, but here for compatibility with
|
|
Adafruit_EPD; you can call Grayscale_IL0398::init with more options.
|
|
*/
|
|
/**************************************************************************/
|
|
void Grayscale_IL0398::powerUp() {
|
|
uint8_t buf[5];
|
|
|
|
hardwareReset();
|
|
|
|
buf[0] = 0x03; // Panel will generate VDH and VDL (1<<0) and VGH and VGL (1<<1)
|
|
buf[1] = 0x00; // VCOMH=VDH+VCOMDC and VCOML=VDL+VCOMDC; VGH and VGL are 16v and -16v respectively
|
|
buf[2] = 0x2b; // VDH= 11V
|
|
buf[3] = 0x2b; // VDL=-11V
|
|
buf[4] = 0x13; // VDHR=6.2V
|
|
EPD_command(IL0398_POWER_SETTING, buf, 5);
|
|
|
|
buf[0] = 0x17; // phase A: soft start 10ms, driving strength 3, off time 6.58us
|
|
buf[1] = 0x17; // phase B: soft start 10ms, driving strength 3, off time 6.58us
|
|
buf[2] = 0x17; // phase C: driving strength 3, off time 6.58us
|
|
EPD_command(IL0398_BOOSTER_SOFT_START, buf, 3);
|
|
|
|
buf[0] = 0x3F; // (1<<4) sets display to monochrome and (1<<5) enables custom LUTs
|
|
EPD_command(IL0398_PANEL_SETTING, buf, 1);
|
|
|
|
buf[0] = 0x3C; // 50 Hz
|
|
EPD_command(IL0398_PLL, buf, 1);
|
|
|
|
buf[0] = (HEIGHT >> 8) & 0xFF;
|
|
buf[1] = HEIGHT & 0xFF;
|
|
buf[2] = (WIDTH >> 8) & 0xFF;
|
|
buf[3] = WIDTH & 0xFF;
|
|
EPD_command(IL0398_RESOLUTION, buf, 4);
|
|
|
|
buf[0] = 0x12; // VCOM_DC = -1.5v
|
|
EPD_command(IL0398_VCM_DC_SETTING, buf, 1);
|
|
|
|
buf[0] = 0xD7; // 0x57 for black border. 0x97 for white border. 0xD7 for floating border.
|
|
EPD_command(IL0398_VCOM, buf, 1);
|
|
|
|
EPD_command(IL0398_LUT1, LUT_VCOM_GRAYSCALE, sizeof(LUT_VCOM_GRAYSCALE));
|
|
EPD_command(IL0398_LUTWW, LUT_WW_GRAYSCALE, sizeof(LUT_WW_GRAYSCALE));
|
|
EPD_command(IL0398_LUTBW, LUT_BW_GRAYSCALE, sizeof(LUT_BW_GRAYSCALE));
|
|
EPD_command(IL0398_LUTWB, LUT_WB_GRAYSCALE, sizeof(LUT_WB_GRAYSCALE));
|
|
EPD_command(IL0398_LUTBB, LUT_BB_GRAYSCALE, sizeof(LUT_BB_GRAYSCALE));
|
|
|
|
EPD_command(IL0398_POWER_ON);
|
|
busy_wait();
|
|
|
|
delay(20);
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief wind down the display
|
|
*/
|
|
/**************************************************************************/
|
|
void Grayscale_IL0398::powerDown()
|
|
{
|
|
uint8_t buf[4];
|
|
|
|
// power off
|
|
buf[0] = 0xF7; // border floating
|
|
EPD_command(IL0398_VCOM, buf, 1);
|
|
EPD_command(IL0398_POWER_OFF);
|
|
busy_wait();
|
|
buf[0] = 0xA5; // deep sleep
|
|
EPD_command(IL0398_DEEP_SLEEP, buf, 1);
|
|
delay(100);
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief draw a single pixel on the screen
|
|
@param x the x axis position
|
|
@param y the y axis position
|
|
@param color the color of the pixel
|
|
*/
|
|
/**************************************************************************/
|
|
void Grayscale_IL0398::drawPixel(int16_t x, int16_t y, uint16_t color) {
|
|
if ((x < 0) || (x >= width()) || (y < 0) || (y >= height()))
|
|
return;
|
|
|
|
uint8_t *pByte1;
|
|
uint8_t *pByte2;
|
|
|
|
// check rotation, move pixel around if necessary
|
|
switch (getRotation()) {
|
|
case 1:
|
|
EPD_swap(x, y);
|
|
x = WIDTH - x - 1;
|
|
break;
|
|
case 2:
|
|
x = WIDTH - x - 1;
|
|
y = HEIGHT - y - 1;
|
|
break;
|
|
case 3:
|
|
EPD_swap(x, y);
|
|
y = HEIGHT - y - 1;
|
|
break;
|
|
}
|
|
|
|
uint16_t addr = ( (uint32_t)(WIDTH - 1 - x) * (uint32_t)HEIGHT + y)/8;
|
|
|
|
if (use_sram) {
|
|
uint8_t byte1 = sram.read8(blackbuffer_addr + addr);
|
|
uint8_t byte2 = sram.read8(colorbuffer_addr + addr);
|
|
pByte1 = &byte1;
|
|
pByte2 = &byte2;
|
|
} else {
|
|
pByte1 = black_buffer + addr;
|
|
pByte2 = color_buffer + addr;
|
|
}
|
|
|
|
switch (color) {
|
|
case EPD_BLACK:
|
|
*pByte1 &= ~(1 << (7 - (y%8)));
|
|
*pByte2 &= ~(1 << (7 - (y%8)));
|
|
break;
|
|
case EPD_DARK:
|
|
*pByte1 |= (1 << (7 - (y%8)));
|
|
*pByte2 &= ~(1 << (7 - (y%8)));
|
|
break;
|
|
case EPD_LIGHT:
|
|
*pByte1 &= ~(1 << (7 - (y%8)));
|
|
*pByte2 |= (1 << (7 - (y%8)));
|
|
break;
|
|
case EPD_WHITE:
|
|
*pByte1 |= (1 << (7 - (y%8)));
|
|
*pByte2 |= (1 << (7 - (y%8)));
|
|
break;
|
|
}
|
|
|
|
if (use_sram) {
|
|
sram.write8(addr, *pByte1);
|
|
sram.write8(addr + buffer1_size, *pByte2);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Send the specific command to start writing to EPD display RAM
|
|
@param index The index for which buffer to write (0 or 1 or tri-color displays)
|
|
Ignored for monochrome displays.
|
|
@returns The byte that is read from SPI at the same time as sending the command
|
|
*/
|
|
/**************************************************************************/
|
|
uint8_t Grayscale_IL0398::writeRAMCommand(uint8_t index) {
|
|
if (index == 0) {
|
|
return EPD_command(IL0398_DTM2, false);
|
|
}
|
|
if (index == 1) {
|
|
return EPD_command(IL0398_DTM1, false);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*!
|
|
@brief Some displays require setting the RAM address pointer
|
|
@param x X address counter value
|
|
@param y Y address counter value
|
|
*/
|
|
/**************************************************************************/
|
|
void Grayscale_IL0398::setRAMAddress(uint16_t x, uint16_t y) {
|
|
// on this chip we do nothing
|
|
}
|
|
|
|
/*!
|
|
* @file Adafruit_IL0398.cpp
|
|
*
|
|
* Forked from Adafruit_IL0398.cpp; copyright notce below.
|
|
*
|
|
* Adafruit invests time and resources providing this open source code,
|
|
* please support Adafruit and open-source hardware by purchasing
|
|
* products from Adafruit!
|
|
*
|
|
* Written by Dean Miller for Adafruit Industries.
|
|
* Open Book additions by Joey Castillo.
|
|
*
|
|
* BSD license, all text here must be included in any redistribution.
|
|
*
|
|
*/
|