Skip to content

Commit 92dc9ee

Browse files
clsourceRekkice
authored andcommitted
Add support for ssd1315 displays
This PR adds supporf for ssd1315 displays. ssd1315 is mostly compatible with ssd1306 drivers, but it has some small differences, such as requiring a special initialization sequence and needing an explicit column address reset when updating. It also includes a small optimization in do_update, that fixed this error i was getting: `E (1225) SSD1306: I2C write failed for display update: 0x107` [(a timeout)](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/error-codes.html) The initialization sequence is based on the one used in [u8g2](https://github.com/olikraus/u8g2/blob/dbd338af5d57b219e48ea1ffdb97c4153668676c/csrc/u8x8_d_ssd1315_128x64_noname.c). Tested on an ESP32.
1 parent 10fa0b2 commit 92dc9ee

2 files changed

Lines changed: 85 additions & 13 deletions

File tree

display_driver.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ Context *display_create_port(GlobalContext *global, term opts)
6767
ctx = ili948x_display_create_port(global, opts);
6868
} else if (!strcmp(compat_string, "solomon-systech,ssd1306")) {
6969
ctx = ssd1306_display_create_port(global, opts);
70+
} else if (!strcmp(compat_string, "solomon-systech,ssd1315")) {
71+
ctx = ssd1306_display_create_port(global, opts);
7072
} else if (!strcmp(compat_string, "sino-wealth,sh1106")) {
7173
ctx = ssd1306_display_create_port(global, opts);
7274
} else if (!strcmp(compat_string, "sitronix,st7789")

ssd1306_display_driver.c

Lines changed: 83 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#define CHAR_WIDTH 8
4343

4444
#define I2C_ADDRESS 0x3C
45+
#define I2C_CHUNK_SIZE 64
4546

4647
#define CTRL_BYTE_CMD_SINGLE 0x80
4748
#define CTRL_BYTE_CMD_STREAM 0x00
@@ -53,11 +54,18 @@
5354
#define CMD_SET_COM_SCAN_MODE 0xC8
5455
#define CMD_SET_CHARGE_PUMP 0x8D
5556

57+
typedef enum
58+
{
59+
DISPLAY_SSD1306,
60+
DISPLAY_SSD1315,
61+
DISPLAY_SH1106,
62+
} display_type_t;
63+
5664
// TODO: let's change name, since also non SPI display are supported now
5765
struct SPI
5866
{
5967
term i2c_host;
60-
bool is_sh1106;
68+
display_type_t type;
6169
Context *ctx;
6270
};
6371

@@ -116,8 +124,9 @@ static void do_update(Context *ctx, term display_list)
116124

117125
i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true);
118126
i2c_master_write_byte(cmd, 0xB0 | ypos / 8, true);
119-
if (spi->is_sh1106) {
120-
// set the column, otherwise the starting column will be somewhere in the middle
127+
128+
if (spi->type == DISPLAY_SH1106 || spi->type == DISPLAY_SSD1315) {
129+
// SSD1315 and SH1106 require explicit column address reset
121130
i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true);
122131
i2c_master_write_byte(cmd, 0x00, true);
123132
i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true);
@@ -126,19 +135,26 @@ static void do_update(Context *ctx, term display_list)
126135
i2c_master_write_byte(cmd, CTRL_BYTE_DATA_STREAM, true);
127136

128137

