ESPHome  2022.5.1
ili9341_display.cpp
Go to the documentation of this file.
1 #include "ili9341_display.h"
2 #include "esphome/core/log.h"
4 #include "esphome/core/helpers.h"
5 #include "esphome/core/hal.h"
6 
7 namespace esphome {
8 namespace ili9341 {
9 
10 static const char *const TAG = "ili9341";
11 
13  this->init_internal_(this->get_buffer_length_());
14  this->dc_pin_->setup(); // OUTPUT
15  this->dc_pin_->digital_write(false);
16  if (this->reset_pin_ != nullptr) {
17  this->reset_pin_->setup(); // OUTPUT
18  this->reset_pin_->digital_write(true);
19  }
20  if (this->led_pin_ != nullptr) {
21  this->led_pin_->setup();
22  this->led_pin_->digital_write(true);
23  }
24  this->spi_setup();
25 
26  this->reset_();
27 }
28 
30  LOG_DISPLAY("", "ili9341", this);
31  ESP_LOGCONFIG(TAG, " Width: %d, Height: %d, Rotation: %d", this->width_, this->height_, this->rotation_);
32  LOG_PIN(" Reset Pin: ", this->reset_pin_);
33  LOG_PIN(" DC Pin: ", this->dc_pin_);
34  LOG_PIN(" Busy Pin: ", this->busy_pin_);
35  LOG_PIN(" Backlight Pin: ", this->led_pin_);
36  LOG_UPDATE_INTERVAL(this);
37 }
38 
40 void ILI9341Display::command(uint8_t value) {
41  this->start_command_();
42  this->write_byte(value);
43  this->end_command_();
44 }
45 
47  if (this->reset_pin_ != nullptr) {
48  this->reset_pin_->digital_write(false);
49  delay(10);
50  this->reset_pin_->digital_write(true);
51  delay(10);
52  }
53 }
54 
55 void ILI9341Display::data(uint8_t value) {
56  this->start_data_();
57  this->write_byte(value);
58  this->end_data_();
59 }
60 
61 void ILI9341Display::send_command(uint8_t command_byte, const uint8_t *data_bytes, uint8_t num_data_bytes) {
62  this->command(command_byte); // Send the command byte
63  this->start_data_();
64  this->write_array(data_bytes, num_data_bytes);
65  this->end_data_();
66 }
67 
68 uint8_t ILI9341Display::read_command(uint8_t command_byte, uint8_t index) {
69  uint8_t data = 0x10 + index;
70  this->send_command(0xD9, &data, 1); // Set Index Register
71  uint8_t result;
72  this->start_command_();
73  this->write_byte(command_byte);
74  this->start_data_();
75  do {
76  result = this->read_byte();
77  } while (index--);
78  this->end_data_();
79  return result;
80 }
81 
83  this->do_update_();
84  this->display_();
85 }
86 
88  // we will only update the changed window to the display
89  uint16_t w = this->x_high_ - this->x_low_ + 1;
90  uint16_t h = this->y_high_ - this->y_low_ + 1;
91 
92  set_addr_window_(this->x_low_, this->y_low_, w, h);
93  this->start_data_();
94  uint32_t start_pos = ((this->y_low_ * this->width_) + x_low_);
95  for (uint16_t row = 0; row < h; row++) {
96  uint32_t pos = start_pos + (row * width_);
97  uint32_t rem = w;
98 
99  while (rem > 0) {
100  uint32_t sz = buffer_to_transfer_(pos, rem);
101  this->write_array(transfer_buffer_, 2 * sz);
102  pos += sz;
103  rem -= sz;
104  }
105  }
106  this->end_data_();
107 
108  // invalidate watermarks
109  this->x_low_ = this->width_;
110  this->y_low_ = this->height_;
111  this->x_high_ = 0;
112  this->y_high_ = 0;
113 }
114 
115 uint16_t ILI9341Display::convert_to_16bit_color_(uint8_t color_8bit) {
116  int r = color_8bit >> 5;
117  int g = (color_8bit >> 2) & 0x07;
118  int b = color_8bit & 0x03;
119  uint16_t color = (r * 0x04) << 11;
120  color |= (g * 0x09) << 5;
121  color |= (b * 0x0A);
122 
123  return color;
124 }
125 
126 uint8_t ILI9341Display::convert_to_8bit_color_(uint16_t color_16bit) {
127  // convert 16bit color to 8 bit buffer
128  uint8_t r = color_16bit >> 11;
129  uint8_t g = (color_16bit >> 5) & 0x3F;
130  uint8_t b = color_16bit & 0x1F;
131 
132  return ((b / 0x0A) | ((g / 0x09) << 2) | ((r / 0x04) << 5));
133 }
134 
136  auto color565 = display::ColorUtil::color_to_565(color);
137  memset(this->buffer_, convert_to_8bit_color_(color565), this->get_buffer_length_());
138  this->x_low_ = 0;
139  this->y_low_ = 0;
140  this->x_high_ = this->get_width_internal() - 1;
141  this->y_high_ = this->get_height_internal() - 1;
142 }
143 
145  if (color.raw_32 == Color::BLACK.raw_32) {
146  memset(transfer_buffer_, 0, sizeof(transfer_buffer_));
147  } else {
148  uint8_t *dst = transfer_buffer_;
149  auto color565 = display::ColorUtil::color_to_565(color);
150 
151  while (dst < transfer_buffer_ + sizeof(transfer_buffer_)) {
152  *dst++ = (uint8_t)(color565 >> 8);
153  *dst++ = (uint8_t) color565;
154  }
155  }
156 
157  uint32_t rem = this->get_width_internal() * this->get_height_internal();
158 
159  this->set_addr_window_(0, 0, this->get_width_internal(), this->get_height_internal());
160  this->start_data_();
161 
162  while (rem > 0) {
163  size_t sz = rem <= sizeof(transfer_buffer_) ? rem : sizeof(transfer_buffer_);
164  this->write_array(transfer_buffer_, sz);
165  rem -= sz;
166  }
167 
168  this->end_data_();
169 
170  memset(buffer_, 0, (this->get_width_internal()) * (this->get_height_internal()));
171 }
172 
174  if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0)
175  return;
176 
177  // low and high watermark may speed up drawing from buffer
178  this->x_low_ = (x < this->x_low_) ? x : this->x_low_;
179  this->y_low_ = (y < this->y_low_) ? y : this->y_low_;
180  this->x_high_ = (x > this->x_high_) ? x : this->x_high_;
181  this->y_high_ = (y > this->y_high_) ? y : this->y_high_;
182 
183  uint32_t pos = (y * width_) + x;
184  auto color565 = display::ColorUtil::color_to_565(color);
185  buffer_[pos] = convert_to_8bit_color_(color565);
186 }
187 
188 // should return the total size: return this->get_width_internal() * this->get_height_internal() * 2 // 16bit color
189 // values per bit is huge
191 
193  this->dc_pin_->digital_write(false);
194  this->enable();
195 }
196 
199  this->dc_pin_->digital_write(true);
200  this->enable();
201 }
203 
204 void ILI9341Display::init_lcd_(const uint8_t *init_cmd) {
205  uint8_t cmd, x, num_args;
206  const uint8_t *addr = init_cmd;
207  while ((cmd = progmem_read_byte(addr++)) > 0) {
208  x = progmem_read_byte(addr++);
209  num_args = x & 0x7F;
210  send_command(cmd, addr, num_args);
211  addr += num_args;
212  if (x & 0x80)
213  delay(150); // NOLINT
214  }
215 }
216 
217 void ILI9341Display::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t w, uint16_t h) {
218  uint16_t x2 = (x1 + w - 1), y2 = (y1 + h - 1);
219  this->command(ILI9341_CASET); // Column address set
220  this->start_data_();
221  this->write_byte(x1 >> 8);
222  this->write_byte(x1);
223  this->write_byte(x2 >> 8);
224  this->write_byte(x2);
225  this->end_data_();
226  this->command(ILI9341_PASET); // Row address set
227  this->start_data_();
228  this->write_byte(y1 >> 8);
229  this->write_byte(y1);
230  this->write_byte(y2 >> 8);
231  this->write_byte(y2);
232  this->end_data_();
233  this->command(ILI9341_RAMWR); // Write to RAM
234 }
235 
236 void ILI9341Display::invert_display_(bool invert) { this->command(invert ? ILI9341_INVON : ILI9341_INVOFF); }
237 
240 
241 uint32_t ILI9341Display::buffer_to_transfer_(uint32_t pos, uint32_t sz) {
242  uint8_t *src = buffer_ + pos;
243  uint8_t *dst = transfer_buffer_;
244 
245  if (sz > sizeof(transfer_buffer_) / 2) {
246  sz = sizeof(transfer_buffer_) / 2;
247  }
248 
249  for (uint32_t i = 0; i < sz; ++i) {
250  uint16_t color = convert_to_16bit_color_(*src++);
251  *dst++ = (uint8_t)(color >> 8);
252  *dst++ = (uint8_t) color;
253  }
254 
255  return sz;
256 }
257 
258 // M5Stack display
260  this->init_lcd_(INITCMD_M5STACK);
261  this->width_ = 320;
262  this->height_ = 240;
263  this->invert_display_(true);
265 }
266 
267 // 24_TFT display
269  this->init_lcd_(INITCMD_TFT);
270  this->width_ = 240;
271  this->height_ = 320;
273 }
274 
275 } // namespace ili9341
276 } // namespace esphome
virtual void digital_write(bool value)=0
void init_lcd_(const uint8_t *init_cmd)
static uint16_t color_to_565(Color color, ColorOrder color_order=ColorOrder::COLOR_ORDER_RGB)
void send_command(uint8_t command_byte, const uint8_t *data_bytes, uint8_t num_data_bytes)
virtual void setup()=0
void init_internal_(uint32_t buffer_length)
void fill(Color color) override
void set_addr_window_(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
int16_t width_
Display width as modified by current rotation.
void draw_absolute_pixel_internal(int x, int y, Color color) override
uint32_t buffer_to_transfer_(uint32_t pos, uint32_t sz)
const float PROCESSOR
For components that use data from sensors like displays.
Definition: component.cpp:19
uint8_t progmem_read_byte(const uint8_t *addr)
Definition: core.cpp:56
uint8_t h
Definition: bl0939.h:21
static const Color BLACK
Definition: color.h:147
uint8_t read_command(uint8_t command_byte, uint8_t index)
float get_setup_priority() const override
Definition: a4988.cpp:4
int16_t height_
Display height as modified by current rotation.
uint8_t convert_to_8bit_color_(uint16_t color_16bit)
uint16_t convert_to_16bit_color_(uint8_t color_8bit)
uint32_t raw_32
Definition: color.h:31
stm32_cmd_t * cmd
Definition: stm32flash.h:96
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:27