libepaper 2.0.0
A C++23 library for controlling Waveshare e-paper displays on Raspberry Pi, featuring transparent sleep/wake management and a fluent builder API.
Loading...
Searching...
No Matches
epd27.hpp
Go to the documentation of this file.
1#pragma once
2
7#include <array>
8#include <cstddef>
9#include <cstdint>
10#include <expected>
11#include <optional>
12#include <span>
13#include <utility>
14
15namespace epaper {
16
20enum class Command : std::uint8_t {
21 BOOSTER_SOFT_START = 0x06,
23 DISPLAY_REFRESH = 0x12,
26 LUT_VCOM = 0x20,
27 LUT_WW = 0x21,
28 LUT_BW = 0x22,
29 LUT_WB = 0x23,
30 LUT_BB = 0x24,
31 LUT_WW2 = 0x25,
32 PLL_CONTROL = 0x30,
33 VCOM_DATA_INTERVAL = 0x50,
34 RESOLUTION_SETTING = 0x61,
35 GET_STATUS = 0x71,
36 VCM_DC_SETTING = 0x82,
37 POWER_OPTIMIZATION = 0xF8,
38 PANEL_SETTING = 0x00,
39 POWER_SETTING = 0x01,
40 POWER_OFF = 0x02,
41 POWER_ON = 0x04,
42 DEEP_SLEEP = 0x07
43};
44
48namespace Timing {
49constexpr std::uint32_t BUSY_WAIT_DELAY_MS = 200;
50constexpr std::uint32_t DISPLAY_REFRESH_DELAY_MS = 200;
51constexpr std::uint32_t RESET_DELAY_MS = 200;
52constexpr std::uint32_t RESET_PULSE_MS = 2;
53constexpr std::uint32_t BUSY_POLL_DELAY_MS = 10;
54} // namespace Timing
55
62 std::uint8_t vds_en_vdg_en;
63 std::uint8_t vcom_hv_vghl_lv;
64 std::uint8_t vdh;
65 std::uint8_t vdl;
66 std::uint8_t vdhr;
67};
68
73 .vds_en_vdg_en = 0x03, .vcom_hv_vghl_lv = 0x00, .vdh = 0x2B, .vdl = 0x2B, .vdhr = 0x09};
74
79 .vds_en_vdg_en = 0x03,
80 .vcom_hv_vghl_lv = 0x00,
81 .vdh = 0x2B,
82 .vdl = 0x2B,
83 .vdhr = 0x00 // Not used in grayscale mode
84};
85
92 std::uint8_t phase1;
93 std::uint8_t phase2;
94 std::uint8_t phase3;
95};
96
100constexpr BoosterConfig BOOSTER_CONFIG = {.phase1 = 0x07, .phase2 = 0x07, .phase3 = 0x17};
101
107namespace PowerOptimization {
108constexpr std::uint8_t REG1 = 0x60;
109constexpr std::uint8_t VAL1 = 0xA5;
110constexpr std::uint8_t REG2 = 0x89;
111constexpr std::uint8_t VAL2 = 0xA5;
112constexpr std::uint8_t REG3 = 0x90;
113constexpr std::uint8_t VAL3 = 0x00;
114constexpr std::uint8_t REG4 = 0x93;
115constexpr std::uint8_t VAL4 = 0x2A;
116constexpr std::uint8_t REG5 = 0xA0;
117constexpr std::uint8_t VAL5 = 0xA5;
118constexpr std::uint8_t REG6 = 0xA1;
119constexpr std::uint8_t VAL6 = 0x00;
120constexpr std::uint8_t REG7 = 0x73;
121constexpr std::uint8_t VAL7 = 0x41;
122} // namespace PowerOptimization
123
127namespace PanelConfig {
128constexpr std::uint8_t PANEL_SETTING_BW = 0xAF;
129constexpr std::uint8_t PANEL_SETTING_GRAYSCALE = 0xBF;
130constexpr std::uint8_t PLL_SETTING_BW = 0x3A;
131constexpr std::uint8_t PLL_SETTING_GRAYSCALE = 0x90;
132constexpr std::uint8_t VCM_DC_SETTING_VALUE = 0x12;
133} // namespace PanelConfig
134
138namespace Resolution {
139constexpr std::uint8_t WIDTH_HIGH = 0x00;
140constexpr std::uint8_t WIDTH_LOW = 0xB0;
141constexpr std::uint8_t HEIGHT_HIGH = 0x01;
142constexpr std::uint8_t HEIGHT_LOW = 0x08;
143} // namespace Resolution
144
148namespace Grayscale {
149constexpr std::uint8_t BLACK_MASK = 0x00;
150constexpr std::uint8_t BIT_SHIFT = 2;
151constexpr std::uint8_t GRAY1_MASK = 0x80;
152constexpr std::uint8_t GRAY2_MASK = 0x40;
153constexpr std::uint8_t PIXEL_MASK = 0xC0;
154constexpr std::size_t TOTAL_PIXELS = 5808;
155constexpr std::uint8_t WHITE_MASK = 0xC0;
156} // namespace Grayscale
157
161namespace DisplayOps {
162constexpr std::uint8_t BUSY_STATUS_MASK = 0x01;
163constexpr std::uint8_t CLEAR_FILL_VALUE = 0xFF;
164constexpr std::uint8_t PARTIAL_REFRESH_DISABLE = 0x00;
165constexpr std::uint8_t SLEEP_VCOM_DATA_INTERVAL = 0xF7;
166constexpr std::uint8_t DEEP_SLEEP_MAGIC = 0xA5;
167constexpr std::uint8_t VCOM_DATA_INTERVAL_GRAYSCALE = 0x97;
168} // namespace DisplayOps
169
175 Pin dc{0};
176 Pin cs{0};
178 std::optional<Pin> pwr;
179
180 static constexpr auto waveshare_hat() -> EPD27PinConfig {
181 return EPD27PinConfig{.rst = Pin{17}, .dc = Pin{25}, .cs = Pin{8}, .busy = Pin{24}, .pwr = Pin{18}};
182 }
183};
184
190class EPD27 {
191public:
192 static constexpr std::size_t WIDTH = 176;
193 static constexpr std::size_t HEIGHT = 264;
194
206 std::optional<Device::HalOutput> pwr = std::nullopt)
207 : spi_(spi), cs_(cs), dc_(dc), rst_(rst), busy_(busy), pwr_(std::move(pwr)) {}
208
216 : EPD27(device.get_spi(), device.get_output(pins.cs), device.get_output(pins.dc), device.get_output(pins.rst),
217 device.get_input(pins.busy), pins.pwr ? std::make_optional(device.get_output(*pins.pwr)) : std::nullopt) {
218 }
219
225 explicit EPD27(Device &device) : EPD27(device, EPD27PinConfig::waveshare_hat()) {}
226
227 ~EPD27() = default;
228
229 // Driver interface implementation
230 [[nodiscard]] auto init(DisplayMode mode) -> std::expected<void, Error> {
231 if (is_color_mode(mode)) {
232 return std::unexpected(
233 Error(ErrorCode::InvalidMode, "Color modes not supported by this EPD27 driver configuration"));
234 }
235
236 current_mode_ = mode;
237 reset();
238
239 if (pwr_.has_value()) {
240 pwr_->write(true); // Turn on potential power switch
241 }
242
244 init_bw();
245 } else {
246 init_grayscale();
247 }
248
249 initialized_ = true;
250 is_asleep_ = false;
251 return {};
252 }
253
254 [[nodiscard]] auto clear() -> std::expected<void, Error> {
255 if (!initialized_) {
256 return std::unexpected(Error(ErrorCode::DriverNotInitialized));
257 }
258 if (is_asleep_) {
259 if (auto res = wake(); !res) {
260 return res;
261 }
262 }
263
264 const auto width_bytes = (WIDTH + 7) / 8;
265
267 for (std::size_t j = 0; j < HEIGHT; ++j) {
268 for (std::size_t i = 0; i < width_bytes; ++i) {
270 }
271 }
272
274 for (std::size_t j = 0; j < HEIGHT; ++j) {
275 for (std::size_t i = 0; i < width_bytes; ++i) {
277 }
278 }
279
280 send_command(Command::DISPLAY_REFRESH);
281 wait_busy();
282 return {};
283 }
284
285 [[nodiscard]] auto display(std::span<const std::byte> buffer) -> std::expected<void, Error> {
286 if (is_asleep_) {
287 if (auto res = wake(); !res) {
288 return res;
289 }
290 }
291
292 if (current_mode_ == DisplayMode::BlackWhite) {
293 const auto width_bytes = (WIDTH + 7) / 8;
295 for (std::size_t j = 0; j < HEIGHT; ++j) {
296 for (std::size_t i = 0; i < width_bytes; ++i) {
297 const auto index = i + (j * width_bytes);
298 send_data(static_cast<std::uint8_t>(buffer[index]));
299 }
300 }
301 send_command(Command::DISPLAY_REFRESH);
302 wait_busy();
303 return {};
304 } // Grayscale
305 const auto total_pixels = Grayscale::TOTAL_PIXELS;
306
308 for (std::size_t i = 0; i < total_pixels; ++i) {
309 std::size_t src_idx = i * 2;
310 auto b1 = static_cast<std::uint8_t>(buffer[src_idx]);
311 auto b2 = static_cast<std::uint8_t>(buffer[src_idx + 1]);
312 send_data(convert_grayscale_pixel(b1, b2, true));
313 }
314
316 for (std::size_t i = 0; i < total_pixels; ++i) {
317 std::size_t src_idx = i * 2;
318 auto b1 = static_cast<std::uint8_t>(buffer[src_idx]);
319 auto b2 = static_cast<std::uint8_t>(buffer[src_idx + 1]);
320 send_data(convert_grayscale_pixel(b1, b2, false));
321 }
322
323 set_lut_grayscale();
324 send_command(Command::DISPLAY_REFRESH);
326 wait_busy();
327 return {};
328 }
329
330 [[nodiscard]] auto display_planes(std::span<const std::span<const std::byte>> planes) -> std::expected<void, Error> {
331 if (!initialized_) {
332 return std::unexpected(Error(ErrorCode::DriverNotInitialized));
333 }
334 if (is_asleep_) {
335 if (auto res = wake(); !res) {
336 return res;
337 }
338 }
339 if (planes.empty()) {
340 return std::unexpected(Error(ErrorCode::InvalidDimensions, "No planes provided"));
341 }
342 if (current_mode_ == DisplayMode::BlackWhite || current_mode_ == DisplayMode::Grayscale4) {
343 return display(planes[0]);
344 }
345 return std::unexpected(Error(ErrorCode::InvalidMode, "Color planes not supported"));
346 }
347
348 [[nodiscard]] auto sleep() -> std::expected<void, Error> {
349 if (is_asleep_) {
350 return {};
351 }
352 if (!initialized_) {
353 return std::unexpected(Error(ErrorCode::DriverNotInitialized));
354 }
355
356 send_command(Command::VCOM_DATA_INTERVAL);
358 send_command(Command::POWER_OFF);
359 send_command(Command::DEEP_SLEEP);
361
362 is_asleep_ = true;
363 return {};
364 }
365
366 [[nodiscard]] auto wake() -> std::expected<void, Error> {
367 if (!is_asleep_) {
368 return {};
369 }
370 return init(current_mode_);
371 }
372
373 [[nodiscard]] auto power_off() -> std::expected<void, Error> {
374 if (!initialized_) {
375 return std::unexpected(Error(ErrorCode::DriverNotInitialized));
376 }
377 send_command(Command::POWER_OFF);
378 wait_busy_simple();
379 return {};
380 }
381
382 [[nodiscard]] auto power_on() -> std::expected<void, Error> {
383 if (!initialized_) {
384 return std::unexpected(Error(ErrorCode::DriverNotInitialized));
385 }
386 send_command(Command::POWER_ON);
387 wait_busy_simple();
388 return {};
389 }
390
391 [[nodiscard]] static auto width() noexcept -> std::size_t { return WIDTH; }
392 [[nodiscard]] static auto height() noexcept -> std::size_t { return HEIGHT; }
393 [[nodiscard]] auto mode() const noexcept -> DisplayMode { return current_mode_; }
394
395 [[nodiscard]] auto buffer_size() const noexcept -> std::size_t {
396 if (current_mode_ == DisplayMode::BlackWhite) {
397 return ((WIDTH + 7) / 8) * HEIGHT;
398 }
399 return ((WIDTH + 3) / 4) * HEIGHT;
400 }
401
402 [[nodiscard]] static auto supports_partial_refresh() noexcept -> bool { return false; }
403 [[nodiscard]] static auto supports_power_control() noexcept -> bool { return true; }
404 [[nodiscard]] static auto supports_wake() noexcept -> bool { return false; }
405
406private:
407 Device::HalSpi spi_;
411 Device::HalInput busy_;
412 std::optional<Device::HalOutput> pwr_;
413 DisplayMode current_mode_ = DisplayMode::BlackWhite;
414 bool initialized_ = false;
415 bool is_asleep_ = false;
416
417 void reset() {
418 rst_.write(true);
420 rst_.write(false);
422 rst_.write(true);
424 }
425
426 void send_command(Command command) {
427 dc_.write(false);
428 cs_.write(false);
429 spi_.transfer(static_cast<std::uint8_t>(command));
430 cs_.write(true);
431 }
432
433 void send_data(std::uint8_t data) {
434 dc_.write(true);
435 cs_.write(false);
436 spi_.transfer(data);
437 cs_.write(true);
438 }
439
440 void wait_busy() {
441 // Advanced wait with GET_STATUS could go here, but omitted for brevity/reliability
442 // Falling back to simple wait which is safer during transitions
443 wait_busy_simple();
444 }
445
446 void wait_busy_simple() {
447 int iterations = 0;
448 while (busy_.read() && iterations < 100) {
450 iterations++;
451 }
452 iterations = 0;
453 while (!busy_.read() && iterations < 1000) {
455 iterations++;
456 }
458 }
459
460 void init_bw() {
461 send_command(Command::POWER_SETTING);
464 send_data(POWER_CONFIG_BW.vdh);
465 send_data(POWER_CONFIG_BW.vdl);
466 send_data(POWER_CONFIG_BW.vdhr);
467
468 send_command(Command::BOOSTER_SOFT_START);
469 send_data(BOOSTER_CONFIG.phase1);
470 send_data(BOOSTER_CONFIG.phase2);
471 send_data(BOOSTER_CONFIG.phase3);
472
473 send_command(Command::POWER_OPTIMIZATION);
474 send_data(PowerOptimization::REG1);
475 send_data(PowerOptimization::VAL1);
476 send_command(Command::POWER_OPTIMIZATION);
477 send_data(PowerOptimization::REG2);
478 send_data(PowerOptimization::VAL2);
479 send_command(Command::POWER_OPTIMIZATION);
480 send_data(PowerOptimization::REG3);
481 send_data(PowerOptimization::VAL3);
482 send_command(Command::POWER_OPTIMIZATION);
483 send_data(PowerOptimization::REG4);
484 send_data(PowerOptimization::VAL4);
485 send_command(Command::POWER_OPTIMIZATION);
486 send_data(PowerOptimization::REG5);
487 send_data(PowerOptimization::VAL5);
488 send_command(Command::POWER_OPTIMIZATION);
489 send_data(PowerOptimization::REG6);
490 send_data(PowerOptimization::VAL6);
491 send_command(Command::POWER_OPTIMIZATION);
492 send_data(PowerOptimization::REG7);
493 send_data(PowerOptimization::VAL7);
494
497
498 send_command(Command::POWER_ON);
499 wait_busy();
500
501 send_command(Command::PANEL_SETTING);
503 send_command(Command::PLL_CONTROL);
505 send_command(Command::VCM_DC_SETTING);
507 set_lut_bw();
508 }
509
510 void init_grayscale() {
511 send_command(Command::POWER_SETTING);
514 send_data(POWER_CONFIG_GRAYSCALE.vdh);
515 send_data(POWER_CONFIG_GRAYSCALE.vdl);
516 send_command(Command::BOOSTER_SOFT_START);
517 send_data(BOOSTER_CONFIG.phase1);
518 send_data(BOOSTER_CONFIG.phase2);
519 send_data(BOOSTER_CONFIG.phase3);
520
521 send_command(Command::POWER_OPTIMIZATION);
522 send_data(PowerOptimization::REG1);
523 send_data(PowerOptimization::VAL1);
524 send_command(Command::POWER_OPTIMIZATION);
525 send_data(PowerOptimization::REG2);
526 send_data(PowerOptimization::VAL2);
527 send_command(Command::POWER_OPTIMIZATION);
528 send_data(PowerOptimization::REG3);
529 send_data(PowerOptimization::VAL3);
530 send_command(Command::POWER_OPTIMIZATION);
531 send_data(PowerOptimization::REG4);
532 send_data(PowerOptimization::VAL4);
533 send_command(Command::POWER_OPTIMIZATION);
534 send_data(PowerOptimization::REG5);
535 send_data(PowerOptimization::VAL5);
536 send_command(Command::POWER_OPTIMIZATION);
537 send_data(PowerOptimization::REG6);
538 send_data(PowerOptimization::VAL6);
539 send_command(Command::POWER_OPTIMIZATION);
540 send_data(PowerOptimization::REG7);
541 send_data(PowerOptimization::VAL7);
542
545
546 send_command(Command::POWER_ON);
547 wait_busy();
548
549 send_command(Command::PANEL_SETTING);
551 send_command(Command::PLL_CONTROL);
553 send_command(Command::RESOLUTION_SETTING);
554 send_data(Resolution::WIDTH_HIGH);
555 send_data(Resolution::WIDTH_LOW);
556 send_data(Resolution::HEIGHT_HIGH);
557 send_data(Resolution::HEIGHT_LOW);
558 send_command(Command::VCM_DC_SETTING);
560 send_command(Command::VCOM_DATA_INTERVAL);
562 }
563
564 static constexpr std::array<std::uint8_t, 44> LUT_VCOM_DC = {
565 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, 0x00,
566 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
567 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
568
569 static constexpr std::array<std::uint8_t, 42> LUT_WW = {
570 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x40, 0x14,
571 0x00, 0x00, 0x00, 0x01, 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
572 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
573
574 static constexpr std::array<std::uint8_t, 42> LUT_BW = {
575 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x40, 0x14,
576 0x00, 0x00, 0x00, 0x01, 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
577 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
578
579 static constexpr std::array<std::uint8_t, 42> LUT_BB = {
580 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x80, 0x14,
581 0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
582 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
583
584 static constexpr std::array<std::uint8_t, 42> LUT_WB = {
585 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x80, 0x14,
586 0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
587 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
588
589 // LUT tables for 4-level grayscale mode
590 static constexpr std::array<std::uint8_t, 44> LUT_VCOM_GRAY = {
591 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x60, 0x14, 0x14, 0x00, 0x00, 0x01, 0x00,
592 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x13, 0x0A, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
593 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
594
595 static constexpr std::array<std::uint8_t, 42> LUT_WW_GRAY = {
596 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, 0x10, 0x14,
597 0x0A, 0x00, 0x00, 0x01, 0xA0, 0x13, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
598 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
599
600 static constexpr std::array<std::uint8_t, 42> LUT_BW_GRAY = {
601 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, 0x00, 0x14,
602 0x0A, 0x00, 0x00, 0x01, 0x99, 0x0C, 0x01, 0x03, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00,
603 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
604
605 static constexpr std::array<std::uint8_t, 42> LUT_WB_GRAY = {
606 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, 0x00, 0x14,
607 0x0A, 0x00, 0x00, 0x01, 0x99, 0x0B, 0x04, 0x04, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
608 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
609
610 static constexpr std::array<std::uint8_t, 42> LUT_BB_GRAY = {
611 0x80, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, 0x20, 0x14,
612 0x0A, 0x00, 0x00, 0x01, 0x50, 0x13, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
613 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
614
615 void set_lut_bw() {
616 send_command(Command::LUT_VCOM);
617 for (auto b : LUT_VCOM_DC) {
618 send_data(b);
619 }
620 send_command(Command::LUT_WW);
621 for (auto b : LUT_WW) {
622 send_data(b);
623 }
624 send_command(Command::LUT_BW);
625 for (auto b : LUT_BW) {
626 send_data(b);
627 }
628 send_command(Command::LUT_WB);
629 for (auto b : LUT_BB) { // Original code behavior preserved
630 send_data(b);
631 }
632 send_command(Command::LUT_BB);
633 for (auto b : LUT_WB) { // Original code behavior preserved
634 send_data(b);
635 }
636 }
637
638 void set_lut_grayscale() {
639 send_command(Command::LUT_VCOM);
640 for (auto b : LUT_VCOM_GRAY) {
641 send_data(b);
642 }
643 send_command(Command::LUT_WW);
644 for (auto b : LUT_WW_GRAY) {
645 send_data(b);
646 }
647 send_command(Command::LUT_BW);
648 for (auto b : LUT_BW_GRAY) {
649 send_data(b);
650 }
651 send_command(Command::LUT_WB);
652 for (auto b : LUT_WB_GRAY) {
653 send_data(b);
654 }
655 send_command(Command::LUT_BB);
656 for (auto b : LUT_BB_GRAY) {
657 send_data(b);
658 }
659 send_command(Command::LUT_WW2);
660 for (auto b : LUT_WW_GRAY) {
661 send_data(b);
662 }
663 }
664
665 static auto convert_grayscale_pixel(std::uint8_t byte1, std::uint8_t byte2, bool is_old_data) -> std::uint8_t {
666 std::uint8_t result = 0;
667 for (std::size_t j = 0; j < 2; ++j) {
668 auto temp1 = (j == 0) ? byte1 : byte2;
669 for (std::size_t k = 0; k < 4; ++k) {
670 const std::uint8_t temp2 = temp1 & Grayscale::PIXEL_MASK;
671 result <<= 1;
672 if (is_old_data) {
673 if (temp2 == Grayscale::WHITE_MASK || temp2 == Grayscale::GRAY1_MASK) {
674 result |= 0x01;
675 }
676 } else {
677 if (temp2 == Grayscale::WHITE_MASK || temp2 == Grayscale::GRAY2_MASK) {
678 result |= 0x01;
679 }
680 }
681 temp1 <<= Grayscale::BIT_SHIFT;
682 }
683 }
684 return result;
685 }
686};
687
688template <> struct driver_traits<EPD27> {
689 static constexpr DisplayMode max_mode = DisplayMode::Grayscale4;
690 static constexpr bool supports_grayscale = true;
691 static constexpr bool supports_partial_refresh = true;
692 static constexpr bool supports_power_control = true;
693 static constexpr bool supports_wake_from_sleep = false;
694 static constexpr std::size_t max_width = 176;
695 static constexpr std::size_t max_height = 264;
696};
697
698static_assert(Driver<EPD27>, "EPD27 must satisfy Driver concept");
699
700} // namespace epaper
Compile-time driver capability traits system.
Definition device.hpp:251
auto read() -> bool
Definition device.hpp:254
Definition device.hpp:240
auto write(bool level) -> void
Definition device.hpp:243
Definition device.hpp:262
auto transfer(std::uint8_t byte) -> std::uint8_t
Definition device.hpp:265
Definition device.hpp:158
2.7 inch e-paper display driver (176x264 pixels).
Definition epd27.hpp:190
auto clear() -> std::expected< void, Error >
Definition epd27.hpp:254
auto mode() const noexcept -> DisplayMode
Definition epd27.hpp:393
EPD27(Device::HalSpi spi, Device::HalOutput cs, Device::HalOutput dc, Device::HalOutput rst, Device::HalInput busy, std::optional< Device::HalOutput > pwr=std::nullopt)
Construct EPD27 driver with direct hardware resources.
Definition epd27.hpp:205
auto sleep() -> std::expected< void, Error >
Definition epd27.hpp:348
static constexpr std::size_t HEIGHT
Definition epd27.hpp:193
auto buffer_size() const noexcept -> std::size_t
Definition epd27.hpp:395
auto init(DisplayMode mode) -> std::expected< void, Error >
Definition epd27.hpp:230
~EPD27()=default
auto display_planes(std::span< const std::span< const std::byte > > planes) -> std::expected< void, Error >
Definition epd27.hpp:330
EPD27(Device &device)
Construct using default Waveshare HAT pins.
Definition epd27.hpp:225
static auto height() noexcept -> std::size_t
Definition epd27.hpp:392
auto power_off() -> std::expected< void, Error >
Definition epd27.hpp:373
static auto supports_wake() noexcept -> bool
Definition epd27.hpp:404
static auto supports_partial_refresh() noexcept -> bool
Definition epd27.hpp:402
auto power_on() -> std::expected< void, Error >
Definition epd27.hpp:382
auto wake() -> std::expected< void, Error >
Definition epd27.hpp:366
static auto supports_power_control() noexcept -> bool
Definition epd27.hpp:403
EPD27(Device &device, EPD27PinConfig pins)
Construct using custom pin configuration.
Definition epd27.hpp:215
static auto width() noexcept -> std::size_t
Definition epd27.hpp:391
static constexpr std::size_t WIDTH
Definition epd27.hpp:192
auto display(std::span< const std::byte > buffer) -> std::expected< void, Error >
Definition epd27.hpp:285
Definition device.hpp:38
Definition driver_concepts.hpp:139
C++20 concept defining the Driver interface contract.
constexpr std::uint8_t BUSY_STATUS_MASK
Mask for busy status bit.
Definition epd27.hpp:162
constexpr std::uint8_t VCOM_DATA_INTERVAL_GRAYSCALE
VCOM interval for grayscale.
Definition epd27.hpp:167
constexpr std::uint8_t SLEEP_VCOM_DATA_INTERVAL
VCOM interval for sleep mode.
Definition epd27.hpp:165
constexpr std::uint8_t DEEP_SLEEP_MAGIC
Magic value for deep sleep.
Definition epd27.hpp:166
constexpr std::uint8_t CLEAR_FILL_VALUE
Fill value for clearing (white)
Definition epd27.hpp:163
constexpr std::uint8_t PARTIAL_REFRESH_DISABLE
Disable partial refresh.
Definition epd27.hpp:164
constexpr std::uint8_t WHITE_MASK
Bit pattern for white pixel.
Definition epd27.hpp:155
constexpr std::uint8_t GRAY2_MASK
Bit pattern for gray level 2.
Definition epd27.hpp:152
constexpr std::size_t TOTAL_PIXELS
Total pixels / 8 for grayscale (176*264/8)
Definition epd27.hpp:154
constexpr std::uint8_t BLACK_MASK
Bit pattern for black pixel.
Definition epd27.hpp:149
constexpr std::uint8_t BIT_SHIFT
Bit shift for 2-bit pixels.
Definition epd27.hpp:150
constexpr std::uint8_t PIXEL_MASK
Mask for extracting 2-bit pixel.
Definition epd27.hpp:153
constexpr std::uint8_t GRAY1_MASK
Bit pattern for gray level 1.
Definition epd27.hpp:151
constexpr std::uint8_t VCM_DC_SETTING_VALUE
VCM DC setting value.
Definition epd27.hpp:132
constexpr std::uint8_t PLL_SETTING_BW
PLL 100Hz for black/white mode.
Definition epd27.hpp:130
constexpr std::uint8_t PANEL_SETTING_BW
Panel setting for black/white mode.
Definition epd27.hpp:128
constexpr std::uint8_t PANEL_SETTING_GRAYSCALE
Panel setting for grayscale mode.
Definition epd27.hpp:129
constexpr std::uint8_t PLL_SETTING_GRAYSCALE
PLL 100Hz for grayscale mode.
Definition epd27.hpp:131
constexpr std::uint8_t VAL7
Definition epd27.hpp:121
constexpr std::uint8_t VAL4
Definition epd27.hpp:115
constexpr std::uint8_t REG5
Definition epd27.hpp:116
constexpr std::uint8_t VAL6
Definition epd27.hpp:119
constexpr std::uint8_t REG4
Definition epd27.hpp:114
constexpr std::uint8_t REG1
Definition epd27.hpp:108
constexpr std::uint8_t VAL3
Definition epd27.hpp:113
constexpr std::uint8_t REG7
Definition epd27.hpp:120
constexpr std::uint8_t VAL2
Definition epd27.hpp:111
constexpr std::uint8_t REG6
Definition epd27.hpp:118
constexpr std::uint8_t REG3
Definition epd27.hpp:112
constexpr std::uint8_t REG2
Definition epd27.hpp:110
constexpr std::uint8_t VAL5
Definition epd27.hpp:117
constexpr std::uint8_t VAL1
Definition epd27.hpp:109
constexpr std::uint8_t HEIGHT_HIGH
Height high byte (264 >> 8)
Definition epd27.hpp:141
constexpr std::uint8_t WIDTH_HIGH
Width high byte (176 >> 8)
Definition epd27.hpp:139
constexpr std::uint8_t HEIGHT_LOW
Height low byte (264 & 0xFF)
Definition epd27.hpp:142
constexpr std::uint8_t WIDTH_LOW
Width low byte (176 & 0xFF)
Definition epd27.hpp:140
constexpr std::uint32_t BUSY_POLL_DELAY_MS
Polling delay when checking busy status.
Definition epd27.hpp:53
constexpr std::uint32_t BUSY_WAIT_DELAY_MS
Delay after busy wait.
Definition epd27.hpp:49
constexpr std::uint32_t RESET_DELAY_MS
Reset signal delay.
Definition epd27.hpp:51
constexpr std::uint32_t RESET_PULSE_MS
Reset pulse duration.
Definition epd27.hpp:52
constexpr std::uint32_t DISPLAY_REFRESH_DELAY_MS
Delay after display refresh.
Definition epd27.hpp:50
Definition color.hpp:5
constexpr auto is_color_mode(DisplayMode mode) noexcept -> bool
Check if mode supports color (non-grayscale).
Definition driver.hpp:80
@ DriverNotInitialized
Driver has not been initialized.
@ InvalidDimensions
Invalid image dimensions.
@ InvalidMode
Invalid display mode specified.
Command
E-paper display command codes for EPD27.
Definition epd27.hpp:20
@ GET_STATUS
Get display status.
@ POWER_OPTIMIZATION
Power optimization register.
@ PARTIAL_DISPLAY_REFRESH
Partial display refresh control.
@ DATA_START_TRANSMISSION_2
Start data transmission (new data)
@ POWER_ON
Power on command.
@ LUT_VCOM
VCOM LUT register.
@ LUT_WW
White-to-White LUT register.
@ DISPLAY_REFRESH
Refresh display.
@ DATA_START_TRANSMISSION_1
Start data transmission (old data)
@ VCM_DC_SETTING
VCM DC setting.
@ PANEL_SETTING
Panel setting register.
@ LUT_BW
Black-to-White LUT register.
@ DEEP_SLEEP
Deep sleep mode.
@ BOOSTER_SOFT_START
Booster soft start control.
@ LUT_WW2
Additional White-to-White LUT register.
@ POWER_OFF
Power off command.
@ POWER_SETTING
Power setting.
@ LUT_BB
Black-to-Black LUT register.
@ VCOM_DATA_INTERVAL
VCOM and data interval setting.
@ LUT_WB
White-to-Black LUT register.
@ RESOLUTION_SETTING
Resolution setting.
@ PLL_CONTROL
PLL control (frame rate)
constexpr PowerConfig POWER_CONFIG_BW
Power configuration for black/white mode.
Definition epd27.hpp:72
constexpr PowerConfig POWER_CONFIG_GRAYSCALE
Power configuration for grayscale mode.
Definition epd27.hpp:78
constexpr BoosterConfig BOOSTER_CONFIG
Standard booster soft start configuration.
Definition epd27.hpp:100
DisplayMode
Definition driver.hpp:46
@ BlackWhite
1-bit black and white (2 colors)
@ Grayscale4
2-bit 4-level grayscale
Booster soft start configuration.
Definition epd27.hpp:91
std::uint8_t phase1
Phase 1 setting.
Definition epd27.hpp:92
std::uint8_t phase2
Phase 2 setting.
Definition epd27.hpp:93
std::uint8_t phase3
Phase 3 setting.
Definition epd27.hpp:94
static auto delay_ms(std::uint32_t ms) -> void
Definition device.hpp:235
Pin configuration for standard Raspberry Pi HATs.
Definition epd27.hpp:173
Pin busy
Definition epd27.hpp:177
std::optional< Pin > pwr
Definition epd27.hpp:178
Pin dc
Definition epd27.hpp:175
Pin rst
Definition epd27.hpp:174
static constexpr auto waveshare_hat() -> EPD27PinConfig
Definition epd27.hpp:180
Pin cs
Definition epd27.hpp:176
Definition errors.hpp:140
Power configuration settings for EPD27.
Definition epd27.hpp:61
std::uint8_t vdh
VDH voltage setting.
Definition epd27.hpp:64
std::uint8_t vds_en_vdg_en
VDS_EN and VDG_EN control.
Definition epd27.hpp:62
std::uint8_t vdhr
VDHR voltage setting (black/white mode only)
Definition epd27.hpp:66
std::uint8_t vcom_hv_vghl_lv
VCOM_HV and VGHL_LV control.
Definition epd27.hpp:63
std::uint8_t vdl
VDL voltage setting.
Definition epd27.hpp:65
Driver capabilities trait template.
Definition capabilities.hpp:110