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