ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
epaper_spi_uc8279.cpp
Go to the documentation of this file.
1#include "epaper_spi_uc8279.h"
2
3#include <algorithm>
4#include <cstring>
5
6#include "esphome/core/hal.h"
7#include "esphome/core/log.h"
8
9namespace esphome::epaper_spi {
10ESPHOME_LOG_TAG(TAG, "epaper_spi.uc8279");
11
12static constexpr uint8_t CMD_PSR = 0x00;
13static constexpr uint8_t CMD_PFS = 0x03;
14static constexpr uint8_t CMD_GATE_SCAN = 0xE1;
15
16static constexpr uint8_t PSR0_OTP_LUT = 0x17;
17static constexpr uint8_t PSR1 = 0x4D;
18static constexpr uint8_t PFS = 0x20;
19static constexpr uint8_t GATE_SCAN = 0x02;
20static constexpr uint8_t CCSET = 0x02;
21static constexpr uint8_t TSSET_FULL = 0x1E;
22static constexpr uint8_t TSSET_PARTIAL = 0x5A;
23static constexpr uint8_t CDI_FULL = 0x97;
24static constexpr uint8_t CDI_PARTIAL = 0xD7;
25
26static constexpr uint16_t RESET_SETTLE_MS = 50;
27static constexpr uint16_t BUSY_ASSERT_MS = 10;
28static constexpr uint16_t REFRESH_START_MS = 50;
29
31 // A partial update follows one that left the controller configured, in standby, with DTM1 holding the image
32 if (this->partial_)
33 return true;
34 if (!EPaperBase::reset())
35 return false;
36 this->next_delay_ = RESET_SETTLE_MS;
37 return true;
38}
39
40bool EPaperUC8279::initialise(bool partial) {
41 if (partial)
42 return true;
43 return EPaperBase::initialise(partial);
44}
45
46void EPaperUC8279::write_gate_(size_t gate, bool white) {
47 const size_t row_width = this->row_width_;
48 if (white || gate < this->offset_height_) {
49 uint8_t white_bytes[MAX_TRANSFER_SIZE];
50 memset(white_bytes, 0xFF, sizeof(white_bytes));
51 for (size_t written = 0; written != row_width;) {
52 const size_t length = std::min(row_width - written, MAX_TRANSFER_SIZE);
53 this->write_array(white_bytes, length);
54 written += length;
55 }
56 return;
57 }
58 size_t index = (gate - this->offset_height_) * row_width;
59 for (size_t remaining = row_width; remaining != 0;) {
60 size_t length;
61 const uint8_t *data = this->buffer_.get_span(index, length);
62 length = std::min(length, remaining);
63 this->write_array(data, length);
64 index += length;
65 remaining -= length;
66 }
67}
68
69// One plane of one gate's row per step, time-sliced; `base` is the value of current_data_index_ at its first gate
70bool HOT EPaperUC8279::write_plane_(uint8_t command, bool white, size_t base) {
71 const uint32_t start_time = millis();
72 const size_t gates = this->gate_count_();
73 if (this->current_data_index_ == base)
74 this->command(command);
75 this->start_data_();
76 while (this->current_data_index_ - base != gates) {
77 this->write_gate_(this->current_data_index_ - base, white);
78 ++this->current_data_index_;
79 if (this->current_data_index_ - base != gates && millis() - start_time > MAX_TRANSFER_TIME) {
80 this->disable();
81 return false;
82 }
83 }
84 this->disable();
85 return true;
86}
87
88// DTM2 gets the new image; a full refresh also writes a white DTM1, so every pixel transitions
90 const size_t gates = this->gate_count_();
91 if (this->current_data_index_ < gates) {
92 if (!this->write_plane_(CMD_DTM2, false, 0))
93 return false;
94 // Start DTM1 on the next pass, so this one stays within its time slice
95 if (!this->partial_)
96 return false;
97 }
98 if (!this->partial_ && !this->write_plane_(CMD_DTM1, true, gates))
99 return false;
100 this->current_data_index_ = 0;
101 return true;
102}
103
105 ESP_LOGV(TAG, "Power on");
106 this->cmd_data(CMD_CDI, {this->partial_ ? CDI_PARTIAL : CDI_FULL});
107 this->cmd_data(CMD_CCSET, {CCSET});
108 this->cmd_data(CMD_TSSET, {this->partial_ ? TSSET_PARTIAL : TSSET_FULL});
109 if (this->partial_) {
110 this->cmd_data(CMD_PFS, {PFS});
111 this->cmd_data(CMD_GATE_SCAN, {GATE_SCAN});
112 }
113 this->command(CMD_POWER_ON);
114 this->next_delay_ = BUSY_ASSERT_MS;
115}
116
118 ESP_LOGV(TAG, "Refresh screen (%s)", partial ? LOG_STR_LITERAL("partial") : LOG_STR_LITERAL("full"));
119 if (partial)
120 this->set_partial_window_(0, this->width_ - 1, this->offset_height_, this->gate_count_() - 1);
121 // Power-on reloads the panel's stored settings, so the panel setting only takes when written after it
122 this->cmd_data(CMD_PSR, {PSR0_OTP_LUT, PSR1});
123 this->command(CMD_REFRESH);
124 this->next_delay_ = REFRESH_START_MS;
125}
126
128 // While partial updates are in use, DTM1 holds the image on the panel for the next partial comparison
129 if (!this->is_using_partial_update_())
130 return true;
131 if (this->current_data_index_ == 0 && this->partial_)
133 if (!this->write_plane_(CMD_DTM1, false, 0))
134 return false;
135 this->current_data_index_ = 0;
136 return true;
137}
138
140 this->command(CMD_POWER_OFF);
141 this->next_delay_ = BUSY_ASSERT_MS;
142}
143
144} // namespace esphome::epaper_spi
void command(uint8_t value)
virtual bool initialise(bool partial)
split_buffer::SplitBuffer buffer_
Definition epaper_spi.h:218
void cmd_data(uint8_t command, const uint8_t *ptr, size_t length)
bool write_plane_(uint8_t command, bool white, size_t base)
bool initialise(bool partial) override
void refresh_screen(bool partial) override
void write_gate_(size_t gate, bool white)
void set_partial_window_(uint16_t x_start, uint16_t x_end, uint16_t y_start, uint16_t y_end)
const uint8_t * get_span(size_t index, size_t &length) const
ESPHOME_LOG_TAG(TAG, "display")
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
int written
Definition helpers.h:1262
static void uint32_t