This commit is contained in:
Jonas Bergler 2024-05-02 15:42:45 +02:00 committed by GitHub
commit ead611a9d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 203 additions and 0 deletions

View File

@ -12,11 +12,13 @@ static const char *const TAG = "display";
void DisplayBuffer::init_internal_(uint32_t buffer_length) {
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
ESP_LOGD(TAG, "allocated %u bytes", buffer_length);
this->buffer_ = allocator.allocate(buffer_length);
if (this->buffer_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate buffer for display!");
return;
}
memset(this->buffer_, 0, buffer_length);
this->clear();
}

View File

@ -94,6 +94,9 @@ WaveshareEPaper2P13InV2 = waveshare_epaper_ns.class_(
WaveshareEPaper2P13InV3 = waveshare_epaper_ns.class_(
"WaveshareEPaper2P13InV3", WaveshareEPaper
)
WeActEPaper2P9In3C = waveshare_epaper_ns.class_(
"WeActEPaper2P9In3C", WaveshareEPaperBWR
)
GDEW0154M09 = waveshare_epaper_ns.class_("GDEW0154M09", WaveshareEPaper)
WaveshareEPaperTypeAModel = waveshare_epaper_ns.enum("WaveshareEPaperTypeAModel")
@ -110,6 +113,7 @@ MODELS = {
"2.13in-ttgo-b74": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B74),
"2.90in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN),
"2.90inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2),
"2.90in3c": ("b", WeActEPaper2P9In3C),
"gdey029t94": ("c", GDEY029T94),
"2.70in": ("b", WaveshareEPaper2P7In),
"2.70in-b": ("b", WaveshareEPaper2P7InB),

View File

@ -769,5 +769,33 @@ class WaveshareEPaper2P13InV3 : public WaveshareEPaper {
bool is_busy_{false};
void write_lut_(const uint8_t *lut);
};
class WeActEPaper2P9In3C : public WaveshareEPaperBWR {
public:
void display() override;
void dump_config() override;
void deep_sleep() override;
void setup() override;
void initialize() override;
protected:
int get_width_internal() override;
int get_height_internal() override;
void draw_absolute_pixel_internal(int x, int y, Color color) override;
uint32_t idle_timeout_() override;
void write_buffer_(int top, int bottom);
void set_window_(int t, int b);
void send_reset_();
void full_update_();
uint32_t full_update_every_{0};
uint32_t at_update_{0};
bool is_busy_{false};
};
} // namespace waveshare_epaper
} // namespace esphome

View File

