mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Add proper support for SH1107 to SSD1306 component (#5166)
This commit is contained in:
		| @@ -33,6 +33,7 @@ MODELS = { | |||||||
|     "SH1106_96X16": SSD1306Model.SH1106_MODEL_96_16, |     "SH1106_96X16": SSD1306Model.SH1106_MODEL_96_16, | ||||||
|     "SH1106_64X48": SSD1306Model.SH1106_MODEL_64_48, |     "SH1106_64X48": SSD1306Model.SH1106_MODEL_64_48, | ||||||
|     "SH1107_128X64": SSD1306Model.SH1107_MODEL_128_64, |     "SH1107_128X64": SSD1306Model.SH1107_MODEL_128_64, | ||||||
|  |     "SH1107_128X128": SSD1306Model.SH1107_MODEL_128_128, | ||||||
|     "SSD1305_128X32": SSD1306Model.SSD1305_MODEL_128_32, |     "SSD1305_128X32": SSD1306Model.SSD1305_MODEL_128_32, | ||||||
|     "SSD1305_128X64": SSD1306Model.SSD1305_MODEL_128_64, |     "SSD1305_128X64": SSD1306Model.SSD1305_MODEL_128_64, | ||||||
| } | } | ||||||
| @@ -63,8 +64,10 @@ SSD1306_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend( | |||||||
|         cv.Optional(CONF_EXTERNAL_VCC): cv.boolean, |         cv.Optional(CONF_EXTERNAL_VCC): cv.boolean, | ||||||
|         cv.Optional(CONF_FLIP_X, default=True): cv.boolean, |         cv.Optional(CONF_FLIP_X, default=True): cv.boolean, | ||||||
|         cv.Optional(CONF_FLIP_Y, default=True): cv.boolean, |         cv.Optional(CONF_FLIP_Y, default=True): cv.boolean, | ||||||
|         cv.Optional(CONF_OFFSET_X, default=0): cv.int_range(min=-32, max=32), |         # Offsets determine shifts of memory location to LCD rows/columns, | ||||||
|         cv.Optional(CONF_OFFSET_Y, default=0): cv.int_range(min=-32, max=32), |         # and this family of controllers supports up to 128x128 screens | ||||||
|  |         cv.Optional(CONF_OFFSET_X, default=0): cv.int_range(min=0, max=128), | ||||||
|  |         cv.Optional(CONF_OFFSET_Y, default=0): cv.int_range(min=0, max=128), | ||||||
|         cv.Optional(CONF_INVERT, default=False): cv.boolean, |         cv.Optional(CONF_INVERT, default=False): cv.boolean, | ||||||
|     } |     } | ||||||
| ).extend(cv.polling_component_schema("1s")) | ).extend(cv.polling_component_schema("1s")) | ||||||
|   | |||||||
| @@ -35,16 +35,31 @@ static const uint8_t SSD1306_COMMAND_INVERSE_DISPLAY = 0xA7; | |||||||
| static const uint8_t SSD1305_COMMAND_SET_BRIGHTNESS = 0x82; | static const uint8_t SSD1305_COMMAND_SET_BRIGHTNESS = 0x82; | ||||||
| static const uint8_t SSD1305_COMMAND_SET_AREA_COLOR = 0xD8; | static const uint8_t SSD1305_COMMAND_SET_AREA_COLOR = 0xD8; | ||||||
|  |  | ||||||
|  | static const uint8_t SH1107_COMMAND_SET_START_LINE = 0xDC; | ||||||
|  | static const uint8_t SH1107_COMMAND_CHARGE_PUMP = 0xAD; | ||||||
|  |  | ||||||
| void SSD1306::setup() { | void SSD1306::setup() { | ||||||
|   this->init_internal_(this->get_buffer_length_()); |   this->init_internal_(this->get_buffer_length_()); | ||||||
|  |  | ||||||
|  |   // SH1107 resources | ||||||
|  |   // | ||||||
|  |   // Datasheet v2.3: | ||||||
|  |   // www.displayfuture.com/Display/datasheet/controller/SH1107.pdf | ||||||
|  |   // Adafruit C++ driver: | ||||||
|  |   // github.com/adafruit/Adafruit_SH110x | ||||||
|  |   // Adafruit CircuitPython driver: | ||||||
|  |   // github.com/adafruit/Adafruit_CircuitPython_DisplayIO_SH1107 | ||||||
|  |  | ||||||
|   // Turn off display during initialization (0xAE) |   // Turn off display during initialization (0xAE) | ||||||
|   this->command(SSD1306_COMMAND_DISPLAY_OFF); |   this->command(SSD1306_COMMAND_DISPLAY_OFF); | ||||||
|  |  | ||||||
|  |   // If SH1107, use POR defaults (0x50) = divider 1, frequency +0% | ||||||
|  |   if (!this->is_sh1107_()) { | ||||||
|     // Set oscillator frequency to 4'b1000 with no clock division (0xD5) |     // Set oscillator frequency to 4'b1000 with no clock division (0xD5) | ||||||
|     this->command(SSD1306_COMMAND_SET_DISPLAY_CLOCK_DIV); |     this->command(SSD1306_COMMAND_SET_DISPLAY_CLOCK_DIV); | ||||||
|     // Oscillator frequency <= 4'b1000, no clock division |     // Oscillator frequency <= 4'b1000, no clock division | ||||||
|     this->command(0x80); |     this->command(0x80); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   // Enable low power display mode for SSD1305 (0xD8) |   // Enable low power display mode for SSD1305 (0xD8) | ||||||
|   if (this->is_ssd1305_()) { |   if (this->is_ssd1305_()) { | ||||||
| @@ -60,11 +75,26 @@ void SSD1306::setup() { | |||||||
|   this->command(SSD1306_COMMAND_SET_DISPLAY_OFFSET_Y); |   this->command(SSD1306_COMMAND_SET_DISPLAY_OFFSET_Y); | ||||||
|   this->command(0x00 + this->offset_y_); |   this->command(0x00 + this->offset_y_); | ||||||
|  |  | ||||||
|  |   if (this->is_sh1107_()) { | ||||||
|  |     // Set start line at line 0 (0xDC) | ||||||
|  |     this->command(SH1107_COMMAND_SET_START_LINE); | ||||||
|  |     this->command(0x00); | ||||||
|  |   } else { | ||||||
|     // Set start line at line 0 (0x40) |     // Set start line at line 0 (0x40) | ||||||
|     this->command(SSD1306_COMMAND_SET_START_LINE | 0x00); |     this->command(SSD1306_COMMAND_SET_START_LINE | 0x00); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (this->is_ssd1305_()) { | ||||||
|     // SSD1305 does not have charge pump |     // SSD1305 does not have charge pump | ||||||
|   if (!this->is_ssd1305_()) { |   } else if (this->is_sh1107_()) { | ||||||
|  |     // Enable charge pump (0xAD) | ||||||
|  |     this->command(SH1107_COMMAND_CHARGE_PUMP); | ||||||
|  |     if (this->external_vcc_) { | ||||||
|  |       this->command(0x8A); | ||||||
|  |     } else { | ||||||
|  |       this->command(0x8B); | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|     // Enable charge pump (0x8D) |     // Enable charge pump (0x8D) | ||||||
|     this->command(SSD1306_COMMAND_CHARGE_PUMP); |     this->command(SSD1306_COMMAND_CHARGE_PUMP); | ||||||
|     if (this->external_vcc_) { |     if (this->external_vcc_) { | ||||||
| @@ -76,14 +106,17 @@ void SSD1306::setup() { | |||||||
|  |  | ||||||
|   // Set addressing mode to horizontal (0x20) |   // Set addressing mode to horizontal (0x20) | ||||||
|   this->command(SSD1306_COMMAND_MEMORY_MODE); |   this->command(SSD1306_COMMAND_MEMORY_MODE); | ||||||
|  |   if (!this->is_sh1107_()) { | ||||||
|  |     // SH1107 memory mode is a 1 byte command | ||||||
|     this->command(0x00); |     this->command(0x00); | ||||||
|  |   } | ||||||
|   // X flip mode (0xA0, 0xA1) |   // X flip mode (0xA0, 0xA1) | ||||||
|   this->command(SSD1306_COMMAND_SEGRE_MAP | this->flip_x_); |   this->command(SSD1306_COMMAND_SEGRE_MAP | this->flip_x_); | ||||||
|  |  | ||||||
|   // Y flip mode (0xC0, 0xC8) |   // Y flip mode (0xC0, 0xC8) | ||||||
|   this->command(SSD1306_COMMAND_COM_SCAN_INC | (this->flip_y_ << 3)); |   this->command(SSD1306_COMMAND_COM_SCAN_INC | (this->flip_y_ << 3)); | ||||||
|  |  | ||||||
|  |   if (!this->is_sh1107_()) { | ||||||
|     // Set pin configuration (0xDA) |     // Set pin configuration (0xDA) | ||||||
|     this->command(SSD1306_COMMAND_SET_COM_PINS); |     this->command(SSD1306_COMMAND_SET_COM_PINS); | ||||||
|     switch (this->model_) { |     switch (this->model_) { | ||||||
| @@ -98,12 +131,16 @@ void SSD1306::setup() { | |||||||
|       case SSD1306_MODEL_64_48: |       case SSD1306_MODEL_64_48: | ||||||
|       case SSD1306_MODEL_64_32: |       case SSD1306_MODEL_64_32: | ||||||
|       case SH1106_MODEL_64_48: |       case SH1106_MODEL_64_48: | ||||||
|     case SH1107_MODEL_128_64: |  | ||||||
|       case SSD1305_MODEL_128_32: |       case SSD1305_MODEL_128_32: | ||||||
|       case SSD1305_MODEL_128_64: |       case SSD1305_MODEL_128_64: | ||||||
|       case SSD1306_MODEL_72_40: |       case SSD1306_MODEL_72_40: | ||||||
|         this->command(0x12); |         this->command(0x12); | ||||||
|         break; |         break; | ||||||
|  |       case SH1107_MODEL_128_64: | ||||||
|  |       case SH1107_MODEL_128_128: | ||||||
|  |         // Not used, but prevents build warning | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Pre-charge period (0xD9) |   // Pre-charge period (0xD9) | ||||||
| @@ -118,6 +155,7 @@ void SSD1306::setup() { | |||||||
|   this->command(SSD1306_COMMAND_SET_VCOM_DETECT); |   this->command(SSD1306_COMMAND_SET_VCOM_DETECT); | ||||||
|   switch (this->model_) { |   switch (this->model_) { | ||||||
|     case SH1107_MODEL_128_64: |     case SH1107_MODEL_128_64: | ||||||
|  |     case SH1107_MODEL_128_128: | ||||||
|       this->command(0x35); |       this->command(0x35); | ||||||
|       break; |       break; | ||||||
|     case SSD1306_MODEL_72_40: |     case SSD1306_MODEL_72_40: | ||||||
| @@ -149,7 +187,7 @@ void SSD1306::setup() { | |||||||
|   this->turn_on(); |   this->turn_on(); | ||||||
| } | } | ||||||
| void SSD1306::display() { | void SSD1306::display() { | ||||||
|   if (this->is_sh1106_()) { |   if (this->is_sh1106_() || this->is_sh1107_()) { | ||||||
|     this->write_display_data(); |     this->write_display_data(); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @@ -183,6 +221,7 @@ bool SSD1306::is_sh1106_() const { | |||||||
|   return this->model_ == SH1106_MODEL_96_16 || this->model_ == SH1106_MODEL_128_32 || |   return this->model_ == SH1106_MODEL_96_16 || this->model_ == SH1106_MODEL_128_32 || | ||||||
|          this->model_ == SH1106_MODEL_128_64; |          this->model_ == SH1106_MODEL_128_64; | ||||||
| } | } | ||||||
|  | bool SSD1306::is_sh1107_() const { return this->model_ == SH1107_MODEL_128_64 || this->model_ == SH1107_MODEL_128_128; } | ||||||
| bool SSD1306::is_ssd1305_() const { | bool SSD1306::is_ssd1305_() const { | ||||||
|   return this->model_ == SSD1305_MODEL_128_64 || this->model_ == SSD1305_MODEL_128_64; |   return this->model_ == SSD1305_MODEL_128_64 || this->model_ == SSD1305_MODEL_128_64; | ||||||
| } | } | ||||||
| @@ -224,6 +263,7 @@ void SSD1306::turn_off() { | |||||||
| int SSD1306::get_height_internal() { | int SSD1306::get_height_internal() { | ||||||
|   switch (this->model_) { |   switch (this->model_) { | ||||||
|     case SH1107_MODEL_128_64: |     case SH1107_MODEL_128_64: | ||||||
|  |     case SH1107_MODEL_128_128: | ||||||
|       return 128; |       return 128; | ||||||
|     case SSD1306_MODEL_128_32: |     case SSD1306_MODEL_128_32: | ||||||
|     case SSD1306_MODEL_64_32: |     case SSD1306_MODEL_64_32: | ||||||
| @@ -254,6 +294,7 @@ int SSD1306::get_width_internal() { | |||||||
|     case SH1106_MODEL_128_64: |     case SH1106_MODEL_128_64: | ||||||
|     case SSD1305_MODEL_128_32: |     case SSD1305_MODEL_128_32: | ||||||
|     case SSD1305_MODEL_128_64: |     case SSD1305_MODEL_128_64: | ||||||
|  |     case SH1107_MODEL_128_128: | ||||||
|       return 128; |       return 128; | ||||||
|     case SSD1306_MODEL_96_16: |     case SSD1306_MODEL_96_16: | ||||||
|     case SH1106_MODEL_96_16: |     case SH1106_MODEL_96_16: | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ enum SSD1306Model { | |||||||
|   SH1106_MODEL_96_16, |   SH1106_MODEL_96_16, | ||||||
|   SH1106_MODEL_64_48, |   SH1106_MODEL_64_48, | ||||||
|   SH1107_MODEL_128_64, |   SH1107_MODEL_128_64, | ||||||
|  |   SH1107_MODEL_128_128, | ||||||
|   SSD1305_MODEL_128_32, |   SSD1305_MODEL_128_32, | ||||||
|   SSD1305_MODEL_128_64, |   SSD1305_MODEL_128_64, | ||||||
| }; | }; | ||||||
| @@ -58,6 +59,7 @@ class SSD1306 : public PollingComponent, public display::DisplayBuffer { | |||||||
|   void init_reset_(); |   void init_reset_(); | ||||||
|  |  | ||||||
|   bool is_sh1106_() const; |   bool is_sh1106_() const; | ||||||
|  |   bool is_sh1107_() const; | ||||||
|   bool is_ssd1305_() const; |   bool is_ssd1305_() const; | ||||||
|  |  | ||||||
|   void draw_absolute_pixel_internal(int x, int y, Color color) override; |   void draw_absolute_pixel_internal(int x, int y, Color color) override; | ||||||
|   | |||||||
| @@ -38,13 +38,19 @@ void I2CSSD1306::dump_config() { | |||||||
| } | } | ||||||
| void I2CSSD1306::command(uint8_t value) { this->write_byte(0x00, value); } | void I2CSSD1306::command(uint8_t value) { this->write_byte(0x00, value); } | ||||||
| void HOT I2CSSD1306::write_display_data() { | void HOT I2CSSD1306::write_display_data() { | ||||||
|   if (this->is_sh1106_()) { |   if (this->is_sh1106_() || this->is_sh1107_()) { | ||||||
|     uint32_t i = 0; |     uint32_t i = 0; | ||||||
|     for (uint8_t page = 0; page < (uint8_t) this->get_height_internal() / 8; page++) { |     for (uint8_t page = 0; page < (uint8_t) this->get_height_internal() / 8; page++) { | ||||||
|       this->command(0xB0 + page);  // row |       this->command(0xB0 + page);  // row | ||||||
|       this->command(0x02);         // lower column |       if (this->is_sh1106_()) { | ||||||
|  |         this->command(0x02);  // lower column - 0x02 is historical SH1106 value | ||||||
|  |       } else { | ||||||
|  |         // Other SH1107 drivers use 0x00 | ||||||
|  |         // Column values dont change and it seems they can be set only once, | ||||||
|  |         // but we follow SH1106 implementation and resend them | ||||||
|  |         this->command(0x00); | ||||||
|  |       } | ||||||
|       this->command(0x10);  // higher column |       this->command(0x10);  // higher column | ||||||
|  |  | ||||||
|       for (uint8_t x = 0; x < (uint8_t) this->get_width_internal() / 16; x++) { |       for (uint8_t x = 0; x < (uint8_t) this->get_width_internal() / 16; x++) { | ||||||
|         uint8_t data[16]; |         uint8_t data[16]; | ||||||
|         for (uint8_t &j : data) |         for (uint8_t &j : data) | ||||||
|   | |||||||
| @@ -36,10 +36,14 @@ void SPISSD1306::command(uint8_t value) { | |||||||
|   this->disable(); |   this->disable(); | ||||||
| } | } | ||||||
| void HOT SPISSD1306::write_display_data() { | void HOT SPISSD1306::write_display_data() { | ||||||
|   if (this->is_sh1106_()) { |   if (this->is_sh1106_() || this->is_sh1107_()) { | ||||||
|     for (uint8_t y = 0; y < (uint8_t) this->get_height_internal() / 8; y++) { |     for (uint8_t y = 0; y < (uint8_t) this->get_height_internal() / 8; y++) { | ||||||
|       this->command(0xB0 + y); |       this->command(0xB0 + y); | ||||||
|  |       if (this->is_sh1106_()) { | ||||||
|         this->command(0x02); |         this->command(0x02); | ||||||
|  |       } else { | ||||||
|  |         this->command(0x00); | ||||||
|  |       } | ||||||
|       this->command(0x10); |       this->command(0x10); | ||||||
|       this->dc_pin_->digital_write(true); |       this->dc_pin_->digital_write(true); | ||||||
|       for (uint8_t x = 0; x < (uint8_t) this->get_width_internal(); x++) { |       for (uint8_t x = 0; x < (uint8_t) this->get_width_internal(); x++) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user