qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

qp_st7735.c (6995B)


      1 // Copyright 2021 Paul Cotter (@gr1mr3aver)
      2 // Copyright 2021-2023 Nick Brassel (@tzarc)
      3 // Copyright 2022 David Hoelscher (@customMK)
      4 // SPDX-License-Identifier: GPL-2.0-or-later
      5 
      6 #include "qp_internal.h"
      7 #include "qp_comms.h"
      8 #include "qp_st7735.h"
      9 #include "qp_st77xx_opcodes.h"
     10 #include "qp_st7735_opcodes.h"
     11 #include "qp_tft_panel.h"
     12 
     13 #ifdef QUANTUM_PAINTER_ST7735_SPI_ENABLE
     14 #    include "qp_comms_spi.h"
     15 #endif // QUANTUM_PAINTER_ST7735_SPI_ENABLE
     16 
     17 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     18 // Common
     19 
     20 // Driver storage
     21 tft_panel_dc_reset_painter_device_t st7735_drivers[ST7735_NUM_DEVICES] = {0};
     22 
     23 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     24 // Automatic viewport offsets
     25 
     26 #ifndef ST7735_NO_AUTOMATIC_OFFSETS
     27 static inline void st7735_automatic_viewport_offsets(painter_device_t device, painter_rotation_t rotation) {
     28     painter_driver_t *driver = (painter_driver_t *)device;
     29 
     30     // clang-format off
     31     const struct {
     32         uint16_t offset_x;
     33         uint16_t offset_y;
     34     } rotation_offsets_80x160[] = {
     35         [QP_ROTATION_0]   = { .offset_x = 24, .offset_y =  0 },
     36         [QP_ROTATION_90]  = { .offset_x =  0, .offset_y = 24 },
     37         [QP_ROTATION_180] = { .offset_x = 24, .offset_y =  0 },
     38         [QP_ROTATION_270] = { .offset_x =  0, .offset_y = 24 },
     39     };
     40     // clang-format on
     41 
     42     if (driver->panel_width == 80 && driver->panel_height == 160) {
     43         driver->offset_x = rotation_offsets_80x160[rotation].offset_x;
     44         driver->offset_y = rotation_offsets_80x160[rotation].offset_y;
     45     }
     46 }
     47 #endif // ST7735_NO_AUTOMATIC_OFFSETS
     48 
     49 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     50 // Initialization
     51 
     52 __attribute__((weak)) bool qp_st7735_init(painter_device_t device, painter_rotation_t rotation) {
     53     // clang-format off
     54     const uint8_t st7735_init_sequence[] = {
     55         // Command,                 Delay, N, Data[N]
     56         ST77XX_CMD_RESET,            120,  0,
     57         ST77XX_CMD_SLEEP_OFF,          5,  0,
     58         ST77XX_SET_PIX_FMT,            0,  1, 0x55,
     59         ST77XX_CMD_INVERT_OFF,         0,  0,
     60         ST77XX_CMD_NORMAL_ON,          0,  0,
     61         ST7735_SET_PGAMMA,             0, 16, 0x02, 0x1C, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2D, 0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10,
     62         ST7735_SET_NGAMMA,             0, 16, 0x03, 0x1D, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D, 0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10,
     63         ST77XX_CMD_DISPLAY_ON,        20,  0
     64     };
     65     // clang-format on
     66     if (!qp_comms_bulk_command_sequence(device, st7735_init_sequence, sizeof(st7735_init_sequence))) {
     67         return false;
     68     }
     69 
     70     // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)
     71     const uint8_t madctl[] = {
     72         [QP_ROTATION_0]   = ST77XX_MADCTL_BGR,
     73         [QP_ROTATION_90]  = ST77XX_MADCTL_BGR | ST77XX_MADCTL_MX | ST77XX_MADCTL_MV,
     74         [QP_ROTATION_180] = ST77XX_MADCTL_BGR | ST77XX_MADCTL_MX | ST77XX_MADCTL_MY,
     75         [QP_ROTATION_270] = ST77XX_MADCTL_BGR | ST77XX_MADCTL_MV | ST77XX_MADCTL_MY,
     76     };
     77     if (!qp_comms_command_databyte(device, ST77XX_SET_MADCTL, madctl[rotation])) {
     78         return false;
     79     }
     80 
     81 #ifndef ST7735_NO_AUTOMATIC_VIEWPORT_OFFSETS
     82     st7735_automatic_viewport_offsets(device, rotation);
     83 #endif // ST7735_NO_AUTOMATIC_VIEWPORT_OFFSETS
     84 
     85     return true;
     86 }
     87 
     88 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     89 // Driver vtable
     90 
     91 const tft_panel_dc_reset_painter_driver_vtable_t st7735_driver_vtable = {
     92     .base =
     93         {
     94             .init            = qp_st7735_init,
     95             .power           = qp_tft_panel_power,
     96             .clear           = qp_tft_panel_clear,
     97             .flush           = qp_tft_panel_flush,
     98             .pixdata         = qp_tft_panel_pixdata,
     99             .viewport        = qp_tft_panel_viewport,
    100             .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,
    101             .append_pixels   = qp_tft_panel_append_pixels_rgb565,
    102             .append_pixdata  = qp_tft_panel_append_pixdata,
    103         },
    104     .num_window_bytes   = 2,
    105     .swap_window_coords = false,
    106     .opcodes =
    107         {
    108             .display_on         = ST77XX_CMD_DISPLAY_ON,
    109             .display_off        = ST77XX_CMD_DISPLAY_OFF,
    110             .set_column_address = ST77XX_SET_COL_ADDR,
    111             .set_row_address    = ST77XX_SET_ROW_ADDR,
    112             .enable_writes      = ST77XX_SET_MEM,
    113         },
    114 };
    115 
    116 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    117 // SPI
    118 
    119 #ifdef QUANTUM_PAINTER_ST7735_SPI_ENABLE
    120 
    121 // Factory function for creating a handle to the ST7735 device
    122 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) {
    123     for (uint32_t i = 0; i < ST7735_NUM_DEVICES; ++i) {
    124         tft_panel_dc_reset_painter_device_t *driver = &st7735_drivers[i];
    125         if (!driver->base.driver_vtable) {
    126             driver->base.driver_vtable         = (const painter_driver_vtable_t *)&st7735_driver_vtable;
    127             driver->base.comms_vtable          = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;
    128             driver->base.panel_width           = panel_width;
    129             driver->base.panel_height          = panel_height;
    130             driver->base.rotation              = QP_ROTATION_0;
    131             driver->base.offset_x              = 0;
    132             driver->base.offset_y              = 0;
    133             driver->base.native_bits_per_pixel = 16; // RGB565
    134 
    135             // SPI and other pin configuration
    136             driver->base.comms_config                                   = &driver->spi_dc_reset_config;
    137             driver->spi_dc_reset_config.spi_config.chip_select_pin      = chip_select_pin;
    138             driver->spi_dc_reset_config.spi_config.divisor              = spi_divisor;
    139             driver->spi_dc_reset_config.spi_config.lsb_first            = false;
    140             driver->spi_dc_reset_config.spi_config.mode                 = spi_mode;
    141             driver->spi_dc_reset_config.dc_pin                          = dc_pin;
    142             driver->spi_dc_reset_config.reset_pin                       = reset_pin;
    143             driver->spi_dc_reset_config.command_params_uses_command_pin = false;
    144 
    145             if (!qp_internal_register_device((painter_device_t)driver)) {
    146                 memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
    147                 return NULL;
    148             }
    149 
    150             return (painter_device_t)driver;
    151         }
    152     }
    153     return NULL;
    154 }
    155 
    156 #endif // QUANTUM_PAINTER_ST7735_SPI_ENABLE
    157 
    158 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////