ESPHome  2024.5.2
st7701s.cpp
Go to the documentation of this file.
1 #ifdef USE_ESP32_VARIANT_ESP32S3
2 #include "st7701s.h"
3 #include "esphome/core/log.h"
4 
5 namespace esphome {
6 namespace st7701s {
7 
8 void ST7701S::setup() {
9  esph_log_config(TAG, "Setting up ST7701S");
10  this->spi_setup();
11  esp_lcd_rgb_panel_config_t config{};
12  config.flags.fb_in_psram = 1;
13  config.timings.h_res = this->width_;
14  config.timings.v_res = this->height_;
15  config.timings.hsync_pulse_width = this->hsync_pulse_width_;
16  config.timings.hsync_back_porch = this->hsync_back_porch_;
17  config.timings.hsync_front_porch = this->hsync_front_porch_;
18  config.timings.vsync_pulse_width = this->vsync_pulse_width_;
19  config.timings.vsync_back_porch = this->vsync_back_porch_;
20  config.timings.vsync_front_porch = this->vsync_front_porch_;
21  config.timings.flags.pclk_active_neg = this->pclk_inverted_;
22  config.timings.pclk_hz = this->pclk_frequency_;
23  config.clk_src = LCD_CLK_SRC_PLL160M;
24  config.sram_trans_align = 64;
25  config.psram_trans_align = 64;
26  size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]);
27  for (size_t i = 0; i != data_pin_count; i++) {
28  config.data_gpio_nums[i] = this->data_pins_[i]->get_pin();
29  }
30  config.data_width = data_pin_count;
31  config.disp_gpio_num = -1;
32  config.hsync_gpio_num = this->hsync_pin_->get_pin();
33  config.vsync_gpio_num = this->vsync_pin_->get_pin();
34  config.de_gpio_num = this->de_pin_->get_pin();
35  config.pclk_gpio_num = this->pclk_pin_->get_pin();
36  esp_err_t err = esp_lcd_new_rgb_panel(&config, &this->handle_);
37  if (err != ESP_OK) {
38  esph_log_e(TAG, "lcd_new_rgb_panel failed: %s", esp_err_to_name(err));
39  }
40  ESP_ERROR_CHECK(esp_lcd_panel_reset(this->handle_));
41  ESP_ERROR_CHECK(esp_lcd_panel_init(this->handle_));
42  this->write_init_sequence_();
43  esph_log_config(TAG, "ST7701S setup complete");
44 }
45 
46 void ST7701S::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order,
47  display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) {
48  if (w <= 0 || h <= 0)
49  return;
50  // if color mapping is required, pass the buck.
51  // note that endianness is not considered here - it is assumed to match!
52  if (bitness != display::COLOR_BITNESS_565) {
53  return display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset,
54  x_pad);
55  }
56  x_start += this->offset_x_;
57  y_start += this->offset_y_;
58  esp_err_t err;
59  // x_ and y_offset are offsets into the source buffer, unrelated to our own offsets into the display.
60  if (x_offset == 0 && x_pad == 0 && y_offset == 0) {
61  // we could deal here with a non-zero y_offset, but if x_offset is zero, y_offset probably will be so don't bother
62  err = esp_lcd_panel_draw_bitmap(this->handle_, x_start, y_start, x_start + w, y_start + h, ptr);
63  } else {
64  // draw line by line
65  auto stride = x_offset + w + x_pad;
66  for (int y = 0; y != h; y++) {
67  err = esp_lcd_panel_draw_bitmap(this->handle_, x_start, y + y_start, x_start + w, y + y_start + 1,
68  ptr + ((y + y_offset) * stride + x_offset) * 2);
69  if (err != ESP_OK)
70  break;
71  }
72  }
73  if (err != ESP_OK)
74  esph_log_e(TAG, "lcd_lcd_panel_draw_bitmap failed: %s", esp_err_to_name(err));
75 }
76 
77 void ST7701S::draw_pixel_at(int x, int y, Color color) {
78  if (!this->get_clipping().inside(x, y))
79  return; // NOLINT
80 
81  switch (this->rotation_) {
83  break;
85  std::swap(x, y);
86  x = this->width_ - x - 1;
87  break;
89  x = this->width_ - x - 1;
90  y = this->height_ - y - 1;
91  break;
93  std::swap(x, y);
94  y = this->height_ - y - 1;
95  break;
96  }
98 
99  this->draw_pixels_at(x, y, 1, 1, (const uint8_t *) &pixel, display::COLOR_ORDER_RGB, display::COLOR_BITNESS_565, true,
100  0, 0, 0);
101  App.feed_wdt();
102 }
103 
104 void ST7701S::write_command_(uint8_t value) {
105  this->enable();
106  if (this->dc_pin_ == nullptr) {
107  this->write(value, 9);
108  } else {
109  this->dc_pin_->digital_write(false);
110  this->write_byte(value);
111  this->dc_pin_->digital_write(true);
112  }
113  this->disable();
114 }
115 
116 void ST7701S::write_data_(uint8_t value) {
117  this->enable();
118  if (this->dc_pin_ == nullptr) {
119  this->write(value | 0x100, 9);
120  } else {
121  this->dc_pin_->digital_write(true);
122  this->write_byte(value);
123  }
124  this->disable();
125 }
126 
131 void ST7701S::write_sequence_(uint8_t cmd, size_t len, const uint8_t *bytes) {
132  this->write_command_(cmd);
133  while (len-- != 0)
134  this->write_data_(*bytes++);
135 }
136 
138  for (size_t i = 0; i != this->init_sequence_.size();) {
139  uint8_t cmd = this->init_sequence_[i++];
140  size_t len = this->init_sequence_[i++];
141  this->write_sequence_(cmd, len, &this->init_sequence_[i]);
142  i += len;
143  esph_log_v(TAG, "Command %X, %d bytes", cmd, len);
144  if (cmd == SW_RESET_CMD)
145  delay(6);
146  }
147  // st7701 does not appear to support axis swapping
148  this->write_sequence_(CMD2_BKSEL, sizeof(CMD2_BK0), CMD2_BK0);
149  this->write_command_(SDIR_CMD); // this is in the BK0 command set
150  this->write_data_(this->mirror_x_ ? 0x04 : 0x00);
151  uint8_t val = this->color_mode_ == display::COLOR_ORDER_BGR ? 0x08 : 0x00;
152  if (this->mirror_y_)
153  val |= 0x10;
154  this->write_command_(MADCTL_CMD);
155  this->write_data_(val);
156  esph_log_d(TAG, "write MADCTL %X", val);
158  this->set_timeout(120, [this] {
159  this->write_command_(SLEEP_OUT);
160  this->write_command_(DISPLAY_ON);
161  });
162 }
163 
165  ESP_LOGCONFIG("", "ST7701S RGB LCD");
166  ESP_LOGCONFIG(TAG, " Height: %u", this->height_);
167  ESP_LOGCONFIG(TAG, " Width: %u", this->width_);
168  LOG_PIN(" CS Pin: ", this->cs_);
169  LOG_PIN(" DC Pin: ", this->dc_pin_);
170  LOG_PIN(" DE Pin: ", this->de_pin_);
171  LOG_PIN(" Reset Pin: ", this->reset_pin_);
172  size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]);
173  for (size_t i = 0; i != data_pin_count; i++)
174  ESP_LOGCONFIG(TAG, " Data pin %d: %s", i, (this->data_pins_[i])->dump_summary().c_str());
175  ESP_LOGCONFIG(TAG, " SPI Data rate: %dMHz", (unsigned) (this->data_rate_ / 1000000));
176 }
177 
178 } // namespace st7701s
179 } // namespace esphome
180 #endif // USE_ESP32_VARIANT_ESP32S3
virtual void digital_write(bool value)=0
const uint8_t INVERT_ON
Definition: st7701s.h:24
InternalGPIOPin * hsync_pin_
Definition: st7701s.h:86
uint16_t hsync_pulse_width_
Definition: st7701s.h:91
uint16_t hsync_back_porch_
Definition: st7701s.h:92
InternalGPIOPin * pclk_pin_
Definition: st7701s.h:85
const uint8_t INVERT_OFF
Definition: st7701s.h:23
const uint8_t MADCTL_CMD
Definition: st7701s.h:22
const uint8_t SLEEP_OUT
Definition: st7701s.h:20
void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override
Definition: st7701s.cpp:46
static uint16_t color_to_565(Color color, ColorOrder color_order=ColorOrder::COLOR_ORDER_RGB)
void write_sequence_(uint8_t cmd, size_t len, const uint8_t *bytes)
this relies upon the init sequence being well-formed, which is guaranteed by the Python init code...
Definition: st7701s.cpp:131
uint16_t vsync_front_porch_
Definition: st7701s.h:96
void write_data_(uint8_t value)
Definition: st7701s.cpp:116
InternalGPIOPin * vsync_pin_
Definition: st7701s.h:87
uint16_t x
Definition: tt21100.cpp:17
InternalGPIOPin * data_pins_[16]
Definition: st7701s.h:90
const uint8_t CMD2_BK0[5]
Definition: st7701s.h:27
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition: component.cpp:69
void write_command_(uint8_t value)
Definition: st7701s.cpp:104
uint16_t vsync_back_porch_
Definition: st7701s.h:95
mopeka_std_values val[4]
GPIOPin * cs_
Definition: spi.h:395
Rect get_clipping() const
Get the current the clipping rectangle.
Definition: display.cpp:567
uint16_t vsync_pulse_width_
Definition: st7701s.h:94
InternalGPIOPin * de_pin_
Definition: st7701s.h:84
uint16_t y
Definition: tt21100.cpp:18
void dump_config() override
Definition: st7701s.cpp:164
virtual uint8_t get_pin() const =0
uint32_t pclk_frequency_
Definition: st7701s.h:98
const uint8_t DISPLAY_ON
Definition: st7701s.h:25
display::ColorOrder color_mode_
Definition: st7701s.h:102
void write(uint16_t data, size_t num_bits)
Write a single data item, up to 32 bits.
Definition: spi.h:440
constexpr14 T convert_big_endian(T val)
Convert a value between host byte order and big endian (most significant byte first) order...
Definition: helpers.h:239
Application App
Global storage of Application pointer - only one Application can exist.
const uint8_t SDIR_CMD
Definition: st7701s.h:21
uint32_t data_rate_
Definition: spi.h:393
void swap(optional< T > &x, optional< T > &y)
Definition: optional.h:210
DisplayRotation rotation_
Definition: display.h:658
std::vector< uint8_t > init_sequence_
Definition: st7701s.h:97
void setup() override
Definition: st7701s.cpp:8
std::string size_t len
Definition: helpers.h:292
uint8_t h
Definition: bl0939.h:21
uint16_t hsync_front_porch_
Definition: st7701s.h:93
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
const uint8_t SW_RESET_CMD
Definition: st7701s.h:19
std::vector< uint8_t > bytes
Definition: sml_parser.h:12
void draw_pixel_at(int x, int y, Color color) override
Definition: st7701s.cpp:77
esp_lcd_panel_handle_t handle_
Definition: st7701s.h:110
virtual void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, ColorOrder order, ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad)
Given an array of pixels encoded in the nominated format, draw these into the display&#39;s buffer...
Definition: display.cpp:54
stm32_cmd_t * cmd
Definition: stm32flash.h:96
const uint8_t CMD2_BKSEL
Definition: st7701s.h:26
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26