summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDasky <32983009+daskygit@users.noreply.github.com>2024-09-18 12:44:33 +0100
committerGitHub <noreply@github.com>2024-09-18 21:44:33 +1000
commit49e339ba8d03e80f13190260ec92ba64bbb34998 (patch)
treec21ada655602b03f0a4075ce365bca62e84391ab
parent7983f7409b3f002849a321757d9bf18533e931a5 (diff)
Add LD7032 support to QP. (#20828)
Co-authored-by: Nick Brassel <nick@tzarc.org> Co-authored-by: Sergey Vlasov <sigprof@gmail.com>
-rw-r--r--docs/quantum_painter.md36
-rw-r--r--drivers/painter/ld7032/qp_ld7032.c411
-rw-r--r--drivers/painter/ld7032/qp_ld7032.h66
-rw-r--r--drivers/painter/ld7032/qp_ld7032_opcodes.h45
-rw-r--r--quantum/painter/qp.h6
-rw-r--r--quantum/painter/qp_internal.c1
-rw-r--r--quantum/painter/rules.mk27
7 files changed, 591 insertions, 1 deletions
diff --git a/docs/quantum_painter.md b/docs/quantum_painter.md
index 1d844d0f94..782d496ff7 100644
--- a/docs/quantum_painter.md
+++ b/docs/quantum_painter.md
@@ -28,6 +28,8 @@ Supported devices:
28| ILI9341 | RGB LCD | 240x320 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9341_spi` | 28| ILI9341 | RGB LCD | 240x320 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9341_spi` |
29| ILI9486 | RGB LCD | 320x480 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9486_spi` | 29| ILI9486 | RGB LCD | 320x480 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9486_spi` |
30| ILI9488 | RGB LCD | 320x480 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9488_spi` | 30| ILI9488 | RGB LCD | 320x480 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9488_spi` |
31| LD7032 (SPI) | Monochrome OLED | 128x40 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ld7032_spi` |
32| LD7032 (I2C) | Monochrome OLED | 128x40 | I2C | `QUANTUM_PAINTER_DRIVERS += ld7032_i2c` |
31| SSD1351 | RGB OLED | 128x128 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ssd1351_spi` | 33| SSD1351 | RGB OLED | 128x128 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ssd1351_spi` |
32| ST7735 | RGB LCD | 132x162, 80x160 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += st7735_spi` | 34| ST7735 | RGB LCD | 132x162, 80x160 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += st7735_spi` |
33| ST7789 | RGB LCD | 240x320, 240x240 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += st7789_spi` | 35| ST7789 | RGB LCD | 240x320, 240x240 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += st7789_spi` |
@@ -478,6 +480,40 @@ Native color format mono2 is compatible with SH1106
478 480
479SSD1306 and SH1106 are almost entirely identical, to the point of being indisinguishable by Quantum Painter. Enable SH1106 support in Quantum Painter and create SH1106 devices in firmware to perform drawing operations on SSD1306 displays. 481SSD1306 and SH1106 are almost entirely identical, to the point of being indisinguishable by Quantum Painter. Enable SH1106 support in Quantum Painter and create SH1106 devices in firmware to perform drawing operations on SSD1306 displays.
480 482
483==== LD7032
484
485Enabling support for the LD7032 in Quantum Painter is done by adding the following to `rules.mk`:
486
487```make
488QUANTUM_PAINTER_ENABLE = yes
489# For SPI:
490QUANTUM_PAINTER_DRIVERS += ld7032_spi
491# For I2C:
492QUANTUM_PAINTER_DRIVERS += ld7032_i2c
493```
494
495Creating a SH1106 device in firmware can then be done with the following APIs:
496
497```c
498// SPI-based LD7032:
499painter_device_t qp_ld7032_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);
500// I2C-based LD7032:
501painter_device_t qp_ld7032_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address);
502```
503
504The device handle returned from the `qp_ld7032_make_???_device` function can be used to perform all other drawing operations.
505
506The maximum number of displays of each type can be configured by changing the following in your `config.h` (default is 1):
507
508```c
509// 3 SPI displays:
510#define LD7032_NUM_SPI_DEVICES 3
511// 3 I2C displays:
512#define LD7032_NUM_I2C_DEVICES 3
513```
514
515Native color format mono2 is compatible with LD7032.
516
481::::: 517:::::
482 518
483===== Surface 519===== Surface
diff --git a/drivers/painter/ld7032/qp_ld7032.c b/drivers/painter/ld7032/qp_ld7032.c
new file mode 100644
index 0000000000..f43ae8e60d
--- /dev/null
+++ b/drivers/painter/ld7032/qp_ld7032.c
@@ -0,0 +1,411 @@
1// Copyright 2023 Nick Brassel (@tzarc)
2// Copyright 2023 Dasky (@daskygit)
3// SPDX-License-Identifier: GPL-2.0-or-later
4
5#include "qp_internal.h"
6#include "qp_comms.h"
7#include "qp_oled_panel.h"
8#include "qp_ld7032.h"
9#include "qp_ld7032_opcodes.h"
10#include "qp_surface.h"
11#include "qp_surface_internal.h"
12
13typedef void (*ld7032_driver_comms_send_command_and_data_func)(painter_device_t device, uint8_t cmd, uint8_t data);
14typedef uint32_t (*ld7032_driver_comms_send_command_and_databuf_func)(painter_device_t device, uint8_t cmd, const void *data, uint32_t byte_count);
15
16typedef struct ld7032_comms_with_command_vtable_t {
17 painter_comms_vtable_t base; // must be first, so this object can be cast from the painter_comms_vtable_t* type
18 painter_driver_comms_send_command_func send_command;
19 painter_driver_comms_bulk_command_sequence bulk_command_sequence;
20 ld7032_driver_comms_send_command_and_data_func send_command_data;
21 ld7032_driver_comms_send_command_and_databuf_func send_command_databuf;
22} ld7032_comms_with_command_vtable_t;
23
24////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
25// LD7032 Internal API
26////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
27
28void ld7032_comms_i2c_send_command_and_data(painter_device_t device, uint8_t cmd, uint8_t data) {
29 uint8_t buf[2] = {cmd, data};
30 qp_comms_i2c_send_data(device, buf, 2);
31}
32
33void ld7032_comms_i2c_bulk_command_sequence(painter_device_t device, const uint8_t *sequence, size_t sequence_len) {
34 uint8_t buf[32];
35 for (size_t i = 0; i < sequence_len;) {
36 uint8_t command = sequence[i];
37 uint8_t delay = sequence[i + 1];
38 uint8_t num_bytes = sequence[i + 2];
39 buf[0] = command;
40 memcpy(&buf[1], &sequence[i + 3], num_bytes);
41 qp_comms_i2c_send_data(device, buf, num_bytes + 1);
42 if (delay > 0) {
43 wait_ms(delay);
44 }
45 i += (3 + num_bytes);
46 }
47}
48
49uint32_t ld7032_comms_i2c_send_command_and_databuf(painter_device_t device, uint8_t cmd, const void *data, uint32_t byte_count) {
50 uint8_t buf[byte_count + 1];
51 memset(buf, 0, sizeof(buf));
52 buf[0] = cmd;
53 memcpy(&buf[1], data, byte_count);
54 return qp_comms_send(device, buf, byte_count + 1);
55}
56
57// Power control
58bool qp_ld7032_power(painter_device_t device, bool power_on) {
59 painter_driver_t * driver = (painter_driver_t *)device;
60 ld7032_comms_with_command_vtable_t *comms_vtable = (ld7032_comms_with_command_vtable_t *)driver->comms_vtable;
61
62 comms_vtable->send_command_data(device, LD7032_DISP_ON_OFF, power_on ? 0x01 : 0x00);
63
64 return true;
65}
66
67// Screen clear
68bool qp_ld7032_clear(painter_device_t device) {
69 qp_rect(device, 0, 0, 127, 127, 0, 0, 0, true); // clear memory
70 painter_driver_t *driver = (painter_driver_t *)device;
71 driver->driver_vtable->init(device, driver->rotation); // Re-init the display
72 return true;
73}
74
75////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
76// Flush helpers
77////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
78
79void ld7032_flush_0(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer, bool inverted) {
80 painter_driver_t * driver = (painter_driver_t *)device;
81 ld7032_comms_with_command_vtable_t *comms_vtable = (ld7032_comms_with_command_vtable_t *)driver->comms_vtable;
82
83 int x_start = dirty->l >> 3;
84 int x_end = dirty->r >> 3;
85 int y_start = dirty->t;
86 int y_end = dirty->b;
87 int x_length = (x_end - x_start) + 1;
88 uint8_t x_view_offset = driver->offset_x >> 3;
89 uint8_t y_view_offset = driver->offset_y;
90
91 for (int y_pos = y_start; y_pos <= y_end; y_pos++) {
92 int y_new_pos = y_pos;
93 if (inverted) {
94 y_new_pos = y_end - y_pos;
95 }
96 uint8_t packet[x_length];
97 memcpy(packet, &framebuffer[(y_pos * (driver->panel_width >> 3)) + x_start], x_length);
98 uint8_t x_write_start = MIN(x_start + x_view_offset, (128 >> 3));
99 uint8_t x_write_end = MIN(x_end + x_view_offset, (128 >> 3));
100 uint8_t y_write_start = MIN(y_new_pos + y_view_offset, 39);
101 uint8_t y_write_end = MIN(y_new_pos + y_view_offset, 39);
102
103 comms_vtable->send_command_data(device, LD7032_X_BOX_ADR_START, x_write_start);
104 comms_vtable->send_command_data(device, LD7032_X_BOX_ADR_END, x_write_end);
105 comms_vtable->send_command_data(device, LD7032_Y_BOX_ADR_START, y_write_start);
106 comms_vtable->send_command_data(device, LD7032_Y_BOX_ADR_END, y_write_end);
107 comms_vtable->send_command_databuf(device, LD7032_DATA_RW, packet, x_length);
108 }
109}
110
111void ld7032_flush_90(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer, bool inverted) {
112 painter_driver_t * driver = (painter_driver_t *)device;
113 ld7032_comms_with_command_vtable_t *comms_vtable = (ld7032_comms_with_command_vtable_t *)driver->comms_vtable;
114
115 int x_start = dirty->t >> 3;
116 int x_end = dirty->b >> 3;
117 int y_start = dirty->l;
118 int y_end = dirty->r;
119 int x_length = (x_end - x_start) + 1;
120 uint8_t x_view_offset = driver->offset_x >> 3;
121 uint8_t y_view_offset = driver->offset_y;
122
123 for (int y_pos = y_start; y_pos <= y_end; y_pos++) {
124 int y_new_pos = y_pos;
125 if (inverted) {
126 y_new_pos = y_end - y_pos;
127 }
128 uint8_t packet[x_length];
129 memset(packet, 0, sizeof(packet));
130 int count = 0;
131 for (int x_pos = x_start; x_pos <= x_end; x_pos++) {
132 for (int x = 0; x < 8; ++x) {
133 uint32_t pixel_num = (((x_pos << 3) + x) * driver->panel_height) + y_pos;
134 uint32_t byte_offset = pixel_num / 8;
135 uint8_t bit_offset = pixel_num % 8;
136 packet[count] |= ((framebuffer[byte_offset] & (1 << bit_offset)) >> bit_offset) << x;
137 }
138 count++;
139 }
140 uint8_t x_width = (driver->panel_width >> 3) - 1;
141 uint8_t x_write_start = MAX((int)x_width - x_end - x_view_offset, 0);
142 uint8_t x_write_end = MAX((int)x_width - x_start - x_view_offset, 0);
143 uint8_t y_write_start = MIN(y_new_pos + y_view_offset, 39);
144 uint8_t y_write_end = MIN(y_new_pos + y_view_offset, 39);
145
146 comms_vtable->send_command_data(device, LD7032_X_BOX_ADR_START, x_write_start);
147 comms_vtable->send_command_data(device, LD7032_X_BOX_ADR_END, x_write_end);
148 comms_vtable->send_command_data(device, LD7032_Y_BOX_ADR_START, y_write_start);
149 comms_vtable->send_command_data(device, LD7032_Y_BOX_ADR_END, y_write_end);
150 comms_vtable->send_command_databuf(device, LD7032_DATA_RW, packet, x_length);
151 }
152}
153
154////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
155// Driver storage
156////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
157
158typedef struct ld7032_device_t {
159 oled_panel_painter_device_t oled;
160
161 uint8_t framebuffer[SURFACE_REQUIRED_BUFFER_BYTE_SIZE(128, 40, 1)];
162} ld7032_device_t;
163
164static ld7032_device_t ld7032_drivers[LD7032_NUM_DEVICES] = {0};
165
166////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
167// Quantum Painter API implementations
168
169// Initialisation
170__attribute__((weak)) bool qp_ld7032_init(painter_device_t device, painter_rotation_t rotation) {
171 ld7032_device_t *driver = (ld7032_device_t *)device;
172
173 // Change the surface geometry based on the panel rotation
174 if (rotation == QP_ROTATION_90 || rotation == QP_ROTATION_270) {
175 driver->oled.surface.base.panel_width = driver->oled.base.panel_height;
176 driver->oled.surface.base.panel_height = driver->oled.base.panel_width;
177 } else {
178 driver->oled.surface.base.panel_width = driver->oled.base.panel_width;
179 driver->oled.surface.base.panel_height = driver->oled.base.panel_height;
180 }
181
182 // Init the internal surface
183 if (!qp_init(&driver->oled.surface.base, QP_ROTATION_0)) {
184 qp_dprintf("Failed to init internal surface in qp_ld7032_init\n");
185 return false;
186 }
187
188 // clang-format off
189 const uint8_t ld7032_init_sequence[] = {
190 // Command, Delay, N, Data[N]
191 LD7032_DISP_STBY_ON_OFF, 0, 1, 0x00,
192 LD7032_DISP_ON_OFF, 0, 1, 0x00,
193 LD7032_DFRAME, 0, 1, 0x05,
194 //LD7032_WRITE_DIRECTION, 0, 1, 0b00001000, // 0 Right, 1 Up, 2 Vertical, 3 Bit Order, 4-7 Unused
195 LD7032_DISP_DIRECTION, 0, 1, 0x00,
196 LD7032_PEAK_WIDTH, 0, 1, 0x1F,
197 LD7032_PEAK_DELAY, 0, 1, 0x05,
198 LD7032_SCAN_MODE, 0, 1, 0x01,
199 LD7032_DOT_CURRENT, 0, 1, 0x1f,
200 LD7032_VDD_SEL, 0, 1, 0x01,
201 };
202 // clang-format on
203
204 qp_comms_bulk_command_sequence(device, ld7032_init_sequence, sizeof(ld7032_init_sequence));
205
206 uint8_t display_y_start = 40 - driver->oled.base.panel_height;
207 uint8_t display_x_start = (128 - driver->oled.base.panel_width) / 2;
208
209 // clang-format off
210 uint8_t ld7032_memory_setup[] = {
211 // Command, Delay, N, Data[N]
212 LD7032_DISP_SIZE_X, 0, 2, 0x00, 0x7F,
213 LD7032_DISP_SIZE_Y, 0, 2, 0x00, 0x27,
214 LD7032_X_DISP_START, 0, 1, 0x0,
215 LD7032_Y_DISP_START, 0, 1, 0x0,
216 };
217 // clang-format on
218
219 ld7032_memory_setup[3] = display_x_start;
220 ld7032_memory_setup[4] = display_x_start + driver->oled.base.panel_width - 1;
221 ld7032_memory_setup[8] = display_y_start;
222 ld7032_memory_setup[9] = display_y_start + driver->oled.base.panel_height - 1;
223 ld7032_memory_setup[13] = ld7032_memory_setup[4] + 1;
224 ld7032_memory_setup[17] = driver->oled.base.panel_height;
225
226 qp_comms_bulk_command_sequence(device, ld7032_memory_setup, sizeof(ld7032_memory_setup));
227
228 uint8_t write_direction = 0;
229 switch (rotation) {
230 default:
231 case QP_ROTATION_0:
232 write_direction = 0b00001000;
233 break;
234 case QP_ROTATION_90:
235 write_direction = 0b00000001;
236 break;
237 case QP_ROTATION_180:
238 write_direction = 0b00000001;
239 break;
240 case QP_ROTATION_270:
241 write_direction = 0b00001000;
242 break;
243 }
244
245 painter_driver_t * pdriver = (painter_driver_t *)device;
246 ld7032_comms_with_command_vtable_t *comms_vtable = (ld7032_comms_with_command_vtable_t *)pdriver->comms_vtable;
247
248 comms_vtable->send_command_data(device, LD7032_WRITE_DIRECTION, write_direction);
249
250 qp_ld7032_power(device, true);
251
252 return true;
253}
254
255// Screen flush
256bool qp_ld7032_flush(painter_device_t device) {
257 ld7032_device_t *driver = (ld7032_device_t *)device;
258
259 if (!driver->oled.surface.dirty.is_dirty) {
260 return true;
261 }
262
263 switch (driver->oled.base.rotation) {
264 default:
265 case QP_ROTATION_0:
266 ld7032_flush_0(device, &driver->oled.surface.dirty, driver->framebuffer, false);
267 break;
268 case QP_ROTATION_180:
269 ld7032_flush_0(device, &driver->oled.surface.dirty, driver->framebuffer, true);
270 break;
271 case QP_ROTATION_90:
272 ld7032_flush_90(device, &driver->oled.surface.dirty, driver->framebuffer, false);
273 break;
274 case QP_ROTATION_270:
275 ld7032_flush_90(device, &driver->oled.surface.dirty, driver->framebuffer, true);
276 break;
277 }
278
279 // Clear the dirty area
280 qp_flush(&driver->oled.surface);
281
282 return true;
283}
284
285////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
286// Driver vtable
287////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
288
289const painter_driver_vtable_t ld7032_driver_vtable = {
290 .init = qp_ld7032_init,
291 .power = qp_ld7032_power,
292 .clear = qp_ld7032_clear,
293 .flush = qp_ld7032_flush,
294 .pixdata = qp_oled_panel_passthru_pixdata,
295 .viewport = qp_oled_panel_passthru_viewport,
296 .palette_convert = qp_oled_panel_passthru_palette_convert,
297 .append_pixels = qp_oled_panel_passthru_append_pixels,
298 .append_pixdata = qp_oled_panel_passthru_append_pixdata,
299};
300
301#ifdef QUANTUM_PAINTER_LD7032_SPI_ENABLE
302
303const ld7032_comms_with_command_vtable_t ld7032_spi_comms_vtable = {
304 .base =
305 {
306 .comms_init = qp_comms_spi_dc_reset_init,
307 .comms_start = qp_comms_spi_start,
308 .comms_send = qp_comms_spi_dc_reset_send_data,
309 .comms_stop = qp_comms_spi_stop,
310 },
311 .send_command = qp_comms_spi_dc_reset_send_command,
312 .send_command_data = qp_comms_command_databyte,
313 .send_command_databuf = qp_comms_command_databuf,
314 .bulk_command_sequence = qp_comms_spi_dc_reset_bulk_command_sequence,
315};
316
317// Factory function for creating a handle to the LD7032 device
318painter_device_t qp_ld7032_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {
319 for (uint32_t i = 0; i < LD7032_NUM_DEVICES; ++i) {
320 ld7032_device_t *driver = &ld7032_drivers[i];
321 if (!driver->oled.base.driver_vtable) {
322 painter_device_t surface = qp_make_mono1bpp_surface_advanced(&driver->oled.surface, 1, panel_width, panel_height, driver->framebuffer);
323 if (!surface) {
324 return NULL;
325 }
326
327 // Setup the OLED device
328 driver->oled.base.driver_vtable = (const painter_driver_vtable_t *)&ld7032_driver_vtable;
329 driver->oled.base.comms_vtable = (const painter_comms_vtable_t *)&ld7032_spi_comms_vtable;
330 driver->oled.base.native_bits_per_pixel = 1; // 1bpp mono
331 driver->oled.base.panel_width = panel_width;
332 driver->oled.base.panel_height = panel_height;
333 driver->oled.base.rotation = QP_ROTATION_0;
334 driver->oled.base.offset_x = 0;
335 driver->oled.base.offset_y = 0;
336
337 // SPI and other pin configuration
338 driver->oled.base.comms_config = &driver->oled.spi_dc_reset_config;
339 driver->oled.spi_dc_reset_config.spi_config.chip_select_pin = chip_select_pin;
340 driver->oled.spi_dc_reset_config.spi_config.divisor = spi_divisor;
341 driver->oled.spi_dc_reset_config.spi_config.lsb_first = false;
342 driver->oled.spi_dc_reset_config.spi_config.mode = spi_mode;
343 driver->oled.spi_dc_reset_config.dc_pin = dc_pin;
344 driver->oled.spi_dc_reset_config.reset_pin = reset_pin;
345 driver->oled.spi_dc_reset_config.command_params_uses_command_pin = true;
346
347 if (!qp_internal_register_device((painter_device_t)driver)) {
348 memset(driver, 0, sizeof(ld7032_device_t));
349 return NULL;
350 }
351
352 return (painter_device_t)driver;
353 }
354 }
355 return NULL;
356}
357
358#endif // QUANTUM_PAINTER_LD7032_SPI_ENABLE
359
360#ifdef QUANTUM_PAINTER_LD7032_I2C_ENABLE
361
362const ld7032_comms_with_command_vtable_t ld7032_i2c_comms_vtable = {
363 .base =
364 {
365 .comms_init = qp_comms_i2c_init,
366 .comms_start = qp_comms_i2c_start,
367 .comms_send = qp_comms_i2c_send_data,
368 .comms_stop = qp_comms_i2c_stop,
369 },
370 .send_command = NULL,
371 .send_command_data = ld7032_comms_i2c_send_command_and_data,
372 .send_command_databuf = ld7032_comms_i2c_send_command_and_databuf,
373 .bulk_command_sequence = ld7032_comms_i2c_bulk_command_sequence,
374};
375
376// Factory function for creating a handle to the LD7032 device
377painter_device_t qp_ld7032_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address) {
378 for (uint32_t i = 0; i < LD7032_NUM_DEVICES; ++i) {
379 ld7032_device_t *driver = &ld7032_drivers[i];
380 if (!driver->oled.base.driver_vtable) {
381 painter_device_t surface = qp_make_mono1bpp_surface_advanced(&driver->oled.surface, 1, panel_width, panel_height, driver->framebuffer);
382 if (!surface) {
383 return NULL;
384 }
385
386 // Setup the OLED device
387 driver->oled.base.driver_vtable = (const painter_driver_vtable_t *)&ld7032_driver_vtable;
388 driver->oled.base.comms_vtable = (const painter_comms_vtable_t *)&ld7032_i2c_comms_vtable;
389 driver->oled.base.native_bits_per_pixel = 1; // 1bpp mono
390 driver->oled.base.panel_width = panel_width;
391 driver->oled.base.panel_height = panel_height;
392 driver->oled.base.rotation = QP_ROTATION_0;
393 driver->oled.base.offset_x = 0;
394 driver->oled.base.offset_y = 0;
395
396 // I2C configuration
397 driver->oled.base.comms_config = &driver->oled.i2c_config;
398 driver->oled.i2c_config.chip_address = i2c_address;
399
400 if (!qp_internal_register_device((painter_device_t)driver)) {
401 memset(driver, 0, sizeof(ld7032_device_t));
402 return NULL;
403 }
404
405 return (painter_device_t)driver;
406 }
407 }
408 return NULL;
409}
410
411#endif // QUANTUM_PAINTER_LD7032_SPI_ENABLE
diff --git a/drivers/painter/ld7032/qp_ld7032.h b/drivers/painter/ld7032/qp_ld7032.h
new file mode 100644
index 0000000000..967eb7999c
--- /dev/null
+++ b/drivers/painter/ld7032/qp_ld7032.h
@@ -0,0 +1,66 @@
1// Copyright 2023 Nick Brassel (@tzarc)
2// SPDX-License-Identifier: GPL-2.0-or-later
3#pragma once
4
5#include "gpio.h"
6#include "qp_internal.h"
7
8////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9// Quantum Painter LD7032 configurables (add to your keyboard's config.h)
10
11#if defined(QUANTUM_PAINTER_LD7032_SPI_ENABLE) && !defined(LD7032_NUM_SPI_DEVICES)
12/**
13 * @def This controls the maximum number of SPI LD7032 devices that Quantum Painter can communicate with at any one time.
14 * Increasing this number allows for multiple displays to be used.
15 */
16# define LD7032_NUM_SPI_DEVICES 1
17#else
18# define LD7032_NUM_SPI_DEVICES 0
19#endif
20
21#if defined(QUANTUM_PAINTER_LD7032_I2C_ENABLE) && !defined(LD7032_NUM_I2C_DEVICES)
22/**
23 * @def This controls the maximum number of I2C LD7032 devices that Quantum Painter can communicate with at any one time.
24 * Increasing this number allows for multiple displays to be used.
25 */
26# define LD7032_NUM_I2C_DEVICES 1
27#else
28# define LD7032_NUM_I2C_DEVICES 0
29#endif
30
31#define LD7032_NUM_DEVICES ((LD7032_NUM_SPI_DEVICES) + (LD7032_NUM_I2C_DEVICES))
32
33////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
34// Quantum Painter LD7032 device factories
35
36#ifdef QUANTUM_PAINTER_LD7032_SPI_ENABLE
37
38/**
39 * Factory method for an LD7032 SPI LCD device.
40 *
41 * @param panel_width[in] the width of the display in pixels (usually 128)
42 * @param panel_height[in] the height of the display in pixels (usually 64)
43 * @param chip_select_pin[in] the GPIO pin used for SPI chip select
44 * @param dc_pin[in] the GPIO pin used for D/C control
45 * @param reset_pin[in] the GPIO pin used for RST
46 * @param spi_divisor[in] the SPI divisor to use when communicating with the display
47 * @param spi_mode[in] the SPI mode to use when communicating with the display
48 * @return the device handle used with all drawing routines in Quantum Painter
49 */
50painter_device_t qp_ld7032_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);
51
52#endif // QUANTUM_PAINTER_LD7032_SPI_ENABLE
53
54#ifdef QUANTUM_PAINTER_LD7032_I2C_ENABLE
55
56/**
57 * Factory method for an LD7032 I2C LCD device.
58 *
59 * @param panel_width[in] the width of the display in pixels (usually 128)
60 * @param panel_height[in] the height of the display in pixels (usually 64)
61 * @param i2c_address[in] the I2C address to use
62 * @return the device handle used with all drawing routines in Quantum Painter
63 */
64painter_device_t qp_ld7032_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address);
65
66#endif // QUANTUM_PAINTER_LD7032_I2C_ENABLE \ No newline at end of file
diff --git a/drivers/painter/ld7032/qp_ld7032_opcodes.h b/drivers/painter/ld7032/qp_ld7032_opcodes.h
new file mode 100644
index 0000000000..08ab77d6f8
--- /dev/null
+++ b/drivers/painter/ld7032/qp_ld7032_opcodes.h
@@ -0,0 +1,45 @@
1// Copyright 2023 Dasky (@daskygit)
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4typedef enum {
5 LD7032_SOFTRES = 0x01,
6 LD7032_DISP_ON_OFF = 0x02,
7 LD7032_DATA_RW = 0x08,
8 LD7032_DISP_DIRECTION = 0x09,
9 LD7032_IFMODE = 0x0D,
10 LD7032_PEAK_WIDTH = 0x10,
11 LD7032_DOT_CURRENT = 0x12,
12 LD7032_SCAN_MODE = 0x13,
13 LD7032_DISP_STBY_ON_OFF = 0x14,
14 LD7032_PEAK_DELAY = 0x16,
15 LD7032_ROW_SCAN = 0x17,
16 LD7032_PRE_C_WIDTH = 0x18,
17 LD7032_DFRAME = 0x1A,
18 LD7032_DATA_REVERSE = 0x1C,
19 LD7032_WRITE_DIRECTION = 0x1D,
20 LD7032_READREG = 0x20,
21 LD7032_DISP_SIZE_X = 0x30,
22 LD7032_DISP_SIZE_Y = 0x32,
23 LD7032_X_BOX_ADR_START = 0x34,
24 LD7032_X_BOX_ADR_END = 0x35,
25 LD7032_Y_BOX_ADR_START = 0x36,
26 LD7032_Y_BOX_ADR_END = 0x37,
27 LD7032_X_DISP_START = 0x38,
28 LD7032_Y_DISP_START = 0x39,
29 LD7032_XTALK_EN = 0x3A,
30 LD7032_XTALK_REF = 0x3B,
31 LD7032_AGING_EN = 0x3C,
32 LD7032_VDD_SEL = 0x3D,
33 LD7032_TESTCNT0 = 0x3E,
34 LD7032_VCC_R_SEL = 0x3F,
35 LD7032_PRE_C_SELECT = 0x44,
36 LD7032_ROW_OVERLAP = 0x48,
37 LD7032_S_SLEEP_TIMER = 0xC0,
38 LD7032_S_SLEEP_START = 0xC2,
39 LD7032_S_STEP_TIMER = 0xC3,
40 LD7032_S_STEP_UNIT = 0xC4,
41 LD7032_S_CONDITION = 0xCC,
42 LD7032_S_START_STOP = 0xCD,
43 LD7032_S_SELECT = 0xCE,
44 LD7032_TESTCNT1 = 0xF0, //-0xFF
45} ld7032_opcodes; \ No newline at end of file
diff --git a/quantum/painter/qp.h b/quantum/painter/qp.h
index 820c418f43..3dc77b42cf 100644
--- a/quantum/painter/qp.h
+++ b/quantum/painter/qp.h
@@ -557,6 +557,12 @@ int16_t qp_drawtext_recolor(painter_device_t device, uint16_t x, uint16_t y, pai
557# define SH1106_NUM_DEVICES 0 557# define SH1106_NUM_DEVICES 0
558#endif // QUANTUM_PAINTER_SH1106_ENABLE 558#endif // QUANTUM_PAINTER_SH1106_ENABLE
559 559
560#ifdef QUANTUM_PAINTER_LD7032_ENABLE
561# include "qp_ld7032.h"
562#else // QUANTUM_PAINTER_LD7032_ENABLE
563# define LD7032_NUM_DEVICES 0
564#endif // QUANTUM_PAINTER_LD7032_ENABLE
565
560//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 566////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
561// Quantum Painter Extras 567// Quantum Painter Extras
562 568
diff --git a/quantum/painter/qp_internal.c b/quantum/painter/qp_internal.c
index 7d4a6430af..5097edfa07 100644
--- a/quantum/painter/qp_internal.c
+++ b/quantum/painter/qp_internal.c
@@ -19,6 +19,7 @@ enum {
19 + (GC9107_NUM_DEVICES) // GC9107 19 + (GC9107_NUM_DEVICES) // GC9107
20 + (SSD1351_NUM_DEVICES) // SSD1351 20 + (SSD1351_NUM_DEVICES) // SSD1351
21 + (SH1106_NUM_DEVICES) // SH1106 21 + (SH1106_NUM_DEVICES) // SH1106
22 + (LD7032_NUM_DEVICES) // LD7032
22}; 23};
23 24
24static painter_device_t qp_devices[QP_NUM_DEVICES] = {NULL}; 25static painter_device_t qp_devices[QP_NUM_DEVICES] = {NULL};
diff --git a/quantum/painter/rules.mk b/quantum/painter/rules.mk
index 7b2ab702ee..b773dd091c 100644
--- a/quantum/painter/rules.mk
+++ b/quantum/painter/rules.mk
@@ -17,7 +17,9 @@ VALID_QUANTUM_PAINTER_DRIVERS := \
17 gc9107_spi \ 17 gc9107_spi \
18 ssd1351_spi \ 18 ssd1351_spi \
19 sh1106_i2c \ 19 sh1106_i2c \
20 sh1106_spi 20 sh1106_spi \
21 ld7032_i2c \
22 ld7032_spi
21 23
22#------------------------------------------------------------------------------- 24#-------------------------------------------------------------------------------
23 25
@@ -182,6 +184,29 @@ define handle_quantum_painter_driver
182 $(DRIVER_PATH)/painter/oled_panel/qp_oled_panel.c \ 184 $(DRIVER_PATH)/painter/oled_panel/qp_oled_panel.c \
183 $(DRIVER_PATH)/painter/sh1106/qp_sh1106.c 185 $(DRIVER_PATH)/painter/sh1106/qp_sh1106.c
184 186
187 else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ld7032_spi)
188 QUANTUM_PAINTER_NEEDS_SURFACE := yes
189 QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes
190 QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes
191 OPT_DEFS += -DQUANTUM_PAINTER_LD7032_ENABLE -DQUANTUM_PAINTER_LD7032_SPI_ENABLE
192 COMMON_VPATH += \
193 $(DRIVER_PATH)/painter/oled_panel \
194 $(DRIVER_PATH)/painter/ld7032
195 SRC += \
196 $(DRIVER_PATH)/painter/oled_panel/qp_oled_panel.c \
197 $(DRIVER_PATH)/painter/ld7032/qp_ld7032.c
198
199 else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ld7032_i2c)
200 QUANTUM_PAINTER_NEEDS_SURFACE := yes
201 QUANTUM_PAINTER_NEEDS_COMMS_I2C := yes
202 OPT_DEFS += -DQUANTUM_PAINTER_LD7032_ENABLE -DQUANTUM_PAINTER_LD7032_I2C_ENABLE
203 COMMON_VPATH += \
204 $(DRIVER_PATH)/painter/oled_panel \
205 $(DRIVER_PATH)/painter/ld7032
206 SRC += \
207 $(DRIVER_PATH)/painter/oled_panel/qp_oled_panel.c \
208 $(DRIVER_PATH)/painter/ld7032/qp_ld7032.c
209
185 endif 210 endif
186endef 211endef
187 212