Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions display_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ Context *display_create_port(GlobalContext *global, term opts)
ctx = ili948x_display_create_port(global, opts);
} else if (!strcmp(compat_string, "solomon-systech,ssd1306")) {
ctx = ssd1306_display_create_port(global, opts);
} else if (!strcmp(compat_string, "solomon-systech,ssd1315")) {
ctx = ssd1306_display_create_port(global, opts);
} else if (!strcmp(compat_string, "sino-wealth,sh1106")) {
ctx = ssd1306_display_create_port(global, opts);
} else if (!strcmp(compat_string, "sitronix,st7789")
Expand Down
93 changes: 78 additions & 15 deletions ssd1306_display_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,18 @@
#define CMD_SET_COM_SCAN_MODE 0xC8
#define CMD_SET_CHARGE_PUMP 0x8D

typedef enum
{
DISPLAY_SSD1306,
DISPLAY_SSD1315,
DISPLAY_SH1106,
} display_type_t;

// TODO: let's change name, since also non SPI display are supported now
struct SPI
{
term i2c_host;
bool is_sh1106;
display_type_t type;
Context *ctx;
};

Expand Down Expand Up @@ -116,8 +123,9 @@ static void do_update(Context *ctx, term display_list)

i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true);
i2c_master_write_byte(cmd, 0xB0 | ypos / 8, true);
if (spi->is_sh1106) {
// set the column, otherwise the starting column will be somewhere in the middle

if (spi->type == DISPLAY_SH1106 || spi->type == DISPLAY_SSD1315) {
// SSD1315 and SH1106 require explicit column address reset
i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true);
i2c_master_write_byte(cmd, 0x00, true);
i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true);
Expand All @@ -126,7 +134,7 @@ static void do_update(Context *ctx, term display_list)
i2c_master_write_byte(cmd, CTRL_BYTE_DATA_STREAM, true);