@ -0,0 +1,169 @@
#include "waveshare_epaper.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
namespace esphome {
namespace waveshare_epaper {
// It's worth adding some notes for this implementation
// - This display doesn't ship with a LUT, instead it relies on the internal values set during OTP
// - This display inverts Black & White in memory, requiring a different implementation for draw_absolute_pixel_internal
// - The reference implementation by the vendor points to
// https://github.com/ZinggJM/GxEPD2/blob/220fc5845c08b83c8dbac63e0cb83e1a774071ca/src/epd3c/GxEPD2_290_C90c.cpp
// - The datasheet is here
// https://github.com/WeActStudio/WeActStudio.EpaperModule/blob/master/Doc/ZJY128296-029EAAMFGN.pdf
static const char *const TAG = "weact_2.90_3c";
static const uint16_t HEIGHT = 296;
static const uint16_t WIDTH = 128;
// General Commands
static const uint8_t SW_RESET = 0x12;
static const uint8_t ACTIVATE = 0x20;
static const uint8_t WRITE_BLACK = 0x24;
static const uint8_t WRITE_COLOR = 0x26;
static const uint8_t SLEEP[] = {0x10, 0x01};
static const uint8_t UPDATE_FULL[] = {0x22, 0xF7};
// Configuration commands
static const uint8_t DRV_OUT_CTL[] = {0x01, 0x27, 0x01, 0x00}; // driver output control
static const uint8_t DATA_ENTRY[] = {0x11, 0x03}; // data entry mode
static const uint8_t BORDER_FULL[] = {0x3C, 0x05}; // border waveform
static const uint8_t TEMP_SENS[] = {0x18, 0x80}; // use internal temp sensor
static const uint8_t DISPLAY_UPDATE[] = {0x21, 0x00, 0x80}; // display update control
// For controlling which part of the image we want to write
static const uint8_t RAM_X_RANGE[] = {0x44, 0x00, WIDTH / 8u - 1};
static const uint8_t RAM_Y_RANGE[] = {0x45, 0x00, 0x00, (uint8_t) HEIGHT - 1, (uint8_t) (HEIGHT >> 8)};
static const uint8_t RAM_X_POS[] = {0x4E, 0x00}; // Always start at 0
static const uint8_t RAM_Y_POS = 0x4F;
#define SEND(x) this->cmd_data(x, sizeof(x))
// Basics
int WeActEPaper2P9In3C::get_width_internal() { return WIDTH; }
int WeActEPaper2P9In3C::get_height_internal() { return HEIGHT; }
uint32_t WeActEPaper2P9In3C::idle_timeout_() { return 2500; }
void WeActEPaper2P9In3C::dump_config() {
LOG_DISPLAY("", "WeAct E-Paper (3 Color)", this)
ESP_LOGCONFIG(TAG, " Model: 2.90in Red+Black");
LOG_PIN(" CS Pin: ", this->cs_)
LOG_PIN(" Reset Pin: ", this->reset_pin_)
LOG_PIN(" DC Pin: ", this->dc_pin_)
LOG_PIN(" Busy Pin: ", this->busy_pin_)
LOG_UPDATE_INTERVAL(this)
}
// Device lifecycle
void WeActEPaper2P9In3C::setup() {
setup_pins_();
delay(20);
this->send_reset_();
// as a one-off delay this is not worth working around.
delay(100); // NOLINT
this->wait_until_idle_();
this->command(SW_RESET);
this->wait_until_idle_();
SEND(DRV_OUT_CTL);
SEND(DATA_ENTRY);
SEND(BORDER_FULL);
SEND(TEMP_SENS);
SEND(DISPLAY_UPDATE);
this->wait_until_idle_();
}
void WeActEPaper2P9In3C::send_reset_() {
if (this->reset_pin_ != nullptr) {
this->reset_pin_->digital_write(false);
delay(2);
this->reset_pin_->digital_write(true);
}
}
// must implement, but we override setup to have more control
void WeActEPaper2P9In3C::initialize() {}
void WeActEPaper2P9In3C::deep_sleep() { SEND(SLEEP); }
// Pixel stuff
// t and b are y positions, i.e. line numbers.
void WeActEPaper2P9In3C::set_window_(int t, int b) {
SEND(RAM_X_RANGE);
SEND(RAM_Y_RANGE);
SEND(RAM_X_POS);
uint8_t buffer[3];
buffer[0] = RAM_Y_POS;
buffer[1] = (uint8_t) t % 256;
buffer[2] = (uint8_t) (t / 256);
SEND(buffer);
}
// send the buffer starting on line `top`, up to line `bottom`.
void WeActEPaper2P9In3C::write_buffer_(int top, int bottom) {
auto width_bytes = this->get_width_internal() / 8u;
auto offset = top * width_bytes;
auto length = (bottom - top) * width_bytes;
this->wait_until_idle_();
this->set_window_(top, bottom);
this->command(WRITE_BLACK);
this->start_data_();
this->write_array(this->buffer_ + offset, length);
this->end_data_();
offset += this->get_buffer_length_() / 2u;
this->command(WRITE_COLOR);
this->start_data_();
this->write_array(this->buffer_ + offset, length);
this->end_data_();
}
void HOT WeActEPaper2P9In3C::draw_absolute_pixel_internal(int x, int y, Color color) {
if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0)
return;
const uint32_t pos = (x + y * this->get_width_internal()) / 8u;
const uint8_t subpos = 0x80 >> (x & 0x07);
// flip logic
if (color == display::COLOR_OFF) {
this->buffer_[pos] |= subpos;
} else {
this->buffer_[pos] &= ~subpos;
}
// draw red pixels only if the color contains red only
const uint32_t buf_half_len = this->get_buffer_length_() / 2u;
if (((color.red > 0) && (color.green == 0) && (color.blue == 0))) {
this->buffer_[pos + buf_half_len] |= subpos;
} else {
this->buffer_[pos + buf_half_len] &= ~subpos;
}
}
void WeActEPaper2P9In3C::full_update_() {
ESP_LOGI(TAG, "Performing full e-paper update.");
this->write_buffer_(0, this->get_height_internal());
SEND(UPDATE_FULL);
this->command(ACTIVATE); // don't wait here
this->is_busy_ = false;
}
void WeActEPaper2P9In3C::display() {
if (this->is_busy_ || (this->busy_pin_ != nullptr && this->busy_pin_->digital_read()))
return;
this->is_busy_ = true;
this->full_update_();
}
} // namespace waveshare_epaper
} // namespace esphome