1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
#include "E290Display.h"
#include "../../MeshCore.h"
bool E290Display::begin() {
if (_init) return true;
powerOn();
display.begin();
// Set to landscape mode rotated 180 degrees
display.setRotation(3);
_init = true;
_isOn = true;
clear();
display.fastmodeOn(); // Enable fast mode for quicker (partial) updates
return true;
}
void E290Display::powerOn() {
if (_periph_power) {
_periph_power->claim();
} else {
#ifdef PIN_VEXT_EN
pinMode(PIN_VEXT_EN, OUTPUT);
digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE);
#endif
}
delay(50); // Allow power to stabilize
}
void E290Display::powerOff() {
if (_periph_power) {
_periph_power->release();
} else {
#ifdef PIN_VEXT_EN
digitalWrite(PIN_VEXT_EN, !PIN_VEXT_EN_ACTIVE); // Turn off power
#endif
}
}
void E290Display::turnOn() {
if (!_init) begin();
else if (!_isOn) {
powerOn();
display.fastmodeOn(); // Reinitialize display controller after power was cut
}
_isOn = true;
}
void E290Display::turnOff() {
if (_isOn) {
powerOff();
_isOn = false;
}
}
void E290Display::clear() {
display.clear();
}
void E290Display::startFrame(Color bkg) {
display_crc.reset();
// Fill screen with white first to ensure clean background
display.fillRect(0, 0, width(), height(), WHITE);
if (bkg == LIGHT) {
// Fill with black if light background requested (inverted for e-ink)
display.fillRect(0, 0, width(), height(), BLACK);
}
}
void E290Display::setTextSize(int sz) {
display_crc.update<int>(sz);
// The library handles text size internally
display.setTextSize(sz);
}
void E290Display::setColor(Color c) {
display_crc.update<Color>(c);
// implemented in individual display methods
}
void E290Display::setCursor(int x, int y) {
display_crc.update<int>(x);
display_crc.update<int>(y);
display.setCursor(x, y);
}
void E290Display::print(const char *str) {
display_crc.update<char>(str, strlen(str));
display.print(str);
}
void E290Display::fillRect(int x, int y, int w, int h) {
display_crc.update<int>(x);
display_crc.update<int>(y);
display_crc.update<int>(w);
display_crc.update<int>(h);
display.fillRect(x, y, w, h, BLACK);
}
void E290Display::drawRect(int x, int y, int w, int h) {
display_crc.update<int>(x);
display_crc.update<int>(y);
display_crc.update<int>(w);
display_crc.update<int>(h);
display.drawRect(x, y, w, h, BLACK);
}
void E290Display::drawXbm(int x, int y, const uint8_t *bits, int w, int h) {
display_crc.update<int>(x);
display_crc.update<int>(y);
display_crc.update<int>(w);
display_crc.update<int>(h);
display_crc.update<uint8_t>(bits, w * h / 8);
// Width in bytes for bitmap processing
uint16_t widthInBytes = (w + 7) / 8;
// Process the bitmap row by row
for (int by = 0; by < h; by++) {
// Scan across the row bit by bit
for (int bx = 0; bx < w; bx++) {
// Get the current bit using MSB ordering (like GxEPDDisplay)
uint16_t byteOffset = (by * widthInBytes) + (bx / 8);
uint8_t bitMask = 0x80 >> (bx & 7);
bool bitSet = bits[byteOffset] & bitMask;
// If the bit is set, draw the pixel
if (bitSet) {
display.drawPixel(x + bx, y + by, BLACK);
}
}
}
}
uint16_t E290Display::getTextWidth(const char *str) {
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h);
return w;
}
void E290Display::endFrame() {
uint32_t crc = display_crc.finalize();
if (crc != last_display_crc_value) {
display.update();
last_display_crc_value = crc;
}
}
|