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
5764struct 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