mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-23 06:07:42 +01:00
391 lines
10 KiB
C
Executable File
391 lines
10 KiB
C
Executable File
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "printf.h"
|
|
#include "sem9010_uk.h"
|
|
#include "spi.h"
|
|
#include "timer.h"
|
|
#include "zbs243.h"
|
|
|
|
__bit display_is_drawing = 0;
|
|
uint32_t __xdata screen_start_time = 0;
|
|
__bit isInverted = false;
|
|
|
|
uint8_t __xdata curEpdSegmentData[12] = {0};
|
|
uint8_t __xdata epdSegmentData[12] = {0};
|
|
|
|
#pragma callee_saves screenPrvTimedWait
|
|
static __bit screenPrvTimedWait(uint32_t maxTicks) {
|
|
uint32_t startTicks = timerGet();
|
|
|
|
while (timerGet() - startTicks < maxTicks) {
|
|
if (!P2_0)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#pragma callee_saves screenPrvRegWriteGuts
|
|
static __bit screenPrvRegWriteGuts(uint32_t val, uint8_t reg) { // order because sdcc sucks
|
|
if (!screenPrvTimedWait(TIMER_TICKS_PER_SECOND / 1000))
|
|
return false;
|
|
|
|
P2_1 = 0;
|
|
spiByte((uint8_t)0x80 + (uint8_t)(reg << 1));
|
|
spiByte(val >> 16);
|
|
spiByte(val >> 8);
|
|
spiByte(val);
|
|
P2_1 = 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
void epdWaitRdy() {
|
|
if (!screenPrvTimedWait(TIMER_TICKS_PER_SECOND / 1000))
|
|
return;
|
|
}
|
|
#define screenPrvRegWrite(_reg, _val) screenPrvRegWriteGuts(_val, _reg)
|
|
static uint8_t charDecode(uint8_t c) {
|
|
const uint8_t __code numbers[] = {0b11110110, 0b11000000, 0b01101110, 0b11101010, 0b11011000, 0b10111010, 0b10111110, 0b11100000, 0b11111110, 0b11111010};
|
|
|
|
static const uint8_t __code lowercase[] = {
|
|
0b11111100,
|
|
0b10011110,
|
|
0b00001110,
|
|
0b11001110,
|
|
0b00111110,
|
|
0b00111100,
|
|
0b11111010,
|
|
0b10011100,
|
|
0b10000000,
|
|
0b11000010,
|
|
0b00011100,
|
|
0b00000110,
|
|
0b10101100,
|
|
0b10001100,
|
|
0b10001110,
|
|
0b01111100,
|
|
0b11111000,
|
|
0b00001100,
|
|
0b10111010,
|
|
0b00011110,
|
|
0b10000110,
|
|
0b10010110,
|
|
0b01011010,
|
|
0b10001000,
|
|
0b11011010,
|
|
0b01101110,
|
|
};
|
|
|
|
static const uint8_t __code uppercase[] = {
|
|
0b11111100,
|
|
0b10011110,
|
|
0b00110110,
|
|
0b11001110,
|
|
0b00111110,
|
|
0b00111100,
|
|
0b10110110,
|
|
0b11011100,
|
|
0b11000000,
|
|
0b11000010,
|
|
0b00011100,
|
|
0b00010110,
|
|
0b10101100,
|
|
0b11110100,
|
|
0b11110110,
|
|
0b01111100,
|
|
0b11111000,
|
|
0b00001100,
|
|
0b10111010,
|
|
0b00011110,
|
|
0b11010110,
|
|
0b10010110,
|
|
0b01011010,
|
|
0b10001000,
|
|
0b11011010,
|
|
0b01101110,
|
|
};
|
|
|
|
if (c > 0x2F && c < 0x3A) {
|
|
return numbers[c - 0x30];
|
|
}
|
|
|
|
if (c > 0x60 && c < 0x7b) {
|
|
return lowercase[c - 0x61];
|
|
}
|
|
|
|
if (c > 0x40 && c < 0x5B) {
|
|
return uppercase[c - 0x41];
|
|
}
|
|
// / \\__5__//
|
|
// | | | |
|
|
// | 4 | | 6 |
|
|
// | | | |
|
|
// \ /< 3 >\ /
|
|
// / \ /
|
|
// | | | |
|
|
// | 2 | | 7 |
|
|
// | | | |
|
|
// \ //__1__\\ /
|
|
switch (c) {
|
|
case 0x20: // space
|
|
return 0x00;
|
|
case 0x2D: // -
|
|
return 0b00001000;
|
|
case 0x28: // (
|
|
case 0x5B: // [
|
|
return 0b00110110;
|
|
case 0x29: // )
|
|
case 0x5D: // ]
|
|
return 0b11100010;
|
|
case 0x3C: // <
|
|
return 0b00001110;
|
|
case 0x3E: // >
|
|
return 0b10001010;
|
|
case 0x5F: // _
|
|
return 0b00000010;
|
|
case 0x5E: // ^
|
|
return 0b01111000;
|
|
case 0x3D: // =
|
|
return 0b00001010;
|
|
case 0x23: // #
|
|
return 0b10000100;
|
|
default:
|
|
return 0x00;
|
|
}
|
|
}
|
|
static uint8_t segmentBitSwap(uint8_t val) {
|
|
uint8_t __xdata ret = 0;
|
|
if (val & 0x02) ret |= (1 << 5);
|
|
if (val & 0x04) ret |= (1 << 6);
|
|
if (val & 0x08) ret |= (1 << 3);
|
|
if (val & 0x10) ret |= (1 << 7);
|
|
if (val & 0x20) ret |= (1 << 1);
|
|
if (val & 0x40) ret |= (1 << 2);
|
|
if (val & 0x80) ret |= (1 << 4);
|
|
return ret;
|
|
}
|
|
static void writeDigitToBuffer(uint8_t pos, uint8_t c) {
|
|
switch (pos) {
|
|
case 0:
|
|
case 1:
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
epdSegmentData[pos] &= 0x01;
|
|
epdSegmentData[pos] |= charDecode(c);
|
|
break;
|
|
case 2:
|
|
epdSegmentData[3] &= 0x01;
|
|
epdSegmentData[3] |= segmentBitSwap(charDecode(c));
|
|
break;
|
|
case 3:
|
|
epdSegmentData[2] &= 0x01;
|
|
epdSegmentData[2] |= segmentBitSwap(charDecode(c));
|
|
break;
|
|
case 7:
|
|
epdSegmentData[9] &= 0x01;
|
|
epdSegmentData[9] |= segmentBitSwap(charDecode(c));
|
|
break;
|
|
case 8:
|
|
epdSegmentData[8] &= 0x01;
|
|
epdSegmentData[8] |= segmentBitSwap(charDecode(c));
|
|
break;
|
|
case 9:
|
|
epdSegmentData[7] &= 0x01;
|
|
epdSegmentData[7] |= segmentBitSwap(charDecode(c));
|
|
break;
|
|
}
|
|
}
|
|
static void iconSet(uint8_t byte, uint8_t bit, bool on) {
|
|
if (on) {
|
|
epdSegmentData[byte] |= (1 << bit);
|
|
} else {
|
|
epdSegmentData[byte] &= ~(1 << bit);
|
|
}
|
|
}
|
|
|
|
void setEPDIcon(uint16_t iconvalue, bool on) {
|
|
if (iconvalue & EPD_SIGN_POUND_LARGE)
|
|
iconSet(2, 0, on);
|
|
if (iconvalue & EPD_DIGIT_ONE_LARGE)
|
|
iconSet(1, 0, on);
|
|
if (iconvalue & EPD_PERIOD_LARGE)
|
|
iconSet(3, 0, on);
|
|
if (iconvalue & EPD_SIGN_PENCE_LARGE)
|
|
iconSet(4, 0, on);
|
|
if (iconvalue & EPD_BG_COLOR)
|
|
iconSet(0, 0, on);
|
|
if (iconvalue & EPD_SIGN_POUND_SMALL)
|
|
iconSet(6, 0, on);
|
|
if (iconvalue & EPD_DIGIT_ONE_SMALL)
|
|
iconSet(7, 0, on);
|
|
if (iconvalue & EPD_PERIOD_SMALL)
|
|
iconSet(9, 0, on);
|
|
if (iconvalue & EPD_SIGN_PENCE_SMALL)
|
|
iconSet(8, 0, on);
|
|
if (iconvalue & EPD_ICON_DIAMOND)
|
|
iconSet(10, 0, on);
|
|
if (iconvalue & EPD_ICON_ARROW)
|
|
iconSet(10, 1, on);
|
|
if (iconvalue & EPD_ICON_STAR)
|
|
iconSet(10, 2, on);
|
|
if (iconvalue & EPD_ICON_CIRCLE)
|
|
iconSet(10, 3, on);
|
|
if (iconvalue & EPD_ICON_SQUARE)
|
|
iconSet(10, 4, on);
|
|
}
|
|
void epdEnable() {
|
|
// P2_0 = BUSY
|
|
// P2_1 = _CS
|
|
// P2_2 = power
|
|
// P1_7 = _RESET
|
|
// P1_6 = EXT_CLK
|
|
// P0_0 = CLK
|
|
// P0_1 = MoSi
|
|
// P0_2 = MiSo
|
|
P2FUNC &= ~((1 << 0) | (1 << 1) | (1 << 2)); // disable functions on busy, _cs, power
|
|
P2DIR &= ~((1 << 1) | (1 << 2)); // _CS 2.1 and POWER 2.2(output)
|
|
P2DIR |= (1 << 0); // BUSY (input)
|
|
//
|
|
P1DIR &= ~((1 << 7) | (1 << 6)); // _RESET and EPD_EXT_CLK as output
|
|
P1FUNC &= ~(1 << 7); // disable function on _reset
|
|
P1FUNC |= (1 << 6); // EPD_EXT_CLK-out enable
|
|
//
|
|
P0FUNC |= (1 << 0) | (1 << 1) | (1 << 2); // enable clk, mosi, miso functions
|
|
P0DIR &= ~((1 << 0) | (1 << 1)); // enable output on clk, mosi
|
|
P0DIR |= (1 << 2); // miso as input
|
|
P0PULL = (1 << 2); // pullup on miso
|
|
|
|
spiInit();
|
|
}
|
|
void epdDisable() {
|
|
P1_7 = 0; // assert reset
|
|
// timerDelay(TIMER_TICKS_PER_SECOND / 100);
|
|
P2_2 = 0; // power it down
|
|
|
|
// shutdown
|
|
P2DIR |= (1 << 1) | (1 << 2); // _cs and power as input
|
|
P1DIR |= (1 << 7) | (1 << 6); // reset and epd-ext-clk as input
|
|
P0DIR |= (1 << 0) | (1 << 1); // mosi/clk as input
|
|
P0PULL &= ~(1 << 2); // disable miso pullup
|
|
P1FUNC &= ~(1 << 6); // disable clock output
|
|
P0FUNC &= ~((1 << 0) | (1 << 1) | (1 << 2)); // disable SPI functions
|
|
}
|
|
|
|
__bit epdDraw() {
|
|
// data (doesnt agree with spec but makes sense)
|
|
if (!screenPrvRegWrite(0x0d, *(uint32_t __xdata *)(epdSegmentData + 0)))
|
|
return false;
|
|
if (!screenPrvRegWrite(0x0e, *(uint32_t __xdata *)(epdSegmentData + 3)))
|
|
return false;
|
|
if (!screenPrvRegWrite(0x0f, *(uint32_t __xdata *)(epdSegmentData + 6)))
|
|
return false;
|
|
if (!screenPrvRegWrite(0x10, *(uint32_t __xdata *)(epdSegmentData + 9)))
|
|
return false;
|
|
// update!
|
|
if (!screenPrvRegWrite(0x00, isInverted ? 0xa0001c : 0x80001c))
|
|
return false;
|
|
|
|
timerDelay(TIMER_TICKS_PER_SECOND / 10000);
|
|
memcpy(curEpdSegmentData, epdSegmentData, sizeof(epdSegmentData));
|
|
screen_start_time = timerGet();
|
|
display_is_drawing = 1;
|
|
return true;
|
|
}
|
|
|
|
__bit epdUpdate() {
|
|
if (memcmp(curEpdSegmentData, epdSegmentData, sizeof(epdSegmentData)) == 0) {
|
|
return false;
|
|
} else {
|
|
return epdDraw();
|
|
}
|
|
}
|
|
|
|
__bit epdSetup(__bit inverted) {
|
|
// P2DIR = 0b11111001;
|
|
isInverted = inverted;
|
|
display_is_drawing = 0;
|
|
P2_1 = 1;
|
|
P2_2 = 1; // power it up
|
|
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
|
|
P1_7 = 0; // assert reset
|
|
timerDelay(TIMER_TICKS_PER_SECOND / 100);
|
|
P1_7 = 1; // release reset
|
|
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
|
|
|
|
// wait for it
|
|
if (!screenPrvTimedWait(TIMER_TICKS_PER_SECOND)) {
|
|
return false;
|
|
}
|
|
|
|
// internal oscillator init?
|
|
// if (!screenPrvRegWrite(0x03, 0x3a0000))
|
|
// return false;
|
|
// iface init?
|
|
if (!screenPrvRegWrite(0x01, 0x070000))
|
|
return false;
|
|
if (!screenPrvRegWrite(0x03, 0x400000))
|
|
return false;
|
|
if (!screenPrvRegWrite(0x04, 0xfc0000))
|
|
return false;
|
|
// LUTs
|
|
if (!screenPrvRegWrite(0x14, 0x440000))
|
|
return false;
|
|
if (!screenPrvRegWrite(0x15, inverted ? 0x680001 : 0x860000))
|
|
return false;
|
|
if (!screenPrvRegWrite(0x16, inverted ? 0x240000 : 0x040000))
|
|
return false;
|
|
|
|
if (!screenPrvRegWrite(0x19, 0x082514))
|
|
return false;
|
|
if (!screenPrvRegWrite(0x1a, 0xa02000))
|
|
return false;
|
|
memset(curEpdSegmentData, 0, sizeof(epdSegmentData));
|
|
return true;
|
|
}
|
|
void epdClear() {
|
|
memset(epdSegmentData, 0, sizeof(epdSegmentData));
|
|
}
|
|
|
|
static uint8_t __xdata position = 0;
|
|
|
|
void epdSetPos(uint8_t p) {
|
|
if (p >= sizeof(epdSegmentData)) {
|
|
p = 0;
|
|
}
|
|
position = p;
|
|
}
|
|
|
|
void writeCharEPD(char c) {
|
|
writeDigitToBuffer(position, c);
|
|
position++;
|
|
if (position >= sizeof(epdSegmentData)) {
|
|
position = 0;
|
|
}
|
|
}
|
|
|
|
void epdPrint(uint8_t pos, const char __code *fmt, ...) __reentrant {
|
|
static uint8_t __xdata buffer[10];
|
|
va_list vl;
|
|
va_start(vl, fmt);
|
|
spr(buffer, fmt, vl);
|
|
char *p = buffer;
|
|
while (*p && pos < sizeof(epdSegmentData)) {
|
|
writeDigitToBuffer(pos++, *p++);
|
|
}
|
|
va_end(vl);
|
|
}
|
|
uint8_t is_drawing() {
|
|
if (display_is_drawing) {
|
|
if (!P2_0 || (timerGet() - screen_start_time > (TIMER_TICKS_PER_SECOND * 3))) {
|
|
display_is_drawing = 0;
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|