Skip to content

Commit d15bdb5

Browse files
committed
Merge pull request #11 from ElixirCL
Add support for ssd1315 displays
2 parents a7a9c3a + b937a15 commit d15bdb5

2 files changed

Lines changed: 80 additions & 15 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: 78 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,18 @@
5353
#define CMD_SET_COM_SCAN_MODE 0xC8
5454
#define CMD_SET_CHARGE_PUMP 0x8D
5555

56+
typedef enum
57+
{
58+
DISPLAY_SSD1306,
59+
DISPLAY_SSD1315,
60+
DISPLAY_SH1106,
61+
} display_type_t;
62+
5663
// TODO: let's change name, since also non SPI display are supported now
5764
struct SPI
5865
{
5966
term i2c_host;
60-
bool is_sh1106;
67+
display_type_t type;
6168
Context *ctx;
6269
};
6370

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

117124
i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true);
118125
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
126+
127+
if (spi->type == DISPLAY_SH1106 || spi->type == DISPLAY_SSD1315) {
128+
// SSD1315 and SH1106 require explicit column address reset
121129
i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true);
122130
i2c_master_write_byte(cmd, 0x00, true);
123131
i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true);
@@ -126,7 +134,7 @@ static void do_update(Context *ctx, term display_list)
126134
i2c_master_write_byte(cmd, CTRL_BYTE_DATA_STREAM, true);
127135

128136

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

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

146154
i2c_master_stop(cmd);
147-
i2c_master_cmd_begin(i2c_num, cmd, 10 / portTICK_PERIOD_MS);
155+
i2c_master_cmd_begin(i2c_num, cmd, 100 / portTICK_PERIOD_MS);
148156
i2c_cmd_link_delete(cmd);
149157

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

178186
spi->ctx = ctx;
187+
spi->type = DISPLAY_SSD1306; // Default to SSD1306
179188

180189
term compat_value_term = interop_kv_get_value_default(opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global);
181190
int str_ok;
182191
char *compat_string = interop_term_to_string(compat_value_term, &str_ok);
183-
if (str_ok && compat_string) {
184-
spi->is_sh1106 = !strcmp(compat_string, "sino-wealth,sh1106");
185-
free(compat_string);
186-
} else {
192+
193+
if (!(str_ok && compat_string)) {
194+
ESP_LOGE(TAG, "No Compatible Device Found.");
187195
return;
188196
}
189197

198+
if (!strcmp(compat_string, "sino-wealth,sh1106")) {
199+
spi->type = DISPLAY_SH1106;
200+
} else if (!strcmp(compat_string, "solomon-systech,ssd1315")) {
201+
spi->type = DISPLAY_SSD1315;
202+
}
203+
204+
free(compat_string);
205+
190206
int reset_gpio;
191207
if (!display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &reset_gpio, glb)) {
192208
ESP_LOGI(TAG, "Reset GPIO not configured.");
@@ -209,11 +225,58 @@ static void display_init(Context *ctx, term opts)
209225
i2c_master_write_byte(cmd, (I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true);
210226
i2c_master_write_byte(cmd, CTRL_BYTE_CMD_STREAM, true);
211227

212-
i2c_master_write_byte(cmd, CMD_SET_CHARGE_PUMP, true);
213-
i2c_master_write_byte(cmd, 0x14, true);
228+
if (spi->type == DISPLAY_SSD1315) {
229+
/*
230+
* Init sequence derived from u8g2 project (BSD-2-Clause).
231+
* Source: https://github.com/olikraus/u8g2
232+
*
233+
* These values are standard hardware initialization commands
234+
* defined by the Solomon Systech SSD1315 datasheet.
235+
*/
236+
237+
i2c_master_write_byte(cmd, 0xAE, true); // Display OFF
238+
239+
i2c_master_write_byte(cmd, 0xD5, true); // Set Display Clock Divide Ratio / Oscillator Frequency
240+
i2c_master_write_byte(cmd, 0x80, true); // 0x80 is standard/stable
241+
242+
i2c_master_write_byte(cmd, 0xA8, true); // Set Multiplex Ratio
243+
i2c_master_write_byte(cmd, 0x3F, true); // 64 MUX
244+
245+
i2c_master_write_byte(cmd, 0xD3, true); // Set Display Offset
246+
i2c_master_write_byte(cmd, 0x00, true); // No offset
214247

215-
i2c_master_write_byte(cmd, CMD_SET_SEGMENT_REMAP, true);
216-
i2c_master_write_byte(cmd, CMD_SET_COM_SCAN_MODE, true);
248+
i2c_master_write_byte(cmd, 0x40, true); // Set Display Start Line to 0
249+
250+
i2c_master_write_byte(cmd, 0x8D, true); // Set Charge Pump
251+
i2c_master_write_byte(cmd, 0x14, true); // Enable Charge Pump
252+
253+
i2c_master_write_byte(cmd, 0xA1, true); // Set Segment Remap
254+
i2c_master_write_byte(cmd, 0xC8, true); // Set COM Scan Mode
255+
256+
i2c_master_write_byte(cmd, 0xDA, true); // Set COM Pins Hardware Configuration
257+
i2c_master_write_byte(cmd, 0x12, true); // Alternative COM pin config
258+
259+
i2c_master_write_byte(cmd, 0x81, true); // Set Contrast Control
260+
i2c_master_write_byte(cmd, 0xCF, true); // Use High Contrast (0xCF) as per u8x8
261+
262+
i2c_master_write_byte(cmd, 0xD9, true); // Set Pre-charge Period
263+
i2c_master_write_byte(cmd, 0xF1, true); // 0xF1 is required for stable 400kHz operation
264+
265+
i2c_master_write_byte(cmd, 0xDB, true); // Set VCOMH Deselect Level
266+
i2c_master_write_byte(cmd, 0x40, true); // 0x40 (approx 0.77x VCC)
267+
268+
i2c_master_write_byte(cmd, 0xA4, true); // Resume to RAM content display
269+
i2c_master_write_byte(cmd, 0xA6, true); // Normal Display (not inverted)
270+
271+
i2c_master_write_byte(cmd, 0xAD, true); // Internal IREF Setting
272+
i2c_master_write_byte(cmd, 0x10, true); // Internal Iref
273+
} else {
274+
i2c_master_write_byte(cmd, CMD_SET_CHARGE_PUMP, true);
275+
i2c_master_write_byte(cmd, 0x14, true);
276+
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)