129-
if (spi->is_sh1106) {
138+
if (spi->type == DISPLAY_SH1106) {
130139
// add 2 empty pages on sh1106 since it can have up to 132 pixels
131140
// and 128 pixel screen starts at (2, 0)
132141
i2c_master_write_byte(cmd, 0, true);
133142
i2c_master_write_byte(cmd, 0, true);
134143
}
135144

136-
for (uint8_t j = 0; j < DISPLAY_WIDTH; j++) {
137-
i2c_master_write_byte(cmd, out_buf[j], true);
145+
// send pages in chunks to avoid timeout error (0x107)
146+
int offset = 0;
147+
while (offset < DISPLAY_WIDTH) {
148+
int chunk_len = DISPLAY_WIDTH - offset;
149+
if (chunk_len > I2C_CHUNK_SIZE) {
150+
chunk_len = I2C_CHUNK_SIZE;
151+
}
152+
i2c_master_write(cmd, out_buf + offset, chunk_len, true);
153+
offset += chunk_len;
138154
}
139155

140156
// no need to send the last 2 page, the position will be set on next line again
141-
// if (spi->is_sh1106) {
157+
// if (spi->type == DISPLAY_SH1106) {
142158
// i2c_master_write_byte(cmd, 0, true);
143159
// i2c_master_write_byte(cmd, 0, true);
144160
// }
@@ -176,12 +192,17 @@ static void display_init(Context *ctx, term opts)
176192
ctx->platform_data = spi;
177193

178194
spi->ctx = ctx;
195+
spi->type = DISPLAY_SSD1306; // Default to SSD1306
179196

180197
term compat_value_term = interop_kv_get_value_default(opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global);
181198
int str_ok;
182199
char *compat_string = interop_term_to_string(compat_value_term, &str_ok);
183200
if (str_ok && compat_string) {
184-
spi->is_sh1106 = !strcmp(compat_string, "sino-wealth,sh1106");
201+
if (!strcmp(compat_string, "sino-wealth,sh1106")) {
202+
spi->type = DISPLAY_SH1106;
203+
} else if (!strcmp(compat_string, "solomon-systech,ssd1315")) {
204+
spi->type = DISPLAY_SSD1315;
205+
}
185206
free(compat_string);
186207
} else {
187208
return;
@@ -209,11 +230,60 @@ static void display_init(Context *ctx, term opts)
209230
i2c_master_write_byte(cmd, (I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true);
210231
i2c_master_write_byte(cmd, CTRL_BYTE_CMD_STREAM, true);
211232

212-
i2c_master_write_byte(cmd, CMD_SET_CHARGE_PUMP, true);
213-
i2c_master_write_byte(cmd, 0x14, true);
233+
if (spi->type == DISPLAY_SSD1315) {
234+
/*
235+
* Init sequence derived from u8g2 project (BSD-2-Clause).
236+
* Source: https://github.com/olikraus/u8g2
237+
*
238+
* These values are standard hardware initialization commands
239+
* defined by the Solomon Systech SSD1315 datasheet.
240+
*/
241+
242+
i2c_master_write_byte(cmd, 0xAE, true); // Display OFF
243+
244+
i2c_master_write_byte(cmd, 0xD5, true); // Set Display Clock Divide Ratio / Oscillator Frequency
245+
i2c_master_write_byte(cmd, 0x80, true); // 0x80 is standard/stable
246+
247+
i2c_master_write_byte(cmd, 0xA8, true); // Set Multiplex Ratio
248+
i2c_master_write_byte(cmd, 0x3F, true); // 64 MUX
249+
250+
i2c_master_write_byte(cmd, 0xD3, true); // Set Display Offset
251+
i2c_master_write_byte(cmd, 0x00, true); // No offset
252+
253+
i2c_master_write_byte(cmd, 0x40, true); // Set Display Start Line to 0
254+
255+
i2c_master_write_byte(cmd, 0x8D, true); // Set Charge Pump
256+
i2c_master_write_byte(cmd, 0x14, true); // Enable Charge Pump
257+
258+
i2c_master_write_byte(cmd, 0xA1, true); // Set Segment Remap
259+
i2c_master_write_byte(cmd, 0xC8, true); // Set COM Scan Mode
260+
261+
i2c_master_write_byte(cmd, 0xDA, true); // Set COM Pins Hardware Configuration
262+
i2c_master_write_byte(cmd, 0x12, true); // Alternative COM pin config
263+
264+
i2c_master_write_byte(cmd, 0x81, true); // Set Contrast Control
265+
i2c_master_write_byte(cmd, 0xCF, true); // Use High Contrast (0xCF) as per u8x8
266+
267+
i2c_master_write_byte(cmd, 0xD9, true); // Set Pre-charge Period
268+
i2c_master_write_byte(cmd, 0xF1, true); // 0xF1 is required for stable 400kHz operation
269+
270+
i2c_master_write_byte(cmd, 0xDB, true); // Set VCOMH Deselect Level
271+
i2c_master_write_byte(cmd, 0x40, true); // 0x40 (approx 0.77x VCC)
272+
273+
i2c_master_write_byte(cmd, 0xA4, true); // Resume to RAM content display
274+
i2c_master_write_byte(cmd, 0xA6, true); // Normal Display (not inverted)
275+
276+
i2c_master_write_byte(cmd, 0xAD, true); // Internal IREF Setting
277+
i2c_master_write_byte(cmd, 0x10, true); // Internal Iref
278+
279+
i2c_master_write_byte(cmd, 0xAF, true); // Display ON
280+
} else {
281+
i2c_master_write_byte(cmd, CMD_SET_CHARGE_PUMP, true);
282+
i2c_master_write_byte(cmd, 0x14, true);
214283

215-
i2c_master_write_byte(cmd, CMD_SET_SEGMENT_REMAP, true);
216-
i2c_master_write_byte(cmd, CMD_SET_COM_SCAN_MODE, true);
284+
i2c_master_write_byte(cmd, CMD_SET_SEGMENT_REMAP, true);
285+
i2c_master_write_byte(cmd, CMD_SET_COM_SCAN_MODE, true);
286+
}
217287

218288
if (invert) {
219289
i2c_master_write_byte(cmd, CMD_DISPLAY_INVERTED, true);
@@ -224,7 +294,7 @@ static void display_init(Context *ctx, term opts)
224294

225295
esp_err_t res = i2c_master_cmd_begin(i2c_num, cmd, 50 / portTICK_PERIOD_MS);
226296
if (res != ESP_OK) {
227-
ESP_LOGE(TAG, "ssd1306 OLED configuration failed. error: 0x%.2X", res);
297+
ESP_LOGE(TAG, "ssd1306/ssd1315 OLED configuration failed. error: 0x%.2X", res);
228298
} else {
229299
xTaskCreate(process_messages, "display", 10000, spi, 1, NULL);
230300
}

0 commit comments

Comments
 (0)