if (spi->is_sh1106) {
if (spi->type == DISPLAY_SH1106) {
// add 2 empty pages on sh1106 since it can have up to 132 pixels
// and 128 pixel screen starts at (2, 0)
i2c_master_write_byte(cmd, 0, true);
Expand All @@ -138,13 +146,13 @@ static void do_update(Context *ctx, term display_list)
}

// no need to send the last 2 page, the position will be set on next line again
// if (spi->is_sh1106) {
// if (spi->type == DISPLAY_SH1106) {
// i2c_master_write_byte(cmd, 0, true);
// i2c_master_write_byte(cmd, 0, true);
// }

i2c_master_stop(cmd);
i2c_master_cmd_begin(i2c_num, cmd, 10 / portTICK_PERIOD_MS);
i2c_master_cmd_begin(i2c_num, cmd, 100 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);

memset(buf, 0, memsize);
Expand Down Expand Up @@ -176,17 +184,25 @@ static void display_init(Context *ctx, term opts)
ctx->platform_data = spi;

spi->ctx = ctx;
spi->type = DISPLAY_SSD1306; // Default to SSD1306

term compat_value_term = interop_kv_get_value_default(opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global);
int str_ok;
char *compat_string = interop_term_to_string(compat_value_term, &str_ok);
if (str_ok && compat_string) {
spi->is_sh1106 = !strcmp(compat_string, "sino-wealth,sh1106");
free(compat_string);
} else {

if (!(str_ok && compat_string)) {
ESP_LOGE(TAG, "No Compatible Device Found.");
return;
}

if (!strcmp(compat_string, "sino-wealth,sh1106")) {
spi->type = DISPLAY_SH1106;
} else if (!strcmp(compat_string, "solomon-systech,ssd1315")) {
spi->type = DISPLAY_SSD1315;
}

free(compat_string);

int reset_gpio;
if (!display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &reset_gpio, glb)) {
ESP_LOGI(TAG, "Reset GPIO not configured.");
Expand All @@ -209,11 +225,58 @@ static void display_init(Context *ctx, term opts)
i2c_master_write_byte(cmd, (I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, CTRL_BYTE_CMD_STREAM, true);

i2c_master_write_byte(cmd, CMD_SET_CHARGE_PUMP, true);
i2c_master_write_byte(cmd, 0x14, true);
if (spi->type == DISPLAY_SSD1315) {
/*
* Init sequence derived from u8g2 project (BSD-2-Clause).
* Source: https://github.com/olikraus/u8g2
*
* These values are standard hardware initialization commands
* defined by the Solomon Systech SSD1315 datasheet.
*/

i2c_master_write_byte(cmd, 0xAE, true); // Display OFF

i2c_master_write_byte(cmd, 0xD5, true); // Set Display Clock Divide Ratio / Oscillator Frequency
i2c_master_write_byte(cmd, 0x80, true); // 0x80 is standard/stable

i2c_master_write_byte(cmd, 0xA8, true); // Set Multiplex Ratio
i2c_master_write_byte(cmd, 0x3F, true); // 64 MUX

i2c_master_write_byte(cmd, 0xD3, true); // Set Display Offset
i2c_master_write_byte(cmd, 0x00, true); // No offset

i2c_master_write_byte(cmd, CMD_SET_SEGMENT_REMAP, true);
i2c_master_write_byte(cmd, CMD_SET_COM_SCAN_MODE, true);
i2c_master_write_byte(cmd, 0x40, true); // Set Display Start Line to 0

i2c_master_write_byte(cmd, 0x8D, true); // Set Charge Pump
i2c_master_write_byte(cmd, 0x14, true); // Enable Charge Pump

i2c_master_write_byte(cmd, 0xA1, true); // Set Segment Remap
i2c_master_write_byte(cmd, 0xC8, true); // Set COM Scan Mode

i2c_master_write_byte(cmd, 0xDA, true); // Set COM Pins Hardware Configuration
i2c_master_write_byte(cmd, 0x12, true); // Alternative COM pin config

i2c_master_write_byte(cmd, 0x81, true); // Set Contrast Control
i2c_master_write_byte(cmd, 0xCF, true); // Use High Contrast (0xCF) as per u8x8

i2c_master_write_byte(cmd, 0xD9, true); // Set Pre-charge Period
i2c_master_write_byte(cmd, 0xF1, true); // 0xF1 is required for stable 400kHz operation

i2c_master_write_byte(cmd, 0xDB, true); // Set VCOMH Deselect Level
i2c_master_write_byte(cmd, 0x40, true); // 0x40 (approx 0.77x VCC)

i2c_master_write_byte(cmd, 0xA4, true); // Resume to RAM content display
i2c_master_write_byte(cmd, 0xA6, true); // Normal Display (not inverted)

i2c_master_write_byte(cmd, 0xAD, true); // Internal IREF Setting
i2c_master_write_byte(cmd, 0x10, true); // Internal Iref
} else {
i2c_master_write_byte(cmd, CMD_SET_CHARGE_PUMP, true);
i2c_master_write_byte(cmd, 0x14, true);

i2c_master_write_byte(cmd, CMD_SET_SEGMENT_REMAP, true);
i2c_master_write_byte(cmd, CMD_SET_COM_SCAN_MODE, true);
}

if (invert) {
i2c_master_write_byte(cmd, CMD_DISPLAY_INVERTED, true);
Expand All @@ -224,7 +287,7 @@ static void display_init(Context *ctx, term opts)

esp_err_t res = i2c_master_cmd_begin(i2c_num, cmd, 50 / portTICK_PERIOD_MS);
if (res != ESP_OK) {
ESP_LOGE(TAG, "ssd1306 OLED configuration failed. error: 0x%.2X", res);
ESP_LOGE(TAG, "ssd1306/ssd1315 OLED configuration failed. error: 0x%.2X", res);
} else {
xTaskCreate(process_messages, "display", 10000, spi, 1, NULL);
}
Expand Down