Add proper support for SH1107 to SSD1306 component (#5166)

This commit is contained in:
Nikita Kuklev 2023-11-16 02:06:03 -06:00 committed by GitHub
parent 754bd5b7be
commit fefdb80fdc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 95 additions and 39 deletions

View file

@ -33,6 +33,7 @@ MODELS = {
"SH1106_96X16": SSD1306Model.SH1106_MODEL_96_16,
"SH1106_64X48": SSD1306Model.SH1106_MODEL_64_48,
"SH1107_128X64": SSD1306Model.SH1107_MODEL_128_64,
"SH1107_128X128": SSD1306Model.SH1107_MODEL_128_128,
"SSD1305_128X32": SSD1306Model.SSD1305_MODEL_128_32,
"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_FLIP_X, 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),
cv.Optional(CONF_OFFSET_Y, default=0): cv.int_range(min=-32, max=32),
# Offsets determine shifts of memory location to LCD rows/columns,
# 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,
}
).extend(cv.polling_component_schema("1s"))

View file

@ -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_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() {
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)
this->command(SSD1306_COMMAND_DISPLAY_OFF);
// Set oscillator frequency to 4'b1000 with no clock division (0xD5)
this->command(SSD1306_COMMAND_SET_DISPLAY_CLOCK_DIV);
// Oscillator frequency <= 4'b1000, no clock division
this->command(0x80);
// 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)
this->command(SSD1306_COMMAND_SET_DISPLAY_CLOCK_DIV);
// Oscillator frequency <= 4'b1000, no clock division
this->command(0x80);
}
// Enable low power display mode for SSD1305 (0xD8)
if (this->is_ssd1305_()) {
@ -60,11 +75,26 @@ void SSD1306::setup() {
this->command(SSD1306_COMMAND_SET_DISPLAY_OFFSET_Y);
this->command(0x00 + this->offset_y_);
// Set start line at line 0 (0x40)
this->command(SSD1306_COMMAND_SET_START_LINE | 0x00);
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)
this->command(SSD1306_COMMAND_SET_START_LINE | 0x00);
}
// SSD1305 does not have charge pump
if (!this->is_ssd1305_()) {
if (this->is_ssd1305_()) {
// SSD1305 does not have charge pump
} 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)
this->command(SSD1306_COMMAND_CHARGE_PUMP);
if (this->external_vcc_) {
@ -76,34 +106,41 @@ void SSD1306::setup() {
// Set addressing mode to horizontal (0x20)
this->command(SSD1306_COMMAND_MEMORY_MODE);
this->command(0x00);
if (!this->is_sh1107_()) {
// SH1107 memory mode is a 1 byte command
this->command(0x00);
}
// X flip mode (0xA0, 0xA1)
this->command(SSD1306_COMMAND_SEGRE_MAP | this->flip_x_);
// Y flip mode (0xC0, 0xC8)
this->command(SSD1306_COMMAND_COM_SCAN_INC | (this->flip_y_ << 3));
// Set pin configuration (0xDA)
this->command(SSD1306_COMMAND_SET_COM_PINS);
switch (this->model_) {
case SSD1306_MODEL_128_32:
case SH1106_MODEL_128_32:
case SSD1306_MODEL_96_16:
case SH1106_MODEL_96_16:
this->command(0x02);
break;
case SSD1306_MODEL_128_64:
case SH1106_MODEL_128_64:
case SSD1306_MODEL_64_48:
case SSD1306_MODEL_64_32:
case SH1106_MODEL_64_48:
case SH1107_MODEL_128_64:
case SSD1305_MODEL_128_32:
case SSD1305_MODEL_128_64:
case SSD1306_MODEL_72_40:
this->command(0x12);
break;
if (!this->is_sh1107_()) {
// Set pin configuration (0xDA)
this->command(SSD1306_COMMAND_SET_COM_PINS);
switch (this->model_) {
case SSD1306_MODEL_128_32:
case SH1106_MODEL_128_32:
case SSD1306_MODEL_96_16:
case SH1106_MODEL_96_16:
this->command(0x02);
break;
case SSD1306_MODEL_128_64:
case SH1106_MODEL_128_64:
case SSD1306_MODEL_64_48:
case SSD1306_MODEL_64_32:
case SH1106_MODEL_64_48:
case SSD1305_MODEL_128_32:
case SSD1305_MODEL_128_64:
case SSD1306_MODEL_72_40:
this->command(0x12);
break;
case SH1107_MODEL_128_64:
case SH1107_MODEL_128_128:
// Not used, but prevents build warning
break;
}
}
// Pre-charge period (0xD9)
@ -118,6 +155,7 @@ void SSD1306::setup() {
this->command(SSD1306_COMMAND_SET_VCOM_DETECT);
switch (this->model_) {
case SH1107_MODEL_128_64:
case SH1107_MODEL_128_128:
this->command(0x35);
break;
case SSD1306_MODEL_72_40:
@ -149,7 +187,7 @@ void SSD1306::setup() {
this->turn_on();
}
void SSD1306::display() {
if (this->is_sh1106_()) {
if (this->is_sh1106_() || this->is_sh1107_()) {
this->write_display_data();
return;
}
@ -183,6 +221,7 @@ bool SSD1306::is_sh1106_() const {
return this->model_ == SH1106_MODEL_96_16 || this->model_ == SH1106_MODEL_128_32 ||
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 {
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() {
switch (this->model_) {
case SH1107_MODEL_128_64:
case SH1107_MODEL_128_128:
return 128;
case SSD1306_MODEL_128_32:
case SSD1306_MODEL_64_32:
@ -254,6 +294,7 @@ int SSD1306::get_width_internal() {
case SH1106_MODEL_128_64:
case SSD1305_MODEL_128_32:
case SSD1305_MODEL_128_64:
case SH1107_MODEL_128_128:
return 128;
case SSD1306_MODEL_96_16:
case SH1106_MODEL_96_16:

View file

@ -19,6 +19,7 @@ enum SSD1306Model {
SH1106_MODEL_96_16,
SH1106_MODEL_64_48,
SH1107_MODEL_128_64,
SH1107_MODEL_128_128,
SSD1305_MODEL_128_32,
SSD1305_MODEL_128_64,
};
@ -58,6 +59,7 @@ class SSD1306 : public PollingComponent, public display::DisplayBuffer {
void init_reset_();
bool is_sh1106_() const;
bool is_sh1107_() const;
bool is_ssd1305_() const;
void draw_absolute_pixel_internal(int x, int y, Color color) override;

View file

@ -38,13 +38,19 @@ void I2CSSD1306::dump_config() {
}
void I2CSSD1306::command(uint8_t value) { this->write_byte(0x00, value); }
void HOT I2CSSD1306::write_display_data() {
if (this->is_sh1106_()) {
if (this->is_sh1106_() || this->is_sh1107_()) {
uint32_t i = 0;
for (uint8_t page = 0; page < (uint8_t) this->get_height_internal() / 8; page++) {
this->command(0xB0 + page); // row
this->command(0x02); // lower column
this->command(0x10); // higher 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
for (uint8_t x = 0; x < (uint8_t) this->get_width_internal() / 16; x++) {
uint8_t data[16];
for (uint8_t &j : data)

View file

@ -36,10 +36,14 @@ void SPISSD1306::command(uint8_t value) {
this->disable();
}
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++) {
this->command(0xB0 + y);
this->command(0x02);
if (this->is_sh1106_()) {
this->command(0x02);
} else {
this->command(0x00);
}
this->command(0x10);
this->dc_pin_->digital_write(true);
for (uint8_t x = 0; x < (uint8_t) this->get_width_internal(); x++) {