11/* *
22 * Marlin 3D Printer Firmware
3- * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
3+ * Copyright (c) 2023 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
44 *
55 * Based on Sprinter and grbl.
66 * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
5454 */
5555
5656/* *
57- * These routines are meant for two wire I2C interfaces.
57+ * For two wire I2C interfaces.
5858 *
59- * Three and four wire I2C interfaces have an A0 line. That line is
59+ * Three and four wire I2C interfaces have an A0 line. That line is
6060 * used to switch between command and data modes.
6161 *
6262 * The two wire LCDs use an instruction byte to signal if data or
63- * command info is to follow. The command stream needs the instruction
64- * byte between eack command byte. The data stream needs one at the
63+ * command info is to follow. The command stream needs the instruction
64+ * byte between eack command byte. The data stream needs one at the
6565 * beginning.
6666 */
6767
6868#include " ../../inc/MarlinConfigPre.h"
6969
70- #if HAS_MARLINUI_U8GLIB
70+ #if ENABLED(U8GLIB_SH1106)
7171
7272#include " HAL_LCD_com_defines.h"
7373
7474#define WIDTH 128
7575#define HEIGHT 64
7676#define PAGE_HEIGHT 8
7777
78- uint8_t u8g_WriteEscSeqP_2_wire (u8g_t *u8g, u8g_dev_t *dev, const uint8_t *esc_seq);
78+ // This routine adds the instruction byte in between the command bytes. This makes the init
79+ // sequences a lot easier to read.
7980
80- // The sh1106 is compatible to the ssd1306, but is 132x64. 128x64 display area is centered within
81- // the 132x64.
81+ #define I2C_CMD_MODE 0x80
8282
83- static const uint8_t u8g_dev_sh1106_128x64_data_start_2_wire[] PROGMEM = {
84- 0x010 , // set upper 4 bit of the col adr to 0
85- 0x002 , // set lower 4 bit of the col adr to 2 (centered display with ssd1306)
86- U8G_ESC_END // end of sequence
87- };
83+ uint8_t u8g_WriteEscSeqP_2_wire (u8g_t *u8g, u8g_dev_t *dev, const uint8_t *esc_seq) {
84+ bool is_escape = false ;
85+ for (;;) {
86+ uint8_t value = u8g_pgm_read (esc_seq);
87+ if (!is_escape) {
88+ if (value != 255 ) {
89+ if (u8g_WriteByte (u8g, dev, value) == 0 ) return 0 ;
90+ if (u8g_WriteByte (u8g, dev, I2C_CMD_MODE) == 0 ) return 0 ;
91+ }
92+ else {
93+ is_escape = true ;
94+ }
95+ }
96+ else {
97+ if (value == 255 ) {
98+ if (u8g_WriteByte (u8g, dev, value) == 0 ) return 0 ;
99+ if (u8g_WriteByte (u8g, dev, I2C_CMD_MODE) == 0 ) return 0 ;
100+ }
101+ else if (value == 254 ) {
102+ break ;
103+ }
104+ else if (value >= 0xF0 ) {
105+ // not yet used, do nothing
106+ }
107+ else if (value >= 0xE0 ) {
108+ u8g_SetAddress (u8g, dev, value & 0x0F );
109+ }
110+ else if (value >= 0xD0 ) {
111+ u8g_SetChipSelect (u8g, dev, value & 0x0F );
112+ }
113+ else if (value >= 0xC0 ) {
114+ u8g_SetResetLow (u8g, dev);
115+ value &= 0x0F ;
116+ value <<= 4 ;
117+ value+=2 ;
118+ u8g_Delay (value);
119+ u8g_SetResetHigh (u8g, dev);
120+ u8g_Delay (value);
121+ }
122+ else if (value >= 0xBE ) { // not yet implemented
123+ // u8g_SetVCC(u8g, dev, value & 0x01);
124+ }
125+ else if (value <= 127 ) {
126+ u8g_Delay (value);
127+ }
128+ is_escape = false ;
129+ }
130+ esc_seq++;
131+ }
132+ return 1 ;
133+ }
88134
135+ // SH1106 is compatible with SSD1306, but is 132x64, so the 128x64 display area
136+ // is centered within the 132x64.
137+
138+ #define SH1106_COL_ADR (N ) (0x10 | ((N)>>4 )), ((N) & 0x0F )
89139#define SH1106_PAGE_ADR (N ) (0x20 ), (N)
90- #define SH1106_COLUMN_RANGE (N ) (0x21 ), (((N) >> 8 ) & 0xFF ) , ((N) & 0xFF )
140+ #define SH1106_COLUMN_RANGE (N,O ) (0x21 ), (N) , (O )
91141#define SH1106_PAGE_RANGE (N,O ) (0x22 ), (N), (O)
92142#define SH1106_SCROLL (N ) ((N) ? 0x2F : 0x2E )
93143#define SH1106_START_LINE (N ) (0x40 | (N))
@@ -104,7 +154,12 @@ static const uint8_t u8g_dev_sh1106_128x64_data_start_2_wire[] PROGMEM = {
104154#define SH1106_CHARGE_PER (P,D ) (0xD9 ), ((D) << 4 | (P))
105155#define SH1106_COM_CONFIG (N ) (0xDA ), ((N) ? 0x12 : 0x02 )
106156#define SH1106_VCOM_DESEL (N ) (0xDB ), (N)
107- #define SH1106_NOOP () (0xE3 )
157+ #define SH1106_NOP () (0xE3 )
158+
159+ static const uint8_t u8g_dev_sh1106_128x64_data_start_2_wire[] PROGMEM = {
160+ SH1106_COL_ADR (2 ), // Start at column 2
161+ U8G_ESC_END // End of sequence
162+ };
108163
109164static const uint8_t u8g_dev_sh1106_128x64_init_seq_2_wire[] PROGMEM = {
110165 U8G_ESC_ADR (0 ), // Initiate command mode
@@ -115,17 +170,17 @@ static const uint8_t u8g_dev_sh1106_128x64_init_seq_2_wire[] PROGMEM = {
115170 SH1106_ADC_REVERSE (1 ), // Segment remap A0/A1
116171 SH1106_OUT_MODE (1 ), // C0: scan dir normal, C8: reverse
117172 SH1106_COM_CONFIG (1 ), // Com pin HW config, sequential com pin config (bit 4), disable left/right remap (bit 5)
118- SH1106_CONTRAST (0xCF ), // [2] set contrast control
119- SH1106_PAGE_ADR (0x02 ), // 2012-05-27: page addressing mode
120- SH1106_COLUMN_RANGE (0x281 ), // Set column range from 0 through 131
173+ SH1106_CONTRAST (0xCF ), // [2] Set contrast control
174+ SH1106_PAGE_ADR (0x02 ), // Page addressing mode
175+ SH1106_COLUMN_RANGE (2 , 129 ), // Set column range from 2 through 129
121176 SH1106_PAGE_RANGE (0 , 7 ), // Set page range from 0 through 7
122- SH1106_CHARGE_PER (0x1 , 0xF ), // [2] pre -charge period 0x22/F1
177+ SH1106_CHARGE_PER (0x1 , 0xF ), // [2] Pre -charge period 0x22/F1
123178 SH1106_VCOM_DESEL (0x40 ), // Vcomh deselect level
124- SH1106_ALL_PIX (0 ), // Output ram to display
179+ SH1106_ALL_PIX (0 ), // Output RAM to display
125180 SH1106_INVERTED (0 ), // Normal display mode
126181 SH1106_OSC_FREQ (0 , 8 ), // Clock divide ratio (0:1) and oscillator frequency (8)
127- SH1106_CHARGE_PUMP (1 ), // [2] charge pump setting (P62): 0x14 enable, 0x10 disable
128- SH1106_SCROLL (0 ), // 2012-05-27: Deactivate scroll
182+ SH1106_CHARGE_PUMP (1 ), // [2] Charge pump setting (P62): 0x14 enable, 0x10 disable
183+ SH1106_SCROLL (0 ), // Deactivate scroll
129184 SH1106_ON (1 ), // Display on
130185 U8G_ESC_END // End of sequence
131186};
@@ -140,16 +195,16 @@ uint8_t u8g_dev_sh1106_128x64_2x_2_wire_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t m
140195 break ;
141196 case U8G_DEV_MSG_PAGE_NEXT: {
142197 u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem );
143- u8g_SetAddress (u8g, dev, 0 ); // instruction mode
198+ u8g_SetAddress (u8g, dev, 0 ); // Instruction mode
144199 u8g_WriteEscSeqP_2_wire (u8g, dev, u8g_dev_sh1106_128x64_data_start_2_wire);
145- u8g_WriteByte (u8g, dev, 0x0B0 | (pb->p .page *2 )); // select current page
146- u8g_SetAddress (u8g, dev, 1 ); // data mode
147- u8g_WriteSequence (u8g, dev, pb->width , (uint8_t *) pb->buf );
200+ u8g_WriteByte (u8g, dev, 0xB0 | (pb->p .page *2 )); // Select current page
201+ u8g_SetAddress (u8g, dev, 1 ); // Data mode
202+ u8g_WriteSequence (u8g, dev, pb->width , (uint8_t *)pb->buf );
148203 u8g_SetChipSelect (u8g, dev, 0 );
149- u8g_SetAddress (u8g, dev, 0 ); // instruction mode
204+ u8g_SetAddress (u8g, dev, 0 ); // Instruction mode
150205 u8g_WriteEscSeqP_2_wire (u8g, dev, u8g_dev_sh1106_128x64_data_start_2_wire);
151- u8g_WriteByte (u8g, dev, 0x0B0 | (pb->p .page *2 +1 )); // select current page
152- u8g_SetAddress (u8g, dev, 1 ); // data mode
206+ u8g_WriteByte (u8g, dev, 0xB0 | (pb->p .page *2 +1 )); // Select current page
207+ u8g_SetAddress (u8g, dev, 1 ); // Data mode
153208 u8g_WriteSequence (u8g, dev, pb->width , (uint8_t *)(pb->buf )+pb->width );
154209 u8g_SetChipSelect (u8g, dev, 0 );
155210 }
@@ -162,141 +217,8 @@ uint8_t u8g_dev_sh1106_128x64_2x_2_wire_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t m
162217 return u8g_dev_pb16v1_base_fn (u8g, dev, msg, arg);
163218}
164219
165- uint8_t u8g_dev_sh1106_128x64_2x_i2c_2_wire_buf[WIDTH* 2 ] U8G_NOCOMMON ;
166- u8g_pb_t u8g_dev_sh1106_128x64_2x_i2c_2_wire_pb = { {16 , HEIGHT, 0 , 0 , 0 }, WIDTH, u8g_dev_sh1106_128x64_2x_i2c_2_wire_buf};
220+ uint8_t u8g_dev_sh1106_128x64_2x_i2c_2_wire_buf[WIDTH * 2 ] U8G_NOCOMMON;
221+ u8g_pb_t u8g_dev_sh1106_128x64_2x_i2c_2_wire_pb = { { 16 , HEIGHT, 0 , 0 , 0 }, WIDTH, u8g_dev_sh1106_128x64_2x_i2c_2_wire_buf };
167222u8g_dev_t u8g_dev_sh1106_128x64_2x_i2c_2_wire = { u8g_dev_sh1106_128x64_2x_2_wire_fn, &u8g_dev_sh1106_128x64_2x_i2c_2_wire_pb, U8G_COM_SSD_I2C_HAL };
168223
169- // ///////////////////////////////////////////////////////////////////////////////////////////
170-
171- static const uint8_t u8g_dev_ssd1306_128x64_data_start_2_wire[] PROGMEM = {
172- 0x010 , // set upper 4 bit of the col adr to 0
173- 0x000 , // set lower 4 bit of the col adr to 0
174- U8G_ESC_END // end of sequence
175- };
176-
177- static const uint8_t u8g_dev_ssd1306_128x64_init_seq_2_wire[] PROGMEM = {
178- U8G_ESC_ADR (0 ), // initiate command mode
179- 0x0AE , // display off, sleep mode
180- 0x0A8 , 0x03F , // mux ratio
181- 0x0D3 , 0x00 , // display offset
182- 0x040 , // start line
183- 0x0A1 , // segment remap a0/a1
184- 0x0C8 , // c0: scan dir normal, c8: reverse
185- 0x0DA , 0x012 , // com pin HW config, sequential com pin config (bit 4), disable left/right remap (bit 5)
186- 0x081 , 0x0CF , // [2] set contrast control
187- 0x020 , 0x002 , // 2012-05-27: page addressing mode
188- 0x21 , 0 , 0x7F , // set column range from 0 through 127
189- 0x22 , 0 , 7 , // set page range from 0 through 7
190- 0x0D9 , 0x0F1 , // [2] pre-charge period 0x022/f1
191- 0x0DB , 0x040 , // vcomh deselect level
192- 0x0A4 , // output ram to display
193- 0x0A6 , // none inverted normal display mode
194- 0x0D5 , 0x080 , // clock divide ratio (0x00=1) and oscillator frequency (0x8)
195- 0x08D , 0x014 , // [2] charge pump setting (p62): 0x014 enable, 0x010 disable
196- 0x02E , // 2012-05-27: Deactivate scroll
197- 0x0AF , // display on
198- U8G_ESC_END // end of sequence
199- };
200-
201- uint8_t u8g_dev_ssd1306_128x64_2x_2_wire_fn (u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg) {
202- switch (msg) {
203- case U8G_DEV_MSG_INIT:
204- u8g_InitCom (u8g, dev, U8G_SPI_CLK_CYCLE_300NS);
205- u8g_WriteEscSeqP_2_wire (u8g, dev, u8g_dev_ssd1306_128x64_init_seq_2_wire);
206- break ;
207- case U8G_DEV_MSG_STOP:
208- break ;
209- case U8G_DEV_MSG_PAGE_NEXT: {
210- u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem );
211- u8g_SetAddress (u8g, dev, 0 ); // instruction mode
212- u8g_WriteEscSeqP_2_wire (u8g, dev, u8g_dev_ssd1306_128x64_data_start_2_wire);
213- u8g_WriteByte (u8g, dev, 0x0B0 | (pb->p .page *2 )); // select current page
214- u8g_SetAddress (u8g, dev, 1 ); // data mode
215- u8g_WriteSequence (u8g, dev, pb->width , (uint8_t *) pb->buf );
216- u8g_SetChipSelect (u8g, dev, 0 );
217- u8g_SetAddress (u8g, dev, 0 ); // instruction mode
218- u8g_WriteEscSeqP_2_wire (u8g, dev, u8g_dev_ssd1306_128x64_data_start_2_wire);
219- u8g_WriteByte (u8g, dev, 0x0B0 | (pb->p .page *2 +1 )); // select current page
220- u8g_SetAddress (u8g, dev, 1 ); // data mode
221- u8g_WriteSequence (u8g, dev, pb->width , (uint8_t *)(pb->buf )+pb->width );
222- u8g_SetChipSelect (u8g, dev, 0 );
223- }
224- break ;
225- case U8G_DEV_MSG_SLEEP_ON:
226- return 1 ;
227- case U8G_DEV_MSG_SLEEP_OFF:
228- return 1 ;
229- }
230- return u8g_dev_pb16v1_base_fn (u8g, dev, msg, arg);
231- }
232-
233-
234- uint8_t u8g_dev_ssd1306_128x64_2x_i2c_2_wire_buf[WIDTH*2 ] U8G_NOCOMMON ;
235- u8g_pb_t u8g_dev_ssd1306_128x64_2x_i2c_2_wire_pb = { {16 , HEIGHT, 0 , 0 , 0 }, WIDTH, u8g_dev_ssd1306_128x64_2x_i2c_2_wire_buf};
236- u8g_dev_t u8g_dev_ssd1306_128x64_2x_i2c_2_wire = { u8g_dev_ssd1306_128x64_2x_2_wire_fn, &u8g_dev_ssd1306_128x64_2x_i2c_2_wire_pb, U8G_COM_SSD_I2C_HAL };
237-
238-
239- // ///////////////////////////////////////////////////////////////////////////////////////////
240-
241- // This routine adds the instruction byte in between the command bytes. This makes the init
242- // sequences a lot easier to read.
243-
244- #define I2C_CMD_MODE 0x080
245-
246- uint8_t u8g_WriteEscSeqP_2_wire (u8g_t *u8g, u8g_dev_t *dev, const uint8_t *esc_seq) {
247- uint8_t is_escape = 0 ;
248- for (;;) {
249- uint8_t value = u8g_pgm_read (esc_seq);
250- if (is_escape == 0 ) {
251- if (value != 255 ) {
252- if (u8g_WriteByte (u8g, dev, value) == 0 )
253- return 0 ;
254- if (u8g_WriteByte (u8g, dev, I2C_CMD_MODE) == 0 )
255- return 0 ;
256- }
257- else {
258- is_escape = 1 ;
259- }
260- }
261- else {
262- if (value == 255 ) {
263- if (u8g_WriteByte (u8g, dev, value) == 0 )
264- return 0 ;
265- if (u8g_WriteByte (u8g, dev, I2C_CMD_MODE) == 0 )
266- return 0 ;
267- }
268- else if (value == 254 ) {
269- break ;
270- }
271- else if (value >= 0x0F0 ) {
272- /* not yet used, do nothing */
273- }
274- else if (value >= 0xE0 ) {
275- u8g_SetAddress (u8g, dev, value & 0x0F );
276- }
277- else if (value >= 0xD0 ) {
278- u8g_SetChipSelect (u8g, dev, value & 0x0F );
279- }
280- else if (value >= 0xC0 ) {
281- u8g_SetResetLow (u8g, dev);
282- value &= 0x0F ;
283- value <<= 4 ;
284- value+=2 ;
285- u8g_Delay (value);
286- u8g_SetResetHigh (u8g, dev);
287- u8g_Delay (value);
288- }
289- else if (value >= 0xBE ) { /* not yet implemented */
290- /* u8g_SetVCC(u8g, dev, value & 0x01); */
291- }
292- else if (value <= 127 ) {
293- u8g_Delay (value);
294- }
295- is_escape = 0 ;
296- }
297- esc_seq++;
298- }
299- return 1 ;
300- }
301-
302- #endif // HAS_MARLINUI_U8GLIB
224+ #endif // U8GLIB_SH1106
0 commit comments