UNPKG

it8951

Version:

Raspberry Pi node.js module for e-papers controlled by IT8951

530 lines (529 loc) 19.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IT8951 = exports.WAVEFORM = exports.ENDIANNESS = exports.PIXEL_PACKING = exports.IMAGE_ROTATION = void 0; /** * Class to controll IT8951. Implemented based on waveshare [documentation]{@link https://www.waveshare.com/wiki/6inch_HD_e-Paper_HAT}, [specification]{@link https://www.waveshare.com/w/upload/c/c4/E-paper-mode-declaration.pdf} and [IT8951 datasheet]{@link https://www.waveshare.net/w/upload/1/18/IT8951_D_V0.2.4.3_20170728.pdf}. * @packageDocumentation */ const spi_1 = require("./spi"); const util_1 = require("util"); /** * Preamble words for SPI communication * * @enum {number} */ var SPI_PREAMBLE; (function (SPI_PREAMBLE) { /** * Command preamble */ SPI_PREAMBLE[SPI_PREAMBLE["CMD"] = 24576] = "CMD"; /** * Write preamble */ SPI_PREAMBLE[SPI_PREAMBLE["WRITE"] = 0] = "WRITE"; /** * Read preamble */ SPI_PREAMBLE[SPI_PREAMBLE["READ"] = 4096] = "READ"; })(SPI_PREAMBLE || (SPI_PREAMBLE = {})); var IT8951_COMMANDS; (function (IT8951_COMMANDS) { /** * System running command ( enable all clocks and go to active state ) */ IT8951_COMMANDS[IT8951_COMMANDS["SYS_RUN"] = 1] = "SYS_RUN"; /** * Standby command ( gate off clocks and go to standby state ) */ IT8951_COMMANDS[IT8951_COMMANDS["STANDBY"] = 2] = "STANDBY"; /** * Skeep command ( disable all clocks and go to sleep state) */ IT8951_COMMANDS[IT8951_COMMANDS["SLEEP"] = 3] = "SLEEP"; /** * Read register command */ IT8951_COMMANDS[IT8951_COMMANDS["REG_RD"] = 16] = "REG_RD"; /** * Write register command */ IT8951_COMMANDS[IT8951_COMMANDS["REG_WR"] = 17] = "REG_WR"; /** * Memory burst read trigger command. * * This command will trigger internal FIFO to read data from memory. */ IT8951_COMMANDS[IT8951_COMMANDS["MEM_BST_RD_T"] = 18] = "MEM_BST_RD_T"; /** * Memory burst read start command. * * This is only a data read command. It will read data from internal FIFO. So, this command should be issued after MEM_BST_RD_T command. */ IT8951_COMMANDS[IT8951_COMMANDS["MEM_BST_RD_S"] = 19] = "MEM_BST_RD_S"; /** * Memory burst read write command */ IT8951_COMMANDS[IT8951_COMMANDS["MEM_BST_WR"] = 20] = "MEM_BST_WR"; /** * End memory burst cycle */ IT8951_COMMANDS[IT8951_COMMANDS["MEM_BST_END"] = 21] = "MEM_BST_END"; /** * Load full image command. Data that follows must be equal to full display size. */ IT8951_COMMANDS[IT8951_COMMANDS["LD_IMG"] = 32] = "LD_IMG"; /** * Load partial image area command. Data that follows must equal size specified in the argument parameters. */ IT8951_COMMANDS[IT8951_COMMANDS["LD_IMG_AREA"] = 33] = "LD_IMG_AREA"; /** * End load image. */ IT8951_COMMANDS[IT8951_COMMANDS["LD_IMG_END"] = 34] = "LD_IMG_END"; })(IT8951_COMMANDS || (IT8951_COMMANDS = {})); /** * User defined commands. These might be specific to waveshare firmware. * * @enum {number} */ var USDEF_I80_CMD; (function (USDEF_I80_CMD) { /** * Display loaded image area. */ USDEF_I80_CMD[USDEF_I80_CMD["DPY_AREA"] = 52] = "DPY_AREA"; /** * Get dev info. */ USDEF_I80_CMD[USDEF_I80_CMD["GET_DEV_INFO"] = 770] = "GET_DEV_INFO"; /** * Display buffer area. */ USDEF_I80_CMD[USDEF_I80_CMD["DPY_BUF_AREA"] = 55] = "DPY_BUF_AREA"; /** * Set vcom command. */ USDEF_I80_CMD[USDEF_I80_CMD["CMD_VCOM"] = 57] = "CMD_VCOM"; })(USDEF_I80_CMD || (USDEF_I80_CMD = {})); /** Base address for the display control register */ const DISPLAY_REG_BASE = 0x1000; /** * Display control register addresses * * @enum {number} */ var DISPLAY_REG; (function (DISPLAY_REG) { /** LUT0 Engine Width Height Reg */ DISPLAY_REG[DISPLAY_REG["LUT0EWHR"] = DISPLAY_REG_BASE + 0x00] = "LUT0EWHR"; /** LUT0 XY Reg */ DISPLAY_REG[DISPLAY_REG["LUT0XYR"] = DISPLAY_REG_BASE + 0x40] = "LUT0XYR"; /** LUT0 Base Address Reg */ DISPLAY_REG[DISPLAY_REG["LUT0BADDR"] = DISPLAY_REG_BASE + 0x80] = "LUT0BADDR"; /** LUT0 Mode and Frame number Reg */ DISPLAY_REG[DISPLAY_REG["LUT0MFN"] = DISPLAY_REG_BASE + 0xc0] = "LUT0MFN"; /** LUT0 and LUT1 Active Flag Reg */ DISPLAY_REG[DISPLAY_REG["LUT01AF"] = DISPLAY_REG_BASE + 0x114] = "LUT01AF"; /** Update Parameter0 Setting Reg */ DISPLAY_REG[DISPLAY_REG["UP0SR"] = DISPLAY_REG_BASE + 0x134] = "UP0SR"; /** Update Parameter1 Setting Reg */ DISPLAY_REG[DISPLAY_REG["UP1SR"] = DISPLAY_REG_BASE + 0x138] = "UP1SR"; /** LUT0 Alpha blend and Fill rectangle Value */ DISPLAY_REG[DISPLAY_REG["LUT0ABFRV"] = DISPLAY_REG_BASE + 0x13c] = "LUT0ABFRV"; /** Update Buffer Base Address */ DISPLAY_REG[DISPLAY_REG["UPBBADDR"] = DISPLAY_REG_BASE + 0x17c] = "UPBBADDR"; /** LUT0 Image buffer X/Y offset Reg */ DISPLAY_REG[DISPLAY_REG["LUT0IMXY"] = DISPLAY_REG_BASE + 0x180] = "LUT0IMXY"; /** LUT Status Reg (status of All LUT Engines) */ DISPLAY_REG[DISPLAY_REG["LUTAFSR"] = DISPLAY_REG_BASE + 0x224] = "LUTAFSR"; /** Bitmap (1bpp) image color table */ DISPLAY_REG[DISPLAY_REG["BGVR"] = DISPLAY_REG_BASE + 0x250] = "BGVR"; })(DISPLAY_REG || (DISPLAY_REG = {})); /** Base address for the system register */ const SYS_REG_BASE = 0x0000; /** * System register addresses * * @enum {number} */ var SYS_REG; (function (SYS_REG) { /** Address of System Registers */ SYS_REG[SYS_REG["I80CPCR"] = SYS_REG_BASE + 0x04] = "I80CPCR"; })(SYS_REG || (SYS_REG = {})); /** Memory converter base register addresses */ const MCSR_BASE_ADDR = 0x0200; /** * Memory converter register addresses. * * @enum {number} */ var MCSR_REG; (function (MCSR_REG) { /** * Memory converter register address. */ MCSR_REG[MCSR_REG["MCSR"] = MCSR_BASE_ADDR] = "MCSR"; /** * Load image buffer address. */ MCSR_REG[MCSR_REG["LISAR"] = MCSR_BASE_ADDR + 0x0008] = "LISAR"; })(MCSR_REG || (MCSR_REG = {})); //Rotate mode /** * Image rotation. All rotations are clockwise from the base of the screen. * * @export * @enum {number} */ var IMAGE_ROTATION; (function (IMAGE_ROTATION) { /** * Rotate zero degrees. Default rotation in methods supporting rotation. */ IMAGE_ROTATION[IMAGE_ROTATION["ROTATE_0"] = 0] = "ROTATE_0"; /** * Rotate 90 degrees. */ IMAGE_ROTATION[IMAGE_ROTATION["ROTATE_90"] = 1] = "ROTATE_90"; /** * Rotate 180 degress. */ IMAGE_ROTATION[IMAGE_ROTATION["ROTATE_180"] = 2] = "ROTATE_180"; /** * Rotate 270 degrees. */ IMAGE_ROTATION[IMAGE_ROTATION["ROTATE_270"] = 3] = "ROTATE_270"; })(IMAGE_ROTATION = exports.IMAGE_ROTATION || (exports.IMAGE_ROTATION = {})); /** * Pixel packaging mode. * * @export * @enum {number} */ var PIXEL_PACKING; (function (PIXEL_PACKING) { /** * Two bits per pixel */ PIXEL_PACKING[PIXEL_PACKING["BPP2"] = 0] = "BPP2"; /** * Three bits per pixel */ PIXEL_PACKING[PIXEL_PACKING["BPP3"] = 1] = "BPP3"; /** * Four bits per pixel */ PIXEL_PACKING[PIXEL_PACKING["BPP4"] = 2] = "BPP4"; /** * Eight bits per pixel */ PIXEL_PACKING[PIXEL_PACKING["BPP8"] = 3] = "BPP8"; })(PIXEL_PACKING = exports.PIXEL_PACKING || (exports.PIXEL_PACKING = {})); //Endian Type /** * Endianness of the image data. Only relevant when sending data less than 8BPP. * * @export * @enum {number} */ var ENDIANNESS; (function (ENDIANNESS) { /** * Data is packed with little endian order. */ ENDIANNESS[ENDIANNESS["LITTLE"] = 0] = "LITTLE"; /** * Data is packed with big endian order. */ ENDIANNESS[ENDIANNESS["BIG"] = 1] = "BIG"; })(ENDIANNESS = exports.ENDIANNESS || (exports.ENDIANNESS = {})); /** * Waveform mode used when updating the display. Further information is available in the [mode declaration]{@link https://www.waveshare.com/w/upload/c/c4/E-paper-mode-declaration.pdf}. * * @export */ var WAVEFORM; (function (WAVEFORM) { /** * The initialization (INIT) mode is used to completely erase the display and leave it in the white state. It is * useful for situations where the display information in memory is not a faithful representation of the optical * state of the display, for example, after the device receives power after it has been fully powered down. This * waveform switches the display several times and leaves it in the white state. * * **Usage:** Display initialization * * **Ghosting:** N/A * * **Typical update time:** 2000ms */ WAVEFORM[WAVEFORM["INIT"] = 0] = "INIT"; /** * The direct update (DU) is a very fast, non-flashy update. This mode supports transitions from any graytone * to black or white only. It cannot be used to update to any graytone other than black or white. The fast * update time for this mode makes it useful for response to touch sensor or pen input or menu selection * indictors. * * **Usage:** Monochrome menu, text input, and touch screen/pen input * * **Ghosting:** Low * * **Typical update time:** 260ms */ WAVEFORM[WAVEFORM["DU"] = 1] = "DU"; /** * The grayscale clearing (GC16) mode is used to update the full display and provide a high image quality. * When GC16 is used with Full Display Update the entire display will update as the new image is written. If a * Partial Update command is used the only pixels with changing graytone values will update. The GC16 mode * has 16 unique gray levels. * * **Usage:** High quality images * * **Ghosting:** Very low * * **Typical update time:** 450ms */ WAVEFORM[WAVEFORM["GC16"] = 2] = "GC16"; /** * The GL16 waveform is primarily used to update sparse content on a white background, such as a page of * anti-aliased text, with reduced flash. The GL16 waveform has 16 unique gray levels. * * **Usage:** Text with white background * * **Ghosting:** Medium * * **Typical update time:** 450ms */ WAVEFORM[WAVEFORM["GL16"] = 3] = "GL16"; /** * The GLR16 mode is used in conjunction with an image preprocessing algorithm to update sparse content on * a white background with reduced flash and reduced image artifacts. The GLR16 mode supports 16 * graytones. If only the even pixel states are used (0, 2, 4, … 30), the mode will behave exactly as a traditional * GL16 waveform mode. If a separately-supplied image preprocessing algorithm is used, the transitions * invoked by the pixel states 29 and 31 are used to improve display quality. For the AF waveform, it is * assured that the GLR16 waveform data will point to the same voltage lists as the GL16 data and does not * need to be stored in a separate memory. * * **Usage:** Text with white background * * **Ghosting:** Low * * **Typical update time:** 450ms */ WAVEFORM[WAVEFORM["GLR16"] = 4] = "GLR16"; /** * The GLD16 mode is used in conjunction with an image preprocessing algorithm to update sparse content * on a white background with reduced flash and reduced image artifacts. It is recommended to be used only * with the full display update. The GLD16 mode supports 16 graytones. If only the even pixel states are used * (0, 2, 4, … 30), the mode will behave exactly as a traditional GL16 waveform mode. If a separately-supplied * image preprocessing algorithm is used, the transitions invoked by the pixel states 29 and 31 are used to * refresh the background with a lighter flash compared to GC16 mode following a predetermined pixel map * as encoded in the waveform file, and reduce image artifacts even more compared to the GLR16 mode. For * the AF waveform, it is assured that the GLD16 waveform data will point to the same voltage lists as the * GL16 data and does not need to be stored in a separate memory. * * **Usage:** Text and graphics with white background * * **Ghosting:** Low * * **Typical update time:** 450ms */ WAVEFORM[WAVEFORM["GLD16"] = 5] = "GLD16"; /** * The A2 mode is a fast, non-flash update mode designed for fast paging turning or simple black/white * animation. This mode supports transitions from and to black or white only. It cannot be used to update to * any graytone other than black or white. The recommended update sequence to transition into repeated A2 * updates is shown in Figure 1. The use of a white image in the transition from 4-bit to 1-bit images will * reduce ghosting and improve image quality for A2 updates. * * **Usage:** Fast page flipping at reduced contrast * * **Ghosting:** Medium * * **Typical update time:** 120ms */ WAVEFORM[WAVEFORM["A2"] = 6] = "A2"; /** * The DU4 is a fast update time (similar to DU), non-flashy waveform. This mode supports transitions from * any gray tone to gray tones 1,6,11,16 represented by pixel states [0 10 20 30]. The combination of fast * update time and four gray tones make it useful for anti-aliased text in menus. There is a moderate increase * in ghosting compared with GC16. * * **Usage:** Anti-aliased text in menus / touch and screen/pen input * * **Ghosting:** Medium * * **Typical update time:** 290ms */ WAVEFORM[WAVEFORM["DU4"] = 7] = "DU4"; })(WAVEFORM = exports.WAVEFORM || (exports.WAVEFORM = {})); class IT8951 { /** * Creates an instance of IT8951. If the voltage is omitted then the controller default of -1.5v is used. * @param vcom Set the voltage of the display. */ constructor(vcom) { this.spi = new spi_1.SPI(); this.setRegister(SYS_REG.I80CPCR, 0x0001); this.info = this.systemInfo(); if (vcom !== undefined) { this.vcom = vcom; } } /** * Queries the controller for the dev info. */ systemInfo() { this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.CMD, USDEF_I80_CMD.GET_DEV_INFO])); const data = this.spi.readWords(20); const decoder = new util_1.TextDecoder(); this.info = { width: data[0], height: data[1], imbufferadr: data[2] | (data[3] << 16), firmware: decoder.decode(data.slice(4, 12)).replace(/\0*$/g, ''), lut: decoder.decode(data.slice(12, 20)).replace(/\0*$/g, ''), }; return this.info; } /** * Return the voltage */ get vcom() { this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.CMD, USDEF_I80_CMD.CMD_VCOM])); this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.WRITE, 0])); const data = this.spi.readWords(1); return data[0]; } /** * Sets the voltage */ set vcom(vcom) { this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.CMD, USDEF_I80_CMD.CMD_VCOM])); this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.WRITE, 1])); this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.WRITE, vcom])); } /** * Sets register `address` to `value`. * * @param address Address value to set * @param value Value to set */ setRegister(address, value) { this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.CMD, IT8951_COMMANDS.REG_WR])); this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.WRITE, address])); this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.WRITE, value])); } /** * Reads register value at `address` * * @param address Address value to read */ readRegister(address) { this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.CMD, IT8951_COMMANDS.REG_RD])); this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.WRITE, address])); const data = this.spi.readWords(1); return data[0]; } /** * Loads an image to memory without updating display. * * @param x vertical start of area to load * @param y horizontal start of area to load * @param width width of data to load * @param height height of data to load * @param image image data to transfer * @param bpp byte packing of image data * @param [rotate=IMAGE_ROTATION.ROTATE_0] rotate data for storage * @param [endianism=ENDIANNESS.LITTLE] image data endianness */ writePixels(x, y, width, height, image, bpp, rotate = IMAGE_ROTATION.ROTATE_0, endianism = ENDIANNESS.LITTLE) { this.setRegister(MCSR_REG.LISAR + 2, (this.info.imbufferadr >> 16) & 0x0000ffff); this.setRegister(MCSR_REG.LISAR, this.info.imbufferadr & 0x0000ffff); // Setting argument for load image start const settings = (endianism << 8) | // byte packings (bpp << 4) | // pixel format rotate; // rotation this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.CMD, IT8951_COMMANDS.LD_IMG_AREA])); this.spi.writeWords(Uint16Array.from([ SPI_PREAMBLE.WRITE, settings, x, y, width, height, ])); this.spi.writeBytes(Uint8Array.from(Buffer.concat([ Buffer.from([SPI_PREAMBLE.WRITE >> 8, SPI_PREAMBLE.WRITE]), image, ]))); this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.CMD, IT8951_COMMANDS.LD_IMG_END])); } /** * Updates a region of the screen with a previously loaded image. * * @param x vertical start of area to dsiplay * @param y horizontal start of area to dsiplay * @param width width of data to dsiplay * @param height height of data to dsiplay * @param mode waveform mode to update display */ displayArea(x, y, width, height, mode) { this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.CMD, USDEF_I80_CMD.DPY_AREA])); this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.WRITE, x])); this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.WRITE, y])); this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.WRITE, width])); this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.WRITE, height])); this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.WRITE, mode])); } /** * Issues the display running command. * * Enables all clocks and goes to active state. * */ run() { this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.CMD, IT8951_COMMANDS.SYS_RUN])); } /** * Issues the display standby command. * * Gate off clocks and go to standby state. */ standby() { this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.CMD, IT8951_COMMANDS.STANDBY])); } /** * Issues the display sleep command. * * Disables all clocks and goes to sleep state. */ sleep() { this.spi.writeWords(Uint16Array.from([SPI_PREAMBLE.CMD, IT8951_COMMANDS.SLEEP])); } /** * Returns a promise that resolves when all the lut engines are ready. * * @return {*} */ waitForDisplayReady() { return new Promise(resolve => { const interval = setInterval(() => { const regval = this.readRegister(DISPLAY_REG.LUTAFSR); if (regval === 0) { clearInterval(interval); resolve(); } }, 10); }); } /** * Resets the controller. */ reset() { return this.spi.reset(); } } exports.IT8951 = IT8951; //# sourceMappingURL=it8951.js.map