ESPHome  2022.6.3
ssd1306_base.cpp
Go to the documentation of this file.
1 #include "ssd1306_base.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/helpers.h"
4 
5 namespace esphome {
6 namespace ssd1306_base {
7 
8 static const char *const TAG = "ssd1306";
9 
10 static const uint8_t SSD1306_MAX_CONTRAST = 255;
11 static const uint8_t SSD1305_MAX_BRIGHTNESS = 255;
12 
13 static const uint8_t SSD1306_COMMAND_DISPLAY_OFF = 0xAE;
14 static const uint8_t SSD1306_COMMAND_DISPLAY_ON = 0xAF;
15 static const uint8_t SSD1306_COMMAND_SET_DISPLAY_CLOCK_DIV = 0xD5;
16 static const uint8_t SSD1306_COMMAND_SET_MULTIPLEX = 0xA8;
17 static const uint8_t SSD1306_COMMAND_SET_DISPLAY_OFFSET_Y = 0xD3;
18 static const uint8_t SSD1306_COMMAND_SET_START_LINE = 0x40;
19 static const uint8_t SSD1306_COMMAND_CHARGE_PUMP = 0x8D;
20 static const uint8_t SSD1306_COMMAND_MEMORY_MODE = 0x20;
21 static const uint8_t SSD1306_COMMAND_SEGRE_MAP = 0xA0;
22 static const uint8_t SSD1306_COMMAND_COM_SCAN_INC = 0xC0;
23 static const uint8_t SSD1306_COMMAND_COM_SCAN_DEC = 0xC8;
24 static const uint8_t SSD1306_COMMAND_SET_COM_PINS = 0xDA;
25 static const uint8_t SSD1306_COMMAND_SET_CONTRAST = 0x81;
26 static const uint8_t SSD1306_COMMAND_SET_PRE_CHARGE = 0xD9;
27 static const uint8_t SSD1306_COMMAND_SET_VCOM_DETECT = 0xDB;
28 static const uint8_t SSD1306_COMMAND_DISPLAY_ALL_ON_RESUME = 0xA4;
29 static const uint8_t SSD1306_COMMAND_DEACTIVATE_SCROLL = 0x2E;
30 static const uint8_t SSD1306_COMMAND_COLUMN_ADDRESS = 0x21;
31 static const uint8_t SSD1306_COMMAND_PAGE_ADDRESS = 0x22;
32 static const uint8_t SSD1306_COMMAND_NORMAL_DISPLAY = 0xA6;
33 static const uint8_t SSD1306_COMMAND_INVERSE_DISPLAY = 0xA7;
34 
35 static const uint8_t SSD1305_COMMAND_SET_BRIGHTNESS = 0x82;
36 static const uint8_t SSD1305_COMMAND_SET_AREA_COLOR = 0xD8;
37 
39  this->init_internal_(this->get_buffer_length_());
40 
41  // Turn off display during initialization (0xAE)
42  this->command(SSD1306_COMMAND_DISPLAY_OFF);
43 
44  // Set oscillator frequency to 4'b1000 with no clock division (0xD5)
45  this->command(SSD1306_COMMAND_SET_DISPLAY_CLOCK_DIV);
46  // Oscillator frequency <= 4'b1000, no clock division
47  this->command(0x80);
48 
49  // Enable low power display mode for SSD1305 (0xD8)
50  if (this->is_ssd1305_()) {
51  this->command(SSD1305_COMMAND_SET_AREA_COLOR);
52  this->command(0x05);
53  }
54 
55  // Set mux ratio to [Y pixels - 1] (0xA8)
56  this->command(SSD1306_COMMAND_SET_MULTIPLEX);
57  this->command(this->get_height_internal() - 1);
58 
59  // Set Y offset (0xD3)
60  this->command(SSD1306_COMMAND_SET_DISPLAY_OFFSET_Y);
61  this->command(0x00 + this->offset_y_);
62  // Set start line at line 0 (0x40)
63  this->command(SSD1306_COMMAND_SET_START_LINE | 0x00);
64 
65  // SSD1305 does not have charge pump
66  if (!this->is_ssd1305_()) {
67  // Enable charge pump (0x8D)
68  this->command(SSD1306_COMMAND_CHARGE_PUMP);
69  if (this->external_vcc_) {
70  this->command(0x10);
71  } else {
72  this->command(0x14);
73  }
74  }
75 
76  // Set addressing mode to horizontal (0x20)
77  this->command(SSD1306_COMMAND_MEMORY_MODE);
78  this->command(0x00);
79 
80  // X flip mode (0xA0, 0xA1)
81  this->command(SSD1306_COMMAND_SEGRE_MAP | this->flip_x_);
82 
83  // Y flip mode (0xC0, 0xC8)
84  this->command(SSD1306_COMMAND_COM_SCAN_INC | (this->flip_y_ << 3));
85 
86  // Set pin configuration (0xDA)
87  this->command(SSD1306_COMMAND_SET_COM_PINS);
88  switch (this->model_) {
92  case SH1106_MODEL_96_16:
93  this->command(0x02);
94  break;
99  case SH1106_MODEL_64_48:
100  case SH1107_MODEL_128_64:
103  this->command(0x12);
104  break;
105  }
106 
107  // Pre-charge period (0xD9)
108  this->command(SSD1306_COMMAND_SET_PRE_CHARGE);
109  if (this->external_vcc_) {
110  this->command(0x22);
111  } else {
112  this->command(0xF1);
113  }
114 
115  // Set V_COM (0xDB)
116  this->command(SSD1306_COMMAND_SET_VCOM_DETECT);
117  switch (this->model_) {
118  case SH1107_MODEL_128_64:
119  this->command(0x35);
120  break;
121  default:
122  this->command(0x00);
123  break;
124  }
125 
126  // Display output follow RAM (0xA4)
127  this->command(SSD1306_COMMAND_DISPLAY_ALL_ON_RESUME);
128 
129  // Inverse display mode (0xA6, 0xA7)
130  this->command(SSD1306_COMMAND_NORMAL_DISPLAY | this->invert_);
131 
132  // Disable scrolling mode (0x2E)
133  this->command(SSD1306_COMMAND_DEACTIVATE_SCROLL);
134 
135  // Contrast and brighrness
136  // SSD1306 does not have brightness setting
137  set_contrast(this->contrast_);
138  if (this->is_ssd1305_())
140 
141  this->fill(Color::BLACK); // clear display - ensures we do not see garbage at power-on
142  this->display(); // ...write buffer, which actually clears the display's memory
143 
144  this->turn_on();
145 }
147  if (this->is_sh1106_()) {
148  this->write_display_data();
149  return;
150  }
151 
152  this->command(SSD1306_COMMAND_COLUMN_ADDRESS);
153  switch (this->model_) {
154  case SSD1306_MODEL_64_48:
155  case SSD1306_MODEL_64_32:
156  this->command(0x20 + this->offset_x_);
157  this->command(0x20 + this->offset_x_ + this->get_width_internal() - 1);
158  break;
159  default:
160  this->command(0 + this->offset_x_); // Page start address, 0
161  this->command(this->get_width_internal() + this->offset_x_ - 1);
162  break;
163  }
164 
165  this->command(SSD1306_COMMAND_PAGE_ADDRESS);
166  // Page start address, 0
167  this->command(0);
168  // Page end address:
169  this->command((this->get_height_internal() / 8) - 1);
170 
171  this->write_display_data();
172 }
173 bool SSD1306::is_sh1106_() const {
174  return this->model_ == SH1106_MODEL_96_16 || this->model_ == SH1106_MODEL_128_32 ||
175  this->model_ == SH1106_MODEL_128_64;
176 }
177 bool SSD1306::is_ssd1305_() const {
178  return this->model_ == SSD1305_MODEL_128_64 || this->model_ == SSD1305_MODEL_128_64;
179 }
181  this->do_update_();
182  this->display();
183 }
184 void SSD1306::set_contrast(float contrast) {
185  // validation
186  this->contrast_ = clamp(contrast, 0.0F, 1.0F);
187  // now write the new contrast level to the display (0x81)
188  this->command(SSD1306_COMMAND_SET_CONTRAST);
189  this->command(int(SSD1306_MAX_CONTRAST * (this->contrast_)));
190 }
191 void SSD1306::set_brightness(float brightness) {
192  // validation
193  if (!this->is_ssd1305_())
194  return;
195  this->brightness_ = clamp(brightness, 0.0F, 1.0F);
196  // now write the new brightness level to the display (0x82)
197  this->command(SSD1305_COMMAND_SET_BRIGHTNESS);
198  this->command(int(SSD1305_MAX_BRIGHTNESS * (this->brightness_)));
199 }
200 bool SSD1306::is_on() { return this->is_on_; }
202  this->command(SSD1306_COMMAND_DISPLAY_ON);
203  this->is_on_ = true;
204 }
206  this->command(SSD1306_COMMAND_DISPLAY_OFF);
207  this->is_on_ = false;
208 }
210  switch (this->model_) {
211  case SH1107_MODEL_128_64:
212  return 128;
214  case SSD1306_MODEL_64_32:
215  case SH1106_MODEL_128_32:
217  return 32;
219  case SH1106_MODEL_128_64:
221  return 64;
222  case SSD1306_MODEL_96_16:
223  case SH1106_MODEL_96_16:
224  return 16;
225  case SSD1306_MODEL_64_48:
226  case SH1106_MODEL_64_48:
227  return 48;
228  default:
229  return 0;
230  }
231 }
233  switch (this->model_) {
235  case SH1106_MODEL_128_32:
237  case SH1106_MODEL_128_64:
240  return 128;
241  case SSD1306_MODEL_96_16:
242  case SH1106_MODEL_96_16:
243  return 96;
244  case SSD1306_MODEL_64_48:
245  case SSD1306_MODEL_64_32:
246  case SH1106_MODEL_64_48:
247  case SH1107_MODEL_128_64:
248  return 64;
249  default:
250  return 0;
251  }
252 }
254  return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 8u;
255 }
256 void HOT SSD1306::draw_absolute_pixel_internal(int x, int y, Color color) {
257  if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0)
258  return;
259 
260  uint16_t pos = x + (y / 8) * this->get_width_internal();
261  uint8_t subpos = y & 0x07;
262  if (color.is_on()) {
263  this->buffer_[pos] |= (1 << subpos);
264  } else {
265  this->buffer_[pos] &= ~(1 << subpos);
266  }
267 }
268 void SSD1306::fill(Color color) {
269  uint8_t fill = color.is_on() ? 0xFF : 0x00;
270  for (uint32_t i = 0; i < this->get_buffer_length_(); i++)
271  this->buffer_[i] = fill;
272 }
274  if (this->reset_pin_ != nullptr) {
275  this->reset_pin_->setup();
276  this->reset_pin_->digital_write(true);
277  delay(1);
278  // Trigger Reset
279  this->reset_pin_->digital_write(false);
280  delay(10);
281  // Wake up
282  this->reset_pin_->digital_write(true);
283  }
284 }
285 const char *SSD1306::model_str_() {
286  switch (this->model_) {
288  return "SSD1306 128x32";
290  return "SSD1306 128x64";
291  case SSD1306_MODEL_64_32:
292  return "SSD1306 64x32";
293  case SSD1306_MODEL_96_16:
294  return "SSD1306 96x16";
295  case SSD1306_MODEL_64_48:
296  return "SSD1306 64x48";
297  case SH1106_MODEL_128_32:
298  return "SH1106 128x32";
299  case SH1106_MODEL_128_64:
300  return "SH1106 128x64";
301  case SH1106_MODEL_96_16:
302  return "SH1106 96x16";
303  case SH1106_MODEL_64_48:
304  return "SH1106 64x48";
305  case SH1107_MODEL_128_64:
306  return "SH1107 128x64";
308  return "SSD1305 128x32";
310  return "SSD1305 128x64";
311  default:
312  return "Unknown";
313  }
314 }
315 
316 } // namespace ssd1306_base
317 } // namespace esphome
virtual void digital_write(bool value)=0
virtual void write_display_data()=0
virtual void setup()=0
constexpr const T & clamp(const T &v, const T &lo, const T &hi, Compare comp)
Definition: helpers.h:84
void set_contrast(float contrast)
void init_internal_(uint32_t buffer_length)
void draw_absolute_pixel_internal(int x, int y, Color color) override
static const Color BLACK
Definition: color.h:147
Definition: a4988.cpp:4
virtual void command(uint8_t value)=0
bool is_on() ALWAYS_INLINE
Definition: color.h:46
void fill(Color color) override
void set_brightness(float brightness)
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:27