diff options
| author | Pablo MartÃnez <58857054+elpekenin@users.noreply.github.com> | 2024-02-17 08:36:05 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-17 18:36:05 +1100 |
| commit | 538333571754c3cbeec92bc793b1b8023744cbbd (patch) | |
| tree | b2c46a2422f11a877bc49bf6be7bbba7e63c1865 | |
| parent | 39627c7620ceac2b5928019624cfab071f28420e (diff) | |
[Driver] ILI9486 on Quantum Painter (#18521)
Co-authored-by: Nick Brassel <nick@tzarc.org>
| -rw-r--r-- | docs/quantum_painter.md | 34 | ||||
| -rw-r--r-- | drivers/painter/comms/qp_comms_spi.h | 1 | ||||
| -rw-r--r-- | drivers/painter/ili9xxx/qp_ili9486.c | 298 | ||||
| -rw-r--r-- | drivers/painter/ili9xxx/qp_ili9486.h | 52 | ||||
| -rw-r--r-- | drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h | 1 | ||||
| -rw-r--r-- | quantum/painter/qp.h | 6 | ||||
| -rw-r--r-- | quantum/painter/qp_internal.c | 1 | ||||
| -rw-r--r-- | quantum/painter/rules.mk | 12 |
8 files changed, 405 insertions, 0 deletions
diff --git a/docs/quantum_painter.md b/docs/quantum_painter.md index dc855b1bf6..7f524b07ee 100644 --- a/docs/quantum_painter.md +++ b/docs/quantum_painter.md | |||
| @@ -24,6 +24,7 @@ Supported devices: | |||
| 24 | | GC9A01 | RGB LCD (circular) | 240x240 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += gc9a01_spi` | | 24 | | GC9A01 | RGB LCD (circular) | 240x240 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += gc9a01_spi` | |
| 25 | | ILI9163 | RGB LCD | 128x128 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9163_spi` | | 25 | | ILI9163 | RGB LCD | 128x128 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9163_spi` | |
| 26 | | ILI9341 | RGB LCD | 240x320 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9341_spi` | | 26 | | ILI9341 | RGB LCD | 240x320 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9341_spi` | |
| 27 | | ILI9486 | RGB LCD | 320x480 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9486_spi` | | ||
| 27 | | ILI9488 | RGB LCD | 320x480 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9488_spi` | | 28 | | ILI9488 | RGB LCD | 320x480 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9488_spi` | |
| 28 | | SSD1351 | RGB OLED | 128x128 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ssd1351_spi` | | 29 | | SSD1351 | RGB OLED | 128x128 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ssd1351_spi` | |
| 29 | | ST7735 | RGB LCD | 132x162, 80x160 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += st7735_spi` | | 30 | | ST7735 | RGB LCD | 132x162, 80x160 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += st7735_spi` | |
| @@ -281,6 +282,39 @@ The maximum number of displays can be configured by changing the following in yo | |||
| 281 | 282 | ||
| 282 | Native color format rgb565 is compatible with ILI9341 | 283 | Native color format rgb565 is compatible with ILI9341 |
| 283 | 284 | ||
| 285 | #### ** ILI9486 ** | ||
| 286 | |||
| 287 | Enabling support for the ILI9486 in Quantum Painter is done by adding the following to `rules.mk`: | ||
| 288 | |||
| 289 | ```make | ||
| 290 | QUANTUM_PAINTER_ENABLE = yes | ||
| 291 | QUANTUM_PAINTER_DRIVERS += ili9486_spi | ||
| 292 | ``` | ||
| 293 | |||
| 294 | Creating a ILI9486 device in firmware can then be done with the following API: | ||
| 295 | |||
| 296 | ```c | ||
| 297 | painter_device_t qp_ili9486_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); | ||
| 298 | ``` | ||
| 299 | |||
| 300 | There's another variant for this [Waveshare module](https://www.waveshare.com/wiki/3.5inch_TFT_Touch_Shield), because it has a quirky SPI->Parallel converter. You can create it with: | ||
| 301 | |||
| 302 | ```c | ||
| 303 | painter_device_t qp_ili9486_make_spi_waveshare_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); | ||
| 304 | ``` | ||
| 305 | |||
| 306 | The device handle returned from these functions can be used to perform all other drawing operations. | ||
| 307 | |||
| 308 | The maximum number of displays can be configured by changing the following in your `config.h` (default is 1): | ||
| 309 | |||
| 310 | ```c | ||
| 311 | // 3 displays: | ||
| 312 | #define ILI9486_NUM_DEVICES 3 | ||
| 313 | ``` | ||
| 314 | |||
| 315 | Native color format rgb888 is compatible with ILI9486 | ||
| 316 | Native color format rgb565 is compatible with ILI9486 Waveshare | ||
| 317 | |||
| 284 | #### ** ILI9488 ** | 318 | #### ** ILI9488 ** |
| 285 | 319 | ||
| 286 | Enabling support for the ILI9488 in Quantum Painter is done by adding the following to `rules.mk`: | 320 | Enabling support for the ILI9488 in Quantum Painter is done by adding the following to `rules.mk`: |
diff --git a/drivers/painter/comms/qp_comms_spi.h b/drivers/painter/comms/qp_comms_spi.h index ff323c3c10..c39ea95f72 100644 --- a/drivers/painter/comms/qp_comms_spi.h +++ b/drivers/painter/comms/qp_comms_spi.h | |||
| @@ -38,6 +38,7 @@ typedef struct qp_comms_spi_dc_reset_config_t { | |||
| 38 | bool command_params_uses_command_pin; // keep D/C held low when sending command sequences for data bytes | 38 | bool command_params_uses_command_pin; // keep D/C held low when sending command sequences for data bytes |
| 39 | } qp_comms_spi_dc_reset_config_t; | 39 | } qp_comms_spi_dc_reset_config_t; |
| 40 | 40 | ||
| 41 | bool qp_comms_spi_dc_reset_init(painter_device_t device); | ||
| 41 | void qp_comms_spi_dc_reset_send_command(painter_device_t device, uint8_t cmd); | 42 | void qp_comms_spi_dc_reset_send_command(painter_device_t device, uint8_t cmd); |
| 42 | uint32_t qp_comms_spi_dc_reset_send_data(painter_device_t device, const void* data, uint32_t byte_count); | 43 | uint32_t qp_comms_spi_dc_reset_send_data(painter_device_t device, const void* data, uint32_t byte_count); |
| 43 | void qp_comms_spi_dc_reset_bulk_command_sequence(painter_device_t device, const uint8_t* sequence, size_t sequence_len); | 44 | void qp_comms_spi_dc_reset_bulk_command_sequence(painter_device_t device, const uint8_t* sequence, size_t sequence_len); |
diff --git a/drivers/painter/ili9xxx/qp_ili9486.c b/drivers/painter/ili9xxx/qp_ili9486.c new file mode 100644 index 0000000000..c4f3c15cec --- /dev/null +++ b/drivers/painter/ili9xxx/qp_ili9486.c | |||
| @@ -0,0 +1,298 @@ | |||
| 1 | // Copyright 2021 Nick Brassel (@tzarc) | ||
| 2 | // Copyright 2023 Pablo Martinez (@elpekenin) <elpekenin@elpekenin.dev> | ||
| 3 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 4 | |||
| 5 | #include "qp_internal.h" | ||
| 6 | #include "qp_comms.h" | ||
| 7 | #include "qp_ili9486.h" | ||
| 8 | #include "qp_ili9xxx_opcodes.h" | ||
| 9 | #include "qp_tft_panel.h" | ||
| 10 | |||
| 11 | #ifdef QUANTUM_PAINTER_ILI9486_SPI_ENABLE | ||
| 12 | # include "spi_master.h" | ||
| 13 | # include <qp_comms_spi.h> | ||
| 14 | #endif // QUANTUM_PAINTER_ILI9486_SPI_ENABLE | ||
| 15 | |||
| 16 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 17 | // Common | ||
| 18 | |||
| 19 | // Driver storage | ||
| 20 | tft_panel_dc_reset_painter_device_t ili9486_drivers[ILI9486_NUM_DEVICES] = {0}; | ||
| 21 | |||
| 22 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 23 | // Initialization | ||
| 24 | |||
| 25 | bool qp_ili9486_init(painter_device_t device, painter_rotation_t rotation) { | ||
| 26 | // clang-format off | ||
| 27 | const uint8_t ili9486_init_sequence[] = { | ||
| 28 | // Command, Delay, N, Data[N] | ||
| 29 | ILI9XXX_CMD_RESET, 120, 0, | ||
| 30 | ILI9XXX_SET_PIX_FMT, 0, 1, 0x55, | ||
| 31 | ILI9XXX_SET_PGAMMA, 0, 15, 0x0F, 0x1F, 0x1C, 0x0C, 0x0F, 0x08, 0x48, 0x98, 0x37, 0x0A, 0x13, 0x04, 0x11, 0x0D, 0x00, | ||
| 32 | ILI9XXX_SET_NGAMMA, 0, 15, 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00, | ||
| 33 | ILI9XXX_SET_POWER_CTL_1, 0, 2, 0x0D, 0x0D, | ||
| 34 | ILI9XXX_SET_POWER_CTL_2, 0, 2, 0x43, 0x00, | ||
| 35 | ILI9XXX_SET_POWER_CTL_3, 0, 1, 0x00, | ||
| 36 | ILI9XXX_SET_VCOM_CTL_1, 0, 4, 0x00, 0x48, 0x00, 0x48, | ||
| 37 | ILI9XXX_SET_INVERSION_CTL, 0, 1, 0x02, | ||
| 38 | }; | ||
| 39 | // clang-format on | ||
| 40 | qp_comms_bulk_command_sequence(device, ili9486_init_sequence, sizeof(ili9486_init_sequence)); | ||
| 41 | |||
| 42 | // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM) | ||
| 43 | const uint8_t madctl[] = { | ||
| 44 | [QP_ROTATION_0] = ILI9XXX_MADCTL_BGR, | ||
| 45 | [QP_ROTATION_90] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MV, | ||
| 46 | [QP_ROTATION_180] = ILI9XXX_MADCTL_BGR, | ||
| 47 | [QP_ROTATION_270] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MV, | ||
| 48 | }; | ||
| 49 | const uint8_t functl[] = { | ||
| 50 | [QP_ROTATION_0] = 0x42, | ||
| 51 | [QP_ROTATION_90] = 0x62, | ||
| 52 | [QP_ROTATION_180] = 0x22, | ||
| 53 | [QP_ROTATION_270] = 0x02, | ||
| 54 | }; | ||
| 55 | |||
| 56 | // clang-format off | ||
| 57 | uint8_t rotation_sequence[] = { | ||
| 58 | // Command, Delay, N, Data[N] | ||
| 59 | ILI9XXX_SET_MEM_ACS_CTL, 0, 1, madctl[rotation], | ||
| 60 | ILI9XXX_SET_FUNCTION_CTL, 0, 2, 0x00, functl[rotation], | ||
| 61 | ILI9XXX_CMD_SLEEP_OFF, 5, 0, | ||
| 62 | ILI9XXX_CMD_DISPLAY_ON, 5, 0, | ||
| 63 | }; | ||
| 64 | // clang-format on | ||
| 65 | qp_comms_bulk_command_sequence(device, rotation_sequence, sizeof(rotation_sequence)); | ||
| 66 | |||
| 67 | return true; | ||
| 68 | } | ||
| 69 | |||
| 70 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 71 | // Driver vtable | ||
| 72 | |||
| 73 | // waveshare variant needs some tweaks due to shift registers | ||
| 74 | static void qp_comms_spi_dc_reset_send_command_odd_cs_pulse(painter_device_t device, uint8_t cmd) { | ||
| 75 | painter_driver_t * driver = (painter_driver_t *)device; | ||
| 76 | qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config; | ||
| 77 | |||
| 78 | writePinLow(comms_config->spi_config.chip_select_pin); | ||
| 79 | qp_comms_spi_dc_reset_send_command(device, cmd); | ||
| 80 | writePinHigh(comms_config->spi_config.chip_select_pin); | ||
| 81 | } | ||
| 82 | |||
| 83 | static uint32_t qp_comms_spi_send_data_odd_cs_pulse(painter_device_t device, const void *data, uint32_t byte_count) { | ||
| 84 | painter_driver_t * driver = (painter_driver_t *)device; | ||
| 85 | qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config; | ||
| 86 | |||
| 87 | uint32_t bytes_remaining = byte_count; | ||
| 88 | const uint8_t *p = (const uint8_t *)data; | ||
| 89 | uint32_t max_msg_length = 1024; | ||
| 90 | |||
| 91 | writePinHigh(comms_config->dc_pin); | ||
| 92 | while (bytes_remaining > 0) { | ||
| 93 | uint32_t bytes_this_loop = QP_MIN(bytes_remaining, max_msg_length); | ||
| 94 | bool odd_bytes = bytes_this_loop & 1; | ||
| 95 | |||
| 96 | // send data | ||
| 97 | writePinLow(comms_config->spi_config.chip_select_pin); | ||
| 98 | spi_transmit(p, bytes_this_loop); | ||
| 99 | p += bytes_this_loop; | ||
| 100 | |||
| 101 | // extra CS toggle, for alignment | ||
| 102 | if (odd_bytes) { | ||
| 103 | writePinHigh(comms_config->spi_config.chip_select_pin); | ||
| 104 | writePinLow(comms_config->spi_config.chip_select_pin); | ||
| 105 | } | ||
| 106 | |||
| 107 | bytes_remaining -= bytes_this_loop; | ||
| 108 | } | ||
| 109 | |||
| 110 | return byte_count - bytes_remaining; | ||
| 111 | } | ||
| 112 | |||
| 113 | static uint32_t qp_ili9486_send_data_toggling(painter_device_t device, const uint8_t *data, uint32_t byte_count) { | ||
| 114 | painter_driver_t * driver = (painter_driver_t *)device; | ||
| 115 | qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config; | ||
| 116 | |||
| 117 | uint32_t ret; | ||
| 118 | for (uint8_t j = 0; j < byte_count; ++j) { | ||
| 119 | writePinLow(comms_config->spi_config.chip_select_pin); | ||
| 120 | ret = qp_comms_spi_dc_reset_send_data(device, &data[j], 1); | ||
| 121 | writePinHigh(comms_config->spi_config.chip_select_pin); | ||
| 122 | } | ||
| 123 | |||
| 124 | return ret; | ||
| 125 | } | ||
| 126 | |||
| 127 | static void qp_comms_spi_send_command_sequence_odd_cs_pulse(painter_device_t device, const uint8_t *sequence, size_t sequence_len) { | ||
| 128 | for (size_t i = 0; i < sequence_len;) { | ||
| 129 | uint8_t command = sequence[i]; | ||
| 130 | uint8_t delay = sequence[i + 1]; | ||
| 131 | uint8_t num_bytes = sequence[i + 2]; | ||
| 132 | |||
| 133 | qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, command); | ||
| 134 | if (num_bytes > 0) { | ||
| 135 | qp_ili9486_send_data_toggling(device, &sequence[i + 3], num_bytes); | ||
| 136 | } | ||
| 137 | |||
| 138 | if (delay > 0) { | ||
| 139 | wait_ms(delay); | ||
| 140 | } | ||
| 141 | i += (3 + num_bytes); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | static bool qp_ili9486_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) { | ||
| 146 | painter_driver_t * driver = (painter_driver_t *)device; | ||
| 147 | tft_panel_dc_reset_painter_driver_vtable_t *vtable = (tft_panel_dc_reset_painter_driver_vtable_t *)driver->driver_vtable; | ||
| 148 | |||
| 149 | // Fix up the drawing location if required | ||
| 150 | left += driver->offset_x; | ||
| 151 | right += driver->offset_x; | ||
| 152 | top += driver->offset_y; | ||
| 153 | bottom += driver->offset_y; | ||
| 154 | |||
| 155 | // Check if we need to manually swap the window coordinates based on whether or not we're in a sideways rotation | ||
| 156 | if (vtable->swap_window_coords && (driver->rotation == QP_ROTATION_90 || driver->rotation == QP_ROTATION_270)) { | ||
| 157 | uint16_t temp; | ||
| 158 | |||
| 159 | temp = left; | ||
| 160 | left = top; | ||
| 161 | top = temp; | ||
| 162 | |||
| 163 | temp = right; | ||
| 164 | right = bottom; | ||
| 165 | bottom = temp; | ||
| 166 | } | ||
| 167 | |||
| 168 | // Set up the x-window | ||
| 169 | uint8_t xbuf[4] = {left >> 8, left & 0xFF, right >> 8, right & 0xFF}; | ||
| 170 | qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, vtable->opcodes.set_column_address); | ||
| 171 | qp_ili9486_send_data_toggling(device, xbuf, 4); | ||
| 172 | |||
| 173 | // Set up the y-window | ||
| 174 | uint8_t ybuf[4] = {top >> 8, top & 0xFF, bottom >> 8, bottom & 0xFF}; | ||
| 175 | qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, vtable->opcodes.set_row_address); | ||
| 176 | qp_ili9486_send_data_toggling(device, ybuf, 4); | ||
| 177 | |||
| 178 | // Lock in the window | ||
| 179 | qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, vtable->opcodes.enable_writes); | ||
| 180 | return true; | ||
| 181 | } | ||
| 182 | |||
| 183 | // Regular | ||
| 184 | const tft_panel_dc_reset_painter_driver_vtable_t ili9486_driver_vtable = { | ||
| 185 | .base = | ||
| 186 | { | ||
| 187 | .init = qp_ili9486_init, | ||
| 188 | .power = qp_tft_panel_power, | ||
| 189 | .clear = qp_tft_panel_clear, | ||
| 190 | .flush = qp_tft_panel_flush, | ||
| 191 | .pixdata = qp_tft_panel_pixdata, | ||
| 192 | .viewport = qp_tft_panel_viewport, | ||
| 193 | .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped, | ||
| 194 | .append_pixels = qp_tft_panel_append_pixels_rgb565, | ||
| 195 | .append_pixdata = qp_tft_panel_append_pixdata, | ||
| 196 | }, | ||
| 197 | .num_window_bytes = 2, | ||
| 198 | .swap_window_coords = false, | ||
| 199 | .opcodes = | ||
| 200 | { | ||
| 201 | .display_on = ILI9XXX_CMD_DISPLAY_ON, | ||
| 202 | .display_off = ILI9XXX_CMD_DISPLAY_OFF, | ||
| 203 | .set_column_address = ILI9XXX_SET_COL_ADDR, | ||
| 204 | .set_row_address = ILI9XXX_SET_PAGE_ADDR, | ||
| 205 | .enable_writes = ILI9XXX_SET_MEM, | ||
| 206 | }, | ||
| 207 | }; | ||
| 208 | |||
| 209 | // Waveshare tweaks | ||
| 210 | const tft_panel_dc_reset_painter_driver_vtable_t ili9486_waveshare_driver_vtable = { | ||
| 211 | .base = | ||
| 212 | { | ||
| 213 | .init = qp_ili9486_init, | ||
| 214 | .power = qp_tft_panel_power, | ||
| 215 | .clear = qp_tft_panel_clear, | ||
| 216 | .flush = qp_tft_panel_flush, | ||
| 217 | .pixdata = qp_tft_panel_pixdata, | ||
| 218 | .viewport = qp_ili9486_viewport, | ||
| 219 | .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped, | ||
| 220 | .append_pixels = qp_tft_panel_append_pixels_rgb565, | ||
| 221 | .append_pixdata = qp_tft_panel_append_pixdata, | ||
| 222 | }, | ||
| 223 | .num_window_bytes = 2, | ||
| 224 | .swap_window_coords = false, | ||
| 225 | .opcodes = | ||
| 226 | { | ||
| 227 | .display_on = ILI9XXX_CMD_DISPLAY_ON, | ||
| 228 | .display_off = ILI9XXX_CMD_DISPLAY_OFF, | ||
| 229 | .set_column_address = ILI9XXX_SET_COL_ADDR, | ||
| 230 | .set_row_address = ILI9XXX_SET_PAGE_ADDR, | ||
| 231 | .enable_writes = ILI9XXX_SET_MEM, | ||
| 232 | }, | ||
| 233 | }; | ||
| 234 | |||
| 235 | static const painter_comms_with_command_vtable_t spi_comms_odd_cs_pulse_vtable = { | ||
| 236 | .base = | ||
| 237 | { | ||
| 238 | .comms_init = qp_comms_spi_dc_reset_init, | ||
| 239 | .comms_start = qp_comms_spi_start, | ||
| 240 | .comms_send = qp_comms_spi_send_data_odd_cs_pulse, | ||
| 241 | .comms_stop = qp_comms_spi_stop, | ||
| 242 | }, | ||
| 243 | .send_command = qp_comms_spi_dc_reset_send_command_odd_cs_pulse, | ||
| 244 | .bulk_command_sequence = qp_comms_spi_send_command_sequence_odd_cs_pulse, | ||
| 245 | }; | ||
| 246 | |||
| 247 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 248 | // SPI | ||
| 249 | |||
| 250 | #ifdef QUANTUM_PAINTER_ILI9486_SPI_ENABLE | ||
| 251 | |||
| 252 | // Factory function for creating a handle to the ILI9486 device | ||
| 253 | painter_device_t qp_ili9486_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) { | ||
| 254 | for (uint32_t i = 0; i < ILI9486_NUM_DEVICES; ++i) { | ||
| 255 | tft_panel_dc_reset_painter_device_t *driver = &ili9486_drivers[i]; | ||
| 256 | if (!driver->base.driver_vtable) { | ||
| 257 | driver->base.driver_vtable = (const painter_driver_vtable_t *)&ili9486_driver_vtable; | ||
| 258 | driver->base.comms_vtable = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable; | ||
| 259 | driver->base.native_bits_per_pixel = 16; // RGB565 | ||
| 260 | driver->base.panel_width = panel_width; | ||
| 261 | driver->base.panel_height = panel_height; | ||
| 262 | driver->base.rotation = QP_ROTATION_0; | ||
| 263 | driver->base.offset_x = 0; | ||
| 264 | driver->base.offset_y = 0; | ||
| 265 | |||
| 266 | // SPI and other pin configuration | ||
| 267 | driver->base.comms_config = &driver->spi_dc_reset_config; | ||
| 268 | driver->spi_dc_reset_config.spi_config.chip_select_pin = chip_select_pin; | ||
| 269 | driver->spi_dc_reset_config.spi_config.divisor = spi_divisor; | ||
| 270 | driver->spi_dc_reset_config.spi_config.lsb_first = false; | ||
| 271 | driver->spi_dc_reset_config.spi_config.mode = spi_mode; | ||
| 272 | driver->spi_dc_reset_config.dc_pin = dc_pin; | ||
| 273 | driver->spi_dc_reset_config.reset_pin = reset_pin; | ||
| 274 | |||
| 275 | if (!qp_internal_register_device((painter_device_t)driver)) { | ||
| 276 | memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t)); | ||
| 277 | return NULL; | ||
| 278 | } | ||
| 279 | |||
| 280 | return (painter_device_t)driver; | ||
| 281 | } | ||
| 282 | } | ||
| 283 | return NULL; | ||
| 284 | } | ||
| 285 | |||
| 286 | painter_device_t qp_ili9486_make_spi_waveshare_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) { | ||
| 287 | painter_device_t device = qp_ili9486_make_spi_device(panel_width, panel_height, chip_select_pin, dc_pin, reset_pin, spi_divisor, spi_mode); | ||
| 288 | if (device) { | ||
| 289 | tft_panel_dc_reset_painter_device_t *driver = (tft_panel_dc_reset_painter_device_t *)device; | ||
| 290 | driver->base.driver_vtable = (const painter_driver_vtable_t *)&ili9486_waveshare_driver_vtable; | ||
| 291 | driver->base.comms_vtable = (const painter_comms_vtable_t *)&spi_comms_odd_cs_pulse_vtable; | ||
| 292 | } | ||
| 293 | return device; | ||
| 294 | } | ||
| 295 | |||
| 296 | #endif // QUANTUM_PAINTER_ILI9486_SPI_ENABLE | ||
| 297 | |||
| 298 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
diff --git a/drivers/painter/ili9xxx/qp_ili9486.h b/drivers/painter/ili9xxx/qp_ili9486.h new file mode 100644 index 0000000000..9976a78da4 --- /dev/null +++ b/drivers/painter/ili9xxx/qp_ili9486.h | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | // Copyright 2021 Nick Brassel (@tzarc) | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "gpio.h" | ||
| 7 | #include "qp_internal.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Quantum Painter ILI9486 configurables (add to your keyboard's config.h) | ||
| 11 | |||
| 12 | #ifndef ILI9486_NUM_DEVICES | ||
| 13 | /** | ||
| 14 | * @def This controls the maximum number of ILI9486 devices that Quantum Painter can communicate with at any one time. | ||
| 15 | * Increasing this number allows for multiple displays to be used. | ||
| 16 | */ | ||
| 17 | # define ILI9486_NUM_DEVICES 1 | ||
| 18 | #endif | ||
| 19 | |||
| 20 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 21 | // Quantum Painter ILI9486 device factories | ||
| 22 | |||
| 23 | #ifdef QUANTUM_PAINTER_ILI9486_SPI_ENABLE | ||
| 24 | /** | ||
| 25 | * Factory method for an ILI9486 SPI LCD device. | ||
| 26 | * | ||
| 27 | * @param panel_width[in] the width of the display panel | ||
| 28 | * @param panel_height[in] the height of the display panel | ||
| 29 | * @param chip_select_pin[in] the GPIO pin used for SPI chip select | ||
| 30 | * @param dc_pin[in] the GPIO pin used for D/C control | ||
| 31 | * @param reset_pin[in] the GPIO pin used for RST | ||
| 32 | * @param spi_divisor[in] the SPI divisor to use when communicating with the display | ||
| 33 | * @param spi_mode[in] the SPI mode to use when communicating with the display | ||
| 34 | * @return the device handle used with all drawing routines in Quantum Painter | ||
| 35 | */ | ||
| 36 | painter_device_t qp_ili9486_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); | ||
| 37 | |||
| 38 | /** | ||
| 39 | * Factory method for an ILI9486 SPI LCD device. | ||
| 40 | * | ||
| 41 | * @param panel_width[in] the width of the display panel | ||
| 42 | * @param panel_height[in] the height of the display panel | ||
| 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 | */ | ||
| 50 | painter_device_t qp_ili9486_make_spi_waveshare_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_ILI9486_SPI_ENABLE | ||
diff --git a/drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h b/drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h index f57e638e03..906f6cd772 100644 --- a/drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h +++ b/drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h | |||
| @@ -70,6 +70,7 @@ | |||
| 70 | #define ILI9XXX_SET_LIGHT_CTL_8 0xBF // Set backlight ctl 8 | 70 | #define ILI9XXX_SET_LIGHT_CTL_8 0xBF // Set backlight ctl 8 |
| 71 | #define ILI9XXX_SET_POWER_CTL_1 0xC0 // Set power ctl 1 | 71 | #define ILI9XXX_SET_POWER_CTL_1 0xC0 // Set power ctl 1 |
| 72 | #define ILI9XXX_SET_POWER_CTL_2 0xC1 // Set power ctl 2 | 72 | #define ILI9XXX_SET_POWER_CTL_2 0xC1 // Set power ctl 2 |
| 73 | #define ILI9XXX_SET_POWER_CTL_3 0xC2 // Set power ctl 3 | ||
| 73 | #define ILI9XXX_SET_VCOM_CTL_1 0xC5 // Set VCOM ctl 1 | 74 | #define ILI9XXX_SET_VCOM_CTL_1 0xC5 // Set VCOM ctl 1 |
| 74 | #define ILI9XXX_SET_VCOM_CTL_2 0xC7 // Set VCOM ctl 2 | 75 | #define ILI9XXX_SET_VCOM_CTL_2 0xC7 // Set VCOM ctl 2 |
| 75 | #define ILI9XXX_POWER_CTL_A 0xCB // Set power control A | 76 | #define ILI9XXX_POWER_CTL_A 0xCB // Set power control A |
diff --git a/quantum/painter/qp.h b/quantum/painter/qp.h index 873a9d9f32..02acbf589a 100644 --- a/quantum/painter/qp.h +++ b/quantum/painter/qp.h | |||
| @@ -509,6 +509,12 @@ int16_t qp_drawtext_recolor(painter_device_t device, uint16_t x, uint16_t y, pai | |||
| 509 | # define ILI9341_NUM_DEVICES 0 | 509 | # define ILI9341_NUM_DEVICES 0 |
| 510 | #endif // QUANTUM_PAINTER_ILI9341_ENABLE | 510 | #endif // QUANTUM_PAINTER_ILI9341_ENABLE |
| 511 | 511 | ||
| 512 | #ifdef QUANTUM_PAINTER_ILI9486_ENABLE | ||
| 513 | # include "qp_ili9486.h" | ||
| 514 | #else // QUANTUM_PAINTER_ILI9486_ENABLE | ||
| 515 | # define ILI9486_NUM_DEVICES 0 | ||
| 516 | #endif // QUANTUM_PAINTER_ILI9486_ENABLE | ||
| 517 | |||
| 512 | #ifdef QUANTUM_PAINTER_ILI9488_ENABLE | 518 | #ifdef QUANTUM_PAINTER_ILI9488_ENABLE |
| 513 | # include "qp_ili9488.h" | 519 | # include "qp_ili9488.h" |
| 514 | #else // QUANTUM_PAINTER_ILI9488_ENABLE | 520 | #else // QUANTUM_PAINTER_ILI9488_ENABLE |
diff --git a/quantum/painter/qp_internal.c b/quantum/painter/qp_internal.c index 0e81467e26..1f0f981796 100644 --- a/quantum/painter/qp_internal.c +++ b/quantum/painter/qp_internal.c | |||
| @@ -11,6 +11,7 @@ enum { | |||
| 11 | // NOTE: We intentionally do not include surfaces here, despite them conforming to the same API. | 11 | // NOTE: We intentionally do not include surfaces here, despite them conforming to the same API. |
| 12 | QP_NUM_DEVICES = (ILI9163_NUM_DEVICES) // ILI9163 | 12 | QP_NUM_DEVICES = (ILI9163_NUM_DEVICES) // ILI9163 |
| 13 | + (ILI9341_NUM_DEVICES) // ILI9341 | 13 | + (ILI9341_NUM_DEVICES) // ILI9341 |
| 14 | + (ILI9486_NUM_DEVICES) // ILI9486 | ||
| 14 | + (ILI9488_NUM_DEVICES) // ILI9488 | 15 | + (ILI9488_NUM_DEVICES) // ILI9488 |
| 15 | + (ST7789_NUM_DEVICES) // ST7789 | 16 | + (ST7789_NUM_DEVICES) // ST7789 |
| 16 | + (ST7735_NUM_DEVICES) // ST7735 | 17 | + (ST7735_NUM_DEVICES) // ST7735 |
diff --git a/quantum/painter/rules.mk b/quantum/painter/rules.mk index ca81cffb03..d991a6d742 100644 --- a/quantum/painter/rules.mk +++ b/quantum/painter/rules.mk | |||
| @@ -9,6 +9,7 @@ VALID_QUANTUM_PAINTER_DRIVERS := \ | |||
| 9 | surface \ | 9 | surface \ |
| 10 | ili9163_spi \ | 10 | ili9163_spi \ |
| 11 | ili9341_spi \ | 11 | ili9341_spi \ |
| 12 | ili9486_spi \ | ||
| 12 | ili9488_spi \ | 13 | ili9488_spi \ |
| 13 | st7735_spi \ | 14 | st7735_spi \ |
| 14 | st7789_spi \ | 15 | st7789_spi \ |
| @@ -80,6 +81,17 @@ define handle_quantum_painter_driver | |||
| 80 | $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \ | 81 | $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \ |
| 81 | $(DRIVER_PATH)/painter/ili9xxx/qp_ili9341.c \ | 82 | $(DRIVER_PATH)/painter/ili9xxx/qp_ili9341.c \ |
| 82 | 83 | ||
| 84 | else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ili9486_spi) | ||
| 85 | QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes | ||
| 86 | QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes | ||
| 87 | OPT_DEFS += -DQUANTUM_PAINTER_ILI9486_ENABLE -DQUANTUM_PAINTER_ILI9486_SPI_ENABLE | ||
| 88 | COMMON_VPATH += \ | ||
| 89 | $(DRIVER_PATH)/painter/tft_panel \ | ||
| 90 | $(DRIVER_PATH)/painter/ili9xxx | ||
| 91 | SRC += \ | ||
| 92 | $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \ | ||
| 93 | $(DRIVER_PATH)/painter/ili9xxx/qp_ili9486.c \ | ||
| 94 | |||
| 83 | else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ili9488_spi) | 95 | else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ili9488_spi) |
| 84 | QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes | 96 | QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes |
| 85 | QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes | 97 | QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes |
