From 9f6cfa612b022c27c8323dd26fcfdf275483a908 Mon Sep 17 00:00:00 2001 From: Laurence Bank Date: Fri, 13 Sep 2024 22:32:07 +0100 Subject: [PATCH] First working version --- src/bb_ei.inl | 3358 ++------------------------------------------- src/bb_ei_gfx.inl | 1907 +++++-------------------- src/bb_ei_io.inl | 71 +- src/bb_eink.cpp | 298 ++-- src/bb_eink.h | 373 +---- 5 files changed, 629 insertions(+), 5378 deletions(-) diff --git a/src/bb_ei.inl b/src/bb_ei.inl index 22bea1d..c5febec 100644 --- a/src/bb_ei.inl +++ b/src/bb_ei.inl @@ -17,335 +17,10 @@ // bb_ei.inl // display interfacing/control code for bb_eink library // -#if defined(_LINUX_) || defined(ARDUINO_ARCH_MCS51) -#define memcpy_P memcpy -#endif -void initSPI(OBDISP *pBBEI, int iSpeed, int iMOSI, int iCLK, int iCS); -void _delay(int iDelay); -static void EPDWaitBusy(OBDISP *pBBEI, int bQuick); -static void EPDSleep(OBDISP *pBBEI, int bDeep); -#ifndef WIMPY_MCU -static void EPDWriteImage4bpp(OBDISP *pBBEI, uint8_t ucCMD, int x, int y, int w, int h); -static void EPDWriteImage2bpp(OBDISP *pBBEI, uint8_t ucCMD, int x, int y, int w, int h); -static void EPDWriteImage(OBDISP *pBBEI, uint8_t ucCMD, uint8_t *pBits, int x, int y, int w, int h, int bInvert); -#endif -void EPD213_Begin(OBDISP *pBBEI, int x, int y, int w, int h, int bPartial); -void EPD_CMD2(OBDISP *pBBEI, uint8_t cmd, uint8_t param); -void obdSetDCMode(OBDISP *pBBEI, int iMode); -void InvertBytes(uint8_t *pData, uint8_t bLen); -void SPI_BitBang(OBDISP *pBBEI, uint8_t *pData, int iLen, uint8_t iMOSIPin, uint8_t iSCKPin); -static void RawWrite(OBDISP *pBBEI, unsigned char *pData, int iLen); -void RawWriteData(OBDISP *pBBEI, unsigned char *pData, int iLen); -// EPD look up tables - +// EPD command and LUT tables // 2.7" 176x264 LUTs -const uint8_t lut_fast_vcom_dc[] PROGMEM = { - 0x00, 0x00, - 0x00, 0x1A, 0x1A, 0x00, 0x00, 0x01, - 0x00, 0x0A, 0x0A, 0x00, 0x00, 0x01, - 0x00, 0x0E, 0x01, 0x0E, 0x01, 0x01, - 0x00, 0x0A, 0x0A, 0x00, 0x00, 0x01, - 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, - 0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A, - 0x00, 0x23, 0x00, 0x00, 0x00, 0x01 -}; -const uint8_t lut_fast_ww[] PROGMEM = { - 0x90, 0x1A, 0x1A, 0x00, 0x00, 0x01, - 0x40, 0x0A, 0x0A, 0x00, 0x00, 0x01, - 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x01, - 0x80, 0x0A, 0x0A, 0x00, 0x00, 0x01, - 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, - 0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A, - 0x00, 0x23, 0x00, 0x00, 0x00, 0x01 -}; -const uint8_t lut_fast_bw[] PROGMEM = { - 0xA0, 0x1A, 0x1A, 0x00, 0x00, 0x01, - 0x00, 0x0A, 0x0A, 0x00, 0x00, 0x01, - 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x01, - 0x90, 0x0A, 0x0A, 0x00, 0x00, 0x01, - 0xB0, 0x04, 0x10, 0x00, 0x00, 0x05, - 0xB0, 0x03, 0x0E, 0x00, 0x00, 0x0A, - 0xC0, 0x23, 0x00, 0x00, 0x00, 0x01 -}; -const uint8_t lut_fast_bb[] PROGMEM = { - 0x90, 0x1A, 0x1A, 0x00, 0x00, 0x01, - 0x40, 0x0A, 0x0A, 0x00, 0x00, 0x01, - 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x01, - 0x80, 0x0A, 0x0A, 0x00, 0x00, 0x01, - 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, - 0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A, - 0x00, 0x23, 0x00, 0x00, 0x00, 0x01 -}; -const uint8_t lut_fast_wb[] PROGMEM = { - 0x90, 0x1A, 0x1A, 0x00, 0x00, 0x01, - 0x20, 0x0A, 0x0A, 0x00, 0x00, 0x01, - 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x01, - 0x10, 0x0A, 0x0A, 0x00, 0x00, 0x01, - 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, - 0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A, - 0x00, 0x23, 0x00, 0x00, 0x00, 0x01 -}; - -const uint8_t lut_vcom_dc[] PROGMEM = -{ - 0x00, 0x00, - 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x05, - 0x00, 0x32, 0x32, 0x00, 0x00, 0x02, - 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -const uint8_t lut_ww[] PROGMEM = -{ - 0x50, 0x0F, 0x0F, 0x00, 0x00, 0x05, - 0x60, 0x32, 0x32, 0x00, 0x00, 0x02, - 0xA0, 0x0F, 0x0F, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; -const uint8_t lut_bw[] PROGMEM = -{ - 0x50, 0x0F, 0x0F, 0x00, 0x00, 0x05, - 0x60, 0x32, 0x32, 0x00, 0x00, 0x02, - 0xA0, 0x0F, 0x0F, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -}; -const uint8_t lut_bb[] PROGMEM = -{ - 0xA0, 0x0F, 0x0F, 0x00, 0x00, 0x05, - 0x60, 0x32, 0x32, 0x00, 0x00, 0x02, - 0x50, 0x0F, 0x0F, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; -const uint8_t lut_wb[] PROGMEM = -{ - 0xA0, 0x0F, 0x0F, 0x00, 0x00, 0x05, - 0x60, 0x32, 0x32, 0x00, 0x00, 0x02, - 0x50, 0x0F, 0x0F, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -const unsigned char lut_vcom0_full[] PROGMEM = -{ - 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x17, 0x17, 0x00, 0x00, 0x02, - 0x00, 0x0A, 0x01, 0x00, 0x00, 0x01, - 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -const unsigned char lut42_4gray_vcom[] 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 -}; -const unsigned char lut42_4gray_ww[] 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 lut42_4gray_bw[] 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 lut42_4gray_bb[] 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 -}; -const unsigned char lut42_4gray_wb[] 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 -}; - -#ifndef WIMPY_MCU -const uint8_t epd29b_bwyr_init_sequence_full[] PROGMEM = { - 0x02, 0x4d, 0x78, - 0x03, 0x00, 0x0f, 0x29, // PSR - 0x03, 0x01, 0x07, 0x00, // PWRR - 0x04, 0x03, 0x10, 0x54, 0x44, // POFS - 0x08, 0x06, 0x05, 0x00, 0x3f, 0x0a, 0x25, 0x12, 0x1a, // BTST_P - 0x02, 0x50, 0x37, // CDI - 0x03, 0x60, 0x02, 0x02, // TCON - 0x05, 0x61, 0x00, 128, 0x01, 296-256, // TRES - 0x02, 0xe7, 0x1c, - 0x02, 0xe3, 0x22, - 0x04, 0xb4, 0xd0, 0xb5, 0x03, - 0x02, 0xe9, 0x01, - 0x02, 0x30, 0x08, - 0x01, 0x04, // PON - BUSY_WAIT, - 0x00 -}; -const uint8_t epd29_bwyr_init_sequence_full[] PROGMEM = { - 0x02, 0x4d, 0x78, - 0x03, 0x00, 0x0f, 0x29, // PSR - 0x03, 0x01, 0x07, 0x00, // PWRR - 0x04, 0x03, 0x10, 0x54, 0x44, // POFS - 0x08, 0x06, 0x05, 0x00, 0x3f, 0x0a, 0x25, 0x12, 0x1a, // BTST_P - 0x02, 0x50, 0x37, // CDI - 0x03, 0x60, 0x02, 0x02, // TCON - 0x05, 0x61, 0x00, 168, 0x01, 384-256, // TRES - 0x02, 0xe7, 0x1c, - 0x02, 0xe3, 0x22, - 0x04, 0xb4, 0xd0, 0xb5, 0x03, - 0x02, 0xe9, 0x01, - 0x02, 0x30, 0x08, - 0x01, 0x04, // PON - BUSY_WAIT, - 0x00 -}; -const uint8_t epd266_bwyr_init_sequence_full[] PROGMEM = { - 0x02, 0x4d, 0x78, - 0x03, 0x00, 0x0f, 0x29, // PSR - 0x03, 0x01, 0x07, 0x00, // PWRR - 0x04, 0x03, 0x10, 0x54, 0x44, // POFS - 0x08, 0x06, 0x05, 0x00, 0x3f, 0x0a, 0x25, 0x12, 0x1a, // BTST_P - 0x02, 0x50, 0x37, // CDI - 0x03, 0x60, 0x02, 0x02, // TCON - 0x05, 0x61, 0x00, 184, 0x01, 360-256, // TRES - 0x02, 0xe7, 0x1c, - 0x02, 0xe3, 0x22, - 0x04, 0xb4, 0xd0, 0xb5, 0x03, - 0x02, 0xe9, 0x01, - 0x02, 0x30, 0x08, - 0x01, 0x04, // PON - BUSY_WAIT, - 0x00 -}; -const uint8_t epd236_bwyr_init_sequence_full[] PROGMEM = { - 0x05, 0x66, 0x49, 0x55, 0x13, 0x5d, - 0x03, 0x66, 0x49, 0x55, - 0x02, 0xb0, 0x03, // boost - 0x03, 0x00, 0x4f, 0x6b, - 0x02, 0x03, 0x00, - 0x06, 0xf0, 0xf6, 0x0d, 0x00, 0x00, 0x00, - 0x04, 0x06, 0xcf, 0xdf, 0x0f, - 0x02, 0x41, 0x00, - 0x02, 0x50, 0x30, - 0x03, 0x60, 0x0c, 0x05, - 0x04, 0x61, 0xa8, 0x01, 0x28, // resolution - 0x02, 0x84, 0x01, - 0x00 -}; -const uint8_t epd164_bwyr_init_sequence_full[] PROGMEM = { - 0x05, 0x66, 0x49, 0x55, 0x13, 0x5d, - 0x03, 0x66, 0x49, 0x55, - 0x02, 0xb0, 0x03, // boost - 0x03, 0x00, 0x4f, 0x6b, - 0x02, 0x03, 0x00, - 0x06, 0xf0, 0xf6, 0x0d, 0x00, 0x00, 0x00, - 0x04, 0x06, 0xcf, 0xdf, 0x0f, - 0x02, 0x41, 0x00, - 0x02, 0x50, 0x30, - 0x03, 0x60, 0x0c, 0x05, - 0x04, 0x61, 0xa8, 0x00, 0xa8, // resolution - 0x02, 0x84, 0x01, - 0x00 -}; -const uint8_t epd30_bwyr_init_sequence_full[] PROGMEM = { - 0x07, 0x66, 0x49, 0x55, 0x13, 0x5d, 0x05, 0x10, - 0x02, 0xb0, 0x00, // boost - 0x03, 0x01, 0x0f, 0x00, - 0x03, 0x00, 0x4f, 0x6b, - 0x04, 0x06, 0xd7, 0xde, 0x12, - 0x05, 0x61, 0x00, 0xa8, 0x01, 0x90, // resolution - 0x02, 0x50, 0x37, - 0x03, 0x60, 0x0c, 0x05, - 0x02, 0xe3, 0xff, - 0x02, 0x84, 0x00, - 0x00 -}; - -const uint8_t epd583r_init_sequence_full[] PROGMEM = { - 0x03, 0x01, 0x37, 0x00, // power setting - 0x03, 0x00, 0xcf, 0x08, // panel setting - 0x04, 0x06, 0xc7, 0xcc, 0x28, // boost - 0x02, 0x30, 0x3a, // PLL 15s refresh - 0x02, 0x41, 0x00, // temperature - 0x02, 0x50, 0x77, // VCOM - 0x02, 0x60, 0x22, // TCON - 0x05, 0x61, 0x02, 0x58, 0x01, 0xc0, // RES - 0x02, 0x82, 0x28, // temp range - 0x02, 0xe5, 0x03, // flash mode - 0x01, 0x04, // power on - BUSY_WAIT, - 0x00 -}; -const uint8_t epd74r_init_sequence_full[] PROGMEM = { - 0x02, 0x65, 0x01, - 0x01, 0xab, - 0x02, 0x65, 0x00, - 0x01, 0x04, - BUSY_WAIT, - 0x03, 0x01, 0x37, 0x00, // 0x05, 0x05, - 0x03, 0x00, 0xcf, 0x08, -// 0x02, 0xe5, 0x03, -// 0x02, 0x03, 0x00, - 0x04, 0x06, 0xc7, 0xcc, 0x28, - 0x02, 0x30, 0x3c, - 0x02, 0x41, 0x00, - 0x02, 0x50, 0x77, - 0x02, 0x60, 0x22, - 0x05, 0x61, 0x02, 0x80, 0x01, 0x80, -// 0x02, 0x65, 0x01, -// 0x02, 0x65, 0x00, -// 0x01, 0x40, -// BUSY_WAIT, -// 0x02, 0x8d, 0x80, - 0x02, 0x82, 0x1e, - 0x02, 0xe5, 0x03, -// 0x01, 0x02, -// BUSY_WAIT, - 0x01, 0x04, - BUSY_WAIT, -// 0x02, 0x65, 0x01, -// 0x02, 0x65, 0x00, -// 0x01, 0x40, -// BUSY_WAIT, -// 0x02, 0x8d, 0xc0, -// 0x02, 0x30, 0x2a, - 0x00 -}; -#endif // !WIMPY_MCU +#ifndef __BB_EI__ +#define __BB_EI__ const uint8_t epd35r_init_sequence_full[] PROGMEM = { 0x01, 0x12, // SW RESET @@ -1128,18 +803,7 @@ const uint8_t epd583_init_sequence_part[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // end of table -};const uint8_t epd42_4gray_init_sequence_full[] PROGMEM = { - 6, UC8151_PWR, 0x03, 0x00, 0x2b, 0x2b, 0x13, - 4, UC8151_BTST, 0x17, 0x17, 0x17, - 1, UC8151_PON, - BUSY_WAIT, - 2, UC8151_PSR, 0x3f, - 2, UC8151_PLL, 0x3c, - 5, UC8151_TRES, 0x01, 0x90, 0x01, 0x2c, - 2, UC8151_VDCS, 0x12, - 2, UC8151_CDI, 0x97, - 0 -}; +}; const uint8_t epd42_init_sequence_full[] PROGMEM = { 1, UC8151_PON, @@ -1207,74 +871,39 @@ const uint8_t epd42_init_sequence_part[] PROGMEM = { 0x00 // end of table }; -#ifndef WIMPY_MCU -const uint8_t st7302_wenting[] PROGMEM PROGMEM = { - 0x02, 0xEB, 0x02, // Enable OTP - 0x02, 0xD7, 0x68, // OTP Load Control - 0x02, 0xD1, 0x01, // Auto Power Control - 0x02, 0xC0, 0x40, // Gate Voltage setting - 0x07, 0xC1, 0x22, 0x22, 0x22, 0x22, 0x14, 0x00, // VSH Setting - 0x05, 0xC2, 0x00, 0x00, 0x00, 0x00, // VSL Setting - 0x02, 0xCB, 0x0E, // VCOMH Setting - 0x0B, 0xB4, 0xE5, 0x66, 0x85, 0xFF, 0xFF, 0x52, 0x85, 0xFF, 0xFF, 0x52, // Gate EQ - 0x03, 0xC7, 0xA6, 0xE9, // OSC Setting - 0x02, 0xB0, 0x3F, // Duty Setting - 0x03, 0xB2, 0x00, 0x00, // Frame Rate Setting (lowest for both HPM/LPM) - 0x02, 0x36, 0x00, // Memory Access Mode - 0x02, 0x3A, 0x11, // Data Format - 0x02, 0xB9, 0x23, // Source Setting - 0x02, 0xB8, 0x09, // Source Setting - 0x01, 0x11, // Sleep out - 0x02, 0xff, 100, // delay 100 - 0x02, 0xD0, 0x1F, // enable auto power down - 0x01, 0x39, // Low power mode - 0x01, 0x29, // display on - 0x00 -}; -const uint8_t st7302_hpm_init[] PROGMEM = { - 0x02, 0xEB, 0x02, // Enable OTP - 0x02, 0xD7, 0x68, // OTP Load Control - 0x02, 0xD1, 0x01, // Auto Power Control - 0x02, 0xC0, 0xba , // Gate Voltage setting - 0x02, 0xCB, 0x00, // VCOMH Setting - 0x02, 0xB3, 0x94, // VCOMEQ enable - 0x07, 0xC1, 0x28, 0x28, 0x28, 0x28, 0x14, 0x00, // source voltage VSH - 0x05, 0xC2, 0x00, 0x00, 0x00, 0x00, // VSL Setting - 0x03, 0xB2, 0x01, 0x05, // Frame Rate Setting (lowest for both HPM/LPM) - 0x0B, 0xB4, 0xA5, 0x66, 0xFD, 0xBF, 0x42, 0x55, 0x81, 0xBE, 0x42, 0x55, // Gate EQ - 0x02, 0xB7, 0x01, // Source eq enable - 0x01, 0x11, // Sleep out - 0x02, 0xff, 100, // delay 100 - 0x02, 0x72, 0x13, // gate setting - 0x02, 0xB0, 0x3F, // Duty Setting - 0x03, 0xC7, 0xA6, 0xE9, // OSC Setting - 0x02, 0xD6, 0x00, // VSHLSEL source voltage select - 0x02, 0x36, 0x00, // Memory Access Mode - 0x02, 0x3A, 0x11, // Data Format - 0x02, 0xB9, 0x23, // Source Setting - 0x02, 0xB8, 0x09, // Source Setting - 0x02, 0x35, 0x00, // tearing effect line on -// 0x02, 0xD0, 0x1F, // enable auto power down - 0x01, 0x38, // High power mode - 0x01, 0x29, // display on - 0x00 -}; -#endif // !WIMPY_MCU +const uint8_t epd294_init_sequence_part[] = +{ + 0x0b, 0x37, 0,0,0,0,0,0x40,0,0,0,0, + 0x04, 0x01, 0x27, 0x01, 0x00, + 0x02, 0x11, 0x01, + 0x03, 0x44, 0x00, 0x0f, + 0x05, 0x45, 0x00, 0x00, 0x27, 0x01, + 0x03, 0x21, 0x00, 0x80, + 0x02, 0x3c, 0xc0, + 0x02, 0x22, 0xc0, + 0x01, 0x20, + BUSY_WAIT, + 0x02, 0x4e, 0x00, + 0x03, 0x4f, 0x00, 0x00, + 0x00 // end of sequence +}; /* epd294_init_sequence_part */ +uint8_t u8Cache[128]; // buffer a single line of up to 1024 pixels // Definitions for each supported panel // The order tracks that of the enumerated panel types // ** ONLY ADD NEW PANELS TO THE END OF THE LIST ** // const EPD_PANEL panelDefs[] = { - {400, 300, epd42_init_sequence_full, NULL, epd42_init_sequence_part, 0, BBEI_CHIP_UC8151}, // EPD42_400x300 + {0}, // undefined panel + {400, 300, epd42_init_sequence_full, NULL, epd42_init_sequence_part, 0, BBEI_CHIP_UC81xx}, // EPD42_400x300 {400, 300, epd42b_init_sequence_full, epd42b_init_sequence_fast, epd42b_init_sequence_part, 0, BBEI_CHIP_SSD16xx}, // EPD42B_400x300 {122, 250, epd213b_init_sequence_full, NULL, epd213b_init_sequence_part, 0, BBEI_CHIP_SSD16xx}, // EPD213B_122x250 {128, 296, epd293_init_sequence_full, epd293_init_sequence_fast, epd293_init_sequence_part, 0, BBEI_CHIP_SSD16xx}, // EPD293_128x296 {128, 296, epd294_init_sequence_full, NULL, NULL, 0, BBEI_CHIP_SSD16xx}, // EPD294_128x296 {128, 296, epd293_init_sequence_full, epd293_init_sequence_fast, epd295_init_sequence_part, 0, BBEI_CHIP_SSD16xx}, // EPD295_128x296 {152, 296, epd266_init_sequence_full, NULL, epd266_init_sequence_part, 0, BBEI_CHIP_SSD16xx}, // EPD266_152x296 - {80, 128, epd102_init_sequence_full, NULL, epd102_init_sequence_part, 0, BBEI_CHIP_UC8151}, // EPD102_80x128 + {80, 128, epd102_init_sequence_full, NULL, epd102_init_sequence_part, 0, BBEI_CHIP_UC81xx}, // EPD102_80x128 {176, 264, epd27_init_sequence_full, NULL, epd27_init_sequence_part, 0, BBEI_CHIP_SSD16xx}, // EPD27B_176x264 }; int bbeiSetPanelType(BBEIDISP *pBBEI, int iPanel) @@ -1282,7 +911,7 @@ int bbeiSetPanelType(BBEIDISP *pBBEI, int iPanel) if (pBBEI == NULL || iPanel <= EPD_PANEL_UNDEFINED || iPanel >= EPD_PANEL_COUNT) return BBEI_ERROR_BAD_PARAMETER; - memset(&pBBEI, 0, sizeof(BBEIDISP)); + memset(pBBEI, 0, sizeof(BBEIDISP)); pBBEI->native_width = pBBEI->width = panelDefs[iPanel].width; pBBEI->native_height = pBBEI->height = panelDefs[iPanel].height; pBBEI->chip_type = panelDefs[iPanel].chip_type; @@ -1293,20 +922,20 @@ int bbeiSetPanelType(BBEIDISP *pBBEI, int iPanel) return BBEI_SUCCESS; } /* bbeiSetPanelType() */ -void bbeiSetPosition(OBDISP *pBBEI, int x, int y, int w, int h) +void bbeiSetPosition(BBEIDISP *pBBEI, int x, int y, int cx, int cy) { uint8_t uc[12]; int i, tx, ty; - tx = x/8; // round down to next lower byte - ty = y; - cx = (cx + 7) & 0xfff8; // make width an even number of bytes - if (pBBEI->chip_type == EEBI_CHIP_UC8151) { + tx = x/8; // round down to next lower byte + ty = y; + cx = (cx + 7) & 0xfff8; // make width an even number of bytes + if (pBBEI->chip_type == BBEI_CHIP_UC81xx) { bbeiWriteCmd(pBBEI, UC8151_PTIN); // partial in bbeiWriteCmd(pBBEI, UC8151_PTL); // partial window i = 0; tx *= 8; - if (iWidth >= 256) { // need 2 bytes per x + if (pBBEI->native_width >= 256) { // need 2 bytes per x uc[i++] = (uint8_t)(tx>>8); // start x uc[i++] = (uint8_t)tx; uc[i++] = (uint8_t)((tx+cx-1)>>8); // end x @@ -1315,7 +944,7 @@ void bbeiSetPosition(OBDISP *pBBEI, int x, int y, int w, int h) uc[i++] = tx; // start x uc[i++] = (tx+cx-1) | 7; // end x } - if (iHeight >= 256) { + if (pBBEI->native_height >= 256) { uc[i++] = (uint8_t)(ty>>8); // start y uc[i++] = (uint8_t)ty; uc[i++] = (uint8_t)((ty+cy-1)>>8); // end y @@ -1347,1801 +976,93 @@ void bbeiSetPosition(OBDISP *pBBEI, int x, int y, int w, int h) // EPD_CMD2(SSD1608_DATA_MODE, 0x3); } } /* bbeiSetPosition() */ - -void obdWriteCommand(OBDISP *pBBEI, unsigned char c); -void obdWriteDataBlock(OBDISP *pBBEI, unsigned char *ucBuf, int iLen, int bRender); -const uint8_t ucMirror[256] PROGMEM = - {0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, - 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248, - 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244, - 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, - 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, - 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, - 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, - 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, - 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, - 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, - 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, - 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, - 3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, - 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, - 7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, - 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255}; - -// -// Draw the contents of a memory buffer onto a display -// The sub-window will be clipped if it specifies too large an area -// for the destination display. The source OBDISP structure must have -// a valid back buffer defined -// The top and bottom destination edges will be drawn on byte boundaries (8 rows) -// The source top/bot edges can be on pixel boundaries -// This can be used for partial screen updates -// -void obdDumpWindow(OBDISP *pBBEISrc, OBDISP *pBBEIDest, int srcx, int srcy, int destx, int desty, int width, int height) -{ -uint8_t *s,ucTemp[32]; // temp buffer to gather source pixels -int x, y, tx, i; -int iPitch; - - if (pBBEISrc == NULL || pBBEIDest == NULL || pBBEISrc->ucScreen == NULL) - return; // invalid pointers - if (width > pBBEIDest->width) - width = pBBEIDest->width; - if (height > pBBEIDest->height) - height = pBBEIDest->height; - iPitch = pBBEISrc->width; - for (y=0; yucScreen[((srcy+y)/8)*iPitch + srcx+x]; - if (srcy & 7) // need to shift the bits to get 8 rows of src data - { - uint8_t uc, ucShift = srcy & 7; - for (i=0; i> ucShift; - uc |= s[iPitch] << (7-ucShift); - ucTemp[i] = uc; - } - obdCachedWrite(pBBEIDest, ucTemp, tx, 1); - } - else - { // simpler case - obdCachedWrite(pBBEIDest, s, tx, 1); // just copy it - } - } // for x - } // for y - obdCachedFlush(pBBEIDest, 1); -} /* obdDumpWindow() */ - -// -// Write a subset of lines to a Sharp memory LCD -// -void obdWriteLCDLines(OBDISP *pBBEI, int iStart, int iCount) -{ - int x, y, j; - uint8_t uc, c, ucMask, *s, *d, ucStart; - uint8_t ucLineBuf[56]; // 400 pixels is max supported width = 50 bytes + 4 - int iPitch = pBBEI->width / 8; - static int iVCOM = 0; - - if (pBBEI == NULL || pBBEI->chip_type != BBEI_CHIP_SHARP || pBBEI->ucScreen == NULL || iStart < 0 || iStart > pBBEI->native_height-1 || iStart+iCount < 0 || iStart+iCount > pBBEI->native_height-1) { - return; // invalid request - } - - digitalWrite(pBBEI->iCSPin, HIGH); // active high - - ucStart = 0x80; // write command - iVCOM++; - if (iVCOM & 0x40) // flip it every 64 lines - ucStart |= 0x40; // VCOM bit - ucLineBuf[0] = ucStart; - RawWriteData(pBBEI, ucLineBuf, 1); // write command(01) + vcom(02) - - for (y=iStart; yucScreen[(y >> 3) * pBBEI->native_width]; - ucLineBuf[0] = pgm_read_byte(&ucMirror[y+1]); // current line number - for (x=0; x=0; j--) { - c = *s++; - if (c & ucMask) uc ^= (1 << j); - } - *d++ = uc; - } // for x - // write this line to the display - ucLineBuf[iPitch+1] = 0; // end of line - RawWriteData(pBBEI, ucLineBuf, iPitch+2); - } // for y - ucLineBuf[0] = 0; - RawWriteData(pBBEI, ucLineBuf, 1); // final transfer - digitalWrite(pBBEI->iCSPin, LOW); // de-activate -} /* obdWriteLCDLines() */ - -// -// Turn the display on or off -// -void obdPower(OBDISP *pBBEI, int bOn) -{ -uint8_t ucCMD; - - if (!pBBEI) return; - switch (pBBEI->type) { - case LCD_NOKIA5110: - ucCMD = (bOn) ? 0x20 : 0x24; - break; -#if !defined( WIMPY_MCU ) && !defined(__AVR__) - case LCD_ST7302: - ucCMD = (bOn) ? 0x29 : 0x28; - break; -#endif - default: // all other supported displays - ucCMD = (bOn) ? 0xaf : 0xae; - break; - // e-ink panels are put to sleep after each action - } - obdWriteCommand(pBBEI, ucCMD); -} /* obdPower() */ - -// Controls the LED backlight -void obdBacklight(OBDISP *pBBEI, int bOn) -{ - if (pBBEI->iLEDPin != 0xff) - { - digitalWrite(pBBEI->iLEDPin, (bOn) ? HIGH:LOW); - } -} /* obdBacklight() */ - -// -// Send the command sequence to power up the LCDs -static void LCDPowerUp(OBDISP *pBBEI) -{ -#ifndef MEMORY_ONLY - int iLen; - uint8_t *s; - u8Cache[0] = 0x00; // send everything in command mode - if (pBBEI->type == LCD_UC1701 || pBBEI->type == LCD_UC1609) - { - s = (uint8_t *)uc1701_initbuf; - iLen = sizeof(uc1701_initbuf); - } - else if (pBBEI->type == LCD_HX1230) - { - s = (uint8_t *)hx1230_initbuf; - iLen = sizeof(hx1230_initbuf); - } - else if (pBBEI->type == LCD_NOKIA5110) // Nokia 5110 - { - s = (uint8_t *)nokia5110_initbuf; - iLen = sizeof(nokia5110_initbuf); - } - else if (pBBEI->type == LCD_32x64) - { - s = (uint8_t *)st7567_initbuf; - iLen = sizeof(st7567_initbuf); - } - memcpy_P(&u8Cache[1], s, iLen); - RawWrite(pBBEI, u8Cache, iLen+1); - _delay(100); - obdWriteCommand(pBBEI, 0xa5); - _delay(100); - obdWriteCommand(pBBEI, 0xa4); - obdWriteCommand(pBBEI, 0xaf); -#endif // MEMORY_ONLY -} /* LCDPowerUp() */ // // More efficient means of sending commands, data and busy-pauses // -void EPDSendCMDSequence(OBDISP *pBBEI, const uint8_t *pSeq) +void bbeiSendCMDSequence(BBEIDISP *pBBEI, const uint8_t *pSeq) { int iLen; uint8_t *s; s = (uint8_t *)pSeq; - while (pgm_read_byte(s) != 0) { - iLen = pgm_read_byte(s++); + while (s[0] != 0) { + iLen = *s++; if (iLen == BUSY_WAIT) { - Serial.println("Calling busy_wait from cmd list"); - EPDWaitBusy(pBBEI, 1); + bbeiWaitBusy(pBBEI); } else { - obdWriteCommand(pBBEI, pgm_read_byte(s)); + bbeiWriteCmd(pBBEI, s[0]); s++; if (iLen > 1) { - memcpy_P(u8Cache, s, iLen-1); + bbeiWriteData(pBBEI, s, iLen-1); s += (iLen-1); - RawWriteData(pBBEI, u8Cache, iLen-1); } } } // while more commands to send -} /* EPDSendCMDSequence() */ +} /* bbeiSendCMDSequence() */ -void EPD29_Init(OBDISP *pBBEI) +int bbeiRefresh(BBEIDISP *pBBEI, int iMode) { -uint8_t ucTemp[4]; - - obdWriteCommand(pBBEI, UC8151_PON); // power on - EPDWaitBusy(pBBEI, 1); - EPD_CMD2(pBBEI, UC8151_PSR, 0x80 | 0x10 | 0x00 | 0x02 | 0x01); // panel setting - obdWriteCommand(pBBEI, UC8151_TRES); // resolution - ucTemp[0] = (uint8_t)pBBEI->native_width; - ucTemp[1] = (uint8_t)(pBBEI->native_height >> 8); - ucTemp[2] = (uint8_t)(pBBEI->native_height & 0xff); - RawWriteData(pBBEI, ucTemp, 3); -// obdWriteCommand(pBBEI, UC8151_CDI); - EPDSleep(pBBEI, 0); -} /* EPD29_Init() */ + if (iMode != REFRESH_FULL && iMode != REFRESH_FAST && iMode != REFRESH_PARTIAL) + return BBEI_ERROR_BAD_PARAMETER; + bbeiWakeUp(pBBEI); // tickle the reset line + switch (iMode) { + case REFRESH_FULL: + bbeiSendCMDSequence(pBBEI, pBBEI->pInitFull); + break; + case REFRESH_FAST: + if (!pBBEI->pInitFast) + return BBEI_ERROR_BAD_PARAMETER; + bbeiSendCMDSequence(pBBEI, pBBEI->pInitFast); + break; + case REFRESH_PARTIAL: + if (!pBBEI->pInitPart) + return BBEI_ERROR_BAD_PARAMETER; + bbeiSendCMDSequence(pBBEI, pBBEI->pInitPart); + break; + default: + return BBEI_ERROR_BAD_PARAMETER; + } // switch on mode + if (pBBEI->chip_type == BBEI_CHIP_UC81xx) { + bbeiWriteCmd(pBBEI, UC8151_PTOU); // partial out (update the entire panel, not just the last memory window) + bbeiWriteCmd(pBBEI, UC8151_DRF); + } else { + const uint8_t u8CMD[3] = {0xf7, 0xc7, 0xff}; // normal, fast, partial + bbeiCMD2(pBBEI, SSD1608_DISP_CTRL2, u8CMD[iMode]); + bbeiWriteCmd(pBBEI, SSD1608_MASTER_ACTIVATE); // refresh + } + return BBEI_SUCCESS; +} /* bbeiRefresh() */ -const unsigned char lut_w_full[] PROGMEM = -{ - 0x60, 0x5A, 0x5A, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -const unsigned char lut_b_full[] PROGMEM = -{ - 0x90, 0x5A, 0x5A, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -#ifdef FUTURE -const uint8_t epd213_lut[] PROGMEM = -{ - // black - 0x48, 0x50, 0x10, 0x10, 0x13, 0x00, 0x00, - 0x48, 0x50, 0x80, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x48, 0x55, 0x00, 0xbb, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x10, 0x04, 0x04, 0x04, 0x04, - 0x10, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x08, 0x08, 0x10, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00 -}; -#endif - -static const uint8_t epd294_lut_partial[] PROGMEM = { -0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x80,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x40,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x0A,0x0,0x0,0x0,0x0,0x0,0x2, -0x1,0x0,0x0,0x0,0x0,0x0,0x0, -0x1,0x0,0x0,0x0,0x0,0x0,0x0, -0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x0,0x0,0x0,0x0,0x0,0x0,0x0, -0x22,0x22,0x22,0x22,0x22,0x22,0x0,0x0,0x0, -//0x22,0x17,0x41,0xB0,0x32,0x36, -}; - -const uint8_t epd213_lut2_partial[] PROGMEM = -{ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT0: BB: VS 0 ~7 - 0x80,0x00,0x00,0x00,0x00,0x00,0x00, //LUT1: BW: VS 0 ~7 - 0x40,0x00,0x00,0x00,0x00,0x00,0x00, //LUT2: WB: VS 0 ~7 - 0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT3: WW: VS 0 ~7 - 0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT4: VCOM: VS 0 ~7 - - 0x0A,0x00,0x00,0x00,0x00, // TP0 A~D RP0 - 0x00,0x00,0x00,0x00,0x00, // TP1 A~D RP1 - 0x00,0x00,0x00,0x00,0x00, // TP2 A~D RP2 - 0x00,0x00,0x00,0x00,0x00, // TP3 A~D RP3 - 0x00,0x00,0x00,0x00,0x00, // TP4 A~D RP4 - 0x00,0x00,0x00,0x00,0x00, // TP5 A~D RP5 - 0x00,0x00,0x00,0x00,0x00, // TP6 A~D RP6 - -}; - -const uint8_t epd213_lut2_full[] PROGMEM = -{ - // black - 0x80,0x60,0x40,0x00,0x00,0x00,0x00, - 0x10,0x60,0x20,0x00,0x00,0x00,0x00, - 0x80,0x60,0x40,0x00,0x00,0x00,0x00, - 0x10,0x60,0x20,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00, - - 0x03,0x03,0x00,0x00,0x02, - 0x09,0x09,0x00,0x00,0x02, - 0x03,0x03,0x00,0x00,0x02, - 0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00 -}; - -const uint8_t lut154_full_update[] PROGMEM = - { - 0x80,0x48,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x40,0x48,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x8, 0x1, 0x0, 0x8, 0x1, 0x0, 0x2, - 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0 - }; - -const uint8_t lut154_partial_update[] PROGMEM = -{ - 0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x80,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x40,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0xF,0x0,0x0,0x0,0x0,0x0,0x0, - 0x1,0x1,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x0,0x0,0x0,0x0,0x0,0x0,0x0, - 0x22,0x22,0x22,0x22,0x22,0x22,0x0,0x0,0x0 -}; - -// -// Initialize the display controller on an SPI bus -// -void obdSPIInit(OBDISP *pBBEI, int iType, int iDC, int iCS, int iReset, int iMOSI, int iCLK, int iLED, int bFlip, int bInvert, int bBitBang, int32_t iSpeed) -{ -#ifndef MEMORY_ONLY - uint8_t *s=NULL; - int iLen=0; - - if (pBBEI == NULL) - return; - pBBEI->iTimeout = 5000; // 5 seconds for most e-ink panels - pBBEI->can_flip = 1; // assume can flip 180 - pBBEI->ucScreen = NULL; // start with no backbuffer; user must provide one later - pBBEI->x_offset = 0; // e-paper memory column offset - pBBEI->iDCPin = iDC; - pBBEI->iCSPin = iCS; - pBBEI->iRSTPin = iReset; - pBBEI->iMOSIPin = iMOSI; - pBBEI->iCLKPin = iCLK; - pBBEI->iLEDPin = iLED; - pBBEI->type = iType; - pBBEI->chip_type = BBEI_CHIP_SSD13xx; // assume common OLEDs - pBBEI->flip = bFlip; - pBBEI->invert = bInvert; - pBBEI->bBitBang = bBitBang; - pBBEI->wrap = 0; // default - disable text wrap - pBBEI->com_mode = COM_SPI; // communication mode - if (pBBEI->iDCPin != 0xff) // Note - not needed on Sharp Memory LCDs - { - pinMode(pBBEI->iDCPin, OUTPUT); - digitalWrite(pBBEI->iDCPin, 0); // for some reason, command mode must be set or some OLEDs/LCDs won't initialize correctly even if set later - } - if (pBBEI->iCSPin != 0xff) - { - pinMode(pBBEI->iCSPin, OUTPUT); - if (iType == SHARP_400x240 || iType == SHARP_128x128 || iType == SHARP_160x68) { - digitalWrite(pBBEI->iCSPin, LOW); - } else { - digitalWrite(pBBEI->iCSPin, HIGH); // set to not-active - } - } - if (bBitBang) - { - pinMode(iMOSI, OUTPUT); - pinMode(iCLK, OUTPUT); - } - - // Reset it - if (pBBEI->iRSTPin != 0xff) - { - pinMode(pBBEI->iRSTPin, OUTPUT); - digitalWrite(pBBEI->iRSTPin, HIGH); - _delay(200); - digitalWrite(pBBEI->iRSTPin, LOW); - _delay(10); - digitalWrite(pBBEI->iRSTPin, HIGH); - _delay(200); - } - if (iLED != -1 && iLED != 0xff) - { - if (iType >= EPD42_400x300) // for EPD displays, LED = BUSY - pinMode(iLED, INPUT_PULLUP); - else - pinMode(iLED, OUTPUT); - } - initSPI(pBBEI, iSpeed, iMOSI, iCLK, iCS); - pBBEI->native_width = pBBEI->width = 128; // assume 128x64 - pBBEI->height = 64; -#if !defined( WIMPY_MCU ) && !defined(__AVR__) - if (iType == LCD_ST7302) - { - pBBEI->native_width = pBBEI->width = 250; - pBBEI->native_height = pBBEI->height = 122; - pBBEI->can_flip = 0; - } -#endif - if (iType == OLED_80x128) - { - pBBEI->native_width = pBBEI->width = 80; - pBBEI->native_height = pBBEI->height = 128; - } - else if (iType == SHARP_160x68) - { - pBBEI->native_width = pBBEI->width = 160; - pBBEI->native_height = pBBEI->height = 68; - pBBEI->chip_type = BBEI_CHIP_SHARP; - pBBEI->can_flip = 0; - pBBEI->iDCPin = 0xff; // no D/C wire on this display - } - else if (iType == SHARP_128x128) - { - pBBEI->native_width = pBBEI->width = 128; - pBBEI->native_height = pBBEI->height = 128; - pBBEI->chip_type = BBEI_CHIP_SHARP; - pBBEI->can_flip = 0; - pBBEI->iDCPin = 0xff; // no D/C wire on this display - } - else if (iType == SHARP_144x168) - { - pBBEI->native_width = pBBEI->width = 144; - pBBEI->native_height = pBBEI->height = 168; - pBBEI->chip_type = BBEI_CHIP_SHARP; - pBBEI->can_flip = 0; - pBBEI->iDCPin = 0xff; // no D/C wire on this display - } - else if (iType == SHARP_400x240) - { - pBBEI->native_width = pBBEI->width = 400; - pBBEI->native_height = pBBEI->height = 240; - pBBEI->chip_type = BBEI_CHIP_SHARP; - pBBEI->can_flip = 0; - pBBEI->iDCPin = 0xff; // no D/C wire on this display - pBBEI->iOrientation = 0; - return; - } - else if (iType == EPD583_648x480) { - pBBEI->native_width = pBBEI->width = 648; - pBBEI->native_height = pBBEI->height = 480; - pBBEI->busy_idle = HIGH; - pBBEI->can_flip = 0; - pBBEI->chip_type = BBEI_CHIP_UC8151; - pBBEI->iFlags |= BBEI_HAS_PARTIAL_UPDATE; - pBBEI->type = EPD583_648x480; // same for the rest - pBBEI->pInitFull = epd583_init_sequence_full; - pBBEI->pInitPart = epd583_init_sequence_part; - return; // nothing else to do yet - } -#ifndef __AVR__ - else if (iType == EPD75_800x480) { - pBBEI->native_width = pBBEI->width = 800; - pBBEI->native_height = pBBEI->height = 480; - pBBEI->busy_idle = HIGH; - pBBEI->can_flip = 0; - pBBEI->chip_type = BBEI_CHIP_UC8151; - pBBEI->iFlags |= BBEI_HAS_PARTIAL_UPDATE; - pBBEI->type = EPD75_800x480; // same for the rest - pBBEI->pInitFull = epd75_init_sequence_full; - pBBEI->pInitFast = epd75_init_sequence_fast; - pBBEI->pInitPart = epd75_init_sequence_partial; - return; // nothing else to do yet - } -#endif // __AVR__ - else if (iType == EPD294_128x296) - { - pBBEI->native_width = pBBEI->width = 128; - pBBEI->native_height = pBBEI->height = 296; - pBBEI->busy_idle = LOW; - pBBEI->can_flip = 0; - //pBBEI->x_offset = 2; // column byte offset - pBBEI->chip_type = BBEI_CHIP_SSD16xx; - pBBEI->iFlags |= BBEI_HAS_PARTIAL_UPDATE; - pBBEI->pInitFull = epd294_init_sequence_full; - pBBEI->type = EPD293_128x296; // same for the rest - return; // nothing else to do yet - } - else if (iType == EPD102_80x128) - { - pBBEI->native_width = pBBEI->width = 80; - pBBEI->native_height = pBBEI->height = 130; - pBBEI->busy_idle = HIGH; - pBBEI->can_flip = 0; - pBBEI->type = iType; - pBBEI->chip_type = BBEI_CHIP_UC8151; - pBBEI->iFlags |= BBEI_HAS_PARTIAL_UPDATE; - pBBEI->pInitFull = epd102_init_sequence_full; - pBBEI->pInitPart = epd102_init_sequence_part; - return; // nothing else to do yet - } - else if (iType == EPD42B_400x300) - { - pBBEI->native_width = pBBEI->width = 400; - pBBEI->native_height = pBBEI->height = 300; - pBBEI->busy_idle = LOW; - pBBEI->can_flip = 0; - pBBEI->type = iType; - pBBEI->chip_type = BBEI_CHIP_SSD16xx; - pBBEI->iFlags |= BBEI_HAS_PARTIAL_UPDATE; - pBBEI->pInitFull = epd42b_init_sequence_full; - pBBEI->pInitFast = epd42b_init_sequence_fast; - pBBEI->pInitPart = epd42b_init_sequence_part; - return; // nothing else to do yet - } - else if (iType == EPD27B_176x264) - { - pBBEI->native_width = pBBEI->width = 176; - pBBEI->native_height = pBBEI->height = 264; - pBBEI->busy_idle = LOW; - pBBEI->can_flip = 0; - pBBEI->chip_type = BBEI_CHIP_SSD16xx; - pBBEI->iFlags |= BBEI_HAS_PARTIAL_UPDATE; - pBBEI->pInitFull = epd27_init_sequence_full; - pBBEI->pInitPart = epd27_init_sequence_part; - return; // nothing else to do yet - } - else if (iType == EPD27_176x264) - { - pBBEI->native_width = pBBEI->width = 176; - pBBEI->native_height = pBBEI->height = 264; - pBBEI->busy_idle = HIGH; - pBBEI->can_flip = 0; - pBBEI->chip_type = BBEI_CHIP_UC8151; - pBBEI->iFlags |= BBEI_CS_EVERY_BYTE; // unfortunately needs this - pBBEI->iFlags |= BBEI_HAS_PARTIAL_UPDATE; - return; // nothing else to do yet - } - else if (iType == EPD579_792x272) { - pBBEI->native_width = pBBEI->width = 792; - pBBEI->native_height = pBBEI->height = 272; - pBBEI->pInitFull = epd579_init_sequence_full; - pBBEI->busy_idle = LOW; - pBBEI->chip_type = BBEI_CHIP_SSD16xx; - pBBEI->iFlags |= BBEI_HAS_PARTIAL_UPDATE; - pBBEI->iFlags |= BBEI_CS_EVERY_BYTE; - pBBEI->can_flip = 0; - } - else if (iType == EPD266_152x296 || iType == EPD266B_152x296) { - pBBEI->native_width = pBBEI->width = 152; - pBBEI->native_height = pBBEI->height = 296; - pBBEI->pInitFull = epd266_init_sequence_full; - pBBEI->pInitPart = epd266_init_sequence_part; - pBBEI->busy_idle = LOW; - pBBEI->chip_type = BBEI_CHIP_SSD16xx; - pBBEI->iFlags |= BBEI_HAS_PARTIAL_UPDATE; - pBBEI->can_flip = 0; - pBBEI->type = iType; - // pBBEI->x_offset = (iType == EPD266B_152x296) ? 0:1; - return; // nothing else to do yet - } - else if (iType == EPD42_400x300 || iType == EPD42_4GRAY_400x300) - { // WFT0420CZ15 - pBBEI->native_width = pBBEI->width = 400; - pBBEI->native_height = pBBEI->height = 300; - pBBEI->busy_idle = HIGH; - pBBEI->chip_type = BBEI_CHIP_UC8151; - pBBEI->can_flip = 0; - if (iType == EPD42_400x300) { - pBBEI->pInitFull = epd42_init_sequence_full; - pBBEI->pInitPart = epd42_init_sequence_part; - pBBEI->iFlags |= BBEI_HAS_PARTIAL_UPDATE; - pBBEI->iFlags |= BBEI_FAST_INVERTED; - } else { - pBBEI->pInitFull = epd42_4gray_init_sequence_full; - pBBEI->iFlags |= BBEI_4COLOR | BBEI_4GRAY; - } - return; // nothing else to do yet - } - else if (iType == EPD293_128x296 || iType == EPD213B_122x250 || iType == EPD295_128x296) - { - if (iType == EPD213B_122x250) { - pBBEI->native_width = pBBEI->width = 122; - pBBEI->native_height = pBBEI->height = 250; - pBBEI->pInitFull = epd213b_init_sequence_full; - pBBEI->pInitPart = epd213b_init_sequence_part; - } else if (iType == EPD293_128x296) { - pBBEI->native_width = pBBEI->width = 128; - pBBEI->native_height = pBBEI->height = 296; - pBBEI->pInitFull = epd293_init_sequence_full; - pBBEI->pInitFast = epd293_init_sequence_fast; - pBBEI->pInitPart = epd293_init_sequence_part; - } else { // EPD295_128x296 - pBBEI->native_width = pBBEI->width = 128; - pBBEI->native_height = pBBEI->height = 296; - pBBEI->pInitFull = epd293_init_sequence_full; - pBBEI->pInitFast = epd293_init_sequence_fast; - pBBEI->pInitPart = epd295_init_sequence_part; - } - pBBEI->busy_idle = LOW; - pBBEI->chip_type = BBEI_CHIP_SSD16xx; - pBBEI->iFlags |= (BBEI_HAS_PARTIAL_UPDATE | BBEI_HAS_FAST_UPDATE); - pBBEI->can_flip = 0; - return; // nothing else to do yet - } else if (iType == EPD29_BWYR_128x296) { - pBBEI->native_width = pBBEI->width = 128; - pBBEI->native_height = pBBEI->height = 296; - pBBEI->can_flip = 0; - pBBEI->busy_idle = HIGH; - pBBEI->iFlags |= BBEI_CS_EVERY_BYTE; - pBBEI->iFlags |= BBEI_4COLOR; - pBBEI->chip_type = BBEI_CHIP_UC8151; - pBBEI->iTimeout = 25000; // 4-color need a longer timeout (20 seconds) - pBBEI->pInitFull = epd29b_bwyr_init_sequence_full; - return; - } else if (iType == EPD29_BWYR_168x384) { - pBBEI->native_width = pBBEI->width = 168; - pBBEI->native_height = pBBEI->height = 384; - pBBEI->can_flip = 0; - pBBEI->busy_idle = HIGH; - pBBEI->iFlags |= BBEI_4COLOR; - pBBEI->chip_type = BBEI_CHIP_UC8151; - pBBEI->iTimeout = 25000; // 4-color need a longer timeout (20 seconds) - pBBEI->pInitFull = epd29_bwyr_init_sequence_full; - return; - } else if (iType == EPD266_BWYR_184x360) { - pBBEI->native_width = pBBEI->width = 184; - pBBEI->native_height = pBBEI->height = 360; - pBBEI->can_flip = 0; - pBBEI->busy_idle = HIGH; - pBBEI->iFlags |= BBEI_4COLOR; - pBBEI->chip_type = BBEI_CHIP_UC8151; - pBBEI->iTimeout = 20000; // 4-color need a longer timeout (20 seconds) - pBBEI->pInitFull = epd266_bwyr_init_sequence_full; - EPDSendCMDSequence(pBBEI, pBBEI->pInitFull); - return; - } else if (iType == EPD236_BWYR_168x296) { - pBBEI->native_width = pBBEI->width = 168; - pBBEI->native_height = pBBEI->height = 296; - pBBEI->can_flip = 0; - pBBEI->busy_idle = HIGH; - pBBEI->iFlags |= BBEI_4COLOR; - pBBEI->chip_type = BBEI_CHIP_UC8151; - pBBEI->iFlags |= BBEI_CS_EVERY_BYTE; - pBBEI->iTimeout = 25000; // 4-color need a longer timeout (20 seconds) - pBBEI->pInitFull = epd236_bwyr_init_sequence_full; - return; - } else if (iType == EPD164_BWYR_168x168) { - pBBEI->native_width = pBBEI->width = 168; - pBBEI->native_height = pBBEI->height = 168; - pBBEI->can_flip = 0; - pBBEI->busy_idle = HIGH; - pBBEI->iFlags |= BBEI_4COLOR; - pBBEI->chip_type = BBEI_CHIP_UC8151; - pBBEI->iFlags |= BBEI_CS_EVERY_BYTE; - pBBEI->iTimeout = 25000; // 4-color need a longer timeout (20 seconds) - pBBEI->pInitFull = epd164_bwyr_init_sequence_full; - return; - } else if (iType == EPD30_BWYR_168x400) { - pBBEI->native_width = pBBEI->width = 168; - pBBEI->native_height = pBBEI->height = 400; - pBBEI->can_flip = 0; - pBBEI->busy_idle = HIGH; - pBBEI->iFlags |= BBEI_4COLOR; - pBBEI->chip_type = BBEI_CHIP_UC8151; - pBBEI->iFlags |= BBEI_CS_EVERY_BYTE; - pBBEI->iTimeout = 20000; // 4-color need a longer timeout (20 seconds) - pBBEI->pInitFull = epd30_bwyr_init_sequence_full; - return; - } else if (iType == EPD213_122x250 || iType == EPD213_104x212 || iType == EPD29B_128x296) - { - if (iType == EPD213_122x250) { - pBBEI->native_width = pBBEI->width = 122; - pBBEI->native_height = pBBEI->height = 250; - pBBEI->iFlags |= BBEI_HAS_FAST_UPDATE; - pBBEI->pInitFull = epd213_122x250_init_sequence_full; - pBBEI->pInitPart = epd213_122x250_init_sequence_part; - } else if (iType == EPD213_104x212) { - pBBEI->native_width = pBBEI->width = 104; - pBBEI->native_height = pBBEI->height = 212; - pBBEI->pInitFull = epd213_104x212_init_sequence_full; - } else { - pBBEI->native_width = pBBEI->width = 128; - pBBEI->native_height = pBBEI->height = 296; - pBBEI->pInitFull = epd29b_init_sequence_full; - } - pBBEI->can_flip = 0; - pBBEI->busy_idle = LOW; - pBBEI->iFlags |= BBEI_HAS_FAST_UPDATE; - pBBEI->chip_type = BBEI_CHIP_SSD16xx; - return; -#if !defined( WIMPY_MCU ) && !defined(__AVR__) -} else if (iType == EPD74R_640x384 || iType == EPD583R_600x448) { - if (iType == EPD583R_600x448) { - pBBEI->native_width = pBBEI->width = 600; - pBBEI->native_height = pBBEI->height = 448; - pBBEI->type = EPD74R_640x384; - } else { - pBBEI->native_width = pBBEI->width = 640; - pBBEI->native_height = pBBEI->height = 384; - } - pBBEI->can_flip = 0; - pBBEI->busy_idle = HIGH; - pBBEI->iFlags |= BBEI_3COLOR; - pBBEI->chip_type = BBEI_CHIP_UC8151; -// pBBEI->iFlags |= BBEI_CS_EVERY_BYTE; - pBBEI->iTimeout = 25000; // 3-color need a longer timeout (25 seconds) - return; -#endif // !WIMPY_MCU - } else if (iType == EPD42R2_400x300 || iType == EPD213R_104x212 || iType == EPD37Y_240x416) { - if (iType == EPD37Y_240x416) { - pBBEI->native_width = pBBEI->width = 240; - pBBEI->native_height = pBBEI->height = 416; - pBBEI->type = EPD42R2_400x300; // use the same commands - }else if (iType == EPD213R_104x212) { - pBBEI->native_width = pBBEI->width = 104; - pBBEI->native_height = pBBEI->height = 212; - pBBEI->type = EPD42R2_400x300; // use the same commands - } else { - pBBEI->native_width = pBBEI->width = 400; - pBBEI->native_height = pBBEI->height = 300; - } - pBBEI->pInitFull = epd42r2_init_sequence_full; - pBBEI->can_flip = 0; - pBBEI->busy_idle = HIGH; - pBBEI->iFlags |= BBEI_3COLOR; - pBBEI->chip_type = BBEI_CHIP_UC8151; - pBBEI->iTimeout = 25000; // 3-color need a longer timeout (25 seconds) - EPDSendCMDSequence(pBBEI, pBBEI->pInitFull); - } else if (iType == EPD213R_104x212_d) { - pBBEI->native_width = pBBEI->width = 104; - pBBEI->native_height = pBBEI->height = 212; - pBBEI->type = EPD213R_104x212_d; - pBBEI->can_flip = 0; - pBBEI->busy_idle = HIGH; - pBBEI->iFlags |= BBEI_3COLOR; - pBBEI->chip_type = BBEI_CHIP_UC8151; - pBBEI->iTimeout = 25000; // 3-color need a longer timeout (25 seconds) - } - else if (iType == EPD35R_184x384) { - pBBEI->native_width = pBBEI->width = 184; - pBBEI->native_height = pBBEI->height = 384; - pBBEI->iTimeout = 25000; - pBBEI->iFlags |= BBEI_3COLOR; - pBBEI->chip_type = BBEI_CHIP_SSD16xx; - pBBEI->can_flip = 0; - pBBEI->busy_idle = LOW; - pBBEI->pInitFull = epd35r_init_sequence_full; - return; - } - else if (iType == EPD29R_128x296 || iType == EPD154R_152x152 || iType == EPD42R_400x300 || iType == EPD31R_168x296 || iType == EPD154Y_152x152 || iType == EPD29Y_128x296 || iType == EPD213R2_122x250 || iType == EPD266Y_152x296 || iType == EPD42Y_400x300) - { // BLACK/WHITE/RED (or yellow) - if (iType == EPD266Y_152x296) { - pBBEI->native_width = pBBEI->width = 152; - pBBEI->native_height = pBBEI->height = 296; - pBBEI->pInitFull = epd29r_init_sequence; - pBBEI->x_offset = 1; - } else if (iType == EPD31R_168x296) { - pBBEI->native_width = pBBEI->width = 168; - pBBEI->native_height = pBBEI->height = 296; - pBBEI->pInitFull = epd29r_init_sequence; - } else if (iType == EPD29Y_128x296) { - pBBEI->native_width = pBBEI->width = 128; - pBBEI->native_height = pBBEI->height = 296; - pBBEI->pInitFull = epd29r_init_sequence; - pBBEI->x_offset = 1; - } else if (iType == EPD29R_128x296) { - pBBEI->native_width = pBBEI->width = 128; - pBBEI->native_height = pBBEI->height = 296; - pBBEI->iFlags |= BBEI_HAS_FAST_UPDATE; - pBBEI->pInitFull = epd29r_init_sequence; - } else if (iType == EPD154Y_152x152) { - pBBEI->native_width = pBBEI->width = 152; - pBBEI->native_height = pBBEI->height = 152; - pBBEI->pInitFull = epd29r_init_sequence_152; - pBBEI->x_offset = 1; - } else if (iType == EPD154R_152x152){ - pBBEI->native_width = pBBEI->width = 152; - pBBEI->native_height = pBBEI->height = 152; - pBBEI->pInitFull = epd29r_init_sequence_152; - } else if (iType == EPD213R2_122x250) { - pBBEI->native_width = pBBEI->width = 122; - pBBEI->native_height = pBBEI->height = 250; - pBBEI->pInitFull = epd29r_init_sequence; - pBBEI->x_offset = 1; - } else { // EPD42R_400x300 and EPD42Y_400x300 - pBBEI->native_width = pBBEI->width = 400; - pBBEI->native_height = pBBEI->height = 300; - pBBEI->pInitFull = epd42r_init_sequence; - } - pBBEI->type = EPD29R_128x296; // use the rest of the same code - pBBEI->iTimeout = 25000; // 3-color need a longer timeout (25 seconds) - pBBEI->iFlags |= BBEI_3COLOR; - pBBEI->chip_type = BBEI_CHIP_SSD16xx; - pBBEI->can_flip = 0; - pBBEI->busy_idle = LOW; -// EPDSendCMDSequence(pBBEI, pBBEI->pInitFull); - return; - } - else if (iType == EPD154_152x152) // GDEW0154M10 - { - pBBEI->native_width = pBBEI->width = 152; - pBBEI->native_height = pBBEI->height = 152; - pBBEI->can_flip = 0; // flip display commands don't exist - pBBEI->busy_idle = HIGH; - pBBEI->chip_type = BBEI_CHIP_UC8151; - pBBEI->iFlags |= BBEI_HAS_PARTIAL_UPDATE; -// pBBEI->iFlags |= BBEI_CS_EVERY_BYTE; - pBBEI->pInitFull = epd154a_init_sequence_full; - pBBEI->pInitPart = epd154a_init_sequence_part; - return; - } - else if (iType == EPD154_200x200 || iType == EPD154B_200x200) - { - pBBEI->native_width = pBBEI->width = 200; - pBBEI->native_height = pBBEI->height = 200; - pBBEI->can_flip = 0; // flip display commands don't exist - pBBEI->busy_idle = LOW; - pBBEI->chip_type = BBEI_CHIP_SSD16xx; - pBBEI->iFlags |= BBEI_HAS_PARTIAL_UPDATE; - pBBEI->pInitFull = epd154_init_sequence_full; - pBBEI->pInitPart = (iType == EPD154B_200x200) ? epd154b_init_sequence_part : epd154_init_sequence_part; - return; - } - else if (iType == EPD29_128x296 || iType == EPD213B_104x212 || iType == EPD37_240x416) - { - pBBEI->iFlags |= BBEI_HAS_PARTIAL_UPDATE; - pBBEI->chip_type = BBEI_CHIP_UC8151; - pBBEI->can_flip = 0; // flip display commands don't exist - if (iType == EPD37_240x416) { - pBBEI->native_width = pBBEI->width = 240; - pBBEI->native_height = pBBEI->height = 416; - pBBEI->busy_idle = HIGH; - pBBEI->pInitFull = epd37_init_sequence_full; - pBBEI->pInitPart = epd37_init_sequence_part; -// EPDSendCMDSequence(pBBEI, epd37_init_sequence_full); - } else if (iType == EPD29_128x296) { - pBBEI->native_width = pBBEI->width = 128; - pBBEI->native_height = pBBEI->height = 296; - pBBEI->busy_idle = HIGH; - pBBEI->pInitFull = epd29_init_sequence_full; - // EPDSendCMDSequence(pBBEI, pBBEI->pInitFull); - } else { - pBBEI->native_width = pBBEI->width = 104; - pBBEI->native_height = pBBEI->height = 212; - pBBEI->busy_idle = LOW; - pBBEI->type = EPD29_128x296; // tell it to use this config -// EPD29_Init(pBBEI); - } - return; - } else if (iType == EPD47_540x960) { - pBBEI->native_width = pBBEI->width = 540; - pBBEI->native_height = pBBEI->height = 960; - pBBEI->can_flip = 0; // flip display commands don't exist - pBBEI->busy_idle = HIGH; - return; - } - else if (iType == LCD_32x64) - { - pBBEI->native_width = pBBEI->width = 32; - pBBEI->native_height = pBBEI->height = 64; - } - else if (iType == LCD_UC1609) - { - pBBEI->native_width = pBBEI->width = 192; - pBBEI->native_height = pBBEI->height = 64; - } - else if (iType == LCD_HX1230) - { - pBBEI->native_width = pBBEI->width = 96; - pBBEI->native_height = pBBEI->height = 68; - pBBEI->iDCPin = 0xff; // flag this as being 3-wire SPI - } - else if (iType == LCD_NOKIA5110) - { - pBBEI->native_width = pBBEI->width = 84; - pBBEI->native_height = pBBEI->height = 48; - } - else if (iType == OLED_96x16) - { - pBBEI->native_width = pBBEI->width = 96; - pBBEI->native_height = pBBEI->height = 16; - } - else if (iType == OLED_64x128) - { - // NOTE: 64x128 SH1107 doesn't seem to properly support - // segment remap, so the init commands are A0/C0 and - // it can't be flipped 180 - pBBEI->can_flip = 0; - pBBEI->native_width = pBBEI->width = 64; - pBBEI->native_height = pBBEI->height = 128; - } - else if (iType == OLED_128x32) - pBBEI->native_height = pBBEI->height = 32; - else if (iType == OLED_128x128) - pBBEI->native_height = pBBEI->height = 128; - else if (iType == OLED_64x48) - { - pBBEI->native_width = pBBEI->width = 64; - pBBEI->native_height = pBBEI->height = 48; - } - else if (iType == OLED_64x32) - { - pBBEI->native_width = pBBEI->width = 64; - pBBEI->native_height = pBBEI->height = 32; - } - else if (iType == OLED_72x40) - { - pBBEI->native_width = pBBEI->width = 72; - pBBEI->native_height = pBBEI->height = 40; - } - if (iType == OLED_80x128) - { - s = (uint8_t *)oled80_initbuf; - iLen = sizeof(oled80_initbuf); - } - else if (iType == OLED_128x32 || iType == OLED_96x16) - { - s = (uint8_t *)oled32_initbuf; - iLen = sizeof(oled32_initbuf); - } - else if (iType == OLED_64x128) - { - s = (uint8_t *)oled64x128_initbuf; - iLen = sizeof(oled64x128_initbuf); - } - else if (iType == OLED_128x128) - { - s = (uint8_t *)oled128_initbuf; - iLen = sizeof(oled128_initbuf); - } - else if (iType == OLED_132x64) { // SH1106 - s = (uint8_t *)oled132_initbuf; - iLen = sizeof(oled132_initbuf); - } - else if (iType < LCD_UC1701) // 128x64 and 64x32 oleds - { - s = (uint8_t *)oled64_initbuf; - iLen = sizeof(oled64_initbuf); - } - // OLED - if (iType < LCD_UC1701) - { - memcpy_P(u8Cache, s, iLen); // do it from RAM - RawWrite(pBBEI, u8Cache, iLen); - _delay(100); // on SPI display this delay is needed or the display - // never sees the "display on" command at the end of the sequence - obdWriteCommand(pBBEI, 0xaf); // turn on display - if (bInvert) - { - u8Cache[0] = 0; // command - u8Cache[1] = 0xa7; // invert command - RawWrite(pBBEI, u8Cache, 2); - } - if (bFlip) // rotate display 180 - { - u8Cache[0] = 0; // command - u8Cache[1] = 0xa0; - RawWrite(pBBEI, u8Cache, 2); - u8Cache[0] = 0; - u8Cache[1] = 0xc0; - RawWrite(pBBEI, u8Cache, 2); - } - } // OLED -#if !defined( WIMPY_MCU ) && !defined(__AVR__) - if (iType == LCD_ST7302) - { - // uint8_t *s = (uint8_t *)st7302_hpm_init; - const uint8_t *s = (uint8_t *)st7302_wenting; //st7302_lpm_init; - iLen = 1; - - while (iLen) { - iLen = pgm_read_byte(s++); // parameter byte count - if (iLen) { - if (pgm_read_byte(s) == 0xff) { // delay - _delay(pgm_read_byte(&s[1])); - s += 2; - } else { - obdWriteCommand(pBBEI, pgm_read_byte(s)); - if (iLen > 1) { - memcpy_P(u8Cache, s, iLen); - RawWrite(pBBEI, u8Cache, iLen); - } - s += iLen; - } - } - } // while commands to transmit - if (pBBEI->invert) - obdWriteCommand(pBBEI, 0x21); // inversion on - // Clear RAM -// obdWriteCommand(pBBEI, 0xb9); -// uc[0] = 0x40; -// uc[1] = 0xe3; -// RawWrite(pBBEI, uc, 2); -// _delay(1000); -// obdWriteCommand(pBBEI, 0xb9); -// uc[0] = 0x40; -// uc[1] = 0x23; -// RawWrite(pBBEI, uc, 2); - return; - } /* ST7302 */ -#endif // !WIMPY_MCU - if (iType == LCD_UC1701 || iType == LCD_HX1230 || iType == LCD_32x64) - { - uint8_t cCOM = 0xc0; - LCDPowerUp(pBBEI); - if (iType == LCD_HX1230) - { - obdSetContrast(pBBEI, 0); // contrast of 0 looks good - cCOM = 0xc8; - } - if (bFlip) // flip horizontal + vertical - { - obdWriteCommand(pBBEI, 0xa1); // set SEG direction (A1 to flip horizontal) - obdWriteCommand(pBBEI, cCOM); // set COM direction (C0 to flip vert) - } - if (bInvert) - { - obdWriteCommand(pBBEI, 0xa7); // set inverted pixel mode - } - } - if (iType == LCD_UC1609) - { - LCDPowerUp(pBBEI); - return; -// obdWriteCommand(pBBEI, 0xe2); // system reset -// _delay(100); -// obdWriteCommand(pBBEI, 0xa0); // set frame rate to 76fps -// obdWriteCommand(pBBEI, 0xeb); // set BR -// obdWriteCommand(pBBEI, 0x2f); // set Power Control -// obdWriteCommand(pBBEI, 0xc4); // set LCD mapping control -// obdWriteCommand(pBBEI, 0x81); // set PM -// obdWriteCommand(pBBEI, 0x90); // set contrast to 144 -// _delay(100); -// obdWriteCommand(pBBEI, 0xaf); // display enable -// if (bFlip) // flip horizontal + vertical -// { -// obdWriteCommand(pBBEI, 0xa1); // set SEG direction (A1 to flip horizontal) -// obdWriteCommand(pBBEI, 0xc2); // set COM direction (C0 to flip vert) -// } -// if (bInvert) -// { -// obdWriteCommand(pBBEI, 0xa7); // set inverted pixel mode -// } - } // UC1609 -#endif // MEMORY_ONLY -} /* obdSPIInit() */ -void obdSetRotation(OBDISP *pBBEI, int iRotation) +void bbeiSetRotation(BBEIDISP *pBBEI, int iRotation) { pBBEI->iScreenOffset = 0; - + pBBEI->iOrientation = iRotation; + switch (iRotation) { default: return; case 0: - obdSetFlip(pBBEI, 0); - pBBEI->iOrientation = 0; pBBEI->width = pBBEI->native_width; pBBEI->height = pBBEI->native_height; break; - case 1: case 90: - obdSetFlip(pBBEI, 0); - pBBEI->iOrientation = 90; pBBEI->width = pBBEI->native_height; pBBEI->height = pBBEI->native_width; break; - case 2: case 180: - pBBEI->iOrientation = 180; - obdSetFlip(pBBEI, 1); pBBEI->width = pBBEI->native_width; pBBEI->height = pBBEI->native_height; break; - case 3: case 270: - pBBEI->iOrientation = 270; - obdSetFlip(pBBEI, 1); pBBEI->width = pBBEI->native_height; pBBEI->height = pBBEI->native_width; break; } -} /* obdSetRotation() */ -// -// Set the memory configuration to display the pixels at 0 or 180 degrees (flipped) -// Pass true (1) to flip 180, false (0) to set to 0 -// -void obdSetFlip(OBDISP *pBBEI, int iOnOff) -{ - if (pBBEI == NULL) return; - if (!pBBEI->can_flip) return; // don't try it on some displays - - pBBEI->flip = iOnOff; - if (pBBEI->type == LCD_UC1701 || pBBEI->type == LCD_UC1609 || pBBEI->type == LCD_32x64) - { - if (iOnOff) // rotate display 180 - { - obdWriteCommand(pBBEI, 0xa1); // SEG direction (A1 to flip horizontal) - obdWriteCommand(pBBEI, 0xc0); // COM direction (C0 to flip vert) - } else { // non-rotated - obdWriteCommand(pBBEI, 0xa0); - obdWriteCommand(pBBEI, 0xc8); - } - } else { // OLEDs - if (iOnOff) - { - obdWriteCommand(pBBEI, 0xa0); - obdWriteCommand(pBBEI, 0xc0); - } else { - obdWriteCommand(pBBEI, 0xa1); - obdWriteCommand(pBBEI, 0xc8); - } - } -} /* obdSetFlip() */ -// -// Initializes the OLED controller into "page mode" -// -int obdI2CInit(OBDISP *pBBEI, int iType, int iAddr, int bFlip, int bInvert, int bWire, int sda, int scl, int reset, int32_t iSpeed) -{ -int rc = OLED_NOT_FOUND; -#ifndef MEMORY_ONLY -unsigned char uc[32]; -uint8_t u8Len, *s; - - if (pBBEI == NULL) - return -1; - pBBEI->can_flip = 1; // assume can flip 180 - if (iType < 0 || iType >= LCD_COUNT) - return -1; // invalid display type - if (sda == scl) { // invalid situation, set to -1 - sda = scl = -1; - } - pBBEI->type = iType; - pBBEI->flip = bFlip; - pBBEI->invert = bInvert; - pBBEI->wrap = 0; // default - disable text wrap - pBBEI->bbi2c.iSDA = sda; - pBBEI->bbi2c.iSCL = scl; - pBBEI->bbi2c.bWire = bWire; - pBBEI->com_mode = COM_I2C; // communication mode -#ifdef _LINUX_ - pBBEI->bbi2c.iBus = sda; - pBBEI->ucScreen = NULL; -#endif - I2CInit(&pBBEI->bbi2c, iSpeed); // on Linux, SDA = bus number, SCL = device address -#ifdef _LINUX_ - pBBEI->oled_addr = (uint8_t)scl; -#else - // Reset it - if (reset != 0xff && reset != -1) - { - pinMode(reset, OUTPUT); - digitalWrite(reset, HIGH); - _delay(50); - digitalWrite(reset, LOW); - _delay(50); - digitalWrite(reset, HIGH); - _delay(10); - } - // find the device address if requested - if (iAddr == -1 || iAddr == 0 || iAddr == 0xff) // find it - { - I2CTest(&pBBEI->bbi2c, 0x3c); - if (I2CTest(&pBBEI->bbi2c, 0x3c)) - pBBEI->oled_addr = 0x3c; - else if (I2CTest(&pBBEI->bbi2c, 0x3d)) - pBBEI->oled_addr = 0x3d; - else - return rc; // no display found! - } - else - { - pBBEI->oled_addr = iAddr; - I2CTest(&pBBEI->bbi2c, iAddr); - if (!I2CTest(&pBBEI->bbi2c, iAddr)) - return rc; // no display found - } - -#endif - // Detect the display controller (SSD1306, SH1107 or SH1106) - uint8_t u = 0; - I2CReadRegister(&pBBEI->bbi2c, pBBEI->oled_addr, 0x00, &u, 1); // read the status register - u &= 0x0f; // mask off power on/off bit - if ((u == 0x7 || u == 0xf) && pBBEI->type == OLED_128x128) // SH1107 - { // A single SSD1306 display returned 7, so only act on it if the - // user specified that they're working with a 128x128 display - rc = OLED_SH1107_3C; - bFlip = !bFlip; // SH1107 seems to have this reversed from the usual direction - } - else if (u == 0x8) // SH1106 - { - rc = OLED_SH1106_3C; - pBBEI->type = OLED_132x64; // needs to be treated a little differently - } - else if (u == 3 || u == 6 || u == 7) // 7=128x64(rare),6=128x64 display, 3=smaller - { - rc = OLED_SSD1306_3C; - } - if (pBBEI->oled_addr == 0x3d) { - rc++; // return the '3D' version of the type - } - if (iType == OLED_80x128) - { - s = (uint8_t *)oled80_initbuf; - u8Len = sizeof(oled80_initbuf); - } - if (iType == OLED_128x32 || iType == OLED_96x16) - { - s = (uint8_t *)oled32_initbuf; - u8Len = sizeof(oled32_initbuf); - } - else if (iType == OLED_128x128) - { - s = (uint8_t *)oled128_initbuf; - u8Len = sizeof(oled128_initbuf); - } - else if (iType == OLED_72x40) - { - s = (uint8_t *)oled72_initbuf; - u8Len = sizeof(oled72_initbuf); - } - else if (iType == OLED_64x128) - { - s = (uint8_t *)oled64x128_initbuf; - u8Len = sizeof(oled64x128_initbuf); - pBBEI->can_flip = 0; // this display can't flip 180 (don't know why) - } - else if (iType == LCD_128x64_I2C) - { - obdWriteCommand(pBBEI, 0xe2); // Need to be reset after power-on - _delay(10); - s = (uint8_t *)lcd128x64_initbuf; - u8Len = sizeof(lcd128x64_initbuf); - rc = LCD_OK; - memcpy_P(uc, s, u8Len); - } - else // 132x64, 128x64, 64x48 and 64x32 - { - s = (uint8_t *)oled64_initbuf; - u8Len = sizeof(oled64_initbuf); - } - - memcpy_P(uc, s, u8Len); - RawWrite(pBBEI, uc, u8Len); - if (bInvert) - { - uc[0] = 0; // command - uc[1] = 0xa7; // invert command - RawWrite(pBBEI,uc, 2); - } - if (bFlip) // rotate display 180 - { - uc[0] = 0; // command - uc[1] = 0xa0; - RawWrite(pBBEI,uc, 2); - uc[1] = 0xc0; - RawWrite(pBBEI,uc, 2); - } - pBBEI->native_width = pBBEI->width = 128; // assume 128x64 - pBBEI->native_height = pBBEI->height = 64; - if (iType == OLED_96x16) - { - pBBEI->native_width = pBBEI->width = 96; - pBBEI->native_height = pBBEI->height = 16; - } - else if (iType == OLED_80x128) - { - pBBEI->native_width = pBBEI->width = 80; - pBBEI->native_height = pBBEI->height = 128; - } - else if (iType == OLED_64x128) - { - pBBEI->native_width = pBBEI->width = 64; - pBBEI->native_height = pBBEI->height = 128; - } - else if (iType == OLED_128x32) - pBBEI->native_height = pBBEI->height = 32; - else if (iType == OLED_128x128) - pBBEI->native_height = pBBEI->height = 128; - else if (iType == OLED_64x32) - { - pBBEI->native_width = pBBEI->width = 64; - pBBEI->native_height = pBBEI->height = 32; - } - else if (iType == OLED_64x48) - { - pBBEI->native_width = pBBEI->width = 64; - pBBEI->native_height = pBBEI->height = 48; - } - else if (iType == OLED_72x40) - { - pBBEI->native_width = pBBEI->width = 72; - pBBEI->native_height = pBBEI->height = 40; - } -#endif // MEMORY_ONLY - return rc; -} /* obdI2CInit() */ -// -// Sends a command to turn on or off the OLED display -// -void oledPower(OBDISP *pBBEI, uint8_t bOn) -{ - if (bOn) - obdWriteCommand(pBBEI, 0xaf); // turn on OLED - else - obdWriteCommand(pBBEI, 0xae); // turn off OLED -} /* oledPower() */ - -// Sets the D/C pin to data or command mode -void obdSetDCMode(OBDISP *pBBEI, int iMode) -{ - if (pBBEI->iDCPin == 0xff) // 9-bit SPI - pBBEI->mode = (uint8_t)iMode; - else // set the GPIO line - digitalWrite(pBBEI->iDCPin, (iMode == MODE_DATA)); -} /* obdSetDCMode() */ - -static void obdWriteCommand2(OBDISP *pBBEI, unsigned char c, unsigned char d) -{ -unsigned char buf[4]; - - if (pBBEI->com_mode == COM_I2C) {// I2C device - buf[0] = 0x00; - buf[1] = c; - buf[2] = d; - RawWrite(pBBEI, buf, 3); - } else { // must be SPI - obdWriteCommand(pBBEI, c); - obdWriteCommand(pBBEI, d); - } -} /* obdWriteCommand2() */ - -// -// Sets the brightness (0=off, 255=brightest) -// -void obdSetContrast(OBDISP *pBBEI, unsigned char ucContrast) -{ - if (pBBEI->type == LCD_HX1230) - { // valid values are 0-31, so scale it - ucContrast >>= 3; - obdWriteCommand(pBBEI, 0x80 + ucContrast); - } - else if (pBBEI->type == LCD_NOKIA5110) - { - // we allow values of 0xb0-0xbf, so shrink the range - ucContrast >>= 4; - obdWriteCommand(pBBEI, 0x21); // set advanced command mode - obdWriteCommand(pBBEI, 0xb0 | ucContrast); - obdWriteCommand(pBBEI, 0x20); // set simple command mode - } - else // OLEDs + UC1701 - obdWriteCommand2(pBBEI, 0x81, ucContrast); -} /* obdSetContrast() */ -static void EPDWaitBusy(OBDISP *pBBEI, int bQuick) -{ -#ifndef MEMORY_ONLY -int iTimeout = 0; -int iEndTime = (int)pBBEI->iTimeout; - long l = micros(); - - if (pBBEI->iLEDPin == 0xff || pBBEI->iLEDPin == -1) { - if (bQuick) - iEndTime /= 5; // 1/5th for short waits such as after Power-on - _delay(iEndTime); // no busy pin, wait fixed amount - return; - } - _delay(1); - while (1) { - if (digitalRead(pBBEI->iLEDPin) == pBBEI->busy_idle) break; - } -// while (1) { -// if (digitalRead(pBBEI->iLEDPin) == pBBEI->busy_idle) -// break; -// iTimeout += (iEndTime/4); -// if (iTimeout > iEndTime) -// break; // DEBUG - timeout -// _delay(iEndTime/4); -// } - l = micros() - l; - Serial.print((int)l, DEC); - Serial.println(" us spent in EPDWaitBusy"); - - if ((l/1000) > iEndTime) { -#if defined( ARDUINO ) && !defined(WIMPY_MCU) - Serial.println("EPDWaitBusy() timed out"); -#endif -#if defined(_LINUX_) - printf("EPDWaitBusy() timed out\n"); -#endif - } -#endif // MEMORY_ONLY -} /* EPDWaitBusy() */ - -void EPD27_SetLut(OBDISP *pBBEI, int bFast) -{ - if (bFast) { - obdWriteCommand(pBBEI, UC8151_LUT_VCOM); //vcom - RawWriteData(pBBEI, (uint8_t *)lut_fast_vcom_dc, 44); - - obdWriteCommand(pBBEI, UC8151_LUT_WW); - RawWriteData(pBBEI, (uint8_t *)lut_fast_ww, 42); - - obdWriteCommand(pBBEI, UC8151_LUT_BW); - RawWriteData(pBBEI, (uint8_t *)lut_fast_bw, 42); - - obdWriteCommand(pBBEI, UC8151_LUT_WB); - RawWriteData(pBBEI, (uint8_t *)lut_fast_bb, 42); - - obdWriteCommand(pBBEI, UC8151_LUT_BB); - RawWriteData(pBBEI, (uint8_t *)lut_fast_wb, 42); - } else { - obdWriteCommand(pBBEI, UC8151_LUT_VCOM); //vcom - RawWriteData(pBBEI, (uint8_t *)lut_vcom_dc, 44); - - obdWriteCommand(pBBEI, UC8151_LUT_WW); - RawWriteData(pBBEI, (uint8_t *)lut_ww, 42); - - obdWriteCommand(pBBEI, UC8151_LUT_BW); - RawWriteData(pBBEI, (uint8_t *)lut_bw, 42); - - obdWriteCommand(pBBEI, UC8151_LUT_WB); - RawWriteData(pBBEI, (uint8_t *)lut_bb, 42); - - obdWriteCommand(pBBEI, UC8151_LUT_BB); - RawWriteData(pBBEI, (uint8_t *)lut_wb, 42); - } -} - -// -// Wake up the EPD controller -// -static void EPDWakeUp(OBDISP *pBBEI, int bResetOnly) -{ - if (pBBEI->iRSTPin != 0xff) { - for (int i=0; i<1; i++) { - digitalWrite(pBBEI->iRSTPin, LOW); - _delay(10); - digitalWrite(pBBEI->iRSTPin, HIGH); - _delay(10); - } - } - if (bResetOnly) return; - - if (pBBEI->type == EPD164_BWYR_168x168 || pBBEI->type == EPD236_BWYR_168x296) return; // these don't like this cmd at this time - if (pBBEI->chip_type == BBEI_CHIP_UC8151) { - obdWriteCommand(pBBEI, UC8151_PON); - } else { - obdWriteCommand(pBBEI, SSD1608_SW_RESET); // soft reset - } - EPDWaitBusy(pBBEI, 1); -} /* EPDWakeUp() */ - -static void EPDSleep(OBDISP *pBBEI, int bDeep) -{ - - if (pBBEI->chip_type == BBEI_CHIP_SSD16xx) { - if (bDeep) - EPD_CMD2(pBBEI, 0x10, 0x02); // deep sleep mode 2 (loses RAM, needs reset) - else // no reset pin, put it into light sleep - EPD_CMD2(pBBEI, 0x10, 0x00); - return; - } else { // UC81xx - EPD_CMD2(pBBEI, UC8151_CDI, 0x17); // border floating - obdWriteCommand(pBBEI, UC8151_POFF); // power off - EPDWaitBusy(pBBEI, 1); - if (bDeep) { - EPD_CMD2(pBBEI, UC8151_DSLP, 0xa5); // deep sleep - } - } -} /* EPDSleep() */ -// -// Set the boundaries of the partial update area -// start/end x/y -// -void EPDSetPartialArea(OBDISP *pBBEI, int sx, int sy, int ex, int ey) -{ -uint8_t ucTemp[12]; - - ucTemp[0] = 0x40; // data operation - sx &= 0xFFF8; // byte boundary - ex = (ex - 1) | 0x7; // byte boundary - 1 - obdWriteCommand(pBBEI, 0x90); // partial window - ucTemp[1] = (uint8_t)(sx / 256); - ucTemp[2] = (uint8_t)(sx & 256); - ucTemp[3] = (uint8_t)(ex / 256); - ucTemp[4] = (uint8_t)(ex & 256); - ucTemp[5] = (uint8_t)(sy / 256); - ucTemp[6] = (uint8_t)(sy & 256); - ucTemp[7] = (uint8_t)(ey / 256); - ucTemp[8] = (uint8_t)(ey & 256); - ucTemp[9] = 0x01; // gates scan inside and outside the partial area (don't see any difference) - RawWrite(pBBEI, ucTemp, 10); - _delay(2); - //IO.writeDataTransaction(0x00); // don't see any difference - //return (7 + xe - x) / 8; // number of bytes to transfer -} /* EPDSetPartialArea() */ - -void EPD154_Finish(OBDISP *pBBEI, int bPartial) -{ - if (bPartial) { - EPD_CMD2(pBBEI, SSD1608_DISP_CTRL2, 0xcf); - } else { - EPD_CMD2(pBBEI, SSD1608_DISP_CTRL2, 0xc7); - } - obdWriteCommand(pBBEI, SSD1608_MASTER_ACTIVATE); - EPDWaitBusy(pBBEI, 0); -} /* EPD154_Finish() */ - -void EPD213_Begin(OBDISP *pBBEI, int x, int y, int w, int h, int bPartial) -{ -uint8_t ucLine[8]; - - // set luts - obdWriteCommand(pBBEI, SSD1608_WRITE_LUT); -// if (pBBEI->type == EPD213_104x212) { -// RawWriteData(pBBEI, (uint8_t *)epd213_lut, sizeof(epd213_lut)); -// } else { // 122x250 - if (bPartial) { -// obdWriteCommand(pBBEI, SSD1608_WRITE_LUT); - memcpy_P(u8Cache, epd213_lut2_partial, sizeof(epd213_lut2_partial)); - RawWriteData(pBBEI, u8Cache, sizeof(epd213_lut2_partial)); - } else { - memcpy_P(u8Cache, epd213_lut2_full, sizeof(epd213_lut2_full)); - RawWriteData(pBBEI, u8Cache, sizeof(epd213_lut2_full)); - } - - obdWriteCommand(pBBEI, 0x44); // set RAM X start/end - if (pBBEI->iOrientation == 0 || pBBEI->iOrientation == 180) { - ucLine[0] = x/8; - ucLine[1] = (x+w-1)/8; - } else { // 90/270 - ucLine[0] = y/8; - ucLine[1] = (y+h-1)/8; - } - RawWriteData(pBBEI, ucLine, 2); - obdWriteCommand(pBBEI, 0x45); // set RAM Y start/end - if (pBBEI->iOrientation == 0 || pBBEI->iOrientation == 180) { - ucLine[0] = (uint8_t)y; - ucLine[1] = (uint8_t)(y>>8); - ucLine[2] = (uint8_t)(y+h-1); - ucLine[3] = (uint8_t)((y+h-1)>>8); - } else { // 90/270 - ucLine[0] = (uint8_t)x; - ucLine[1] = (uint8_t)(x>>8); - ucLine[2] = (uint8_t)(x+w-1); - ucLine[3] = (uint8_t)((x+w-1)>>8); - } - RawWriteData(pBBEI, ucLine, 4); - EPD_CMD2(pBBEI, 0x4e, 0x00); // set RAM X pointer start - ucLine[0] = 0x00; - ucLine[1] = 0x00; - obdWriteCommand(pBBEI, 0x4f); // set RAM Y pointer start - RawWriteData(pBBEI, ucLine, 2); - if (bPartial) { - EPD_CMD2(pBBEI, SSD1608_WRITE_VCOM, 0x26); // VCOM - EPDWaitBusy(pBBEI, 1); - obdWriteCommand(pBBEI, 0x37); // ? - memset(ucLine, 0, 7); - ucLine[4] = 0x40; - RawWriteData(pBBEI, ucLine, 7); - - EPD_CMD2(pBBEI, 0x22, 0xc0); - obdWriteCommand(pBBEI, 0x20); - EPDWaitBusy(pBBEI, 1); - EPD_CMD2(pBBEI, 0x3c, 0x01); // border waveform - } else { - EPD_CMD2(pBBEI, SSD1608_WRITE_VCOM, 0x55); // VCOM - EPDWaitBusy(pBBEI, 1); - } -} /* EPD213_Begin() */ - -// -// Special case for some e-ink displays -// -static int EPDDumpPartial(OBDISP *pBBEI, uint8_t *pBuffer, int x, int y, int w, int h) -{ - long lTime; - - if (pBuffer == NULL && pBBEI->ucScreen == NULL) - return BBEI_ERROR_NO_MEMORY; - - EPDWakeUp(pBBEI, 1); // tickle the reset line - - if (pBBEI->pInitPart) { // command tables ready to go - lTime = millis(); - EPDSendCMDSequence(pBBEI, pBBEI->pInitPart); - if (pBBEI->chip_type == BBEI_CHIP_UC8151) { - int iSize = pBBEI->width * ((pBBEI->height+7)>>3); - EPDWriteImage(pBBEI, 0x10, &pBBEI->ucScreen[iSize], 0,0,pBBEI->width, pBBEI->height, pBBEI->iFlags & BBEI_FAST_INVERTED); // write old data inverted - EPDWriteImage(pBBEI, 0x13, NULL, 0, 0, pBBEI->width, pBBEI->height, pBBEI->iFlags & BBEI_FAST_INVERTED); - memcpy(&pBBEI->ucScreen[iSize], pBBEI->ucScreen, iSize); // new becomes old - lTime = millis() - lTime; - pBBEI->iDataTime = (int)lTime; - lTime = millis(); - obdWriteCommand(pBBEI, UC8151_DRF); - _delay(1); // wait before checking busy line (necessary!) - EPDWaitBusy(pBBEI, 1); - obdWriteCommand(pBBEI, UC8151_POFF); - lTime = millis() - lTime; - pBBEI->iOpTime = (int)lTime; - return 0; - } else { // SSD16xx - EPDWriteImage(pBBEI, 0x24, NULL, 0, 0, pBBEI->width, pBBEI->height, pBBEI->iFlags & BBEI_FAST_INVERTED); - EPD_CMD2(pBBEI, 0x22, 0xff); - lTime = millis() - lTime; - pBBEI->iDataTime = (int)lTime; - lTime = millis(); - obdWriteCommand(pBBEI, 0x20); // refresh - EPDWaitBusy(pBBEI, 0); - lTime = millis() - lTime; - pBBEI->iOpTime = (int)lTime; - return 0; - //EPD_CMD2(pBBEI, 0x10, 0x1); // deep sleep is needed (very strange behavior if it is not issued here) - } // SSD16xx - } // has a command list for fast updates - if (pBBEI->type == EPD29R_128x296 || pBBEI->type == EPD154R_152x152 || pBBEI->type == EPD42R_400x300) { - if (pBBEI->iFlags & BBEI_FULLUPDATE) { - // Kludge alert - fool it into going into B&W mode to allow partial updates. Need to combine the black+red pixels and send them to the ALTRAM first - uint8_t *s = pBBEI->ucScreen; - int iOffset = ((pBBEI->native_width+7)/8) * pBBEI->native_height; // full buffer size - for (int i=0; iwidth, pBBEI->height, false); -#ifndef WIMPY_MCU - EPDWriteImage(pBBEI, SSD1608_WRITE_RAM, NULL, 0, 0, pBBEI->width, pBBEI->height, 0); - EPDWriteImage(pBBEI, SSD1608_WRITE_ALTRAM, NULL, 0, 0, pBBEI->width, pBBEI->height, 0); // send image to current and previous buffers -#endif - return BBEI_SUCCESS; - } - EPD213_Begin(pBBEI, x, y, w, h, true); -#ifndef WIMPY_MCU - EPDWriteImage(pBBEI, SSD1608_WRITE_RAM, NULL, x, y, w, h, 0); -#endif - EPD154_Finish(pBBEI, true); - } - if (pBBEI->type == EPD213_104x212 || pBBEI->type == EPD213_122x250) { - EPD213_Begin(pBBEI, x, y, w, h, true); -#ifndef WIMPY_MCU - EPDWriteImage(pBBEI, SSD1608_WRITE_RAM, NULL, x, y, w, h, 0); -#endif - EPD154_Finish(pBBEI, true); - } else if (pBBEI->type == EPD154_152x152) { - EPDSendCMDSequence(pBBEI, pBBEI->pInitFast); -#ifndef WIMPY_MCU - if (pBBEI->ucScreen) { - EPDWriteImage(pBBEI, UC8151_DTM2, NULL, 0, 0, pBBEI->width, pBBEI->height, 1); - } -#endif - obdWriteCommand(pBBEI, UC8151_DRF); - EPDWaitBusy(pBBEI, 1); - } - EPDSleep(pBBEI, 0); - return BBEI_SUCCESS; -} /* EPDDumpFast() */ - -void EPD_CMD2(OBDISP *pBBEI, uint8_t cmd, uint8_t param) -{ - obdWriteCommand(pBBEI, cmd); - RawWriteData(pBBEI, ¶m, 1); -} /* EPD_CMD2() */ - -void EPD8951_REG(OBDISP *pBBEI, uint16_t addr, uint16_t data) -{ -uint8_t ucTemp[4]; - - EPDWaitBusy(pBBEI, 1); - ucTemp[0] = 0x60; // cmd intro 6000h - ucTemp[1] = 0x00; - ucTemp[2] = 0x00; // reg write 0011h - ucTemp[3] = 0x11; - RawWriteData(pBBEI, ucTemp, 4); - EPDWaitBusy(pBBEI, 1); - ucTemp[0] = ucTemp[1] = 0; // 0000 data intro - ucTemp[2] = (uint8_t)(addr >> 8); - ucTemp[3] = (uint8_t)(addr & 0xff); - RawWriteData(pBBEI, ucTemp, 4); - ucTemp[2] = (uint8_t)(data >> 8); - ucTemp[3] = (uint8_t)(data & 0xff); - RawWriteData(pBBEI, ucTemp, 4); - -} /* EPD8951_REG() */ - -void EPD8951_ARGS(OBDISP *pBBEI, uint16_t u16CMD, uint16_t *pArgs, int iCount) -{ - uint8_t ucTemp[4]; - EPDWaitBusy(pBBEI, 1); - ucTemp[0] = 0x60; - ucTemp[1] = 0x00; // 6000 cmd - ucTemp[2] = (uint8_t)(u16CMD >> 8); - ucTemp[3] = (uint8_t)(u16CMD & 0xff); - RawWriteData(pBBEI, ucTemp, 4); - - for (int i=0; i> 8); - ucTemp[3] = (uint8_t)(pArgs[i] & 0xff); - RawWriteData(pBBEI, ucTemp, 4); - } -} /* EPD8951_ARGS() */ - -void EPD8951_CMD(OBDISP *pBBEI, int iCMD) -{ -uint8_t ucTemp[4]; - - EPDWaitBusy(pBBEI, 1); - ucTemp[0] = 0x60; - ucTemp[1] = 0x00; // cmd prefix = 6000h - ucTemp[2] = (uint8_t)(iCMD >> 8); - ucTemp[3] = (uint8_t)(iCMD & 0xff); - RawWriteData(pBBEI, ucTemp, 4); -} /* EPD8951_CMD() */ - -void EPD8951_WORD(OBDISP *pBBEI, uint16_t u16Word) -{ -uint8_t ucTemp[4]; - - EPDWaitBusy(pBBEI, 1); - ucTemp[0] = 0x00; - ucTemp[1] = 0x00; // cmd prefix = 6000h - ucTemp[2] = (uint8_t)(u16Word >> 8); - ucTemp[3] = (uint8_t)(u16Word & 0xff); - RawWriteData(pBBEI, ucTemp, 4); -} /* EPD8951_WORD() */ - -void EPDFill(OBDISP *pBBEI, uint8_t ucCMD, uint8_t ucPattern) -{ -int iSize; - - if (ucCMD) { - obdWriteCommand(pBBEI, ucCMD); // start write - } - if (pBBEI->type == EPD579_792x272) { - iSize = 400*(272>>3); -#ifndef __AVR__ - } else if (pBBEI->type == EPD74R_640x384) { - iSize = (pBBEI->native_width * pBBEI->native_height)/2; // 4bpp -#endif // __AVR__ - } else { - iSize = ((pBBEI->native_width+7)>>3) * pBBEI->native_height; - } - for (int i=0; itype == EPD47_540x960) - RawWriteData(pBBEI, u8Cache, 2 + ((w+7)/8)); - else - RawWriteData(pBBEI, u8Cache+2, (w+7)/8); + bbeiWriteData(pBBEI, u8Cache+2, (w+7)/8); } // for ty } else if (pBBEI->iOrientation == 0) { for (ty=y; tytype == EPD47_540x960) - RawWriteData(pBBEI, u8Cache, 2+((w+7)/8)); - else - RawWriteData(pBBEI, u8Cache+2, (w+7)/8); + bbeiWriteData(pBBEI, u8Cache+2, (w+7)/8); } // for ty } else if (pBBEI->iOrientation == 90) { for (tx=x; txtype == EPD47_540x960) - RawWriteData(pBBEI, u8Cache, 2+((h+7)/8)); - else - RawWriteData(pBBEI, u8Cache+2, (h+7)/8); + bbeiWriteData(pBBEI, u8Cache+2, (h+7)/8); } // for tx } else if (pBBEI->iOrientation == 270) { for (tx=x+w-1; tx>=x; tx--) { @@ -3258,1094 +1170,46 @@ int iPitch; ucSrcMask = 1; } // for ty *d++ = (uc ^ ucInvert); // store final partial byte - if (pBBEI->type == EPD47_540x960) - RawWriteData(pBBEI, u8Cache, 2+((h+7)/8)); - else - RawWriteData(pBBEI, u8Cache+2, (h+7)/8); + bbeiWriteData(pBBEI, u8Cache+2, (h+7)/8); } // for x } // 270 -} /* EPDWriteImage() */ +} /* bbeiWriteImage() */ // -// Write EPD Image data to a 4-color (black/white/yellow/red) e-paper +// Write the local copy of the memory plane(s) to the eink's internal framebuffer // -static void EPDWriteImage2bpp(OBDISP *pBBEI, uint8_t ucCMD, int x, int y, int w, int h) +int bbeiWritePlane(BBEIDISP *pBBEI, int iPlane) { -uint8_t ucLine[324]; // send the data one line at a time -int tx, ty, iRedOff; -uint8_t *s, *d, ucSrcMask, uc; -uint8_t *pBuffer; - - pBuffer = pBBEI->ucScreen; - iRedOff = pBBEI->width * ((pBBEI->height+7)/8); - if (ucCMD) { - obdWriteCommand(pBBEI, ucCMD); // start write - } - // Convert the bit direction and write the data to the EPD - if (pBBEI->iOrientation == 180) { - for (ty=y+h-1; ty>=y; ty--) { - ucSrcMask = 1 << (ty & 7); - d = ucLine; - s = &pBuffer[(ty>>3) * pBBEI->width]; - for (tx=x+w-4; tx>=x; tx-=4) { - uc = 0; - for (int pix=3; pix>=0; pix--) { - uc <<= 2; // shift down 1 pixel - if (!(s[tx+pix] & ucSrcMask)) // inverted plane 0 - uc |= 0x01; - if (s[tx+pix+iRedOff] & ucSrcMask) // plane 1 - uc |= 0x02; - } - *d++ = uc; // store 4 pixels - } // for tx - RawWriteData(pBBEI, ucLine, w/4); - } // for ty - } else if (pBBEI->iOrientation == 0) { - for (ty=y; ty> 3) * pBBEI->width]; - for (tx=x; txiOrientation == 90) { - for (tx=x; tx=y; ty-=4) { - s = &pBuffer[tx + (ty>>3) * pBBEI->width]; - uc = 0; - for (int pix=3; pix>=0; pix--) { - uc <<= 2; // shift down 1 pixel - if (!(s[tx] & ucSrcMask)) // inverted plane 0 - uc |= 0x01; - if (s[tx+iRedOff] & ucSrcMask) // plane 1 - uc |= 0x02; - ucSrcMask >>= 1; - if (ucSrcMask == 0) ucSrcMask = 0x80; - } - *d++ = uc; // store 4 pixels - } // for ty - RawWriteData(pBBEI, ucLine, h/4); - } // for tx - } else if (pBBEI->iOrientation == 270) { - for (tx=x+w-1; tx>=x; tx--) { - d = ucLine; - ucSrcMask = 1 << (y & 7); - for (ty=y; ty>3) * pBBEI->width]; - uc = 0; - for (int pix=0; pix<4; pix++) { - uc <<= 2; - if (!(s[tx] & ucSrcMask)) // inverted plane 0 - uc |= 0x01; - if (s[tx+iRedOff] & ucSrcMask) // plane 1 - uc |= 0x02; - ucSrcMask <<= 1; - if (ucSrcMask == 0) ucSrcMask = 1; - } // for pix - *d++ = uc; // store 2 pixels - } // for ty - RawWriteData(pBBEI, ucLine, h/4); - } // for x - } // 270 -} /* EPDWriteImage2bpp() */ -// -// Write EPD Image data -// -static void EPDWriteImage4bpp(OBDISP *pBBEI, uint8_t ucCMD, int x, int y, int w, int h) -{ -uint8_t ucLine[324]; // send the data one line at a time -int tx, ty, iRedOff; -uint8_t *s, *d, ucSrcMask, uc; -uint8_t *pBuffer; + uint8_t ucCMD1, ucCMD2; + int iOffset; - pBuffer = pBBEI->ucScreen; - iRedOff = pBBEI->width * ((pBBEI->height+7)/8); - if (ucCMD) { - obdWriteCommand(pBBEI, ucCMD); // start write - } - // Convert the bit direction and write the data to the EPD - // This particular controller has 4 bits per pixel where 0=black, 3=white, 4=red - // this wastes 50% of the time transmitting bloated info (only need 2 bits) - if (pBBEI->iOrientation == 180) { - for (ty=y+h-1; ty>=y; ty--) { - ucSrcMask = 1 << (ty & 7); - d = ucLine; - s = &pBuffer[(ty>>3) * pBBEI->width]; - for (tx=x+w-1; tx>=x; tx-=2) { - uc = 0x33; // start with white - if (s[tx] & ucSrcMask) // black - uc = 0x03; - if (s[tx+iRedOff] & ucSrcMask) // red - uc = 0x43; - if (s[tx-1] & ucSrcMask) {// src pixel = black - uc &= 0xf0; - } - if (s[tx-1+iRedOff] & ucSrcMask) { // red - uc &= 0xf0; uc |= 0x4; - } - *d++ = uc; // store 2 pixels - } // for tx - RawWriteData(pBBEI, ucLine, w/2); - } // for ty - } else if (pBBEI->iOrientation == 0) { - for (ty=y; ty> 3) * pBBEI->width]; - for (tx=x; txiOrientation == 90) { - for (tx=x; tx=y; ty-=2) { - s = &pBuffer[tx + (ty>>3) * pBBEI->width]; - uc = 0x33; - if (s[0] & ucSrcMask) - uc = 0x03; // black - if (s[iRedOff] & ucSrcMask) - uc = 0x43; // red - ucSrcMask >>= 1; - if (s[0] & ucSrcMask) {// src pixel = black - uc &= 0xf0; - } - if (s[iRedOff] & ucSrcMask) { // red - uc &= 0xf0; uc |= 0x4; - } - ucSrcMask >>= 1; - *d++ = uc; // store 2 pixels - if (ucSrcMask == 0) - ucSrcMask = 0x80; // bottom up - } // for ty - RawWriteData(pBBEI, ucLine, h/2); - } // for tx - } else if (pBBEI->iOrientation == 270) { - for (tx=x+w-1; tx>=x; tx--) { - d = ucLine; - ucSrcMask = 1 << (y & 7); - for (ty=y; ty>3) * pBBEI->width]; - uc = 0x33; // start with white - if (s[0] & ucSrcMask) - uc = 0x03; // black - if (s[iRedOff] & ucSrcMask) - uc = 0x43; // red - ucSrcMask <<= 1; - if (s[0] & ucSrcMask) {// src pixel = black - uc &= 0xf0; - } - if (s[iRedOff] & ucSrcMask) { // red - uc &= 0xf0; uc |= 0x4; - } - ucSrcMask <<= 1; - *d++ = uc; // store 2 pixels - if (ucSrcMask == 0) - ucSrcMask = 1; - } // for ty - RawWriteData(pBBEI, ucLine, h/2); - } // for x - } // 270 -} /* EPDWriteImage4bpp() */ -#endif // !WIMPY_MCU -const uint8_t epd294_init_sequence_part[] = -{ - 0x0b, 0x37, 0,0,0,0,0,0x40,0,0,0,0, - 0x04, 0x01, 0x27, 0x01, 0x00, - 0x02, 0x11, 0x01, - 0x03, 0x44, 0x00, 0x0f, - 0x05, 0x45, 0x00, 0x00, 0x27, 0x01, - 0x03, 0x21, 0x00, 0x80, - 0x02, 0x3c, 0xc0, - 0x02, 0x22, 0xc0, - 0x01, 0x20, - BUSY_WAIT, - 0x02, 0x4e, 0x00, - 0x03, 0x4f, 0x00, 0x00, - 0x00 // end of sequence -}; /* epd294_init_sequence_part */ - -const uint8_t epd293_lut[] = -{ - 0x22, 0x11, 0x10, 0x00, 0x10, 0x00, 0x00, 0x11, 0x88, 0x80, 0x80, 0x80, 0x00, 0x00, 0x6A, 0x9B, - 0x9B, 0x9B, 0x9B, 0x00, 0x00, 0x6A, 0x9B, 0x9B, 0x9B, 0x9B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x04, 0x18, 0x04, 0x16, 0x01, 0x0A, 0x0A, 0x0A, 0x0A, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x08, 0x3C, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -void EPD47_Begin(OBDISP *pBBEI, int x, int y, int w, int h, int bPartial) -{ - (void)x; - (void)y; - (void)w; - (void)h; - EPD8951_CMD(pBBEI, 0x0001); // IT8951_TCON_SYS_RUN - EPD8951_REG(pBBEI, 0x0004, 0x0001); // enable pack write - // set vcom to -2.3v - EPD8951_CMD(pBBEI, 0x0039); - EPD8951_WORD(pBBEI, 0x0001); - EPD8951_WORD(pBBEI, 2300); - if (bPartial) { - - } else { - uint16_t u16Args[5]; - // set target memory area - EPD8951_REG(pBBEI, 0x000a, 0x0012); // _dev_memaddr_h - EPD8951_REG(pBBEI, 0x0008, 0x36e0); // _dev_memaddr_l - // set image area - u16Args[0] = 0; // little endian, 2bpp, no rotation - u16Args[1] = 0; // start x - u16Args[2] = 0; // start y - u16Args[3] = pBBEI->native_width; - u16Args[4] = pBBEI->native_height; - EPD8951_ARGS(pBBEI, 0x0021, u16Args, 5); // IT8951_TCON_LD_IMG_AREA - EPD8951_CMD(pBBEI, 0x20); // IT8951_TCON_LD_IMG - EPD8951_WORD(pBBEI, 0); // default settings (endian, format, rotation) - } -} /* EPD47_Begin() */ -// -// Special case for e-ink displays -// -static int EPDDumpBuffer(OBDISP *pBBEI, int bRefresh, int bWait, int bFast) -{ - long lTime; - - if (!(pBBEI->iFlags & BBEI_HAS_FAST_UPDATE)) - bFast = 0; // no fast update available - - pBBEI->iFlags |= BBEI_FULLUPDATE; // mark it for later - // UC81xx need to be told the old and new images for fast updates - // Keep a copy of the last full update for this purpose - if (pBBEI->type >= EPD42_400x300 && pBBEI->chip_type == BBEI_CHIP_UC8151 && pBBEI->ucScreen && !(pBBEI->iFlags & BBEI_3COLOR)) { - int iSize = pBBEI->width * ((pBBEI->height+7)>>3); - memcpy(&pBBEI->ucScreen[iSize], pBBEI->ucScreen, iSize); - } - EPDWakeUp(pBBEI, 0); // tickle the reset line - if (pBBEI->pInitFull) { // if we have the command list, use common code - int iSize = pBBEI->width * ((pBBEI->height+7)>>3); - int bInvert = 0; - uint8_t *pBuf1 = NULL, *pBuf2 = NULL; - - lTime = millis(); // get data transmission time - if (bFast && pBBEI->pInitFast) { - Serial.println("Sending fast init"); - EPDSendCMDSequence(pBBEI, pBBEI->pInitFast); - } else { - EPDSendCMDSequence(pBBEI, pBBEI->pInitFull); - } - if (pBBEI->chip_type == BBEI_CHIP_UC8151) { - if (pBBEI->ucScreen) { // we have a back buffer, send it - if (pBBEI->iFlags & BBEI_3COLOR) { // B/W/R - bInvert = 1; // invert the red plane - pBuf1 = pBBEI->ucScreen; // B/W plane - pBuf2 = &pBBEI->ucScreen[iSize]; // R plane - } - EPDWriteImage(pBBEI, UC8151_DTM1, pBuf1, 0, 0, pBBEI->width, pBBEI->height, 0); // B/W or 'old' plane - EPDWriteImage(pBBEI, UC8151_DTM2, pBuf2, 0, 0, pBBEI->width, pBBEI->height, bInvert); // R or 'new' plane - } - lTime = millis() - lTime; - pBBEI->iDataTime = (int)lTime; // record the time to xfer data - if (bRefresh) { - lTime = millis(); - obdWriteCommand(pBBEI, UC8151_DRF); - EPDWaitBusy(pBBEI, 0); - lTime = millis() - lTime; - pBBEI->iOpTime = (int)lTime; // record the update time - } - } else { // SSD16xx controllers - if (pBBEI->ucScreen) { // we have a back buffer, send it - uint8_t ucTemp[4]; - if (pBBEI->iFlags & BBEI_3COLOR) { // B/W/R - pBuf1 = pBBEI->ucScreen; // B/W plane - pBuf2 = &pBBEI->ucScreen[iSize]; // R plane - bInvert = 1; // invert the R plane - } - EPDWriteImage(pBBEI, SSD1608_WRITE_ALTRAM, pBuf2, 0, 0, pBBEI->width, pBBEI->height, bInvert); - // need to reset the write pointer between planes - EPD_CMD2(pBBEI, SSD1608_SET_RAMXCOUNT, 0x00); - ucTemp[0] = ucTemp[1] = 0; - obdWriteCommand(pBBEI, SSD1608_SET_RAMYCOUNT); - RawWriteData(pBBEI, ucTemp, 2); - EPDWriteImage(pBBEI, SSD1608_WRITE_RAM, pBuf1, 0, 0, pBBEI->width, pBBEI->height, 0); - } - lTime = millis() - lTime; // record data xfer time - pBBEI->iDataTime = (int)lTime; - if (bRefresh) { - lTime = millis(); - EPD_CMD2(pBBEI, SSD1608_DISP_CTRL2, (bFast || (pBBEI->iFlags & BBEI_3COLOR)) ? 0xc7 : 0xf7); - obdWriteCommand(pBBEI, SSD1608_MASTER_ACTIVATE); // refresh - EPDWaitBusy(pBBEI, 0); - lTime = millis() - lTime; // record the update time - pBBEI->iOpTime = (int)lTime; - } - } // UC8151 / SSD16xx - return 0; - } // has init sequence - // Special case EPDs below (4-gray, 4-color or 3-color/4-bpp) -#if !defined( WIMPY_MCU ) && !defined(__AVR__) - // not enough RAM on AVR to use this type of EPD - if (pBBEI->type == EPD42_4GRAY_400x300) { - EPDSendCMDSequence(pBBEI, pBBEI->pInitFull); - if (pBBEI->ucScreen) { - EPDWriteImage(pBBEI, 0x10, NULL, 0, 0, pBBEI->width, pBBEI->height, 1); // MSB plane - EPDWriteImage(pBBEI, 0x13, NULL, 0, 0, pBBEI->width, pBBEI->height, 0); // LSB plane - } - obdWriteCommand(pBBEI, UC8151_LUT_VCOM); - RawWriteData(pBBEI, (uint8_t *)lut42_4gray_vcom, 42); - obdWriteCommand(pBBEI, UC8151_LUT_WW); - RawWriteData(pBBEI, (uint8_t *)lut42_4gray_ww, 42); - obdWriteCommand(pBBEI, UC8151_LUT_BW); - RawWriteData(pBBEI, (uint8_t *)lut42_4gray_bw, 42); - obdWriteCommand(pBBEI, UC8151_LUT_WB); - RawWriteData(pBBEI, (uint8_t *)lut42_4gray_wb, 42); - obdWriteCommand(pBBEI, UC8151_LUT_BB); - RawWriteData(pBBEI, (uint8_t *)lut42_4gray_bb, 42); - obdWriteCommand(pBBEI, UC8151_LUT_VCOM2); - RawWriteData(pBBEI, (uint8_t *)lut42_4gray_vcom, 42); - if (bRefresh) - obdWriteCommand(pBBEI, UC8151_DRF); // refresh display - else return 0; - } - if (pBBEI->type == EPD74R_640x384) { - if (pBBEI->native_width == 640) { - EPDSendCMDSequence(pBBEI, epd74r_init_sequence_full); - } else { - EPDSendCMDSequence(pBBEI, epd583r_init_sequence_full); - } - if (pBBEI->ucScreen) { - EPDWriteImage4bpp(pBBEI, 0x10, 0, 0, pBBEI->width, pBBEI->height); - } - if (bRefresh) { - obdWriteCommand(pBBEI, 0x12); // master activation - } - if (bWait) { - EPDWaitBusy(pBBEI, 0); - } - return BBEI_SUCCESS; - } -#endif // !WIMPY_MCU - if (pBBEI->type == EPD30_BWYR_168x400 || pBBEI->type == EPD164_BWYR_168x168 || pBBEI->type == EPD236_BWYR_168x296) { - EPDSendCMDSequence(pBBEI, pBBEI->pInitFull); - if (pBBEI->ucScreen) { - if (pBBEI->type == EPD164_BWYR_168x168 || pBBEI->type == EPD236_BWYR_168x296) { - EPD_CMD2(pBBEI, 0x68, 0x01); - obdWriteCommand(pBBEI, 0x04); - EPDWaitBusy(pBBEI, 0); - } - EPDWriteImage2bpp(pBBEI, 0x10, 0, 0, pBBEI->width, pBBEI->height); - } - if (pBBEI->type == EPD164_BWYR_168x168 || pBBEI->type == EPD236_BWYR_168x296) { - EPD_CMD2(pBBEI, 0x68, 0x00); - } - EPD_CMD2(pBBEI, 0x12, 0x01); // display refresh - } - if (pBBEI->type == EPD266_BWYR_184x360 || pBBEI->type == EPD29_BWYR_168x384 || pBBEI->type == EPD29_BWYR_128x296) { - EPDSendCMDSequence(pBBEI, pBBEI->pInitFull); - if (pBBEI->ucScreen) { - EPDWriteImage2bpp(pBBEI, 0x10, 0, 0, pBBEI->width, pBBEI->height); - } - EPD_CMD2(pBBEI, 0x12, 0x00); // display refresh - } - if (pBBEI->type == EPD42R2_400x300) { - EPDSendCMDSequence(pBBEI, pBBEI->pInitFull); - // EPD27_SetLut(pBBEI, false); -#ifndef WIMPY_MCU - if (pBBEI->ucScreen) { - EPDWriteImage(pBBEI, 0x10, NULL, 0, 0, pBBEI->width, pBBEI->height, 0); // black/white plane - EPDWriteImage(pBBEI, 0x13, NULL, 0, 0, pBBEI->width, pBBEI->height, 1); // red plane - } -#endif // !WIMPY_MCU - if (bRefresh) { - obdWriteCommand(pBBEI, 0x12); // master activation - } - } - if (pBBEI->type == EPD213R_104x212_d) { - EPDSendCMDSequence(pBBEI, EPD213R_104x212_d_init_sequence_full); - EPD27_SetLut(pBBEI, false); -#ifndef WIMPY_MCU - if (pBBEI->ucScreen) { - EPDWriteImage(pBBEI, 0x10, NULL, 0, 0, pBBEI->width, pBBEI->height, 0); // black/white plane - EPDWriteImage(pBBEI, 0x13, NULL, 0, 0, pBBEI->width, pBBEI->height, 1); // red plane - } -#endif // !WIMPY_MCU - if (bRefresh) { - obdWriteCommand(pBBEI, 0x12); // master activation - } - } - if (pBBEI->type == EPD579_792x272) { - EPD_CMD2(pBBEI, SSD1608_DISP_CTRL2, 0xf7); - obdWriteCommand(pBBEI, SSD1608_MASTER_ACTIVATE); - } - if (pBBEI->type == EPD47_540x960) { - uint16_t u16Args[7]; - EPD47_Begin(pBBEI, 0, 0, pBBEI->width, pBBEI->height, false); -#ifndef WIMPY_MCU - if (pBBEI->ucScreen) { - EPDWriteImage(pBBEI, 0, NULL, 0, 0, pBBEI->width, pBBEI->height, 0); - } -#endif - EPD8951_CMD(pBBEI, 0x0022); // LCD_IMG_END - u16Args[0] = 0; // x - u16Args[1] = 0; // y - u16Args[2] = pBBEI->native_width; - u16Args[3] = pBBEI->native_height; - u16Args[4] = 0; // full update mode (2 seconds) - u16Args[5] = 0x36e0; // _dev_memaddr_l - u16Args[6] = 0x0012; // _dev_memaddr_h - EPD8951_ARGS(pBBEI, 0x0037, u16Args, 7); // IT8951_I80_CMD_DPY_BUF_AREA - } - if (bWait) { - EPDWaitBusy(pBBEI, 0); - EPDSleep(pBBEI, 0); - } - return BBEI_SUCCESS; -} /* EPDDumpBuffer() */ -// -// Special case for ST7302 -// -#ifndef WIMPY_MCU -void obdST7302SetPos(OBDISP *pBBEI, int x, int y) -{ -uint8_t ucTemp[4]; - - obdWriteCommand(pBBEI, 0x2a); // Column set - ucTemp[0] = 0x19 + (y/12); // start x (we treat it as y) - ucTemp[1] = 0x27; // end x - RawWriteData(pBBEI, ucTemp, 2); - obdWriteCommand(pBBEI, 0x2b); // Row set - ucTemp[0] = (x/2); // start y (we treat it as x) - ucTemp[1] = 0x80; // end y - RawWriteData(pBBEI, ucTemp, 2); - obdWriteCommand(pBBEI, 0x2c); // memory write -} /* obdST7302SetPos() */ -// -// Special case for ST7302 -// -static int ST7302DumpBuffer(OBDISP *pBBEI, uint8_t *pBuffer) -{ -uint8_t ucPixels[40]; -int x, y, iPitch, count; -uint8_t ucMask, uc1, *s, *d; - - iPitch = pBBEI->width; -// Shift out the image in pairs of lines - ucPixels[0] = 0x40; - switch (pBBEI->iOrientation) { - case 0: - case 180: - for (x = 0; x < pBBEI->width; x += 2) { // a pair of columns at a time - d = ucPixels+1; - ucMask = 1; - uc1 = 0; - count = 0; - if (pBBEI->iOrientation == 180) { - ucMask = 1<<((pBBEI->height-1) & 7); - s = &pBuffer[pBBEI->width-2-x + ((pBBEI->height-1)>>3)*iPitch]; - for (y = pBBEI->height-1; y >= 0; y--) { - uc1 <<= 2; - if (s[0] & ucMask) uc1 |= 1; - if (s[1] & ucMask) uc1 |= 2; - count++; - if (count == 4) { // finish the byte - *d++ = uc1; - count = 0; - uc1 = 0; - } - ucMask >>= 1; - if (ucMask == 0) { - ucMask = 0x80; - s -= iPitch; - } // next line - } // for y - if (count) { - uc1 <<= (count*2); - *d++ = uc1; - } - } else { - s = &pBuffer[x]; - uc1 = 0; - for (y=0; y < pBBEI->height+3; y++) { - uc1 <<= 2; - if (s[0] & ucMask) uc1 |= 2; - if (s[1] & ucMask) uc1 |= 1; - count++; - if (count == 4) { // finish the byte - *d++ = uc1; - count = 0; - uc1 = 0; - } - ucMask <<= 1; - if (ucMask == 0) { - ucMask = 1; - s += iPitch; - } // next line - } // for y - } // flipped - obdSetPosition(pBBEI, (pBBEI->flip) ? 248-x : x, 0, 1); - RawWrite(pBBEI, ucPixels, 3 + (int)(d - ucPixels)); - } // for x - break; - case 90: - case 270: // x/y swapped - if (pBBEI->iOrientation == 270) { - for (y=pBBEI->height-2; y >= 0; y-=2) { - ucMask = 1<<(y & 7); - s = &pBuffer[(y>>3)*pBBEI->width]; - count = 0; - d = ucPixels+1; - uc1 = 0; - for (x=0; xwidth; x++) { - uc1 <<= 2; - if (s[x] & ucMask) uc1 |= 1; - if (s[x] & (ucMask << 1)) uc1 |= 2; - count++; - if (count == 4) { // finish the byte - *d++ = uc1; - count = 0; - } - } // for x - obdSetPosition(pBBEI, pBBEI->height - 2 - y, 0, 1); - RawWrite(pBBEI, ucPixels, 3 + (int)(d - ucPixels)); - } // for y - } else { // 90 - for (y=0; y < pBBEI->height; y+=2) { - ucMask = 1<<(y & 7); - s = &pBuffer[(y>>3)*pBBEI->width]; - count = 0; - d = ucPixels+1; - uc1 = 0; - for (x=pBBEI->width-1; x>=0; x--) { - uc1 <<= 2; - if (s[x] & ucMask) uc1 |= 2; - if (s[x] & (ucMask << 1)) uc1 |= 1; - count++; - if (count == 4) { // finish the byte - *d++ = uc1; - count = 0; - } - } // for x - obdSetPosition(pBBEI, y, 0, 1); - RawWrite(pBBEI, ucPixels, 3 + (int)(d - ucPixels)); - } // for y - } // flipped - break; - } // switch - return BBEI_SUCCESS; -} /* ST7302DumpBuffer() */ -#endif // !WIMPY_MCU -// -// Special case for Sharp Memory LCD -// -static int SharpDumpBuffer(OBDISP *pBBEI, uint8_t *pBuffer) -{ -int x, y; -uint8_t c, ucInvert, ucStart, ucMask; -uint8_t *s, *d; -uint8_t ucLineBuf[64]; -int iPitch = pBBEI->native_width / 8; -static int ucVCOM = 0; -int iBit; - - ucInvert = (pBBEI->invert) ? 0x00 : 0xff; - digitalWrite(pBBEI->iCSPin, HIGH); // active high - ucLineBuf[0] = 0; // Tell RawWrite that it's command (doesn't matter for Sharp LCDs) - ucStart = 0x80; // write command - if (ucVCOM) - ucStart |= 0x40; // VCOM bit - ucLineBuf[1] = ucStart; - // this code assumes I2C, so the first byte is ignored - RawWrite(pBBEI, ucLineBuf, 2); // write command(01) + vcom(02) - ucVCOM = !ucVCOM; // need to toggle this each transaction - - // We need to flip and invert the image in code because the Sharp memory LCD - // controller only has the simplest of commands for data writing - if (pBBEI->iOrientation == 180) - { - for (y=0; yheight; y++) // we have to write the memory in the wrong direction - { - ucMask = 0x80 >> (y & 7); - s = &pBuffer[pBBEI->width - 1 + (pBBEI->width * ((pBBEI->height - 1 - y) >> 3))]; // point to last line first - d = &ucLineBuf[2]; - ucLineBuf[1] = pgm_read_byte(&ucMirror[y+1]); // current line number - for (x=0; xwidth/8; x++) - { - c = ucInvert; // we need to brute-force invert it - for (iBit=7; iBit>=0; iBit--) - { - if (s[0] & ucMask) - c ^= (1 << iBit); - s--; - } - *d++ = c; - } // for y - // write this line to the display - ucLineBuf[iPitch+2] = 0; // end of line - RawWrite(pBBEI, ucLineBuf, iPitch+3); - } // for x - } - else if (pBBEI->iOrientation == 0) // normal orientation - { - for (y=0; yheight; y++) // we have to write the memory in the wrong direction - { - ucMask = 1 << (y & 7); - s = &pBuffer[pBBEI->width * (y >> 3)]; // point to last line first - d = &ucLineBuf[2]; - ucLineBuf[1] = pgm_read_byte(&ucMirror[y+1]); // current line number - for (x=0; xwidth/8; x++) - { - c = ucInvert; - for (iBit=7; iBit>=0; iBit--) - { - if (s[0] & ucMask) - c ^= (1 << iBit); - s++; - } - *d++ = c; - } // for x - // write this line to the display - *d++ = 0; // end of line - RawWrite(pBBEI, ucLineBuf, iPitch+3); - } // for y - } else if (pBBEI->iOrientation == 90) { - for (x=0; xwidth; x++) { - s = &pBuffer[x+((pBBEI->height-1)>>3)*pBBEI->width]; - d = &ucLineBuf[2]; - ucLineBuf[1] = pgm_read_byte(&ucMirror[x+1]); // line number - for (y=0; yheight; y+=8) { // we have to write the memory in the wrong direction - *d++ = s[0] ^ ucInvert; - s -= pBBEI->width; - } // for y - ucLineBuf[iPitch+2] = 0; // end of line - RawWrite(pBBEI, ucLineBuf, iPitch+3); - } // for x - } else { // must be 270 - for (x=0; xwidth; x++) { - d = &ucLineBuf[2]; - s = &pBuffer[(pBBEI->width-1-x)]; - ucLineBuf[1] = pgm_read_byte(&ucMirror[x+1]); // line number - for (y=0; yheight; y+=8) { - *d++ = pgm_read_byte(&ucMirror[s[0] ^ ucInvert]); - s += pBBEI->width; - } // for y - ucLineBuf[iPitch+2] = 0; // end of line - RawWrite(pBBEI, ucLineBuf, iPitch+3); - } // for x - } // 270 - ucLineBuf[1] = 0; - RawWrite(pBBEI, ucLineBuf, 2); // final transfer - digitalWrite(pBBEI->iCSPin, LOW); // de-activate - return BBEI_SUCCESS; -} /* SharpDumpBuffer() */ -// -// Dump a screen's worth of data directly to the display -// Try to speed it up by comparing the new bytes with the existing buffer -// -int obdDumpBuffer(OBDISP *pBBEI, uint8_t *pBuffer, int bRefresh, int bWait, int bFast) -{ -int x, y; -int iLines; - - if (pBBEI->type == LCD_VIRTUAL) { // wrong function for this type of display + if (pBBEI == NULL || iPlane < PLANE_0 || iPlane > PLANE_DUPLICATE) return BBEI_ERROR_BAD_PARAMETER; - } - if (pBuffer == NULL) { // dump the internal buffer if none is given - pBuffer = pBBEI->ucScreen; - } -// if (pBuffer == NULL) { -// return; // no backbuffer and no provided buffer -// } - - if (pBBEI->type >= EPD42_400x300) // all e-ink panels - { - return EPDDumpBuffer(pBBEI, bRefresh, bWait, bFast); - } -#if !defined( WIMPY_MCU ) && !defined(__AVR__) - if (pBBEI->type == LCD_ST7302) // special case for ST7302 - { - return ST7302DumpBuffer(pBBEI, pBuffer); - } -#endif // !WIMPY_MCU - if (pBBEI->chip_type == BBEI_CHIP_SHARP) // special case for Sharp Memory LCDs - { - return SharpDumpBuffer(pBBEI, pBuffer); - } - iLines = (pBBEI->native_height+7) >> 3; - // 0/180 we can send the 8 lines of pixels straight through - if (pBBEI->iOrientation == 0 || pBBEI->iOrientation == 180) { - if (pBBEI->iOrientation == 0 || (pBBEI->iOrientation == 180 && pBBEI->can_flip)) { - for (y=0; ywidth); - pBuffer += pBBEI->width; - } - } else { // have to manually do 180 flipped - for (y=iLines-1; y>=0; y--) { - obdSetPosition(pBBEI, 0, y*8, 1); - for (int x=0; xwidth; x++) { - u8Cache[pBBEI->width-1-x] = ucMirror[pBuffer[x]]; - } - RawWriteData(pBBEI, u8Cache, pBBEI->width); - pBuffer += pBBEI->width; - } - } - return BBEI_SUCCESS; - } else { // must be 90/270 - // Capture the pixels 'sideways' and send a line at a time - if (pBBEI->iOrientation == 90 || (pBBEI->iOrientation == 270 && pBBEI->can_flip)) { - for (x=0; xwidth; x+=8) { - uint8_t *s, *d, uc, ucMask, ucTemp[132]; - int j; - d = ucTemp; - *d++ = 0x40; - s = &pBuffer[x + ((pBBEI->height-7)>>3)*pBBEI->width]; - ucMask = 0x80; - for (y=0; yheight; y++) { - uc = 0; - for (j=7; j>=0; j--) { - uc <<= 1; - if (s[j] & ucMask) uc |= 1; - } // for j - *d++ = uc; - ucMask >>= 1; - if (ucMask == 0) { - ucMask = 0x80; - s -= pBBEI->width; - } - } // for y - obdSetPosition(pBBEI, 0, x, 1); - RawWrite(pBBEI, ucTemp, pBBEI->height + 1); - } // for x - } else { // 270 degrees and can't flip 180 - for (x=pBBEI->width-8; x>=0; x-=8) { - uint8_t j, *s, *d, uc, ucMask, ucTemp[132]; - d = ucTemp; - *d++ = 0x40; - s = &pBuffer[x]; - ucMask = 1; - for (y=0; yheight; y++) { - uc = 0; - for (j=0; j<8; j++) { - uc <<= 1; - if (s[j] & ucMask) uc |= 1; - } // for j - *d++ = uc; - ucMask <<= 1; - if (ucMask == 0) { - ucMask = 1; - s += pBBEI->width; - } - } // for y - obdSetPosition(pBBEI, 0, pBBEI->width-8-x, 1); - RawWrite(pBBEI, ucTemp, pBBEI->height + 1); - } // for x - } - } // 90/270 degrees rotated - return BBEI_SUCCESS; -} /* obdDumpBuffer() */ - -// Send a single byte command to the OLED controller -void obdWriteCommand(OBDISP *pBBEI, unsigned char c) -{ -#ifndef MEMORY_ONLY -unsigned char buf[4]; - - buf[0] = 0x00; // command introducer - buf[1] = c; - RawWrite(pBBEI, buf, 2); -#endif // MEMORY_ONLY -} /* obdWriteCommand() */ -// -// Experimental bufferless E-ink drawing code -// -void EPDSetPosition(OBDISP *pBBEI, int x, int y, int cx, int cy) -{ -uint8_t uc[12]; -int i, tx, ty; - - // We need to rotate the drawing direction since native direction for EPDs - // is horizontal bytes across the X direction with the MSB on the left - if (pBBEI->iOrientation == 90 || pBBEI->iOrientation == 270) { - if (x + cx > pBBEI->native_height) - cx = pBBEI->native_height - x; - if (y + cy > pBBEI->native_width) - cy = pBBEI->native_width - y; - y &= 0xff8; // fix to byte boundary - if ((pBBEI->iFlags & BBEI_FLIPV) || (pBBEI->ucScreen == NULL/* && pBBEI->chip_type == BBEI_CHIP_SSD16xx*/)) // flipped Y when drawing with no back buffer - tx = (pBBEI->native_width /*+ 7*/ - y - cy)/8; - else - tx = y/8; - ty = x; - // swap width/height - i = cx; - cx = cy; - cy = i; + if (pBBEI->chip_type == BBEI_CHIP_UC81xx) { + ucCMD1 = UC8151_DTM1; + ucCMD2 = UC8151_DTM2; } else { - tx = (x+7)/8; - ty = y; - if (ty+cy > pBBEI->native_height) { - cy = pBBEI->native_height - ty; - } - if ((tx*8)+cx > pBBEI->native_width) { - cx = pBBEI->native_width - (tx*8); - } + ucCMD1 = SSD1608_WRITE_RAM; + ucCMD2 = SSD1608_WRITE_ALTRAM; } - switch (pBBEI->chip_type) { - case BBEI_CHIP_SSD16xx: - if (pBBEI->type == EPD579_792x272) { // special case - EPD_CMD2(pBBEI, SSD1608_DATA_MODE, 0x02); - if (cx > 400 && cy == 8) { // a single line of graphics - cx = 8; - cy = 271-ty; // different mem direction - } - if (cx > 400) cx = 400; // two sets of 400 pixels - obdWriteCommand(pBBEI, SSD1608_SET_RAMXPOS); - uc[1] = tx; // start x (byte boundary) - uc[0] = tx+(cx>>3); // end x - RawWriteData(pBBEI, uc, 2); - obdWriteCommand(pBBEI, SSD1608_SET_RAMYPOS); - uc[0] = (uint8_t)ty; // start y - uc[1] = (uint8_t)(ty>>8); - uc[2] = (uint8_t)(ty+cy-1); // end y - uc[3] = (uint8_t)((ty+cy-1)>>8); - RawWriteData(pBBEI, uc, 4); - EPD_CMD2(pBBEI, SSD1608_SET_RAMXCOUNT, 0xf); - obdWriteCommand(pBBEI, SSD1608_SET_RAMYCOUNT); - uc[0] = uc[1] = 0; - RawWriteData(pBBEI, uc, 2); - if (pBBEI->iFG == BBEI_RED) // write to red plane - obdWriteCommand(pBBEI, SSD1608_WRITE_ALTRAM); - else - obdWriteCommand(pBBEI, SSD1608_WRITE_RAM); // prepare to start writing pixels - } else { - obdWriteCommand(pBBEI, SSD1608_SET_RAMXPOS); - uc[0] = tx + pBBEI->x_offset; // start x (byte boundary) - // if (cx == pBBEI->native_width && (pBBEI->native_width & 7) != 0) - // cx += 7; // needs one more byte for the full display width - uc[1] = tx+((cx-1)>>3) + pBBEI->x_offset; // end x - RawWriteData(pBBEI, uc, 2); - obdWriteCommand(pBBEI, SSD1608_SET_RAMYPOS); - if (pBBEI->iFlags & BBEI_FLIPV) { - uc[0] = (uint8_t)(ty+cy-1); // start y - uc[1] = (uint8_t)((ty+cy-1)>>8); - uc[2] = (uint8_t)ty; // end y - uc[3] = (uint8_t)(ty>>8); - } else { - uc[0] = (uint8_t)ty; // start y - uc[1] = (uint8_t)(ty>>8); - uc[2] = (uint8_t)(ty+cy-1); // end y - uc[3] = (uint8_t)((ty+cy-1)>>8); - } - RawWriteData(pBBEI, uc, 4); - // set ram counter to start of this region - EPD_CMD2(pBBEI, SSD1608_SET_RAMXCOUNT, tx+pBBEI->x_offset); - uc[0] = ty; - uc[1] = (ty>>8); - obdWriteCommand(pBBEI, SSD1608_SET_RAMYCOUNT); - RawWriteData(pBBEI, uc, 2); - if (pBBEI->iFlags & BBEI_FLIPV) - EPD_CMD2(pBBEI, SSD1608_DATA_MODE, 0x1); - else - EPD_CMD2(pBBEI, SSD1608_DATA_MODE, 0x3); - if (pBBEI->iFG == BBEI_RED) // write to red plane - obdWriteCommand(pBBEI, SSD1608_WRITE_ALTRAM); - else - obdWriteCommand(pBBEI, SSD1608_WRITE_RAM); // prepare to start writing pixels - } - break; - case BBEI_CHIP_UC8151: - obdWriteCommand(pBBEI, UC8151_PTIN); // partial in - obdWriteCommand(pBBEI, UC8151_PTL); // partial window - i = 0; - tx *= 8; - if (pBBEI->native_width >= 256) { // need 2 bytes per x - uc[i++] = (uint8_t)(tx>>8); // start x - uc[i++] = (uint8_t)tx; - uc[i++] = (uint8_t)((tx+cx-1)>>8); // end x - uc[i++] = (uint8_t)((tx+cx-1) | 7); - } else { - uc[i++] = tx; // start x - uc[i++] = (tx+cx-1) | 7; // end x - } - if (pBBEI->iFlags & BBEI_FLIPV) { - if (pBBEI->native_height >= 256) { - uc[i++] = (uint8_t)((ty+cy-1)>>8); // start y - uc[i++] = (uint8_t)(ty+cy-1); - uc[i++] = (uint8_t)(ty>>8); // end y - uc[i++] = (uint8_t)ty; - } else { - uc[i++] = (uint8_t)(ty+cy-1); - uc[i++] = (uint8_t)ty; - } - } else { - if (pBBEI->native_height >= 256) { - uc[i++] = (uint8_t)(ty>>8); // start y - uc[i++] = (uint8_t)ty; - uc[i++] = (uint8_t)((ty+cy-1)>>8); // end y - uc[i++] = (uint8_t)(ty+cy-1); - } else { - uc[i++] = (uint8_t)ty; - uc[i++] = (uint8_t)(ty+cy-1); - } - } - uc[i++] = 0; // stay inside the partial window3 - RawWriteData(pBBEI, uc, i); - if (pBBEI->iFlags & BBEI_3COLOR) { - if (pBBEI->iFG == BBEI_RED) // write to red plane - obdWriteCommand(pBBEI, UC8151_DTM2); - else - obdWriteCommand(pBBEI, UC8151_DTM1); // prepare to start writing pixels - } else { - obdWriteCommand(pBBEI, UC8151_DTM2); - } - break; - default: // error - break; - } -} /* EPDSetPosition() */ -// -// Send commands to position the "cursor" (aka memory write address) -// to the given row and column -// -void obdSetPosition(OBDISP *pBBEI, int x, int y, int bRender) -{ -unsigned char buf[4]; -int iPitch = pBBEI->width; - - // e-paper direct access - if (pBBEI->type >= EPD42_400x300 && !pBBEI->ucScreen) { // immediate mode for e-paper displays - EPDSetPosition(pBBEI, x, y, 800, 8); // let the width get corrected in the command prep - return; - } -#if !defined( WIMPY_MCU ) && !defined(__AVR__) - if (pBBEI->type == LCD_ST7302) { // special case for ST7302 - obdST7302SetPos(pBBEI, x, y); - } -#endif // !WIMPY_MCU - y >>= 3; // DEBUG - since we address the display by lines of 8 pixels - pBBEI->iScreenOffset = (y*iPitch)+x; - - if (pBBEI->type == LCD_VIRTUAL || pBBEI->chip_type == BBEI_CHIP_SHARP) - return; // nothing to do - if (!bRender) - return; // don't send the commands to the OLED if we're not rendering the graphics now - obdCachedFlush(pBBEI, bRender); // flush any cached data first - if (pBBEI->type == LCD_NOKIA5110) - { - obdWriteCommand(pBBEI, 0x40 | y); - obdWriteCommand(pBBEI, 0x80 | x); - return; - } - if (pBBEI->type == OLED_80x128) // visible portion starts at column 24, row 0 - { - x += 24; - } - else if (pBBEI->type == LCD_32x64) - { - x += 50; - } - else if (pBBEI->type == OLED_64x32) // visible display starts at column 32, row 4 - { - x += 32; // display is centered in VRAM, so this is always true - if (pBBEI->flip == 0) // non-flipped display starts from line 4 - y += 4; - } - else if (pBBEI->type == OLED_64x48) { - x += 32; - if (!pBBEI->flip) - y += 2; // 128x64 internal RAM - } - else if (pBBEI->type == LCD_UC1701 && pBBEI->flip) - { - x += 4; - } - else if (pBBEI->type == OLED_132x64) // SH1106 has 128 pixels centered in 132 - { - x += 2; - } - else if (pBBEI->type == OLED_96x16) // visible display starts at line 2 - { // mapping is a bit strange on the 96x16 OLED - if (pBBEI->flip) - x += 32; - else - y += 2; - } - else if (pBBEI->type == OLED_72x40) // starts at x=28,y=3 - { - x += 28; - if (!pBBEI->flip) - { - y += 3; - } - } - if (pBBEI->com_mode == COM_I2C) { // I2C device - buf[0] = 0x00; // command introducer - buf[1] = 0xb0 | y; // set page to Y - buf[2] = x & 0xf; // lower column address - buf[3] = 0x10 | (x >> 4); // upper column addr - RawWrite(pBBEI, buf, 4); - } else { // SPI mode - obdWriteCommand(pBBEI, 0xb0 | y); // set Y - obdWriteCommand(pBBEI, 0x10 | (x >> 4)); // set X MSB - obdWriteCommand(pBBEI, 0x00 | (x & 0xf)); // set X LSB - } -} /* obdSetPosition() */ - -// -// Write a block of pixel data to the OLED -// Length can be anything from 1 to 1024 (whole display) -// -void obdWriteDataBlock(OBDISP *pBBEI, unsigned char *ucBuf, int iLen, int bRender) -{ -int iPitch, iBufferSize; -int iRedOffset = 0; + iOffset = ((pBBEI->native_width+7)>>3) * pBBEI->native_height; - iPitch = pBBEI->width; - iBufferSize = iPitch * ((pBBEI->height+7) / 8); - if (pBBEI->iFG >= BBEI_YELLOW) { - iRedOffset = pBBEI->width * ((pBBEI->height+7)/8); + switch (iPlane) { + case PLANE_0: + bbeiWriteImage(pBBEI, ucCMD1, pBBEI->ucScreen, 0, 0, pBBEI->width, pBBEI->height, 0); + break; + case PLANE_1: + bbeiWriteImage(pBBEI, ucCMD2, &pBBEI->ucScreen[iOffset], 0, 0, pBBEI->width, pBBEI->height, 0); + break; + case PLANE_BOTH: + bbeiWriteImage(pBBEI, ucCMD1, pBBEI->ucScreen, 0, 0, pBBEI->width, pBBEI->height, 0); + bbeiWriteImage(pBBEI, ucCMD2, &pBBEI->ucScreen[iOffset], 0, 0, pBBEI->width, pBBEI->height, 0); + break; + case PLANE_DUPLICATE: + bbeiWriteImage(pBBEI, ucCMD1, pBBEI->ucScreen, 0, 0, pBBEI->width, pBBEI->height, 0); + bbeiWriteImage(pBBEI, ucCMD2, pBBEI->ucScreen, 0, 0, pBBEI->width, pBBEI->height, 0); + break; } -// Keep a copy in local buffer -if (pBBEI->ucScreen && (iLen + pBBEI->iScreenOffset) <= iBufferSize) -{ - memcpy(&pBBEI->ucScreen[iRedOffset + pBBEI->iScreenOffset], ucBuf, iLen); - pBBEI->iScreenOffset += iLen; - // wrap around ? - if (pBBEI->iScreenOffset >= iBufferSize) - pBBEI->iScreenOffset -= iBufferSize; -} -if (pBBEI->type == LCD_VIRTUAL || pBBEI->chip_type == BBEI_CHIP_SHARP) - return; // nothing else to do -#ifndef MEMORY_ONLY - if (bRender) - { - RawWriteData(pBBEI, ucBuf, iLen); - } -#endif // MEMORY_ONLY -} /* obdWriteDataBlock() */ + return BBEI_SUCCESS; +} /* bbeiWritePlane() */ + +#endif // __BB_EI__ diff --git a/src/bb_ei_gfx.inl b/src/bb_ei_gfx.inl index bbb8299..05e3cb1 100644 --- a/src/bb_ei_gfx.inl +++ b/src/bb_ei_gfx.inl @@ -1,8 +1,8 @@ // -// OneBitDisplay (OLED/LCD/E-Paper library) -// Copyright (c) 2020 BitBank Software, Inc. +// bb_eink +// Copyright (c) 2024 BitBank Software, Inc. // Written by Larry Bank (bitbank@pobox.com) -// Project started 3/23/2020 +// Project started 9/11/2024 // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,8 +14,11 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// obd_gfx.inl - graphics functions +// bb_ei_gfx.inl - graphics functions // +#ifndef __BB_EI_GFX__ +#define __BB_EI_GFX__ + // forward declarations void InvertBytes(uint8_t *pData, uint8_t bLen); @@ -68,395 +71,6 @@ const uint8_t ucFont[]PROGMEM = { 0x4c,0x64,0x74,0x5c,0x4c,0x64,0x00,0x08,0x08,0x3e,0x77,0x41,0x41,0x00, 0x00,0x00,0x00,0x77,0x77,0x00,0x00,0x41,0x41,0x77,0x3e,0x08,0x08,0x00, 0x02,0x03,0x01,0x03,0x02,0x03,0x01,0x70,0x78,0x4c,0x46,0x4c,0x78,0x70}; -// AVR MCUs have very little memory; save 6K of FLASH by stretching the 'normal' -// font instead of using this large font -#ifndef WIMPY_MCU -const uint8_t ucBigFont[]PROGMEM = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xfc,0xfc,0xff,0xff,0xff,0xff,0xfc,0xfc,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x3f,0x3f,0x3f,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, - 0x00,0x00,0x0f,0x0f,0x3f,0x3f,0x00,0x00,0x00,0x00,0x3f,0x3f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xc0,0xc0,0xfc,0xfc,0xfc,0xfc,0xc0,0xc0,0xfc,0xfc,0xfc,0xfc,0xc0,0xc0,0x00,0x00, - 0xc0,0xc0,0xff,0xff,0xff,0xff,0xc0,0xc0,0xff,0xff,0xff,0xff,0xc0,0xc0,0x00,0x00, - 0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0x0f,0x0f,0x3c,0x3c,0x00,0x00, - 0xf0,0xf0,0xc3,0xc3,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0x00,0x00,0x03,0x03,0x03,0x03,0x3f,0x3f,0x3f,0x3f,0x03,0x03,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xf0,0xf0,0x00,0x00, - 0x00,0x00,0xc0,0xc0,0xf0,0xf0,0x3c,0x3c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, - 0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x3c,0x3c,0xff,0xff,0xc3,0xc3,0xff,0xff,0x3c,0x3c,0x00,0x00,0x00,0x00, - 0xfc,0xfc,0xff,0xff,0x03,0x03,0x0f,0x0f,0xfc,0xfc,0xff,0xff,0x03,0x03,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00, - 0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x30,0x30,0x3f,0x3f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xf0,0xf0,0xfc,0xfc,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0xfc,0xfc,0xf0,0xf0,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, - 0x0c,0x0c,0xcc,0xcc,0xff,0xff,0x3f,0x3f,0x3f,0x3f,0xff,0xff,0xcc,0xcc,0x0c,0x0c, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x0c,0x0c,0x0c,0x0c,0xff,0xff,0xff,0xff,0x0c,0x0c,0x0c,0x0c,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x30,0x30,0x3f,0x3f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xf0,0xf0,0x00,0x00, - 0x00,0x00,0xc0,0xc0,0xf0,0xf0,0x3c,0x3c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, - 0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0xc3,0xc3,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0xff,0xff,0xff,0xff,0x30,0x30,0x0f,0x0f,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x30,0x30,0x3c,0x3c,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x03,0x03,0xc3,0xc3,0xff,0xff,0x3c,0x3c,0x00,0x00, - 0xc0,0xc0,0xf0,0xf0,0x3c,0x3c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xc0,0xc0,0xf0,0xf0,0x3c,0x3c,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0xff,0xff,0xff,0xff,0x0c,0x0c,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00, - 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x0f,0x0f,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xf0,0xf0,0xfc,0xfc,0x0f,0x0f,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0f,0x0f,0x0f,0x0f,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00, - 0x00,0x00,0x00,0x00,0xf0,0xf0,0xfc,0xfc,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00, - 0x00,0x00,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xf0,0xf0,0x3c,0x3c,0x0c,0x0c,0x00,0x00, - 0x00,0x00,0x0c,0x0c,0x3f,0x3f,0xf3,0xf3,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x0c,0x0c,0x3c,0x3c,0xf0,0xf0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xf3,0xf3,0x3f,0x3f,0x0c,0x0c,0x00,0x00, - 0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x3c,0x3c,0x3f,0x3f,0x03,0x03,0x03,0x03,0xc3,0xc3,0xff,0xff,0x3c,0x3c,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x3f,0x3f,0x3f,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xf0,0xf0,0xfc,0xfc,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0xfc,0xfc,0xf0,0xf0,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x3f,0x3f,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xc0,0xc0,0xf0,0xf0,0x3c,0x3c,0x0f,0x0f,0x3c,0x3c,0xf0,0xf0,0xc0,0xc0,0x00,0x00, - 0xff,0xff,0xff,0xff,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0xff,0xff,0xff,0xff,0x00,0x00, - 0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xf0,0xf0,0xfc,0xfc,0x0f,0x0f,0x03,0x03,0x03,0x03,0x0f,0x0f,0x3c,0x3c,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0x00,0x00, - 0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x0f,0x0f,0xfc,0xfc,0xf0,0xf0,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0xc3,0xc3,0x0f,0x0f,0x3f,0x3f,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x0f,0x0f,0x00,0x00,0xc0,0xc0,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0xc3,0xc3,0x0f,0x0f,0x3f,0x3f,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xf0,0xf0,0xfc,0xfc,0x0f,0x0f,0x03,0x03,0x03,0x03,0x0f,0x0f,0x3c,0x3c,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x0c,0x0c,0x0c,0x0c,0xfc,0xfc,0xfc,0xfc,0x00,0x00, - 0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x03,0x03,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00, - 0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x00,0x00, - 0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00,0xf0,0xf0,0xff,0xff,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x0f,0x0f,0x3f,0x3f,0xf0,0xf0,0xc0,0xc0,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0xfc,0xfc,0xf0,0xf0,0xfc,0xfc,0xff,0xff,0xff,0xff,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x03,0x03,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0xfc,0xfc,0xf0,0xf0,0xc0,0xc0,0xff,0xff,0xff,0xff,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x03,0x03,0x0f,0x0f,0xff,0xff,0xff,0xff,0x00,0x00, - 0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xf0,0xf0,0xfc,0xfc,0x0f,0x0f,0x03,0x03,0x0f,0x0f,0xfc,0xfc,0xf0,0xf0,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0xc0,0xc0,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0f,0x0f,0xff,0xff,0xff,0xff,0xc3,0xc3,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x0f,0x0f,0xff,0xff,0xf0,0xf0,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x3c,0x3c,0xff,0xff,0xc3,0xc3,0x03,0x03,0x03,0x03,0x3f,0x3f,0x3c,0x3c,0x00,0x00, - 0xc0,0xc0,0xc0,0xc0,0x03,0x03,0x03,0x03,0x0f,0x0f,0xfc,0xfc,0xf0,0xf0,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x3f,0x3f,0x0f,0x0f,0xff,0xff,0xff,0xff,0x0f,0x0f,0x3f,0x3f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x3f,0x3f,0xff,0xff,0xc0,0xc0,0x00,0x00,0xc0,0xc0,0xff,0xff,0x3f,0x3f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0xff,0xff,0xff,0xff,0xc0,0xc0,0xfc,0xfc,0xc0,0xc0,0xff,0xff,0xff,0xff,0x00,0x00, - 0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0f,0x0f,0xff,0xff,0xf0,0xf0,0x00,0x00,0xf0,0xf0,0xff,0xff,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0xf0,0xf0,0xff,0xff,0x0f,0x0f,0xff,0xff,0xf0,0xf0,0x00,0x00,0x00,0x00, - 0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x00,0x00,0x00,0x00,0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x3f,0x3f,0x0f,0x0f,0x03,0x03,0x03,0x03,0xc3,0xc3,0xff,0xff,0x3f,0x3f,0x00,0x00, - 0xc0,0xc0,0xf0,0xf0,0x3c,0x3c,0x0f,0x0f,0x03,0x03,0x00,0x00,0xc0,0xc0,0x00,0x00, - 0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfc,0xfc,0xf0,0xf0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x03,0x03,0x0f,0x0f,0x3f,0x3f,0xfc,0xfc,0xf0,0xf0,0xc0,0xc0,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xc0,0xc0,0xf0,0xf0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0, - 0x00,0x00,0x00,0x00,0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, - 0xf0,0xf0,0xfc,0xfc,0x0c,0x0c,0x0c,0x0c,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x03,0xff,0xff,0xff,0xff,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xc0,0xc0,0xc3,0xc3,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0xfc,0xfc,0xff,0xff,0x03,0x03,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xfc,0xfc,0xff,0xff,0x03,0x03,0x0f,0x0f,0x3c,0x3c,0x00,0x00,0x00,0x00, - 0x03,0x03,0xff,0xff,0xff,0xff,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0xc3,0xc3,0xcf,0xcf,0x0c,0x0c,0x0c,0x0c,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, - 0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xc0,0xc0,0xcf,0xcf,0xcf,0xcf,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xcf,0xcf,0xcf,0xcf,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x00,0x00,0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00, - 0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x3c,0x3c,0xff,0xff,0xc3,0xc3,0x00,0x00,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0x03,0x03,0xff,0xff,0x03,0x03,0xff,0xff,0xff,0xff,0x00,0x00, - 0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x0f,0x0f,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00, - 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x03,0x03,0x00,0x00,0x03,0x03,0x0f,0x0f,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x3c,0x3c,0x30,0x30,0xf0,0xf0,0xc3,0xc3,0x03,0x03,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xc0,0xc0,0xc0,0xc0,0xfc,0xfc,0xff,0xff,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0xf0,0xf0,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0f,0x0f,0x03,0x03,0x0f,0x0f,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, - 0x00,0x00,0x03,0x03,0xff,0xff,0xfc,0xfc,0xff,0xff,0x03,0x03,0x00,0x00,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0x00,0x00, - 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00, - 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0xcc,0xcc,0xff,0xff,0x3f,0x3f,0x00,0x00, - 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, - 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00, - 0x03,0x03,0xc3,0xc3,0xf0,0xf0,0x3c,0x3c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00, - 0x0f,0x0f,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x0f,0x0f,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0x00,0x00, - 0x00,0x00,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0xfc,0xfc,0xfc,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x03,0x03,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0xfc,0xff,0xff,0x03,0x03,0x03,0x03,0x00,0x00, - 0x00,0x00,0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xc0,0xc0,0xf0,0xf0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfc,0xfc,0xff,0xff,0x03,0x03,0x00,0x00,0x03,0x03,0xff,0xff,0xfc,0xfc,0x00,0x00, - 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; -#endif // !WIMPY_MCU // 5x7 font (in 6x8 cell) const uint8_t ucSmallFont[] PROGMEM = { @@ -557,254 +171,6 @@ const uint8_t ucSmallFont[] PROGMEM = { 0x02,0x01,0x02,0x01,0x00, 0x3c,0x26,0x23,0x26,0x3c}; -static void obdCachedFlush(OBDISP *pOBD, int bRender) -{ - if (u8End > 0) { - obdWriteDataBlock(pOBD, u8Cache, u8End, bRender); - u8End = 0; - } -} /* obdCachedFlush() */ - -static void obdCachedWrite(OBDISP *pOBD, uint8_t *pData, uint8_t u8Len, int bRender) -{ - if (u8End + u8Len > MAX_CACHE) // need to flush it - { - obdCachedFlush(pOBD, bRender); // write the old data - } - memcpy(&u8Cache[u8End], pData, u8Len); - u8End = u8End + u8Len; - -} /* obdCachedWrite() */ -// -// Create a virtual display of any size -// The memory buffer must be provided at the time of creation -// -void obdCreateVirtualDisplay(OBDISP *pOBD, int width, int height, uint8_t *buffer) -{ - if (pOBD != NULL && buffer != NULL) - { - pOBD->width = width; - pOBD->height = height; - pOBD->type = LCD_VIRTUAL; - pOBD->ucScreen = buffer; - pOBD->iCursorX = pOBD->iCursorY = 0; - pOBD->iScreenOffset = 0; - } -} /* obdCreateVirtualDisplay() */ -// -// Scroll the internal buffer by 1 scanline (up/down) -// width is in pixels, lines is group of 8 rows -// -int obdScrollBuffer(OBDISP *pOBD, int iStartCol, int iEndCol, int iStartRow, int iEndRow, int bUp) -{ - uint8_t b, *s; - int col, row; - int iPitch; - - if (iStartCol < 0 || iStartCol >= pOBD->width || iEndCol < 0 || iEndCol > pOBD->width || iStartCol > iEndCol) // invalid - return OBD_ERROR_BAD_PARAMETER; - if (iStartRow < 0 || iStartRow >= (pOBD->height/8) || iEndRow < 0 || iEndRow >= (pOBD->height/8) || iStartRow > iEndRow) - return OBD_ERROR_BAD_PARAMETER; - iPitch = pOBD->width; - if (bUp) - { - for (row=iStartRow; row<=iEndRow; row++) - { - s = &pOBD->ucScreen[(row * iPitch) + iStartCol]; - for (col=iStartCol; col<=iEndCol; col++) - { - b = *s; - b >>= 1; // scroll pixels 'up' - if (row < iEndRow) - b |= (s[iPitch] << 7); // capture pixel of row below, except for last row - *s++ = b; - } // for col - } // for row - } // up - else // down - { - for (row=iEndRow; row>=iStartRow; row--) - { - s = &pOBD->ucScreen[(row * iPitch)+iStartCol]; - for (col=iStartCol; col<=iEndCol; col++) - { - b = *s; - b <<= 1; // scroll down - if (row > iStartRow) - b |= (s[-iPitch] >> 7); // capture pixel of row above - *s++ = b; - } // for col - } // for row - } - return OBD_SUCCESS; -} /* obdScrollBuffer() */ -// -// Return the number of bytes accumulated as commands -// -int obdGetCommandLen(OBDISP *pOBD) -{ - if (pOBD != NULL && pOBD->type == DISPLAY_COMMANDS) - return pOBD->iScreenOffset; - else - return 0; -} /* obdGetCommandLen() */ -// -// Write a single byte into the command queue -// -static void obdWriteCmdByte(OBDISP *pOBD, uint8_t u8) -{ -uint8_t *d = pOBD->ucScreen; -int i = pOBD->iScreenOffset; - - d[i++] = u8; - pOBD->iScreenOffset = i; - -} /* obdWriteCmdByte() */ -// -// Write an unsigned integer into the command queue -// use integer expansion to write small values as 1 byte -// and larger values as 2 bytes -// -static void obdWriteCmdInt(OBDISP *pOBD, int iValue) -{ -uint8_t *d = pOBD->ucScreen; -int i = pOBD->iScreenOffset; - - if (iValue <= 127) { - d[i++] = (uint8_t)iValue; - } else { - d[i++] = (uint8_t)(iValue | 0x80); // store lower 7 bits - d[i++] = (uint8_t)(iValue >> 7); // store next 8 bits - } - pOBD->iScreenOffset = i; - -} /* obdWriteCmdInt() */ -// -// Read an unsigned integer from the command queue -// use integer expansion to store small values as 1 byte -// and larger values as 2 bytes -// -static uint8_t * obdReadCmdInt(uint8_t *pData, int *iValue) -{ -int i; - - i = *pData++; - if (i & 0x80) { // two byte integer - i &= 0x7f; - i |= (pData[0] << 7); - pData++; - } - *iValue = i; - return pData; -} /* obdReadCmdInt() */ -// -// Set the current custom font pointers for playing back -// bytewise commands -// -void obdSetCustomFont(OBDISP *pOBD, GFXfont *pFont, uint8_t ucFont) -{ - if (pOBD != NULL && pFont != NULL && ucFont >= FONT_CUSTOM0 && ucFont <= FONT_CUSTOM2) { - pOBD->pFont[ucFont-FONT_CUSTOM0] = (void *)pFont; - } -} /* obdSetCustomFont() */ -// -// Execute a set of bytewise command bytes -// and execute the drawing instructions on the current display/buffer -// Optionally render on backbuffer or physical display -// -void obdExecCommands(uint8_t *pData, int iLen, OBDISP *pOBD, int bRender) -{ -uint8_t *s, *pEnd; -uint8_t uc, ucColor=1, ucFill, ucFont; -int x1, y1, x2, y2; -int iTextLen, iPitch; -uint8_t ucTemp[64]; - - if (pData == NULL || pOBD == NULL) return; - - s = pData; // source of the command data - pEnd = &s[iLen]; - while (s < pEnd-1) { - uc = *s++; - switch (uc & 0xf) { // lower 4 bits hold command - case OBD_FILL: - obdFill(pOBD, s[0], bRender); - s++; - break; - case OBD_DRAWTEXT: - iTextLen = *s++; - ucColor = (uc >> 7); // invert flag - ucFont = (uc >> 4) & 7; // font size - if (pEnd - s >= iTextLen+3) { - s = obdReadCmdInt(s, &x1); // col - s = obdReadCmdInt(s, &y1); // row - memcpy(ucTemp, s, iTextLen); - ucTemp[iTextLen] = 0; // terminate the string - s += iTextLen; - if (ucFont >= FONT_CUSTOM0) { // up to 3 custom fonts - if (pOBD->pFont[ucFont-FONT_CUSTOM0] != NULL) { - obdWriteStringCustom(pOBD, (GFXfont *)pOBD->pFont[ucFont-FONT_CUSTOM0], x1, y1, (char *)ucTemp, ucColor); - } - } else { - obdWriteString(pOBD, 0, x1, y1, (char *)ucTemp, ucFont, ucColor, bRender); - } - } else { - return; // something went wrong! - } - break; - case OBD_DRAWLINE: - if (pEnd - s >= 4) { - ucColor = (uc >> 4) & 1; - s = obdReadCmdInt(s, &x1); - s = obdReadCmdInt(s, &y1); - s = obdReadCmdInt(s, &x2); - s = obdReadCmdInt(s, &y2); - obdDrawLine(pOBD, x1, y1, x2, y2, ucColor, bRender); - } - break; - case OBD_DRAWRECT: - if (pEnd - s >= 4) { - ucColor = (uc >> 4) & 1; - ucFill = (uc >> 5) & 1; - s = obdReadCmdInt(s, &x1); - s = obdReadCmdInt(s, &y1); - s = obdReadCmdInt(s, &x2); - s = obdReadCmdInt(s, &y2); - obdRectangle(pOBD, x1, y1, x2, y2, ucColor, ucFill); - } - break; - case OBD_DRAWELLIPSE: - if (pEnd - s >= 4) { - ucColor = (uc >> 4) & 1; - ucFill = (uc >> 5) & 1; - s = obdReadCmdInt(s, &x1); - s = obdReadCmdInt(s, &y1); - s = obdReadCmdInt(s, &x2); - s = obdReadCmdInt(s, &y2); - obdEllipse(pOBD, x1, y1, x2, y2, ucColor, ucFill); - } - break; - case OBD_DRAWSPRITE: - if (pEnd - s >= 8) { - ucColor = (uc >> 4) & 1; - s = obdReadCmdInt(s, &x1); // width / height - s = obdReadCmdInt(s, &y1); - s = obdReadCmdInt(s, &x2); // destination x/y - s = obdReadCmdInt(s, &y2); - iPitch = (x1 + 7) >> 3; - if (pEnd - s >= (iPitch * y1)) { // enough to hold the data - obdDrawSprite(pOBD, s, x1, y1, iPitch, x2, y2, ucColor); - s += (iPitch * y1); - } else { - return; // error! - } - } - break; - default: - return; // invalid command! - } - } -} /* obdParseCommands() */ // // Draw a sprite of any size in any position // If it goes beyond the left/right or top/bottom edges @@ -813,34 +179,15 @@ uint8_t ucTemp[64]; // The priority color (0 or 1) determines which color is painted // when a 1 is encountered in the source image. // -void obdDrawSprite(OBDISP *pOBD, uint8_t *pSprite, int cx, int cy, int iPitch, int x, int y, uint8_t iPriority) +void bbeiDrawSprite(BBEIDISP *pBBEI, uint8_t *pSprite, int cx, int cy, int iPitch, int x, int y, uint8_t iPriority) { int tx, ty, dx, dy, iStartX; uint8_t *s, *d, uc, pix, ucSrcMask, ucDstMask; int iLocalPitch; - iLocalPitch = pOBD->width; + iLocalPitch = pBBEI->width; - if (pOBD == NULL) return; - if (pOBD->type == DISPLAY_COMMANDS) { // encode this as a command sequence - int iLocalPitch = (cx+7)>>3; - obdWriteCmdByte(pOBD, OBD_DRAWSPRITE | ((iPriority & 1) << 4)); - obdWriteCmdInt(pOBD, cx); - obdWriteCmdInt(pOBD, cy); - obdWriteCmdInt(pOBD, x); - obdWriteCmdInt(pOBD, y); - d = pOBD->ucScreen; - tx = pOBD->iScreenOffset; - s = pSprite; - for (ty=0; tyiScreenOffset = tx; // store new length - return; // done - } - - if (x+cx < 0 || y+cy < 0 || x >= pOBD->width || y >= pOBD->height || pOBD->ucScreen == NULL) + if (pBBEI == NULL) return; + if (x+cx < 0 || y+cy < 0 || x >= pBBEI->width || y >= pBBEI->height || pBBEI->ucScreen == NULL) return; // no backbuffer or out of bounds dy = y; // destination y if (y < 0) // skip the invisible parts @@ -850,8 +197,8 @@ void obdDrawSprite(OBDISP *pOBD, uint8_t *pSprite, int cx, int cy, int iPitch, i pSprite += (y * iPitch); dy = 0; } - if (y + cy > pOBD->height) - cy = pOBD->height - y; + if (y + cy > pBBEI->height) + cy = pBBEI->height - y; iStartX = 0; dx = x; if (x < 0) @@ -861,12 +208,12 @@ void obdDrawSprite(OBDISP *pOBD, uint8_t *pSprite, int cx, int cy, int iPitch, i iStartX = x; dx = 0; } - if (x + cx > pOBD->width) - cx = pOBD->width - x; + if (x + cx > pBBEI->width) + cx = pBBEI->width - x; for (ty=0; ty> 3)]; - d = &pOBD->ucScreen[(dy>>3) * iLocalPitch + dx]; + d = &pBBEI->ucScreen[(dy>>3) * iLocalPitch + dx]; ucSrcMask = 0x80 >> (iStartX & 7); pix = *s++; ucDstMask = 1 << (dy & 7); @@ -905,156 +252,56 @@ void obdDrawSprite(OBDISP *pOBD, uint8_t *pSprite, int cx, int cy, int iPitch, i dy++; pSprite += iPitch; } // for ty -} /* obdDrawSprite() */ -// -// Draw a 16x16 tile in any of 4 rotated positions -// Assumes input image is laid out like "normal" graphics with -// the MSB on the left and 2 bytes per line -// On AVR, the source image is assumed to be in FLASH memory -// The function can draw the tile on byte boundaries, so the x value -// can be from 0 to width-16 and y can be from 0 to (height/8)-2 -// -void obdDrawTile(OBDISP *pOBD, const uint8_t *pTile, int x, int y, int iRotation, int bInvert, int bRender) -{ - uint8_t ucTemp[32]; // prepare LCD data here - uint8_t i, j, k, iOffset, ucMask, uc, ucPixels; - uint8_t bFlipX=0, bFlipY=0; - - if (x < 0 || y < 0 || y > (pOBD->height/8)-2 || x > pOBD->width-16) - return; // out of bounds - if (pTile == NULL) return; // bad pointer; really? :( - if (iRotation == ANGLE_180 || iRotation == ANGLE_270 || iRotation == ANGLE_FLIPX) - bFlipX = 1; - if (iRotation == ANGLE_180 || iRotation == ANGLE_270 || iRotation == ANGLE_FLIPY) - bFlipY = 1; - - memset(ucTemp, 0, sizeof(ucTemp)); // we only set white pixels, so start from black - if (iRotation == ANGLE_0 || iRotation == ANGLE_180 || iRotation == ANGLE_FLIPX || iRotation == ANGLE_FLIPY) - { - for (j=0; j<16; j++) // y - { - for (i=0; i<16; i+=8) // x - { - ucPixels = pgm_read_byte((uint8_t*)pTile++); - ucMask = 0x80; // MSB is the first source pixel - for (k=0; k<8; k++) - { - if (ucPixels & ucMask) // translate the pixel - { - if (bFlipY) - uc = 0x80 >> (j & 7); - else - uc = 1 << (j & 7); - iOffset = i+k; - if (bFlipX) iOffset = 15-iOffset; - iOffset += (j & 8)<<1; // top/bottom half of output - if (bFlipY) - iOffset ^= 16; - ucTemp[iOffset] |= uc; - } - ucMask >>= 1; - } // for k - } // for i - } // for j - } - else // rotated 90/270 - { - for (j=0; j<16; j++) // y - { - for (i=0; i<16; i+=8) // x - { - ucPixels = pgm_read_byte((uint8_t*)pTile++); - ucMask = 0x80; // MSB is the first source pixel - for (k=0; k<8; k++) - { - if (ucPixels & ucMask) // translate the pixel - { - if (bFlipY) - uc = 0x80 >> k; - else - uc = 1 << k; - iOffset = 15-j; - if (bFlipX) iOffset = 15-iOffset; - iOffset += i<<1; // top/bottom half of output - if (bFlipY) - iOffset ^= 16; - ucTemp[iOffset] |= uc; - } - ucMask >>= 1; - } // for k - } // for i - } // for j - } - if (bInvert) InvertBytes(ucTemp, 32); - // Send the data to the display - obdSetPosition(pOBD, x, y, bRender); - obdWriteDataBlock(pOBD, ucTemp, 16, bRender); // top half - obdSetPosition(pOBD, x,y+1, bRender); - obdWriteDataBlock(pOBD, &ucTemp[16], 16, bRender); // bottom half -} /* obdDrawTile() */ +} /* bbeiDrawSprite() */ // Set (or clear) an individual pixel // The local copy of the frame buffer is used to avoid // reading data from the display controller -int obdSetPixel(OBDISP *pOBD, int x, int y, unsigned char ucColor, int bRender) +int bbeiSetPixel(BBEIDISP *pBBEI, int x, int y, unsigned char ucColor) { int i; unsigned char uc, ucOld; int iPitch, iSize; - iPitch = pOBD->width; - iSize = iPitch * ((pOBD->height+7)/8); + iPitch = pBBEI->width; + iSize = iPitch * ((pBBEI->height+7)/8); i = ((y >> 3) * iPitch) + x; if (i < 0 || i > iSize-1) { // off the screen - return OBD_ERROR_BAD_PARAMETER; + return BBEI_ERROR_BAD_PARAMETER; } // Special case for 4-color e-ink - if (pOBD->iFlags & OBD_4COLOR) { + if (pBBEI->iFlags & BBEI_4COLOR) { uint8_t ucMask = (1 << (y & 7)); ucColor ^= 1; // invert low bit if (ucColor & 1) - pOBD->ucScreen[i] |= ucMask; + pBBEI->ucScreen[i] |= ucMask; else - pOBD->ucScreen[i] &= ~ucMask; + pBBEI->ucScreen[i] &= ~ucMask; if (ucColor & 2) - pOBD->ucScreen[iSize + i] |= ucMask; + pBBEI->ucScreen[iSize + i] |= ucMask; else - pOBD->ucScreen[iSize + i] &= ~ucMask; - return OBD_SUCCESS; + pBBEI->ucScreen[iSize + i] &= ~ucMask; + return BBEI_SUCCESS; } // special case for 3-color e-ink - if (pOBD->iFlags & OBD_3COLOR) { - if (ucColor >= OBD_YELLOW) { // yellow/red has priority - pOBD->ucScreen[iSize + i] |= (1 << (y & 7)); + if (pBBEI->iFlags & BBEI_3COLOR) { + if (ucColor >= BBEI_YELLOW) { // yellow/red has priority + pBBEI->ucScreen[iSize + i] |= (1 << (y & 7)); } else { - pOBD->ucScreen[iSize + i] &= ~(1 << (y & 7)); // clear red plane bit - if (ucColor == OBD_WHITE) { - pOBD->ucScreen[i] &= ~(1 << (y & 7)); + pBBEI->ucScreen[iSize + i] &= ~(1 << (y & 7)); // clear red plane bit + if (ucColor == BBEI_WHITE) { + pBBEI->ucScreen[i] &= ~(1 << (y & 7)); } else { // must be black - pOBD->ucScreen[i] |= (1 << (y & 7)); + pBBEI->ucScreen[i] |= (1 << (y & 7)); } } - return OBD_SUCCESS; + return BBEI_SUCCESS; } - obdSetPosition(pOBD, x, y, bRender); + bbeiSetPosition(pBBEI, x, y, 8, 1); - if (pOBD->ucScreen) - uc = ucOld = pOBD->ucScreen[i]; -#ifndef MEMORY_ONLY - else if (pOBD->type == OLED_132x64 || pOBD->type == OLED_128x128) // SH1106/SH1107 can read data - { - uint8_t ucTemp[3]; - ucTemp[0] = 0x80; // one command - ucTemp[1] = 0xE0; // read_modify_write - ucTemp[2] = 0xC0; // one data - RawWrite(pOBD, ucTemp, 3); - - // read a dummy byte followed by the data byte we want - I2CRead(&pOBD->bbi2c, pOBD->oled_addr, ucTemp, 2); - uc = ucOld = ucTemp[1]; // first byte is garbage - } -#endif // MEMORY_ONLY + if (pBBEI->ucScreen) + uc = ucOld = pBBEI->ucScreen[i]; else uc = ucOld = 0; @@ -1066,22 +313,13 @@ int iPitch, iSize; if (uc != ucOld) // pixel changed { // obdSetPosition(x, y>>3); - if (pOBD->ucScreen) + if (pBBEI->ucScreen) { - obdWriteDataBlock(pOBD, &uc, 1, bRender); - pOBD->ucScreen[i] = uc; - } - else if (pOBD->type == OLED_132x64 || pOBD->type == OLED_128x128) // end the read_modify_write operation - { - uint8_t ucTemp[4]; - ucTemp[0] = 0xc0; // one data - ucTemp[1] = uc; // actual data - ucTemp[2] = 0x80; // one command - ucTemp[3] = 0xEE; // end read_modify_write operation - RawWrite(pOBD, ucTemp, 4); + bbeiWriteData(pBBEI, &uc, 1); + pBBEI->ucScreen[i] = uc; } } - return OBD_ERROR_BAD_PARAMETER; + return BBEI_SUCCESS; } /* obdSetPixel() */ // @@ -1104,7 +342,7 @@ uint8_t i; // draw the 1's bits as the FG color and leave // the background (0 pixels) unchanged - aka transparent. // -int obdLoadBMP(OBDISP *pOBD, const uint8_t *pBMP, int dx, int dy, int iFG, int iBG) +int bbeiLoadBMP(BBEIDISP *pBBEI, const uint8_t *pBMP, int dx, int dy, int iFG, int iBG) { int16_t i16, cx, cy; int iOffBits; // offset to bitmap data @@ -1113,31 +351,31 @@ uint8_t b=0, *s, *d=NULL; uint8_t ucFill, dst_mask=0, src_mask; uint8_t bFlipped = 0; - if (pOBD->ucScreen == NULL) { + if (pBBEI->ucScreen == NULL) { // no BG specified or no back buffer, BG color is opposite of FG - if (iFG == -1) iFG = OBD_WHITE; - if (iBG == -1) iBG = OBD_BLACK; + if (iFG == -1) iFG = BBEI_WHITE; + if (iBG == -1) iBG = BBEI_BLACK; } // Don't use pgm_read_word because it can cause an unaligned // access on RP2040 for odd addresses i16 = pgm_read_byte(pBMP); i16 += (pgm_read_byte(&pBMP[1]) << 8); if (i16 != 0x4d42) // must start with 'BM' - return OBD_ERROR_BAD_DATA; // not a BMP file + return BBEI_ERROR_BAD_DATA; // not a BMP file cx = pgm_read_byte(pBMP + 18); cx += (pgm_read_byte(pBMP+19)<<8); - if (cx + dx > pOBD->width) // must fit on the display - return OBD_ERROR_BAD_PARAMETER; + if (cx + dx > pBBEI->width) // must fit on the display + return BBEI_ERROR_BAD_PARAMETER; cy = pgm_read_byte(pBMP + 22); cy += (pgm_read_byte(pBMP+23)<<8); if (cy < 0) cy = -cy; else bFlipped = 1; - if (cy + dy > pOBD->height) // must fit on the display - return OBD_ERROR_BAD_PARAMETER; + if (cy + dy > pBBEI->height) // must fit on the display + return BBEI_ERROR_BAD_PARAMETER; i16 = pgm_read_byte(pBMP + 28); i16 += (pgm_read_byte(pBMP+29)<<8); if (i16 != 1) // must be 1 bit per pixel - return OBD_ERROR_BAD_DATA; + return BBEI_ERROR_BAD_DATA; iOffBits = pgm_read_byte(pBMP + 10); iOffBits += (pgm_read_byte(pBMP+11)); iPitch = (((cx+7)>>3) + 3) & 0xfffc; // must be DWORD aligned @@ -1146,8 +384,8 @@ uint8_t bFlipped = 0; iOffBits += ((cy-1) * iPitch); // start from bottom iPitch = -iPitch; } - ucFill = (iBG == OBD_WHITE && pOBD->type < EPD42_400x300) ? iBG : 0xff; - if (!pOBD->ucScreen || iFG >= OBD_YELLOW) { // this will override the B/W plane, so invert things + ucFill = (iBG == BBEI_WHITE && pBBEI->type < EPD42_400x300) ? iBG : 0xff; + if (!pBBEI->ucScreen || iFG >= BBEI_YELLOW) { // this will override the B/W plane, so invert things ucFill = 0x00; x = iFG; iFG = iBG; @@ -1155,7 +393,7 @@ uint8_t bFlipped = 0; } for (y=0; yucScreen) + if (!pBBEI->ucScreen) { dst_mask = (1 << (y & 7)); d = u8Cache; @@ -1164,7 +402,7 @@ uint8_t bFlipped = 0; } s = (uint8_t *)&pBMP[iOffBits + (y*iPitch)]; src_mask = 0; - if (!pOBD->ucScreen) // direct to display + if (!pBBEI->ucScreen) // direct to display { for (x=0; x= 0) - obdSetPixel(pOBD, dx+x, dy+y, (uint8_t)iFG, 0); + bbeiSetPixel(pBBEI, dx+x, dy+y, (uint8_t)iFG); } else { if (iBG >= 0) - obdSetPixel(pOBD, dx+x, dy+y, (uint8_t)iBG, 0); + bbeiSetPixel(pBBEI, dx+x, dy+y, (uint8_t)iBG); } src_mask >>= 1; } // for x } - if (pOBD->ucScreen == NULL && ((y & 7) == 7 || y == cy-1)) // dump to LCD + if (pBBEI->ucScreen == NULL && ((y & 7) == 7 || y == cy-1)) // dump to LCD { - obdSetPosition(pOBD, dx, y+dy, 1); - obdWriteDataBlock(pOBD, u8Cache, cx, 1); + bbeiSetPosition(pBBEI, dx, y+dy, cx, 1); + bbeiWriteData(pBBEI, u8Cache, cx); } } // for y - return OBD_SUCCESS; -} /* obdLoadBMP() */ + return BBEI_SUCCESS; +} /* bbeiLoadBMP() */ // // Load a 4-bpp Windows bitmap for a 3-color bitmap // Pass the pointer to the beginning of the BMP file // First pass version assumes a full screen bitmap // -int obdLoadBMP3(OBDISP *pOBD, const uint8_t *pBMP, int dx, int dy) +int bbeiLoadBMP3(BBEIDISP *pBBEI, const uint8_t *pBMP, int dx, int dy) { int16_t i16, cx, cy, bpp; int x, y, iOffBits; // offset to bitmap data @@ -1231,33 +469,33 @@ uint8_t dst_mask; uint8_t bFlipped = 0; uint8_t ucColorMap[16]; - if (!(pOBD->iFlags & OBD_3COLOR) || pOBD->ucScreen == 0) - return OBD_ERROR_NOT_SUPPORTED; // if not 3-color EPD or no back buffer, bye-byte - iDestPitch = pOBD->width; - iRedOff = ((pOBD->height+7)>>3) * iDestPitch; + if (!(pBBEI->iFlags & BBEI_3COLOR) || pBBEI->ucScreen == 0) + return BBEI_ERROR_NOT_SUPPORTED; // if not 3-color EPD or no back buffer, bye-byte + iDestPitch = pBBEI->width; + iRedOff = ((pBBEI->height+7)>>3) * iDestPitch; // Need to avoid pgm_read_word because it can cause an // unaligned address exception on the RP2040 for odd addresses i16 = pgm_read_byte(pBMP); i16 += (pgm_read_byte(pBMP+1)<<8); if (i16 != 0x4d42) // must start with 'BM' - return OBD_ERROR_BAD_DATA; // not a BMP file + return BBEI_ERROR_BAD_DATA; // not a BMP file cx = pgm_read_byte(&pBMP[18]); cx += (pgm_read_byte(&pBMP[19]) << 8); - if (cx + dx > pOBD->width) // must fit on the display - return OBD_ERROR_BAD_PARAMETER; + if (cx + dx > pBBEI->width) // must fit on the display + return BBEI_ERROR_BAD_PARAMETER; cy = pgm_read_byte(&pBMP[22]); cy += (pgm_read_byte(&pBMP[23])<<8); if (cy < 0) cy = -cy; else bFlipped = 1; - if (cy + dy > pOBD->height) // must fit on the display - return OBD_ERROR_BAD_PARAMETER; + if (cy + dy > pBBEI->height) // must fit on the display + return BBEI_ERROR_BAD_PARAMETER; if (pgm_read_byte(&pBMP[30]) != 0) // compression must be NONE - return OBD_ERROR_BAD_DATA; + return BBEI_ERROR_BAD_DATA; bpp = pgm_read_byte(&pBMP[28]); if (bpp != 4) // must be 4 bits per pixel - return OBD_ERROR_BAD_DATA; + return BBEI_ERROR_BAD_DATA; iOffBits = pgm_read_byte(&pBMP[10]); iOffBits += (pgm_read_byte(&pBMP[11])<<8); iColors = pgm_read_byte(&pBMP[46]); // colors used BMP field @@ -1279,15 +517,15 @@ uint8_t ucColorMap[16]; r = pgm_read_byte(&pBMP[iPalOff+2+(x*4)]); uc = (b >> 6) | ((r >> 5) << 2) | ((g >> 5) << 5); if (uc >= 0x1c) { // check for red/white - ucColorMap[x] = ((0xff - uc) < (uc - 0x1c)) ? OBD_WHITE : OBD_RED; + ucColorMap[x] = ((0xff - uc) < (uc - 0x1c)) ? BBEI_WHITE : BBEI_RED; } else { - ucColorMap[x] = ((0x1c - uc) < uc) ? OBD_RED : OBD_BLACK; + ucColorMap[x] = ((0x1c - uc) < uc) ? BBEI_RED : BBEI_BLACK; } } for (y=0; yucScreen[(((y+dy)>>3)*iDestPitch)+dx]; + d = &pBBEI->ucScreen[(((y+dy)>>3)*iDestPitch)+dx]; s = (uint8_t *)&pBMP[iOffBits+(y*iPitch)]; for (x=0; x> 4]; // left pixel - if (uc == OBD_BLACK) + if (uc == BBEI_BLACK) d[x] |= dst_mask; - else if (uc == OBD_RED) + else if (uc == BBEI_RED) d[x+iRedOff] |= dst_mask; // we made it white already uc = ucColorMap[b & 0xf]; // right pixel - if (uc == OBD_BLACK) + if (uc == BBEI_BLACK) d[x+1] |= dst_mask; - else if (uc == OBD_RED) + else if (uc == BBEI_RED) d[x+1+iRedOff] |= dst_mask; } // for x } // for y - return OBD_SUCCESS; -} /* obdLoadBMP3() */ -// -// Draw 1 or 2 planes of raw image into a specific spot -// in e-paper memory -// -int obdDrawEPDGFX(OBDISP *pOBD, int xdest, int ydest, int cx, int cy, uint8_t *pPlane0, uint8_t *pPlane1) -{ - int y, iPitch = ((cx + 7)/8); - uint8_t *s; - - if (xdest < 0 || xdest >= pOBD->native_width || xdest+cx > pOBD->native_width || ydest < 0 || ydest > pOBD->native_height || ydest+cy > pOBD->native_height || pPlane0 == NULL) - return OBD_ERROR_BAD_PARAMETER; // invalid - // Write plane 0 - pOBD->iFG = OBD_BLACK; // make sure we write to plane 0 - EPDSetPosition(pOBD, xdest, ydest, cx, cy); - s = pPlane0; - for (y=0; yiFG = OBD_RED; // write to plane 1 - EPDSetPosition(pOBD, xdest, ydest, cx, cy); - s = pPlane1; - for (y=0; yiCursorX = x; - pOBD->iCursorY = y; -} /* obdSetCursor() */ -// -// Advance to the next line -// -void obdNextLine(OBDISP *pOBD) -{ - pOBD->iCursorX = 0; - pOBD->iCursorY++; -} /* obdNextLine() */ + pBBEI->iCursorX = x; + pBBEI->iCursorY = y; +} /* bbeiSetCursor() */ // // Turn text wrap on or off for the oldWriteString() function // -void obdSetTextWrap(OBDISP *pOBD, int bWrap) +void bbeiSetTextWrap(BBEIDISP *pBBEI, int bWrap) { - pOBD->wrap = bWrap; -} /* obdSetTextWrap() */ -// -// Draw a string with a fractional scale in both dimensions -// the scale is a 16-bit integer with and 8-bit fraction and 8-bit mantissa -// To draw at 1x scale, set the scale factor to 256. To draw at 2x, use 512 -// The output must be drawn into a memory buffer, not directly to the display -// -int obdScaledString(OBDISP *pOBD, int x, int y, char *szMsg, int iSize, int iColor, int iXScale, int iYScale, int iRotation) -{ -uint32_t row, col, dx, dy; -uint32_t sx, sy; -uint8_t c, uc, color; -const uint8_t *s; -uint8_t ucTemp[16]; -int tx, ty, bit, iFontOff; -int iFontWidth; - - if (iXScale == 0 || iYScale == 0 || szMsg == NULL || pOBD == NULL || pOBD->ucScreen == NULL || x < 0 || y < 0 || x >= pOBD->width-1 || y >= pOBD->height-1) - return OBD_ERROR_BAD_PARAMETER; // invalid display structure - if (iSize != FONT_8x8 && iSize != FONT_6x8) - return OBD_ERROR_BAD_PARAMETER; // only on the small fonts (for now) - iFontWidth = (iSize == FONT_6x8) ? 6:8; - s = (iSize == FONT_6x8) ? ucSmallFont : ucFont; - dx = (iFontWidth * iXScale) >> 8; // width of each character - dy = (8 * iYScale) >> 8; // height of each character - sx = 65536 / iXScale; // turn the scale into an accumulator value - sy = 65536 / iYScale; - while (*szMsg) { - c = *szMsg++; // debug - start with normal font - iFontOff = (int)(c-32) * (iFontWidth-1); - // we can't directly use the pointer to FLASH memory, so copy to a local buffer - ucTemp[0] = 0; // first column is blank - memcpy_P(&ucTemp[1], &s[iFontOff], iFontWidth-1); -// if (iColor == OBD_WHITE) InvertBytes(ucTemp, iFontWidth); - col = 0; - for (tx=0; tx<(int)dx; tx++) { - row = 0; - uc = ucTemp[col >> 8]; - for (ty=0; ty<(int)dy; ty++) { - int nx=0, ny=0; - bit = row >> 8; - color = (uc & (1 << bit)); // set or clear the pixel - switch (iRotation) { - case ROT_0: - nx = x + tx; - ny = y + ty; - break; - case ROT_90: - nx = x - ty; - ny = y + tx; - break; - case ROT_180: - nx = x - tx; - ny = y - ty; - break; - case ROT_270: - nx = x + ty; - ny = y - tx; - break; - } // switch on rotation direction - // plot the pixel if it's within the image boundaries - if (color) - obdSetPixel(pOBD, nx, ny, pOBD->iFG, iColor); - row += sy; // add fractional increment to source row of character - } // for ty - col += sx; // add fractional increment to source column - } // for tx - // update the 'cursor' position - switch (iRotation) { - case ROT_0: - x += dx; - break; - case ROT_90: - y += dx; - break; - case ROT_180: - x -= dx; - break; - case ROT_270: - y -= dx; - break; - } // switch on rotation - } // while (*szMsg) - return OBD_SUCCESS; -} /* obdScaledString() */ - + pBBEI->wrap = bWrap; +} /* bbeiSetTextWrap() */ // // Draw a string of normal (8x8), small (6x8) or large (16x32) characters // At the given col+row // -int obdWriteString(OBDISP *pOBD, int iScroll, int x, int y, char *szMsg, int iSize, int iColor, int bRender) +int bbeiWriteString(BBEIDISP *pBBEI, int iScroll, int x, int y, char *szMsg, int iSize, int iColor, int bRender) { int i, iFontOff, iLen, iFontSkip; unsigned char c, *s; int iOldFG; // old fg color to make sure red works - - if (pOBD == NULL) return OBD_ERROR_BAD_PARAMETER; - if (pOBD->type == DISPLAY_COMMANDS) { // encode this as a command sequence - uint8_t *d = pOBD->ucScreen; - iLen = (int)strlen(szMsg); - obdWriteCmdByte(pOBD, OBD_DRAWTEXT | ((iColor & 1) << 7) | ((iSize & 7) << 4)); - obdWriteCmdByte(pOBD, (uint8_t) iLen); - obdWriteCmdInt(pOBD, x); - obdWriteCmdInt(pOBD, y); - i = pOBD->iScreenOffset; - memcpy(&d[i], szMsg, iLen); - i += iLen; - pOBD->iScreenOffset = i; // store new length - return OBD_SUCCESS; // done - } + uint8_t u8Temp[128]; + + if (pBBEI == NULL) return BBEI_ERROR_BAD_PARAMETER; // e-paper color is inverted compared to OLED/LCD. If we're in bufferless mode, we'll need to // invert the requested color - if (pOBD->type >= EPD42_400x300 && !pOBD->ucScreen) { + if (pBBEI->type >= EPD42_400x300 && !pBBEI->ucScreen) { iColor = 1 - iColor; // invert the color } if (x == -1 || y == -1) // use the cursor position { - x = pOBD->iCursorX; y = pOBD->iCursorY; + x = pBBEI->iCursorX; y = pBBEI->iCursorY; } else { - pOBD->iCursorX = x; pOBD->iCursorY = y; + pBBEI->iCursorX = x; pBBEI->iCursorY = y; } - if (pOBD->iCursorX >= pOBD->width || pOBD->iCursorY >= pOBD->height) - return OBD_ERROR_BAD_PARAMETER; // can't draw off the display + if (pBBEI->iCursorX >= pBBEI->width || pBBEI->iCursorY >= pBBEI->height) + return BBEI_ERROR_BAD_PARAMETER; // can't draw off the display - obdSetPosition(pOBD, pOBD->iCursorX, pOBD->iCursorY, bRender); - iOldFG = pOBD->iFG; // save old fg color - if (iColor >= OBD_YELLOW) { - pOBD->iFG = iColor; + bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY, 8, 16); + iOldFG = pBBEI->iFG; // save old fg color + if (iColor >= BBEI_YELLOW) { + pBBEI->iFG = iColor; } if (iSize == FONT_8x8) // 8x8 font { i = 0; iFontSkip = iScroll & 7; // number of columns to initially skip - while (pOBD->iCursorX < pOBD->width && szMsg[i] != 0 && pOBD->iCursorY < pOBD->height) + while (pBBEI->iCursorX < pBBEI->width && szMsg[i] != 0 && pBBEI->iCursorY < pBBEI->height) { if (iScroll < 8) // only display visible characters { @@ -1510,79 +610,32 @@ int iOldFG; // old fg color to make sure red works // we can't directly use the pointer to FLASH memory, so copy to a local buffer u8Temp[0] = 0; // first column is blank memcpy_P(&u8Temp[1], &ucFont[iFontOff], 7); - if (iColor == OBD_WHITE) InvertBytes(u8Temp, 8); + if (iColor == BBEI_WHITE) InvertBytes(u8Temp, 8); iLen = 8 - iFontSkip; - if (pOBD->iCursorX + iLen > pOBD->width) // clip right edge - iLen = pOBD->width - pOBD->iCursorX; - obdCachedWrite(pOBD, &u8Temp[iFontSkip], iLen, bRender); -// obdWriteDataBlock(pOBD, &ucTemp[iFontSkip], iLen, bRender); // write character pattern - pOBD->iCursorX += iLen; - if (pOBD->iCursorX >= pOBD->width-7 && pOBD->wrap) // word wrap enabled? + if (pBBEI->iCursorX + iLen > pBBEI->width) // clip right edge + iLen = pBBEI->width - pBBEI->iCursorX; + bbeiWriteData(pBBEI, &u8Temp[iFontSkip], iLen); +// obdWriteDataBlock(pBBEI, &ucTemp[iFontSkip], iLen, bRender); // write character pattern + pBBEI->iCursorX += iLen; + if (pBBEI->iCursorX >= pBBEI->width-7 && pBBEI->wrap) // word wrap enabled? { - obdCachedFlush(pOBD, bRender); - pOBD->iCursorX = 0; // start at the beginning of the next line - pOBD->iCursorY+=8; - obdSetPosition(pOBD, pOBD->iCursorX, pOBD->iCursorY, bRender); + pBBEI->iCursorX = 0; // start at the beginning of the next line + pBBEI->iCursorY+=8; + bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY, 8, 8); } iFontSkip = 0; } iScroll -= 8; i++; } // while - obdCachedFlush(pOBD, bRender); // write any remaining data - pOBD->iFG = iOldFG; // restore color - return OBD_SUCCESS; + pBBEI->iFG = iOldFG; // restore color + return BBEI_SUCCESS; } // 8x8 -#ifndef WIMPY_MCU - else if (iSize == FONT_16x32) - { - i = 0; - iFontSkip = iScroll & 15; // number of columns to initially skip - while (pOBD->iCursorX < pOBD->width && pOBD->iCursorY < pOBD->height && szMsg[i] != 0) - { - if (iScroll < 16) // if characters are visible - { - s = (unsigned char *)&ucBigFont[(unsigned char)(szMsg[i]-32)*64]; - iLen = 16 - iFontSkip; - if (pOBD->iCursorX + iLen > pOBD->width) // clip right edge - iLen = pOBD->width - pOBD->iCursorX; - // we can't directly use the pointer to FLASH memory, so copy to a local buffer - obdSetPosition(pOBD, pOBD->iCursorX, pOBD->iCursorY, bRender); - memcpy_P(u8Cache, s, 16); - if (iColor == OBD_WHITE) InvertBytes(u8Cache, 16); - obdWriteDataBlock(pOBD, &u8Cache[iFontSkip], iLen, bRender); // write character pattern - obdSetPosition(pOBD, pOBD->iCursorX, pOBD->iCursorY+8, bRender); - memcpy_P(u8Cache, s+16, 16); - if (iColor == OBD_WHITE) InvertBytes(u8Cache, 16); - obdWriteDataBlock(pOBD, &u8Cache[iFontSkip], iLen, bRender); // write character pattern - obdSetPosition(pOBD, pOBD->iCursorX, pOBD->iCursorY+16, bRender); - memcpy_P(u8Cache, s+32, 16); - if (iColor == OBD_WHITE) InvertBytes(u8Cache, 16); - obdWriteDataBlock(pOBD, &u8Cache[iFontSkip], iLen, bRender); // write character pattern - obdSetPosition(pOBD, pOBD->iCursorX, pOBD->iCursorY+24, bRender); - memcpy_P(u8Cache, s+48, 16); - if (iColor == OBD_WHITE) InvertBytes(u8Cache, 16); - obdWriteDataBlock(pOBD, &u8Cache[iFontSkip], iLen, bRender); // write character pattern - pOBD->iCursorX += iLen; - if (pOBD->iCursorX >= pOBD->width-15 && pOBD->wrap) // word wrap enabled? - { - pOBD->iCursorX = 0; // start at the beginning of the next line - pOBD->iCursorY+=32; - } - iFontSkip = 0; - } // if character visible from scrolling - iScroll -= 16; - i++; - } // while - pOBD->iFG = iOldFG; // restore color - return OBD_SUCCESS; - } // 16x32 -#endif // !WIMPY_MCU else if (iSize == FONT_16x16) // 8x8 stretched to 16x16 { i = 0; iFontSkip = iScroll & 15; // number of columns to initially skip - while (pOBD->iCursorX < pOBD->width && pOBD->iCursorY < pOBD->height && szMsg[i] != 0) + while (pBBEI->iCursorX < pBBEI->width && pBBEI->iCursorY < pBBEI->height && szMsg[i] != 0) { // stretch the 'normal' font instead of using the big font if (iScroll < 16) // if characters are visible @@ -1593,7 +646,7 @@ int iOldFG; // old fg color to make sure red works s = (unsigned char *)&ucFont[(int)c*7]; u8Temp[0] = 0; // first column is blank memcpy_P(&u8Temp[1], s, 7); - if (iColor == OBD_WHITE) + if (iColor == BBEI_WHITE) InvertBytes(u8Temp, 8); // Stretch the font to double width + double height memset(&u8Temp[8], 0, 32); // write 32 new bytes @@ -1617,32 +670,32 @@ int iOldFG; // old fg color to make sure red works pDest[17] = uc2; } iLen = 16 - iFontSkip; - if (pOBD->iCursorX + iLen > pOBD->width) // clip right edge - iLen = pOBD->width - pOBD->iCursorX; - obdSetPosition(pOBD, pOBD->iCursorX, pOBD->iCursorY, bRender); - obdWriteDataBlock(pOBD, &u8Temp[8+iFontSkip], iLen, bRender); - obdSetPosition(pOBD, pOBD->iCursorX, pOBD->iCursorY+8, bRender); - obdWriteDataBlock(pOBD, &u8Temp[24+iFontSkip], iLen, bRender); - pOBD->iCursorX += iLen; - if (pOBD->iCursorX >= pOBD->width-15 && pOBD->wrap) // word wrap enabled? + if (pBBEI->iCursorX + iLen > pBBEI->width) // clip right edge + iLen = pBBEI->width - pBBEI->iCursorX; + bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY, 8, iLen); + bbeiWriteData(pBBEI, &u8Temp[8+iFontSkip], iLen); + bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY+8, 8, iLen); + bbeiWriteData(pBBEI, &u8Temp[24+iFontSkip], iLen); + pBBEI->iCursorX += iLen; + if (pBBEI->iCursorX >= pBBEI->width-15 && pBBEI->wrap) // word wrap enabled? { - pOBD->iCursorX = 0; // start at the beginning of the next line - pOBD->iCursorY += 16; - obdSetPosition(pOBD, pOBD->iCursorX, pOBD->iCursorY, bRender); + pBBEI->iCursorX = 0; // start at the beginning of the next line + pBBEI->iCursorY += 16; + bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY, 8, 16); } iFontSkip = 0; } // if characters are visible iScroll -= 16; i++; } // while - pOBD->iFG = iOldFG; // restore color - return OBD_SUCCESS; + pBBEI->iFG = iOldFG; // restore color + return BBEI_SUCCESS; } // 16x16 else if (iSize == FONT_12x16) // 6x8 stretched to 12x16 { i = 0; iFontSkip = iScroll % 12; // number of columns to initially skip - while (pOBD->iCursorX < pOBD->width && pOBD->iCursorY < pOBD->height && szMsg[i] != 0) + while (pBBEI->iCursorX < pBBEI->width && pBBEI->iCursorY < pBBEI->height && szMsg[i] != 0) { // stretch the 'normal' font instead of using the big font if (iScroll < 12) // if characters are visible @@ -1653,7 +706,7 @@ int iOldFG; // old fg color to make sure red works s = (unsigned char *)&ucSmallFont[(int)c*5]; u8Temp[0] = 0; // first column is blank memcpy_P(&u8Temp[1], s, 6); - if (iColor == OBD_WHITE) + if (iColor == BBEI_WHITE) InvertBytes(u8Temp, 6); // Stretch the font to double width + double height memset(&u8Temp[6], 0, 24); // write 24 new bytes @@ -1691,7 +744,7 @@ int iOldFG; // old fg color to make sure red works { if (ty < 3) // top half { - if (iColor == OBD_WHITE) { + if (iColor == BBEI_WHITE) { pDest[1] &= ~(1 << ((ty * 2)+1)); pDest[2] &= ~(1 << ((ty * 2)+1)); pDest[1] &= ~(1 << ((ty+1) * 2)); @@ -1705,7 +758,7 @@ int iOldFG; // old fg color to make sure red works } else if (ty == 3) // on the border { - if (iColor == OBD_WHITE) { + if (iColor == BBEI_WHITE) { pDest[1] &= ~0x80; pDest[2] &= ~0x80; pDest[13] &= ~1; pDest[14] &= ~1; } else { @@ -1715,7 +768,7 @@ int iOldFG; // old fg color to make sure red works } else // bottom half { - if (iColor == OBD_WHITE) { + if (iColor == BBEI_WHITE) { pDest[13] &= ~(1 << (2*(ty-4)+1)); pDest[14] &= ~(1 << (2*(ty-4)+1)); pDest[13] &= ~(1 << ((ty-3) * 2)); @@ -1732,7 +785,7 @@ int iOldFG; // old fg color to make sure red works { if (ty < 4) // top half { - if (iColor == OBD_WHITE) { + if (iColor == BBEI_WHITE) { pDest[1] &= ~(1 << ((ty * 2)+1)); pDest[2] &= ~(1 << ((ty+1) * 2)); } else { @@ -1742,7 +795,7 @@ int iOldFG; // old fg color to make sure red works } else { - if (iColor == OBD_WHITE) { + if (iColor == BBEI_WHITE) { pDest[13] &= ~(1 << (2*(ty-4)+1)); pDest[14] &= ~(1 << ((ty-3) * 2)); } else { @@ -1755,32 +808,32 @@ int iOldFG; // old fg color to make sure red works } } iLen = 12 - iFontSkip; - if (pOBD->iCursorX + iLen > pOBD->width) // clip right edge - iLen = pOBD->width - pOBD->iCursorX; - obdSetPosition(pOBD, pOBD->iCursorX, pOBD->iCursorY, bRender); - obdWriteDataBlock(pOBD, &u8Temp[6+iFontSkip], iLen, bRender); - obdSetPosition(pOBD, pOBD->iCursorX, pOBD->iCursorY+8, bRender); - obdWriteDataBlock(pOBD, &u8Temp[18+iFontSkip], iLen, bRender); - pOBD->iCursorX += iLen; - if (pOBD->iCursorX >= pOBD->width-11 && pOBD->wrap) // word wrap enabled? + if (pBBEI->iCursorX + iLen > pBBEI->width) // clip right edge + iLen = pBBEI->width - pBBEI->iCursorX; + bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY, 8, iLen); + bbeiWriteData(pBBEI, &u8Temp[6+iFontSkip], iLen); + bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY+8, 8, iLen); + bbeiWriteData(pBBEI, &u8Temp[18+iFontSkip], iLen); + pBBEI->iCursorX += iLen; + if (pBBEI->iCursorX >= pBBEI->width-11 && pBBEI->wrap) // word wrap enabled? { - pOBD->iCursorX = 0; // start at the beginning of the next line - pOBD->iCursorY += 16; - obdSetPosition(pOBD, pOBD->iCursorX, pOBD->iCursorY, bRender); + pBBEI->iCursorX = 0; // start at the beginning of the next line + pBBEI->iCursorY += 16; + bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY, 8, 16); } iFontSkip = 0; } // if characters are visible iScroll -= 12; i++; } // while - pOBD->iFG = iOldFG; // restore color - return OBD_SUCCESS; + pBBEI->iFG = iOldFG; // restore color + return BBEI_SUCCESS; } // 12x16 else if (iSize == FONT_6x8) { i = 0; iFontSkip = iScroll % 6; - while (pOBD->iCursorX < pOBD->width && pOBD->iCursorY < pOBD->height && szMsg[i] != 0) + while (pBBEI->iCursorX < pBBEI->width && pBBEI->iCursorY < pBBEI->height && szMsg[i] != 0) { if (iScroll < 6) // if characters are visible { @@ -1788,36 +841,35 @@ int iOldFG; // old fg color to make sure red works // we can't directly use the pointer to FLASH memory, so copy to a local buffer u8Temp[0] = 0; // first column is blank memcpy_P(&u8Temp[1], &ucSmallFont[(int)c*5], 5); - if (iColor == OBD_WHITE) InvertBytes(u8Temp, 6); + if (iColor == BBEI_WHITE) InvertBytes(u8Temp, 6); iLen = 6 - iFontSkip; - if (pOBD->iCursorX + iLen > pOBD->width) // clip right edge - iLen = pOBD->width - pOBD->iCursorX; - obdCachedWrite(pOBD, &u8Temp[iFontSkip], iLen, bRender); -// obdWriteDataBlock(pOBD, &ucTemp[iFontSkip], iLen, bRender); // write character pattern - pOBD->iCursorX += iLen; + if (pBBEI->iCursorX + iLen > pBBEI->width) // clip right edge + iLen = pBBEI->width - pBBEI->iCursorX; + bbeiWriteData(pBBEI, &u8Temp[iFontSkip], iLen); +// obdWriteDataBlock(pBBEI, &ucTemp[iFontSkip], iLen, bRender); // write character pattern + pBBEI->iCursorX += iLen; iFontSkip = 0; - if (pOBD->iCursorX >= pOBD->width-5 && pOBD->wrap) // word wrap enabled? + if (pBBEI->iCursorX >= pBBEI->width-5 && pBBEI->wrap) // word wrap enabled? { - obdCachedFlush(pOBD, bRender); - pOBD->iCursorX = 0; // start at the beginning of the next line - pOBD->iCursorY +=8; - obdSetPosition(pOBD, pOBD->iCursorX, pOBD->iCursorY, bRender); + pBBEI->iCursorX = 0; // start at the beginning of the next line + pBBEI->iCursorY +=8; + bbeiSetPosition(pBBEI, pBBEI->iCursorX, pBBEI->iCursorY, 8, 16); } } // if characters are visible iScroll -= 6; i++; } - obdCachedFlush(pOBD, bRender); // write any remaining data - pOBD->iFG = iOldFG; // restore color - return OBD_SUCCESS; + pBBEI->iFG = iOldFG; // restore color + return BBEI_SUCCESS; } // 6x8 - pOBD->iFG = iOldFG; // restore color - return OBD_ERROR_BAD_PARAMETER; // invalid size -} /* obdWriteString() */ + pBBEI->iFG = iOldFG; // restore color + return BBEI_ERROR_BAD_PARAMETER; // invalid size +} /* bbeiWriteString() */ +#ifdef FUTURE // // Get the width of text in a custom font // -void obdGetStringBox(GFXfont *pFont, char *szMsg, int *width, int *top, int *bottom) +void bbeiGetStringBox(GFXfont *pFont, char *szMsg, int *width, int *top, int *bottom) { int cx = 0; unsigned int c, i = 0; @@ -1839,13 +891,13 @@ int miny, maxy; *width = cx; *top = miny; *bottom = maxy; -} /* obdGetStringBox() */ +} /* bbeiGetStringBox() */ // // Draw a string of characters in a custom font // A back buffer must be defined // -int obdWriteStringCustom(OBDISP *pOBD, GFXfont *pFont, int x, int y, char *szMsg, uint8_t ucColor) +int bbeiWriteStringCustom(BBEIDISP *pBBEI, GFXfont *pFont, int x, int y, char *szMsg, uint8_t ucColor) { int i, end_y, dx, dy, tx, ty, iBitOff; unsigned int c; @@ -1854,48 +906,31 @@ GFXfont font; GFXglyph glyph, *pGlyph; int iPitch, iRedOffset = 0; - if (pOBD == NULL || pFont == NULL) - return OBD_ERROR_BAD_PARAMETER; - if (pOBD->ucScreen == NULL && pOBD->type >= EPD42_400x300) { + if (pBBEI == NULL || pFont == NULL) + return BBEI_ERROR_BAD_PARAMETER; + if (pBBEI->ucScreen == NULL && pBBEI->type >= EPD42_400x300) { // EPD direct draw mode; colors are inverted - if (ucColor == OBD_BLACK) + if (ucColor == BBEI_BLACK) ucColor = 1-ucColor; ucFill = 0xff; } if (x == -1) - x = pOBD->iCursorX; + x = pBBEI->iCursorX; if (y == -1) - y = pOBD->iCursorY; - if (ucColor >= OBD_YELLOW && pOBD->iFlags & (OBD_3COLOR | OBD_4COLOR)) { + y = pBBEI->iCursorY; + if (ucColor >= BBEI_YELLOW && pBBEI->iFlags & (BBEI_3COLOR | BBEI_4COLOR)) { // use the second half of the image buffer - iRedOffset = pOBD->width * ((pOBD->height+7)/8); + iRedOffset = pBBEI->width * ((pBBEI->height+7)/8); ucFill = 0x00; - ucColor = OBD_BLACK; + ucColor = BBEI_BLACK; } - if (pOBD->type == DISPLAY_COMMANDS) { // encode this as a command sequence - uint8_t *d = pOBD->ucScreen; - dx = (int)strlen(szMsg); - // The font pointer is really the integer font index - i = (int)pFont; - if (i >= FONT_CUSTOM0) { // must be a valid index - obdWriteCmdByte(pOBD, OBD_DRAWTEXT | ((ucColor & 1) << 7) | ((i & 7) << 4)); - obdWriteCmdByte(pOBD, (uint8_t) dx); - obdWriteCmdInt(pOBD, x); - obdWriteCmdInt(pOBD, y); - i = pOBD->iScreenOffset; - memcpy(&d[i], szMsg, dx); - i += dx; - pOBD->iScreenOffset = i; // store new length - } - return OBD_SUCCESS; // done - } - iPitch = pOBD->width; + iPitch = pBBEI->width; // in case of running on Harvard architecture, get copy of data from FLASH memcpy_P(&font, pFont, sizeof(font)); pGlyph = &glyph; i = 0; - while (szMsg[i] && x < pOBD->width) + while (szMsg[i] && x < pBBEI->width) { c = szMsg[i++]; if (c < font.first || c > font.last) // undefined character @@ -1914,13 +949,13 @@ int iPitch, iRedOffset = 0; iBitOff += (pGlyph->width * (-dy)); dy = 0; } - if (!pOBD->ucScreen) { + if (!pBBEI->ucScreen) { memset(u8Cache, ucFill, sizeof(u8Cache)); } - for (ty=dy; tyheight; ty++) { + for (ty=dy; tyheight; ty++) { ucMask = 1<<(ty & 7); // destination bit number for this line - if (pOBD->ucScreen) { - d = &pOBD->ucScreen[iRedOffset + (ty >> 3) * iPitch + dx]; // internal buffer dest + if (pBBEI->ucScreen) { + d = &pBBEI->ucScreen[iRedOffset + (ty >> 3) * iPitch + dx]; // internal buffer dest } else { d = u8Cache; // no ram; buffer 8 lines at a time } @@ -1931,14 +966,14 @@ int iPitch, iRedOffset = 0; iBitOff += bits; // because of a clipped line uc <<= (8-bits); } // if we ran out of bits - if ((dx+tx) < pOBD->width) { // foreground pixel + if ((dx+tx) < pBBEI->width) { // foreground pixel if (uc & 0x80) { - if (ucColor == OBD_BLACK) + if (ucColor == BBEI_BLACK) d[tx] |= ucMask; else d[tx] &= ~ucMask; } else { - if (ucColor == OBD_BLACK) + if (ucColor == BBEI_BLACK) d[tx] &= ~ucMask; else d[tx] |= ucMask; @@ -1947,169 +982,76 @@ int iPitch, iRedOffset = 0; bits--; // next bit uc <<= 1; } // for x - if (!pOBD->ucScreen && (ucMask == 0x80 || ty == end_y-1)) { // dump this line - obdSetPosition(pOBD, dx, (ty & 0xfff8), 1); - obdWriteDataBlock(pOBD, u8Cache, pGlyph->width, 1); + if (!pBBEI->ucScreen && (ucMask == 0x80 || ty == end_y-1)) { // dump this line + bbeiSetPosition(pBBEI, dx, (ty & 0xfff8), pGlyph->width*8, 1); + bbeiWriteData(pBBEI, u8Cache, pGlyph->width); memset(u8Cache, ucFill, sizeof(u8Cache)); // NB: assume no DMA } } // for y x += pGlyph->xAdvance; // width of this character } // while drawing characters - pOBD->iCursorX = x; - pOBD->iCursorY = y; - return OBD_SUCCESS; -} /* obdWriteStringCustom() */ - + pBBEI->iCursorX = x; + pBBEI->iCursorY = y; + return BBEI_SUCCESS; +} /* bbeiWriteStringCustom() */ +#endif // FUTURE // -// Render a sprite/rectangle of pixels from a provided buffer to the display. -// The row values refer to byte rows, not pixel rows due to the memory -// layout of OLEDs and LCDs. -// returns 0 for success, -1 for invalid parameter +// Fill the display with a color or byte pattern +// e.g. all black (0x00) or all white (0xff) +// if there is no backing buffer, write directly to +// the EPD's framebuffer // -int obdDrawGFX(OBDISP *pOBD, uint8_t *pBuffer, int iSrcCol, int iSrcRow, int iDestCol, int iDestRow, int iWidth, int iHeight, int iSrcPitch) +void bbeiFill(BBEIDISP *pBBEI, unsigned char ucData, int iPlane) { - int y; +uint8_t uc; +int y, iSize, iPitch; +uint8_t ucCMD1, ucCMD2; - if (iSrcCol < 0 || iSrcCol >= pOBD->width || iSrcRow < 0 || iSrcRow > (pOBD->height/8)-1 || iDestCol < 0 || iDestCol >= pOBD->width || iDestRow < 0 || iDestRow >= (pOBD->height >> 3) || iSrcPitch <= 0) - return OBD_ERROR_BAD_PARAMETER; // invalid + if (pBBEI == NULL) return; - for (y=iSrcRow; ytype == DISPLAY_COMMANDS) { // encode this as a command sequence - obdWriteCmdByte(pOBD, OBD_FILL); - obdWriteCmdByte(pOBD, ucData); - return; - } - - pOBD->iCursorX = pOBD->iCursorY = 0; - if (pOBD->type == LCD_VIRTUAL || pOBD->type >= SHARP_144x168) // special display types - { - if (pOBD->ucScreen) { - int iSize = pOBD->width * ((pOBD->height+7)/8); - if (ucData == OBD_WHITE) { - memset(pOBD->ucScreen, ucData, iSize); - if (pOBD->iFlags & (OBD_3COLOR | OBD_4COLOR)) - memset(&pOBD->ucScreen[iSize], ucData, iSize); - } else if (ucData == OBD_BLACK) { - memset(pOBD->ucScreen, 0xff, iSize); - if (pOBD->iFlags & (OBD_3COLOR | OBD_4COLOR)) - memset(&pOBD->ucScreen[iSize], 0, iSize); - } else if (ucData == OBD_RED) { - memset(pOBD->ucScreen, 0, iSize); - if (pOBD->iFlags & (OBD_3COLOR | OBD_4COLOR)) - memset(&pOBD->ucScreen[iSize], 0xff, iSize); - } else { // write pattern - if (pOBD->iFlags & (OBD_3COLOR | OBD_4COLOR)) iSize *= 2; - memset(pOBD->ucScreen, ucData, iSize); - } - } -#ifndef MEMORY_ONLY - else if (pOBD->type >= EPD42_400x300) { - uint8_t ucPattern1 = 0xff, ucPattern2 = 0xff; // assume white - uint8_t ucRAM1, ucRAM2; - int iOldRotation; - if (pOBD->iFlags & (OBD_3COLOR | OBD_4COLOR)) { - ucPattern2 = 0x00; // red plane is not inverted - if (ucData == OBD_BLACK) - ucPattern1 = 0x00; - else if (ucData == OBD_RED) - ucPattern2 = 0xff; - } else { // black/white - if (ucData == OBD_BLACK) - ucPattern1 = ucPattern2 = 0x00; - } - ucRAM1 = (pOBD->chip_type == OBD_CHIP_UC8151) ? (uint8_t)UC8151_DTM1 : (uint8_t)SSD1608_WRITE_RAM; - ucRAM2 = (pOBD->chip_type == OBD_CHIP_UC8151) ? (uint8_t)UC8151_DTM2 : (uint8_t)SSD1608_WRITE_ALTRAM; - // Force 0 rotation because bufferless operation - // will miss pixel rows not a multiple of 8 - // when rotated 90 (e.g. 122x250 resolution) - iOldRotation = pOBD->iOrientation; - pOBD->iOrientation = 0; - EPDSetPosition(pOBD, 0,0,pOBD->native_width, pOBD->native_height); - if (pOBD->type == EPD579_792x272) { - EPDFill(pOBD, ucRAM1, ucPattern1); - EPDFill(pOBD, ucRAM2, ~ucPattern1); - EPDFill(pOBD, ucRAM1 | 0x80, ucPattern1); - EPDFill(pOBD, ucRAM2 | 0x80, ~ucPattern1); - } else if (pOBD->type == EPD74R_640x384) { - if (ucData == OBD_BLACK) - ucPattern1 = 0; - else if (ucData == OBD_WHITE) - ucPattern1 = 0x33; - else ucPattern1 = 0x44; // red - EPDFill(pOBD, 0x10, ucPattern1); - } else { - EPDFill(pOBD, ucRAM1, ucPattern1); - EPDFill(pOBD, ucRAM2, ucPattern2); - } - pOBD->iOrientation = iOldRotation; - } -#endif // !MEMORY_ONLY - return; - } - if (pOBD->iOrientation == 0 || pOBD->iOrientation == 180) { - iLines = (pOBD->height+7) >> 3; - iCols = pOBD->width; - } else { // rotated - iLines = (pOBD->width+7) >> 3; - iCols = pOBD->height; - } - memset(u8Cache, ucData, iCols); - - if (bRender) { // write to the physical display if render = true - for (y=0; yucScreen) { - memset(pOBD->ucScreen, ucData, (pOBD->width * pOBD->height)/8); + pBBEI->iCursorX = pBBEI->iCursorY = 0; + iPitch = ((pBBEI->native_width+7)/8); + iSize = pBBEI->native_height * iPitch; + uc = ucData; + if (ucData == BBEI_WHITE) uc = 0xff; + else if (ucData == BBEI_BLACK) uc = 0; + if (pBBEI->ucScreen) { // there's a local framebuffer, use it + memset(pBBEI->ucScreen, uc, iSize); + } else { // write directly to the EPD's framebuffer + if (pBBEI->chip_type == BBEI_CHIP_UC81xx) { + ucCMD1 = UC8151_DTM1; + ucCMD2 = UC8151_DTM2; + } else { + ucCMD1 = SSD1608_WRITE_RAM; + ucCMD2 = SSD1608_WRITE_ALTRAM; + } + // Write one or both memory planes to the EPD + if (iPlane == PLANE_0 || iPlane == PLANE_DUPLICATE) { // write to first plane + bbeiSetPosition(pBBEI, 0,0, pBBEI->native_width, pBBEI->native_height); + bbeiWriteCmd(pBBEI, ucCMD1); + for (y=0; ynative_height; y++) { + memset(u8Cache, uc, iPitch); // the data is overwritten after each write + bbeiWriteData(pBBEI, u8Cache, iPitch); + } // for y + } + if (iPlane == PLANE_1 || iPlane == PLANE_DUPLICATE) { // write to first plane + bbeiSetPosition(pBBEI, 0,0, pBBEI->native_width, pBBEI->native_height); + bbeiWriteCmd(pBBEI, ucCMD2); + for (y=0; ynative_height; y++) { + memset(u8Cache, uc, iPitch); // the data is overwritten after each write + bbeiWriteData(pBBEI, u8Cache, iPitch); + } // for y + } } -} /* obdFill() */ +} /* bbeiFill() */ -// -// Provide or revoke a back buffer for your OLED graphics -// This allows you to manage the RAM used by ss_oled on tiny -// embedded platforms like the ATmega series -// Pass NULL to revoke the buffer. Make sure you provide a buffer -// large enough for your display (e.g. 128x64 needs 1K - 1024 bytes) -// -void obdSetBackBuffer(OBDISP *pOBD, uint8_t *pBuffer) +void bbeiAllocBuffer(BBEIDISP *pBBEI) { - if (pOBD == NULL || pBuffer == NULL) return; - - pOBD->ucScreen = pBuffer; - pOBD->iScreenOffset = 0; - if (pOBD->type >= LCD_COUNT) // invalid type, set to command output - pOBD->type = DISPLAY_COMMANDS; -} /* obdSetBackBuffer() */ - -void obdAllocBuffer(OBDISP *pOBD) -{ - pOBD->ucScreen = (uint8_t *)malloc(pOBD->width * ((pOBD->height+7)>>3)); - pOBD->iScreenOffset = 0; + pBBEI->ucScreen = (uint8_t *)malloc(pBBEI->width * ((pBBEI->height+7)>>3)); + pBBEI->iScreenOffset = 0; } /* obdAllocBuffer() */ -void obdDrawLine(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, int bRender) +void bbeiDrawLine(BBEIDISP *pBBEI, int x1, int y1, int x2, int y2, uint8_t ucColor) { int temp; int dx = x2 - x1; @@ -2118,35 +1060,26 @@ void obdDrawLine(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, uint8_t *p, *pStart, ucFill = 0, mask, bOld, bNew; int xinc, yinc; int y, x; - int iPitch = pOBD->width; + int iPitch = pBBEI->width; int iRedOffset = 0; - if (pOBD == NULL) return; - if (pOBD->type == DISPLAY_COMMANDS) { // encode this as a command sequence - obdWriteCmdByte(pOBD, OBD_DRAWLINE | ((ucColor & 1) << 4)); - obdWriteCmdInt(pOBD, x1); - obdWriteCmdInt(pOBD, y1); - obdWriteCmdInt(pOBD, x2); - obdWriteCmdInt(pOBD, y2); - return; // done - } + if (pBBEI == NULL) return; - if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0 || x1 >= pOBD->width || x2 >= pOBD->width || y1 >= pOBD->height || y2 >= pOBD->height) + if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0 || x1 >= pBBEI->width || x2 >= pBBEI->width || y1 >= pBBEI->height || y2 >= pBBEI->height) return; - if (ucColor >= OBD_YELLOW && pOBD->iFlags & (OBD_3COLOR | OBD_4COLOR)) { + if (ucColor >= BBEI_YELLOW && pBBEI->iFlags & (BBEI_3COLOR | BBEI_4COLOR)) { // use the second half of the image buffer - iRedOffset = pOBD->width * ((pOBD->height+7)/8); + iRedOffset = pBBEI->width * ((pBBEI->height+7)/8); } - if (!pOBD->ucScreen) { // no back buffer, draw in local buffer - bRender = 1; // make sure it gets transmitted - if (pOBD->type >= EPD42_400x300) { + if (!pBBEI->ucScreen) { // no back buffer, draw in local buffer + if (pBBEI->type >= EPD42_400x300) { // no back buffer on EPD means we may need to invert the color - if (ucColor >= OBD_YELLOW) - ucColor = OBD_BLACK; + if (ucColor >= BBEI_YELLOW) + ucColor = BBEI_BLACK; else ucColor = 1-ucColor; // swap black/white } - if (ucColor == OBD_BLACK) // fill with opposite color + if (ucColor == BBEI_BLACK) // fill with opposite color ucFill = 0; else ucFill = 0xff; @@ -2173,14 +1106,14 @@ void obdDrawLine(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, dy = -dy; yinc = -1; } - if (pOBD->ucScreen) { - p = pStart = &pOBD->ucScreen[iRedOffset + x1 + ((y >> 3) * iPitch)]; // point to current spot in back buffer + if (pBBEI->ucScreen) { + p = pStart = &pBBEI->ucScreen[iRedOffset + x1 + ((y >> 3) * iPitch)]; // point to current spot in back buffer } else { // no back buffer, draw directly to the display RAM p = pStart = u8Cache; } mask = 1 << (y & 7); // current bit offset for(x=x1; x1 <= x2; x1++) { - if (ucColor == OBD_BLACK) + if (ucColor == BBEI_BLACK) *p++ |= mask; // set pixel and increment x pointer else *p++ &= ~mask; @@ -2194,13 +1127,11 @@ void obdDrawLine(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, mask >>= 1; if (mask == 0) // we've moved outside the current row, write the data we changed { - if (bRender) { - obdSetPosition(pOBD, x, y, bRender); - obdWriteDataBlock(pOBD, pStart, (int)(p-pStart), bRender); // write the row we changed - } + bbeiSetPosition(pBBEI, x, y, 8*(int)(p-pStart), 1); + bbeiWriteData(pBBEI, pStart, (int)(p-pStart)); // write the row we changed x = x1+1; // we've already written the byte at x1 y1 = y+yinc; - if (pOBD->ucScreen) { + if (pBBEI->ucScreen) { p += (yinc > 0) ? iPitch : -iPitch; pStart = p; } else { // no buffer @@ -2212,10 +1143,10 @@ void obdDrawLine(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, y += yinc; } } // for x1 - if (p != pStart && bRender) // some data needs to be written + if (p != pStart) // some data needs to be written { - obdSetPosition(pOBD, x, y, bRender); - obdWriteDataBlock(pOBD, pStart, (int)(p-pStart), bRender); + bbeiSetPosition(pBBEI, x, y, 8*(int)(p-pStart), 1); + bbeiWriteData(pBBEI, pStart, (int)(p-pStart)); } } else { // Y major case @@ -2228,8 +1159,8 @@ void obdDrawLine(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, y1 = y2; y2 = temp; } - if (pOBD->ucScreen) { - p = &pOBD->ucScreen[iRedOffset + x1 + ((y1 >> 3) * iPitch)]; // point to current spot in back buffer + if (pBBEI->ucScreen) { + p = &pBBEI->ucScreen[iRedOffset + x1 + ((y1 >> 3) * iPitch)]; // point to current spot in back buffer bOld = bNew = p[0]; // current pixels } else { p = u8Cache; @@ -2245,7 +1176,7 @@ void obdDrawLine(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, xinc = -1; } for(x = x1; y1 <= y2; y1++) { - if (ucColor == OBD_BLACK) + if (ucColor == BBEI_BLACK) bNew |= mask; // set the pixel else bNew &= ~mask; @@ -2256,12 +1187,10 @@ void obdDrawLine(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, if (bOld != bNew) { p[0] = bNew; // save to RAM - if (bRender) { - obdSetPosition(pOBD, x, y1, bRender); - obdWriteDataBlock(pOBD, &bNew, 1, bRender); - } + bbeiSetPosition(pBBEI, x, y1, 8, 1); + bbeiWriteData(pBBEI, &bNew, 1); } // data changed - if (pOBD->ucScreen) { + if (pBBEI->ucScreen) { p += iPitch; // next line bOld = bNew = p[0]; } else { @@ -2275,13 +1204,11 @@ void obdDrawLine(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, if (bOld != bNew) // write the last byte we modified if it changed { p[0] = bNew; // save to RAM - if (bRender) { - obdSetPosition(pOBD, x, y1, bRender); - obdWriteDataBlock(pOBD, &bNew, 1, bRender); - } + bbeiSetPosition(pBBEI, x, y1, 8, 1); + bbeiWriteData(pBBEI, &bNew, 1); } x += xinc; - if (pOBD->ucScreen) { + if (pBBEI->ucScreen) { p += xinc; bOld = bNew = p[0]; } else { @@ -2292,35 +1219,33 @@ void obdDrawLine(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, if (bOld != bNew) // write the last byte we modified if it changed { p[0] = bNew; // save to RAM - if (bRender) { - obdSetPosition(pOBD, x, y2, bRender); - obdWriteDataBlock(pOBD, &bNew, 1, bRender); - } + bbeiSetPosition(pBBEI, x, y2, 8, 1); + bbeiWriteData(pBBEI, &bNew, 1); } } // y major case -} /* obdDrawLine() */ +} /* bbeiDrawLine() */ // // For drawing ellipses, a circle is drawn and the x and y pixels are scaled by a 16-bit integer fraction // This function draws a single pixel and scales its position based on the x/y fraction of the ellipse // -static void DrawScaledPixel(OBDISP *pOBD, int iCX, int iCY, int x, int y, int32_t iXFrac, int32_t iYFrac, uint8_t ucColor) +static void DrawScaledPixel(BBEIDISP *pBBEI, int iCX, int iCY, int x, int y, int32_t iXFrac, int32_t iYFrac, uint8_t ucColor) { uint8_t *d, ucMask; int iPitch; int iRedOffset = 0; - iPitch = pOBD->width; - if (ucColor >= OBD_YELLOW && pOBD->iFlags & (OBD_3COLOR | OBD_4COLOR)) { + iPitch = pBBEI->width; + if (ucColor >= BBEI_YELLOW && pBBEI->iFlags & (BBEI_3COLOR | BBEI_4COLOR)) { // use the second half of the image buffer - iRedOffset = pOBD->width * ((pOBD->height+7)/8); + iRedOffset = pBBEI->width * ((pBBEI->height+7)/8); } if (iXFrac != 0x10000) x = ((x * iXFrac) >> 16); if (iYFrac != 0x10000) y = ((y * iYFrac) >> 16); x += iCX; y += iCY; - if (x < 0 || x >= pOBD->width || y < 0 || y >= pOBD->height) + if (x < 0 || x >= pBBEI->width || y < 0 || y >= pBBEI->height) return; // off the screen - d = &pOBD->ucScreen[iRedOffset + ((y >> 3)*iPitch) + x]; + d = &pBBEI->ucScreen[iRedOffset + ((y >> 3)*iPitch) + x]; ucMask = 1 << (y & 7); if (ucColor) *d |= ucMask; @@ -2330,29 +1255,29 @@ static void DrawScaledPixel(OBDISP *pOBD, int iCX, int iCY, int x, int y, int32_ // // For drawing filled ellipses // -static void DrawScaledLine(OBDISP *pOBD, int iCX, int iCY, int x, int y, int32_t iXFrac, int32_t iYFrac, uint8_t ucColor) +static void DrawScaledLine(BBEIDISP *pBBEI, int iCX, int iCY, int x, int y, int32_t iXFrac, int32_t iYFrac, uint8_t ucColor) { int iLen, x2; uint8_t *d, ucMask; int iPitch; int iRedOffset = 0; - if (ucColor >= OBD_YELLOW && pOBD->iFlags & (OBD_3COLOR | OBD_4COLOR)) { + if (ucColor >= BBEI_YELLOW && pBBEI->iFlags & (BBEI_3COLOR | BBEI_4COLOR)) { // use the second half of the image buffer - iRedOffset = pOBD->width * ((pOBD->height+7)/8); + iRedOffset = pBBEI->width * ((pBBEI->height+7)/8); } - iPitch = pOBD->width; + iPitch = pBBEI->width; if (iXFrac != 0x10000) x = ((x * iXFrac) >> 16); if (iYFrac != 0x10000) y = ((y * iYFrac) >> 16); iLen = x*2; x = iCX - x; y += iCY; x2 = x + iLen; - if (y < 0 || y >= pOBD->height) + if (y < 0 || y >= pBBEI->height) return; // completely off the screen if (x < 0) x = 0; - if (x2 >= pOBD->width) x2 = pOBD->width-1; + if (x2 >= pBBEI->width) x2 = pBBEI->width-1; iLen = x2 - x + 1; // new length - d = &pOBD->ucScreen[iRedOffset + ((y >> 3)*iPitch) + x]; + d = &pBBEI->ucScreen[iRedOffset + ((y >> 3)*iPitch) + x]; ucMask = 1 << (y & 7); if (ucColor) // white { @@ -2369,47 +1294,39 @@ static void DrawScaledLine(OBDISP *pOBD, int iCX, int iCY, int x, int y, int32_t // Draw the 8 pixels around the Bresenham circle // (scaled to make an ellipse) // -static void BresenhamCircle(OBDISP *pOBD, int iCX, int iCY, int x, int y, int32_t iXFrac, int32_t iYFrac, uint8_t ucColor, uint8_t bFill) +static void BresenhamCircle(BBEIDISP *pBBEI, int iCX, int iCY, int x, int y, int32_t iXFrac, int32_t iYFrac, uint8_t ucColor, uint8_t bFill) { if (bFill) // draw a filled ellipse { // for a filled ellipse, draw 4 lines instead of 8 pixels - DrawScaledLine(pOBD, iCX, iCY, x, y, iXFrac, iYFrac, ucColor); - DrawScaledLine(pOBD, iCX, iCY, x, -y, iXFrac, iYFrac, ucColor); - DrawScaledLine(pOBD, iCX, iCY, y, x, iXFrac, iYFrac, ucColor); - DrawScaledLine(pOBD, iCX, iCY, y, -x, iXFrac, iYFrac, ucColor); + DrawScaledLine(pBBEI, iCX, iCY, x, y, iXFrac, iYFrac, ucColor); + DrawScaledLine(pBBEI, iCX, iCY, x, -y, iXFrac, iYFrac, ucColor); + DrawScaledLine(pBBEI, iCX, iCY, y, x, iXFrac, iYFrac, ucColor); + DrawScaledLine(pBBEI, iCX, iCY, y, -x, iXFrac, iYFrac, ucColor); } else // draw 8 pixels around the edges { - DrawScaledPixel(pOBD, iCX, iCY, x, y, iXFrac, iYFrac, ucColor); - DrawScaledPixel(pOBD, iCX, iCY, -x, y, iXFrac, iYFrac, ucColor); - DrawScaledPixel(pOBD, iCX, iCY, x, -y, iXFrac, iYFrac, ucColor); - DrawScaledPixel(pOBD, iCX, iCY, -x, -y, iXFrac, iYFrac, ucColor); - DrawScaledPixel(pOBD, iCX, iCY, y, x, iXFrac, iYFrac, ucColor); - DrawScaledPixel(pOBD, iCX, iCY, -y, x, iXFrac, iYFrac, ucColor); - DrawScaledPixel(pOBD, iCX, iCY, y, -x, iXFrac, iYFrac, ucColor); - DrawScaledPixel(pOBD, iCX, iCY, -y, -x, iXFrac, iYFrac, ucColor); + DrawScaledPixel(pBBEI, iCX, iCY, x, y, iXFrac, iYFrac, ucColor); + DrawScaledPixel(pBBEI, iCX, iCY, -x, y, iXFrac, iYFrac, ucColor); + DrawScaledPixel(pBBEI, iCX, iCY, x, -y, iXFrac, iYFrac, ucColor); + DrawScaledPixel(pBBEI, iCX, iCY, -x, -y, iXFrac, iYFrac, ucColor); + DrawScaledPixel(pBBEI, iCX, iCY, y, x, iXFrac, iYFrac, ucColor); + DrawScaledPixel(pBBEI, iCX, iCY, -y, x, iXFrac, iYFrac, ucColor); + DrawScaledPixel(pBBEI, iCX, iCY, y, -x, iXFrac, iYFrac, ucColor); + DrawScaledPixel(pBBEI, iCX, iCY, -y, -x, iXFrac, iYFrac, ucColor); } } /* BresenhamCircle() */ // // Draw an outline or filled ellipse // -void obdEllipse(OBDISP *pOBD, int iCenterX, int iCenterY, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, uint8_t bFilled) +void bbeiEllipse(BBEIDISP *pBBEI, int iCenterX, int iCenterY, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, uint8_t bFilled) { int32_t iXFrac, iYFrac; int iRadius, iDelta, x, y; - if (pOBD == NULL || pOBD->ucScreen == NULL) + if (pBBEI == NULL || pBBEI->ucScreen == NULL) return; // must have back buffer defined - if (pOBD->type == DISPLAY_COMMANDS) { // encode this as a command sequence - obdWriteCmdByte(pOBD, OBD_DRAWELLIPSE | ((ucColor & 1) << 4) | ((bFilled & 1) << 5)); - obdWriteCmdInt(pOBD, iCenterX); - obdWriteCmdInt(pOBD, iCenterY); - obdWriteCmdInt(pOBD, iRadiusX); - obdWriteCmdInt(pOBD, iRadiusY); - return; // done - } if (iRadiusX <= 0 || iRadiusY <= 0) return; // invalid radii @@ -2429,7 +1346,7 @@ void obdEllipse(OBDISP *pOBD, int iCenterX, int iCenterY, int32_t iRadiusX, int3 x = 0; y = iRadius; while (x <= y) { - BresenhamCircle(pOBD, iCenterX, iCenterY, x, y, iXFrac, iYFrac, ucColor, bFilled); + BresenhamCircle(pBBEI, iCenterX, iCenterY, x, y, iXFrac, iYFrac, ucColor, bFilled); x++; if (iDelta < 0) { @@ -2441,40 +1358,32 @@ void obdEllipse(OBDISP *pOBD, int iCenterX, int iCenterY, int32_t iRadiusX, int3 y--; } } -} /* obdEllipse() */ +} /* bbeiEllipse() */ // // Draw an outline or filled rectangle // -void obdRectangle(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, uint8_t bFilled) +void bbeiRectangle(BBEIDISP *pBBEI, int x1, int y1, int x2, int y2, uint8_t ucColor, uint8_t bFilled) { uint8_t *d, ucMask, ucMask2; int tmp, iOff; int iPitch; int iRedOffset = 0; - if (ucColor >= OBD_YELLOW) { - if (pOBD->iFlags & (OBD_3COLOR | OBD_4COLOR)) { + if (ucColor >= BBEI_YELLOW) { + if (pBBEI->iFlags & (BBEI_3COLOR | BBEI_4COLOR)) { // use the second half of the image buffer - iRedOffset = pOBD->width * ((pOBD->height+7)/8); + iRedOffset = pBBEI->width * ((pBBEI->height+7)/8); } else { // force red to black if not present - ucColor = OBD_BLACK; + ucColor = BBEI_BLACK; } } - if (pOBD == NULL || pOBD->ucScreen == NULL) + if (pBBEI == NULL || pBBEI->ucScreen == NULL) return; // only works with a back buffer - if (pOBD->type == DISPLAY_COMMANDS) { // encode this as a command sequence - obdWriteCmdByte(pOBD, OBD_DRAWRECT | ((ucColor & 1) << 4) | ((bFilled & 1) << 5)); - obdWriteCmdInt(pOBD, x1); - obdWriteCmdInt(pOBD, y1); - obdWriteCmdInt(pOBD, x2); - obdWriteCmdInt(pOBD, y2); - return; // done - } if (x1 < 0 || y1 < 0 || x2 < 0 || y2 < 0 || - x1 >= pOBD->width || y1 >= pOBD->height || x2 >= pOBD->width || y2 >= pOBD->height) return; // invalid coordinates - iPitch = pOBD->width; + x1 >= pBBEI->width || y1 >= pBBEI->height || x2 >= pBBEI->width || y2 >= pBBEI->height) return; // invalid coordinates + iPitch = pBBEI->width; // Make sure that X1/Y1 is above and to the left of X2/Y2 // swap coordinates as needed to make this true if (x2 < x1) @@ -2496,7 +1405,7 @@ void obdRectangle(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, ucMask = 0xff << (y1 & 7); if (iMiddle == 0) // top and bottom lines are in the same row ucMask &= (0xff >> (7-(y2 & 7))); - d = &pOBD->ucScreen[iRedOffset + (y1 >> 3)*iPitch + x1]; + d = &pBBEI->ucScreen[iRedOffset + (y1 >> 3)*iPitch + x1]; // Draw top for (x = x1; x <= x2; x++) { @@ -2511,7 +1420,7 @@ void obdRectangle(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, ucMask = (ucColor) ? 0xff : 0x00; for (y=1; yucScreen[iRedOffset + (y1 >> 3)*iPitch + x1 + (y*iPitch)]; + d = &pBBEI->ucScreen[iRedOffset + (y1 >> 3)*iPitch + x1 + (y*iPitch)]; for (x = x1; x <= x2; x++) *d++ = ucMask; } @@ -2519,7 +1428,7 @@ void obdRectangle(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, if (iMiddle >= 1) // need to draw bottom part { ucMask = 0xff >> (7-(y2 & 7)); - d = &pOBD->ucScreen[iRedOffset + (y2 >> 3)*iPitch + x1]; + d = &pBBEI->ucScreen[iRedOffset + (y2 >> 3)*iPitch + x1]; for (x = x1; x <= x2; x++) { if (ucColor) @@ -2532,7 +1441,7 @@ void obdRectangle(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, else // outline { // see if top and bottom lines are within the same byte rows - d = &pOBD->ucScreen[iRedOffset + (y1 >> 3)*iPitch + x1]; + d = &pBBEI->ucScreen[iRedOffset + (y1 >> 3)*iPitch + x1]; if ((y1 >> 3) == (y2 >> 3)) { ucMask2 = 0xff << (y1 & 7); // L/R end masks @@ -2583,7 +1492,7 @@ void obdRectangle(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, ucMask = 1 << (y1 & 7); ucMask2 = 1 << (y2 & 7); x1++; - d = &pOBD->ucScreen[iRedOffset + (y1 >> 3)*iPitch + x1]; + d = &pBBEI->ucScreen[iRedOffset + (y1 >> 3)*iPitch + x1]; iOff = (y2 >> 3) - (y1 >> 3); iOff *= iPitch; for (; x1 < x2; x1++) @@ -2599,175 +1508,7 @@ void obdRectangle(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, } } } // outline -} /* obdRectangle() */ -// -// Copy the current bitmap buffer from its native form (LSB_FIRST, VERTICAL_BYTES) to the requested form -// returns 0 for success, -1 for error -// Constants to be combined for the iFlags parameter: -// Output format options - -// OBD_LSB_FIRST 0x001 -// OBD_MSB_FIRST 0x002 -// OBD_VERT_BYTES 0x004 -// OBD_HORZ_BYTES 0x008 -// Orientation Options - -// OBD_ROTATE_90 0x010 -// OBD_FLIP_VERT 0x020 -// OBD_FLIP_HORZ 0x040 -// Polarity Options - -// OBD_INVERT 0x080 -int obdCopy(OBDISP *pOBD, int iFlags, uint8_t *pDestination) -{ -int i, x, y, iPitch, iSize; -int xStart, xEnd, yStart, yEnd, yDst, xDst, dx, dy; -uint8_t ucSrcMask, ucDstMask, *s, *d; - - iPitch = pOBD->width; +} /* bbeiRectangle() */ + +#endif // __BB_EI_GFX__ - if (pDestination == NULL || pOBD == NULL || pOBD->ucScreen == NULL) - return OBD_ERROR_BAD_PARAMETER; - // calculate output buffer size - if (iFlags & OBD_HORZ_BYTES) { - if (iFlags & OBD_ROTATE_90) - iSize = ((pOBD->height + 7)>>3) * pOBD->width; - else - iSize = ((pOBD->width + 7)>>3) * pOBD->height; - } else { - if (iFlags & OBD_ROTATE_90) - iSize = (pOBD->height * ((pOBD->width+7)>>3)); - else - iSize = (pOBD->width * ((pOBD->height+7)>>3)); - } - memset(pDestination, 0, iSize); // start with 0 in dest - // Prepare vars for walking through the source image - if (iFlags & OBD_ROTATE_90) { - if (iFlags & OBD_FLIP_HORZ) { - dy = 1; - yStart = 0; - yEnd = pOBD->height; - } else { - dy = -1; - yStart = pOBD->height-1; - yEnd = -1; - } - if (iFlags & OBD_FLIP_VERT) { - dx = -1; - xStart = pOBD->width-1; - xEnd = -1; - } else { - dx = 1; - xStart = 0; - xEnd = pOBD->width; - } - } else { // not rotated - if (iFlags & OBD_FLIP_HORZ) { - dx = -1; - xStart = pOBD->width-1; - xEnd = -1; - } else { - dx = 1; - xStart = 0; - xEnd = pOBD->width; - } - if (iFlags & OBD_FLIP_VERT) { - dy = -1; - yStart = pOBD->height-1; - yEnd = -1; - } else { - dy = 1; - yStart = 0; - yEnd = pOBD->height; - } - } - // Due to the possible number of permutations, the different loops - // are more generic and handle flips/bit-direction with a more general - // approach which moves individual pixels even when a more efficient - // method is possible. More cycles, but able to do EVERYTHING -// Separate output by byte orientation - // Vertical bytes here - if (iFlags & OBD_VERT_BYTES) - { - if (iFlags & OBD_ROTATE_90) - { - xDst = 0; - for (y=yStart; y!=yEnd; y += dy, xDst++) { - ucSrcMask = (1 << (y & 7)); - yDst = 0; - s = &pOBD->ucScreen[(y >> 3) * iPitch]; - d = &pDestination[xDst]; - for (x=xStart; x!=xEnd; x += dx, yDst++) { - if (s[x] & ucSrcMask) {// set pixel, copy to dest - if (iFlags & OBD_LSB_FIRST) - d[(yDst >> 3)*pOBD->height] |= (1 << (yDst & 7)); - else - d[(yDst >> 3)*pOBD->height] |= (0x80 >> (yDst & 7)); - } - } // for x - } // for y - } // rotate 90 - else // normally oriented - { - yDst = 0; - for (y=yStart; y!=yEnd; y += dy, yDst++) { - ucSrcMask = (1 << (y & 7)); - if (iFlags & OBD_LSB_FIRST) - ucDstMask = (1 << (y & 7)); - else - ucDstMask = (0x80 >> (y & 7)); - xDst = 0; - s = &pOBD->ucScreen[(y >> 3) * iPitch]; - d = &pDestination[(yDst >> 3) * iPitch]; - for (x=xStart; x!=xEnd; x += dx, xDst++) { - if (s[x] & ucSrcMask) // set pixel, copy to dest - d[xDst] |= ucDstMask; - } // for x - } // for y - } // normal orientation - } // vertical bytes - else // Horizontal bytes here - { - if (iFlags & OBD_ROTATE_90) - { - int iDstPitch = (pOBD->height + 7)>>3; // dest bytes per line - xDst = 0; - for (y=yStart; y!=yEnd; y += dy, xDst++) { - ucSrcMask = (1 << (y & 7)); - yDst = 0; - s = &pOBD->ucScreen[(y >> 3) * iPitch]; - d = &pDestination[xDst >> 3]; - ucDstMask = (iFlags & OBD_LSB_FIRST) ? (1 << (xDst & 7)) : (0x80 >> (xDst & 7)); - for (x=xStart; x!=xEnd; x += dx, yDst++) { - if (s[x] & ucSrcMask) // set pixel, copy to dest - d[yDst * iDstPitch] |= ucDstMask; - } // for x - } // for y - } // rotate 90 - else // normally oriented - { - int iDstPitch = (pOBD->width + 7)>>3; // dest bytes per line - yDst = 0; - for (y=yStart; y!=yEnd; y += dy, yDst++) { - ucSrcMask = (1 << (y & 7)); - xDst = 0; - s = &pOBD->ucScreen[(y >> 3) * iPitch]; - d = &pDestination[yDst * iDstPitch]; - ucDstMask = (iFlags & OBD_LSB_FIRST) ? 0x1 : 0x80; - for (x=xStart; x!=xEnd; x += dx, xDst++) { - if (s[x] & ucSrcMask) // set pixel, copy to dest - d[(xDst>>3)] |= ucDstMask; - if (iFlags & OBD_LSB_FIRST) - ucDstMask <<= 1; - else - ucDstMask >>= 1; - if (ucDstMask == 0) - ucDstMask = (iFlags & OBD_LSB_FIRST) ? 0x1 : 0x80; - } // for x - } // for y - } // normal orientation - } - // Invert all pixels? - if (iFlags & OBD_INVERT) { - for (i=0; iiDCPin, OUTPUT); - if (pBBEI->iRSTPin != 0xff) { - pinMode(pBBEI->iRSTPin, OUTPUT); - digitalWrite(pBBEI->iRSTPin, LOW); - delay(100); - digitalWrite(pBBEI->iRSTPin, HIGH); - delay(1000); - } + pinMode(pBBEI->iRSTPin, OUTPUT); + digitalWrite(pBBEI->iRSTPin, LOW); + delay(100); + digitalWrite(pBBEI->iRSTPin, HIGH); + delay(100); if (pBBEI->iBUSYPin != 0xff) { pinMode(pBBEI->iBUSYPin, INPUT); } pBBEI->iSpeed = u32Speed; +#ifdef ARDUINO_ARCH_ESP32 SPI.begin(pBBEI->iCLKPin, -1, pBBEI->iMOSIPin, pBBEI->iCSPin); +#else + pinMode(pBBEI->iCSPin, OUTPUT); + digitalWrite(pBBEI->iCSPin, HIGH); // we have to manually control the CS pin + SPI.begin(); // other architectures have fixed SPI pins +#endif SPI.beginTransaction(SPISettings(u32Speed, MSBFIRST, SPI_MODE0)); SPI.endTransaction(); // N.B. - if you call beginTransaction() again without a matching endTransaction(), it will hang on ESP32 } /* bbeiInitIO() */ +void bbeiWakeUp(BBEIDISP *pBBEI) +{ + digitalWrite(pBBEI->iRSTPin, LOW); + delay(10); + digitalWrite(pBBEI->iRSTPin, HIGH); + delay(10); +} /* bbeiWakeUp() */ + +void bbeiWaitBusy(BBEIDISP *pBBEI) +{ + uint8_t busy_idle = (pBBEI->chip_type == BBEI_CHIP_UC81xx) ? HIGH : LOW; + while (1) { + if (digitalRead(pBBEI->iBUSYPin) == busy_idle) break; + } +} /* bbeiWaitBusy() */ + void bbeiCMD2(BBEIDISP *pBBEI, uint8_t cmd1, uint8_t cmd2) { digitalWrite(pBBEI->iDCPin, LOW); +#ifndef ARDUINO_ARCH_ESP32 + digitalWrite(pBBEI->iCSPin, LOW); +#endif SPI.transfer(cmd1); SPI.transfer(cmd2); digitalWrite(pBBEI->iDCPin, HIGH); // leave data mode as the default +#ifndef ARDUINO_ARCH_ESP32 + digitalWrite(pBBEI->iCSPin, HIGH); +#endif } /* bbeiCMD2() */ void bbeiWriteCmd(BBEIDISP *pBBEI, uint8_t cmd) { digitalWrite(pBBEI->iDCPin, LOW); +#ifndef ARDUINO_ARCH_ESP32 + digitalWrite(pBBEI->iCSPin, LOW); +#endif SPI.transfer(cmd); digitalWrite(pBBEI->iDCPin, HIGH); // leave data mode as the default +#ifndef ARDUINO_ARCH_ESP32 + digitalWrite(pBBEI->iCSPin, HIGH); +#endif } /* bbeiWriteCmd() */ +void bbeiSleep(BBEIDISP *pBBEI, int bDeep) +{ + if (pBBEI->chip_type == BBEI_CHIP_UC81xx) { + bbeiCMD2(pBBEI, UC8151_CDI, 0x17); // border floating + bbeiWriteCmd(pBBEI, UC8151_POFF); // power off + bbeiWaitBusy(pBBEI); + if (bDeep) { + bbeiCMD2(pBBEI, UC8151_DSLP, 0xa5); // deep sleep + } + } else { + bbeiCMD2(pBBEI, SSD1608_DEEP_SLEEP, (bDeep) ? 0x02 : 0x01); // deep sleep mode 1 keeps RAM, mode 2 loses RAM + return; + } +} /* bbeiSleep() */ + + void bbeiWriteData(BBEIDISP *pBBEI, uint8_t *pData, int iLen) { +#ifdef ARDUINO_ARCH_ESP32 SPI.transferBytes(pData, NULL, iLen); +#else + digitalWrite(pBBEI->iCSPin, LOW); + SPI.transfer(pData, iLen); + digitalWrite(pBBEI->iCSPin, HIGH); +#endif } /* bbeiWriteData() */ #else // LINUX -#endif // ARDUINO +#endif // ARDUINO / LINUX #endif // __BB_EI_IO__ diff --git a/src/bb_eink.cpp b/src/bb_eink.cpp index 9b4ef06..c45d38e 100644 --- a/src/bb_eink.cpp +++ b/src/bb_eink.cpp @@ -32,18 +32,20 @@ void delay(int); #else // Arduino #endif // _LINUX_ + #include "bb_eink.h" #include "bb_ei_io.inl" // I/O (non-portable) code is in here #include "bb_ei.inl" // All of the display interface code is in here #include "bb_ei_gfx.inl" // drawing code + #ifdef __cplusplus // // C++ Class implementation // -uint32_t BBEINK::getRefreshTime(void) -{ - return _bbei.iTimeout; -} +//uint32_t BBEINK::getRefreshTime(void) +//{ +// return _bbei.iTimeout; +//} void BBEINK::setPosition(int x, int y, int w, int h) { @@ -53,38 +55,41 @@ int BBEINK::setPanelType(int iPanel) { return bbeiSetPanelType(&_bbei, iPanel); } -void BBEINK::initIO(int iCS, int iMOSI, int iSCLK, int iDC, int iReset, int iBusy, uint32_t u32Speed) +void BBEINK::initIO(int iDC, int iReset, int iBusy, int iCS, int iMOSI, int iSCLK, uint32_t u32Speed) { _bbei.iCSPin = iCS; _bbei.iMOSIPin = iMOSI; _bbei.iCLKPin = iSCLK; _bbei.iDCPin = iDC; _bbei.iRSTPin = iReset; - _bbei.iBusyPin = iBusy; + _bbei.iBUSYPin = iBusy; bbeiInitIO(&_bbei, u32Speed); } /* initIO() */ -int BBEINK::getFlags(void) +int BBEINK::writePlane(int iPlane) { - return _bbei.iFlags; -} + return bbeiWritePlane(&_bbei, iPlane); +} /* writePlane() */ -void BBEINK::setFlags(int iFlags) +int BBEINK::refresh(int iMode, bool bWait) { - _bbei.iFlags = iFlags; - _bbei.invert = iFlags & OBD_INVERTED; - _bbei.flip = iFlags & OBD_FLIP180; -} + int rc; + rc = bbeiRefresh(&_bbei, iMode); + if (rc == BBEI_SUCCESS && bWait) { + bbeiWaitBusy(&_bbei); + } + return rc; +} /* refresh() */ -void BBEINK::setContrast(uint8_t ucContrast) -{ - obdSetContrast(&_bbei, ucContrast); -} +//int BBEINK::getFlags(void) +//{ +// return _bbei.iFlags; +//} -int BBEINK::I2Cbegin(int iType, int iAddr, int32_t iSpeed) -{ - return obdI2CInit(&_bbei, iType, iAddr, _bbei.flip, _bbei.invert, !_bbei.bBitBang, _bbei.iSDAPin, _bbei.iSCLPin, _bbei.iRSTPin, iSpeed); -} /* I2Cbegin() */ +//void BBEINK::setFlags(int iFlags) +//{ +// _bbei.iFlags = iFlags; +//} void BBEINK::setBuffer(uint8_t *pBuffer) { @@ -93,23 +98,16 @@ void BBEINK::setBuffer(uint8_t *pBuffer) int BBEINK::allocBuffer(void) { - int iSize = _bbei.width * ((_bbei.height+7)>>3); - if (_bbei.iFlags & (OBD_3COLOR | OBD_4COLOR) || _bbei.chip_type == OBD_CHIP_UC8151) - iSize *= 2; // 2 bit planes + int iSize = ((_bbei.width+7)>>3) * _bbei.height; +// if (_bbei.iFlags & (BBEI_3COLOR | BBEI_4COLOR) || _bbei.chip_type == BBEI_CHIP_UC81xx) +// iSize *= 2; // 2 bit planes _bbei.ucScreen = (uint8_t *)malloc(iSize); if (_bbei.ucScreen != NULL) { - _bbei.render = false; // draw into RAM only - memset(_bbei.ucScreen, 0xff, iSize); - return OBD_SUCCESS; + return BBEI_SUCCESS; } - return OBD_ERROR_NO_MEMORY; // failed + return BBEI_ERROR_NO_MEMORY; // failed } /* allocBuffer() */ -uint32_t BBEINK::capabilities(void) -{ - return _bbei.iFlags; -} - void * BBEINK::getBuffer(void) { return (void *)_bbei.ucScreen; @@ -123,29 +121,34 @@ void BBEINK::freeBuffer(void) } } /* freeBuffer() */ -void BBEINK::setScroll(bool bScroll) +uint32_t BBEINK::capabilities(void) { - _bbei.bScroll = bScroll; + return _bbei.iFlags; } void BBEINK::setRotation(int iRotation) { - obdSetRotation(&_bbei, iRotation); +// obdSetRotation(&_bbei, iRotation); } /* setRotation() */ -void BBEINK::fillScreen(int iColor) +int BBEINK::getRotation(void) { - obdFill(&_bbei, iColor, (_bbei.ucScreen == NULL)); + return _bbei.iOrientation; +} + +void BBEINK::fillScreen(int iColor, int iPlane) +{ + bbeiFill(&_bbei, iColor, iPlane); } /* fillScreen() */ void BBEINK::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { - obdRectangle(&_bbei, x, y, x+w-1, y+h-1, color, 0); + bbeiRectangle(&_bbei, x, y, x+w-1, y+h-1, color, 0); } /* drawRect() */ void BBEINK::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { - obdRectangle(&_bbei, x, y, x+w-1, y+h-1, color, 1); + bbeiRectangle(&_bbei, x, y, x+w-1, y+h-1, color, 1); } /* fillRect() */ void BBEINK::setTextWrap(bool bWrap) @@ -155,11 +158,11 @@ void BBEINK::setTextWrap(bool bWrap) void BBEINK::setTextColor(int iFG, int iBG) { - if (iFG > OBD_RED) iFG = OBD_BLACK; - if (iBG > OBD_RED) iBG = OBD_BLACK; - if ((_bbei.iFlags & (OBD_3COLOR | OBD_4COLOR)) == 0) { - if (iFG == OBD_RED) iFG = OBD_BLACK; // can't set red color - if (iBG == OBD_RED) iBG = OBD_BLACK; + if (iFG > BBEI_RED) iFG = BBEI_BLACK; + if (iBG > BBEI_RED) iBG = BBEI_BLACK; + if ((_bbei.iFlags & (BBEI_3COLOR | BBEI_4COLOR)) == 0) { + if (iFG == BBEI_RED) iFG = BBEI_BLACK; // can't set red color + if (iBG == BBEI_RED) iBG = BBEI_BLACK; } _bbei.iFG = iFG; _bbei.iBG = (iBG == -1) ? iFG : iBG; @@ -175,77 +178,30 @@ void BBEINK::setCursor(int x, int y) int BBEINK::loadBMP(const uint8_t *pBMP, int x, int y, int iFG, int iBG) { - return obdLoadBMP(&_bbei, pBMP, x, y, iFG, iBG); + return bbeiLoadBMP(&_bbei, pBMP, x, y, iFG, iBG); } /* loadBMP() */ -int BBEINK::drawEPDGFX(int x, int y, int cx, int cy, uint8_t *pPlane0, uint8_t *pPlane1) -{ - return obdDrawEPDGFX(&_bbei, x, y, cx, cy, pPlane0, pPlane1); -} /* drawEPDGFX() */ - int BBEINK::loadBMP3(const uint8_t *pBMP, int x, int y) { - return obdLoadBMP3(&_bbei, pBMP, x, y); + return bbeiLoadBMP3(&_bbei, pBMP, x, y); } /* loadBMP3() */ void BBEINK::setFont(int iFont) { _bbei.iFont = iFont; - _bbei.pFreeFont = NULL; + // _bbei.pFreeFont = NULL; } /* setFont() */ -void BBEINK::setTextSize(uint8_t s) -{ - _bbei.u32FontScaleX = _bbei.u32FontScaleY = 256 * s; - _bbei.iFont = -1; - _bbei.pFreeFont = NULL; -} -void BBEINK::setTextSize(uint8_t sx, uint8_t sy) -{ - _bbei.u32FontScaleX = 256 * sx; - _bbei.u32FontScaleY = 256 * sy; - _bbei.iFont = -1; - _bbei.pFreeFont = NULL; -} -int BBEINK::scrollBuffer(int iStartCol, int iEndCol, int iStartRow, int iEndRow, int bUp) -{ - return obdScrollBuffer(&_bbei, iStartCol, iEndCol, iStartRow, iEndRow, bUp); -} /* scrollBuffer() */ - -void BBEINK::setFreeFont(const GFXfont *pFont) -{ - _bbei.pFreeFont = (GFXfont *)pFont; -} /* setFreeFont() */ +//void BBEINK::setFreeFont(const GFXfont *pFont) +//{ +// _bbei.pFreeFont = (GFXfont *)pFont; +//} /* setFreeFont() */ void BBEINK::drawLine(int x1, int y1, int x2, int y2, int iColor) { - obdDrawLine(&_bbei, x1, y1, x2, y2, iColor, 1); + bbeiDrawLine(&_bbei, x1, y1, x2, y2, iColor); } /* drawLine() */ -inline GFXglyph *pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) { -#ifdef __AVR__ - return &(((GFXglyph *)pgm_read_ptr(&gfxFont->glyph))[c]); -#else - // expression in __AVR__ section may generate "dereferencing type-punned - // pointer will break strict-aliasing rules" warning In fact, on other - // platforms (such as STM32) there is no need to do this pointer magic as - // program memory may be read in a usual way So expression may be simplified - return gfxFont->glyph + c; -#endif //__AVR__ -} - -void obdScroll1Line(OBDISP *pOBD, int iAmount) -{ -int y, iLines; - - if (pOBD == NULL || pOBD->ucScreen == NULL) - return; - iLines = (pOBD->height+7)>>3; - for (y=0; yucScreen[y*pOBD->width], &pOBD->ucScreen[(y+iAmount)*pOBD->width], pOBD->width*iAmount); - } - memset(&pOBD->ucScreen[(iLines-iAmount) * pOBD->width], (char)pOBD->iBG, pOBD->width*iAmount); -} /* obdScroll1Line() */ #ifdef _LINUX_ void BBEINK::print(const string &str) { @@ -349,6 +305,7 @@ char ucTemp[4]; // #ifndef __AVR__ size_t BBEINK::write(uint8_t c) { +#ifdef FUTURE char szTemp[2]; // used to draw 1 character at a time to the C methods int w, h; @@ -414,18 +371,19 @@ int w, h; _bbei.iCursorX = 0; _bbei.iCursorY += h; } - obdWriteStringCustom(&_bbei, _bbei.pFreeFont, -1, -1, szTemp, _bbei.iFG); + bbeiWriteStringCustom(&_bbei, _bbei.pFreeFont, -1, -1, szTemp, _bbei.iFG); } } } } +#endif // FUTURE return 1; } /* write() */ #endif // !__AVR__ -void BBEINK::drawPixel(int16_t x, int16_t y, uint16_t color) +void BBEINK::drawPixel(int16_t x, int16_t y, uint8_t color) { - obdSetPixel(&_bbei, x, y, color, _bbei.render); + bbeiSetPixel(&_bbei, x, y, color); } int16_t BBEINK::getCursorX(void) { @@ -435,33 +393,16 @@ int16_t BBEINK::getCursorY(void) { return _bbei.iCursorY; } -uint8_t BBEINK::getRotation(void) -{ - return _bbei.iOrientation; -} -void BBEINK::wake(void) -{ - if (_bbei.type >= EPD42_400x300) { - EPDWakeUp(&_bbei, 1); - } -} -void BBEINK::sleep(int bDeep) -{ - if (_bbei.type >= EPD42_400x300) { - EPDSleep(&_bbei, bDeep); - } -} - void BBEINK::getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) { - if (_bbei.pFreeFont) { - int width, top, bottom; - obdGetStringBox(_bbei.pFreeFont, (char *)string, &width, &top, &bottom); - *x1 = x; - *w = width; - *y1 = y + top; - *h = (bottom - top + 1); - } +// if (_bbei.pFreeFont) { +// int width, top, bottom; + // obdGetStringBox(_bbei.pFreeFont, (char *)string, &width, &top, &bottom); +// *x1 = x; +// *w = width; +// *y1 = y + top; +// *h = (bottom - top + 1); +// } } void BBEINK::getTextBounds(const String &str, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) { @@ -483,114 +424,29 @@ int16_t BBEINK::height(void) { return _bbei.height; } -void BBEINK::drawSprite(uint8_t *pSprite, int cx, int cy, int iPitch, int x, int y, uint8_t iPriority) -{ - obdDrawSprite(&_bbei, pSprite, cx, cy, iPitch, x, y, iPriority); -} -int BBEINK::drawGFX(uint8_t *pSrc, int iSrcCol, int iSrcRow, int iDestCol, int iDestRow, int iWidth, int iHeight, int iSrcPitch) -{ - return obdDrawGFX(&_bbei, pSrc, iSrcCol, iSrcRow, iDestCol, iDestRow, iWidth, iHeight, iSrcPitch); -} /* drawGFX() */ - -void BBEINK::drawTile(const uint8_t *pTile, int x, int y, int iRotation, int bInvert, int bRender) -{ - obdDrawTile(&_bbei, pTile, x, y, iRotation, bInvert, bRender); -} void BBEINK::drawCircle(int32_t x, int32_t y, int32_t r, uint32_t color) { - obdEllipse(&_bbei, x, y, r, r, color, 0); + bbeiEllipse(&_bbei, x, y, r, r, color, 0); } void BBEINK::fillCircle(int32_t x, int32_t y, int32_t r, uint32_t color) { - obdEllipse(&_bbei, x, y, r, r, color, 1); + bbeiEllipse(&_bbei, x, y, r, r, color, 1); } void BBEINK::drawEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color) { - obdEllipse(&_bbei, x, y, rx, ry, color, 0); + bbeiEllipse(&_bbei, x, y, rx, ry, color, 0); } void BBEINK::fillEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color) { - obdEllipse(&_bbei, x, y, rx, ry, color, 1); -} -void BBEINK::setPosition(int x, int y, int w, int h) -{ - if (_bbei.type >= EPD42_400x300) - EPDSetPosition(&_bbei, x, y, w, h); - else - obdSetPosition(&_bbei, x, y, 1); -} /* setPosition() */ -void BBEINK::writeRaw(uint8_t *pData, int iLen) -{ - RawWrite(&_bbei, pData, iLen); + bbeiEllipse(&_bbei, x, y, rx, ry, color, 1); } -void BBEINK::writeCommand(uint8_t ucCMD) +void BBEINK::sleep(int bDeep) { - obdWriteCommand(&_bbei, ucCMD); + bbeiSleep(&_bbei, bDeep); } -void BBEINK::pushPixels(uint8_t *pPixels, int iCount) -{ - RawWriteData(&_bbei, pPixels, iCount); -} /* pushPixels() */ - -void BBEINK::pushImage(int x, int y, int w, int h, uint16_t *pixels) -{ - // DEBUG - (void)x; (void)y; (void)w; (void)h; (void)pixels; -} - -void BBEINK::displayLines(int iStartLine, int iLineCount) -{ - obdWriteLCDLines(&_bbei, iStartLine, iLineCount); -} /* displayLines() */ - -int BBEINK::display(bool bRefresh, bool bWait, bool bFast) -{ - return obdDumpBuffer(&_bbei, NULL, bRefresh, bWait, bFast); -} - void BBEINK::wait(bool bQuick) { - EPDWaitBusy(&_bbei, bQuick); -} -int BBEINK::displayPartial() -{ - if (_bbei.type >= EPD42_400x300 && (_bbei.iFlags & OBD_HAS_PARTIAL_UPDATE)) { - return EPDDumpPartial(&_bbei, NULL, 0, 0, _bbei.width, _bbei.height); - } - return OBD_ERROR_NOT_SUPPORTED; -} -int BBEINK::displayPartial(int x, int y, int w, int h, uint8_t *pBuffer) -{ - if (_bbei.type >= EPD42_400x300 && (_bbei.iFlags & OBD_HAS_PARTIAL_UPDATE)) { - return obdDumpPartial(&_bbei, x, y, w, h, pBuffer); - } else { - return OBD_ERROR_NOT_SUPPORTED; - } -} -void BBEINK::drawString(const char *pText, int x, int y) -{ - setCursor(x,y); - if (_bbei.pFreeFont != NULL) - obdWriteStringCustom(&_bbei, _bbei.pFreeFont, x, y, (char *)pText, _bbei.iFG); - else - obdWriteString(&_bbei, 0, x, y, (char *)pText, _bbei.iFont, _bbei.iFG, 1); -} /* drawString() */ -void BBEINK::drawString(String text, int x, int y) -{ - drawString(text.c_str(), x, y); -} /* drawString() */ - -void BBEINK::backlight(int bOn) -{ - obdBacklight(&_bbei, bOn); -} -OBDISP * BBEINK::getOBD() -{ - return &_bbei; -} -void BBEINK::setRender(bool bRAMOnly) -{ - _bbei.render = !bRAMOnly; + bbeiWaitBusy(&_bbei); } #endif // __cplusplus diff --git a/src/bb_eink.h b/src/bb_eink.h index 2a2d4aa..bfc0309 100644 --- a/src/bb_eink.h +++ b/src/bb_eink.h @@ -16,11 +16,9 @@ #ifndef __BB_EINK__ #define __BB_EINK__ -#ifndef MEMORY_ONLY -#include -#endif - -#ifdef _LINUX_ +#ifdef ARDUINO +#include +#else // _LINUX_ // for Print support #define DEC 10 #define HEX 16 @@ -42,6 +40,9 @@ enum { BBEI_ERROR_COUNT }; +#define LIGHT_SLEEP 0 +#define DEEP_SLEEP 1 + // Display refresh modes enum { REFRESH_FULL=0, @@ -56,6 +57,13 @@ enum { BBEI_CHIP_COUNT }; +// enum for writing local framebuffer to EPD memory plane(s) +enum { + PLANE_0=0, + PLANE_1, + PLANE_BOTH, + PLANE_DUPLICATE // duplicate 0 to both 0 and 1 +}; // 5 possible font sizes: 8x8, 16x32, 6x8, 12x16 (stretched from 6x8 with smoothing), 16x16 (stretched from 8x8) enum { FONT_6x8 = 0, @@ -72,9 +80,9 @@ enum { typedef struct epd_panel { uint16_t width; uint16_t height; - uint8_t *pInitFull; - uint8_t *pInitFast; - uint8_t *pInitPart; + const uint8_t *pInitFull; + const uint8_t *pInitFast; + const uint8_t *pInitPart; uint8_t flags; uint8_t chip_type; } EPD_PANEL; @@ -90,7 +98,8 @@ enum { EPD295_128x296, // harvested from Solum 2.9" BW ESLs EPD266_152x296, // GDEY0266T90 EPD102_80x128, // GDEW0102T4 - EPD27B_176x264 // GDEY027T91 + EPD27B_176x264, // GDEY027T91 + EPD_PANEL_COUNT }; #ifdef FUTURE EPD42_4GRAY_400x300, // WFT0420CZ15 @@ -154,7 +163,7 @@ enum { #define BBEI_GRAY3 3 // (UC81xx commands) -enum reg_uc81xx { +enum { UC8151_PSR = 0x00, UC8151_PWR = 0x01, UC8151_POFF = 0x02, @@ -198,7 +207,7 @@ enum reg_uc81xx { UC8151_TSSET = 0xe5 }; -enum reg_ssd16xx { +enum { SSD1608_DRIVER_CONTROL = 0x01, SSD1608_GATE_VOLTAGE = 0x03, SSD1608_SOURCE_VOLTAGE = 0x04, @@ -238,10 +247,7 @@ enum reg_ssd16xx { typedef struct bbeistruct { -#ifndef MEMORY_ONLY -BBI2C bbi2c; -#endif -uint8_t wrap, flip, invert, type, render, can_flip, chip_type; +uint8_t wrap, type, chip_type; uint8_t *ucScreen; int iCursorX, iCursorY; int width, height, native_width, native_height; @@ -249,12 +255,11 @@ int iScreenOffset, iOrientation; int iFG, iBG; //current color int iFont, iFlags; int iDataTime, iOpTime; // time in milliseconds for data transmission and operation -uint32_t u32FontScaleX, u32FontScaleY; uint32_t iSpeed; uint32_t iTimeout; // for e-ink panels uint8_t iDCPin, iMOSIPin, iCLKPin, iCSPin, iRSTPin, iBUSYPin; uint8_t x_offset, y_offset; // memory offsets -uint8_t bBitBang; +uint8_t bBitBang, iPlane; const uint8_t *pInitFull; // full update init sequence const uint8_t *pInitFast; // fast update init sequence const uint8_t *pInitPart; // partial update init sequence @@ -274,55 +279,59 @@ class BBEINK #endif // _LINUX_ { public: - BBEINK() { memset(&_bbei, 0, sizeof(_bbei)); _bbei.iFG = BBEI_BLACK; _bbei.render = 1; _bbei.type = EPD_UNDEFINED;} - void initIO(int iCS, int iMOSI, int iSCLK, int iDC, int iReset=-1, int iBusy=-1, uint32_t u32Speed = 8000000); - int setPanelType(int iPanel); - uint32_t capabilities(); - int16_t height(void); - int16_t width(void); + BBEINK() { memset(&_bbei, 0, sizeof(_bbei)); _bbei.iFG = BBEI_BLACK; _bbei.type = EPD_PANEL_UNDEFINED;} void setPosition(int x, int y, int w, int h); - void sleep(int bDeep); - void wait(bool bQuick = false); - int dataTime(); - int opTime(); + int setPanelType(int iPanel); + void initIO(int iDC, int iReset, int iBusy, int iCS = SS, int iMOSI = MOSI, int iSCLK = SCK, uint32_t u32Speed = 8000000); + int writePlane(int iPlane = PLANE_DUPLICATE); + int refresh(int iMode, bool bWait = true); + void setBuffer(uint8_t *pBuffer); int allocBuffer(void); void * getBuffer(void); - void setBuffer(uint8_t *pBuffer); void freeBuffer(void); - void fillScreen(int iColor); + uint32_t capabilities(); void setRotation(int iAngle); int getRotation(void); + void fillScreen(int iColor, int iPlane = PLANE_DUPLICATE); + void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); + void setTextWrap(bool bWrap); + void setTextColor(int iFG, int iBG = -1); + void setCursor(int x, int y); + int loadBMP(const uint8_t *pBMP, int x, int y, int iFG, int iBG); + int loadBMP3(const uint8_t *pBMP, int x, int y); + void setFont(int iFont); + void drawLine(int x1, int y1, int x2, int y2, int iColor); + void drawPixel(int16_t x, int16_t y, uint8_t color); int16_t getCursorX(void); int16_t getCursorY(void); - void setCursor(int x, int y); - void setPlane(int iPlane); - void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); + void getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h); + void getTextBounds(const String &str, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h); + int dataTime(); + int opTime(); + int16_t height(void); + int16_t width(void); void drawCircle(int32_t x, int32_t y, int32_t r, uint32_t color); void fillCircle(int32_t x, int32_t y, int32_t r, uint32_t color); void drawEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color); void fillEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color); - int drawGFX(uint8_t *pSrc, int iSrcCol, int iSrcRow, int iDestCol, int iDestRow, int iWidth, int iHeight, int iSrcPitch); - void drawPixel(int16_t x, int16_t y, uint16_t color); + void sleep(int bDeep); + void wait(bool bQuick = false); + +#ifdef FUTURE + void setPlane(int iPlane); void drawSprite(uint8_t *pSprite, int cx, int cy, int iPitch, int x, int y, uint8_t iPriority); void drawTile(const uint8_t *pTile, int x, int y, int iRotation, int bInvert, int bRender); - void setTextColor(int iFG, int iBG = -1); - int refresh(int iMode); - int drawEPDGFX(int x, int y, int cx, int cy, uint8_t *pPlane0, uint8_t *pPlane1); - int loadBMP(const uint8_t *pBMP, int x, int y, int iFG, int iBG); - int loadBMP3(const uint8_t *pBMP, int x, int y); - void getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h); - void getTextBounds(const String &str, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h); - void setTextWrap(bool bWrap); - void setFont(int iFont); - void setFreeFont(const BB_FONT *pFont); +// int drawGFX(int x, int y, int cx, int cy, uint8_t *pPlane0, uint8_t *pPlane1); +// void setFreeFont(const BB_FONT *pFont); void pushBytes(uint8_t *pByte, int iCount); void writeCommand(uint8_t ucCMD); void writeRaw(uint8_t *pData, int iLen); void pushImage(int x, int y, int w, int h, uint16_t *pixels); void drawString(const char *pText, int x, int y); void drawString(String text, int x, int y); - void drawLine(int x1, int y1, int x2, int y2, int iColor); +#endif // FUTURE + #ifdef _LINUX_ void print(const char *pString); void println(const char *pString); @@ -342,11 +351,6 @@ class BBEINK }; // class BBEINK #endif // __cplusplus -// Make the Linux library interface C instead of C++ -#if defined(_LINUX_) && defined(__cplusplus) -extern "C" { -#endif - #if !defined(BITBANK_LCD_MODES) #define BITBANK_LCD_MODES typedef enum @@ -356,272 +360,5 @@ typedef enum } DC_MODE; #endif -void EPDFill(OBDISP *pOBD, uint8_t ucCMD, uint8_t ucPattern); -void EPDSetPosition(OBDISP *pOBD, int x, int y, int cx, int cy); - -// -// Create a virtual display of any size -// The memory buffer must be provided at the time of creation -// -void obdCreateVirtualDisplay(OBDISP *pOBD, int width, int height, uint8_t *buffer); -// -// Draw the contents of a memory buffer onto a display -// The sub-window will be clipped if it specifies too large an area -// for the destination display. The source OBDISP structure must have -// a valid back buffer defined -// The top and bottom destination edges will be drawn on byte boundaries (8 rows) -// The source top/bot edges can be on pixel boundaries -// -void obdDumpWindow(OBDISP *pOBDSrc, OBDISP *pOBDDest, int srcx, int srcy, int destx, int desty, int width, int height); -// -// Write one of more lines to a Sharp memory LCD -// -void obdWriteLCDLines(OBDISP *pOBD, int iStart, int iCount); -// -// Initializes the display controller into "page mode" on I2C -// If SDAPin and SCLPin are not -1, then bit bang I2C on those pins -// Otherwise use the Wire library. -// If you don't need to use a separate reset pin, set it to -1 -// -int obdI2CInit(OBDISP *pOBD, int iType, int iAddr, int bFlip, int bInvert, int bWire, int iSDAPin, int iSCLPin, int iResetPin, int32_t iSpeed); -// -// Initialize an SPI version of the display -// -void obdSPIInit(OBDISP *pOBD, int iType, int iDC, int iCS, int iReset, int iMOSI, int iCLK, int iLED, int bFlip, int bInvert, int iBitBang, int32_t iSpeed); -// -// Set the drawing direction in 90 degree increments -// -void obdSetRotation(OBDISP *pOBD, int iRotation); -// -// Set the memory configuration to display the pixels at 0 or 180 degrees (flipped) -// pass true (1) to flip 180, false (0) to set to 0 -void obdSetFlip(OBDISP *pOBD, int iOnOff); -// -// Provide or revoke a back buffer for your OLED graphics -// This allows you to manage the RAM used by ss_oled on tiny -// embedded platforms like the ATmega series -// Pass NULL to revoke the buffer. Make sure you provide a buffer -// large enough for your display (e.g. 128x64 needs 1K - 1024 bytes) -// -void obdSetBackBuffer(OBDISP *pOBD, uint8_t *pBuffer); -void obdAllocBuffer(OBDISP *pOBD); -// -// Sets the brightness (0=off, 255=brightest) -// -void obdSetContrast(OBDISP *pOBD, unsigned char ucContrast); -// -// Load a 1-bpp Windows bitmap -// Pass the pointer to the beginning of the BMP file -// First pass version assumes a full screen bitmap -// -int obdLoadBMP(OBDISP *pOBD, const uint8_t *pBMP, int x, int y, int iFG, int iBG); -// -// load a 4-bpp Windows bitmap -// into memory for 3-color (BLACK/WHITE/RED) -// e-paper displays -// It does a 'best match' of the colors to -// B/W/R -// -int obdLoadBMP3(OBDISP *pOBD, const uint8_t *pBMP, int dx, int dy); -// -// Power up/down the display -// useful for low power situations -// -void obdPower(OBDISP *pOBD, int bOn); -// -// Set the current cursor position -// The column represents the pixel column (0-127) -// The row represents the text row (0-7) -// -void obdSetCursor(OBDISP *pOBD, int x, int y); - -// -// Turn text wrap on or off for the obdWriteString() function -// -void obdSetTextWrap(OBDISP *pOBD, int bWrap); -// -// Advance to the next line -// -void obdNextLine(OBDISP *pOBD); -// -// Draw a string of normal (8x8), small (6x8) or large (16x32) characters -// At the given col+row with the given scroll offset. The scroll offset allows you to -// horizontally scroll text which does not fit on the width of the display. The offset -// represents the pixels to skip when drawing the text. An offset of 0 starts at the beginning -// of the text. -// The system remembers where the last text was written (the cursor position) -// To continue writing from the last position, set the x,y values to -1 -// The text can optionally wrap around to the next line by calling oledSetTextWrap(true); -// otherwise text which would go off the right edge will not be drawn and the cursor will -// be left "off screen" until set to a new position explicitly -// -// Returns 0 for success, -1 for invalid parameter -// -int obdWriteString(OBDISP *pOBD, int iScrollX, int x, int y, char *szMsg, int iSize, int iColor, int bRender); -// -// Draw a string with a fractional scale in both dimensions -// the scale is a 16-bit integer with and 8-bit fraction and 8-bit mantissa -// To draw at 1x scale, set the scale factor to 256. To draw at 2x, use 512 -// The output must be drawn into a memory buffer, not directly to the display -// The string can be drawn in one of 4 rotations (ROT_0, ROT_90, ROT_180, ROT_270) -// -int obdScaledString(OBDISP *pOBD, int x, int y, char *szMsg, int iSize, int iColor, int iXScale, int iYScale, int iRotation); -// -// Draw a string in a proportional font you supply -// Requires a back buffer -// -int obdWriteStringCustom(OBDISP *pOBD, GFXfont *pFont, int x, int y, char *szMsg, uint8_t ucColor); -// -// Get the width of text in a custom font -// -void obdGetStringBox(GFXfont *pFont, char *szMsg, int *width, int *top, int *bottom); -// -// Fill the frame buffer with a byte pattern -// e.g. all off (0x00) or all on (0xff) -// -void obdFill(OBDISP *pOBD, unsigned char ucData, int bRender); -// -// Set (or clear) an individual pixel -// The local copy of the frame buffer is used to avoid -// reading data from the display controller -// (which isn't possible in most configurations) -// This function needs the USE_BACKBUFFER macro to be defined -// otherwise, new pixels will erase old pixels within the same byte -// -int obdSetPixel(OBDISP *pOBD, int x, int y, unsigned char ucColor, int bRender); -// -// Dump a portion of the screen -// optional buffer pointer if no back buffer -// -int obdDumpPartial(OBDISP *pOBD, int startx, int starty, int width, int height, uint8_t *pBuffer); -// -// Dump an entire custom buffer to the display -// useful for custom animation effects -// -int obdDumpBuffer(OBDISP *pOBD, uint8_t *pBuffer, int bRefresh, int bWait, int bFast); -// -// Render a window of pixels from a provided buffer or the library's internal buffer -// to the display. The row values refer to byte rows, not pixel rows due to the memory -// layout of OLEDs. Pass a src pointer of NULL to use the internal backing buffer -// returns 0 for success, -1 for invalid parameter -// -int obdDrawGFX(OBDISP *pOBD, uint8_t *pSrc, int iSrcCol, int iSrcRow, int iDestCol, int iDestRow, int iWidth, int iHeight, int iSrcPitch); -// -// Set the current custom font pointers for playing back -// bytewise commands -// -void obdSetCustomFont(OBDISP *pOBD, GFXfont *pFont, uint8_t ucFont); -// -// Execute a set of bytewise command bytes -// and execute the drawing instructions on the current display/buffer -// Optionally render on backbuffer or physical display -// -void obdExecCommands(uint8_t *pData, int iLen, OBDISP *pOBD, int bRender); -// -// Return the number of bytes accumulated as commands -// -int obdGetCommandLen(OBDISP *pOBD); -// -// Draw a line between 2 points -// -void obdDrawLine(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, int bRender); -// -// Play a frame of animation data -// The animation data is assumed to be encoded for a full frame of the display -// Given the pointer to the start of the compressed data, -// it returns the pointer to the start of the next frame -// Frame rate control is up to the calling program to manage -// When it finishes the last frame, it will start again from the beginning -// -uint8_t * obdPlayAnimFrame(OBDISP *pOBD, uint8_t *pAnimation, uint8_t *pCurrent, int iLen); -void obdWriteCommand(OBDISP *pOBD, unsigned char c); -void obdSetPosition(OBDISP *pOBD, int x, int y, int bRender); -void obdWriteDataBlock(OBDISP *pOBD, unsigned char *ucBuf, int iLen, int bRender); -void RawWriteData(OBDISP *pOBD, unsigned char *ucBuf, int iLen); -// -// Scroll the internal buffer by 1 scanline (up/down) -// width is in pixels, lines is group of 8 rows -// Returns 0 for success, -1 for invalid parameter -// -int obdScrollBuffer(OBDISP *pOBD, int iStartCol, int iEndCol, int iStartRow, int iEndRow, int bUp); -// -// Draw a sprite of any size in any position -// If it goes beyond the left/right or top/bottom edges -// it's trimmed to show the valid parts -// This function requires a back buffer to be defined -// The priority color (0 or 1) determines which color is painted -// when a 1 is encountered in the source image. -// e.g. when 0, the input bitmap acts like a mask to clear -// the destination where bits are set. -// -void obdDrawSprite(OBDISP *pOBD, uint8_t *pSprite, int cx, int cy, int iPitch, int x, int y, uint8_t iPriority); - -// -// Draw 1 or 2 planes of raw image into a specific spot -// in e-paper memory -// -int obdDrawEPDGFX(OBDISP *pOBD, int x, int y, int cx, int cy, uint8_t *pPlane0, uint8_t *pPlane1); - -// -// Draw a 16x16 tile in any of 4 rotated positions -// Assumes input image is laid out like "normal" graphics with -// the MSB on the left and 2 bytes per line -// On AVR, the source image is assumed to be in FLASH memory -// The function can draw the tile on byte boundaries, so the x value -// can be from 0 to 112 and y can be from 0 to 6 -// -void obdDrawTile(OBDISP *pOBD, const uint8_t *pTile, int x, int y, int iRotation, int bInvert, int bRender); -// -// Draw an outline or filled ellipse -// -void obdEllipse(OBDISP *pOBD, int iCenterX, int iCenterY, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, uint8_t bFilled); -// -// Draw an outline or filled rectangle -// -void obdRectangle(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, uint8_t bFilled); - -// -// Turn the LCD backlight on or off -// -void obdBacklight(OBDISP *pODB, int bOn); - -// -// Menu functions -// -// Initialize the simple menu structure -// -int obdMenuInit(OBDISP *pOBD, SIMPLEMENU *sm, char **pText, int iFontSize, int bCenter, int btnUp, int btnDn, int btnEnter, int iPressedState, int bIsRotary); -// -// Erase the display and show the given menu -// -void obdMenuShow(SIMPLEMENU *sm, int iItem); -// -// Set a callback function to return custom info/status -// for each menu item -// -void obdMenuSetCallback(SIMPLEMENU *sm, SIMPLECALLBACK pfnCallBack); -// -// Display the text of a single menu item -// optionally erases what's under it to prevent left-over text when the length changes -// -void obdMenuShowItem(OBDISP *pOBD, int x, int y, char *szText, int bInvert, int bErase, int iFontSize, int bRender); -// -// Change the menu index incrementally -// redraws the minimum amount of screen to show the new info -// (this prevents flicker/flash and saves battery life) -// returns the new menu index -// -int obdMenuDelta(SIMPLEMENU *sm, int iDelta); -// -// With the given setup, check for button presses -// and act accordingly -// returns -1 for normal interactions and the menu item index if the user presses the ENTER button -// -int obdMenuRun(SIMPLEMENU *sm); - -#if defined(_LINUX_) && defined(__cplusplus) -} -#endif // _LINUX_ - -#endif // __ONEBITDISPLAY__ +#endif // __BB_EINK__