Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ST7735 driver to Quantum Painter #17848

Merged
merged 3 commits into from
Aug 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion docs/quantum_painter.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Hardware supported:
| ILI9488 | RGB LCD | 320x480 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS = ili9488_spi` |
| SSD1351 | RGB OLED | 128x128 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS = ssd1351_spi` |
| ST7789 | RGB LCD | 240x320, 240x240 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS = st7789_spi` |
| ST7735 | RGB LCD | 132x162, 80x160 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS = st7735_spi` |

## Quantum Painter Configuration :id=quantum-painter-config

Expand Down Expand Up @@ -727,4 +728,30 @@ The maximum number of displays can be configured by changing the following in yo
#define ST7789_NUM_DEVICES 3
```

!> Some ST7789 devices are known to have different drawing offsets -- despite being a 240x320 pixel display controller internally, some display panels are only 240x240, or smaller. These may require an offset to be applied; see `qp_set_viewport_offsets` above for information on how to override the offsets if they aren't correctly rendered.
!> Some ST7789 devices are known to have different drawing offsets -- despite being a 240x320 pixel display controller internally, some display panels are only 240x240, or smaller. These may require an offset to be applied; see `qp_set_viewport_offsets` above for information on how to override the offsets if they aren't correctly rendered.

### ST7735 :id=qp-driver-st7735

Enabling support for the ST7735 in Quantum Painter is done by adding the following to `rules.mk`:

```make
QUANTUM_PAINTER_ENABLE = yes
QUANTUM_PAINTER_DRIVERS = st7735_spi
```

Creating a ST7735 device in firmware can then be done with the following API:

```c
painter_device_t qp_st7735_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);
```

The device handle returned from the `qp_st7735_make_spi_device` function can be used to perform all other drawing operations.

The maximum number of displays can be configured by changing the following in your `config.h` (default is 1):

```c
// 3 displays:
#define ST7735_NUM_DEVICES 3
```

!> Some ST7735 devices are known to have different drawing offsets -- despite being a 132x162 pixel display controller internally, some display panels are only 80x160, or smaller. These may require an offset to be applied; see `qp_set_viewport_offsets` above for information on how to override the offsets if they aren't correctly rendered.
144 changes: 144 additions & 0 deletions drivers/painter/st77xx/qp_st7735.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright 2021 Paul Cotter (@gr1mr3aver)
// Copyright 2021 Nick Brassel (@tzarc)
// Copyright 2022 David Hoelscher (@customMK)
// SPDX-License-Identifier: GPL-2.0-or-later

#include "qp_internal.h"
#include "qp_comms.h"
#include "qp_st7735.h"
#include "qp_st77xx_opcodes.h"
#include "qp_st7735_opcodes.h"
#include "qp_tft_panel.h"

#ifdef QUANTUM_PAINTER_ST7735_SPI_ENABLE
# include "qp_comms_spi.h"
#endif // QUANTUM_PAINTER_ST7735_SPI_ENABLE

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Common

// Driver storage
tft_panel_dc_reset_painter_device_t st7735_drivers[ST7735_NUM_DEVICES] = {0};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Automatic viewport offsets

#ifndef ST7735_NO_AUTOMATIC_OFFSETS
static inline void st7735_automatic_viewport_offsets(painter_device_t device, painter_rotation_t rotation) {
struct painter_driver_t *driver = (struct painter_driver_t *)device;

// clang-format off
const struct {
uint16_t offset_x;
uint16_t offset_y;
} rotation_offsets_80x160[] = {
[QP_ROTATION_0] = { .offset_x = 24, .offset_y = 0 },
[QP_ROTATION_90] = { .offset_x = 0, .offset_y = 24 },
[QP_ROTATION_180] = { .offset_x = 24, .offset_y = 0 },
[QP_ROTATION_270] = { .offset_x = 0, .offset_y = 24 },
};
// clang-format on

if (driver->panel_width == 80 && driver->panel_height == 160) {
driver->offset_x = rotation_offsets_80x160[rotation].offset_x;
driver->offset_y = rotation_offsets_80x160[rotation].offset_y;
}
}
#endif // ST7735_NO_AUTOMATIC_OFFSETS

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Initialization

bool qp_st7735_init(painter_device_t device, painter_rotation_t rotation) {
// clang-format off
const uint8_t st7735_init_sequence[] = {
// Command, Delay, N, Data[N]
ST77XX_CMD_RESET, 120, 0,
ST77XX_CMD_SLEEP_OFF, 5, 0,
ST77XX_SET_PIX_FMT, 0, 1, 0x55,
ST77XX_CMD_INVERT_OFF, 0, 0,
ST77XX_CMD_NORMAL_ON, 0, 0,
ST77XX_CMD_DISPLAY_ON, 20, 0
};
// clang-format on
qp_comms_bulk_command_sequence(device, st7735_init_sequence, sizeof(st7735_init_sequence));

// Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)
const uint8_t madctl[] = {
[QP_ROTATION_0] = ST77XX_MADCTL_BGR,
[QP_ROTATION_90] = ST77XX_MADCTL_BGR | ST77XX_MADCTL_MX | ST77XX_MADCTL_MV,
[QP_ROTATION_180] = ST77XX_MADCTL_BGR | ST77XX_MADCTL_MX | ST77XX_MADCTL_MY,
[QP_ROTATION_270] = ST77XX_MADCTL_BGR | ST77XX_MADCTL_MV | ST77XX_MADCTL_MY,
};
qp_comms_command_databyte(device, ST77XX_SET_MADCTL, madctl[rotation]);

#ifndef ST7735_NO_AUTOMATIC_VIEWPORT_OFFSETS
st7735_automatic_viewport_offsets(device, rotation);
#endif // ST7735_NO_AUTOMATIC_VIEWPORT_OFFSETS

return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Driver vtable

const struct tft_panel_dc_reset_painter_driver_vtable_t st7735_driver_vtable = {
.base =
{
.init = qp_st7735_init,
.power = qp_tft_panel_power,
.clear = qp_tft_panel_clear,
.flush = qp_tft_panel_flush,
.pixdata = qp_tft_panel_pixdata,
.viewport = qp_tft_panel_viewport,
.palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,
.append_pixels = qp_tft_panel_append_pixels_rgb565,
},
.num_window_bytes = 2,
.swap_window_coords = false,
.opcodes =
{
.display_on = ST77XX_CMD_DISPLAY_ON,
.display_off = ST77XX_CMD_DISPLAY_OFF,
.set_column_address = ST77XX_SET_COL_ADDR,
.set_row_address = ST77XX_SET_ROW_ADDR,
.enable_writes = ST77XX_SET_MEM,
},
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SPI

#ifdef QUANTUM_PAINTER_ST7735_SPI_ENABLE

// Factory function for creating a handle to the ST7735 device
painter_device_t qp_st7735_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) {
for (uint32_t i = 0; i < ST7735_NUM_DEVICES; ++i) {
tft_panel_dc_reset_painter_device_t *driver = &st7735_drivers[i];
if (!driver->base.driver_vtable) {
driver->base.driver_vtable = (const struct painter_driver_vtable_t *)&st7735_driver_vtable;
driver->base.comms_vtable = (const struct painter_comms_vtable_t *)&spi_comms_with_dc_vtable;
driver->base.panel_width = panel_width;
driver->base.panel_height = panel_height;
driver->base.rotation = QP_ROTATION_0;
driver->base.offset_x = 0;
driver->base.offset_y = 0;
driver->base.native_bits_per_pixel = 16; // RGB565

// SPI and other pin configuration
driver->base.comms_config = &driver->spi_dc_reset_config;
driver->spi_dc_reset_config.spi_config.chip_select_pin = chip_select_pin;
driver->spi_dc_reset_config.spi_config.divisor = spi_divisor;
driver->spi_dc_reset_config.spi_config.lsb_first = false;
driver->spi_dc_reset_config.spi_config.mode = spi_mode;
driver->spi_dc_reset_config.dc_pin = dc_pin;
driver->spi_dc_reset_config.reset_pin = reset_pin;
return (painter_device_t)driver;
}
}
return NULL;
}

#endif // QUANTUM_PAINTER_ST7735_SPI_ENABLE

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
45 changes: 45 additions & 0 deletions drivers/painter/st77xx/qp_st7735.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2021 Paul Cotter (@gr1mr3aver)
// Copyright 2021 Nick Brassel (@tzarc)
// Copyright 2022 David Hoelscher (@customMK)
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "gpio.h"
#include "qp_internal.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter ST7735 configurables (add to your keyboard's config.h)

#ifndef ST7735_NUM_DEVICES
/**
* @def This controls the maximum number of ST7735 devices that Quantum Painter can communicate with at any one time.
* Increasing this number allows for multiple displays to be used.
*/
# define ST7735_NUM_DEVICES 1
#endif

// Additional configuration options to be copied to your keyboard's config.h (don't change here):

// If you know exactly which offsets should be used on your panel with respect to selected rotation, then this config
// option allows you to save some flash space -- you'll need to invoke qp_set_viewport_offsets() instead from your keyboard.
// #define ST7735_NO_AUTOMATIC_VIEWPORT_OFFSETS

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter ST7735 device factories

#ifdef QUANTUM_PAINTER_ST7735_SPI_ENABLE
/**
* Factory method for an ST7735 SPI LCD device.
*
* @param panel_width[in] the width of the display panel
* @param panel_height[in] the height of the display panel
* @param chip_select_pin[in] the GPIO pin used for SPI chip select
* @param dc_pin[in] the GPIO pin used for D/C control
* @param reset_pin[in] the GPIO pin used for RST
* @param spi_divisor[in] the SPI divisor to use when communicating with the display
* @param spi_mode[in] the SPI mode to use when communicating with the display
* @return the device handle used with all drawing routines in Quantum Painter
*/
painter_device_t qp_st7735_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);
#endif // QUANTUM_PAINTER_ST7735_SPI_ENABLE
31 changes: 31 additions & 0 deletions drivers/painter/st77xx/qp_st7735_opcodes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2022 David Hoelscher (@customMK)
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter ST7735 additional command opcodes

// Panel Function Commands
#define ST7735_SET_FRAME_RATE_CTL_1 0xB1 // Set frame rate control 1
#define ST7735_SET_FRAME_RATE_CTL_2 0xB2 // Set frame rate control 2
#define ST7735_SET_FRAME_RATE_CTL_3 0xB3 // Set frame rate control 3
#define ST7735_SET_INVERSION_CTL 0xB4 // Set inversion mode control
#define ST7735_SET_DISPLAY_CTL 0xB6 // Set display control 5
#define ST7735_SET_POWER_CTL_1 0xC0 // Set GVDD
#define ST7735_SET_POWER_CTL_2 0xC1 // Set VGH and VGL
#define ST7735_SET_POWER_CTL_3 0xC2 // Set normal mode op amp current
#define ST7735_SET_POWER_CTL_4 0xC3 // Set idle mode op amp current
#define ST7735_SET_POWER_CTL_5 0xC4 // Set partial mode op amp current
#define ST7735_SET_VCOM_CTL 0xC5 // Set VCOM voltages
#define ST7735_SET_VCOM_OFFSET_CTL 0xC7 // Set VCOM offset ctl
#define ST7735_SET_LCD_ID 0xD1 // Set LCD module version
#define ST7735_SET_PROJECT_ID 0xD2 // Set product project ID
#define ST7735_SET_POWER_CTL_6 0xFC // Set partial+idle op amp current
#define ST7735_SET_NVMEM_CTL_STATUS 0xD9 // EEPROM Control Status
#define ST7735_SET_NVMEM_READ_CMD 0xCC // EEPROM Read Command
#define ST7735_SET_NVMEM_WRITE_CMD 0xDF // EEPROM Write Command
#define ST7735_SET_PGAMMA 0xE0 // Set positive gamma
#define ST7735_SET_NGAMMA 0xE1 // Set negative gamma
#define ST7735_SET_EXTENSION_ENABLE 0xF0 // Enable extension command
#define ST7735_SET_VCOM_DELAY 0xFF // Set VCOM delay time
4 changes: 4 additions & 0 deletions quantum/painter/qp.h
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,10 @@ int16_t qp_drawtext_recolor(painter_device_t device, uint16_t x, uint16_t y, pai
# include "qp_st7789.h"
#endif // QUANTUM_PAINTER_ST7789_ENABLE

#ifdef QUANTUM_PAINTER_ST7735_ENABLE
# include "qp_st7735.h"
#endif // QUANTUM_PAINTER_ST7735_ENABLE

#ifdef QUANTUM_PAINTER_GC9A01_ENABLE
# include "qp_gc9a01.h"
#endif // QUANTUM_PAINTER_GC9A01_ENABLE
Expand Down
13 changes: 12 additions & 1 deletion quantum/painter/rules.mk
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ QUANTUM_PAINTER_DRIVERS ?=
QUANTUM_PAINTER_ANIMATIONS_ENABLE ?= yes

# The list of permissible drivers that can be listed in QUANTUM_PAINTER_DRIVERS
VALID_QUANTUM_PAINTER_DRIVERS := ili9163_spi ili9341_spi ili9488_spi st7789_spi gc9a01_spi ssd1351_spi
VALID_QUANTUM_PAINTER_DRIVERS := ili9163_spi ili9341_spi ili9488_spi st7789_spi st7735_spi gc9a01_spi ssd1351_spi

#-------------------------------------------------------------------------------

Expand Down Expand Up @@ -83,6 +83,17 @@ define handle_quantum_painter_driver
$(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \
$(DRIVER_PATH)/painter/st77xx/qp_st7789.c

else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),st7735_spi)
QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes
QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes
OPT_DEFS += -DQUANTUM_PAINTER_ST7735_ENABLE -DQUANTUM_PAINTER_ST7735_SPI_ENABLE
COMMON_VPATH += \
$(DRIVER_PATH)/painter/tft_panel \
$(DRIVER_PATH)/painter/st77xx
SRC += \
$(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \
$(DRIVER_PATH)/painter/st77xx/qp_st7735.c

else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),gc9a01_spi)
QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes
QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes
Expand Down