ESPHome 2026.9.0-dev
Loading...
Searching...
No Matches
epaper_spi_uc8179.cpp
Go to the documentation of this file.
1#include "epaper_spi_uc8179.h"
2
3#include <algorithm>
4
5#include "esphome/core/log.h"
6
7namespace esphome::epaper_spi {
8
9static constexpr const char *const TAG = "epaper_spi.uc8179";
10
11bool EPaperUC8179::initialise(bool partial) {
12 EPaperBase::initialise(partial); // send the model init sequence
13 this->partial_ = partial;
14 ESP_LOGV(TAG, "Power on");
15 // POWER ON must precede the waveform/mode registers and the data transfer
16 // (the original driver powers on and busy-waits before writing them).
17 // The state machine busy-waits before entering TRANSFER_DATA.
18 this->command(0x04);
19 // Give the busy line time to assert before the state machine polls it
20 this->next_delay_ = 100;
21 return true;
22}
23
24// Set up the refresh mode. Must be called after power-on has completed.
26 if (!this->is_using_partial_update_()) {
27 return; // plain full refresh uses the mode set by the init sequence
28 }
29 // Fast and partial refresh use flipped data polarity and a floating border
30 this->cmd_data(0x50, {0xA9, 0x07});
31 // Force the waveform via the temperature registers: 0x5A selects the fast
32 // full-refresh waveform, 0x6E the partial-refresh waveform
33 this->cmd_data(0xE0, {0x02});
34 if (this->partial_) {
35 this->cmd_data(0xE5, {0x6E});
36 this->command(0x91); // enter partial mode
37 // Set the partial window to the full screen
38 const uint16_t x_end = this->width_ - 1;
39 const uint16_t y_end = this->height_ - 1;
40 this->cmd_data(0x90, {0x00, 0x00, static_cast<uint8_t>(x_end >> 8), static_cast<uint8_t>(x_end & 0xFF), 0x00, 0x00,
41 static_cast<uint8_t>(y_end >> 8), static_cast<uint8_t>(y_end & 0xFF), 0x01});
42 } else {
43 this->cmd_data(0xE5, {0x5A});
44 this->command(0x92); // exit partial mode
45 }
46}
47
49 const uint32_t start_time = millis();
50 const size_t buffer_length = this->buffer_length_;
51 if (this->current_data_index_ == 0) {
52 this->set_refresh_mode_();
53 }
54 // Fast full refresh sends the previous-image plane as well, so that every pixel transitions
55 const bool two_pass = this->is_using_partial_update_() && !this->partial_;
56 // Plain full refresh sends inverted data (buffer is 1=white, the wire wants 0=white);
57 // in fast/partial mode the data polarity is flipped via the VCOM/data-interval
58 // register instead, so the new-image plane is sent unmodified
59 const bool invert_new_data = !this->is_using_partial_update_();
60
61 uint8_t bytes_to_send[MAX_TRANSFER_SIZE];
62
63 // Phase 1 (fast full refresh only): previous image via 0x10 (DTM1), inverse of the new image
64 if (two_pass && this->current_data_index_ < buffer_length) {
65 if (this->current_data_index_ == 0) {
66 this->command(0x10); // DATA START TRANSMISSION 1 (previous image)
67 }
68 this->start_data_();
69 while (this->current_data_index_ < buffer_length) {
70 const size_t bytes_to_copy = std::min(MAX_TRANSFER_SIZE, buffer_length - this->current_data_index_);
71 for (size_t i = 0; i < bytes_to_copy; i++) {
72 bytes_to_send[i] = ~this->buffer_[this->current_data_index_ + i];
73 }
74 this->write_array(bytes_to_send, bytes_to_copy);
75 this->current_data_index_ += bytes_to_copy;
76 if (millis() - start_time > MAX_TRANSFER_TIME) {
77 this->disable();
78 return false;
79 }
80 }
81 this->disable();
82 }
83
84 // Phase 2: new image via 0x13 (DTM2)
85 const size_t offset = two_pass ? buffer_length : 0;
86 const size_t total = offset + buffer_length;
87 if (this->current_data_index_ < total) {
88 if (this->current_data_index_ == offset) {
89 this->command(0x13); // DATA START TRANSMISSION 2 (new image)
90 }
91 this->start_data_();
92 while (this->current_data_index_ < total) {
93 const size_t bytes_to_copy = std::min(MAX_TRANSFER_SIZE, total - this->current_data_index_);
94 const size_t data_idx = this->current_data_index_ - offset;
95 for (size_t i = 0; i < bytes_to_copy; i++) {
96 const uint8_t byte = this->buffer_[data_idx + i];
97 bytes_to_send[i] = invert_new_data ? ~byte : byte;
98 }
99 this->write_array(bytes_to_send, bytes_to_copy);
100 this->current_data_index_ += bytes_to_copy;
101 if (millis() - start_time > MAX_TRANSFER_TIME) {
102 this->disable();
103 return false;
104 }
105 }
106 this->disable();
107 }
108
109 this->current_data_index_ = 0;
110 return true;
111}
112
114 // Power-on is sent at the end of initialise() instead, because the
115 // waveform/mode registers and the data transfer must follow it
116}
117
118void EPaperUC8179::refresh_screen(bool /*partial*/) {
119 ESP_LOGV(TAG, "Refresh");
120 this->command(0x12); // DISPLAY REFRESH
121 // Delay the next busy poll: the busy line takes a short time to assert after
122 // the refresh command, and polling too early would read it as already idle
123 this->next_delay_ = 100;
124}
125
127 ESP_LOGV(TAG, "Power off");
128 this->command(0x02); // POWER OFF
129}
130
132 // Deep sleep loses the previous-image RAM that partial refresh compares against
133 if (!this->is_using_partial_update_()) {
134 ESP_LOGV(TAG, "Deep sleep");
135 this->cmd_data(0x07, {0xA5}); // DEEP SLEEP with check code
136 }
137}
138
139} // namespace esphome::epaper_spi
void command(uint8_t value)
virtual bool initialise(bool partial)
split_buffer::SplitBuffer buffer_
Definition epaper_spi.h:177
void cmd_data(uint8_t command, const uint8_t *ptr, size_t length)
bool initialise(bool partial) override
void refresh_screen(bool partial) override
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:28
static void uint32_t