Skip to content

Commit f3f4721

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. I also increased the timeout window from 10 to 100, to fix 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 f3f4721

2 files changed

Lines changed: 77 additions & 12 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: 75 additions & 12 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,7 +135,7 @@ 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);
@@ -138,13 +147,13 @@ static void do_update(Context *ctx, term display_list)
138147
}
139148

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

146155
i2c_master_stop(cmd);
147-
i2c_master_cmd_begin(i2c_num, cmd, 10 / portTICK_PERIOD_MS);
156+
i2c_master_cmd_begin(i2c_num, cmd, 100 / portTICK_PERIOD_MS);
148157
i2c_cmd_link_delete(cmd);
149158

150159
memset(buf, 0, memsize);
@@ -176,12 +185,17 @@ static void display_init(Context *ctx, term opts)
176185
ctx->platform_data = spi;
177186

178187
spi->ctx = ctx;
188+
spi->type = DISPLAY_SSD1306; // Default to SSD1306
179189

180190
term compat_value_term = interop_kv_get_value_default(opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global);
181191
int str_ok;
182192
char *compat_string = interop_term_to_string(compat_value_term, &str_ok);
183193
if (str_ok && compat_string) {
184-
spi->is_sh1106 = !strcmp(compat_string, "sino-wealth,sh1106");
194+
if (!strcmp(compat_string, "sino-wealth,sh1106")) {
195+
spi->type = DISPLAY_SH1106;
196+
} else if (!strcmp(compat_string, "solomon-systech,ssd1315")) {
197+
spi->type = DISPLAY_SSD1315;
198+
}
185199
free(compat_string);
186200
} else {
187201
return;
@@ -209,11 +223,60 @@ static void display_init(Context *ctx, term opts)
209223
i2c_master_write_byte(cmd, (I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true);
210224
i2c_master_write_byte(cmd, CTRL_BYTE_CMD_STREAM, true);
211225

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

215-
i2c_master_write_byte(cmd, CMD_SET_SEGMENT_REMAP, true);
216-
i2c_master_write_byte(cmd, CMD_SET_COM_SCAN_MODE, true);
277+
i2c_master_write_byte(cmd, CMD_SET_SEGMENT_REMAP, true);
278+
i2c_master_write_byte(cmd, CMD_SET_COM_SCAN_MODE, true);
279+
}
217280

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

225288
esp_err_t res = i2c_master_cmd_begin(i2c_num, cmd, 50 / portTICK_PERIOD_MS);
226289
if (res != ESP_OK) {
227-
ESP_LOGE(TAG, "ssd1306 OLED configuration failed. error: 0x%.2X", res);
290+
ESP_LOGE(TAG, "ssd1306/ssd1315 OLED configuration failed. error: 0x%.2X", res);
228291
} else {
229292
xTaskCreate(process_messages, "display", 10000, spi, 1, NULL);
230293
}

0 commit comments

Comments
 (0)