mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 05:06:39 +01:00
742 lines
20 KiB
C
Executable File
742 lines
20 KiB
C
Executable File
#include "uc8151.h"
|
|
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
|
|
#include "asmUtil.h"
|
|
#include "barcode.h"
|
|
#include "board.h"
|
|
#include "cpu.h"
|
|
#include "font.h"
|
|
#include "lut.h"
|
|
#include "printf.h"
|
|
#include "screen.h"
|
|
#include "settings.h"
|
|
#include "sleep.h"
|
|
#include "spi.h"
|
|
#include "timer.h"
|
|
#include "wdt.h"
|
|
|
|
#define CMD_PANEL_SETTING 0x00
|
|
#define CMD_POWER_SETTING 0x01
|
|
#define CMD_POWER_OFF 0x02
|
|
#define CMD_POWER_OFF_SEQUENCE 0x03
|
|
#define CMD_POWER_ON 0x04
|
|
#define CMD_POWER_ON_MEASURE 0x05
|
|
#define CMD_BOOSTER_SOFT_START 0x06
|
|
#define CMD_DEEP_SLEEP 0x07
|
|
#define CMD_DISPLAY_START_TRANSMISSION_DTM1 0x10
|
|
#define CMD_DATA_STOP 0x11
|
|
#define CMD_DISPLAY_REFRESH 0x12
|
|
#define CMD_DISPLAY_START_TRANSMISSION_DTM2 0x13
|
|
#define CMD_PLL_CONTROL 0x30
|
|
#define CMD_TEMPERATURE_CALIB 0x40
|
|
#define CMD_TEMPERATURE_SELECT 0x41
|
|
#define CMD_TEMPERATURE_WRITE 0x42
|
|
#define CMD_TEMPERATURE_READ 0x43
|
|
#define CMD_VCOM_INTERVAL 0x50
|
|
#define CMD_LOWER_POWER_DETECT 0x51
|
|
#define CMD_TCON_SETTING 0x60
|
|
#define CMD_RESOLUTION_SETING 0x61
|
|
#define CMD_REVISION 0x70
|
|
#define CMD_STATUS 0x71
|
|
#define CMD_AUTO_MEASUREMENT_VCOM 0x80
|
|
#define CMD_READ_VCOM 0x81
|
|
#define CMD_VCOM_DC_SETTING 0x82
|
|
#define CMD_PARTIAL_WINDOW 0x90
|
|
#define CMD_PARTIAL_IN 0x91
|
|
#define CMD_PARTIAL_OUT 0x92
|
|
#define CMD_PROGRAM_MODE 0xA0
|
|
#define CMD_ACTIVE_PROGRAM 0xA1
|
|
#define CMD_READ_OTP 0xA2
|
|
#define CMD_CASCADE_SET 0xE0
|
|
#define CMD_POWER_SAVING 0xE3
|
|
#define CMD_FORCE_TEMPERATURE 0xE5
|
|
|
|
enum PSR_FLAGS {
|
|
RES_96x230 = 0b00000000,
|
|
RES_96x252 = 0b01000000,
|
|
RES_128x296 = 0b10000000,
|
|
RES_160x296 = 0b11000000,
|
|
|
|
LUT_OTP = 0b00000000,
|
|
LUT_REG = 0b00100000,
|
|
|
|
FORMAT_BWR = 0b00000000,
|
|
FORMAT_BW = 0b00010000,
|
|
|
|
SCAN_DOWN = 0b00000000,
|
|
SCAN_UP = 0b00001000,
|
|
|
|
SHIFT_LEFT = 0b00000000,
|
|
SHIFT_RIGHT = 0b00000100,
|
|
|
|
BOOSTER_OFF = 0b00000000,
|
|
BOOSTER_ON = 0b00000010,
|
|
|
|
RESET_SOFT = 0b00000000,
|
|
RESET_NONE = 0b00000001
|
|
};
|
|
|
|
enum PWR_FLAGS_1 {
|
|
VDS_EXTERNAL = 0b00000000,
|
|
VDS_INTERNAL = 0b00000010,
|
|
|
|
VDG_EXTERNAL = 0b00000000,
|
|
VDG_INTERNAL = 0b00000001
|
|
};
|
|
|
|
enum PWR_FLAGS_2 {
|
|
VCOM_VD = 0b00000000,
|
|
VCOM_VG = 0b00000100,
|
|
|
|
VGHL_16V = 0b00000000,
|
|
VGHL_15V = 0b00000001,
|
|
VGHL_14V = 0b00000010,
|
|
VGHL_13V = 0b00000011
|
|
};
|
|
|
|
enum BOOSTER_FLAGS {
|
|
START_10MS = 0b00000000,
|
|
START_20MS = 0b01000000,
|
|
START_30MS = 0b10000000,
|
|
START_40MS = 0b11000000,
|
|
|
|
STRENGTH_1 = 0b00000000,
|
|
STRENGTH_2 = 0b00001000,
|
|
STRENGTH_3 = 0b00010000,
|
|
STRENGTH_4 = 0b00011000,
|
|
STRENGTH_5 = 0b00100000,
|
|
STRENGTH_6 = 0b00101000,
|
|
STRENGTH_7 = 0b00110000,
|
|
STRENGTH_8 = 0b00111000,
|
|
|
|
OFF_0_27US = 0b00000000,
|
|
OFF_0_34US = 0b00000001,
|
|
OFF_0_40US = 0b00000010,
|
|
OFF_0_54US = 0b00000011,
|
|
OFF_0_80US = 0b00000100,
|
|
OFF_1_54US = 0b00000101,
|
|
OFF_3_34US = 0b00000110,
|
|
OFF_6_58US = 0b00000111
|
|
};
|
|
|
|
enum PFS_FLAGS {
|
|
FRAMES_1 = 0b00000000,
|
|
FRAMES_2 = 0b00010000,
|
|
FRAMES_3 = 0b00100000,
|
|
FRAMES_4 = 0b00110000
|
|
};
|
|
|
|
enum TSE_FLAGS {
|
|
TEMP_INTERNAL = 0b00000000,
|
|
TEMP_EXTERNAL = 0b10000000,
|
|
|
|
OFFSET_0 = 0b00000000,
|
|
OFFSET_1 = 0b00000001,
|
|
OFFSET_2 = 0b00000010,
|
|
OFFSET_3 = 0b00000011,
|
|
OFFSET_4 = 0b00000100,
|
|
OFFSET_5 = 0b00000101,
|
|
OFFSET_6 = 0b00000110,
|
|
OFFSET_7 = 0b00000111,
|
|
|
|
OFFSET_MIN_8 = 0b00001000,
|
|
OFFSET_MIN_7 = 0b00001001,
|
|
OFFSET_MIN_6 = 0b00001010,
|
|
OFFSET_MIN_5 = 0b00001011,
|
|
OFFSET_MIN_4 = 0b00001100,
|
|
OFFSET_MIN_3 = 0b00001101,
|
|
OFFSET_MIN_2 = 0b00001110,
|
|
OFFSET_MIN_1 = 0b00001111
|
|
};
|
|
|
|
enum PLL_FLAGS {
|
|
// other frequency options exist but there doesn't seem to be much
|
|
// point in including them - this is a fair range of options...
|
|
HZ_29 = 0b00111111,
|
|
HZ_33 = 0b00111110,
|
|
HZ_40 = 0b00111101,
|
|
HZ_50 = 0b00111100,
|
|
HZ_67 = 0b00111011,
|
|
HZ_100 = 0b00111010,
|
|
HZ_200 = 0b00111001
|
|
};
|
|
|
|
#define commandEnd() \
|
|
do { \
|
|
P1_7 = 1; \
|
|
} while (0)
|
|
|
|
#define markCommand() \
|
|
do { \
|
|
P2_2 = 0; \
|
|
} while (0)
|
|
|
|
#define markData() \
|
|
do { \
|
|
P2_2 = 1; \
|
|
} while (0)
|
|
|
|
extern void dump(uint8_t* __xdata a, uint16_t __xdata l); // remove me when done
|
|
|
|
static uint8_t __xdata epdCharSize = 1; // character size, 1 or 2 (doubled)
|
|
static bool __xdata directionY = true; // print direction, X or Y (true)
|
|
static uint8_t __xdata rbuffer[32]; // used to rotate bits around
|
|
static uint16_t __xdata fontCurXpos = 0; // current X value we're working with
|
|
static uint16_t __xdata fontCurYpos = 0; // current Y value we're working with
|
|
static uint8_t __xdata currentLut = 0;
|
|
static uint8_t __xdata dispLutSize = 0;
|
|
static bool __xdata drawDirection = false;
|
|
static bool __xdata isInited = false;
|
|
|
|
bool __xdata epdGPIOActive = false;
|
|
|
|
#define LUT_BUFFER_SIZE 128
|
|
uint8_t waveformbuffer[LUT_BUFFER_SIZE];
|
|
struct waveform10* __xdata waveform10 = (struct waveform10*)waveformbuffer; // holds the LUT/waveform
|
|
struct waveform* __xdata waveform7 = (struct waveform*)waveformbuffer; // holds the LUT/waveform
|
|
|
|
#pragma callee_saves epdBusySleep
|
|
#pragma callee_saves epdBusyWait
|
|
static void epdBusySleep(uint32_t timeout) {
|
|
uint8_t tmp_P2FUNC = P2FUNC;
|
|
uint8_t tmp_P2DIR = P2DIR;
|
|
uint8_t tmp_P2PULL = P2PULL;
|
|
uint8_t tmp_P2LVLSEL = P2LVLSEL;
|
|
P2FUNC &= 0xfd;
|
|
P2DIR |= 2;
|
|
P2PULL |= 2;
|
|
P2LVLSEL &= ~(2);
|
|
|
|
P2CHSTA &= 0xfd;
|
|
P2INTEN |= 2;
|
|
P2CHSTA &= 0xfd;
|
|
sleepForMsec(timeout);
|
|
wdtOn();
|
|
P2CHSTA &= 0xfd;
|
|
P2INTEN &= 0xfd;
|
|
|
|
P2FUNC = tmp_P2FUNC;
|
|
P2DIR = tmp_P2DIR;
|
|
P2PULL = tmp_P2PULL;
|
|
P2LVLSEL = tmp_P2LVLSEL;
|
|
eepromPrvDeselect();
|
|
}
|
|
static void epdBusyWait(uint32_t timeout) {
|
|
uint32_t __xdata start = timerGet();
|
|
|
|
while (timerGet() - start < timeout) {
|
|
if (P2_1)
|
|
return;
|
|
}
|
|
pr("screen timeout %lu ticks :(\n", timerGet() - start);
|
|
while (1)
|
|
;
|
|
}
|
|
static void commandReadBegin(uint8_t cmd) {
|
|
epdSelect();
|
|
markCommand();
|
|
spiByte(cmd); // dump LUT
|
|
|
|
P0DIR = (P0DIR & ~(1 << 0)) | (1 << 1);
|
|
P0 &= ~(1 << 0);
|
|
P0FUNC &= ~((1 << 0) | (1 << 1));
|
|
P2_2 = 1;
|
|
}
|
|
static void commandReadEnd() {
|
|
// set up pins for spi (0.0,0.1,0.2)
|
|
P0FUNC |= (1 << 0) | (1 << 1);
|
|
epdDeselect();
|
|
}
|
|
#pragma callee_saves epdReadByte
|
|
static uint8_t epdReadByte() {
|
|
uint8_t val = 0, i;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
P0_0 = 1;
|
|
__asm__("nop\nnop\nnop\nnop\nnop\nnop\n");
|
|
val <<= 1;
|
|
if (P0_1)
|
|
val++;
|
|
P0_0 = 0;
|
|
__asm__("nop\nnop\nnop\nnop\nnop\nnop\n");
|
|
}
|
|
|
|
return val;
|
|
}
|
|
static void shortCommand(uint8_t cmd) {
|
|
epdSelect();
|
|
markCommand();
|
|
spiTXByte(cmd);
|
|
epdDeselect();
|
|
}
|
|
static void shortCommand1(uint8_t cmd, uint8_t arg) {
|
|
epdSelect();
|
|
markCommand();
|
|
spiTXByte(cmd);
|
|
markData();
|
|
spiTXByte(arg);
|
|
epdDeselect();
|
|
}
|
|
static void shortCommand2(uint8_t cmd, uint8_t arg1, uint8_t arg2) {
|
|
epdSelect();
|
|
markCommand();
|
|
spiTXByte(cmd);
|
|
markData();
|
|
spiTXByte(arg1);
|
|
spiTXByte(arg2);
|
|
epdDeselect();
|
|
}
|
|
static void commandBegin(uint8_t cmd) {
|
|
epdSelect();
|
|
markCommand();
|
|
spiTXByte(cmd);
|
|
markData();
|
|
}
|
|
static void epdReset() {
|
|
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
|
|
P2_0 = 0;
|
|
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
|
|
P2_0 = 1;
|
|
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
|
|
}
|
|
void epdConfigGPIO(bool setup) {
|
|
// data / _command: 2.2
|
|
// busy 2.1
|
|
// reset 2.0
|
|
// _select 1.7
|
|
// bs1 1.2
|
|
|
|
// GENERIC SPI BUS PINS
|
|
// spi.clk 0.0
|
|
// spi.mosi 0.1
|
|
if (epdGPIOActive == setup) return;
|
|
if (setup) {
|
|
P2DIR |= (1 << 1); // busy as input
|
|
P2DIR &= ~((1 << 2) | (1 << 0)); // D/C and Reset as output
|
|
P1DIR &= ~((1 << 7) | (1 << 2)); // select and bs1 as output
|
|
P1_2 = 0; // select 4-wire SPI / BS1 = low
|
|
P1_7 = 1; // deselect EPD
|
|
} else {
|
|
P2DIR |= ((1 << 2) | (1 << 0)); // DC and Reset as input
|
|
P2 &= ~((1 << 2) | (1 << 0));
|
|
P1DIR |= ((1 << 7) | (1 << 2)); // Select and BS1 as input
|
|
P2 &= ~((1 << 7));
|
|
}
|
|
epdGPIOActive = setup;
|
|
}
|
|
void epdEnterSleep() {
|
|
timerDelay(20 * TIMER_TICKS_PER_MS);
|
|
epdReset();
|
|
timerDelay(20 * TIMER_TICKS_PER_MS);
|
|
shortCommand1(CMD_VCOM_INTERVAL, 0x17);
|
|
shortCommand1(CMD_VCOM_DC_SETTING, 0x00);
|
|
// shortCommand(CMD_POWER_OFF);
|
|
// epdWaitRdy();
|
|
shortCommand1(CMD_DEEP_SLEEP, 0xA5);
|
|
isInited = false;
|
|
}
|
|
|
|
static void epdDrawDirection(bool direction) {
|
|
if (direction == drawDirection) return;
|
|
|
|
drawDirection = direction;
|
|
|
|
uint8_t psr_setting = RES_128x296 | FORMAT_BWR | BOOSTER_ON | RESET_NONE | LUT_OTP | SHIFT_RIGHT;
|
|
if (drawDirection) {
|
|
psr_setting |= SCAN_DOWN;
|
|
} else {
|
|
psr_setting |= SCAN_UP;
|
|
}
|
|
shortCommand1(CMD_PANEL_SETTING, psr_setting);
|
|
}
|
|
|
|
void epdSetup() {
|
|
epdReset();
|
|
|
|
drawDirection = false;
|
|
epdDrawDirection(true);
|
|
|
|
commandBegin(CMD_POWER_SETTING);
|
|
epdSend(VDS_INTERNAL | VDG_INTERNAL);
|
|
epdSend(VCOM_VD | VGHL_16V);
|
|
epdSend(0b101011);
|
|
epdSend(0b101011);
|
|
epdSend(0b101011);
|
|
commandEnd();
|
|
|
|
shortCommand(CMD_POWER_ON);
|
|
epdWaitRdy();
|
|
|
|
commandBegin(CMD_BOOSTER_SOFT_START);
|
|
epdSend(START_10MS | STRENGTH_3 | OFF_6_58US);
|
|
epdSend(START_10MS | STRENGTH_3 | OFF_6_58US);
|
|
epdSend(START_10MS | STRENGTH_3 | OFF_6_58US);
|
|
commandEnd();
|
|
|
|
commandBegin(CMD_RESOLUTION_SETING);
|
|
epdSend(SCREEN_WIDTH);
|
|
epdSend(SCREEN_HEIGHT >> 8);
|
|
epdSend(SCREEN_HEIGHT & 0xFF);
|
|
commandEnd();
|
|
|
|
shortCommand1(CMD_POWER_OFF_SEQUENCE, FRAMES_1);
|
|
shortCommand1(CMD_TEMPERATURE_SELECT, TEMP_INTERNAL | OFFSET_0);
|
|
shortCommand1(CMD_TCON_SETTING, 0x22);
|
|
shortCommand1(CMD_VCOM_INTERVAL, 0x8d); // 0x87
|
|
shortCommand1(CMD_PLL_CONTROL, HZ_200);
|
|
epdWaitRdy();
|
|
shortCommand(CMD_POWER_ON);
|
|
epdWaitRdy();
|
|
}
|
|
static uint8_t epdGetStatus() {
|
|
uint8_t sta;
|
|
commandReadBegin(0x2F);
|
|
sta = epdReadByte();
|
|
commandReadEnd();
|
|
return sta;
|
|
}
|
|
uint16_t epdGetBattery(void) {
|
|
uint8_t sta;
|
|
commandReadBegin(0x51);
|
|
sta = epdReadByte();
|
|
commandReadEnd();
|
|
if (sta) return 3000;
|
|
return 2100;
|
|
}
|
|
|
|
|
|
void selectLUT(uint8_t lut) {
|
|
// implement alternative LUTs here. Currently just reset the watchdog to two minutes,
|
|
// to ensure it doesn't reset during the much longer bootup procedure
|
|
lut += 1; // make the compiler a happy camper
|
|
wdtSetResetVal(0xFF8E797F); // 120 s
|
|
wdtOn();
|
|
return;
|
|
}
|
|
|
|
void setWindowXY(uint16_t xstart, uint16_t xend, uint16_t ystart, uint16_t yend) {
|
|
shortCommand(CMD_PARTIAL_IN);
|
|
commandBegin(CMD_PARTIAL_WINDOW);
|
|
epdSend((xstart / 8) << 3);
|
|
epdSend(((xend / 8 - 1) << 3) | 0x07);
|
|
epdSend(ystart >> 8);
|
|
epdSend(ystart & 0xFF);
|
|
epdSend((yend - 1) >> 8);
|
|
epdSend((yend - 1) & 0xff);
|
|
epdSend(0x01);
|
|
commandEnd();
|
|
}
|
|
|
|
void setColorMode(uint8_t red, uint8_t bw) {
|
|
// this does exactly nothing, just keeps the compiler from barking
|
|
red = 1;
|
|
bw = 0;
|
|
return;
|
|
}
|
|
void clearScreen() {
|
|
shortCommand(CMD_PARTIAL_OUT);
|
|
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM2);
|
|
for (uint16_t c = 0; c < ((1UL * SCREEN_HEIGHT * SCREEN_WIDTH) / 8); c++) {
|
|
epdSend(0x00);
|
|
}
|
|
commandEnd();
|
|
epdWaitRdy();
|
|
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM1);
|
|
for (uint16_t c = 0; c < ((1UL * SCREEN_HEIGHT * SCREEN_WIDTH) / 8); c++) {
|
|
epdSend(0x00);
|
|
}
|
|
commandEnd();
|
|
}
|
|
void draw() {
|
|
shortCommand(CMD_DISPLAY_REFRESH);
|
|
epdWaitRdy();
|
|
}
|
|
void drawNoWait() {
|
|
shortCommand(CMD_DISPLAY_REFRESH);
|
|
}
|
|
void drawWithSleep() {
|
|
shortCommand(CMD_DISPLAY_REFRESH);
|
|
uint8_t tmp_P2FUNC = P2FUNC;
|
|
uint8_t tmp_P2DIR = P2DIR;
|
|
uint8_t tmp_P2PULL = P2PULL;
|
|
uint8_t tmp_P2LVLSEL = P2LVLSEL;
|
|
P2FUNC &= 0xfd;
|
|
P2DIR |= 2;
|
|
P2PULL |= 2;
|
|
P2LVLSEL &= ~(2);
|
|
|
|
P2CHSTA &= 0xfd;
|
|
P2INTEN = 2;
|
|
P2CHSTA &= 0xfd;
|
|
sleepForMsec(TIMER_TICKS_PER_SECOND * 120);
|
|
wdtOn();
|
|
P2CHSTA &= 0xfd;
|
|
P2INTEN &= 0xfd;
|
|
|
|
P2FUNC = tmp_P2FUNC;
|
|
P2DIR = tmp_P2DIR;
|
|
P2PULL = tmp_P2PULL;
|
|
P2LVLSEL = tmp_P2LVLSEL;
|
|
eepromPrvDeselect();
|
|
}
|
|
void epdWaitRdy() {
|
|
epdBusyWait(TIMER_TICKS_PER_SECOND * 120);
|
|
}
|
|
void beginFullscreenImage() {
|
|
shortCommand(CMD_PARTIAL_OUT);
|
|
epdDrawDirection(false);
|
|
// shortCommand1(CMD_DATA_ENTRY_MODE, 3);
|
|
// setPosXY(0, 0);
|
|
}
|
|
void beginWriteFramebuffer(bool color) {
|
|
if (color == EPD_COLOR_RED) {
|
|
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM2);
|
|
} else {
|
|
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM1);
|
|
}
|
|
epdDeselect();
|
|
}
|
|
void endWriteFramebuffer() {
|
|
commandEnd();
|
|
}
|
|
|
|
void loadRawBitmap(uint8_t* bmp, uint16_t x, uint16_t y, bool color) __reentrant {
|
|
// this function is very badly hurt by the switch to UC8151, taking up LOTS of valuable idata space. Only defining variables
|
|
// as static, or the function as reentrant (relegating variables to the stack) seemed to fix the idata issue. Fix me, or put me out of my misery...
|
|
|
|
uint16_t xsize = bmp[0] / 8;
|
|
if (bmp[0] % 8) xsize++;
|
|
uint16_t ysize = bmp[1];
|
|
uint16_t size = xsize * bmp[1];
|
|
|
|
// shortCommand1(CMD_DATA_ENTRY_MODE, 3);
|
|
|
|
bmp += 2;
|
|
|
|
uint16_t c = 0;
|
|
uint16_t curY = y;
|
|
while (1) {
|
|
if (c % xsize == 0) {
|
|
commandEnd();
|
|
if (drawDirection) {
|
|
setWindowXY(x, x + xsize * 8, SCREEN_HEIGHT - curY - 1, SCREEN_HEIGHT - curY);
|
|
} else {
|
|
setWindowXY(x, x + xsize * 8, curY - 1, curY);
|
|
}
|
|
curY++;
|
|
if (color) {
|
|
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM2);
|
|
} else {
|
|
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM1);
|
|
}
|
|
}
|
|
epdSend(*(bmp++));
|
|
c++;
|
|
if (!size--) break;
|
|
}
|
|
commandEnd();
|
|
shortCommand(CMD_PARTIAL_OUT);
|
|
}
|
|
void printBarcode(const uint8_t* string, uint16_t x, uint16_t y) {
|
|
setWindowXY(x, x + 8, SCREEN_HEIGHT - y, SCREEN_HEIGHT);
|
|
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM1);
|
|
struct BarcodeInfo __xdata bci = {
|
|
.str = string,
|
|
};
|
|
while (!barcodeIsDone(&bci)) {
|
|
if (barcodeNextBar(&bci)) {
|
|
epdSend(0xFF);
|
|
} else {
|
|
epdSend(0x00);
|
|
}
|
|
}
|
|
commandEnd();
|
|
shortCommand(CMD_PARTIAL_OUT);
|
|
}
|
|
// stuff for printing text
|
|
static void pushXFontBytesToEPD(uint8_t byte1, uint8_t byte2) {
|
|
if (epdCharSize == 1) {
|
|
uint8_t offset = 7 - (fontCurXpos % 8);
|
|
for (uint8_t c = 0; c < 8; c++) {
|
|
if (byte2 & (1 << (7 - c))) rbuffer[c] |= (1 << offset);
|
|
}
|
|
for (uint8_t c = 0; c < 8; c++) {
|
|
if (byte1 & (1 << (7 - c))) rbuffer[8 + c] |= (1 << offset);
|
|
}
|
|
fontCurXpos++;
|
|
} else {
|
|
uint8_t offset = 6 - (fontCurXpos % 8);
|
|
// double font size
|
|
for (uint8_t c = 0; c < 8; c++) {
|
|
if (byte2 & (1 << (7 - c))) {
|
|
rbuffer[c * 2] |= (3 << offset);
|
|
rbuffer[(c * 2) + 1] |= (3 << offset);
|
|
}
|
|
}
|
|
for (uint8_t c = 0; c < 8; c++) {
|
|
if (byte1 & (1 << (7 - c))) {
|
|
rbuffer[(c * 2) + 16] |= (3 << offset);
|
|
rbuffer[(c * 2) + 17] |= (3 << offset);
|
|
}
|
|
}
|
|
fontCurXpos += 2;
|
|
}
|
|
if (fontCurXpos % 8 == 0) {
|
|
// next byte, flush current byte to EPD
|
|
for (uint8_t i = 0; i < (16 * epdCharSize); i++) {
|
|
epdSend(rbuffer[i]);
|
|
}
|
|
memset(rbuffer, 0, 32);
|
|
}
|
|
}
|
|
static void bufferByteShift(uint8_t byte) {
|
|
/*
|
|
rbuffer[0] = 0; // previous value
|
|
rbuffer[1] = y%8; // offset
|
|
rbuffer[2] = 0; // current byte counter;
|
|
rbuffer[3] = 1+(epdCharsize*2);
|
|
*/
|
|
|
|
if (rbuffer[1] == 0) {
|
|
epdSend(byte);
|
|
} else {
|
|
uint8_t offset = rbuffer[1];
|
|
rbuffer[0] |= (byte >> offset);
|
|
epdSend(rbuffer[0]);
|
|
// epdSend(byte);
|
|
rbuffer[0] = (byte << (8 - offset));
|
|
rbuffer[2]++;
|
|
if (rbuffer[2] == rbuffer[3]) {
|
|
epdSend(rbuffer[0]);
|
|
rbuffer[0] = 0;
|
|
rbuffer[2] = 0;
|
|
}
|
|
}
|
|
}
|
|
static void pushYFontBytesToEPD(uint8_t byte1, uint8_t byte2) {
|
|
if (epdCharSize == 2) {
|
|
for (uint8_t j = 0; j < 2; j++) {
|
|
uint8_t c = 0;
|
|
for (uint8_t i = 7; i != 255; i--) {
|
|
if (byte1 & (1 << i)) c |= (0x03 << ((i % 4) * 2));
|
|
if ((i % 4) == 0) {
|
|
bufferByteShift(c);
|
|
c = 0;
|
|
}
|
|
}
|
|
for (uint8_t i = 7; i != 255; i--) {
|
|
if (byte2 & (1 << i)) c |= (0x03 << ((i % 4) * 2));
|
|
if ((i % 4) == 0) {
|
|
bufferByteShift(c);
|
|
c = 0;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
bufferByteShift(byte1);
|
|
bufferByteShift(byte2);
|
|
}
|
|
}
|
|
void writeCharEPD(uint8_t c) {
|
|
// Writes a single character to the framebuffer
|
|
bool empty = true;
|
|
for (uint8_t i = 0; i < 20; i++) {
|
|
if (font[c][i]) empty = false;
|
|
}
|
|
if (empty) {
|
|
for (uint8_t i = 0; i < 8; i++) {
|
|
if (directionY) {
|
|
pushYFontBytesToEPD(0x00, 0x00);
|
|
} else {
|
|
pushXFontBytesToEPD(0x00, 0x00);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
uint8_t begin = 0;
|
|
while (font[c][begin] == 0x00 && font[c][begin + 1] == 0x00) {
|
|
begin += 2;
|
|
}
|
|
|
|
uint8_t end = 20;
|
|
while (font[c][end - 1] == 0x00 && font[c][end - 2] == 0x00) {
|
|
end -= 2;
|
|
}
|
|
|
|
for (uint8_t pos = begin; pos < end; pos += 2) {
|
|
if (directionY) {
|
|
pushYFontBytesToEPD(font[c][pos + 1], font[c][pos]);
|
|
} else {
|
|
pushXFontBytesToEPD(font[c][pos], font[c][pos + 1]);
|
|
}
|
|
}
|
|
|
|
// spacing between characters
|
|
if (directionY) {
|
|
pushYFontBytesToEPD(0x00, 0x00);
|
|
} else {
|
|
pushXFontBytesToEPD(0x00, 0x00);
|
|
}
|
|
}
|
|
|
|
// Print text to the EPD. Origin is top-left
|
|
void epdPrintBegin(uint16_t x, uint16_t y, bool direction, bool fontsize, bool color) {
|
|
directionY = direction;
|
|
epdCharSize = 1 + fontsize;
|
|
if (directionY) {
|
|
uint8_t extra = 0;
|
|
|
|
// provisions for dealing with font in Y direction, byte-unaligned
|
|
if (x % 8) {
|
|
extra = 8;
|
|
rbuffer[0] = 0; // previous value
|
|
rbuffer[1] = x % 8; // offset
|
|
rbuffer[2] = 0; // current byte counter;
|
|
rbuffer[3] = (epdCharSize * 2);
|
|
} else {
|
|
rbuffer[1] = 0;
|
|
}
|
|
|
|
// setWindowY(y, 1);
|
|
if (epdCharSize == 2) {
|
|
setWindowXY(x, x + 32 + extra, SCREEN_HEIGHT - y, SCREEN_HEIGHT);
|
|
// setPosXY(x, y);
|
|
} else {
|
|
setWindowXY(x, x + 16 + extra, SCREEN_HEIGHT - y, SCREEN_HEIGHT);
|
|
// setPosXY(x, y);
|
|
}
|
|
// shortCommand1(CMD_DATA_ENTRY_MODE, 1); // was 3
|
|
} else {
|
|
if (epdCharSize == 2) {
|
|
x /= 2;
|
|
x *= 2;
|
|
setWindowXY(x, SCREEN_WIDTH, y, y + 32);
|
|
} else {
|
|
setWindowXY(x, SCREEN_WIDTH, y, y + 16);
|
|
}
|
|
// setPosXY(x, y);
|
|
fontCurXpos = x;
|
|
// setWindowXY(x, SCREEN_WIDTH);
|
|
// shortCommand1(CMD_DATA_ENTRY_MODE, 7);
|
|
memset(rbuffer, 0, 32);
|
|
}
|
|
|
|
if (color) {
|
|
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM2);
|
|
} else {
|
|
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM1);
|
|
}
|
|
}
|
|
void epdPrintEnd() {
|
|
if (!directionY && ((fontCurXpos % 8) != 0)) {
|
|
for (uint8_t i = 0; i < (16 * epdCharSize); i++) {
|
|
epdSend(rbuffer[i]);
|
|
}
|
|
}
|
|
commandEnd();
|
|
shortCommand(CMD_PARTIAL_OUT);
|
|
epdDrawDirection(true);
|
|
} |